/**************************************************************************** ** 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(AtYourMarks, {{"path","qrc:/sound/AtYourMarksSound.wav"}, {"duration", 1000}}); this->soundFiles.insert(Ready, {{"path","qrc:/sound/ReadySound.wav"}, {"duration", 570}}); this->soundFiles.insert(Start, {{"path","qrc:/sound/StartsignalSoundExtended.wav"}, {"duration", 3200}}); this->soundFiles.insert(FalseStart, {{"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); } ScStwSoundPlayer::PlayResult ScStwSoundPlayer::play(ScStwSoundPlayer::StartSound sound, double volume, double *timeOfStart) { if(!this->soundFiles.contains(sound)) { qDebug() << "[ERROR][SoundPlayer] Sound file was not found for sound" << sound; return ScStwSoundPlayer::Error; } // stop playback if(this->soundEffect->isPlaying()) this->soundEffect->stop(); // update currently playing action this->currentlyPlayingSound = sound; #ifdef RASPI // set volume on raspi using amixer QProcess soundProcess; // determine current audio output device soundProcess.start("amixer", {"scontrols"}); soundProcess.waitForFinished(); QStringList outputDeviceNameList = QString(soundProcess.readAllStandardOutput()).split("'"); if(outputDeviceNameList.length() == 3) { QString outputDeviceName = outputDeviceNameList[1]; soundProcess.execute("amixer", {"sset", outputDeviceName, QString::number(volume * 100, 'f', 0) + "%"}); } else { qDebug() << "[WARNING][SoundPlayer] Could not determine output device to set volume"; } this->soundEffect->setVolume(1); #else this->soundEffect->setVolume(volume); #endif // load this->soundEffect->setSource(this->soundFiles[sound]["path"].toString()); // wait for the effect to load QEventLoop loop; while(this->soundEffect->status() != QSoundEffect::Ready) { qDebug() << "[DEBUG][Sound] Sound is not ready yet, status is: " << this->soundEffect->status(); QObject::connect(this->soundEffect, &QSoundEffect::statusChanged, &loop, &QEventLoop::quit); loop.exec(); } qDebug() << "[DEBUG][Sound] Playing sound now: " << sound; // 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(sound < Start) return this->waitForSoundFinish(); return ScStwSoundPlayer::Success; } bool ScStwSoundPlayer::cancel() { if(!this->soundEffect->isPlaying() ) return false; // stop playback this->soundEffect->stop(); this->waitLoop->exit(-1); return true; } ScStwSoundPlayer::PlayResult ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) { if(!this->soundEffect->isPlaying()) return ScStwSoundPlayer::Error; // wait until the audio output reports the sound is over if(waitLoop->exec() == -1) return ScStwSoundPlayer::Cancelled; // wait until the sound is actually over // the timeOffset is the buffer time before the audio started! int timeOffset = this->soundFiles[this->currentlyPlayingSound]["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->currentlyPlayingSound]["duration"].toDouble(); *timeOfStop = QDateTime::currentMSecsSinceEpoch() - latency; } return ScStwSoundPlayer::Success; } bool ScStwSoundPlayer::isPlaying() { return this->soundEffect->isPlaying(); }