mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Cleanup and improve audio input
We now subscribe to an event and wait for frames when capturing audio input, the big avdantage is that we only have to fetch the frames from the hardware once, and we don't need to cache anything. The frames are simply dispatched to the client's callbacks immediately. Also removes some outdated ifdefs that did not apply anymore.
This commit is contained in:
parent
0a1833a74b
commit
ce2f8fd1d5
|
@ -17,14 +17,6 @@
|
|||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Output some extra debug info
|
||||
#define AUDIO_DEBUG 1
|
||||
|
||||
// Fix a 7 years old openal-soft/alsa bug
|
||||
// http://blog.gmane.org/gmane.comp.lib.openal.devel/month=20080501
|
||||
// If set to 1, the capture will be started as long as the device is open
|
||||
#define FIX_SND_PCM_PREPARE_BUG 0
|
||||
|
||||
#include "audio.h"
|
||||
#include "src/core/core.h"
|
||||
#include "src/core/coreav.h"
|
||||
|
@ -117,7 +109,7 @@ Audio& Audio::getInstance()
|
|||
Audio::Audio()
|
||||
: audioThread(new QThread)
|
||||
, alInDev(nullptr)
|
||||
, inputInitialized(false)
|
||||
, inGain{1.f}
|
||||
, inSubscriptions(0)
|
||||
, alOutDev(nullptr)
|
||||
, alOutContext(nullptr)
|
||||
|
@ -129,6 +121,15 @@ Audio::Audio()
|
|||
|
||||
moveToThread(audioThread);
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
filterer.startFilter(AUDIO_SAMPLE_RATE);
|
||||
#endif
|
||||
|
||||
connect(&captureTimer, &QTimer::timeout, this, &Audio::doCapture);
|
||||
captureTimer.setInterval(AUDIO_FRAME_DURATION/2);
|
||||
captureTimer.setSingleShot(false);
|
||||
captureTimer.start();
|
||||
|
||||
if (!audioThread->isRunning())
|
||||
audioThread->start();
|
||||
else
|
||||
|
@ -141,12 +142,13 @@ Audio::~Audio()
|
|||
audioThread->wait();
|
||||
cleanupInput();
|
||||
cleanupOutput();
|
||||
filterer.closeFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the current output volume (between 0 and 1)
|
||||
*/
|
||||
qreal Audio::outputVolume()
|
||||
ALfloat Audio::outputVolume()
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
|
@ -165,38 +167,28 @@ Set the master output volume.
|
|||
|
||||
@param[in] volume the master volume (between 0 and 1)
|
||||
*/
|
||||
void Audio::setOutputVolume(qreal volume)
|
||||
void Audio::setOutputVolume(ALfloat volume)
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
if (volume < 0.0) {
|
||||
volume = 0.0;
|
||||
} else if (volume > 1.0) {
|
||||
volume = 1.0;
|
||||
}
|
||||
volume = std::max(0.f, std::min(volume, 1.f));
|
||||
|
||||
alListenerf(AL_GAIN, volume);
|
||||
CHECK_AL_ERROR;
|
||||
}
|
||||
|
||||
qreal Audio::inputVolume()
|
||||
ALfloat Audio::inputVolume()
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
return gain;
|
||||
return inGain;
|
||||
}
|
||||
|
||||
void Audio::setInputVolume(qreal volume)
|
||||
void Audio::setInputVolume(ALfloat volume)
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
if (volume < 0.0) {
|
||||
volume = 0.0;
|
||||
} else if (volume > 1.0) {
|
||||
volume = 1.0;
|
||||
}
|
||||
|
||||
gain = volume;
|
||||
inGain = std::max(0.f, std::min(volume, 1.f));
|
||||
}
|
||||
|
||||
void Audio::reinitInput(const QString& inDevDesc)
|
||||
|
@ -240,10 +232,11 @@ void Audio::unsubscribeInput()
|
|||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
if (inSubscriptions > 0) {
|
||||
inSubscriptions--;
|
||||
qDebug() << "Unsubscribed from audio input device [" << inSubscriptions << "subscriptions left ]";
|
||||
}
|
||||
if (!inSubscriptions)
|
||||
return;
|
||||
|
||||
inSubscriptions--;
|
||||
qDebug() << "Unsubscribed from audio input device [" << inSubscriptions << "subscriptions left ]";
|
||||
|
||||
if (!inSubscriptions)
|
||||
cleanupInput();
|
||||
|
@ -273,7 +266,6 @@ bool Audio::initInput(QString inDevDescr)
|
|||
{
|
||||
qDebug() << "Opening audio input" << inDevDescr;
|
||||
|
||||
inputInitialized = false;
|
||||
if (inDevDescr == "none")
|
||||
return true;
|
||||
|
||||
|
@ -304,7 +296,6 @@ bool Audio::initInput(QString inDevDescr)
|
|||
qDebug() << "Opened audio input" << inDevDescr;
|
||||
alcCaptureStart(alInDev);
|
||||
|
||||
inputInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -416,36 +407,7 @@ void Audio::playGroupAudioQueued(void*,int group, int peer, const int16_t* data,
|
|||
emit static_cast<Core*>(core)->groupPeerAudioPlaying(group, peer);
|
||||
}
|
||||
|
||||
/**
|
||||
Must be called from the audio thread, plays a group call's received audio
|
||||
*/
|
||||
void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate)
|
||||
{
|
||||
assert(QThread::currentThread() == audioThread);
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
if (!CoreAV::groupCalls.contains(group))
|
||||
return;
|
||||
|
||||
ToxGroupCall& call = CoreAV::groupCalls[group];
|
||||
|
||||
if (call.inactive || call.muteVol)
|
||||
return;
|
||||
|
||||
qreal volume = 0.;
|
||||
int bufsize = samples * 2 * channels;
|
||||
for (int i = 0; i < bufsize; ++i)
|
||||
volume += abs(data[i]);
|
||||
|
||||
emit groupAudioPlayed(group, peer, volume / bufsize);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
playAudioBuffer(call.alSource, data, samples, channels, sample_rate);
|
||||
}
|
||||
|
||||
void Audio::playAudioBuffer(quint32 alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||
{
|
||||
assert(channels == 1 || channels == 2);
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
@ -492,21 +454,15 @@ Close active audio input device.
|
|||
*/
|
||||
void Audio::cleanupInput()
|
||||
{
|
||||
inputInitialized = false;
|
||||
if (!alInDev)
|
||||
return;
|
||||
|
||||
if (alInDev)
|
||||
{
|
||||
#if (!FIX_SND_PCM_PREPARE_BUG)
|
||||
qDebug() << "stopping audio capture";
|
||||
alcCaptureStop(alInDev);
|
||||
#endif
|
||||
|
||||
qDebug() << "Closing audio input";
|
||||
if (alcCaptureCloseDevice(alInDev) == ALC_TRUE)
|
||||
alInDev = nullptr;
|
||||
else
|
||||
qWarning() << "Failed to close input";
|
||||
}
|
||||
qDebug() << "Closing audio input";
|
||||
alcCaptureStop(alInDev);
|
||||
if (alcCaptureCloseDevice(alInDev) == ALC_TRUE)
|
||||
alInDev = nullptr;
|
||||
else
|
||||
qWarning() << "Failed to close input";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -537,13 +493,43 @@ void Audio::cleanupOutput()
|
|||
}
|
||||
}
|
||||
|
||||
void Audio::doCapture()
|
||||
{
|
||||
QMutexLocker lock(&audioLock);
|
||||
|
||||
if (!alInDev || !inSubscriptions)
|
||||
return;
|
||||
|
||||
ALint curSamples = 0;
|
||||
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(curSamples), &curSamples);
|
||||
if (curSamples < AUDIO_FRAME_SAMPLE_COUNT)
|
||||
return;
|
||||
|
||||
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS];
|
||||
alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT);
|
||||
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
#ifdef ALC_LOOPBACK_CAPTURE_SAMPLES
|
||||
// compatibility with older versions of OpenAL
|
||||
getEchoesToFilter(filterer, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
#endif
|
||||
filterer.filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i)
|
||||
buf[i] *= inGain;
|
||||
|
||||
emit frameAvailable(buf, AUDIO_FRAME_SAMPLE_COUNT, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if the input device is open and suscribed to
|
||||
*/
|
||||
bool Audio::isInputReady()
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
return alInDev && inputInitialized;
|
||||
return alInDev && inSubscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -634,43 +620,9 @@ void Audio::stopLoop()
|
|||
alSourceStop(alMainSource);
|
||||
}
|
||||
|
||||
/**
|
||||
Does nothing and return false on failure
|
||||
*/
|
||||
bool Audio::tryCaptureSamples(int16_t* buf, int samples)
|
||||
{
|
||||
QMutexLocker lock(&audioLock);
|
||||
|
||||
if (!(alInDev && inputInitialized))
|
||||
return false;
|
||||
|
||||
ALint curSamples = 0;
|
||||
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(curSamples), &curSamples);
|
||||
if (curSamples < samples)
|
||||
return false;
|
||||
|
||||
alcCaptureSamples(alInDev, buf, samples);
|
||||
|
||||
for (size_t i = 0; i < samples * AUDIO_CHANNELS; ++i)
|
||||
{
|
||||
int sample = buf[i];
|
||||
|
||||
if (sample < std::numeric_limits<int16_t>::min())
|
||||
sample = std::numeric_limits<int16_t>::min();
|
||||
else if (sample > std::numeric_limits<int16_t>::max())
|
||||
sample = std::numeric_limits<int16_t>::max();
|
||||
|
||||
buf[i] = sample;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES)
|
||||
void Audio::getEchoesToFilter(AudioFilterer* filterer, int samples)
|
||||
{
|
||||
QMutexLocker locker(&audioLock);
|
||||
|
||||
ALint samples;
|
||||
alcGetIntegerv(&alOutDev, ALC_LOOPBACK_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
if (samples >= samples)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
|
@ -41,29 +42,28 @@
|
|||
#include <AL/alext.h>
|
||||
#endif
|
||||
|
||||
class AudioFilterer;
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
#include "audiofilterer.h"
|
||||
#endif
|
||||
|
||||
// Public default audio settings
|
||||
static constexpr uint32_t AUDIO_SAMPLE_RATE = 48000; ///< The next best Opus would take is 24k
|
||||
static constexpr uint32_t AUDIO_FRAME_DURATION = 20; ///< In milliseconds
|
||||
static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE/1000;
|
||||
static constexpr ALint AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE/1000;
|
||||
static constexpr uint32_t AUDIO_CHANNELS = 2; ///< Ideally, we'd auto-detect, but that's a sane default
|
||||
|
||||
class Audio : public QObject
|
||||
{
|
||||
using SID = unsigned int;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Audio& getInstance();
|
||||
|
||||
public:
|
||||
qreal outputVolume();
|
||||
void setOutputVolume(qreal volume);
|
||||
ALfloat outputVolume();
|
||||
void setOutputVolume(ALfloat volume);
|
||||
|
||||
qreal inputVolume();
|
||||
void setInputVolume(qreal volume);
|
||||
ALfloat inputVolume();
|
||||
void setInputVolume(ALfloat volume);
|
||||
|
||||
void reinitInput(const QString& inDevDesc);
|
||||
bool reinitOutput(const QString& outDevDesc);
|
||||
|
@ -75,31 +75,25 @@ public:
|
|||
static const char* inDeviceNames();
|
||||
void subscribeOutput(ALuint& sid);
|
||||
void unsubscribeOutput(ALuint& sid);
|
||||
void subscribeInput();
|
||||
void unsubscribeInput();
|
||||
|
||||
void startLoop();
|
||||
void stopLoop();
|
||||
void playMono16Sound(const QByteArray& data);
|
||||
void playMono16Sound(const QString& path);
|
||||
bool tryCaptureSamples(int16_t *buf, int samples);
|
||||
|
||||
void playAudioBuffer(quint32 alSource, const int16_t *data, int samples,
|
||||
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
|
||||
unsigned channels, int sampleRate);
|
||||
|
||||
static void playGroupAudioQueued(void *, int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate, void*);
|
||||
|
||||
#if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES)
|
||||
void getEchoesToFilter(AudioFilterer* filter, int samples);
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
void subscribeInput();
|
||||
void unsubscribeInput();
|
||||
void playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate);
|
||||
|
||||
signals:
|
||||
void groupAudioPlayed(int group, int peer, unsigned short volume);
|
||||
/// When there are input subscribers, we regularly emit captured audio frames with this signal
|
||||
/// Always connect with a blocking queued connection or a lambda, or the behavior is undefined
|
||||
void frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate);
|
||||
|
||||
private:
|
||||
Audio();
|
||||
|
@ -111,22 +105,30 @@ private:
|
|||
bool initOutput(QString outDevDescr);
|
||||
void cleanupInput();
|
||||
void cleanupOutput();
|
||||
/// Called on the captureTimer events to capture audio
|
||||
void doCapture();
|
||||
#if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES)
|
||||
void getEchoesToFilter(AudioFilterer* filter, int samples);
|
||||
#endif
|
||||
|
||||
private:
|
||||
QThread* audioThread;
|
||||
QMutex audioLock;
|
||||
|
||||
ALCdevice* alInDev;
|
||||
ALfloat gain;
|
||||
bool inputInitialized;
|
||||
ALfloat inGain;
|
||||
quint32 inSubscriptions;
|
||||
QTimer captureTimer;
|
||||
|
||||
ALCdevice* alOutDev;
|
||||
ALCcontext* alOutContext;
|
||||
ALuint alMainSource;
|
||||
bool outputInitialized;
|
||||
|
||||
QList<ALuint> outSources;
|
||||
QList<ALuint> outSources;
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
AudioFilterer filterer;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // AUDIO_H
|
||||
|
|
|
@ -238,7 +238,7 @@ void CoreAV::timeoutCall(uint32_t friendNum)
|
|||
emit avEnd(friendNum);
|
||||
}
|
||||
|
||||
bool CoreAV::sendCallAudio(uint32_t callId)
|
||||
bool CoreAV::sendCallAudio(uint32_t callId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
||||
{
|
||||
if (!calls.contains(callId))
|
||||
return false;
|
||||
|
@ -246,58 +246,54 @@ bool CoreAV::sendCallAudio(uint32_t callId)
|
|||
ToxFriendCall& call = calls[callId];
|
||||
|
||||
if (call.muteMic || call.inactive
|
||||
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A)
|
||||
|| !Audio::getInstance().isInputReady())
|
||||
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
||||
if (Audio::getInstance().tryCaptureSamples(buf, AUDIO_FRAME_SAMPLE_COUNT))
|
||||
{
|
||||
#if 0
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
if (!call.filterer)
|
||||
{
|
||||
if (!call.filterer)
|
||||
{
|
||||
call.filterer = new AudioFilterer();
|
||||
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
call.filterer = new AudioFilterer();
|
||||
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
#ifdef ALC_LOOPBACK_CAPTURE_SAMPLES
|
||||
// compatibility with older versions of OpenAL
|
||||
Audio::getInstance().getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
// compatibility with older versions of OpenAL
|
||||
Audio::getInstance().getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
#endif
|
||||
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
}
|
||||
else if (call.filterer)
|
||||
{
|
||||
delete call.filterer;
|
||||
call.filterer = nullptr;
|
||||
}
|
||||
#endif
|
||||
call.filterer->filterAudio(buf, AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS);
|
||||
}
|
||||
else if (call.filterer)
|
||||
{
|
||||
delete call.filterer;
|
||||
call.filterer = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case
|
||||
TOXAV_ERR_SEND_FRAME err;
|
||||
int retries = 0;
|
||||
do {
|
||||
if (!toxav_audio_send_frame(toxav, callId, buf, AUDIO_FRAME_SAMPLE_COUNT,
|
||||
AUDIO_CHANNELS, AUDIO_SAMPLE_RATE, &err))
|
||||
// TOXAV_ERR_SEND_FRAME_SYNC means toxav failed to lock, retry 5 times in this case
|
||||
TOXAV_ERR_SEND_FRAME err;
|
||||
int retries = 0;
|
||||
do {
|
||||
if (!toxav_audio_send_frame(toxav, callId, pcm, samples, chans, rate, &err))
|
||||
{
|
||||
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
|
||||
{
|
||||
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
|
||||
{
|
||||
retries++;
|
||||
QThread::usleep(500);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "toxav_audio_send_frame error: "<<err;
|
||||
}
|
||||
retries++;
|
||||
QThread::usleep(500);
|
||||
}
|
||||
} while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
|
||||
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
|
||||
qDebug() << "toxav_audio_send_frame error: Lock busy, dropping frame";
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "toxav_audio_send_frame error: "<<err;
|
||||
}
|
||||
}
|
||||
} while (err == TOXAV_ERR_SEND_FRAME_SYNC && retries < 5);
|
||||
if (err == TOXAV_ERR_SEND_FRAME_SYNC)
|
||||
qDebug() << "toxav_audio_send_frame error: Lock busy, dropping frame";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -394,7 +390,7 @@ void CoreAV::leaveGroupCall(int groupId)
|
|||
groupCalls.remove(groupId);
|
||||
}
|
||||
|
||||
bool CoreAV::sendGroupCallAudio(int groupId)
|
||||
bool CoreAV::sendGroupCallAudio(int groupId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
||||
{
|
||||
if (!groupCalls.contains(groupId))
|
||||
return false;
|
||||
|
@ -404,33 +400,9 @@ bool CoreAV::sendGroupCallAudio(int groupId)
|
|||
if (call.inactive || call.muteMic || !Audio::getInstance().isInputReady())
|
||||
return true;
|
||||
|
||||
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
|
||||
if (Audio::getInstance().tryCaptureSamples(buf, AUDIO_FRAME_SAMPLE_COUNT))
|
||||
{
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
if (!call.filterer)
|
||||
{
|
||||
call.filterer = new AudioFilterer();
|
||||
call.filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
#ifdef ALC_LOOPBACK_CAPTURE_SAMPLES
|
||||
Audio::getInstance().getEchoesToFilter(call.filterer, AUDIO_FRAME_SAMPLE_COUNT);
|
||||
#endif
|
||||
}
|
||||
else if (call.filterer)
|
||||
{
|
||||
delete call.filterer;
|
||||
call.filterer = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, buf, AUDIO_FRAME_SAMPLE_COUNT,
|
||||
AUDIO_CHANNELS, AUDIO_SAMPLE_RATE) != 0)
|
||||
qDebug() << "toxav_group_send_audio error";
|
||||
}
|
||||
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, pcm, samples, chans, rate) != 0)
|
||||
qDebug() << "toxav_group_send_audio error";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -52,9 +52,10 @@ public:
|
|||
|
||||
bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
||||
bool isCallVideoEnabled(uint32_t friendNum);
|
||||
bool sendCallAudio(uint32_t friendNum); ///< Returns false only on error, but not if there's nothing to send
|
||||
/// Returns false only on error, but not if there's nothing to send
|
||||
bool sendCallAudio(uint32_t friendNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate);
|
||||
void sendCallVideo(uint32_t friendNum, std::shared_ptr<VideoFrame> frame);
|
||||
bool sendGroupCallAudio(int groupNum);
|
||||
bool sendGroupCallAudio(int groupNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate);
|
||||
|
||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||
void invalidateCallSources(); ///< Forces to regenerate each call's audio sources
|
||||
|
|
|
@ -14,71 +14,40 @@
|
|||
using namespace std;
|
||||
|
||||
ToxCall::ToxCall(uint32_t CallId)
|
||||
: sendAudioTimer{new QTimer}, callId{CallId},
|
||||
inactive{true}, muteMic{false}, muteVol{false}, alSource{0}
|
||||
: callId{CallId}, alSource{0},
|
||||
inactive{true}, muteMic{false}, muteVol{false}
|
||||
{
|
||||
sendAudioTimer->setInterval(5);
|
||||
sendAudioTimer->setSingleShot(true);
|
||||
|
||||
Audio& audio = Audio::getInstance();
|
||||
audio.subscribeInput();
|
||||
audio.subscribeOutput(alSource);
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
filterer = new AudioFilterer();
|
||||
filterer->startFilter(AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
filterer = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ToxCall::ToxCall(ToxCall&& other) noexcept
|
||||
: sendAudioTimer{other.sendAudioTimer}, callId{other.callId},
|
||||
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol},
|
||||
alSource{other.alSource}
|
||||
: audioInConn{other.audioInConn}, callId{other.callId}, alSource{other.alSource},
|
||||
inactive{other.inactive}, muteMic{other.muteMic}, muteVol{other.muteVol}
|
||||
{
|
||||
other.sendAudioTimer = nullptr;
|
||||
other.audioInConn = QMetaObject::Connection();
|
||||
other.callId = numeric_limits<decltype(callId)>::max();
|
||||
other.alSource = 0;
|
||||
|
||||
// required -> ownership of audio input is moved to new instance
|
||||
Audio& audio = Audio::getInstance();
|
||||
audio.subscribeInput();
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
filterer = other.filterer;
|
||||
other.filterer = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
ToxCall::~ToxCall()
|
||||
{
|
||||
Audio& audio = Audio::getInstance();
|
||||
|
||||
if (sendAudioTimer)
|
||||
{
|
||||
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
||||
sendAudioTimer->stop();
|
||||
}
|
||||
|
||||
QObject::disconnect(audioInConn);
|
||||
audio.unsubscribeInput();
|
||||
audio.unsubscribeOutput(alSource);
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (filterer)
|
||||
delete filterer;
|
||||
#endif
|
||||
}
|
||||
|
||||
const ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
|
||||
{
|
||||
sendAudioTimer = other.sendAudioTimer;
|
||||
other.sendAudioTimer = nullptr;
|
||||
audioInConn = other.audioInConn;
|
||||
other.audioInConn = QMetaObject::Connection();
|
||||
callId = other.callId;
|
||||
other.callId = numeric_limits<decltype(callId)>::max();
|
||||
inactive = other.inactive;
|
||||
|
@ -91,11 +60,6 @@ const ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
|
|||
Audio& audio = Audio::getInstance();
|
||||
audio.subscribeInput();
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
filterer = other.filterer;
|
||||
other.filterer = nullptr;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -132,14 +96,11 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
|||
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)},
|
||||
av{&av}, timeoutTimer{nullptr}
|
||||
{
|
||||
auto audioTimerCopy = sendAudioTimer; // this might change after a move, but not sendAudioTimer
|
||||
QObject::connect(sendAudioTimer, &QTimer::timeout, [FriendNum,&av,audioTimerCopy]()
|
||||
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
||||
[&av,FriendNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
||||
{
|
||||
// If sendCallAudio returns false, there was a serious error and we might as well stop the timer
|
||||
if (av.sendCallAudio(FriendNum))
|
||||
audioTimerCopy->start();
|
||||
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
|
||||
});
|
||||
sendAudioTimer->start();
|
||||
|
||||
if (videoEnabled)
|
||||
{
|
||||
|
@ -205,14 +166,11 @@ ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV &av)
|
|||
static_assert(numeric_limits<decltype(callId)>::max() >= numeric_limits<decltype(GroupNum)>::max(),
|
||||
"The callId must be able to represent any group number, change its type if needed");
|
||||
|
||||
auto audioTimerCopy = sendAudioTimer; // this might change after a move, but not sendAudioTimer
|
||||
QObject::connect(sendAudioTimer, &QTimer::timeout, [GroupNum,&av,audioTimerCopy]()
|
||||
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
|
||||
[&av,GroupNum](const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate)
|
||||
{
|
||||
// If sendGroupCallAudio returns false, there was a serious error and we might as well stop the timer
|
||||
if (av.sendGroupCallAudio(GroupNum))
|
||||
audioTimerCopy->start();
|
||||
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
|
||||
});
|
||||
sendAudioTimer->start();
|
||||
}
|
||||
|
||||
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <QtGlobal>
|
||||
#include <QMetaObject>
|
||||
|
||||
#include "src/core/indexedlist.h"
|
||||
|
||||
|
@ -28,18 +29,14 @@ public:
|
|||
const ToxCall& operator=(ToxCall&& other) noexcept;
|
||||
|
||||
protected:
|
||||
QTimer* sendAudioTimer;
|
||||
QMetaObject::Connection audioInConn;
|
||||
|
||||
public:
|
||||
uint32_t callId; ///< Could be a friendNum or groupNum, must uniquely identify the call
|
||||
uint32_t callId; ///< Could be a friendNum or groupNum, must uniquely identify the call. Do not modify!
|
||||
quint32 alSource;
|
||||
bool inactive; ///< True while we're not participating. (stopped group call, ringing but hasn't started yet, ...)
|
||||
bool muteMic;
|
||||
bool muteVol;
|
||||
quint32 alSource;
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
AudioFilterer* filterer;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ToxFriendCall : public ToxCall
|
||||
|
|
Loading…
Reference in New Issue
Block a user