Mercurial > hg > svapp
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()) {