1
0
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:
tux3 2016-01-21 20:17:18 +01:00
parent 0a1833a74b
commit ce2f8fd1d5
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
6 changed files with 149 additions and 267 deletions

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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