mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'cleanaudio'
This commit is contained in:
commit
fbecf4cfd5
|
@ -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);
|
||||||
|
}
|
||||||
|
|
26
src/audio.h
26
src/audio.h
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user