2020-04-13 23:48:29 +02:00
|
|
|
#include "../headers/scstwsoundplayer.h"
|
|
|
|
|
|
|
|
ScStwSoundPlayer::ScStwSoundPlayer(QObject *parent) : QObject(parent)
|
|
|
|
{
|
|
|
|
this->waitLoop = new QEventLoop(this);
|
|
|
|
this->waitTimer = new QTimer(this);
|
|
|
|
|
|
|
|
this->soundFiles.insert(0, new QFile(":/sound/AtYourMarksSound.wav", this));
|
|
|
|
this->soundFiles.insert(1, new QFile(":/sound/ReadySound.wav", this));
|
|
|
|
this->soundFiles.insert(2, new QFile(":/sound/StartsignalSound.wav", this));
|
|
|
|
this->falseStartSoundFile = new QFile(":/sound/FalseStartSound.wav", this);
|
|
|
|
|
|
|
|
// init sound format
|
|
|
|
QAudioFormat format;
|
|
|
|
format.setSampleRate(44100);
|
|
|
|
format.setChannelCount(2);
|
|
|
|
format.setSampleSize(16);
|
|
|
|
format.setCodec("audio/pcm");
|
|
|
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
|
|
|
format.setSampleType(QAudioFormat::SignedInt);
|
|
|
|
|
|
|
|
// check if format is valid
|
|
|
|
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
|
|
|
|
if (!info.isFormatSupported(format)) {
|
|
|
|
qWarning() << "Raw audio format not supported by backend, cannot play audio.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->audioOutput = new QAudioOutput(format, this);
|
|
|
|
this->audioOutput->setCategory("ScStw start sound");
|
|
|
|
|
|
|
|
connect(this->audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
|
|
|
|
connect(this, &ScStwSoundPlayer::playbackStarted, this->waitLoop, &QEventLoop::quit);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwSoundPlayer::play(int action, double volume, double *timeOfStart) {
|
|
|
|
if(!this->soundFiles.contains(action))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// stop playback
|
2020-04-17 19:57:00 +02:00
|
|
|
if(this->audioOutput->state() == QAudio::ActiveState)
|
|
|
|
this->audioOutput->stop();
|
2020-04-13 23:48:29 +02:00
|
|
|
|
|
|
|
// update currently playing action
|
|
|
|
this->currentlyPlayingAction = action;
|
|
|
|
|
|
|
|
QFile *playbackFile = this->soundFiles[action];
|
|
|
|
|
|
|
|
// close soundfile
|
|
|
|
if(playbackFile->isOpen())
|
|
|
|
playbackFile->close();
|
|
|
|
|
|
|
|
// open soundfile
|
|
|
|
if(!playbackFile->open(QFile::ReadOnly)) {
|
|
|
|
qWarning() << "[ERROR][SOUNDS] Could not open sound file " << action << " !!";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update volume
|
|
|
|
this->audioOutput->setVolume(volume);
|
|
|
|
|
|
|
|
// start
|
|
|
|
this->audioOutput->start(playbackFile);
|
|
|
|
|
|
|
|
// pass the time of start if requested
|
|
|
|
if(timeOfStart != nullptr) {
|
|
|
|
*timeOfStart = QDateTime::currentMSecsSinceEpoch() - (this->audioOutput->elapsedUSecs() / 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(action < 2)
|
|
|
|
return this->waitForSoundFinish();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwSoundPlayer::cancel(double volume) {
|
|
|
|
if(!QList<QAudio::State>{QAudio::ActiveState}.contains(this->audioOutput->state()) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// stop playback
|
|
|
|
this->audioOutput->stop();
|
2020-04-15 19:21:32 +02:00
|
|
|
this->waitLoop->quit();
|
2020-04-13 23:48:29 +02:00
|
|
|
|
|
|
|
if(this->currentlyPlayingAction != 2)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// close soundfile
|
|
|
|
if(this->falseStartSoundFile->isOpen())
|
|
|
|
this->falseStartSoundFile->close();
|
|
|
|
|
|
|
|
// open sound file
|
|
|
|
if(!this->falseStartSoundFile->open(QFile::ReadOnly)) {
|
|
|
|
qWarning() << "[ERROR][SOUNDS] Could not open false start sound file!!";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update volume
|
|
|
|
this->audioOutput->setVolume(volume);
|
|
|
|
|
|
|
|
// start
|
|
|
|
this->audioOutput->start(this->falseStartSoundFile);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
|
|
|
|
if(this->audioOutput->state() != QAudio::ActiveState)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// wait until the audio output reports the sound is over
|
|
|
|
waitLoop->exec();
|
|
|
|
|
|
|
|
// wait until the sound is actually over
|
|
|
|
// the timeOffset is the buffer time before the audio started!
|
|
|
|
int timeOffset = ((this->audioOutput->processedUSecs() - this->audioOutput->elapsedUSecs()) / 1000);
|
|
|
|
|
|
|
|
if(timeOffset > 0) {
|
|
|
|
QTimer timer;
|
|
|
|
timer.singleShot(timeOffset, this->waitLoop, &QEventLoop::quit);
|
|
|
|
this->waitLoop->exec();
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate the point in time where the sound playback actually ended
|
|
|
|
if(timeOfStop != nullptr) {
|
|
|
|
int latency = (this->audioOutput->processedUSecs() - this->audioOutput->elapsedUSecs()) / 1000;
|
|
|
|
*timeOfStop = QDateTime::currentMSecsSinceEpoch() - latency;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwSoundPlayer::handleStateChanged(QAudio::State newState)
|
|
|
|
{
|
|
|
|
switch (newState) {
|
|
|
|
case QAudio::IdleState:
|
|
|
|
// Finished playing (no more data)
|
|
|
|
waitLoop->exit();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QAudio::StoppedState:
|
|
|
|
// Stopped for other reasons
|
|
|
|
if (this->audioOutput->error() != QAudio::NoError) {
|
|
|
|
// Error handling
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QAudio::ActiveState:
|
|
|
|
emit this->playbackStarted();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// ... other cases as appropriate
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|