From 40b699e0a5d1398af1be6e9855e9ab32c1e73c25 Mon Sep 17 00:00:00 2001 From: Peter L Jones Date: Sat, 20 Jun 2020 17:04:06 +0100 Subject: [PATCH 1/2] Save the source --- src/recorder/jamrecorder.cpp | 32 ++++---- src/recorder/jamrecorder.h | 14 ++-- src/server.cpp | 139 ++++++++++++++++++++++------------- src/server.h | 17 +++-- 4 files changed, 124 insertions(+), 78 deletions(-) diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp index 68fc2ab4..1a84ac61 100755 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -302,27 +302,32 @@ QMap> CJamSession::TracksFromSessionDir(const QString /** * @brief CJamRecorder::Init Create recording directory, if necessary, and connect signal handlers * @param server Server object emiting signals + * @return QString::null on success else the failure reason */ -bool CJamRecorder::Init( const CServer* server, - const int _iServerFrameSizeSamples ) +QString CJamRecorder::Init( const CServer* server, + const int _iServerFrameSizeSamples ) { + QString errmsg = QString::null; QFileInfo fi(recordBaseDir.absolutePath()); fi.setCaching(false); if (!fi.exists() && !QDir().mkpath(recordBaseDir.absolutePath())) { - qCritical() << recordBaseDir.absolutePath() << "does not exist but could not be created"; - return false; + errmsg = recordBaseDir.absolutePath() + " does not exist but could not be created"; + qCritical() << errmsg; + return errmsg; } if (!fi.isDir()) { - qCritical() << recordBaseDir.absolutePath() << "exists but is not a directory"; - return false; + errmsg = recordBaseDir.absolutePath() + " exists but is not a directory"; + qCritical() << errmsg; + return errmsg; } if (!fi.isWritable()) { - qCritical() << recordBaseDir.absolutePath() << "is a directory but cannot be written to"; - return false; + errmsg = recordBaseDir.absolutePath() + " is a directory but cannot be written to"; + qCritical() << errmsg; + return errmsg; } QObject::connect( (const QObject *)server, SIGNAL ( RestartRecorder() ), @@ -333,6 +338,9 @@ bool CJamRecorder::Init( const CServer* server, this, SLOT( OnEnd() ), Qt::ConnectionType::QueuedConnection ); + QObject::connect( (const QObject *)server, SIGNAL ( EndRecorderThread() ), + this, SLOT( OnAboutToQuit() ) ); + QObject::connect( (const QObject *)server, SIGNAL ( Stopped() ), this, SLOT( OnEnd() ), Qt::ConnectionType::QueuedConnection ); @@ -351,11 +359,7 @@ bool CJamRecorder::Init( const CServer* server, iServerFrameSizeSamples = _iServerFrameSizeSamples; - thisThread = new QThread(); - moveToThread ( thisThread ); - thisThread->start(); - - return true; + return errmsg; } /** @@ -410,7 +414,7 @@ void CJamRecorder::OnAboutToQuit() { OnEnd(); - thisThread->exit(); + QThread::currentThread()->exit(); } void CJamRecorder::ReaperProjectFromCurrentSession() diff --git a/src/recorder/jamrecorder.h b/src/recorder/jamrecorder.h index 4bf8fa8f..a739f4f5 100755 --- a/src/recorder/jamrecorder.h +++ b/src/recorder/jamrecorder.h @@ -149,7 +149,7 @@ public: * @brief Create recording directory, if necessary, and connect signal handlers * @param server Server object emiting signals */ - bool Init( const CServer* server, const int _iServerFrameSizeSamples ); + QString Init( const CServer* server, const int _iServerFrameSizeSamples ); /** * @brief SessionDirToReaper Method that allows an RPP file to be recreated @@ -169,35 +169,33 @@ private: CJamSession* currentSession; int iServerFrameSizeSamples; - QThread* thisThread; - signals: void RecordingSessionStarted ( QString sessionDir ); private slots: /** - * @brief Raised when last client leaves the server, ending the recording. + * @brief Handle last client leaving the server, ends the recording. */ void OnEnd(); /** - * @brief Raised to end one session and start a new one. + * @brief Handle request to end one session and start a new one. */ void OnTriggerSession(); /** - * @brief Raised when application is stopping + * @brief Handle application stopping */ void OnAboutToQuit(); /** - * @brief Raised when an existing client leaves the server. + * @brief Handle an existing client leaving the server. * @param iChID channel number of client */ void OnDisconnected ( int iChID ); /** - * @brief Raised when a frame of data is available to process + * @brief Handle a frame of data to process */ void OnFrame ( const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector data ); }; diff --git a/src/server.cpp b/src/server.cpp index b958c8fd..d89ef5bc 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -240,10 +240,8 @@ CServer::CServer ( const int iNewMaxNumChan, Socket ( this, iPortNumber ), Logging ( iMaxDaysHistory ), iFrameCount ( 0 ), - JamRecorder ( strRecordingDirName ), - bRecorderInitialised ( false ), - bEnableRecording ( false ), bWriteStatusHTMLFile ( false ), + bRecorderInitialised ( false ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), ServerListManager ( iPortNumber, strCentralServer, @@ -424,11 +422,7 @@ CServer::CServer ( const int iNewMaxNumChan, strWelcomeMessage = strWelcomeMessage.left ( MAX_LEN_CHAT_TEXT ); // enable jam recording (if requested) - kicks off the thread - if ( !strRecordingDirName.isEmpty() ) - { - bRecorderInitialised = JamRecorder.Init ( this, iServerFrameSizeSamples ); - SetEnableRecording ( bRecorderInitialised ); - } + SetRecordingDir ( strRecordingDirName ); // enable all channels (for the server all channel must be enabled the // entire life time of the software) @@ -479,9 +473,6 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); - QObject::connect( &JamRecorder, &recorder::CJamRecorder::RecordingSessionStarted, - this, &CServer::RecordingSessionStarted ); - QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &CServer::OnAboutToQuit ); @@ -742,46 +733,6 @@ void CServer::OnHandledSignal ( int sigNum ) #endif } -void CServer::RequestNewRecording() -{ - if ( bRecorderInitialised && bEnableRecording ) - { - emit RestartRecorder(); - } - - // send recording state message - doesn't hurt - CreateAndSendRecorderStateForAllConChannels(); -} - -void CServer::SetEnableRecording ( bool bNewEnableRecording ) -{ - if ( bRecorderInitialised ) - { - // note that this block executes regardless of whether - // what appears to be a change is being applied, to ensure - // the requested state is the result - bEnableRecording = bNewEnableRecording; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) -// TODO we should use the ConsoleWriterFactory() instead of qInfo() - qInfo() << "Recording state" << ( bEnableRecording ? "enabled" : "disabled" ); -#endif - - if ( !bEnableRecording ) - { - emit StopRecorder(); - } - else if ( !IsRunning() ) - { - // This dirty hack is for the GUI. It doesn't care. - emit StopRecorder(); - } - } - - // send recording state message - CreateAndSendRecorderStateForAllConChannels(); -} - void CServer::Start() { // only start if not already running @@ -1639,6 +1590,92 @@ void CServer::WriteHTMLChannelList() streamFileOut << "" << endl; } +void CServer::RequestNewRecording() +{ + if ( bRecorderInitialised && bEnableRecording ) + { + emit RestartRecorder(); + } + + // send recording state message - doesn't hurt + CreateAndSendRecorderStateForAllConChannels(); +} + +void CServer::SetEnableRecording ( bool bNewEnableRecording ) +{ + if ( bRecorderInitialised ) + { + // note that this block executes regardless of whether + // what appears to be a change is being applied, to ensure + // the requested state is the result + bEnableRecording = bNewEnableRecording; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qInfo() + qInfo() << "Recording state" << ( bEnableRecording ? "enabled" : "disabled" ); +#endif + + if ( !bEnableRecording ) + { + emit StopRecorder(); + } + else if ( !IsRunning() ) + { + // This dirty hack is for the GUI. It doesn't care. + emit StopRecorder(); + } + } + + // send recording state message + CreateAndSendRecorderStateForAllConChannels(); +} + +void CServer::SetRecordingDir ( QString newRecordingDir ) +{ + if ( bRecorderInitialised ) + { + // We have a thread and we want to start a new one. + // We only want one running. + // This could take time, unfortunately. + // Hopefully changing recording directory will NOT happen during a long jam... + emit EndRecorderThread(); + thJamRecorder.wait(); + } + + if ( !newRecordingDir.isEmpty() ) + { + pJamRecorder = new recorder::CJamRecorder ( newRecordingDir ); + strRecorderErrMsg = pJamRecorder->Init ( this, iServerFrameSizeSamples ); + bRecorderInitialised = ( strRecorderErrMsg == QString::null ); + bEnableRecording = bRecorderInitialised; + } + else + { + // This is the only time this is ever true - UI needs to handle it + strRecorderErrMsg = QString::null; + bRecorderInitialised = false; + bEnableRecording = false; + } + if ( bRecorderInitialised ) + { + strRecordingDir = newRecordingDir; + pJamRecorder->moveToThread ( &thJamRecorder ); + thJamRecorder.setObjectName ( "Jamulus::JamRecorder" ); + + QObject::connect ( &thJamRecorder, &QThread::finished, + pJamRecorder, &QObject::deleteLater ); + + QObject::connect ( pJamRecorder, &recorder::CJamRecorder::RecordingSessionStarted, + this, &CServer::RecordingSessionStarted ); + + thJamRecorder.start(); + } + else + { + strRecordingDir = ""; + } +} + void CServer::customEvent ( QEvent* pEvent ) { if ( pEvent->type() == QEvent::User + 11 ) diff --git a/src/server.h b/src/server.h index bb58dc2d..10f84df5 100755 --- a/src/server.h +++ b/src/server.h @@ -200,9 +200,12 @@ public: CVector& veciNetwFrameSizeFact ); bool GetRecorderInitialised() { return bRecorderInitialised; } + QString GetRecorderErrMsg() { return strRecorderErrMsg; } bool GetRecordingEnabled() { return bEnableRecording; } void RequestNewRecording(); void SetEnableRecording ( bool bNewEnableRecording ); + QString GetRecordingDir() { return strRecordingDir; } + void SetRecordingDir( QString newRecordingDir ); // Server list management -------------------------------------------------- void UpdateServerList() { ServerListManager.Update(); } @@ -358,16 +361,19 @@ protected: // channel level update frame interval counter int iFrameCount; - // recording thread - recorder::CJamRecorder JamRecorder; - bool bRecorderInitialised; - bool bEnableRecording; - // HTML file server status bool bWriteStatusHTMLFile; QString strServerHTMLFileListName; QString strServerNameWithPort; + // recording thread + bool bRecorderInitialised; + bool bEnableRecording; + QThread thJamRecorder; + recorder::CJamRecorder* pJamRecorder; + QString strRecorderErrMsg; + QString strRecordingDir; + CHighPrecisionTimer HighPrecisionTimer; // server list @@ -396,6 +402,7 @@ signals: void RestartRecorder(); void StopRecorder(); void RecordingSessionStarted ( QString sessionDir ); + void EndRecorderThread(); public slots: void OnTimer(); From d3059b608a5233d9bb2100e0c1a7acbadbd5e341 Mon Sep 17 00:00:00 2001 From: Peter L Jones Date: Sun, 21 Jun 2020 17:22:38 +0100 Subject: [PATCH 2/2] Move the source --- Jamulus.pro | 2 + src/recorder/creaperproject.cpp | 0 src/recorder/creaperproject.h | 0 src/recorder/cwavestream.cpp | 0 src/recorder/cwavestream.h | 0 src/recorder/jamcontroller.cpp | 171 ++++++++++++++++++++++++++++++++ src/recorder/jamcontroller.h | 77 ++++++++++++++ src/recorder/jamrecorder.cpp | 47 +++------ src/recorder/jamrecorder.h | 20 ++-- src/server.cpp | 166 +++++++++---------------------- src/server.h | 41 ++++---- 11 files changed, 339 insertions(+), 185 deletions(-) mode change 100755 => 100644 src/recorder/creaperproject.cpp mode change 100755 => 100644 src/recorder/creaperproject.h mode change 100755 => 100644 src/recorder/cwavestream.cpp mode change 100755 => 100644 src/recorder/cwavestream.h create mode 100755 src/recorder/jamcontroller.cpp create mode 100755 src/recorder/jamcontroller.h mode change 100755 => 100644 src/recorder/jamrecorder.cpp mode change 100755 => 100644 src/recorder/jamrecorder.h diff --git a/Jamulus.pro b/Jamulus.pro index 306d65e8..890d5dba 100755 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -355,6 +355,7 @@ HEADERS += src/buffer.h \ src/global.h \ src/multicolorled.h \ src/protocol.h \ + src/recorder/jamcontroller.h \ src/server.h \ src/serverlist.h \ src/serverlogging.h \ @@ -454,6 +455,7 @@ SOURCES += src/buffer.cpp \ src/client.cpp \ src/main.cpp \ src/protocol.cpp \ + src/recorder/jamcontroller.cpp \ src/server.cpp \ src/serverlist.cpp \ src/serverlogging.cpp \ diff --git a/src/recorder/creaperproject.cpp b/src/recorder/creaperproject.cpp old mode 100755 new mode 100644 diff --git a/src/recorder/creaperproject.h b/src/recorder/creaperproject.h old mode 100755 new mode 100644 diff --git a/src/recorder/cwavestream.cpp b/src/recorder/cwavestream.cpp old mode 100755 new mode 100644 diff --git a/src/recorder/cwavestream.h b/src/recorder/cwavestream.h old mode 100755 new mode 100644 diff --git a/src/recorder/jamcontroller.cpp b/src/recorder/jamcontroller.cpp new file mode 100755 index 00000000..e6a4f335 --- /dev/null +++ b/src/recorder/jamcontroller.cpp @@ -0,0 +1,171 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * pljones + * + ****************************************************************************** + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "jamcontroller.h" + +using namespace recorder; + +CJamController::CJamController() : + bRecorderInitialised ( false ), + bEnableRecording ( false ), + strRecordingDir ( "" ), + pthJamRecorder ( nullptr ) +{ +} + +void CJamController::RequestNewRecording() +{ + + if ( bRecorderInitialised && bEnableRecording ) + { + emit RestartRecorder(); + } +} + +void CJamController::SetEnableRecording ( bool bNewEnableRecording, bool isRunning ) +{ + + if ( bRecorderInitialised ) + { + // note that this block executes regardless of whether + // what appears to be a change is being applied, to ensure + // the requested state is the result + bEnableRecording = bNewEnableRecording; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qInfo() + qInfo() << "Recording state" << ( bEnableRecording ? "enabled" : "disabled" ); +#endif + + if ( !bEnableRecording ) + { + emit StopRecorder(); + } + else if ( !isRunning ) + { + // This dirty hack is for the GUI. It doesn't care. + emit StopRecorder(); + } + } +} + +void CJamController::SetRecordingDir ( QString newRecordingDir, + int iServerFrameSizeSamples ) +{ + if ( bRecorderInitialised && pthJamRecorder != nullptr ) + { + // We have a thread and we want to start a new one. + // We only want one running. + // This could take time, unfortunately. + // Hopefully changing recording directory will NOT happen during a long jam... + emit EndRecorderThread(); + pthJamRecorder->wait(); + pthJamRecorder = nullptr; + } + + if ( !newRecordingDir.isEmpty() ) + { + pJamRecorder = new recorder::CJamRecorder ( newRecordingDir, iServerFrameSizeSamples ); + strRecorderErrMsg = pJamRecorder->Init(); + bRecorderInitialised = ( strRecorderErrMsg == QString::null ); + bEnableRecording = bRecorderInitialised; + } + else + { + // This is the only time this is ever true - UI needs to handle it + strRecorderErrMsg = QString::null; + bRecorderInitialised = false; + bEnableRecording = false; + } + + if ( bRecorderInitialised ) + { + strRecordingDir = newRecordingDir; + + pthJamRecorder = new QThread(); + pJamRecorder->moveToThread ( pthJamRecorder ); + pthJamRecorder->setObjectName ( "Jamulus::JamRecorder" ); + + // QT signals + QObject::connect ( pthJamRecorder, &QThread::finished, + pJamRecorder, &QObject::deleteLater ); + + QObject::connect( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + pJamRecorder, &CJamRecorder::OnAboutToQuit, + Qt::ConnectionType::BlockingQueuedConnection ); + + // from the controller to the recorder + QObject::connect( this, &CJamController::RestartRecorder, + pJamRecorder, &CJamRecorder::OnTriggerSession ); + + QObject::connect( this, &CJamController::StopRecorder, + pJamRecorder, &CJamRecorder::OnEnd ); + + QObject::connect( this, &CJamController::EndRecorderThread, + pJamRecorder, &CJamRecorder::OnAboutToQuit, + Qt::ConnectionType::BlockingQueuedConnection ); + + // from the server to the recorder + QObject::connect( this, &CJamController::Stopped, + pJamRecorder, &CJamRecorder::OnEnd ); + + QObject::connect( this, &CJamController::ClientDisconnected, + pJamRecorder, &CJamRecorder::OnDisconnected ); + + qRegisterMetaType> ( "CVector" ); + QObject::connect( this, &CJamController::AudioFrame, + pJamRecorder, &CJamRecorder::OnFrame ); + + // from the recorder to the server + QObject::connect ( pJamRecorder, &CJamRecorder::RecordingSessionStarted, + this, &CJamController::RecordingSessionStarted ); + + pthJamRecorder->start(); + + } + else + { + strRecordingDir = ""; + } +} + +ERecorderState CJamController::GetRecorderState() +{ + // return recorder state + if ( bRecorderInitialised ) + { + if ( bEnableRecording ) + { + return RS_RECORDING; + } + else + { + return RS_NOT_ENABLED; + } + } + else + { + return RS_NOT_INITIALISED; + } +} diff --git a/src/recorder/jamcontroller.h b/src/recorder/jamcontroller.h new file mode 100755 index 00000000..0fc2a178 --- /dev/null +++ b/src/recorder/jamcontroller.h @@ -0,0 +1,77 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * pljones + * + ****************************************************************************** + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include + +#include "jamrecorder.h" + +namespace recorder { + +class CJamController : public QObject +{ + Q_OBJECT +public: + explicit CJamController(); + + bool GetRecorderInitialised() { return bRecorderInitialised; } + QString GetRecorderErrMsg() { return strRecorderErrMsg; } + bool GetRecordingEnabled() { return bEnableRecording; } + void RequestNewRecording(); + void SetEnableRecording ( bool bNewEnableRecording, bool isRunning ); + QString GetRecordingDir() { return strRecordingDir; } + void SetRecordingDir ( QString newRecordingDir, + int iServerFrameSizeSamples ); + ERecorderState GetRecorderState(); + +private: + CServer* pServer; + + bool bRecorderInitialised; + bool bEnableRecording; + QString strRecordingDir; + QThread* pthJamRecorder; + + CJamRecorder* pJamRecorder; + QString strRecorderErrMsg; + +signals: + void RestartRecorder(); + void StopRecorder(); + void RecordingSessionStarted ( QString sessionDir ); + void EndRecorderThread(); + void Stopped(); + void ClientDisconnected ( int iChID ); + void AudioFrame ( const int iChID, + const QString stChName, + const CHostAddress RecHostAddr, + const int iNumAudChan, + const CVector vecsData ); + +}; + +} + +Q_DECLARE_METATYPE(int16_t) diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp old mode 100755 new mode 100644 index 1a84ac61..651755b1 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -304,61 +304,40 @@ QMap> CJamSession::TracksFromSessionDir(const QString * @param server Server object emiting signals * @return QString::null on success else the failure reason */ -QString CJamRecorder::Init( const CServer* server, - const int _iServerFrameSizeSamples ) +QString CJamRecorder::Init() { QString errmsg = QString::null; - QFileInfo fi(recordBaseDir.absolutePath()); - fi.setCaching(false); + QFileInfo fi ( recordBaseDir.absolutePath() ); + fi.setCaching ( false ); - if (!fi.exists() && !QDir().mkpath(recordBaseDir.absolutePath())) + if ( !fi.exists() && !QDir().mkpath ( recordBaseDir.absolutePath() ) ) { errmsg = recordBaseDir.absolutePath() + " does not exist but could not be created"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() qCritical() << errmsg; +#endif return errmsg; } if (!fi.isDir()) { errmsg = recordBaseDir.absolutePath() + " exists but is not a directory"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() qCritical() << errmsg; +#endif return errmsg; } if (!fi.isWritable()) { errmsg = recordBaseDir.absolutePath() + " is a directory but cannot be written to"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() qCritical() << errmsg; +#endif return errmsg; } - QObject::connect( (const QObject *)server, SIGNAL ( RestartRecorder() ), - this, SLOT( OnTriggerSession() ), - Qt::ConnectionType::QueuedConnection ); - - QObject::connect( (const QObject *)server, SIGNAL ( StopRecorder() ), - this, SLOT( OnEnd() ), - Qt::ConnectionType::QueuedConnection ); - - QObject::connect( (const QObject *)server, SIGNAL ( EndRecorderThread() ), - this, SLOT( OnAboutToQuit() ) ); - - QObject::connect( (const QObject *)server, SIGNAL ( Stopped() ), - this, SLOT( OnEnd() ), - Qt::ConnectionType::QueuedConnection ); - - QObject::connect( (const QObject *)server, SIGNAL ( ClientDisconnected ( int ) ), - this, SLOT( OnDisconnected ( int ) ), - Qt::ConnectionType::QueuedConnection ); - - qRegisterMetaType> ( "CVector" ); - QObject::connect( (const QObject *)server, SIGNAL ( AudioFrame( const int, const QString, const CHostAddress, const int, const CVector ) ), - this, SLOT( OnFrame (const int, const QString, const CHostAddress, const int, const CVector ) ), - Qt::ConnectionType::QueuedConnection ); - - QObject::connect( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), - this, SLOT( OnAboutToQuit() ) ); - - iServerFrameSizeSamples = _iServerFrameSizeSamples; - return errmsg; } diff --git a/src/recorder/jamrecorder.h b/src/recorder/jamrecorder.h old mode 100755 new mode 100644 index a739f4f5..9ecb7934 --- a/src/recorder/jamrecorder.h +++ b/src/recorder/jamrecorder.h @@ -139,9 +139,11 @@ class CJamRecorder : public QObject Q_OBJECT public: - CJamRecorder ( const QString recordingDirName ) : - recordBaseDir ( recordingDirName ), - isRecording ( false ) + CJamRecorder ( const QString strRecordingBaseDir, + const int iServerFrameSizeSamples ) : + recordBaseDir ( strRecordingBaseDir ), + iServerFrameSizeSamples ( iServerFrameSizeSamples ), + isRecording ( false ) { } @@ -149,7 +151,7 @@ public: * @brief Create recording directory, if necessary, and connect signal handlers * @param server Server object emiting signals */ - QString Init( const CServer* server, const int _iServerFrameSizeSamples ); + QString Init(); /** * @brief SessionDirToReaper Method that allows an RPP file to be recreated @@ -163,16 +165,15 @@ private: void ReaperProjectFromCurrentSession(); void AudacityLofFromCurrentSession(); - QDir recordBaseDir; - + QDir recordBaseDir; + int iServerFrameSizeSamples; bool isRecording; CJamSession* currentSession; - int iServerFrameSizeSamples; signals: void RecordingSessionStarted ( QString sessionDir ); -private slots: +public slots: /** * @brief Handle last client leaving the server, ends the recording. */ @@ -201,6 +202,3 @@ private slots: }; } - -Q_DECLARE_METATYPE(int16_t) -Q_DECLARE_METATYPE(CVector) diff --git a/src/server.cpp b/src/server.cpp index d89ef5bc..899a6bbe 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -241,7 +241,6 @@ CServer::CServer ( const int iNewMaxNumChan, Logging ( iMaxDaysHistory ), iFrameCount ( 0 ), bWriteStatusHTMLFile ( false ), - bRecorderInitialised ( false ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), ServerListManager ( iPortNumber, strCentralServer, @@ -404,6 +403,9 @@ CServer::CServer ( const int iNewMaxNumChan, QString().number( static_cast ( iPortNumber ) ) ); } + // jam recorder needs the frame size + JamController.SetRecordingDir ( strRecordingDirName, iServerFrameSizeSamples ); + // manage welcome message: if the welcome message is a valid link to a local // file, the content of that file is used as the welcome message (#361) strWelcomeMessage = strNewWelcomeMessage; // first copy text, may be overwritten @@ -473,6 +475,28 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); + QObject::connect ( &JamController, &recorder::CJamController::RestartRecorder, + this, &CServer::RestartRecorder ); + + QObject::connect ( &JamController, &recorder::CJamController::StopRecorder, + this, &CServer::StopRecorder ); + + QObject::connect ( &JamController, &recorder::CJamController::RecordingSessionStarted, + this, &CServer::RecordingSessionStarted ); + + QObject::connect ( &JamController, &recorder::CJamController::EndRecorderThread, + this, &CServer::EndRecorderThread ); + + QObject::connect( this, &CServer::Stopped, + &JamController, &recorder::CJamController::Stopped ); + + QObject::connect( this, &CServer::ClientDisconnected, + &JamController, &recorder::CJamController::ClientDisconnected ); + + qRegisterMetaType> ( "CVector" ); + QObject::connect( this, &CServer::AudioFrame, + &JamController, &recorder::CJamController::AudioFrame ); + QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &CServer::OnAboutToQuit ); @@ -626,7 +650,7 @@ void CServer::OnNewConnection ( int iChID, vecChannels[iChID].CreateVersionAndOSMes(); // send recording state message on connection - vecChannels[iChID].CreateRecorderStateMes ( GetRecorderState() ); + vecChannels[iChID].CreateRecorderStateMes ( JamController.GetRecorderState() ); // reset the conversion buffers DoubleFrameSizeConvBufIn[iChID].Reset(); @@ -718,7 +742,7 @@ void CServer::OnHandledSignal ( int sigNum ) break; case SIGUSR2: - SetEnableRecording ( !bEnableRecording ); + SetEnableRecording ( !JamController.GetRecordingEnabled() ); break; case SIGINT: @@ -904,7 +928,7 @@ static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); // TE // and emit the client disconnected signal if ( eGetStat == GS_CHAN_NOW_DISCONNECTED ) { - if ( bEnableRecording ) + if ( JamController.GetRecordingEnabled() ) { emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock? } @@ -989,7 +1013,7 @@ static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); // TE const int iCurNumAudChan = vecNumAudioChannels[i]; // export the audio data for recording purpose - if ( bEnableRecording ) + if ( JamController.GetRecordingEnabled() ) { emit AudioFrame ( iCurChanID, vecChannels[iCurChanID].GetName(), @@ -1324,42 +1348,6 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID } } -void CServer::CreateAndSendRecorderStateForAllConChannels() -{ - // get recorder state - ERecorderState eRecorderState = GetRecorderState(); - - // now send recorder state to all connected clients - for ( int i = 0; i < iMaxNumChannels; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - // send message - vecChannels[i].CreateRecorderStateMes ( eRecorderState ); - } - } -} - -ERecorderState CServer::GetRecorderState() -{ - // return recorder state - if ( bRecorderInitialised ) - { - if ( bEnableRecording ) - { - return RS_RECORDING; - } - else - { - return RS_NOT_ENABLED; - } - } - else - { - return RS_NOT_INITIALISED; - } -} - void CServer::CreateOtherMuteStateChanged ( const int iCurChanID, const int iOtherChanID, const bool bIsMuted ) @@ -1541,6 +1529,14 @@ void CServer::GetConCliParam ( CVector& vecHostAddresses, } } +void CServer::SetEnableRecording ( bool bNewEnableRecording ) +{ + JamController.SetEnableRecording ( bNewEnableRecording, IsRunning() ); + + // send recording state message - doesn't hurt + CreateAndSendRecorderStateForAllConChannels(); +} + void CServer::StartStatusHTMLFileWriting ( const QString& strNewFileName, const QString& strNewServerNameWithPort ) { @@ -1590,89 +1586,19 @@ void CServer::WriteHTMLChannelList() streamFileOut << "" << endl; } -void CServer::RequestNewRecording() +void CServer::CreateAndSendRecorderStateForAllConChannels() { - if ( bRecorderInitialised && bEnableRecording ) + // get recorder state + ERecorderState eRecorderState = JamController.GetRecorderState(); + + // now send recorder state to all connected clients + for ( int i = 0; i < iMaxNumChannels; i++ ) { - emit RestartRecorder(); - } - - // send recording state message - doesn't hurt - CreateAndSendRecorderStateForAllConChannels(); -} - -void CServer::SetEnableRecording ( bool bNewEnableRecording ) -{ - if ( bRecorderInitialised ) - { - // note that this block executes regardless of whether - // what appears to be a change is being applied, to ensure - // the requested state is the result - bEnableRecording = bNewEnableRecording; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) -// TODO we should use the ConsoleWriterFactory() instead of qInfo() - qInfo() << "Recording state" << ( bEnableRecording ? "enabled" : "disabled" ); -#endif - - if ( !bEnableRecording ) + if ( vecChannels[i].IsConnected() ) { - emit StopRecorder(); + // send message + vecChannels[i].CreateRecorderStateMes ( eRecorderState ); } - else if ( !IsRunning() ) - { - // This dirty hack is for the GUI. It doesn't care. - emit StopRecorder(); - } - } - - // send recording state message - CreateAndSendRecorderStateForAllConChannels(); -} - -void CServer::SetRecordingDir ( QString newRecordingDir ) -{ - if ( bRecorderInitialised ) - { - // We have a thread and we want to start a new one. - // We only want one running. - // This could take time, unfortunately. - // Hopefully changing recording directory will NOT happen during a long jam... - emit EndRecorderThread(); - thJamRecorder.wait(); - } - - if ( !newRecordingDir.isEmpty() ) - { - pJamRecorder = new recorder::CJamRecorder ( newRecordingDir ); - strRecorderErrMsg = pJamRecorder->Init ( this, iServerFrameSizeSamples ); - bRecorderInitialised = ( strRecorderErrMsg == QString::null ); - bEnableRecording = bRecorderInitialised; - } - else - { - // This is the only time this is ever true - UI needs to handle it - strRecorderErrMsg = QString::null; - bRecorderInitialised = false; - bEnableRecording = false; - } - if ( bRecorderInitialised ) - { - strRecordingDir = newRecordingDir; - pJamRecorder->moveToThread ( &thJamRecorder ); - thJamRecorder.setObjectName ( "Jamulus::JamRecorder" ); - - QObject::connect ( &thJamRecorder, &QThread::finished, - pJamRecorder, &QObject::deleteLater ); - - QObject::connect ( pJamRecorder, &recorder::CJamRecorder::RecordingSessionStarted, - this, &CServer::RecordingSessionStarted ); - - thJamRecorder.start(); - } - else - { - strRecordingDir = ""; } } diff --git a/src/server.h b/src/server.h index 10f84df5..1cf1c40b 100755 --- a/src/server.h +++ b/src/server.h @@ -43,8 +43,7 @@ #include "util.h" #include "serverlogging.h" #include "serverlist.h" -#include "recorder/jamrecorder.h" - +#include "recorder/jamcontroller.h" /* Definitions ****************************************************************/ // no valid channel number @@ -199,13 +198,20 @@ public: CVector& veciJitBufNumFrames, CVector& veciNetwFrameSizeFact ); - bool GetRecorderInitialised() { return bRecorderInitialised; } - QString GetRecorderErrMsg() { return strRecorderErrMsg; } - bool GetRecordingEnabled() { return bEnableRecording; } - void RequestNewRecording(); + // Jam recorder ------------------------------------------------------------ + bool GetRecorderInitialised() { return JamController.GetRecorderInitialised(); } + QString GetRecorderErrMsg() { return JamController.GetRecorderErrMsg(); } + bool GetRecordingEnabled() { return JamController.GetRecordingEnabled(); } + void RequestNewRecording() { JamController.RequestNewRecording(); } + void SetEnableRecording ( bool bNewEnableRecording ); - QString GetRecordingDir() { return strRecordingDir; } - void SetRecordingDir( QString newRecordingDir ); + + QString GetRecordingDir() { return JamController.GetRecordingDir(); } + + void SetRecordingDir( QString newRecordingDir ) + { JamController.SetRecordingDir ( newRecordingDir, iServerFrameSizeSamples ); } + + virtual void CreateAndSendRecorderStateForAllConChannels(); // Server list management -------------------------------------------------- void UpdateServerList() { ServerListManager.Update(); } @@ -277,10 +283,6 @@ protected: virtual void CreateAndSendChatTextForAllConChannels ( const int iCurChanID, const QString& strChatText ); - virtual void CreateAndSendRecorderStateForAllConChannels(); - - ERecorderState GetRecorderState(); - virtual void CreateOtherMuteStateChanged ( const int iCurChanID, const int iOtherChanID, const bool bIsMuted ); @@ -366,19 +368,14 @@ protected: QString strServerHTMLFileListName; QString strServerNameWithPort; - // recording thread - bool bRecorderInitialised; - bool bEnableRecording; - QThread thJamRecorder; - recorder::CJamRecorder* pJamRecorder; - QString strRecorderErrMsg; - QString strRecordingDir; - CHighPrecisionTimer HighPrecisionTimer; // server list CServerListManager ServerListManager; + // jam recorder + recorder::CJamController JamController; + // GUI settings bool bAutoRunMinimized; @@ -399,6 +396,8 @@ signals: const CHostAddress RecHostAddr, const int iNumAudChan, const CVector vecsData ); + + // pass through from jam controller void RestartRecorder(); void StopRecorder(); void RecordingSessionStarted ( QString sessionDir ); @@ -480,3 +479,5 @@ public slots: void OnHandledSignal ( int sigNum ); }; + +Q_DECLARE_METATYPE(CVector)