1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Refactor audio out of code, better resource management

Fixes #739 and fixes #777
This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-11-16 19:04:45 +01:00
parent 6c1d2bc9ac
commit c1f2e9a71d
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
8 changed files with 196 additions and 131 deletions

View File

@ -154,7 +154,8 @@ HEADERS += src/widget/form/addfriendform.h \
src/widget/toxsave.h \ src/widget/toxsave.h \
src/autoupdate.h \ src/autoupdate.h \
src/misc/serialize.h \ src/misc/serialize.h \
src/widget/form/settings/advancedform.h src/widget/form/settings/advancedform.h \
src/audio.h
SOURCES += \ SOURCES += \
src/widget/form/addfriendform.cpp \ src/widget/form/addfriendform.cpp \
@ -220,4 +221,5 @@ SOURCES += \
src/widget/toxsave.cpp \ src/widget/toxsave.cpp \
src/autoupdate.cpp \ src/autoupdate.cpp \
src/misc/serialize.cpp \ src/misc/serialize.cpp \
src/widget/form/settings/advancedform.cpp src/widget/form/settings/advancedform.cpp \
src/audio.cpp

112
src/audio.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "audio.h"
#include "src/core.h"
#include <QDebug>
std::atomic<int> 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 "<<inDevDescr;
Core::getInstance()->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 "<<outDevDescr;
}
Core::getInstance()->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);
}

43
src/audio.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef AUDIO_H
#define AUDIO_H
#include <atomic>
#if defined(__APPLE__) && defined(__MACH__)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#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<int> userCount;
};
#endif // AUDIO_H

View File

@ -20,6 +20,7 @@
#include "misc/settings.h" #include "misc/settings.h"
#include "widget/widget.h" #include "widget/widget.h"
#include "historykeeper.h" #include "historykeeper.h"
#include "src/audio.h"
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
@ -69,6 +70,8 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
for (int i=0; i<TOXAV_MAX_CALLS;i++) for (int i=0; i<TOXAV_MAX_CALLS;i++)
{ {
calls[i].active = false;
calls[i].alSource = 0;
calls[i].sendAudioTimer = new QTimer(); calls[i].sendAudioTimer = new QTimer();
calls[i].sendVideoTimer = new QTimer(); calls[i].sendVideoTimer = new QTimer();
calls[i].sendAudioTimer->moveToThread(coreThread); calls[i].sendAudioTimer->moveToThread(coreThread);
@ -78,38 +81,9 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
// OpenAL init // OpenAL init
QString outDevDescr = Settings::getInstance().getOutDev(); ; QString outDevDescr = Settings::getInstance().getOutDev(); ;
if (outDevDescr.isEmpty()) Audio::openOutput(outDevDescr);
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);
}
QString inDevDescr = Settings::getInstance().getInDev(); QString inDevDescr = Settings::getInstance().getInDev();
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; Audio::openInput(inDevDescr);
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";
} }
Core::~Core() Core::~Core()
@ -125,15 +99,8 @@ Core::~Core()
videobuf=nullptr; videobuf=nullptr;
} }
if (alContext) Audio::closeInput();
{ Audio::closeOutput();
alcMakeContextCurrent(nullptr);
alcDestroyContext(alContext);
}
if (alOutDev)
alcCloseDevice(alOutDev);
if (alInDev)
alcCaptureCloseDevice(alInDev);
clearPassword(Core::ptMain); clearPassword(Core::ptMain);
clearPassword(Core::ptHistory); clearPassword(Core::ptHistory);
@ -1876,64 +1843,20 @@ void Core::setNospam(uint32_t nospam)
tox_set_nospam(tox, 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 "<<inDevDescr;
// Force to regen each call's sources
for (ToxGroupCall& call : groupCalls) for (ToxGroupCall& call : groupCalls)
call.alSources.clear(); call.alSources.clear();
// Force to restart any call's capture for (ToxCall& call : calls)
if (alInDev)
alcCaptureStart(alInDev);
}
void Core::useAudioOutput(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() << "Core: Cannot open output audio device"; if (call.active && call.alSource)
}
else
{
alcDestroyContext(alContext);
alContext=alcCreateContext(alOutDev,nullptr);
if (!alcMakeContextCurrent(alContext))
{ {
qWarning() << "Core: Cannot create output audio context"; ALuint tmp = call.alSource;
alcCloseDevice(alOutDev); call.alSource = 0;
alDeleteSources(1, &tmp);
alGenSources(1, &call.alSource);
} }
else
alGenSources(1, &alMainSource);
qDebug() << "Core: Opening audio output "<<outDevDescr;
} }
// Force to regen each call's sources
for (ToxGroupCall& call : groupCalls)
call.alSources.clear();
} }

View File

@ -76,8 +76,7 @@ public:
bool isPasswordSet(PasswordType passtype); bool isPasswordSet(PasswordType passtype);
bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
void useAudioInput(const QString &name); void resetCallSources(); ///< Forces to regenerate each call's audio sources
void useAudioOutput(const QString &name);
public slots: public slots:
void start(); ///< Initializes the core, must be called before anything else void start(); ///< Initializes the core, must be called before anything else
@ -286,7 +285,7 @@ private:
QList<DhtServer> dhtServerList; QList<DhtServer> dhtServerList;
int dhtServerId; int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue; static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[]; static ToxCall calls[TOXAV_MAX_CALLS];
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
QMutex fileSendMutex, messageSendMutex; QMutex fileSendMutex, messageSendMutex;
bool ready; bool ready;
@ -296,12 +295,7 @@ private:
static const int videobufsize; static const int videobufsize;
static uint8_t* videobuf; static uint8_t* videobuf;
static ALCdevice* alOutDev, *alInDev;
static ALCcontext* alContext;
static QThread *coreThread; static QThread *coreThread;
public:
static ALuint alMainSource;
}; };
#endif // CORE_HPP #endif // CORE_HPP

View File

@ -16,6 +16,7 @@
#include "core.h" #include "core.h"
#include "video/camera.h" #include "video/camera.h"
#include "audio.h"
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
@ -23,10 +24,6 @@ ToxCall Core::calls[TOXAV_MAX_CALLS];
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
uint8_t* Core::videobuf; uint8_t* Core::videobuf;
ALCdevice* Core::alOutDev, *Core::alInDev;
ALCcontext* Core::alContext;
ALuint Core::alMainSource;
bool Core::anyActiveCalls() bool Core::anyActiveCalls()
{ {
for (auto& call : calls) 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); qWarning() << QString("Error starting call %1: toxav_prepare_transmission failed with %2").arg(callId).arg(r);
// Audio // Audio
alGenSources(1, &calls[callId].alSource); Audio::suscribeInput();
alcCaptureStart(alInDev);
// Go // Go
calls[callId].active = true; calls[callId].active = true;
@ -197,7 +193,7 @@ void Core::cleanupCall(int callId)
calls[callId].sendVideoTimer->stop(); calls[callId].sendVideoTimer->stop();
if (calls[callId].videoEnabled) if (calls[callId].videoEnabled)
Camera::getInstance()->unsubscribe(); Camera::getInstance()->unsubscribe();
alcCaptureStop(alInDev); Audio::unsuscribeInput();
} }
void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int samples, void *user_data) 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) if (!calls[callId].active)
return; return;
if (!calls[callId].alSource)
alGenSources(1, &calls[callId].alSource);
ToxAvCSettings dest; ToxAvCSettings dest;
if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0) if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0)
playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate); 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) if (!calls[callId].active)
return; return;
if (calls[callId].muteMic || !alInDev) if (calls[callId].muteMic || !Audio::alInDev)
{ {
calls[callId].sendAudioTimer->start(); calls[callId].sendAudioTimer->start();
return; return;
@ -229,11 +228,11 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
bool frame = false; bool frame = false;
ALint samples; ALint samples;
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
if(samples >= framesize) if(samples >= framesize)
{ {
memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind) memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind)
alcCaptureSamples(alInDev, buf, framesize); alcCaptureSamples(Audio::alInDev, buf, framesize);
frame = 1; frame = 1;
} }
@ -619,8 +618,7 @@ void Core::joinGroupCall(int groupId)
groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
// Audio // Audio
//alGenSources(1, &groupCalls[groupId].alSource); Audio::suscribeInput();
alcCaptureStart(alInDev);
// Go // Go
Core* core = Core::getInstance(); Core* core = Core::getInstance();
@ -640,8 +638,8 @@ void Core::leaveGroupCall(int groupId)
groupCalls[groupId].active = false; groupCalls[groupId].active = false;
disconnect(groupCalls[groupId].sendAudioTimer,0,0,0); disconnect(groupCalls[groupId].sendAudioTimer,0,0,0);
groupCalls[groupId].sendAudioTimer->stop(); groupCalls[groupId].sendAudioTimer->stop();
alcCaptureStop(alInDev);
groupCalls[groupId].alSources.clear(); groupCalls[groupId].alSources.clear();
Audio::unsuscribeInput();
} }
void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
@ -649,7 +647,7 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
if (!groupCalls[groupId].active) if (!groupCalls[groupId].active)
return; return;
if (groupCalls[groupId].muteMic || !alInDev) if (groupCalls[groupId].muteMic || !Audio::alInDev)
{ {
groupCalls[groupId].sendAudioTimer->start(); groupCalls[groupId].sendAudioTimer->start();
return; return;
@ -661,11 +659,11 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
bool frame = false; bool frame = false;
ALint samples; ALint samples;
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
if(samples >= framesize) if(samples >= framesize)
{ {
memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind) memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind)
alcCaptureSamples(alInDev, buf, framesize); alcCaptureSamples(Audio::alInDev, buf, framesize);
frame = 1; frame = 1;
} }

View File

@ -17,7 +17,7 @@
#include "avform.h" #include "avform.h"
#include "ui_avsettings.h" #include "ui_avsettings.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/core.h" #include "src/audio.h"
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#include <OpenAL/al.h> #include <OpenAL/al.h>
@ -180,11 +180,11 @@ void AVForm::getAudioOutDevices()
void AVForm::onInDevChanged(const QString &deviceDescriptor) void AVForm::onInDevChanged(const QString &deviceDescriptor)
{ {
Settings::getInstance().setInDev(deviceDescriptor); Settings::getInstance().setInDev(deviceDescriptor);
Core::getInstance()->useAudioInput(deviceDescriptor); Audio::openInput(deviceDescriptor);
} }
void AVForm::onOutDevChanged(const QString& deviceDescriptor) void AVForm::onOutDevChanged(const QString& deviceDescriptor)
{ {
Settings::getInstance().setOutDev(deviceDescriptor); Settings::getInstance().setOutDev(deviceDescriptor);
Core::getInstance()->useAudioOutput(deviceDescriptor); Audio::openOutput(deviceDescriptor);
} }

View File

@ -34,6 +34,7 @@
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "form/inputpassworddialog.h" #include "form/inputpassworddialog.h"
#include "src/autoupdate.h" #include "src/autoupdate.h"
#include "src/audio.h"
#include <QMessageBox> #include <QMessageBox>
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
@ -807,11 +808,7 @@ void Widget::newMessageAlert(GenericChatroomWidget* chat)
sndFile.close(); sndFile.close();
} }
ALuint buffer; Audio::playMono16Sound(sndData);
alGenBuffers(1, &buffer);
alBufferData(buffer, AL_FORMAT_MONO16, sndData.data(), sndData.size(), 44100);
alSourcei(core->alMainSource, AL_BUFFER, buffer);
alSourcePlay(core->alMainSource);
} }
void Widget::playRingtone() void Widget::playRingtone()
@ -827,11 +824,7 @@ void Widget::playRingtone()
sndFile1.close(); sndFile1.close();
} }
ALuint buffer; Audio::playMono16Sound(sndData1);
alGenBuffers(1, &buffer);
alBufferData(buffer, AL_FORMAT_MONO16, sndData1.data(), sndData1.size(), 44100);
alSourcei(core->alMainSource, AL_BUFFER, buffer);
alSourcePlay(core->alMainSource);
} }
void Widget::onFriendRequestReceived(const QString& userId, const QString& message) void Widget::onFriendRequestReceived(const QString& userId, const QString& message)