/**************************************************************************** ** ScStw Libraries ** Copyright (C) 2020 Itsblue development ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ****************************************************************************/ #include "../headers/scstwsoundplayer.h" ScStwSoundPlayer::ScStwSoundPlayer(QObject *parent) : QObject(parent) { this->waitLoop = new QEventLoop(this); this->waitTimer = new QTimer(this); this->soundFiles.insert(0, {{"path","qrc:/sound/AtYourMarksSound.wav"}, {"duration", 1000}}); this->soundFiles.insert(1, {{"path","qrc:/sound/ReadySound.wav"}, {"duration", 570}}); this->soundFiles.insert(2, {{"path","qrc:/sound/StartsignalSoundExtended.wav"}, {"duration", 3200}}); this->soundFiles.insert(3, {{"path","qrc:/sound/FalseStartSound.wav"}, {"duration", 2000}}); this->soundEffect = new QSoundEffect(this); this->soundEffect->setLoopCount(1); connect(this, &ScStwSoundPlayer::playbackStarted, this->waitLoop, &QEventLoop::quit); connect(this->soundEffect, &QSoundEffect::playingChanged, this->waitLoop, &QEventLoop::quit); } bool ScStwSoundPlayer::play(int action, double volume, double *timeOfStart) { if(!this->soundFiles.contains(action)) return false; if(action > 2 || action < 0) return false; // stop playback if(this->soundEffect->isPlaying()) this->soundEffect->stop(); // update currently playing action this->currentlyPlayingAction = action; // update volume this->soundEffect->setVolume(volume); // load this->soundEffect->setSource(this->soundFiles[action]["path"].toString()); // wait for the effect to load QEventLoop loop; while(this->soundEffect->status() != QSoundEffect::Ready) { QObject::connect(this->soundEffect, &QSoundEffect::statusChanged, &loop, &QEventLoop::quit); loop.exec(); } // start this->soundEffect->play(); // emit a playback start emit this->playbackStarted(); // save started at this->playingStartedAt = QDateTime::currentMSecsSinceEpoch(); // pass the time of start if requested if(timeOfStart != nullptr) { *timeOfStart = this->playingStartedAt; } if(action < 2) return this->waitForSoundFinish(); return true; } bool ScStwSoundPlayer::cancel(double volume) { if(!this->soundEffect->isPlaying() ) return false; // stop playback this->soundEffect->stop(); this->waitLoop->quit(); if(this->currentlyPlayingAction != 2) return true; // update volume this->soundEffect->setVolume(volume); // load this->soundEffect->setSource(this->soundFiles[3]["path"].toString()); // play this->soundEffect->play(); return true; } bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) { if(!this->soundEffect->isPlaying()) 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->soundFiles[this->currentlyPlayingAction]["duration"].toDouble() - (QDateTime::currentMSecsSinceEpoch() - playingStartedAt); 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->playingStartedAt + this->soundFiles[this->currentlyPlayingAction]["duration"].toDouble(); *timeOfStop = QDateTime::currentMSecsSinceEpoch() - latency; } return true; }