changeset 775:56a81812c131 smoother-recording

Use ModelId for recording model; add mix-to-mono option
author Chris Cannam
date Tue, 16 Jun 2020 15:17:50 +0100
parents 7bded7599874
children
files audio/AudioCallbackRecordTarget.cpp audio/AudioCallbackRecordTarget.h framework/MainWindowBase.cpp
diffstat 3 files changed, 72 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/audio/AudioCallbackRecordTarget.cpp	Wed Jun 03 13:58:29 2020 +0100
+++ b/audio/AudioCallbackRecordTarget.cpp	Tue Jun 16 15:17:50 2020 +0100
@@ -17,6 +17,7 @@
 #include "base/ViewManagerBase.h"
 #include "base/RecordDirectory.h"
 #include "base/Debug.h"
+#include "base/Preferences.h"
 
 #include "data/model/WritableWaveFileModel.h"
 
@@ -34,9 +35,9 @@
     m_clientName(clientName.toUtf8().data()),
     m_recording(false),
     m_recordSampleRate(44100),
-    m_recordChannelCount(2),
+    m_systemRecordChannelCount(2),
+    m_recordMono(false),
     m_frameCount(0),
-    m_model(nullptr),
     m_buffers(nullptr),
     m_bufferCount(0),
     m_inputLeft(0.f),
@@ -71,7 +72,7 @@
 {
     static int bufferSize = 441000;
     
-    int count = m_recordChannelCount;
+    int count = m_systemRecordChannelCount;
 
     if (count > m_bufferCount) {
 
@@ -103,7 +104,9 @@
 int
 AudioCallbackRecordTarget::getApplicationChannelCount() const
 {
-    return m_recordChannelCount;
+    // Pretend to just have as many as the system expects - we do our
+    // own mixing-down optionally in the m_recordMono case
+    return m_systemRecordChannelCount;
 }
 
 void
@@ -114,6 +117,7 @@
 void
 AudioCallbackRecordTarget::setSystemRecordSampleRate(int n)
 {
+    SVCERR << "AudioCallbackRecordTarget: system sample rate is " << n << endl;
     m_recordSampleRate = n;
 }
 
@@ -125,7 +129,8 @@
 void
 AudioCallbackRecordTarget::setSystemRecordChannelCount(int c)
 {
-    m_recordChannelCount = c;
+    SVCERR << "AudioCallbackRecordTarget: system channel count is " << c << endl;
+    m_systemRecordChannelCount = c;
     recreateBuffers();
 }
 
@@ -138,8 +143,8 @@
     if (!m_recording) return;
 
     QMutexLocker locker(&m_bufPtrMutex);
-    if (m_buffers && m_bufferCount >= m_recordChannelCount) {
-        for (int c = 0; c < m_recordChannelCount; ++c) {
+    if (m_buffers && m_bufferCount >= m_systemRecordChannelCount) {
+        for (int c = 0; c < m_systemRecordChannelCount; ++c) {
             m_buffers[c]->write(samples[c], nframes);
         }
     }
@@ -155,7 +160,7 @@
     sv_frame_t frameToEmit = 0;
 
     int nframes = 0;
-    for (int c = 0; c < m_recordChannelCount; ++c) {
+    for (int c = 0; c < m_systemRecordChannelCount; ++c) {
         if (c == 0 || m_buffers[c]->getReadSpace() < nframes) {
             nframes = m_buffers[c]->getReadSpace();
         }
@@ -175,29 +180,36 @@
     cerr << "AudioCallbackRecordTarget::updateModel: have " << nframes << " frames" << endl;
 #endif
 
-    if (!m_model) {
+    auto model = ModelById::getAs<WritableWaveFileModel>(m_modelId);
+    if (!model) {
 #ifdef DEBUG_AUDIO_CALLBACK_RECORD_TARGET
         cerr << "AudioCallbackRecordTarget::updateModel: have no model to update; I am hoping there is a good reason for this" << endl;
 #endif
         return;
     }
 
-    float **samples = new float *[m_recordChannelCount];
-    for (int c = 0; c < m_recordChannelCount; ++c) {
+    float **samples = new float *[m_systemRecordChannelCount];
+    for (int c = 0; c < m_systemRecordChannelCount; ++c) {
         samples[c] = new float[nframes];
         m_buffers[c]->read(samples[c], nframes);
     }
 
-    m_model->addSamples(samples, nframes);
+    if (m_recordMono) {
+        breakfastquay::v_reconfigure_channels_inplace(samples, 1,
+                                                      m_systemRecordChannelCount,
+                                                      nframes);
+    }
+    
+    model->addSamples(samples, nframes);
 
-    for (int c = 0; c < m_recordChannelCount; ++c) {
+    for (int c = 0; c < m_systemRecordChannelCount; ++c) {
         delete[] samples[c];
     }
     delete[] samples;
     
     m_frameCount += nframes;
     
-    m_model->updateModel();
+    model->updateModel();
     frameToEmit = m_frameCount;
     emit recordDurationChanged(frameToEmit, m_recordSampleRate);
 
@@ -226,33 +238,19 @@
     return valid;
 }
 
-void
-AudioCallbackRecordTarget::modelAboutToBeDeleted()
-{
-    if (sender() == m_model) {
-#ifdef DEBUG_AUDIO_CALLBACK_RECORD_TARGET
-        cerr << "AudioCallbackRecordTarget::modelAboutToBeDeleted: taking note" << endl;
-#endif
-        m_model = nullptr;
-        m_recording = false;
-    } else if (m_model) {
-        SVCERR << "WARNING: AudioCallbackRecordTarget::modelAboutToBeDeleted: this is not my model!" << endl;
-    }
-}
-
-WritableWaveFileModel *
+ModelId
 AudioCallbackRecordTarget::startRecording()
 {
     if (m_recording) {
         SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: We are already recording" << endl;
-        return nullptr;
+        return {};
     }
 
-    m_model = nullptr;
+    m_modelId = {};
     m_frameCount = 0;
 
     QString folder = RecordDirectory::getRecordDirectory();
-    if (folder == "") return nullptr;
+    if (folder == "") return {};
     QDir recordedDir(folder);
 
     QDateTime now = QDateTime::currentDateTime();
@@ -266,32 +264,42 @@
 
     m_audioFileName = recordedDir.filePath(filename);
 
-    m_model = new WritableWaveFileModel
+    m_recordMono = Preferences::getInstance()->getRecordMono();
+    
+    int modelChannelCount = m_systemRecordChannelCount;
+    if (m_recordMono) {
+        modelChannelCount = 1;
+    }
+    
+    SVCERR << "AudioCallbackRecordTarget::startRecording: Recording to \""
+           << m_audioFileName << "\", sample rate " << m_recordSampleRate
+           << ", system channel count " << m_systemRecordChannelCount
+           << ", model channel count " << modelChannelCount
+           << " (recordMono = " << m_recordMono << ")" << endl;
+    
+    auto model = std::make_shared<WritableWaveFileModel>
         (m_audioFileName,
          m_recordSampleRate,
-         m_recordChannelCount,
+         modelChannelCount,
          WritableWaveFileModel::Normalisation::None);
 
-    if (!m_model->isOK()) {
+    if (!model->isOK()) {
         SVCERR << "ERROR: AudioCallbackRecordTarget::startRecording: Recording failed"
                << endl;
-        //!!! and throw?
-        delete m_model;
-        m_model = nullptr;
-        return nullptr;
+        m_recording = false;
+        return {};
     }
 
-    connect(m_model, SIGNAL(aboutToBeDeleted()), 
-            this, SLOT(modelAboutToBeDeleted()));
-
-    m_model->setObjectName(label);
+    m_modelId = ModelById::add(model);
+    
+    model->setObjectName(label);
     m_recording = true;
 
     emit recordStatusChanged(true);
 
     QTimer::singleShot(recordUpdateTimeout, this, SLOT(updateModel()));
     
-    return m_model;
+    return m_modelId;
 }
 
 void
@@ -310,11 +318,14 @@
     // buffers should now be up-to-date
     updateModel();
 
-    m_model->writeComplete();
-    m_model = nullptr;
+    auto model = ModelById::getAs<WritableWaveFileModel>(m_modelId);
+    if (model) {
+        model->writeComplete();
+    }
+
+    m_modelId = {};
     
     emit recordStatusChanged(false);
     emit recordCompleted();
 }
 
-
--- a/audio/AudioCallbackRecordTarget.h	Wed Jun 03 13:58:29 2020 +0100
+++ b/audio/AudioCallbackRecordTarget.h	Tue Jun 16 15:17:50 2020 +0100
@@ -16,6 +16,7 @@
 #define SV_AUDIO_CALLBACK_RECORD_TARGET_H
 
 #include "base/AudioRecordTarget.h"
+#include "data/model/Model.h"
 
 #include <bqaudioio/ApplicationRecordTarget.h>
 
@@ -71,9 +72,10 @@
      * last called (i.e. if they are meaningful). Return false if they
      * have not been set (in which case both will be zero).
      */
-     virtual bool getInputLevels(float &left, float &right) override;
+    virtual bool getInputLevels(float &left, float &right) override;
 
-    WritableWaveFileModel *startRecording(); // caller takes ownership of model
+    ModelId startRecording(); // a WritableWaveFileModel; has not been
+                              // added to document yet
     void stopRecording();
 
 signals:
@@ -82,7 +84,6 @@
     void recordCompleted();
 
 protected slots:
-    void modelAboutToBeDeleted();
     void updateModel();
     
 private:
@@ -90,10 +91,11 @@
     std::string m_clientName;
     std::atomic_bool m_recording;
     sv_samplerate_t m_recordSampleRate;
-    int m_recordChannelCount;
+    int m_systemRecordChannelCount;
+    bool m_recordMono;
     sv_frame_t m_frameCount;
     QString m_audioFileName;
-    WritableWaveFileModel *m_model;
+    ModelId m_modelId; // a WritableWaveFileModel
     RingBuffer<float> **m_buffers;
     QMutex m_bufPtrMutex;
     int m_bufferCount;
--- a/framework/MainWindowBase.cpp	Wed Jun 03 13:58:29 2020 +0100
+++ b/framework/MainWindowBase.cpp	Tue Jun 16 15:17:50 2020 +0100
@@ -3407,8 +3407,8 @@
     SVCERR << "MainWindowBase::record: about to resume" << endl;
     m_audioIO->resume();
 
-    WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
-    if (!modelPtr) {
+    ModelId modelId = m_recordTarget->startRecording();
+    if (modelId.isNone()) {
         SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
         QMessageBox::critical
             (this, tr("Recording failed"),
@@ -3417,20 +3417,16 @@
         return;
     }
 
-    if (!modelPtr->isOK()) {
-        SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
-        m_recordTarget->stopRecording();
-        m_audioIO->suspend();
+    auto model = ModelById::getAs<WritableWaveFileModel>(modelId);
+    if (!model) {
+        SVCERR << "ERROR: MainWindowBase::record: Model lost?" << endl;
         if (action) action->setChecked(false);
-        delete modelPtr;
         return;
     }
-
+    
     SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
 
-    QString location = modelPtr->getLocation();
-    
-    auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
+    QString location = model->getLocation();
 
     if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {