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:
parent
f57bf331d6
commit
6425448196
@ -51,52 +51,6 @@
|
||||
} while (0)
|
||||
#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.
|
||||
*/
|
||||
@ -114,6 +68,7 @@ Audio::Audio()
|
||||
, alOutDev(nullptr)
|
||||
, alOutContext(nullptr)
|
||||
, alMainSource(0)
|
||||
, alMainBuffer(0)
|
||||
, outputInitialized(false)
|
||||
{
|
||||
audioThread->setObjectName("qTox Audio");
|
||||
@ -129,6 +84,8 @@ Audio::Audio()
|
||||
captureTimer.setInterval(AUDIO_FRAME_DURATION/2);
|
||||
captureTimer.setSingleShot(false);
|
||||
captureTimer.start();
|
||||
connect(&playMono16Timer, &QTimer::timeout, this, &Audio::playMono16SoundCleanup);
|
||||
playMono16Timer.setSingleShot(true);
|
||||
|
||||
if (!audioThread->isRunning())
|
||||
audioThread->start();
|
||||
@ -360,6 +317,16 @@ bool Audio::initOutput(QString outDevDescr)
|
||||
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
|
||||
*/
|
||||
@ -370,27 +337,23 @@ void Audio::playMono16Sound(const QByteArray& data)
|
||||
if (!autoInitOutput())
|
||||
return;
|
||||
|
||||
AudioPlayer *player = new AudioPlayer(alMainSource, data);
|
||||
connect(player, &AudioPlayer::finished, [=]() {
|
||||
QMutexLocker locker(&audioLock);
|
||||
if (!alMainBuffer)
|
||||
alGenBuffers(1, &alMainBuffer);
|
||||
|
||||
if (outSources.isEmpty())
|
||||
cleanupOutput();
|
||||
else
|
||||
qDebug("Audio output not closed -> there are pending subscriptions.");
|
||||
});
|
||||
ALint state;
|
||||
alGetSourcei(alMainSource, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_PLAYING)
|
||||
{
|
||||
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);
|
||||
|
||||
/**
|
||||
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()));
|
||||
int durationMs = data.size() * 1000 / 2 / 44100;
|
||||
playMono16Timer.start(durationMs + 50);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,6 +442,12 @@ void Audio::cleanupOutput()
|
||||
alSourceStop(alMainSource);
|
||||
alDeleteSources(1, &alMainSource);
|
||||
|
||||
if (alMainBuffer)
|
||||
{
|
||||
alDeleteBuffers(1, &alMainBuffer);
|
||||
alMainBuffer = 0;
|
||||
}
|
||||
|
||||
if (!alcMakeContextCurrent(nullptr))
|
||||
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()
|
||||
{
|
||||
QMutexLocker lock(&audioLock);
|
||||
|
@ -105,6 +105,8 @@ private:
|
||||
bool initOutput(QString outDevDescr);
|
||||
void cleanupInput();
|
||||
void cleanupOutput();
|
||||
/// Called after a mono16 sound stopped playing
|
||||
void playMono16SoundCleanup();
|
||||
/// Called on the captureTimer events to capture audio
|
||||
void doCapture();
|
||||
#if defined(QTOX_FILTER_AUDIO) && defined(ALC_LOOPBACK_CAPTURE_SAMPLES)
|
||||
@ -118,11 +120,12 @@ private:
|
||||
ALCdevice* alInDev;
|
||||
ALfloat inGain;
|
||||
quint32 inSubscriptions;
|
||||
QTimer captureTimer;
|
||||
QTimer captureTimer, playMono16Timer;
|
||||
|
||||
ALCdevice* alOutDev;
|
||||
ALCcontext* alOutContext;
|
||||
ALuint alMainSource;
|
||||
ALuint alMainBuffer;
|
||||
bool outputInitialized;
|
||||
|
||||
QList<ALuint> outSources;
|
||||
|
Loading…
x
Reference in New Issue
Block a user