diff audio/AudioCallbackRecordTarget.cpp @ 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 aee03ad6d3f6
children
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();
 }
 
-