#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 this->audioOutput->stop(); // 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); qDebug() << "playback stared, took " << this->audioOutput->elapsedUSecs(); // 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::ActiveState}.contains(this->audioOutput->state()) ) return false; // stop playback this->audioOutput->stop(); this->waitLoop->quit(); 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(); qDebug() << "waitLoop exited!! elapsed: " << this->audioOutput->elapsedUSecs() << " processed: " << this->audioOutput->processedUSecs(); // 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; } qDebug() << "finished now"; return true; } void ScStwSoundPlayer::handleStateChanged(QAudio::State newState) { switch (newState) { case QAudio::IdleState: // Finished playing (no more data) qDebug() << "sound reported over!! elapsed: " << this->audioOutput->elapsedUSecs() << " processed: " << this->audioOutput->processedUSecs(); 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; } }