From c1f2e9a71d0ec6269df991f103f5a95cf26a8ca0 Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Sun, 16 Nov 2014 19:04:45 +0100 Subject: [PATCH] Refactor audio out of code, better resource management Fixes #739 and fixes #777 --- qtox.pro | 6 +- src/audio.cpp | 112 ++++++++++++++++++++++++++++ src/audio.h | 43 +++++++++++ src/core.cpp | 107 ++++---------------------- src/core.h | 10 +-- src/coreav.cpp | 30 ++++---- src/widget/form/settings/avform.cpp | 6 +- src/widget/widget.cpp | 13 +--- 8 files changed, 196 insertions(+), 131 deletions(-) create mode 100644 src/audio.cpp create mode 100644 src/audio.h diff --git a/qtox.pro b/qtox.pro index 5733a9aa0..ad7d2f34d 100644 --- a/qtox.pro +++ b/qtox.pro @@ -154,7 +154,8 @@ HEADERS += src/widget/form/addfriendform.h \ src/widget/toxsave.h \ src/autoupdate.h \ src/misc/serialize.h \ - src/widget/form/settings/advancedform.h + src/widget/form/settings/advancedform.h \ + src/audio.h SOURCES += \ src/widget/form/addfriendform.cpp \ @@ -220,4 +221,5 @@ SOURCES += \ src/widget/toxsave.cpp \ src/autoupdate.cpp \ src/misc/serialize.cpp \ - src/widget/form/settings/advancedform.cpp + src/widget/form/settings/advancedform.cpp \ + src/audio.cpp diff --git a/src/audio.cpp b/src/audio.cpp new file mode 100644 index 000000000..c8e27109b --- /dev/null +++ b/src/audio.cpp @@ -0,0 +1,112 @@ +#include "audio.h" +#include "src/core.h" + +#include + +std::atomic Audio::userCount{0}; +ALCdevice* Audio::alInDev{nullptr}; +ALCdevice* Audio::alOutDev{nullptr}; +ALCcontext* Audio::alContext{0}; +ALuint Audio::alMainSource{0}; + +void Audio::suscribeInput() +{ + if (userCount++) + if (alInDev) + alcCaptureStart(alInDev); +} + +void Audio::unsuscribeInput() +{ + if (--userCount) + if (alInDev) + alcCaptureStop(alInDev); +} + +void Audio::openInput(const QString& inDevDescr) +{ + auto* tmp = alInDev; + alInDev = nullptr; + alcCaptureCloseDevice(tmp); + int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + if (inDevDescr.isEmpty()) + alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag, + (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) + / 1000 * av_DefaultSettings.audio_channels); + else + alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag, + (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) + / 1000 * av_DefaultSettings.audio_channels); + if (!alInDev) + qWarning() << "Audio: Cannot open input audio device"; + else + qDebug() << "Audio: Opening audio input "<resetCallSources(); // Force to regen each group call's sources + + // Restart the capture if necessary + if (userCount.load() != 0) + alcCaptureStart(alInDev); +} + +void Audio::openOutput(const QString& outDevDescr) +{ + auto* tmp = alOutDev; + alOutDev = nullptr; + alcCloseDevice(tmp); + if (outDevDescr.isEmpty()) + alOutDev = alcOpenDevice(nullptr); + else + alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str()); + if (!alOutDev) + { + qWarning() << "Audio: Cannot open output audio device"; + } + else + { + alcDestroyContext(alContext); + alContext=alcCreateContext(alOutDev,nullptr); + if (!alcMakeContextCurrent(alContext)) + { + qWarning() << "Audio: Cannot create output audio context"; + alcCloseDevice(alOutDev); + } + else + alGenSources(1, &alMainSource); + + + qDebug() << "Audio: Opening audio output "<resetCallSources(); // Force to regen each group call's sources +} + +void Audio::closeInput() +{ + if (alInDev) + alcCaptureCloseDevice(alInDev); + + userCount = 0; +} + +void Audio::closeOutput() +{ + if (alContext) + { + alcMakeContextCurrent(nullptr); + alcDestroyContext(alContext); + } + + if (alOutDev) + alcCloseDevice(alOutDev); +} + +void Audio::playMono16Sound(const QByteArray& data) +{ + ALuint buffer; + alGenBuffers(1, &buffer); + alBufferData(buffer, AL_FORMAT_MONO16, data.data(), data.size(), 44100); + alSourcei(alMainSource, AL_BUFFER, buffer); + alSourcePlay(alMainSource); + alDeleteBuffers(1, &buffer); +} diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 000000000..5fb946f28 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,43 @@ +#ifndef AUDIO_H +#define AUDIO_H + +#include + +#if defined(__APPLE__) && defined(__MACH__) + #include + #include +#else + #include + #include +#endif + +class QString; +class QByteArray; + +class Audio +{ +public: + static void suscribeInput(); ///< Call when you need to capture sound from the open input device. + static void unsuscribeInput(); ///< Call once you don't need to capture on the open input device anymore. + + static void openInput(const QString& inDevDescr); ///< Open an input device, use before suscribing + static void openOutput(const QString& outDevDescr); ///< Open an output device + + static void closeInput(); ///< Close an input device, please don't use unless everyone's unsuscribed + static void closeOutput(); ///< Close an output device + + static void playMono16Sound(const QByteArray& data); ///< Play a 44100Hz mono 16bit PCM sound + +public: + static ALCdevice* alOutDev, *alInDev; + static ALCcontext* alContext; + static ALuint alMainSource; + +private: + Audio(); + +private: + static std::atomic userCount; +}; + +#endif // AUDIO_H diff --git a/src/core.cpp b/src/core.cpp index ffdb72317..5b83a0bcd 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -20,6 +20,7 @@ #include "misc/settings.h" #include "widget/widget.h" #include "historykeeper.h" +#include "src/audio.h" #include #include @@ -69,6 +70,8 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : for (int i=0; imoveToThread(coreThread); @@ -78,38 +81,9 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : // OpenAL init QString outDevDescr = Settings::getInstance().getOutDev(); ; - if (outDevDescr.isEmpty()) - alOutDev = alcOpenDevice(nullptr); - else - alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str()); - if (!alOutDev) - { - qWarning() << "Core: Cannot open output audio device"; - } - else - { - alContext=alcCreateContext(alOutDev,nullptr); - if (!alcMakeContextCurrent(alContext)) - { - qWarning() << "Core: Cannot create output audio context"; - alcCloseDevice(alOutDev); - } - else - alGenSources(1, &alMainSource); - } - + Audio::openOutput(outDevDescr); QString inDevDescr = Settings::getInstance().getInDev(); - int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; - if (inDevDescr.isEmpty()) - alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) - / 1000 * av_DefaultSettings.audio_channels); - else - alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) - / 1000 * av_DefaultSettings.audio_channels); - if (!alInDev) - qWarning() << "Core: Cannot open input audio device"; + Audio::openInput(inDevDescr); } Core::~Core() @@ -125,15 +99,8 @@ Core::~Core() videobuf=nullptr; } - if (alContext) - { - alcMakeContextCurrent(nullptr); - alcDestroyContext(alContext); - } - if (alOutDev) - alcCloseDevice(alOutDev); - if (alInDev) - alcCaptureCloseDevice(alInDev); + Audio::closeInput(); + Audio::closeOutput(); clearPassword(Core::ptMain); clearPassword(Core::ptHistory); @@ -1876,64 +1843,20 @@ void Core::setNospam(uint32_t nospam) tox_set_nospam(tox, nospam); } -void Core::useAudioInput(const QString& inDevDescr) +void Core::resetCallSources() { - auto* tmp = alInDev; - alInDev = nullptr; - alcCaptureCloseDevice(tmp); - int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; - if (inDevDescr.isEmpty()) - alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) - / 1000 * av_DefaultSettings.audio_channels); - else - alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) - / 1000 * av_DefaultSettings.audio_channels); - if (!alInDev) - qWarning() << "Core: Cannot open input audio device"; - else - qDebug() << "Core: Opening audio input "< dhtServerList; int dhtServerId; static QList fileSendQueue, fileRecvQueue; - static ToxCall calls[]; + static ToxCall calls[TOXAV_MAX_CALLS]; static QHash groupCalls; // Maps group IDs to ToxGroupCalls QMutex fileSendMutex, messageSendMutex; bool ready; @@ -296,12 +295,7 @@ private: static const int videobufsize; static uint8_t* videobuf; - static ALCdevice* alOutDev, *alInDev; - static ALCcontext* alContext; - static QThread *coreThread; -public: - static ALuint alMainSource; }; #endif // CORE_HPP diff --git a/src/coreav.cpp b/src/coreav.cpp index 80c6cf75e..fb6ad3343 100644 --- a/src/coreav.cpp +++ b/src/coreav.cpp @@ -16,6 +16,7 @@ #include "core.h" #include "video/camera.h" +#include "audio.h" #include #include @@ -23,10 +24,6 @@ ToxCall Core::calls[TOXAV_MAX_CALLS]; const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; uint8_t* Core::videobuf; -ALCdevice* Core::alOutDev, *Core::alInDev; -ALCcontext* Core::alContext; -ALuint Core::alMainSource; - bool Core::anyActiveCalls() { for (auto& call : calls) @@ -53,8 +50,7 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled qWarning() << QString("Error starting call %1: toxav_prepare_transmission failed with %2").arg(callId).arg(r); // Audio - alGenSources(1, &calls[callId].alSource); - alcCaptureStart(alInDev); + Audio::suscribeInput(); // Go calls[callId].active = true; @@ -197,7 +193,7 @@ void Core::cleanupCall(int callId) calls[callId].sendVideoTimer->stop(); if (calls[callId].videoEnabled) Camera::getInstance()->unsubscribe(); - alcCaptureStop(alInDev); + Audio::unsuscribeInput(); } void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int samples, void *user_data) @@ -207,6 +203,9 @@ void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int sample if (!calls[callId].active) return; + if (!calls[callId].alSource) + alGenSources(1, &calls[callId].alSource); + ToxAvCSettings dest; if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0) playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate); @@ -217,7 +216,7 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) if (!calls[callId].active) return; - if (calls[callId].muteMic || !alInDev) + if (calls[callId].muteMic || !Audio::alInDev) { calls[callId].sendAudioTimer->start(); return; @@ -229,11 +228,11 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) bool frame = false; ALint samples; - alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); + alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); if(samples >= framesize) { memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind) - alcCaptureSamples(alInDev, buf, framesize); + alcCaptureSamples(Audio::alInDev, buf, framesize); frame = 1; } @@ -619,8 +618,7 @@ void Core::joinGroupCall(int groupId) groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; // Audio - //alGenSources(1, &groupCalls[groupId].alSource); - alcCaptureStart(alInDev); + Audio::suscribeInput(); // Go Core* core = Core::getInstance(); @@ -640,8 +638,8 @@ void Core::leaveGroupCall(int groupId) groupCalls[groupId].active = false; disconnect(groupCalls[groupId].sendAudioTimer,0,0,0); groupCalls[groupId].sendAudioTimer->stop(); - alcCaptureStop(alInDev); groupCalls[groupId].alSources.clear(); + Audio::unsuscribeInput(); } void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) @@ -649,7 +647,7 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) if (!groupCalls[groupId].active) return; - if (groupCalls[groupId].muteMic || !alInDev) + if (groupCalls[groupId].muteMic || !Audio::alInDev) { groupCalls[groupId].sendAudioTimer->start(); return; @@ -661,11 +659,11 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) bool frame = false; ALint samples; - alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); + alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); if(samples >= framesize) { memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind) - alcCaptureSamples(alInDev, buf, framesize); + alcCaptureSamples(Audio::alInDev, buf, framesize); frame = 1; } diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp index c4920291b..962819c26 100644 --- a/src/widget/form/settings/avform.cpp +++ b/src/widget/form/settings/avform.cpp @@ -17,7 +17,7 @@ #include "avform.h" #include "ui_avsettings.h" #include "src/misc/settings.h" -#include "src/core.h" +#include "src/audio.h" #if defined(__APPLE__) && defined(__MACH__) #include @@ -180,11 +180,11 @@ void AVForm::getAudioOutDevices() void AVForm::onInDevChanged(const QString &deviceDescriptor) { Settings::getInstance().setInDev(deviceDescriptor); - Core::getInstance()->useAudioInput(deviceDescriptor); + Audio::openInput(deviceDescriptor); } void AVForm::onOutDevChanged(const QString& deviceDescriptor) { Settings::getInstance().setOutDev(deviceDescriptor); - Core::getInstance()->useAudioOutput(deviceDescriptor); + Audio::openOutput(deviceDescriptor); } diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 81e359370..6077b60ec 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -34,6 +34,7 @@ #include "src/historykeeper.h" #include "form/inputpassworddialog.h" #include "src/autoupdate.h" +#include "src/audio.h" #include #include #include @@ -807,11 +808,7 @@ void Widget::newMessageAlert(GenericChatroomWidget* chat) sndFile.close(); } - ALuint buffer; - alGenBuffers(1, &buffer); - alBufferData(buffer, AL_FORMAT_MONO16, sndData.data(), sndData.size(), 44100); - alSourcei(core->alMainSource, AL_BUFFER, buffer); - alSourcePlay(core->alMainSource); + Audio::playMono16Sound(sndData); } void Widget::playRingtone() @@ -827,11 +824,7 @@ void Widget::playRingtone() sndFile1.close(); } - ALuint buffer; - alGenBuffers(1, &buffer); - alBufferData(buffer, AL_FORMAT_MONO16, sndData1.data(), sndData1.size(), 44100); - alSourcei(core->alMainSource, AL_BUFFER, buffer); - alSourcePlay(core->alMainSource); + Audio::playMono16Sound(sndData1); } void Widget::onFriendRequestReceived(const QString& userId, const QString& message)