1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Implement the playMono16Sound API sanely

This API used to start *A NEW THREAD* for every sound played!!

Now we simply have a dedicated source and buffer to play those sounds, we use a timer to cleanup the buffer 50ms after the sound is done playing (if a new sound hasn't started in the meantime)
This commit is contained in:
tux3 2016-01-22 00:10:54 +01:00
parent f57bf331d6
commit 6425448196
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
2 changed files with 51 additions and 65 deletions

View File

@ -51,52 +51,6 @@
} while (0) } while (0)
#endif #endif
/**
@class AudioPlayer
@brief Non-blocking audio player.
The audio data is played from start to finish (no streaming).
*/
class AudioPlayer : public QThread
{
public:
AudioPlayer(ALuint source, const QByteArray& data)
: mSource(source)
{
alGenBuffers(1, &mBuffer);
alBufferData(mBuffer, AL_FORMAT_MONO16, data.constData(), data.size(), 44100);
alSourcei(mSource, AL_BUFFER, mBuffer);
connect(this, &AudioPlayer::finished, this, &AudioPlayer::deleteLater);
}
private:
void run() override final
{
alSourceRewind(mSource);
alSourcePlay(mSource);
QMutexLocker locker(&playLock);
ALint state = AL_PLAYING;
while (state == AL_PLAYING) {
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
waitPlaying.wait(&playLock, 2000);
}
alSourceStop(mSource);
alDeleteBuffers(1, &mBuffer);
}
public:
QMutex playLock;
QWaitCondition waitPlaying;
private:
ALuint mBuffer;
ALuint mSource;
};
/** /**
Returns the singleton instance. Returns the singleton instance.
*/ */
@ -114,6 +68,7 @@ Audio::Audio()
, alOutDev(nullptr) , alOutDev(nullptr)
, alOutContext(nullptr) , alOutContext(nullptr)
, alMainSource(0) , alMainSource(0)
, alMainBuffer(0)
, outputInitialized(false) , outputInitialized(false)
{ {
audioThread->setObjectName("qTox Audio"); audioThread->setObjectName("qTox Audio");
@ -129,6 +84,8 @@ Audio::Audio()
captureTimer.setInterval(AUDIO_FRAME_DURATION/2); captureTimer.setInterval(AUDIO_FRAME_DURATION/2);
captureTimer.setSingleShot(false); captureTimer.setSingleShot(false);
captureTimer.start(); captureTimer.start();
connect(&playMono16Timer, &QTimer::timeout, this, &Audio::playMono16SoundCleanup);
playMono16Timer.setSingleShot(true);
if (!audioThread->isRunning()) if (!audioThread->isRunning())
audioThread->start(); audioThread->start();
@ -360,6 +317,16 @@ bool Audio::initOutput(QString outDevDescr)
return true; return true;
} }
/**
Play a 44100Hz mono 16bit PCM sound from a file
*/
void Audio::playMono16Sound(const QString& path)
{
QFile sndFile(path);
sndFile.open(QIODevice::ReadOnly);
playMono16Sound(QByteArray(sndFile.readAll()));
}
/** /**
Play a 44100Hz mono 16bit PCM sound Play a 44100Hz mono 16bit PCM sound
*/ */
@ -370,27 +337,23 @@ void Audio::playMono16Sound(const QByteArray& data)
if (!autoInitOutput()) if (!autoInitOutput())
return; return;
AudioPlayer *player = new AudioPlayer(alMainSource, data); if (!alMainBuffer)
connect(player, &AudioPlayer::finished, [=]() { alGenBuffers(1, &alMainBuffer);
QMutexLocker locker(&audioLock);
if (outSources.isEmpty()) ALint state;
cleanupOutput(); alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
else if (state == AL_PLAYING)
qDebug("Audio output not closed -> there are pending subscriptions."); {
}); alSourceStop(alMainSource);
alSourcei(alMainSource, AL_BUFFER, AL_NONE);
}
player->start(); alBufferData(alMainBuffer, AL_FORMAT_MONO16, data.constData(), data.size(), 44100);
} alSourcei(alMainSource, AL_BUFFER, static_cast<ALint>(alMainBuffer));
alSourcePlay(alMainSource);
/** int durationMs = data.size() * 1000 / 2 / 44100;
Play a 44100Hz mono 16bit PCM sound from a file playMono16Timer.start(durationMs + 50);
*/
void Audio::playMono16Sound(const QString& path)
{
QFile sndFile(path);
sndFile.open(QIODevice::ReadOnly);
playMono16Sound(QByteArray(sndFile.readAll()));
} }
/** /**
@ -479,6 +442,12 @@ void Audio::cleanupOutput()
alSourceStop(alMainSource); alSourceStop(alMainSource);
alDeleteSources(1, &alMainSource); alDeleteSources(1, &alMainSource);
if (alMainBuffer)
{
alDeleteBuffers(1, &alMainBuffer);
alMainBuffer = 0;
}
if (!alcMakeContextCurrent(nullptr)) if (!alcMakeContextCurrent(nullptr))
qWarning("Failed to clear audio context."); qWarning("Failed to clear audio context.");
@ -493,6 +462,20 @@ void Audio::cleanupOutput()
} }
} }
void Audio::playMono16SoundCleanup()
{
QMutexLocker locker(&audioLock);
ALint state;
alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
if (state == AL_STOPPED)
{
alSourcei(alMainSource, AL_BUFFER, AL_NONE);
alDeleteBuffers(1, &alMainBuffer);
alMainBuffer = 0;
}
}
void Audio::doCapture() void Audio::doCapture()
{ {
QMutexLocker lock(&audioLock); QMutexLocker lock(&audioLock);

View File

@ -105,6 +105,8 @@ private:
bool initOutput(QString outDevDescr); bool initOutput(QString outDevDescr);
void cleanupInput(); void cleanupInput();
void cleanupOutput(); void cleanupOutput();
/// Called after a mono16 sound stopped playing
void playMono16SoundCleanup();
/// Called on the captureTimer events to capture audio /// Called on the captureTimer events to capture audio
void doCapture(); void doCapture();
#if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES) #if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES)
@ -118,11 +120,12 @@ private:
ALCdevice* alInDev; ALCdevice* alInDev;
ALfloat inGain; ALfloat inGain;
quint32 inSubscriptions; quint32 inSubscriptions;
QTimer captureTimer; QTimer captureTimer, playMono16Timer;
ALCdevice* alOutDev; ALCdevice* alOutDev;
ALCcontext* alOutContext; ALCcontext* alOutContext;
ALuint alMainSource; ALuint alMainSource;
ALuint alMainBuffer;
bool outputInitialized; bool outputInitialized;
QList<ALuint> outSources; QList<ALuint> outSources;