diff --git a/Jamulus.pro b/Jamulus.pro index 2f7fdb58..4955b7e1 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 68fc2ab4..651755b1 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -302,60 +302,43 @@ 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() { - QFileInfo fi(recordBaseDir.absolutePath()); - fi.setCaching(false); + QString errmsg = QString::null; + QFileInfo fi ( recordBaseDir.absolutePath() ); + fi.setCaching ( false ); - if (!fi.exists() && !QDir().mkpath(recordBaseDir.absolutePath())) + 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"; +#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()) { - qCritical() << recordBaseDir.absolutePath() << "exists but is not a directory"; - return false; + 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()) { - qCritical() << recordBaseDir.absolutePath() << "is a directory but cannot be written to"; - return false; + 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 ( 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; - - thisThread = new QThread(); - moveToThread ( thisThread ); - thisThread->start(); - - return true; + return errmsg; } /** @@ -410,7 +393,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 old mode 100755 new mode 100644 index 4bf8fa8f..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 */ - bool Init( const CServer* server, const int _iServerFrameSizeSamples ); + QString Init(); /** * @brief SessionDirToReaper Method that allows an RPP file to be recreated @@ -163,46 +165,40 @@ private: void ReaperProjectFromCurrentSession(); void AudacityLofFromCurrentSession(); - QDir recordBaseDir; - + QDir recordBaseDir; + int iServerFrameSizeSamples; bool isRecording; CJamSession* currentSession; - int iServerFrameSizeSamples; - - QThread* thisThread; signals: void RecordingSessionStarted ( QString sessionDir ); -private slots: +public 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 ); }; } - -Q_DECLARE_METATYPE(int16_t) -Q_DECLARE_METATYPE(CVector) diff --git a/src/server.cpp b/src/server.cpp index b958c8fd..899a6bbe 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -240,9 +240,6 @@ CServer::CServer ( const int iNewMaxNumChan, Socket ( this, iPortNumber ), Logging ( iMaxDaysHistory ), iFrameCount ( 0 ), - JamRecorder ( strRecordingDirName ), - bRecorderInitialised ( false ), - bEnableRecording ( false ), bWriteStatusHTMLFile ( false ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), ServerListManager ( iPortNumber, @@ -406,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 @@ -424,11 +424,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 +475,28 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); - QObject::connect( &JamRecorder, &recorder::CJamRecorder::RecordingSessionStarted, + 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 ); @@ -635,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(); @@ -727,7 +742,7 @@ void CServer::OnHandledSignal ( int sigNum ) break; case SIGUSR2: - SetEnableRecording ( !bEnableRecording ); + SetEnableRecording ( !JamController.GetRecordingEnabled() ); break; case SIGINT: @@ -742,46 +757,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 @@ -953,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? } @@ -1038,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(), @@ -1373,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 ) @@ -1590,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 ) { @@ -1639,6 +1586,22 @@ void CServer::WriteHTMLChannelList() streamFileOut << "" << endl; } +void CServer::CreateAndSendRecorderStateForAllConChannels() +{ + // get recorder state + ERecorderState eRecorderState = JamController.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 ); + } + } +} + void CServer::customEvent ( QEvent* pEvent ) { if ( pEvent->type() == QEvent::User + 11 ) diff --git a/src/server.h b/src/server.h index bb58dc2d..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,11 +198,21 @@ public: CVector& veciJitBufNumFrames, CVector& veciNetwFrameSizeFact ); - bool GetRecorderInitialised() { return bRecorderInitialised; } - 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 JamController.GetRecordingDir(); } + + void SetRecordingDir( QString newRecordingDir ) + { JamController.SetRecordingDir ( newRecordingDir, iServerFrameSizeSamples ); } + + virtual void CreateAndSendRecorderStateForAllConChannels(); + // Server list management -------------------------------------------------- void UpdateServerList() { ServerListManager.Update(); } @@ -274,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 ); @@ -358,11 +363,6 @@ 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; @@ -373,6 +373,9 @@ protected: // server list CServerListManager ServerListManager; + // jam recorder + recorder::CJamController JamController; + // GUI settings bool bAutoRunMinimized; @@ -393,9 +396,12 @@ signals: const CHostAddress RecHostAddr, const int iNumAudChan, const CVector vecsData ); + + // pass through from jam controller void RestartRecorder(); void StopRecorder(); void RecordingSessionStarted ( QString sessionDir ); + void EndRecorderThread(); public slots: void OnTimer(); @@ -473,3 +479,5 @@ public slots: void OnHandledSignal ( int sigNum ); }; + +Q_DECLARE_METATYPE(CVector)