Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dorian Zedler 2020-07-03 14:34:29 +02:00
commit a46e916999
Signed by: dorian
GPG key ID: 989DE36109AFA354
12 changed files with 102 additions and 194 deletions

View file

@ -1,20 +1,41 @@
!isEmpty(SCSTWLIBRARIES_LIB):error("ScStwLibraries.pri already included") !isEmpty(SCSTWLIBRARIES_LIB):error("ScStwLibraries.pri already included")
SCSTWLIBRARIES_LIB = 1 SCSTWLIBRARIES_LIB = 1
#DEPENDS ScStwLibraries_QML {
CONFIG(release, debug|release): { QT += qml quickcontrols2
SCSTWLIBRARIES_LIB_OUTPUT_DIR="$$PWD/build/release" DEFINES += ScStwLibraries_QML
} else {
SCSTWLIBRARIES_LIB_OUTPUT_DIR="$$PWD/build/debug"
} }
unix:LIBS += -L$$SCSTWLIBRARIES_LIB_OUTPUT_DIR -lScStwLibraries ScStwLibraries_ClientLibs {
DEFINES += ScStwLibraries_ClientLibs
win32:LIBS += -L$$SCSTWLIBRARIES_LIB_OUTPUT_DIR -lScStwLibraries1
android {
ANDROID_EXTRA_LIBS += $$SCSTWLIBRARIES_LIB_OUTPUT_DIR/libScStwLibraries.so
} }
INCLUDEPATH += "$$PWD" INCLUDEPATH += $$PWD/headers $$PWD
INCLUDEPATH += "$$PWD"/headers
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

View file

@ -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

View file

@ -150,6 +150,8 @@ public:
AccessDeniedError = 401, AccessDeniedError = 401,
NoSessionActiveError = 408, NoSessionActiveError = 408,
UpdateFailedError = 500,
Error = 900, Error = 900,
NotConnectedError = 910, NotConnectedError = 910,
TimeoutError = 911, TimeoutError = 911,

View file

@ -131,15 +131,7 @@ public slots:
*/ */
QVariantMap sendCommand(int header, QJsonValue data = "", int timeout = 3000); QVariantMap sendCommand(int header, QJsonValue data = "", int timeout = 3000);
/*! updater functions */ /*! helper 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();
/*! /*!
* \brief Function to write a setting on the base station * \brief Function to write a setting on the base station

View file

@ -20,10 +20,15 @@
#define SCSTWLIBRARIES_H #define SCSTWLIBRARIES_H
#include <QObject> #include <QObject>
#ifdef ScStwLibraries_QML
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "scstwclient.h" #endif
#include "scstwtimer.h" #include "scstwtimer.h"
#include "scstwrace.h" #include "scstwrace.h"
#ifdef ScStwLibraries_ClientLibs
#include "scstwremotemonitorrace.h"
#include "scstwclient.h"
#endif
class ScStwLibraries : public QObject class ScStwLibraries : public QObject
{ {

View file

@ -26,6 +26,7 @@
#include <QEventLoop> #include <QEventLoop>
#include <QTimer> #include <QTimer>
#include <QDateTime> #include <QDateTime>
#include <QSoundEffect>
/*! /*!
* \brief The ScStwSoundPlayer class is used for ultra low latency sound playback of the speed clibing start tones and commands * \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 * 0: AtYourMarksSound
* 1: ReadySound * 1: ReadySound
* 2: StartSound * 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; QSoundEffect *soundEffect;
/*!
* \brief The audio output object
*/
QAudioOutput *audioOutput;
/*! /*!
* \brief The QEventLoop used to wait for the sound to finish * \brief The QEventLoop used to wait for the sound to finish
@ -74,6 +71,11 @@ private:
*/ */
int currentlyPlayingAction; int currentlyPlayingAction;
/*!
* \brief Holds the time the playback started at
*/
double playingStartedAt;
public slots: public slots:
/*! /*!
@ -104,12 +106,6 @@ public slots:
private 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: signals:
/*! /*!

File diff suppressed because one or more lines are too long

View file

@ -1,10 +1,10 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>ScStwBasestation.sb64</file>
<file>sound/AtYourMarksSound.wav</file> <file>sound/AtYourMarksSound.wav</file>
<file>sound/IFSC frequenzy and duration conform false start sound.wav</file> <file>sound/IFSC frequenzy and duration conform false start sound.wav</file>
<file>sound/ReadySound.wav</file> <file>sound/ReadySound.wav</file>
<file>sound/FalseStartSound.wav</file> <file>sound/FalseStartSound.wav</file>
<file>sound/StartsignalSound.wav</file> <file>sound/StartsignalSound.wav</file>
<file>sound/StartsignalSoundExtended.wav</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -106,7 +106,7 @@ bool ScStwClient::init() {
this->firmwareVersion = initResponse["data"].toMap()["firmwareVersion"].toString(); this->firmwareVersion = initResponse["data"].toMap()["firmwareVersion"].toString();
this->timeOffset = initResponse["data"].toMap()["time"].toDouble() - this->date->currentMSecsSinceEpoch(); 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); this->setState(CONNECTED);
return true; 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) //wait for an answer to finish (programm gets stuck in here)
loop->exec(); loop->exec();
bool replyFound = false; bool replyFound = false;
// find reply and delete the request from waiting list // find reply and delete the request from waiting list
@ -386,22 +388,6 @@ void ScStwClient::handleSignal(QVariantMap data) {
emit this->gotSignal(signalKey, data["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 --- // --- helper functions ---
// ------------------------ // ------------------------

View file

@ -24,11 +24,16 @@ ScStwLibraries::ScStwLibraries(QObject *parent) : QObject(parent)
} }
void ScStwLibraries::init() { void ScStwLibraries::init() {
#ifdef ScStwLibraries_QML
qmlRegisterType<ScStw>("de.itsblue.ScStw", 2, 0, "ScStw"); qmlRegisterType<ScStw>("de.itsblue.ScStw", 2, 0, "ScStw");
qRegisterMetaType<ScStw::BaseStationSetting>("ScStw::BaseStationSetting"); qRegisterMetaType<ScStw::BaseStationSetting>("ScStw::BaseStationSetting");
qRegisterMetaType<ScStw::SocketCommand>("ScStw::SocketCommand"); qRegisterMetaType<ScStw::SocketCommand>("ScStw::SocketCommand");
qmlRegisterType<ScStwRace>("de.itsblue.ScStw", 2, 0, "ScStwRace"); qmlRegisterType<ScStwRace>("de.itsblue.ScStw", 2, 0, "ScStwRace");
qmlRegisterType<ScStwTimer>("de.itsblue.ScStw", 2, 0, "ScStwTimer"); qmlRegisterType<ScStwTimer>("de.itsblue.ScStw", 2, 0, "ScStwTimer");
#ifdef ScStwLibraries_ClientLibs
qmlRegisterType<ScStwClient>("de.itsblue.ScStw", 2, 0, "ScStwClient"); qmlRegisterType<ScStwClient>("de.itsblue.ScStw", 2, 0, "ScStwClient");
#endif
#endif
} }

View file

@ -23,66 +23,57 @@ ScStwSoundPlayer::ScStwSoundPlayer(QObject *parent) : QObject(parent)
this->waitLoop = new QEventLoop(this); this->waitLoop = new QEventLoop(this);
this->waitTimer = new QTimer(this); this->waitTimer = new QTimer(this);
this->soundFiles.insert(0, new QFile(":/sound/AtYourMarksSound.wav", this)); this->soundFiles.insert(0, {{"path","qrc:/sound/AtYourMarksSound.wav"}, {"duration", 1000}});
this->soundFiles.insert(1, new QFile(":/sound/ReadySound.wav", this)); this->soundFiles.insert(1, {{"path","qrc:/sound/ReadySound.wav"}, {"duration", 570}});
this->soundFiles.insert(2, new QFile(":/sound/StartsignalSound.wav", this)); this->soundFiles.insert(2, {{"path","qrc:/sound/StartsignalSoundExtended.wav"}, {"duration", 3200}});
this->falseStartSoundFile = new QFile(":/sound/FalseStartSound.wav", this); this->soundFiles.insert(3, {{"path","qrc:/sound/FalseStartSound.wav"}, {"duration", 2000}});
// init sound format this->soundEffect = new QSoundEffect(this);
QAudioFormat format; this->soundEffect->setLoopCount(1);
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("media");
connect(this->audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
connect(this, &ScStwSoundPlayer::playbackStarted, this->waitLoop, &QEventLoop::quit); 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) { bool ScStwSoundPlayer::play(int action, double volume, double *timeOfStart) {
if(!this->soundFiles.contains(action)) if(!this->soundFiles.contains(action))
return false; return false;
if(action > 2 || action < 0)
return false;
// stop playback // stop playback
if(this->audioOutput->state() == QAudio::ActiveState) if(this->soundEffect->isPlaying())
this->audioOutput->stop(); this->soundEffect->stop();
// update currently playing action // update currently playing action
this->currentlyPlayingAction = action; this->currentlyPlayingAction = action;
QFile *playbackFile = this->soundFiles[action]; // update volume
this->soundEffect->setVolume(volume);
// close soundfile // load
if(playbackFile->isOpen()) this->soundEffect->setSource(this->soundFiles[action]["path"].toString());
playbackFile->close();
// open soundfile // wait for the effect to load
if(!playbackFile->open(QFile::ReadOnly)) { QEventLoop loop;
qWarning() << "[ERROR][SOUNDS] Could not open sound file " << action << " !!"; while(this->soundEffect->status() != QSoundEffect::Ready) {
return false; QObject::connect(this->soundEffect, &QSoundEffect::statusChanged, &loop, &QEventLoop::quit);
loop.exec();
} }
// update volume
this->audioOutput->setVolume(volume);
// start // 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 // pass the time of start if requested
if(timeOfStart != nullptr) { if(timeOfStart != nullptr) {
*timeOfStart = QDateTime::currentMSecsSinceEpoch() - (this->audioOutput->elapsedUSecs() / 1000); *timeOfStart = this->playingStartedAt;
} }
if(action < 2) if(action < 2)
@ -92,37 +83,30 @@ bool ScStwSoundPlayer::play(int action, double volume, double *timeOfStart) {
} }
bool ScStwSoundPlayer::cancel(double volume) { bool ScStwSoundPlayer::cancel(double volume) {
if(!QList<QAudio::State>{QAudio::ActiveState}.contains(this->audioOutput->state()) ) if(!this->soundEffect->isPlaying() )
return false; return false;
// stop playback // stop playback
this->audioOutput->stop(); this->soundEffect->stop();
this->waitLoop->quit(); this->waitLoop->quit();
if(this->currentlyPlayingAction != 2) if(this->currentlyPlayingAction != 2)
return true; 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 // update volume
this->audioOutput->setVolume(volume); this->soundEffect->setVolume(volume);
// start // load
this->audioOutput->start(this->falseStartSoundFile); this->soundEffect->setSource(this->soundFiles[3]["path"].toString());
// play
this->soundEffect->play();
return true; return true;
} }
bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) { bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
if(this->audioOutput->state() != QAudio::ActiveState) if(!this->soundEffect->isPlaying())
return false; return false;
// wait until the audio output reports the sound is over // 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 // wait until the sound is actually over
// the timeOffset is the buffer time before the audio started! // 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) { if(timeOffset > 0) {
QTimer timer; QTimer timer;
@ -140,34 +124,9 @@ bool ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
// calculate the point in time where the sound playback actually ended // calculate the point in time where the sound playback actually ended
if(timeOfStop != nullptr) { 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; *timeOfStop = QDateTime::currentMSecsSinceEpoch() - latency;
} }
return true; 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;
}
}