mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
refactor(audio): introduce IAudioSource interface and use it
This commit is contained in:
parent
2ccb1ec150
commit
e7e35642d7
@ -238,10 +238,13 @@ set(${PROJECT_NAME}_SOURCES
|
|||||||
src/audio/audio.h
|
src/audio/audio.h
|
||||||
src/audio/backend/alsink.cpp
|
src/audio/backend/alsink.cpp
|
||||||
src/audio/backend/alsink.h
|
src/audio/backend/alsink.h
|
||||||
|
src/audio/backend/alsource.cpp
|
||||||
|
src/audio/backend/alsource.h
|
||||||
src/audio/backend/openal.cpp
|
src/audio/backend/openal.cpp
|
||||||
src/audio/backend/openal.h
|
src/audio/backend/openal.h
|
||||||
src/audio/iaudiosettings.h
|
src/audio/iaudiosettings.h
|
||||||
src/audio/iaudiosink.h
|
src/audio/iaudiosink.h
|
||||||
|
src/audio/iaudiosource.h
|
||||||
src/chatlog/chatlinecontent.cpp
|
src/chatlog/chatlinecontent.cpp
|
||||||
src/chatlog/chatlinecontent.h
|
src/chatlog/chatlinecontent.h
|
||||||
src/chatlog/chatlinecontentproxy.cpp
|
src/chatlog/chatlinecontentproxy.cpp
|
||||||
|
@ -24,34 +24,9 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "src/persistence/settings.h"
|
#include "src/persistence/settings.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Audio
|
* @class Audio
|
||||||
*
|
*
|
||||||
* @enum Audio::Sound
|
|
||||||
* @brief Provides the different sounds for use in the getSound function.
|
|
||||||
* @see getSound
|
|
||||||
*
|
|
||||||
* @value NewMessage Returns the new message notification sound.
|
|
||||||
* @value Test Returns the test sound.
|
|
||||||
* @value IncomingCall Returns the incoming call sound.
|
|
||||||
* @value OutgoingCall Returns the outgoing call sound.
|
|
||||||
*
|
|
||||||
* @fn QString Audio::getSound(Sound s)
|
|
||||||
* @brief Function to get the path of the requested sound.
|
|
||||||
*
|
|
||||||
* @param s Name of the sound to get the path of.
|
|
||||||
* @return The path of the requested sound.
|
|
||||||
*
|
|
||||||
* @fn void Audio::frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels,
|
|
||||||
* uint32_t sampling_rate);
|
|
||||||
*
|
|
||||||
* When there are input subscribers, we regularly emit captured audio frames with this signal
|
|
||||||
* Always connect with a blocking queued connection lambda, else the behaviour is undefined
|
|
||||||
*
|
|
||||||
* @var Audio::AUDIO_SAMPLE_RATE
|
* @var Audio::AUDIO_SAMPLE_RATE
|
||||||
* @brief The next best Opus would take is 24k
|
* @brief The next best Opus would take is 24k
|
||||||
*
|
*
|
||||||
@ -89,37 +64,6 @@
|
|||||||
*
|
*
|
||||||
* @note Default is 30dB; usually you don't need to alter this value.
|
* @note Default is 30dB; usually you don't need to alter this value.
|
||||||
*
|
*
|
||||||
* @fn void Audio::subscribeInput()
|
|
||||||
* @brief Subscribe to capture sound from the opened input device.
|
|
||||||
*
|
|
||||||
* If the input device is not open, it will be opened before capturing.
|
|
||||||
*
|
|
||||||
* @fn void Audio::unsubscribeInput()
|
|
||||||
* @brief Unsubscribe from capturing from an opened input device.
|
|
||||||
*
|
|
||||||
* If the input device has no more subscriptions, it will be closed.
|
|
||||||
*
|
|
||||||
* @fn void Audio::playMono16Sound(const QString& path)
|
|
||||||
* @brief Play a 44100Hz mono 16bit PCM sound from a file
|
|
||||||
*
|
|
||||||
* @param[in] path the path to the sound file
|
|
||||||
*
|
|
||||||
* @fn void Audio::playMono16Sound(const QByteArray& data)
|
|
||||||
* @brief Play a 44100Hz mono 16bit PCM sound
|
|
||||||
*
|
|
||||||
* @param[in] data 44100Hz mono 16bit PCM data in host byte order
|
|
||||||
*
|
|
||||||
* @fn void Audio::playAudioBuffer(uint sourceId, const int16_t* data, int samples,
|
|
||||||
* unsigned channels, int sampleRate)
|
|
||||||
* @brief adds a number of audio frames to the play buffer
|
|
||||||
*
|
|
||||||
* @param[in] sourceId id obtained by subscribeOutput(uint &)
|
|
||||||
* @param[in] data 16bit mono or stereo PCM data with alternating channel
|
|
||||||
* mapping for stereo (LRLR)
|
|
||||||
* @param[in] samples number of samples per channel
|
|
||||||
* @param[in] channels number of channels, currently 1 or 2 is supported
|
|
||||||
* @param[in] sampleRate sample rate in Hertz
|
|
||||||
*
|
|
||||||
* @fn bool Audio::isOutputReady() const
|
* @fn bool Audio::isOutputReady() const
|
||||||
* @brief check if the output is ready to play audio
|
* @brief check if the output is ready to play audio
|
||||||
*
|
*
|
||||||
@ -135,24 +79,6 @@
|
|||||||
*
|
*
|
||||||
* @return list of input devices
|
* @return list of input devices
|
||||||
*
|
*
|
||||||
* @fn void Audio::subscribeOutput(uint& sid)
|
|
||||||
* @brief register a new output source
|
|
||||||
*
|
|
||||||
* param[out] sid contains the sourceId if source creation was successful,
|
|
||||||
* unchanged otherwise
|
|
||||||
*
|
|
||||||
* @fn void Audio::unsubscribeOutput(uint& sid)
|
|
||||||
* @brief unregisters an output source
|
|
||||||
*
|
|
||||||
* param[out] sid contains 0 if source deletion was successful,
|
|
||||||
* unchanged otherwise
|
|
||||||
*
|
|
||||||
* @fn void Audio::startLoop()
|
|
||||||
* @brief starts looping the sound played with playMono16Sound()
|
|
||||||
*
|
|
||||||
* @fn void Audio::stopLoop()
|
|
||||||
* @brief stops looping the sound played with playMono16Sound()
|
|
||||||
*
|
|
||||||
* @fn qreal Audio::inputGain() const
|
* @fn qreal Audio::inputGain() const
|
||||||
* @brief get the current input gain
|
* @brief get the current input gain
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class IAudioSink;
|
class IAudioSink;
|
||||||
|
class IAudioSource;
|
||||||
class Audio : public QObject
|
class Audio : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -60,12 +61,8 @@ public:
|
|||||||
virtual QStringList outDeviceNames() = 0;
|
virtual QStringList outDeviceNames() = 0;
|
||||||
virtual QStringList inDeviceNames() = 0;
|
virtual QStringList inDeviceNames() = 0;
|
||||||
|
|
||||||
virtual void subscribeInput() = 0;
|
|
||||||
virtual void unsubscribeInput() = 0;
|
|
||||||
|
|
||||||
virtual void stopActive() = 0;
|
|
||||||
|
|
||||||
virtual std::unique_ptr<IAudioSink> makeSink() = 0;
|
virtual std::unique_ptr<IAudioSink> makeSink() = 0;
|
||||||
|
virtual std::unique_ptr<IAudioSource> makeSource() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Public default audio settings
|
// Public default audio settings
|
||||||
@ -75,12 +72,6 @@ protected:
|
|||||||
static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL =
|
static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL =
|
||||||
AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000;
|
AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000;
|
||||||
uint32_t AUDIO_FRAME_SAMPLE_COUNT_TOTAL = 0;
|
uint32_t AUDIO_FRAME_SAMPLE_COUNT_TOTAL = 0;
|
||||||
|
|
||||||
signals:
|
|
||||||
void frameAvailable(const int16_t* pcm, size_t sample_count, uint8_t channels,
|
|
||||||
uint32_t sampling_rate);
|
|
||||||
void volumeAvailable(float value);
|
|
||||||
void startActive(qreal msec);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUDIO_H
|
#endif // AUDIO_H
|
||||||
|
41
src/audio/backend/alsource.cpp
Normal file
41
src/audio/backend/alsource.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "src/audio/backend/alsource.h"
|
||||||
|
#include "src/audio/backend/openal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emits audio frames captured by an input device or other audio source.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserves ressources for an audio source
|
||||||
|
* @param audio Main audio object, must have longer lifetime than this object.
|
||||||
|
*/
|
||||||
|
AlSource::AlSource(OpenAL& al)
|
||||||
|
: audio(al)
|
||||||
|
, killLock(QMutex::Recursive)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AlSource::~AlSource()
|
||||||
|
{
|
||||||
|
QMutexLocker{&killLock};
|
||||||
|
|
||||||
|
// unsubscribe only if not already killed
|
||||||
|
if (!killed) {
|
||||||
|
audio.destroySource(*this);
|
||||||
|
killed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlSource::operator bool() const
|
||||||
|
{
|
||||||
|
QMutexLocker{&killLock};
|
||||||
|
return !killed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlSource::kill()
|
||||||
|
{
|
||||||
|
killLock.lock();
|
||||||
|
// this flag is only set once here, afterwards the object is considered dead
|
||||||
|
killed = true;
|
||||||
|
killLock.unlock();
|
||||||
|
emit invalidated();
|
||||||
|
}
|
30
src/audio/backend/alsource.h
Normal file
30
src/audio/backend/alsource.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef ALSOURCE_H
|
||||||
|
#define ALSOURCE_H
|
||||||
|
|
||||||
|
#include "src/audio/iaudiosource.h"
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class OpenAL;
|
||||||
|
class AlSource : public IAudioSource
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AlSource(OpenAL& al);
|
||||||
|
AlSource(AlSource& src) = delete;
|
||||||
|
AlSource& operator=(const AlSource&) = delete;
|
||||||
|
AlSource(AlSource&& other) = delete;
|
||||||
|
AlSource& operator=(AlSource&& other) = delete;
|
||||||
|
~AlSource();
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
void kill();
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenAL& audio;
|
||||||
|
bool killed = false;
|
||||||
|
mutable QMutex killLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALSOURCE_H
|
@ -18,8 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "openal.h"
|
#include "openal.h"
|
||||||
#include "src/core/core.h"
|
|
||||||
#include "src/core/coreav.h"
|
|
||||||
#include "src/persistence/settings.h"
|
#include "src/persistence/settings.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -74,8 +72,9 @@ OpenAL::OpenAL()
|
|||||||
|
|
||||||
voiceTimer.setSingleShot(true);
|
voiceTimer.setSingleShot(true);
|
||||||
voiceTimer.moveToThread(audioThread);
|
voiceTimer.moveToThread(audioThread);
|
||||||
connect(this, &Audio::startActive, &voiceTimer, static_cast<void (QTimer::*)(int)>(&QTimer::start));
|
connect(this, &OpenAL::startActive, &voiceTimer,
|
||||||
connect(&voiceTimer, &QTimer::timeout, this, &Audio::stopActive);
|
static_cast<void (QTimer::*)(int)>(&QTimer::start));
|
||||||
|
connect(&voiceTimer, &QTimer::timeout, this, &OpenAL::stopActive);
|
||||||
|
|
||||||
connect(&captureTimer, &QTimer::timeout, this, &OpenAL::doAudio);
|
connect(&captureTimer, &QTimer::timeout, this, &OpenAL::doAudio);
|
||||||
captureTimer.setInterval(AUDIO_FRAME_DURATION / 2);
|
captureTimer.setInterval(AUDIO_FRAME_DURATION / 2);
|
||||||
@ -218,8 +217,19 @@ qreal OpenAL::maxInputThreshold() const
|
|||||||
void OpenAL::reinitInput(const QString& inDevDesc)
|
void OpenAL::reinitInput(const QString& inDevDesc)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&audioLock);
|
QMutexLocker locker(&audioLock);
|
||||||
|
|
||||||
|
const auto bakSources = sources;
|
||||||
|
sources.clear();
|
||||||
|
|
||||||
cleanupInput();
|
cleanupInput();
|
||||||
initInput(inDevDesc);
|
initInput(inDevDesc);
|
||||||
|
|
||||||
|
locker.unlock();
|
||||||
|
// this must happen outside `audioLock`, to avoid a deadlock when
|
||||||
|
// a slot on AlSource::invalidate tries to create a new source immedeately.
|
||||||
|
for (auto& source : bakSources) {
|
||||||
|
source->kill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenAL::reinitOutput(const QString& outDevDesc)
|
bool OpenAL::reinitOutput(const QString& outDevDesc)
|
||||||
@ -308,17 +318,25 @@ void OpenAL::destroySink(AlSink& sink)
|
|||||||
*
|
*
|
||||||
* If the input device is not open, it will be opened before capturing.
|
* If the input device is not open, it will be opened before capturing.
|
||||||
*/
|
*/
|
||||||
void OpenAL::subscribeInput()
|
std::unique_ptr<IAudioSource> OpenAL::makeSource()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&audioLock);
|
QMutexLocker locker(&audioLock);
|
||||||
|
|
||||||
if (!autoInitInput()) {
|
if (!autoInitInput()) {
|
||||||
qWarning("Failed to subscribe to audio input device.");
|
qWarning("Failed to subscribe to audio input device.");
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
++inSubscriptions;
|
auto const source = new AlSource(*this);
|
||||||
qDebug() << "Subscribed to audio input device [" << inSubscriptions << "subscriptions ]";
|
if (source == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.insert(source);
|
||||||
|
|
||||||
|
qDebug() << "Subscribed to audio input device [" << sources.size() << "subscriptions ]";
|
||||||
|
|
||||||
|
return std::unique_ptr<IAudioSource>{source};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -326,19 +344,24 @@ void OpenAL::subscribeInput()
|
|||||||
*
|
*
|
||||||
* If the input device has no more subscriptions, it will be closed.
|
* If the input device has no more subscriptions, it will be closed.
|
||||||
*/
|
*/
|
||||||
void OpenAL::unsubscribeInput()
|
void OpenAL::destroySource(AlSource& source)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&audioLock);
|
QMutexLocker locker(&audioLock);
|
||||||
|
|
||||||
if (!inSubscriptions)
|
const auto s = sources.find(&source);
|
||||||
|
if (s == sources.end()) {
|
||||||
|
qWarning() << "Destroyed non-existant source";
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inSubscriptions--;
|
sources.erase(s);
|
||||||
qDebug() << "Unsubscribed from audio input device [" << inSubscriptions << "subscriptions left ]";
|
|
||||||
|
|
||||||
if (!inSubscriptions)
|
qDebug() << "Unsubscribed from audio input device [" << sources.size() << "subscriptions left ]";
|
||||||
|
|
||||||
|
if (sources.empty()) {
|
||||||
cleanupInput();
|
cleanupInput();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize audio input device, if not initialized.
|
* @brief Initialize audio input device, if not initialized.
|
||||||
@ -646,14 +669,20 @@ void OpenAL::doInput()
|
|||||||
volume = 0;
|
volume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Audio::volumeAvailable(volume);
|
// NOTE(sudden6): this loop probably doesn't scale too well with many sources
|
||||||
|
for (auto source : sources) {
|
||||||
|
emit source->volumeAvailable(volume);
|
||||||
|
}
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Audio::frameAvailable(inputBuffer, AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL, channels,
|
// NOTE(sudden6): this loop probably doesn't scale too well with many sources
|
||||||
|
for (auto source : sources) {
|
||||||
|
emit source->frameAvailable(inputBuffer, AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL, channels,
|
||||||
AUDIO_SAMPLE_RATE);
|
AUDIO_SAMPLE_RATE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OpenAL::doOutput()
|
void OpenAL::doOutput()
|
||||||
{
|
{
|
||||||
@ -670,7 +699,7 @@ void OpenAL::doAudio()
|
|||||||
// Output section does nothing
|
// Output section does nothing
|
||||||
|
|
||||||
// Input section
|
// Input section
|
||||||
if (alInDev && inSubscriptions) {
|
if (alInDev && !sources.empty()) {
|
||||||
doInput();
|
doInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "src/audio/audio.h"
|
#include "src/audio/audio.h"
|
||||||
#include "src/audio/backend/alsink.h"
|
#include "src/audio/backend/alsink.h"
|
||||||
|
#include "src/audio/backend/alsource.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@ -89,8 +90,8 @@ public:
|
|||||||
std::unique_ptr<IAudioSink> makeSink();
|
std::unique_ptr<IAudioSink> makeSink();
|
||||||
void destroySink(AlSink& sink);
|
void destroySink(AlSink& sink);
|
||||||
|
|
||||||
void subscribeInput();
|
std::unique_ptr<IAudioSource> makeSource();
|
||||||
void unsubscribeInput();
|
void destroySource(AlSource& source);
|
||||||
|
|
||||||
void startLoop(uint sourceId);
|
void startLoop(uint sourceId);
|
||||||
void stopLoop(uint sourceId);
|
void stopLoop(uint sourceId);
|
||||||
@ -99,6 +100,8 @@ public:
|
|||||||
|
|
||||||
void playAudioBuffer(uint sourceId, const int16_t* data, int samples, unsigned channels,
|
void playAudioBuffer(uint sourceId, const int16_t* data, int samples, unsigned channels,
|
||||||
int sampleRate);
|
int sampleRate);
|
||||||
|
signals:
|
||||||
|
void startActive(qreal msec);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void checkAlError() noexcept;
|
static void checkAlError() noexcept;
|
||||||
@ -135,7 +138,6 @@ protected:
|
|||||||
QString outDev{};
|
QString outDev{};
|
||||||
|
|
||||||
ALCdevice* alInDev = nullptr;
|
ALCdevice* alInDev = nullptr;
|
||||||
quint32 inSubscriptions = 0;
|
|
||||||
QTimer captureTimer;
|
QTimer captureTimer;
|
||||||
QTimer cleanupTimer;
|
QTimer cleanupTimer;
|
||||||
|
|
||||||
@ -147,9 +149,7 @@ protected:
|
|||||||
// Qt containers need copy operators, so use stdlib containers
|
// Qt containers need copy operators, so use stdlib containers
|
||||||
std::unordered_set<AlSink*> sinks;
|
std::unordered_set<AlSink*> sinks;
|
||||||
std::unordered_set<AlSink*> soundSinks;
|
std::unordered_set<AlSink*> soundSinks;
|
||||||
|
std::unordered_set<AlSource*> sources;
|
||||||
// number of output sources
|
|
||||||
int outCount = 0;
|
|
||||||
|
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
qreal gain = 0;
|
qreal gain = 0;
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "openal2.h"
|
#include "openal2.h"
|
||||||
#include "src/core/core.h"
|
|
||||||
#include "src/core/coreav.h"
|
|
||||||
#include "src/persistence/settings.h"
|
#include "src/persistence/settings.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -3,7 +3,14 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class Audio;
|
/**
|
||||||
|
* @fn void Audio::frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels,
|
||||||
|
* uint32_t sampling_rate);
|
||||||
|
*
|
||||||
|
* When there are input subscribers, we regularly emit captured audio frames with this signal
|
||||||
|
* Always connect with a blocking queued connection lambda, else the behaviour is undefined
|
||||||
|
*/
|
||||||
|
|
||||||
class IAudioSource : public QObject
|
class IAudioSource : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -31,14 +31,14 @@
|
|||||||
ToxCall::ToxCall(bool VideoEnabled, CoreAV& av)
|
ToxCall::ToxCall(bool VideoEnabled, CoreAV& av)
|
||||||
: av{&av}
|
: av{&av}
|
||||||
, videoEnabled{VideoEnabled}
|
, videoEnabled{VideoEnabled}
|
||||||
|
, audioSource{Audio::getInstance().makeSource()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ToxCall::~ToxCall()
|
ToxCall::~ToxCall()
|
||||||
{
|
{
|
||||||
Audio& audio = Audio::getInstance();
|
|
||||||
|
|
||||||
QObject::disconnect(audioInConn);
|
QObject::disconnect(audioInConn);
|
||||||
audio.unsubscribeInput();
|
QObject::disconnect(audioSrcInvalid);
|
||||||
|
|
||||||
if (videoEnabled) {
|
if (videoEnabled) {
|
||||||
QObject::disconnect(videoInConn);
|
QObject::disconnect(videoInConn);
|
||||||
CameraSource::getInstance().unsubscribe();
|
CameraSource::getInstance().unsubscribe();
|
||||||
@ -105,15 +105,16 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
|||||||
, sink(Audio::getInstance().makeSink())
|
, sink(Audio::getInstance().makeSink())
|
||||||
, friendId{FriendNum}
|
, friendId{FriendNum}
|
||||||
{
|
{
|
||||||
// register audio
|
// TODO(sudden6): move this to audio source
|
||||||
Audio& audio = Audio::getInstance();
|
audioInConn =
|
||||||
audio.subscribeInput();
|
QObject::connect(audioSource.get(), &IAudioSource::frameAvailable,
|
||||||
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
[this](const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) {
|
||||||
[&av, FriendNum](const int16_t* pcm, size_t samples,
|
this->av->sendCallAudio(this->friendId, pcm, samples, chans, rate);
|
||||||
uint8_t chans, uint32_t rate) {
|
|
||||||
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
audioSrcInvalid = QObject::connect(audioSource.get(), &IAudioSource::invalidated,
|
||||||
|
[this]() { this->onAudioSourceInvalidated(); });
|
||||||
|
|
||||||
if (!audioInConn) {
|
if (!audioInConn) {
|
||||||
qDebug() << "Audio input connection not working";
|
qDebug() << "Audio input connection not working";
|
||||||
}
|
}
|
||||||
@ -146,6 +147,20 @@ ToxFriendCall::~ToxFriendCall()
|
|||||||
QObject::disconnect(audioSinkInvalid);
|
QObject::disconnect(audioSinkInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToxFriendCall::onAudioSourceInvalidated()
|
||||||
|
{
|
||||||
|
auto newSrc = Audio::getInstance().makeSource();
|
||||||
|
audioInConn =
|
||||||
|
QObject::connect(newSrc.get(), &IAudioSource::frameAvailable,
|
||||||
|
[this](const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) {
|
||||||
|
this->av->sendCallAudio(this->friendId, pcm, samples, chans, rate);
|
||||||
|
});
|
||||||
|
audioSource = std::move(newSrc);
|
||||||
|
|
||||||
|
audioSrcInvalid = QObject::connect(audioSource.get(), &IAudioSource::invalidated,
|
||||||
|
[this]() { this->onAudioSourceInvalidated(); });
|
||||||
|
}
|
||||||
|
|
||||||
void ToxFriendCall::onAudioSinkInvalidated()
|
void ToxFriendCall::onAudioSinkInvalidated()
|
||||||
{
|
{
|
||||||
auto newSink = Audio::getInstance().makeSink();
|
auto newSink = Audio::getInstance().makeSink();
|
||||||
@ -201,17 +216,18 @@ ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV& av)
|
|||||||
, groupId{GroupNum}
|
, groupId{GroupNum}
|
||||||
{
|
{
|
||||||
// register audio
|
// register audio
|
||||||
Audio& audio = Audio::getInstance();
|
audioInConn =
|
||||||
audio.subscribeInput();
|
QObject::connect(audioSource.get(), &IAudioSource::frameAvailable,
|
||||||
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
[this](const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) {
|
||||||
[&av, GroupNum](const int16_t* pcm, size_t samples,
|
this->av->sendGroupCallAudio(this->groupId, pcm, samples, chans, rate);
|
||||||
uint8_t chans, uint32_t rate) {
|
|
||||||
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!audioInConn) {
|
if (!audioInConn) {
|
||||||
qDebug() << "Audio input connection not working";
|
qDebug() << "Audio input connection not working";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audioSrcInvalid = QObject::connect(audioSource.get(), &IAudioSource::invalidated,
|
||||||
|
[this]() { this->onAudioSourceInvalidated(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxGroupCall::~ToxGroupCall()
|
ToxGroupCall::~ToxGroupCall()
|
||||||
@ -220,6 +236,23 @@ ToxGroupCall::~ToxGroupCall()
|
|||||||
clearPeers();
|
clearPeers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToxGroupCall::onAudioSourceInvalidated()
|
||||||
|
{
|
||||||
|
auto newSrc = Audio::getInstance().makeSource();
|
||||||
|
// TODO(sudden6): move this to audio source
|
||||||
|
audioInConn =
|
||||||
|
QObject::connect(audioSource.get(), &IAudioSource::frameAvailable,
|
||||||
|
[this](const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) {
|
||||||
|
this->av->sendGroupCallAudio(this->groupId, pcm, samples, chans, rate);
|
||||||
|
});
|
||||||
|
|
||||||
|
audioSource = std::move(newSrc);
|
||||||
|
|
||||||
|
audioSrcInvalid = QObject::connect(audioSource.get(), &IAudioSource::invalidated,
|
||||||
|
[this]() { this->onAudioSourceInvalidated(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ToxGroupCall::onAudioSinkInvalidated(ToxPk peerId)
|
void ToxGroupCall::onAudioSinkInvalidated(ToxPk peerId)
|
||||||
{
|
{
|
||||||
removePeer(peerId);
|
removePeer(peerId);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define TOXCALL_H
|
#define TOXCALL_H
|
||||||
|
|
||||||
#include "src/audio/iaudiosink.h"
|
#include "src/audio/iaudiosink.h"
|
||||||
|
#include "src/audio/iaudiosource.h"
|
||||||
#include <src/core/toxpk.h>
|
#include <src/core/toxpk.h>
|
||||||
#include <tox/toxav.h>
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ protected:
|
|||||||
QMetaObject::Connection videoInConn;
|
QMetaObject::Connection videoInConn;
|
||||||
bool videoEnabled{false};
|
bool videoEnabled{false};
|
||||||
bool nullVideoBitrate{false};
|
bool nullVideoBitrate{false};
|
||||||
|
std::unique_ptr<IAudioSource> audioSource = nullptr;
|
||||||
|
QMetaObject::Connection audioSrcInvalid;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ToxFriendCall : public ToxCall
|
class ToxFriendCall : public ToxCall
|
||||||
@ -77,8 +80,7 @@ public:
|
|||||||
TOXAV_FRIEND_CALL_STATE getState() const;
|
TOXAV_FRIEND_CALL_STATE getState() const;
|
||||||
void setState(const TOXAV_FRIEND_CALL_STATE& value);
|
void setState(const TOXAV_FRIEND_CALL_STATE& value);
|
||||||
|
|
||||||
void playAudioBuffer(const int16_t* data, int samples, unsigned channels,
|
void playAudioBuffer(const int16_t* data, int samples, unsigned channels, int sampleRate) const;
|
||||||
int sampleRate) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<QTimer> timeoutTimer;
|
std::unique_ptr<QTimer> timeoutTimer;
|
||||||
@ -106,7 +108,8 @@ public:
|
|||||||
ToxGroupCall& operator=(ToxGroupCall&& other) = delete;
|
ToxGroupCall& operator=(ToxGroupCall&& other) = delete;
|
||||||
void removePeer(ToxPk peerId);
|
void removePeer(ToxPk peerId);
|
||||||
|
|
||||||
void playAudioBuffer(const ToxPk& peer, const int16_t* data, int samples, unsigned channels, int sampleRate);
|
void playAudioBuffer(const ToxPk& peer, const int16_t* data, int samples, unsigned channels,
|
||||||
|
int sampleRate);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addPeer(ToxPk peerId);
|
void addPeer(ToxPk peerId);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "src/audio/audio.h"
|
#include "src/audio/audio.h"
|
||||||
#include "src/audio/iaudiosettings.h"
|
#include "src/audio/iaudiosettings.h"
|
||||||
|
#include "src/audio/iaudiosource.h"
|
||||||
#include "src/core/core.h"
|
#include "src/core/core.h"
|
||||||
#include "src/core/coreav.h"
|
#include "src/core/coreav.h"
|
||||||
#include "src/video/cameradevice.h"
|
#include "src/video/cameradevice.h"
|
||||||
@ -50,7 +51,6 @@ AVForm::AVForm(Audio* audio, CoreAV* coreAV, CameraSource& camera, IAudioSetting
|
|||||||
, coreAV{coreAV}
|
, coreAV{coreAV}
|
||||||
, audioSettings{audioSettings}
|
, audioSettings{audioSettings}
|
||||||
, videoSettings{videoSettings}
|
, videoSettings{videoSettings}
|
||||||
, subscribedToAudioIn(false)
|
|
||||||
, camVideoSurface(nullptr)
|
, camVideoSurface(nullptr)
|
||||||
, camera(camera)
|
, camera(camera)
|
||||||
{
|
{
|
||||||
@ -98,7 +98,6 @@ AVForm::AVForm(Audio* audio, CoreAV* coreAV, CameraSource& camera, IAudioSetting
|
|||||||
audioThresholdSlider->setTracking(false);
|
audioThresholdSlider->setTracking(false);
|
||||||
audioThresholdSlider->installEventFilter(this);
|
audioThresholdSlider->installEventFilter(this);
|
||||||
|
|
||||||
connect(audio, &Audio::volumeAvailable, this, &AVForm::setVolume);
|
|
||||||
volumeDisplay->setMaximum(totalSliderSteps);
|
volumeDisplay->setMaximum(totalSliderSteps);
|
||||||
|
|
||||||
fillAudioQualityComboBox();
|
fillAudioQualityComboBox();
|
||||||
@ -120,13 +119,8 @@ AVForm::~AVForm()
|
|||||||
|
|
||||||
void AVForm::hideEvent(QHideEvent* event)
|
void AVForm::hideEvent(QHideEvent* event)
|
||||||
{
|
{
|
||||||
if (subscribedToAudioIn) {
|
|
||||||
// TODO: This should not be done in show/hide events
|
|
||||||
audio->unsubscribeInput();
|
|
||||||
subscribedToAudioIn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
audioSink.reset();
|
audioSink.reset();
|
||||||
|
audioSrc.reset();
|
||||||
|
|
||||||
if (camVideoSurface) {
|
if (camVideoSurface) {
|
||||||
camVideoSurface->setSource(nullptr);
|
camVideoSurface->setSource(nullptr);
|
||||||
@ -144,10 +138,9 @@ void AVForm::showEvent(QShowEvent* event)
|
|||||||
createVideoSurface();
|
createVideoSurface();
|
||||||
getVideoDevices();
|
getVideoDevices();
|
||||||
|
|
||||||
if (!subscribedToAudioIn) {
|
if (audioSrc == nullptr) {
|
||||||
// TODO: This should not be done in show/hide events
|
audioSrc = audio->makeSource();
|
||||||
audio->subscribeInput();
|
connect(audioSrc.get(), &IAudioSource::volumeAvailable, this, &AVForm::setVolume);
|
||||||
subscribedToAudioIn = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioSink == nullptr) {
|
if (audioSink == nullptr) {
|
||||||
@ -540,17 +533,17 @@ void AVForm::on_inDevCombobox_currentIndexChanged(int deviceIndex)
|
|||||||
const bool inputEnabled = deviceIndex > 0;
|
const bool inputEnabled = deviceIndex > 0;
|
||||||
audioSettings->setAudioInDevEnabled(inputEnabled);
|
audioSettings->setAudioInDevEnabled(inputEnabled);
|
||||||
|
|
||||||
QString deviceName;
|
QString deviceName{};
|
||||||
if (inputEnabled) {
|
if (inputEnabled) {
|
||||||
deviceName = inDevCombobox->itemText(deviceIndex);
|
deviceName = inDevCombobox->itemText(deviceIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString oldName = audioSettings->getInDev();
|
||||||
|
if (oldName != deviceName) {
|
||||||
audioSettings->setInDev(deviceName);
|
audioSettings->setInDev(deviceName);
|
||||||
|
|
||||||
audio->reinitInput(deviceName);
|
audio->reinitInput(deviceName);
|
||||||
subscribedToAudioIn = inputEnabled;
|
audioSrc = audio->makeSource();
|
||||||
if (inputEnabled) {
|
connect(audioSrc.get(), &IAudioSource::volumeAvailable, this, &AVForm::setVolume);
|
||||||
audio->subscribeInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
microphoneSlider->setEnabled(inputEnabled);
|
microphoneSlider->setEnabled(inputEnabled);
|
||||||
|
@ -31,12 +31,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Audio;
|
class Audio;
|
||||||
|
class IAudioSettings;
|
||||||
|
class IAudioSink;
|
||||||
|
class IAudioSource;
|
||||||
class CameraSource;
|
class CameraSource;
|
||||||
class CoreAV;
|
class CoreAV;
|
||||||
class IAudioSettings;
|
|
||||||
class IVideoSettings;
|
class IVideoSettings;
|
||||||
class VideoSurface;
|
class VideoSurface;
|
||||||
class IAudioSink;
|
|
||||||
class AVForm : public GenericForm, private Ui::AVForm
|
class AVForm : public GenericForm, private Ui::AVForm
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -103,6 +104,7 @@ private:
|
|||||||
|
|
||||||
bool subscribedToAudioIn;
|
bool subscribedToAudioIn;
|
||||||
std::unique_ptr<IAudioSink> audioSink = nullptr;
|
std::unique_ptr<IAudioSink> audioSink = nullptr;
|
||||||
|
std::unique_ptr<IAudioSource> audioSrc = nullptr;
|
||||||
VideoSurface* camVideoSurface;
|
VideoSurface* camVideoSurface;
|
||||||
CameraSource& camera;
|
CameraSource& camera;
|
||||||
QVector<QPair<QString, QString>> videoDeviceList;
|
QVector<QPair<QString, QString>> videoDeviceList;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user