Browse Source

major changes to audio volume control

* use the ALListener object for master volume control
* initialize audio sources
* audio volumes are now expressed as percentage values between 0 and 1 to the public API
* removed artificial amplification of input samples
* removed invalid audio source generation in group calls
* minor: fixed gain slider max. value to 100 percent
pull/2860/head
Nils Fenner 10 years ago committed by tux3
parent
commit
7d547b10eb
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
  1. 191
      src/audio/audio.cpp
  2. 2
      src/widget/form/settings/avsettings.ui

191
src/audio/audio.cpp

@ -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* alOutContext;
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->gain;
} }
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 true; return false;
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 44100Hz mono 16bit PCM sound Play a 44100Hz mono 16bit 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(alOutContext);
alContext = nullptr; alOutContext = 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();

2
src/widget/form/settings/avsettings.ui

@ -90,7 +90,7 @@
<string>Use slider to set volume of your microphone.</string> <string>Use slider to set volume of your microphone.</string>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>400</number> <number>100</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>

Loading…
Cancel
Save