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)
|
} 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);
|
||||||
Play a 44100Hz mono 16bit PCM sound from a file
|
alSourcei(alMainSource, AL_BUFFER, static_cast<ALint>(alMainBuffer));
|
||||||
*/
|
alSourcePlay(alMainSource);
|
||||||
void Audio::playMono16Sound(const QString& path)
|
|
||||||
{
|
int durationMs = data.size() * 1000 / 2 / 44100;
|
||||||
QFile sndFile(path);
|
playMono16Timer.start(durationMs + 50);
|
||||||
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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user