@ -28,8 +28,8 @@
# include "audio.h"
# include "audio.h"
# include "src/core/core.h"
# include "src/core/core.h"
# include "src/persistence/settings.h"
# include "src/core/coreav.h"
# include "src/core/coreav.h"
# include "src/persistence/settings.h"
# include <QDebug>
# include <QDebug>
# include <QFile>
# include <QFile>
@ -56,6 +56,22 @@
# include "audiofilterer.h"
# include "audiofilterer.h"
# endif
# endif
# ifndef CHECK_AL_ERROR
# define CHECK_AL_ERROR \
do { \
const ALenum al_err = alGetError ( ) ; \
if ( al_err ) { qWarning ( " OpenAL error: %d " , al_err ) ; } \
} while ( 0 )
# endif
# ifndef CHECK_ALC_ERROR
# define CHECK_ALC_ERROR(device) \
do { \
const ALCenum alc_err = alcGetError ( device ) ; \
if ( alc_err ) { qWarning ( " OpenAL error: %d " , alc_err ) ; } \
} while ( 0 )
# endif
/**
/**
@ class AudioPlayer
@ class AudioPlayer
@ -156,13 +172,12 @@ public:
AudioPrivate ( )
AudioPrivate ( )
: audioThread ( new QThread )
: audioThread ( new QThread )
, alInDev ( nullptr )
, alInDev ( nullptr )
, alOutDev ( nullptr )
, alContext ( nullptr )
, inputVolume ( 1.f )
, outputVolume ( 1.f )
, inputInitialized ( false )
, inputInitialized ( false )
, outputInitialized ( false )
, inSubscriptions ( 0 )
, inSubscriptions ( 0 )
, alOutDev ( nullptr )
, alOutContext ( nullptr )
, alMainSource ( 0 )
, outputInitialized ( false )
{
{
audioThread - > setObjectName ( " qTox Audio " ) ;
audioThread - > setObjectName ( " qTox Audio " ) ;
QObject : : connect ( audioThread , & QThread : : finished , audioThread , & QThread : : deleteLater ) ;
QObject : : connect ( audioThread , & QThread : : finished , audioThread , & QThread : : deleteLater ) ;
@ -179,6 +194,8 @@ public:
cleanupOutput ( ) ;
cleanupOutput ( ) ;
}
}
bool autoInitInput ( ) ;
bool autoInitOutput ( ) ;
bool initInput ( QString inDevDescr ) ;
bool initInput ( QString inDevDescr ) ;
bool initOutput ( QString outDevDescr ) ;
bool initOutput ( QString outDevDescr ) ;
void cleanupInput ( ) ;
void cleanupInput ( ) ;
@ -189,15 +206,15 @@ public:
QMutex audioLock ;
QMutex audioLock ;
ALCdevice * alInDev ;
ALCdevice * alInDev ;
ALfloat gain ;
bool inputInitialized ;
quint32 inSubscriptions ;
ALCdevice * alOutDev ;
ALCdevice * alOutDev ;
ALCcontext * alContext ;
ALCcontext * alOut Context ;
ALuint alMainSource ;
ALuint alMainSource ;
qreal inputVolume ;
qreal outputVolume ;
bool inputInitialized ;
bool outputInitialized ;
bool outputInitialized ;
quint32 inSubscriptions ;
ALSources outSources ;
ALSources outSources ;
QPointer < AudioMeter > audioMeter ;
QPointer < AudioMeter > audioMeter ;
@ -298,12 +315,21 @@ Audio::~Audio()
}
}
/**
/**
Returns the current output volume , between 0 and 1
Returns the current output volume ( between 0 and 1 )
*/
*/
qreal Audio : : outputVolume ( )
qreal Audio : : outputVolume ( )
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
return d - > outputVolume ;
if ( ! d - > alMainSource ) {
qWarning ( " Audio output source not initialized. " ) ;
return 0.0 ;
}
ALfloat volume ;
alGetSourcef ( d - > alMainSource , AL_GAIN , & volume ) ;
return volume ;
}
}
/**
/**
@ -315,31 +341,34 @@ void Audio::setOutputVolume(qreal volume)
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
d - > outputVolume = volume ;
if ( volume < 0.0 ) {
alSourcef ( d - > alMainSource , AL_GAIN , volume ) ;
volume = 0.0 ;
} else if ( volume > 1.0 ) {
for ( const ToxGroupCall & call : CoreAV : : groupCalls )
volume = 1.0 ;
{
alSourcef ( call . alSource , AL_GAIN , volume ) ;
}
}
for ( const ToxFriendCall & call : CoreAV : : calls )
alListenerf ( AL_GAIN , volume ) ;
{
CHECK_AL_ERROR ;
alSourcef ( call . alSource , AL_GAIN , volume ) ;
}
}
}
qreal Audio : : inputVolume ( )
qreal Audio : : inputVolume ( )
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
return d - > inputVolume ;
return d - > ga in;
}
}
void Audio : : setInputVolume ( qreal volume )
void Audio : : setInputVolume ( qreal volume )
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
d - > inputVolume = volume ;
if ( volume < 0.0 ) {
volume = 0.0 ;
} else if ( volume > 1.0 ) {
volume = 1.0 ;
}
d - > gain = volume ;
}
}
void Audio : : reinitInput ( const QString & inDevDesc )
void Audio : : reinitInput ( const QString & inDevDesc )
@ -365,12 +394,10 @@ void Audio::subscribeInput()
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
if ( ! d - > alInDev ) {
if ( ! d - > autoInitInput ( ) ) {
if ( ! d - > initInput ( Settings : : getInstance ( ) . getInDev ( ) ) ) {
qWarning ( " Failed to subscribe to audio input device. " ) ;
qWarning ( " Failed to subscribe to audio input device. " ) ;
return ;
return ;
}
}
}
d - > inSubscriptions + + ;
d - > inSubscriptions + + ;
qDebug ( ) < < " Subscribed to audio input device [ " < < d - > inSubscriptions < < " subscriptions ] " ;
qDebug ( ) < < " Subscribed to audio input device [ " < < d - > inSubscriptions < < " subscriptions ] " ;
@ -394,6 +421,26 @@ void Audio::unsubscribeInput()
d - > cleanupInput ( ) ;
d - > cleanupInput ( ) ;
}
}
/**
Initialize audio input device , if not initialized .
@ return true , if device was initialized ; false otherwise
*/
bool AudioPrivate : : autoInitInput ( )
{
return alInDev ? true : initInput ( Settings : : getInstance ( ) . getInDev ( ) ) ;
}
/**
Initialize audio output device , if not initialized .
@ return true , if device was initialized ; false otherwise
*/
bool AudioPrivate : : autoInitOutput ( )
{
return alOutDev ? true : initOutput ( Settings : : getInstance ( ) . getOutDev ( ) ) ;
}
bool AudioPrivate : : initInput ( QString inDevDescr )
bool AudioPrivate : : initInput ( QString inDevDescr )
{
{
qDebug ( ) < < " Opening audio input " < < inDevDescr ;
qDebug ( ) < < " Opening audio input " < < inDevDescr ;
@ -421,14 +468,14 @@ bool AudioPrivate::initInput(QString inDevDescr)
sampleRate , stereoFlag , bufSize ) ;
sampleRate , stereoFlag , bufSize ) ;
// Restart the capture if necessary
// Restart the capture if necessary
if ( alInDev ) {
if ( ! alInDev ) {
qDebug ( ) < < " Opened audio input " < < inDevDescr ;
qWarning ( ) < < " Failed to initialize audio input device: " < < inDevDescr ;
alcCaptureStart ( alInDev ) ;
} else {
qCritical ( ) < < " Failed to initialize audio input device: " < < inDevDescr ;
return false ;
return false ;
}
}
qDebug ( ) < < " Opened audio input " < < inDevDescr ;
alcCaptureStart ( alInDev ) ;
inputInitialized = true ;
inputInitialized = true ;
return true ;
return true ;
}
}
@ -445,7 +492,7 @@ bool AudioPrivate::initOutput(QString outDevDescr)
outputInitialized = false ;
outputInitialized = false ;
if ( outDevDescr = = " none " )
if ( outDevDescr = = " none " )
return tru e;
return fals e;
assert ( ! alOutDev ) ;
assert ( ! alOutDev ) ;
@ -459,33 +506,41 @@ bool AudioPrivate::initOutput(QString outDevDescr)
if ( ! outDevDescr . isEmpty ( ) )
if ( ! outDevDescr . isEmpty ( ) )
alOutDev = alcOpenDevice ( outDevDescr . toUtf8 ( ) . constData ( ) ) ;
alOutDev = alcOpenDevice ( outDevDescr . toUtf8 ( ) . constData ( ) ) ;
if ( alOutDev )
if ( ! alOutDev )
{
alContext = alcCreateContext ( alOutDev , nullptr ) ;
if ( alcMakeContextCurrent ( alContext ) )
{
{
alGenSources ( 1 , & alMainSource ) ;
qWarning ( ) < < " Cannot open output audio device " < < outDevDescr ;
alSourcef ( alMainSource , AL_GAIN , outputVolume ) ;
return false ;
}
}
else
{
qDebug ( ) < < " Opened audio output " < < outDevDescr ;
alOutContext = alcCreateContext ( alOutDev , nullptr ) ;
CHECK_ALC_ERROR ( alOutDev ) ;
if ( ! alcMakeContextCurrent ( alOutContext ) ) {
qWarning ( ) < < " Cannot create output audio context " ;
qWarning ( ) < < " Cannot create output audio context " ;
cleanupOutput ( ) ;
return false ;
return false ;
}
}
alGenSources ( 1 , & alMainSource ) ; CHECK_AL_ERROR ;
alSourcef ( alMainSource , AL_GAIN , 1.f ) ; CHECK_AL_ERROR ;
alSourcef ( alMainSource , AL_PITCH , 1.f ) ; CHECK_AL_ERROR ;
alSource3f ( alMainSource , AL_VELOCITY , 0.f , 0.f , 0.f ) ; CHECK_AL_ERROR ;
alSource3f ( alMainSource , AL_POSITION , 0.f , 0.f , 0.f ) ; CHECK_AL_ERROR ;
// init master volume
alListenerf ( AL_GAIN , Settings : : getInstance ( ) . getOutVolume ( ) * 0.01f ) ;
CHECK_AL_ERROR ;
Core * core = Core : : getInstance ( ) ;
Core * core = Core : : getInstance ( ) ;
if ( core )
if ( core ) {
core - > getAv ( ) - > invalidateCallSources ( ) ; // Force to regen each group call's sources
// reset each call's audio source
core - > getAv ( ) - > invalidateCallSources ( ) ;
}
outputInitialized = true ;
outputInitialized = true ;
return true ;
return true ;
}
}
qWarning ( ) < < " Cannot open output audio device " < < outDevDescr ;
return false ;
}
/**
/**
Play a 44100 Hz mono 16 bit PCM sound
Play a 44100 Hz mono 16 bit PCM sound
*/
*/
@ -493,10 +548,8 @@ void Audio::playMono16Sound(const QByteArray& data)
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
if ( ! d - > alOutDev )
if ( ! d - > autoInitOutput ( ) )
d - > initOutput ( Settings : : getInstance ( ) . getOutDev ( ) ) ;
return ;
alSourcef ( d - > alMainSource , AL_GAIN , d - > outputVolume ) ;
AudioPlayer * player = new AudioPlayer ( d - > alMainSource , data ) ;
AudioPlayer * player = new AudioPlayer ( d - > alMainSource , data ) ;
connect ( player , & AudioPlayer : : finished , [ = ] ( ) {
connect ( player , & AudioPlayer : : finished , [ = ] ( ) {
@ -552,12 +605,6 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
if ( call . inactive | | call . muteVol )
if ( call . inactive | | call . muteVol )
return ;
return ;
if ( ! call . alSource )
{
alGenSources ( 1 , & call . alSource ) ;
alSourcef ( call . alSource , AL_GAIN , d - > outputVolume ) ;
}
qreal volume = 0. ;
qreal volume = 0. ;
int bufsize = samples * 2 * channels ;
int bufsize = samples * 2 * channels ;
for ( int i = 0 ; i < bufsize ; + + i )
for ( int i = 0 ; i < bufsize ; + + i )
@ -606,7 +653,6 @@ void Audio::playAudioBuffer(quint32 alSource, const int16_t *data, int samples,
ALint state ;
ALint state ;
alGetSourcei ( alSource , AL_SOURCE_STATE , & state ) ;
alGetSourcei ( alSource , AL_SOURCE_STATE , & state ) ;
alSourcef ( alSource , AL_GAIN , d - > outputVolume ) ;
if ( state ! = AL_PLAYING )
if ( state ! = AL_PLAYING )
alSourcePlay ( alSource ) ;
alSourcePlay ( alSource ) ;
}
}
@ -645,17 +691,17 @@ void AudioPrivate::cleanupOutput()
outputInitialized = false ;
outputInitialized = false ;
if ( alOutDev ) {
if ( alOutDev ) {
qDebug ( ) < < " Closing audio output " ;
alSourcei ( alMainSource , AL_LOOPING , AL_FALSE ) ;
alSourcei ( alMainSource , AL_LOOPING , AL_FALSE ) ;
alSourceStop ( alMainSource ) ;
alSourceStop ( alMainSource ) ;
alDeleteSources ( 1 , & alMainSource ) ;
alDeleteSources ( 1 , & alMainSource ) ;
if ( ! alcMakeContextCurrent ( nullptr ) )
if ( ! alcMakeContextCurrent ( nullptr ) )
qWarning ( " Failed to clear current audio context. " ) ;
qWarning ( " Failed to clear audio context. " ) ;
alcDestroyContext ( alContext ) ;
alcDestroyContext ( alOut Context ) ;
alContext = nullptr ;
alOut Context = nullptr ;
qDebug ( ) < < " Closing audio output " ;
if ( alcCloseDevice ( alOutDev ) )
if ( alcCloseDevice ( alOutDev ) )
alOutDev = nullptr ;
alOutDev = nullptr ;
else
else
@ -701,17 +747,26 @@ void Audio::subscribeOutput(SID& sid)
{
{
QMutexLocker locker ( & d - > audioLock ) ;
QMutexLocker locker ( & d - > audioLock ) ;
if ( ! d - > alOutDev ) {
if ( ! d - > autoInitOutput ( ) ) {
if ( ! d - > initOutput ( Settings : : getInstance ( ) . getOutDev ( ) ) ) {
qWarning ( " Failed to subscribe to audio output device. " ) ;
qWarning ( " Failed to subscribe to audio output device. " ) ;
return ;
return ;
}
}
if ( ! alcMakeContextCurrent ( d - > alOutContext ) ) {
qWarning ( " Failed to activate output context. " ) ;
return ;
}
}
alGenSources ( 1 , & sid ) ;
alGenSources ( 1 , & sid ) ;
assert ( sid ) ;
assert ( sid ) ;
alSourcef ( sid , AL_GAIN , d - > outputVolume ) ;
d - > outSources < < sid ;
d - > outSources < < sid ;
// initialize source
alSourcef ( sid , AL_GAIN , 1.f ) ; CHECK_AL_ERROR ;
alSourcef ( sid , AL_PITCH , 1.f ) ; CHECK_AL_ERROR ;
alSource3f ( sid , AL_VELOCITY , 0.f , 0.f , 0.f ) ; CHECK_AL_ERROR ;
alSource3f ( sid , AL_POSITION , 0.f , 0.f , 0.f ) ; CHECK_AL_ERROR ;
qDebug ( ) < < " Audio source " < < sid < < " created. Sources active: "
qDebug ( ) < < " Audio source " < < sid < < " created. Sources active: "
< < d - > outSources . size ( ) ;
< < d - > outSources . size ( ) ;
}
}
@ -770,7 +825,7 @@ bool Audio::tryCaptureSamples(int16_t* buf, int samples)
for ( size_t i = 0 ; i < samples * AUDIO_CHANNELS ; + + i )
for ( size_t i = 0 ; i < samples * AUDIO_CHANNELS ; + + i )
{
{
int sample = buf [ i ] * pow ( d - > inputVolume , 2 ) ;
int sample = buf [ i ] ;
if ( sample < std : : numeric_limits < int16_t > : : min ( ) )
if ( sample < std : : numeric_limits < int16_t > : : min ( ) )
sample = std : : numeric_limits < int16_t > : : min ( ) ;
sample = std : : numeric_limits < int16_t > : : min ( ) ;