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:
parent
4d4067cf88
commit
599c246ec7
|
@ -19,13 +19,31 @@
|
|||
#include "src/core.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
std::atomic<int> Audio::userCount{0};
|
||||
Audio* Audio::instance{nullptr};
|
||||
QThread* Audio::audioThread{nullptr};
|
||||
ALCdevice* Audio::alInDev{nullptr};
|
||||
ALCdevice* Audio::alOutDev{nullptr};
|
||||
ALCcontext* Audio::alContext{0};
|
||||
ALCcontext* Audio::alContext{nullptr};
|
||||
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()
|
||||
{
|
||||
if (!userCount++ && alInDev)
|
||||
|
@ -125,3 +143,64 @@ void Audio::playMono16Sound(const QByteArray& data)
|
|||
alSourcePlay(alMainSource);
|
||||
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);
|
||||
}
|
||||
|
|
26
src/audio.h
26
src/audio.h
|
@ -18,6 +18,8 @@
|
|||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
@ -30,10 +32,17 @@
|
|||
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QTimer;
|
||||
class QThread;
|
||||
struct Tox;
|
||||
|
||||
class Audio
|
||||
class Audio : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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 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
|
||||
|
||||
/// 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:
|
||||
static QThread* audioThread;
|
||||
static ALCdevice* alOutDev, *alInDev;
|
||||
static ALCcontext* alContext;
|
||||
static ALuint alMainSource;
|
||||
|
||||
private:
|
||||
Audio();
|
||||
explicit Audio()=default;
|
||||
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
|
||||
private:
|
||||
static Audio* instance;
|
||||
static std::atomic<int> userCount;
|
||||
};
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
|
|||
|
||||
coreThread = CoreThread;
|
||||
|
||||
audioThread = new QThread();
|
||||
audioThread->start();
|
||||
Audio::getInstance();
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -1641,7 +1641,7 @@ void Core::createGroup(uint8_t type)
|
|||
}
|
||||
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
|
||||
{
|
||||
|
|
|
@ -250,8 +250,6 @@ private:
|
|||
static void onAvPeerTimeout(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 prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
|
||||
|
@ -295,7 +293,9 @@ private:
|
|||
static const int videobufsize;
|
||||
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
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
||||
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
|
||||
uint8_t* Core::videobuf;
|
||||
QThread* Core::audioThread{nullptr};
|
||||
|
||||
bool Core::anyActiveCalls()
|
||||
{
|
||||
|
@ -590,22 +589,6 @@ VideoSource *Core::getVideoSourceFromCall(int callNumber)
|
|||
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)
|
||||
{
|
||||
qDebug() << QString("Core: Joining group call %1").arg(groupId);
|
||||
|
@ -631,7 +614,6 @@ void Core::joinGroupCall(int groupId)
|
|||
groupCalls[groupId].sendAudioTimer->setSingleShot(true);
|
||||
connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);});
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
groupCalls[groupId].sendAudioTimer->moveToThread(audioThread);
|
||||
}
|
||||
|
||||
void Core::leaveGroupCall(int groupId)
|
||||
|
|
|
@ -187,6 +187,7 @@ void Widget::init()
|
|||
qRegisterMetaType<vpx_image>("vpx_image");
|
||||
qRegisterMetaType<uint8_t>("uint8_t");
|
||||
qRegisterMetaType<uint16_t>("uint16_t");
|
||||
qRegisterMetaType<const int16_t*>("const int16_t*");
|
||||
qRegisterMetaType<int32_t>("int32_t");
|
||||
qRegisterMetaType<int64_t>("int64_t");
|
||||
qRegisterMetaType<QPixmap>("QPixmap");
|
||||
|
@ -196,6 +197,7 @@ void Widget::init()
|
|||
|
||||
QString profilePath = detectProfile();
|
||||
coreThread = new QThread(this);
|
||||
coreThread->setObjectName("qTox Core");
|
||||
core = new Core(Camera::getInstance(), coreThread, profilePath);
|
||||
core->moveToThread(coreThread);
|
||||
connect(coreThread, &QThread::started, core, &Core::start);
|
||||
|
|
Loading…
Reference in New Issue
Block a user