From d5c754b58013ef929903ceea9db3345ee2086f72 Mon Sep 17 00:00:00 2001
From: Peter L Jones <peter@drealm.info>
Date: Fri, 15 May 2020 20:01:57 +0100
Subject: [PATCH 1/3] Expanded signal handling

---
 src/server.cpp        | 29 +++++++++++++++++++++++++----
 src/server.h          |  2 +-
 src/signalhandler.cpp |  8 +++++---
 src/signalhandler.h   |  4 ++--
 4 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/src/server.cpp b/src/server.cpp
index 87af18bb..b6c7f3b2 100755
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -471,8 +471,8 @@ CServer::CServer ( const int          iNewMaxNumChan,
         this, SLOT ( OnAboutToQuit() ) );
 
     QObject::connect ( pSignalHandler,
-        SIGNAL ( ShutdownSignal ( int ) ),
-        this, SLOT ( OnShutdown ( int ) ) );
+        SIGNAL ( HandledSignal ( int ) ),
+        this, SLOT ( OnHandledSignal ( int ) ) );
 
     connectChannelSignalsToServerSlots<MAX_NUM_CHANNELS>();
 
@@ -665,10 +665,31 @@ void CServer::OnAboutToQuit()
     }
 }
 
-void CServer::OnShutdown ( int )
+void CServer::OnHandledSignal ( int sigNum )
 {
-    // This should trigger OnAboutToQuit
+#ifdef _WIN32
+
+    // Windows does not actually get OnHandledSignal triggered
     QCoreApplication::instance()->exit();
+    Q_UNUSED ( sigNum )
+
+#else
+
+    switch ( sigNum )
+    {
+
+    case SIGINT:
+    case SIGTERM:
+        // This should trigger OnAboutToQuit
+        QCoreApplication::instance()->exit();
+        break;
+
+    default:
+        break;
+
+    }
+
+#endif
 }
 
 void CServer::Start()
diff --git a/src/server.h b/src/server.h
index da38bc35..52633b4a 100755
--- a/src/server.h
+++ b/src/server.h
@@ -441,5 +441,5 @@ public slots:
 
     void OnAboutToQuit();
 
-    void OnShutdown ( int );
+    void OnHandledSignal ( int sigNum );
 };
diff --git a/src/signalhandler.cpp b/src/signalhandler.cpp
index 4c37d018..d0205c9e 100755
--- a/src/signalhandler.cpp
+++ b/src/signalhandler.cpp
@@ -75,7 +75,7 @@ CSignalHandler* CSignalHandler::getSingletonP() { return singleton; }
 
 bool CSignalHandler::emitSignal ( int sigNum )
 {
-    return QMetaObject::invokeMethod( singleton, "ShutdownSignal", Qt::QueuedConnection, Q_ARG( int, sigNum ) );
+    return QMetaObject::invokeMethod( singleton, "HandledSignal", Qt::QueuedConnection, Q_ARG( int, sigNum ) );
 }
 
 #ifndef _WIN32
@@ -124,11 +124,11 @@ QReadWriteLock* CSignalWin::getLock() const
     return &lock;
 }
 
-BOOL WINAPI CSignalWin::signalHandler ( _In_ DWORD sigNum )
+BOOL WINAPI CSignalWin::signalHandler ( _In_ DWORD )
 {
     auto self = getSelf<CSignalWin>();
     QReadLocker lock ( self->getLock() );
-    return self->pSignalHandler->emitSignal ( static_cast<int>( sigNum ) );
+    return self->pSignalHandler->emitSignal ( -1 );
 }
 
 #else
@@ -145,12 +145,14 @@ CSignalUnix::CSignalUnix ( CSignalHandler* nPSignalHandler ) :
 
         socketNotifier->setEnabled ( true );
 
+        setSignalHandled ( SIGUSR1, true );
         setSignalHandled ( SIGINT, true );
         setSignalHandled ( SIGTERM, true );
     }
 }
 
 CSignalUnix::~CSignalUnix() {
+    setSignalHandled ( SIGUSR1, false );
     setSignalHandled ( SIGINT, false );
     setSignalHandled ( SIGTERM, false );
 }
diff --git a/src/signalhandler.h b/src/signalhandler.h
index 1263ee6a..59409d13 100755
--- a/src/signalhandler.h
+++ b/src/signalhandler.h
@@ -104,7 +104,7 @@ public slots:
 #endif
 
 signals:
-    void ShutdownSignal ( int sigNum );
+    void HandledSignal ( int sigNum );
 
 private:
     QScopedPointer<CSignalBase> pSignalBase;
@@ -153,7 +153,7 @@ public:
 private:
     mutable QReadWriteLock lock;
 
-    static BOOL WINAPI signalHandler ( _In_ DWORD sigNum );
+    static BOOL WINAPI signalHandler ( _In_ DWORD );
 };
 
 #else

From 376ce88f76a4c3253951a8132e2f9246b0375d3a Mon Sep 17 00:00:00 2001
From: Peter L Jones <peter@drealm.info>
Date: Fri, 15 May 2020 19:50:41 +0100
Subject: [PATCH 2/3] Recorder support for session cut

---
 src/recorder/jamrecorder.cpp | 51 ++++++++++++++++++++++++++++--------
 src/recorder/jamrecorder.h   | 42 +++++++++++++++++++----------
 2 files changed, 68 insertions(+), 25 deletions(-)

diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp
index 2c817919..7719bd6d 100755
--- a/src/recorder/jamrecorder.cpp
+++ b/src/recorder/jamrecorder.cpp
@@ -322,13 +322,12 @@ void CJamRecorder::Init( const CServer* server,
                       this, SLOT( OnDisconnected ( int ) ),
                       Qt::ConnectionType::QueuedConnection );
 
-    qRegisterMetaType<CVector<int16_t>>();
+    qRegisterMetaType<CVector<int16_t>> ( "CVector<int16_t>" );
     QObject::connect( (const QObject *)server, SIGNAL ( AudioFrame( const int, const QString, const CHostAddress, const int, const CVector<int16_t> ) ),
                       this, SLOT(  OnFrame (const int, const QString, const CHostAddress, const int, const CVector<int16_t> ) ),
                       Qt::ConnectionType::QueuedConnection );
 
-    QObject::connect( QCoreApplication::instance(),
-                      SIGNAL ( aboutToQuit() ),
+    QObject::connect( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ),
                       this, SLOT( OnAboutToQuit() ) );
 
     iServerFrameSizeSamples = _iServerFrameSizeSamples;
@@ -338,11 +337,10 @@ void CJamRecorder::Init( const CServer* server,
     thisThread->start();
 }
 
-
 /**
- * @brief CJamRecorder::OnStart Start up tasks when the first client connects
+ * @brief CJamRecorder::Start Start up tasks for a new session
  */
-void CJamRecorder::OnStart() {
+void CJamRecorder::Start() {
     // Ensure any previous cleaning up has been done.
     OnEnd();
 
@@ -350,11 +348,17 @@ void CJamRecorder::OnStart() {
     isRecording = true;
 }
 
+
 /**
  * @brief CJamRecorder::OnEnd Finalise the recording and emit the Reaper RPP file
+ *
+ * Emits RecordingSessionEnded with the Reaper project file name,
+ * or null if was not recording or a problem occurs
  */
 void CJamRecorder::OnEnd()
 {
+    QString reaperProjectFileName = QString::Null();
+
     if ( isRecording )
     {
         isRecording = false;
@@ -366,21 +370,46 @@ void CJamRecorder::OnEnd()
         if (fi.exists())
         {
             qWarning() << "CJamRecorder::OnEnd():" << fi.absolutePath() << "exists and will not be overwritten.";
+            reaperProjectFileName = QString::Null();
         }
         else
         {
             QFile outf (reaperProjectFileName);
-            outf.open(QFile::WriteOnly);
-            QTextStream out(&outf);
-            out << CReaperProject( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << endl;
-            qDebug() << "Session RPP:" << reaperProjectFileName;
+            if ( outf.open(QFile::WriteOnly) )
+            {
+                QTextStream out(&outf);
+                out << CReaperProject( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << endl;
+                qDebug() << "Session RPP:" << reaperProjectFileName;
+            }
+            else
+            {
+                qWarning() << "CJamRecorder::OnEnd():" << fi.absolutePath() << "could not be created, no RPP written.";
+                reaperProjectFileName = QString::Null();
+            }
         }
 
         delete currentSession;
         currentSession = nullptr;
     }
+
+    emit RecordingSessionEnded ( reaperProjectFileName );
 }
 
+/**
+ * @brief CJamRecorder::OnTriggerSession End one session and start a new one
+ */
+void CJamRecorder::OnTriggerSession()
+{
+    // This should magically get everything right...
+    if ( isRecording )
+    {
+        Start();
+    }
+}
+
+/**
+ * @brief CJamRecorder::OnAboutToQuit End any recording and exit thread
+ */
 void CJamRecorder::OnAboutToQuit()
 {
     OnEnd();
@@ -452,7 +481,7 @@ void CJamRecorder::OnFrame(const int iChID, const QString name, const CHostAddre
     // Make sure we are ready
     if ( !isRecording )
     {
-        OnStart();
+        Start();
     }
 
     currentSession->Frame( iChID, name, address, numAudioChannels, data, iServerFrameSizeSamples );
diff --git a/src/recorder/jamrecorder.h b/src/recorder/jamrecorder.h
index eb5a0581..2bc128db 100755
--- a/src/recorder/jamrecorder.h
+++ b/src/recorder/jamrecorder.h
@@ -143,21 +143,44 @@ public:
     {
     }
 
+    /**
+     * @brief Create recording directory, if necessary, and connect signal handlers
+     * @param server Server object emiting signals
+     */
     void Init( const CServer* server, const int _iServerFrameSizeSamples );
 
+    /**
+     * @brief SessionDirToReaper Method that allows an RPP file to be recreated
+     * @param strSessionDirName Where the session wave files are
+     * @param serverFrameSizeSamples What the server frame size was for the session
+     */
     static void SessionDirToReaper( QString& strSessionDirName, int serverFrameSizeSamples );
 
-public slots:
-    /**
-     * @brief Raised when first client joins the server, triggering a new recording.
-     */
-    void OnStart();
+private:
+    void Start();
 
+    QDir recordBaseDir;
+
+    bool         isRecording;
+    CJamSession* currentSession;
+    int          iServerFrameSizeSamples;
+
+    QThread* thisThread;
+
+signals:
+    void RecordingSessionEnded ( QString sessionDir );
+
+private slots:
     /**
      * @brief Raised when last client leaves the server, ending the recording.
      */
     void OnEnd();
 
+    /**
+     * @brief Raised to end one session and start a new one.
+     */
+    void OnTriggerSession();
+
     /**
      * @brief Raised when application is stopping
      */
@@ -173,15 +196,6 @@ public slots:
      * @brief Raised when a frame of data is available to process
      */
     void OnFrame ( const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data );
-
-private:
-    QDir recordBaseDir;
-
-    bool         isRecording;
-    CJamSession* currentSession;
-    int          iServerFrameSizeSamples;
-
-    QThread* thisThread;
 };
 
 }

From 4bf5176e6aa757e459649744c8b0277625bfed19 Mon Sep 17 00:00:00 2001
From: Peter L Jones <peter@drealm.info>
Date: Fri, 15 May 2020 21:52:13 +0100
Subject: [PATCH 3/3] Request new recording on SIGUSR1

---
 src/recorder/jamrecorder.cpp |  4 ++++
 src/server.cpp               | 12 ++++++++++++
 src/server.h                 |  3 +++
 3 files changed, 19 insertions(+)

diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp
index 7719bd6d..4327ccf2 100755
--- a/src/recorder/jamrecorder.cpp
+++ b/src/recorder/jamrecorder.cpp
@@ -314,6 +314,10 @@ void CJamRecorder::Init( const CServer* server,
         throw std::runtime_error( (recordBaseDir.absolutePath() + " is a directory but cannot be written to").toStdString() );
     }
 
+    QObject::connect( (const QObject *)server, SIGNAL ( RestartRecorder() ),
+                      this, SLOT( OnTriggerSession() ),
+                      Qt::ConnectionType::QueuedConnection );
+
     QObject::connect( (const QObject *)server, SIGNAL ( Stopped() ),
                       this, SLOT( OnEnd() ),
                       Qt::ConnectionType::QueuedConnection );
diff --git a/src/server.cpp b/src/server.cpp
index b6c7f3b2..718875e8 100755
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -678,6 +678,10 @@ void CServer::OnHandledSignal ( int sigNum )
     switch ( sigNum )
     {
 
+    case SIGUSR1:
+        RequestNewRecording();
+        break;
+
     case SIGINT:
     case SIGTERM:
         // This should trigger OnAboutToQuit
@@ -1586,3 +1590,11 @@ void CServer::CreateLevelsForAllConChannels ( const int                        i
         vecLevelsOut[j] = static_cast<uint16_t> ( ceil ( dCurSigLevel ) );
     }
 }
+
+void CServer::RequestNewRecording()
+{
+    if ( bEnableRecording )
+    {
+        emit RestartRecorder();
+    }
+}
diff --git a/src/server.h b/src/server.h
index 52633b4a..475928e4 100755
--- a/src/server.h
+++ b/src/server.h
@@ -285,6 +285,8 @@ protected:
                                           const CVector<CVector<int16_t> > vecvecsData,
                                           CVector<uint16_t>&               vecLevelsOut );
 
+    void RequestNewRecording();
+
     // do not use the vector class since CChannel does not have appropriate
     // copy constructor/operator
     CChannel                   vecChannels[MAX_NUM_CHANNELS];
@@ -364,6 +366,7 @@ signals:
                       const CHostAddress     RecHostAddr,
                       const int              iNumAudChan,
                       const CVector<int16_t> vecsData );
+    void RestartRecorder();
 
 public slots:
     void OnTimer();