Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
a46e916999
12 changed files with 102 additions and 194 deletions
|
@ -1,20 +1,41 @@
|
|||
!isEmpty(SCSTWLIBRARIES_LIB):error("ScStwLibraries.pri already included")
|
||||
SCSTWLIBRARIES_LIB = 1
|
||||
|
||||
#DEPENDS
|
||||
CONFIG(release, debug|release): {
|
||||
SCSTWLIBRARIES_LIB_OUTPUT_DIR="$$PWD/build/release"
|
||||
} else {
|
||||
SCSTWLIBRARIES_LIB_OUTPUT_DIR="$$PWD/build/debug"
|
||||
ScStwLibraries_QML {
|
||||
QT += qml quickcontrols2
|
||||
DEFINES += ScStwLibraries_QML
|
||||
}
|
||||
|
||||
unix:LIBS += -L$$SCSTWLIBRARIES_LIB_OUTPUT_DIR -lScStwLibraries
|
||||
|
||||
win32:LIBS += -L$$SCSTWLIBRARIES_LIB_OUTPUT_DIR -lScStwLibraries1
|
||||
|
||||
android {
|
||||
ANDROID_EXTRA_LIBS += $$SCSTWLIBRARIES_LIB_OUTPUT_DIR/libScStwLibraries.so
|
||||
ScStwLibraries_ClientLibs {
|
||||
DEFINES += ScStwLibraries_ClientLibs
|
||||
}
|
||||
|
||||
INCLUDEPATH += "$$PWD"
|
||||
INCLUDEPATH += "$$PWD"/headers
|
||||
INCLUDEPATH += $$PWD/headers $$PWD
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/sources/ScStw.cpp \
|
||||
$$PWD/sources/scstwsoundplayer.cpp \
|
||||
$$PWD/sources/scstwlibraries.cpp \
|
||||
$$PWD/sources/scstwrace.cpp \
|
||||
$$PWD/sources/scstwtimer.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/headers/ScStw.hpp \
|
||||
$$PWD/headers/ScStwLibraries_global.h \
|
||||
$$PWD/headers/scstwlibraries.h \
|
||||
$$PWD/headers/scstwrace.h \
|
||||
$$PWD/headers/scstwsoundplayer.h \
|
||||
$$PWD/headers/scstwtimer.h
|
||||
|
||||
ScStwLibraries_ClientLibs {
|
||||
SOURCES += \
|
||||
$$PWD/sources/scstwclient.cpp
|
||||
$$PWD/sources/scstwremotemonitorrace.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/headers/scstwclient.h \
|
||||
$$PWD/headers/scstwremotemonitorrace.h
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
$$PWD/resources/ScStwLibrariesShared.qrc
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
QT -= gui
|
||||
QT += network multimedia quick
|
||||
|
||||
CONFIG(contains(QMAKE_LFLAGS_CONSOLE)){
|
||||
message("this is console")
|
||||
}
|
||||
|
||||
TEMPLATE = lib
|
||||
DEFINES += SCSTWLIBRARIES_LIBRARY
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
sources/ScStw.cpp \
|
||||
sources/scstwclient.cpp \
|
||||
sources/scstwlibraries.cpp \
|
||||
sources/scstwrace.cpp \
|
||||
sources/scstwremotemonitorrace.cpp \
|
||||
sources/scstwsoundplayer.cpp \
|
||||
sources/scstwtimer.cpp
|
||||
|
||||
HEADERS += \
|
||||
headers/ScStw.hpp \
|
||||
headers/ScStwLibraries_global.h \
|
||||
headers/scstwlibraries.h \
|
||||
headers/scstwrace.h \
|
||||
headers/scstwclient.h \
|
||||
headers/scstwremotemonitorrace.h \
|
||||
headers/scstwsoundplayer.h \
|
||||
headers/scstwtimer.h
|
||||
|
||||
RESOURCES += \
|
||||
resources/ScStwLibrariesShared.qrc
|
||||
|
||||
DISTFILES +=
|
||||
|
||||
#DEPENDS
|
||||
CONFIG(release, debug|release): {
|
||||
DESTDIR="$$PWD/build/release"
|
||||
} else {
|
||||
DESTDIR="$$PWD/build/debug"
|
||||
}
|
||||
|
||||
# Default rules for deployment.
|
||||
target.path = /usr/local/lib
|
||||
!isEmpty(target.path): INSTALLS += target
|
|
@ -150,6 +150,8 @@ public:
|
|||
AccessDeniedError = 401,
|
||||
NoSessionActiveError = 408,
|
||||
|
||||
UpdateFailedError = 500,
|
||||
|
||||
Error = 900,
|
||||
NotConnectedError = 910,
|
||||
TimeoutError = 911,
|
||||
|
|
|
@ -131,15 +131,7 @@ public slots:
|
|||
*/
|
||||
QVariantMap sendCommand(int header, QJsonValue data = "", int timeout = 3000);
|
||||
|
||||
/*! updater functions */
|
||||
|
||||
/*!
|
||||
* \brief Function to check wether the firmware of the base station is up-to-date
|
||||
* \see getFirmwareVersion()
|
||||
* \see updateFirmware()
|
||||
* \return true or false
|
||||
*/
|
||||
bool isFirmwareUpToDate();
|
||||
/*! helper functions */
|
||||
|
||||
/*!
|
||||
* \brief Function to write a setting on the base station
|
||||
|
|
|
@ -20,10 +20,15 @@
|
|||
#define SCSTWLIBRARIES_H
|
||||
|
||||
#include <QObject>
|
||||
#ifdef ScStwLibraries_QML
|
||||
#include <QQmlApplicationEngine>
|
||||
#include "scstwclient.h"
|
||||
#endif
|
||||
#include "scstwtimer.h"
|
||||
#include "scstwrace.h"
|
||||
#ifdef ScStwLibraries_ClientLibs
|
||||
#include "scstwremotemonitorrace.h"
|
||||
#include "scstwclient.h"
|
||||
#endif
|
||||
|
||||
class ScStwLibraries : public QObject
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
#include <QSoundEffect>
|
||||
|
||||
/*!
|
||||
* \brief The ScStwSoundPlayer class is used for ultra low latency sound playback of the speed clibing start tones and commands
|
||||
|
@ -46,18 +47,14 @@ private:
|
|||
* 0: AtYourMarksSound
|
||||
* 1: ReadySound
|
||||
* 2: StartSound
|
||||
* 3: FalseStartSound
|
||||
*/
|
||||
QMap<int, QFile*> soundFiles;
|
||||
QMap<int, QVariantMap> soundFiles;
|
||||
|
||||
/*!
|
||||
* \brief Containng the false start sound file
|
||||
* \brief The sound effect object
|
||||
*/
|
||||
QFile *falseStartSoundFile;
|
||||
|
||||
/*!
|
||||
* \brief The audio output object
|
||||
*/
|
||||
QAudioOutput *audioOutput;
|
||||
QSoundEffect *soundEffect;
|
||||
|
||||
/*!
|
||||
* \brief The QEventLoop used to wait for the sound to finish
|
||||
|
@ -74,6 +71,11 @@ private:
|
|||
*/
|
||||
int currentlyPlayingAction;
|
||||
|
||||
/*!
|
||||
* \brief Holds the time the playback started at
|
||||
*/
|
||||
double playingStartedAt;
|
||||
|
||||
public slots:
|
||||
|
||||
/*!
|
||||
|
@ -104,12 +106,6 @@ public slots:
|
|||
|
||||
private slots:
|
||||
|
||||
/*!
|
||||
* \brief Handle a state change of the audio output
|
||||
* \param newState the new state of the audio output
|
||||
*/
|
||||
void handleStateChanged(QAudio::State newState);
|
||||
|
||||
signals:
|
||||
|
||||
/*!
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,10 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>ScStwBasestation.sb64</file>
|
||||
<file>sound/AtYourMarksSound.wav</file>
|
||||
<file>sound/IFSC frequenzy and duration conform false start sound.wav</file>
|
||||
<file>sound/ReadySound.wav</file>
|
||||
<file>sound/FalseStartSound.wav</file>
|
||||
<file>sound/StartsignalSound.wav</file>
|
||||
<file>sound/StartsignalSoundExtended.wav</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
BIN
ScStwLibraries/resources/sound/StartsignalSoundExtended.wav
Normal file
BIN
ScStwLibraries/resources/sound/StartsignalSoundExtended.wav
Normal file
Binary file not shown.
|
@ -106,7 +106,7 @@ bool ScStwClient::init() {
|
|||
this->firmwareVersion = initResponse["data"].toMap()["firmwareVersion"].toString();
|
||||
this->timeOffset = initResponse["data"].toMap()["time"].toDouble() - this->date->currentMSecsSinceEpoch();
|
||||
|
||||
qDebug() << "[INFO][BaseStation] Init done! firmware: version: " << this->firmwareVersion << " up-to-date: " << this->isFirmwareUpToDate() << " time offset: " << this->timeOffset;
|
||||
qDebug() << "[INFO][BaseStation] Init done! firmware: " << this->firmwareVersion << " time offset: " << this->timeOffset;
|
||||
|
||||
this->setState(CONNECTED);
|
||||
return true;
|
||||
|
@ -188,6 +188,8 @@ QVariantMap ScStwClient::sendCommand(int header, QJsonValue data, int timeout, b
|
|||
//wait for an answer to finish (programm gets stuck in here)
|
||||
loop->exec();
|
||||
|
||||
|
||||
|
||||
bool replyFound = false;
|
||||
|
||||
// find reply and delete the request from waiting list
|
||||
|
@ -386,22 +388,6 @@ void ScStwClient::handleSignal(QVariantMap data) {
|
|||
emit this->gotSignal(signalKey, data["data"]);
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// --- updater functions ---
|
||||
// -------------------------
|
||||
|
||||
bool ScStwClient::isFirmwareUpToDate() {
|
||||
QString file = ":/ScStwBasestation.sb64";
|
||||
QFile f(file);
|
||||
if (!f.open(QFile::ReadOnly)) return false;
|
||||
QString fileContents = f.readAll();
|
||||
|
||||
QString newFirmwareVersion = fileContents.split("<VER>")[1].split("</VER>")[0];
|
||||
QString currentFirmwareVersion = this->firmwareVersion;
|
||||
|
||||
return ScStw::firmwareCompare(newFirmwareVersion, currentFirmwareVersion) <= 0;
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// --- helper functions ---
|
||||
// ------------------------
|
||||
|
|
|
@ -24,11 +24,16 @@ ScStwLibraries::ScStwLibraries(QObject *parent) : QObject(parent)
|
|||
}
|
||||
|
||||
void ScStwLibraries::init() {
|
||||
#ifdef ScStwLibraries_QML
|
||||
qmlRegisterType<ScStw>("de.itsblue.ScStw", 2, 0, "ScStw");
|
||||
qRegisterMetaType<ScStw::BaseStationSetting>("ScStw::BaseStationSetting");
|
||||
qRegisterMetaType<ScStw::SocketCommand>("ScStw::SocketCommand");
|
||||
|
||||
qmlRegisterType<ScStwRace>("de.itsblue.ScStw", 2, 0, "ScStwRace");
|
||||
qmlRegisterType<ScStwTimer>("de.itsblue.ScStw", 2, 0, "ScStwTimer");
|
||||
|
||||
#ifdef ScStwLibraries_ClientLibs
|
||||
qmlRegisterType<ScStwClient>("de.itsblue.ScStw", 2, 0, "ScStwClient");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -23,66 +23,57 @@ 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);
|
||||
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}});
|
||||
|
||||
// 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);
|
||||
this->soundEffect = new QSoundEffect(this);
|
||||
this->soundEffect->setLoopCount(1);
|
||||
|
||||
// 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("media");
|
||||
|
||||
connect(this->audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
|
||||
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->audioOutput->state() == QAudio::ActiveState)
|
||||
this->audioOutput->stop();
|
||||
if(this->soundEffect->isPlaying())
|
||||
this->soundEffect->stop();
|
||||
|
||||
// update currently playing action
|
||||
this->currentlyPlayingAction = action;
|
||||
|
||||
QFile *playbackFile = this->soundFiles[action];
|
||||
// update volume
|
||||
this->soundEffect->setVolume(volume);
|
||||
|
||||
// close soundfile
|
||||
if(playbackFile->isOpen())
|
||||
playbackFile->close();
|
||||
// load
|
||||
this->soundEffect->setSource(this->soundFiles[action]["path"].toString());
|
||||
|
||||
// open soundfile
|
||||
if(!playbackFile->open(QFile::ReadOnly)) {
|
||||
qWarning() << "[ERROR][SOUNDS] Could not open sound file " << action << " !!";
|
||||
return false;
|
||||
// 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();
|
||||
}
|
||||
|
||||
// update volume
|
||||
this->audioOutput->setVolume(volume);
|
||||
|
||||
// start
|
||||
this->audioOutput->start(playbackFile);
|
||||
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 = QDateTime::currentMSecsSinceEpoch() - (this->audioOutput->elapsedUSecs() / 1000);
|
||||
*timeOfStart = this->playingStartedAt;
|
||||
}
|
||||
|
||||
if(action < 2)
|
||||
|
@ -92,37 +83,30 @@ bool ScStwSoundPlayer::play(int action, double volume, double *timeOfStart) {
|
|||
}
|
||||
|
||||
bool ScStwSoundPlayer::cancel(double volume) {
|
||||
if(!QList<QAudio::State>{QAudio::ActiveState}.contains(this->audioOutput->state()) )
|
||||
if(!this->soundEffect->isPlaying() )
|
||||
return false;
|
||||
|
||||
// stop playback
|
||||
this->audioOutput->stop();
|
||||
this->soundEffect->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);
|
||||
this->soundEffect->setVolume(volume);
|
||||
|
||||
// start
|
||||
this->audioOutput->start(this->falseStartSoundFile);
|
||||
// load
|
||||
this->soundEffect->setSource(this->soundFiles[3]["path"].toString());
|
||||
|
||||
// play
|
||||
this->soundEffect->play();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
|
||||
if(this->audioOutput->state() != QAudio::ActiveState)
|
||||
if(!this->soundEffect->isPlaying())
|
||||
return false;
|
||||
|
||||
// wait until the audio output reports the sound is over
|
||||
|
@ -130,7 +114,7 @@ bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
|
|||
|
||||
// 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);
|
||||
int timeOffset = this->soundFiles[this->currentlyPlayingAction]["duration"].toDouble() - (QDateTime::currentMSecsSinceEpoch() - playingStartedAt);
|
||||
|
||||
if(timeOffset > 0) {
|
||||
QTimer timer;
|
||||
|
@ -140,34 +124,9 @@ bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
|
|||
|
||||
// calculate the point in time where the sound playback actually ended
|
||||
if(timeOfStop != nullptr) {
|
||||
int latency = (this->audioOutput->processedUSecs() - this->audioOutput->elapsedUSecs()) / 1000;
|
||||
int latency = this->playingStartedAt + this->soundFiles[this->currentlyPlayingAction]["duration"].toDouble();
|
||||
*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;
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue