mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
introduce a non-blocking AudioPlayer
Also moved private stuff to private class.
This commit is contained in:
parent
1fb9bce78c
commit
b44ef6c596
|
@ -32,9 +32,10 @@
|
||||||
#include "src/core/coreav.h"
|
#include "src/core/coreav.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QThread>
|
|
||||||
#include <QMutexLocker>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -55,17 +56,88 @@ public:
|
||||||
, alContext(nullptr)
|
, alContext(nullptr)
|
||||||
, inputVolume(1.f)
|
, inputVolume(1.f)
|
||||||
, outputVolume(1.f)
|
, outputVolume(1.f)
|
||||||
|
, audioThread(new QThread())
|
||||||
|
, inputInitialized(false)
|
||||||
|
, outputInitialized(false)
|
||||||
{
|
{
|
||||||
|
audioThread->setObjectName("qTox Audio");
|
||||||
|
QObject::connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initInput(const QString& inDevDescr);
|
||||||
|
bool initOutput(const QString& outDevDescr);
|
||||||
|
void cleanupInput();
|
||||||
|
void cleanupOutput();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ALCdevice* alInDev;
|
ALCdevice* alInDev;
|
||||||
ALCdevice* alOutDev;
|
ALCdevice* alOutDev;
|
||||||
ALCcontext* alContext;
|
ALCcontext* alContext;
|
||||||
|
|
||||||
|
// predefined sources
|
||||||
ALuint alMainSource;
|
ALuint alMainSource;
|
||||||
|
|
||||||
qreal inputVolume;
|
qreal inputVolume;
|
||||||
qreal outputVolume;
|
qreal outputVolume;
|
||||||
|
|
||||||
|
QThread* audioThread;
|
||||||
|
QMutex audioLock;
|
||||||
|
Audio::PtrList inputSubscriptions;
|
||||||
|
Audio::PtrList outputSubscriptions;
|
||||||
|
bool inputInitialized;
|
||||||
|
bool outputInitialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
@class AudioPlayer
|
||||||
|
|
||||||
|
@brief Non-blocking audio player.
|
||||||
|
|
||||||
|
The audio data is played from start to finish (no streaming).
|
||||||
|
*/
|
||||||
|
class AudioPlayer : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioPlayer(AudioPrivate* _q, const QByteArray& data)
|
||||||
|
: q(_q)
|
||||||
|
{
|
||||||
|
assert(q);
|
||||||
|
alGenBuffers(1, &buffer);
|
||||||
|
alBufferData(buffer, AL_FORMAT_MONO16, data.constData(), data.size(), 44100);
|
||||||
|
alSourcei(q->alMainSource, AL_BUFFER, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioPlayer()
|
||||||
|
{
|
||||||
|
QMutexLocker(&q->audioLock);
|
||||||
|
|
||||||
|
alDeleteBuffers(1, &buffer);
|
||||||
|
if (q->outputSubscriptions.isEmpty())
|
||||||
|
q->cleanupOutput();
|
||||||
|
else
|
||||||
|
qDebug("Audio output not closed -> there are pending subscriptions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
alSourceRewind(q->alMainSource);
|
||||||
|
alSourcePlay(q->alMainSource);
|
||||||
|
|
||||||
|
QMutexLocker locker(&playLock);
|
||||||
|
ALint state = AL_PLAYING;
|
||||||
|
while (state == AL_PLAYING) {
|
||||||
|
alGetSourcei(q->alMainSource, AL_SOURCE_STATE, &state);
|
||||||
|
waitPlaying.wait(&playLock, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
QMutex playLock;
|
||||||
|
QWaitCondition waitPlaying;
|
||||||
|
ALuint buffer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AudioPrivate* q;
|
||||||
};
|
};
|
||||||
|
|
||||||
Audio* Audio::instance{nullptr};
|
Audio* Audio::instance{nullptr};
|
||||||
|
@ -85,22 +157,15 @@ Audio& Audio::getInstance()
|
||||||
|
|
||||||
Audio::Audio()
|
Audio::Audio()
|
||||||
: d(new AudioPrivate)
|
: d(new AudioPrivate)
|
||||||
, audioThread(new QThread())
|
|
||||||
, mInputInitialized(false)
|
|
||||||
, mOutputInitialized(false)
|
|
||||||
{
|
{
|
||||||
audioThread->setObjectName("qTox Audio");
|
|
||||||
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio::~Audio()
|
Audio::~Audio()
|
||||||
{
|
{
|
||||||
audioThread->exit();
|
d->audioThread->exit();
|
||||||
audioThread->wait();
|
d->audioThread->wait();
|
||||||
if (audioThread->isRunning())
|
d->cleanupInput();
|
||||||
audioThread->terminate();
|
d->cleanupOutput();
|
||||||
cleanupInput();
|
|
||||||
cleanupOutput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,10 +173,10 @@ Start the audio thread for capture and playback.
|
||||||
*/
|
*/
|
||||||
void Audio::startAudioThread()
|
void Audio::startAudioThread()
|
||||||
{
|
{
|
||||||
moveToThread(audioThread);
|
moveToThread(d->audioThread);
|
||||||
|
|
||||||
if (!audioThread->isRunning())
|
if (!d->audioThread->isRunning())
|
||||||
audioThread->start();
|
d->audioThread->start();
|
||||||
else
|
else
|
||||||
qWarning("Audio thread already started -> ignored.");
|
qWarning("Audio thread already started -> ignored.");
|
||||||
|
|
||||||
|
@ -122,7 +187,7 @@ Returns the current output volume, between 0 and 1
|
||||||
*/
|
*/
|
||||||
qreal Audio::outputVolume()
|
qreal Audio::outputVolume()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
return d->outputVolume;
|
return d->outputVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +196,7 @@ The volume must be between 0 and 1
|
||||||
*/
|
*/
|
||||||
void Audio::setOutputVolume(qreal volume)
|
void Audio::setOutputVolume(qreal volume)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
d->outputVolume = volume;
|
d->outputVolume = volume;
|
||||||
alSourcef(d->alMainSource, AL_GAIN, volume);
|
alSourcef(d->alMainSource, AL_GAIN, volume);
|
||||||
|
@ -149,17 +214,31 @@ void Audio::setOutputVolume(qreal volume)
|
||||||
|
|
||||||
qreal Audio::inputVolume()
|
qreal Audio::inputVolume()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
return d->inputVolume;
|
return d->inputVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::setInputVolume(qreal volume)
|
void Audio::setInputVolume(qreal volume)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
d->inputVolume = volume;
|
d->inputVolume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Audio::reinitInput(const QString& inDevDesc)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
d->cleanupInput();
|
||||||
|
d->initInput(inDevDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Audio::reinitOutput(const QString& outDevDesc)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
d->cleanupOutput();
|
||||||
|
return d->initOutput(outDevDesc);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Subscribe to capture sound from the opened input device.
|
@brief Subscribe to capture sound from the opened input device.
|
||||||
|
|
||||||
|
@ -167,14 +246,14 @@ If the input device is not open, it will be opened before capturing.
|
||||||
*/
|
*/
|
||||||
void Audio::subscribeInput(const void* inListener)
|
void Audio::subscribeInput(const void* inListener)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (!d->alInDev)
|
if (!d->alInDev)
|
||||||
initInput(Settings::getInstance().getInDev());
|
d->initInput(Settings::getInstance().getInDev());
|
||||||
|
|
||||||
if (!inputSubscriptions.contains(inListener)) {
|
if (!d->inputSubscriptions.contains(inListener)) {
|
||||||
inputSubscriptions << inListener;
|
d->inputSubscriptions << inListener;
|
||||||
qDebug() << "Subscribed to audio input device [" << inputSubscriptions.size() << "subscriptions ]";
|
qDebug() << "Subscribed to audio input device [" << d->inputSubscriptions.size() << "subscriptions ]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,54 +264,54 @@ If the input device has no more subscriptions, it will be closed.
|
||||||
*/
|
*/
|
||||||
void Audio::unsubscribeInput(const void* inListener)
|
void Audio::unsubscribeInput(const void* inListener)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (inListener && inputSubscriptions.size())
|
if (inListener && d->inputSubscriptions.size())
|
||||||
{
|
{
|
||||||
inputSubscriptions.removeOne(inListener);
|
d->inputSubscriptions.removeOne(inListener);
|
||||||
qDebug() << "Unsubscribed from audio input device [" << inputSubscriptions.size() << "subscriptions left ]";
|
qDebug() << "Unsubscribed from audio input device [" << d->inputSubscriptions.size() << "subscriptions left ]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputSubscriptions.isEmpty())
|
if (d->inputSubscriptions.isEmpty())
|
||||||
cleanupInput();
|
d->cleanupInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::subscribeOutput(const void* outListener)
|
void Audio::subscribeOutput(const void* outListener)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (!d->alOutDev)
|
if (!d->alOutDev)
|
||||||
initOutput(Settings::getInstance().getOutDev());
|
d->initOutput(Settings::getInstance().getOutDev());
|
||||||
|
|
||||||
if (!outputSubscriptions.contains(outListener)) {
|
if (!d->outputSubscriptions.contains(outListener)) {
|
||||||
outputSubscriptions << outListener;
|
d->outputSubscriptions << outListener;
|
||||||
qDebug() << "Subscribed to audio output device [" << outputSubscriptions.size() << "subscriptions ]";
|
qDebug() << "Subscribed to audio output device [" << d->outputSubscriptions.size() << "subscriptions ]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::unsubscribeOutput(const void* outListener)
|
void Audio::unsubscribeOutput(const void* outListener)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (outListener && outputSubscriptions.size())
|
if (outListener && d->outputSubscriptions.size())
|
||||||
{
|
{
|
||||||
outputSubscriptions.removeOne(outListener);
|
d->outputSubscriptions.removeOne(outListener);
|
||||||
qDebug() << "Unsubscribed from audio output device [" << outputSubscriptions.size() << " subscriptions left ]";
|
qDebug() << "Unsubscribed from audio output device [" << d->outputSubscriptions.size() << " subscriptions left ]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputSubscriptions.isEmpty())
|
if (d->outputSubscriptions.isEmpty())
|
||||||
cleanupOutput();
|
d->cleanupOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::initInput(const QString& inDevDescr)
|
void AudioPrivate::initInput(const QString& inDevDescr)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening audio input" << inDevDescr;
|
qDebug() << "Opening audio input" << inDevDescr;
|
||||||
|
|
||||||
mInputInitialized = false;
|
inputInitialized = false;
|
||||||
if (inDevDescr == "none")
|
if (inDevDescr == "none")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!d->alInDev);
|
assert(!alInDev);
|
||||||
|
|
||||||
/// TODO: Try to actually detect if our audio source is stereo
|
/// TODO: Try to actually detect if our audio source is stereo
|
||||||
int stereoFlag = AUDIO_CHANNELS == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
int stereoFlag = AUDIO_CHANNELS == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||||
|
@ -260,10 +339,10 @@ void Audio::initInput(const QString& inDevDescr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
d->alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),
|
alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),
|
||||||
sampleRate, stereoFlag, bufSize);
|
sampleRate, stereoFlag, bufSize);
|
||||||
|
|
||||||
if (d->alInDev)
|
if (alInDev)
|
||||||
qDebug() << "Opened audio input" << inDevDescr;
|
qDebug() << "Opened audio input" << inDevDescr;
|
||||||
else
|
else
|
||||||
qWarning() << "Cannot open input audio device" << inDevDescr;
|
qWarning() << "Cannot open input audio device" << inDevDescr;
|
||||||
|
@ -273,9 +352,9 @@ void Audio::initInput(const QString& inDevDescr)
|
||||||
core->getAv()->resetCallSources(); // Force to regen each group call's sources
|
core->getAv()->resetCallSources(); // Force to regen each group call's sources
|
||||||
|
|
||||||
// Restart the capture if necessary
|
// Restart the capture if necessary
|
||||||
if (d->alInDev)
|
if (alInDev)
|
||||||
{
|
{
|
||||||
alcCaptureStart(d->alInDev);
|
alcCaptureStart(alInDev);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -284,7 +363,7 @@ void Audio::initInput(const QString& inDevDescr)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
mInputInitialized = true;
|
inputInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -292,15 +371,15 @@ void Audio::initInput(const QString& inDevDescr)
|
||||||
|
|
||||||
Open an audio output device
|
Open an audio output device
|
||||||
*/
|
*/
|
||||||
bool Audio::initOutput(const QString& outDevDescr)
|
bool AudioPrivate::initOutput(const QString& outDevDescr)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening audio output" << outDevDescr;
|
qDebug() << "Opening audio output" << outDevDescr;
|
||||||
|
|
||||||
mOutputInitialized = false;
|
outputInitialized = false;
|
||||||
if (outDevDescr == "none")
|
if (outDevDescr == "none")
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
assert(!d->alOutDev);
|
assert(!alOutDev);
|
||||||
|
|
||||||
if (outDevDescr.isEmpty())
|
if (outDevDescr.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -327,15 +406,15 @@ bool Audio::initOutput(const QString& outDevDescr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
d->alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
|
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
|
||||||
|
|
||||||
if (d->alOutDev)
|
if (alOutDev)
|
||||||
{
|
{
|
||||||
d->alContext = alcCreateContext(d->alOutDev, nullptr);
|
alContext = alcCreateContext(alOutDev, nullptr);
|
||||||
if (alcMakeContextCurrent(d->alContext))
|
if (alcMakeContextCurrent(alContext))
|
||||||
{
|
{
|
||||||
alGenSources(1, &d->alMainSource);
|
alGenSources(1, &alMainSource);
|
||||||
alSourcef(d->alMainSource, AL_GAIN, d->outputVolume);
|
alSourcef(alMainSource, AL_GAIN, outputVolume);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -348,7 +427,7 @@ bool Audio::initOutput(const QString& outDevDescr)
|
||||||
if (core)
|
if (core)
|
||||||
core->getAv()->resetCallSources(); // Force to regen each group call's sources
|
core->getAv()->resetCallSources(); // Force to regen each group call's sources
|
||||||
|
|
||||||
mOutputInitialized = true;
|
outputInitialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,35 +440,16 @@ Play a 44100Hz mono 16bit PCM sound
|
||||||
*/
|
*/
|
||||||
void Audio::playMono16Sound(const QByteArray& data)
|
void Audio::playMono16Sound(const QByteArray& data)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (!d->alOutDev)
|
if (!d->alOutDev)
|
||||||
initOutput(Settings::getInstance().getOutDev());
|
d->initOutput(Settings::getInstance().getOutDev());
|
||||||
|
|
||||||
alSourcei(d->alMainSource, AL_LOOPING, false);
|
|
||||||
alSourcef(d->alMainSource, AL_GAIN, d->outputVolume);
|
alSourcef(d->alMainSource, AL_GAIN, d->outputVolume);
|
||||||
alSource3f(d->alMainSource, AL_POSITION, 0, 0, 0);
|
|
||||||
alSource3f(d->alMainSource, AL_VELOCITY, 0, 0, 0);
|
|
||||||
|
|
||||||
ALuint buffer;
|
AudioPlayer *player = new AudioPlayer(d, data);
|
||||||
alGenBuffers(1, &buffer);
|
connect(player, &AudioPlayer::finished, player, &AudioPlayer::deleteLater);
|
||||||
alBufferData(buffer, AL_FORMAT_MONO16, data.constData(), data.size(), 44100);
|
player->start();
|
||||||
|
|
||||||
alSourcei(d->alMainSource, AL_BUFFER, buffer);
|
|
||||||
|
|
||||||
alSourceRewind(d->alMainSource);
|
|
||||||
alSourcePlay(d->alMainSource);
|
|
||||||
|
|
||||||
ALint state;
|
|
||||||
alGetSourcei(d->alMainSource, AL_SOURCE_STATE, &state);
|
|
||||||
while (state == AL_PLAYING) {
|
|
||||||
alGetSourcei(d->alMainSource, AL_SOURCE_STATE, &state);
|
|
||||||
}
|
|
||||||
|
|
||||||
alDeleteBuffers(1, &buffer);
|
|
||||||
|
|
||||||
if (outputSubscriptions.isEmpty())
|
|
||||||
cleanupOutput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -422,8 +482,8 @@ 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,
|
void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||||
unsigned samples, uint8_t channels, unsigned sample_rate)
|
unsigned samples, uint8_t channels, unsigned sample_rate)
|
||||||
{
|
{
|
||||||
assert(QThread::currentThread() == audioThread);
|
assert(QThread::currentThread() == d->audioThread);
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
if (!CoreAV::groupCalls.contains(group))
|
if (!CoreAV::groupCalls.contains(group))
|
||||||
return;
|
return;
|
||||||
|
@ -452,7 +512,7 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||||
void Audio::playAudioBuffer(quint32 alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
void Audio::playAudioBuffer(quint32 alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||||
{
|
{
|
||||||
assert(channels == 1 || channels == 2);
|
assert(channels == 1 || channels == 2);
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
|
|
||||||
ALuint bufid;
|
ALuint bufid;
|
||||||
ALint processed = 0, queued = 16;
|
ALint processed = 0, queued = 16;
|
||||||
|
@ -492,20 +552,20 @@ void Audio::playAudioBuffer(quint32 alSource, const int16_t *data, int samples,
|
||||||
|
|
||||||
Close active audio input device.
|
Close active audio input device.
|
||||||
*/
|
*/
|
||||||
void Audio::cleanupInput()
|
void AudioPrivate::cleanupInput()
|
||||||
{
|
{
|
||||||
mInputInitialized = false;
|
inputInitialized = false;
|
||||||
|
|
||||||
if (d->alInDev)
|
if (alInDev)
|
||||||
{
|
{
|
||||||
#if (!FIX_SND_PCM_PREPARE_BUG)
|
#if (!FIX_SND_PCM_PREPARE_BUG)
|
||||||
qDebug() << "stopping audio capture";
|
qDebug() << "stopping audio capture";
|
||||||
alcCaptureStop(d->alInDev);
|
alcCaptureStop(alInDev);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
qDebug() << "Closing audio input";
|
qDebug() << "Closing audio input";
|
||||||
if (alcCaptureCloseDevice(d->alInDev) == ALC_TRUE)
|
if (alcCaptureCloseDevice(alInDev) == ALC_TRUE)
|
||||||
d->alInDev = nullptr;
|
alInDev = nullptr;
|
||||||
else
|
else
|
||||||
qWarning() << "Failed to close input";
|
qWarning() << "Failed to close input";
|
||||||
}
|
}
|
||||||
|
@ -516,24 +576,24 @@ void Audio::cleanupInput()
|
||||||
|
|
||||||
Close active audio output device
|
Close active audio output device
|
||||||
*/
|
*/
|
||||||
void Audio::cleanupOutput()
|
void AudioPrivate::cleanupOutput()
|
||||||
{
|
{
|
||||||
mOutputInitialized = false;
|
outputInitialized = false;
|
||||||
|
|
||||||
if (d->alOutDev) {
|
if (alOutDev) {
|
||||||
qDebug() << "Closing audio output";
|
qDebug() << "Closing audio output";
|
||||||
alSourcei(d->alMainSource, AL_LOOPING, AL_FALSE);
|
alSourcei(alMainSource, AL_LOOPING, AL_FALSE);
|
||||||
alSourceStop(d->alMainSource);
|
alSourceStop(alMainSource);
|
||||||
alDeleteSources(1, &d->alMainSource);
|
alDeleteSources(1, &alMainSource);
|
||||||
|
|
||||||
if (!alcMakeContextCurrent(nullptr))
|
if (!alcMakeContextCurrent(nullptr))
|
||||||
qWarning("Failed to clear current audio context.");
|
qWarning("Failed to clear current audio context.");
|
||||||
|
|
||||||
alcDestroyContext(d->alContext);
|
alcDestroyContext(alContext);
|
||||||
d->alContext = nullptr;
|
alContext = nullptr;
|
||||||
|
|
||||||
if (alcCloseDevice(d->alOutDev))
|
if (alcCloseDevice(alOutDev))
|
||||||
d->alOutDev = nullptr;
|
alOutDev = nullptr;
|
||||||
else
|
else
|
||||||
qWarning("Failed to close output.");
|
qWarning("Failed to close output.");
|
||||||
}
|
}
|
||||||
|
@ -544,8 +604,8 @@ Returns true if the input device is open and suscribed to
|
||||||
*/
|
*/
|
||||||
bool Audio::isInputReady()
|
bool Audio::isInputReady()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
return d->alInDev && mInputInitialized;
|
return d->alInDev && d->inputInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -553,8 +613,8 @@ Returns true if the output device is open
|
||||||
*/
|
*/
|
||||||
bool Audio::isOutputReady()
|
bool Audio::isOutputReady()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mAudioLock);
|
QMutexLocker locker(&d->audioLock);
|
||||||
return d->alOutDev && mOutputInitialized;
|
return d->alOutDev && d->outputInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Audio::outDeviceNames()
|
const char* Audio::outDeviceNames()
|
||||||
|
@ -604,9 +664,9 @@ Does nothing and return false on failure
|
||||||
*/
|
*/
|
||||||
bool Audio::tryCaptureSamples(int16_t* buf, int samples)
|
bool Audio::tryCaptureSamples(int16_t* buf, int samples)
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&mAudioLock);
|
QMutexLocker lock(&d->audioLock);
|
||||||
|
|
||||||
if (!(d->alInDev && mInputInitialized))
|
if (!(d->alInDev && d->inputInitialized))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ALint curSamples=0;
|
ALint curSamples=0;
|
||||||
|
|
|
@ -40,6 +40,7 @@ class Audio : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
typedef QList<const void*> PtrList;
|
typedef QList<const void*> PtrList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -54,18 +55,8 @@ public:
|
||||||
qreal inputVolume();
|
qreal inputVolume();
|
||||||
void setInputVolume(qreal volume);
|
void setInputVolume(qreal volume);
|
||||||
|
|
||||||
inline void reinitInput(const QString& inDevDesc)
|
void reinitInput(const QString& inDevDesc);
|
||||||
{
|
bool reinitOutput(const QString& outDevDesc);
|
||||||
QMutexLocker locker(&mAudioLock);
|
|
||||||
cleanupInput();
|
|
||||||
initInput(inDevDesc);
|
|
||||||
}
|
|
||||||
inline bool reinitOutput(const QString& outDevDesc)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&mAudioLock);
|
|
||||||
cleanupOutput();
|
|
||||||
return initOutput(outDevDesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInputReady();
|
bool isInputReady();
|
||||||
bool isOutputReady();
|
bool isOutputReady();
|
||||||
|
@ -107,24 +98,11 @@ private:
|
||||||
Audio();
|
Audio();
|
||||||
~Audio();
|
~Audio();
|
||||||
|
|
||||||
void initInput(const QString& inDevDescr);
|
|
||||||
bool initOutput(const QString& outDevDescr);
|
|
||||||
void cleanupInput();
|
|
||||||
void cleanupOutput();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Audio* instance;
|
static Audio* instance;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioPrivate* d;
|
AudioPrivate* d;
|
||||||
|
|
||||||
private:
|
|
||||||
QThread* audioThread;
|
|
||||||
QMutex mAudioLock;
|
|
||||||
PtrList inputSubscriptions;
|
|
||||||
PtrList outputSubscriptions;
|
|
||||||
bool mInputInitialized;
|
|
||||||
bool mOutputInitialized;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUDIO_H
|
#endif // AUDIO_H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user