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

Move group audio playing to Audio singletong/thread

This is a bit of a quick fix for a new bug, as but part of cleaning up the audio code and moving it to the Audio singleton
This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-11-19 22:26:04 +01:00
parent 4d4067cf88
commit 599c246ec7
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
6 changed files with 113 additions and 28 deletions

View File

@ -19,13 +19,31 @@
#include "src/core.h" #include "src/core.h"
#include <QDebug> #include <QDebug>
#include <QThread>
#include <cassert>
std::atomic<int> Audio::userCount{0}; std::atomic<int> Audio::userCount{0};
Audio* Audio::instance{nullptr};
QThread* Audio::audioThread{nullptr};
ALCdevice* Audio::alInDev{nullptr}; ALCdevice* Audio::alInDev{nullptr};
ALCdevice* Audio::alOutDev{nullptr}; ALCdevice* Audio::alOutDev{nullptr};
ALCcontext* Audio::alContext{0}; ALCcontext* Audio::alContext{nullptr};
ALuint Audio::alMainSource{0}; ALuint Audio::alMainSource{0};
Audio& Audio::getInstance()
{
if (!instance)
{
instance = new Audio();
audioThread = new QThread(instance);
audioThread->setObjectName("qTox Audio");
audioThread->start();
instance->moveToThread(audioThread);
}
return *instance;
}
void Audio::suscribeInput() void Audio::suscribeInput()
{ {
if (!userCount++ && alInDev) if (!userCount++ && alInDev)
@ -125,3 +143,64 @@ void Audio::playMono16Sound(const QByteArray& data)
alSourcePlay(alMainSource); alSourcePlay(alMainSource);
alDeleteBuffers(1, &buffer); alDeleteBuffers(1, &buffer);
} }
void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate,void*)
{
QMetaObject::invokeMethod(instance, "playGroupAudio", Qt::BlockingQueuedConnection,
Q_ARG(int,group), Q_ARG(int,peer), Q_ARG(const int16_t*,data),
Q_ARG(unsigned,samples), Q_ARG(uint8_t,channels), Q_ARG(unsigned,sample_rate));
}
void Audio::playGroupAudio(int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate)
{
assert(QThread::currentThread() == audioThread);
ToxGroupCall& call = Core::groupCalls[group];
if (!call.active || call.muteVol)
return;
if (!call.alSources.contains(peer))
alGenSources(1, &call.alSources[peer]);
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
}
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
{
assert(channels == 1 || channels == 2);
ALuint bufid;
ALint processed = 0, queued = 16;
alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
alSourcei(alSource, AL_LOOPING, AL_FALSE);
if(processed)
{
ALuint bufids[processed];
alSourceUnqueueBuffers(alSource, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
}
else if(queued < 16)
{
alGenBuffers(1, &bufid);
}
else
{
qDebug() << "Audio: Dropped frame";
return;
}
alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data,
samples * 2 * channels, sampleRate);
alSourceQueueBuffers(alSource, 1, &bufid);
ALint state;
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING)
alSourcePlay(alSource);
}

View File

@ -18,6 +18,8 @@
#ifndef AUDIO_H #ifndef AUDIO_H
#define AUDIO_H #define AUDIO_H
#include <QObject>
#include <QHash>
#include <atomic> #include <atomic>
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
@ -30,10 +32,17 @@
class QString; class QString;
class QByteArray; class QByteArray;
class QTimer;
class QThread;
struct Tox;
class Audio class Audio : QObject
{ {
Q_OBJECT
public: public:
static Audio& getInstance(); ///< Returns the singleton's instance. Will construct on first call.
static void suscribeInput(); ///< Call when you need to capture sound from the open input device. 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 unsuscribeInput(); ///< Call once you don't need to capture on the open input device anymore.
@ -45,15 +54,28 @@ public:
static void playMono16Sound(const QByteArray& data); ///< Play a 44100Hz mono 16bit PCM sound static void playMono16Sound(const QByteArray& data); ///< Play a 44100Hz mono 16bit PCM sound
/// May be called from any thread, will always queue a call to playGroupAudio
/// The first and last argument are ignored, but allow direct compatibility with toxcore
static void playGroupAudioQueued(Tox*, int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate, void*);
public slots:
/// Must be called from the audio thread, plays a group call's received audio
void playGroupAudio(int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate);
public: public:
static QThread* audioThread;
static ALCdevice* alOutDev, *alInDev; static ALCdevice* alOutDev, *alInDev;
static ALCcontext* alContext; static ALCcontext* alContext;
static ALuint alMainSource; static ALuint alMainSource;
private: private:
Audio(); explicit Audio()=default;
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
private: private:
static Audio* instance;
static std::atomic<int> userCount; static std::atomic<int> userCount;
}; };

View File

@ -56,8 +56,7 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
coreThread = CoreThread; coreThread = CoreThread;
audioThread = new QThread(); Audio::getInstance();
audioThread->start();
videobuf = new uint8_t[videobufsize]; videobuf = new uint8_t[videobufsize];
@ -1496,7 +1495,8 @@ int Core::joinGroupchat(int32_t friendnumber, uint8_t type, const uint8_t* frien
else if (type == TOX_GROUPCHAT_TYPE_AV) else if (type == TOX_GROUPCHAT_TYPE_AV)
{ {
qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber); qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber);
return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length, playGroupAudio, const_cast<Core*>(this)); return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length,
&Audio::playGroupAudioQueued, const_cast<Core*>(this));
} }
else else
{ {
@ -1641,7 +1641,7 @@ void Core::createGroup(uint8_t type)
} }
else if (type == TOX_GROUPCHAT_TYPE_AV) else if (type == TOX_GROUPCHAT_TYPE_AV)
{ {
emit emptyGroupCreated(toxav_add_av_groupchat(tox, playGroupAudio, this)); emit emptyGroupCreated(toxav_add_av_groupchat(tox, &Audio::playGroupAudioQueued, this));
} }
else else
{ {

View File

@ -250,8 +250,6 @@ private:
static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core); static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core);
static void onAvMediaChange(void *toxav, int32_t call_index, void* core); static void onAvMediaChange(void *toxav, int32_t call_index, void* core);
static void playGroupAudio(Tox* tox, int groupnumber, int friendgroupnumber, const int16_t* out_audio,
unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* userdata);
static void sendGroupCallAudio(int groupId, ToxAv* toxav); static void sendGroupCallAudio(int groupId, ToxAv* toxav);
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled); static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
@ -295,7 +293,9 @@ private:
static const int videobufsize; static const int videobufsize;
static uint8_t* videobuf; static uint8_t* videobuf;
static QThread *coreThread, *audioThread; static QThread *coreThread;
friend class Audio; ///< Audio can access our calls directly to reduce latency
}; };
#endif // CORE_HPP #endif // CORE_HPP

View File

@ -23,7 +23,6 @@
ToxCall Core::calls[TOXAV_MAX_CALLS]; 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;
QThread* Core::audioThread{nullptr};
bool Core::anyActiveCalls() bool Core::anyActiveCalls()
{ {
@ -590,22 +589,6 @@ VideoSource *Core::getVideoSourceFromCall(int callNumber)
return &calls[callNumber].videoSource; return &calls[callNumber].videoSource;
} }
void Core::playGroupAudio(Tox* /*tox*/, int groupnumber, int friendgroupnumber, const int16_t* out_audio,
unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* /*userdata*/)
{
if (!groupCalls[groupnumber].active)
return;
if (groupCalls[groupnumber].muteVol)
return;
if (!groupCalls[groupnumber].alSources.contains(friendgroupnumber))
alGenSources(1, &groupCalls[groupnumber].alSources[friendgroupnumber]);
playAudioBuffer(groupCalls[groupnumber].alSources[friendgroupnumber], out_audio,
out_audio_samples, decoder_channels, audio_sample_rate);
}
void Core::joinGroupCall(int groupId) void Core::joinGroupCall(int groupId)
{ {
qDebug() << QString("Core: Joining group call %1").arg(groupId); qDebug() << QString("Core: Joining group call %1").arg(groupId);
@ -631,7 +614,6 @@ void Core::joinGroupCall(int groupId)
groupCalls[groupId].sendAudioTimer->setSingleShot(true); groupCalls[groupId].sendAudioTimer->setSingleShot(true);
connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);}); connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);});
groupCalls[groupId].sendAudioTimer->start(); groupCalls[groupId].sendAudioTimer->start();
groupCalls[groupId].sendAudioTimer->moveToThread(audioThread);
} }
void Core::leaveGroupCall(int groupId) void Core::leaveGroupCall(int groupId)

View File

@ -187,6 +187,7 @@ void Widget::init()
qRegisterMetaType<vpx_image>("vpx_image"); qRegisterMetaType<vpx_image>("vpx_image");
qRegisterMetaType<uint8_t>("uint8_t"); qRegisterMetaType<uint8_t>("uint8_t");
qRegisterMetaType<uint16_t>("uint16_t"); qRegisterMetaType<uint16_t>("uint16_t");
qRegisterMetaType<const int16_t*>("const int16_t*");
qRegisterMetaType<int32_t>("int32_t"); qRegisterMetaType<int32_t>("int32_t");
qRegisterMetaType<int64_t>("int64_t"); qRegisterMetaType<int64_t>("int64_t");
qRegisterMetaType<QPixmap>("QPixmap"); qRegisterMetaType<QPixmap>("QPixmap");
@ -196,6 +197,7 @@ void Widget::init()
QString profilePath = detectProfile(); QString profilePath = detectProfile();
coreThread = new QThread(this); coreThread = new QThread(this);
coreThread->setObjectName("qTox Core");
core = new Core(Camera::getInstance(), coreThread, profilePath); core = new Core(Camera::getInstance(), coreThread, profilePath);
core->moveToThread(coreThread); core->moveToThread(coreThread);
connect(coreThread, &QThread::started, core, &Core::start); connect(coreThread, &QThread::started, core, &Core::start);