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/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

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 "widget/widget.h"
#include "historykeeper.h"
#include "src/audio.h"
#include <tox/tox.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++)
{
calls[i].active = false;
calls[i].alSource = 0;
calls[i].sendAudioTimer = new QTimer();
calls[i].sendVideoTimer = new QTimer();
calls[i].sendAudioTimer->moveToThread(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 "<<inDevDescr;
// Force to regen each call's sources
for (ToxGroupCall& call : groupCalls)
call.alSources.clear();
// Force to restart any call's capture
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)
for (ToxCall& call : calls)
{
qWarning() << "Core: Cannot open output audio device";
}
else
{
alcDestroyContext(alContext);
alContext=alcCreateContext(alOutDev,nullptr);
if (!alcMakeContextCurrent(alContext))
if (call.active && call.alSource)
{
qWarning() << "Core: Cannot create output audio context";
alcCloseDevice(alOutDev);
ALuint tmp = call.alSource;
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 isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
void useAudioInput(const QString &name);
void useAudioOutput(const QString &name);
void resetCallSources(); ///< Forces to regenerate each call's audio sources
public slots:
void start(); ///< Initializes the core, must be called before anything else
@ -286,7 +285,7 @@ private:
QList<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[];
static ToxCall calls[TOXAV_MAX_CALLS];
static QHash<int, ToxGroupCall> 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

View File

@ -16,6 +16,7 @@
#include "core.h"
#include "video/camera.h"
#include "audio.h"
#include <QDebug>
#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};
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;
}

View File

@ -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 <OpenAL/al.h>
@ -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);
}

View File

@ -34,6 +34,7 @@
#include "src/historykeeper.h"
#include "form/inputpassworddialog.h"
#include "src/autoupdate.h"
#include "src/audio.h"
#include <QMessageBox>
#include <QDebug>
#include <QFile>
@ -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)