# HG changeset patch # User Chris Cannam # Date 1483545792 0 # Node ID b3c35447ef31561c4a77fd0c7ae6106570f593a4 # Parent 9fb190c6521b5d464e88309cc6b2f282a9e4b91b Wire up record monitoring diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioCallbackPlaySource.cpp --- a/audio/AudioCallbackPlaySource.cpp Wed Jan 04 14:22:39 2017 +0000 +++ b/audio/AudioCallbackPlaySource.cpp Wed Jan 04 16:03:12 2017 +0000 @@ -965,8 +965,8 @@ void AudioCallbackPlaySource::setOutputLevels(float left, float right) { - m_outputLeft = left; - m_outputRight = right; + if (left > m_outputLeft) m_outputLeft = left; + if (right > m_outputRight) m_outputRight = right; } bool @@ -974,6 +974,8 @@ { left = m_outputLeft; right = m_outputRight; + m_outputLeft = 0.f; + m_outputRight = 0.f; return true; } diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioCallbackPlaySource.h --- a/audio/AudioCallbackPlaySource.h Wed Jan 04 14:22:39 2017 +0000 +++ b/audio/AudioCallbackPlaySource.h Wed Jan 04 16:03:12 2017 +0000 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef AUDIO_CALLBACK_PLAY_SOURCE_H -#define AUDIO_CALLBACK_PLAY_SOURCE_H +#ifndef SV_AUDIO_CALLBACK_PLAY_SOURCE_H +#define SV_AUDIO_CALLBACK_PLAY_SOURCE_H #include "base/RingBuffer.h" #include "base/AudioPlaySource.h" diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioCallbackRecordTarget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/AudioCallbackRecordTarget.cpp Wed Jan 04 16:03:12 2017 +0000 @@ -0,0 +1,239 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#include "AudioCallbackRecordTarget.h" + +#include "base/ViewManagerBase.h" +#include "base/TempDirectory.h" + +#include "data/model/WritableWaveFileModel.h" + +#include + +AudioCallbackRecordTarget::AudioCallbackRecordTarget(ViewManagerBase *manager, + QString clientName) : + m_viewManager(manager), + m_clientName(clientName.toUtf8().data()), + m_recording(false), + m_recordSampleRate(44100), + m_recordChannelCount(2), + m_frameCount(0), + m_model(0), + m_inputLeft(0.f), + m_inputRight(0.f) +{ + m_viewManager->setAudioRecordTarget(this); + + connect(this, SIGNAL(recordStatusChanged(bool)), + m_viewManager, SLOT(recordStatusChanged(bool))); +} + +AudioCallbackRecordTarget::~AudioCallbackRecordTarget() +{ + QMutexLocker locker(&m_mutex); + m_viewManager->setAudioRecordTarget(0); +} + +int +AudioCallbackRecordTarget::getApplicationSampleRate() const +{ + return 0; // don't care +} + +int +AudioCallbackRecordTarget::getApplicationChannelCount() const +{ + return m_recordChannelCount; +} + +void +AudioCallbackRecordTarget::setSystemRecordBlockSize(int) +{ +} + +void +AudioCallbackRecordTarget::setSystemRecordSampleRate(int n) +{ + m_recordSampleRate = n; +} + +void +AudioCallbackRecordTarget::setSystemRecordLatency(int) +{ +} + +void +AudioCallbackRecordTarget::setSystemRecordChannelCount(int c) +{ + m_recordChannelCount = c; +} + +void +AudioCallbackRecordTarget::putSamples(const float *const *samples, int, int nframes) +{ + bool secChanged = false; + sv_frame_t frameToEmit = 0; + + { + QMutexLocker locker(&m_mutex); //!!! bad here + if (!m_recording) return; + + m_model->addSamples(samples, nframes); + + sv_frame_t priorFrameCount = m_frameCount; + m_frameCount += nframes; + + RealTime priorRT = RealTime::frame2RealTime + (priorFrameCount, m_recordSampleRate); + RealTime postRT = RealTime::frame2RealTime + (m_frameCount, m_recordSampleRate); + + secChanged = (postRT.sec > priorRT.sec); + if (secChanged) { + m_model->updateModel(); //!!! v bad here + frameToEmit = m_frameCount; + } + } + + if (secChanged) { + emit recordDurationChanged(frameToEmit, m_recordSampleRate); + } +} + +void +AudioCallbackRecordTarget::setInputLevels(float left, float right) +{ + if (left > m_inputLeft) m_inputLeft = left; + if (right > m_inputRight) m_inputRight = right; +} + +bool +AudioCallbackRecordTarget::getInputLevels(float &left, float &right) +{ + left = m_inputLeft; + right = m_inputRight; + m_inputLeft = 0.f; + m_inputRight = 0.f; + return true; +} + +void +AudioCallbackRecordTarget::modelAboutToBeDeleted() +{ + QMutexLocker locker(&m_mutex); + if (sender() == m_model) { + m_model = 0; + m_recording = false; + } +} + +QString +AudioCallbackRecordTarget::getRecordContainerFolder() +{ + QDir parent(TempDirectory::getInstance()->getContainingPath()); + QString subdirname("recorded"); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +QString +AudioCallbackRecordTarget::getRecordFolder() +{ + QDir parent(getRecordContainerFolder()); + QDateTime now = QDateTime::currentDateTime(); + QString subdirname = QString("%1").arg(now.toString("yyyyMMdd")); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +WritableWaveFileModel * +AudioCallbackRecordTarget::startRecording() +{ + { + QMutexLocker locker(&m_mutex); + + if (m_recording) { + SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: We are already recording" << endl; + return 0; + } + + m_model = 0; + m_frameCount = 0; + + QString folder = getRecordFolder(); + if (folder == "") return 0; + QDir recordedDir(folder); + + QDateTime now = QDateTime::currentDateTime(); + + // Don't use QDateTime::toString(Qt::ISODate) as the ":" character + // isn't permitted in filenames on Windows + QString nowString = now.toString("yyyyMMdd-HHmmss-zzz"); + + QString filename = tr("recorded-%1.wav").arg(nowString); + QString label = tr("Recorded %1").arg(nowString); + + m_audioFileName = recordedDir.filePath(filename); + + m_model = new WritableWaveFileModel(m_recordSampleRate, + m_recordChannelCount, + m_audioFileName); + + if (!m_model->isOK()) { + SVCERR << "ERROR: AudioCallbackRecordTarget::startRecording: Recording failed" + << endl; + //!!! and throw? + delete m_model; + m_model = 0; + return 0; + } + + m_model->setObjectName(label); + m_recording = true; + } + + emit recordStatusChanged(true); + return m_model; +} + +void +AudioCallbackRecordTarget::stopRecording() +{ + { + QMutexLocker locker(&m_mutex); + if (!m_recording) { + SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: Not recording" << endl; + return; + } + + m_model->writeComplete(); + m_model = 0; + m_recording = false; + } + + emit recordStatusChanged(false); + emit recordCompleted(); +} + + diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioCallbackRecordTarget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/AudioCallbackRecordTarget.h Wed Jan 04 16:03:12 2017 +0000 @@ -0,0 +1,91 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_AUDIO_CALLBACK_RECORD_TARGET_H +#define SV_AUDIO_CALLBACK_RECORD_TARGET_H + +#include "base/AudioRecordTarget.h" + +#include + +#include + +#include +#include + +#include "base/BaseTypes.h" + +class ViewManagerBase; +class WritableWaveFileModel; + +class AudioCallbackRecordTarget : public QObject, + public AudioRecordTarget, + public breakfastquay::ApplicationRecordTarget +{ + Q_OBJECT + +public: + AudioCallbackRecordTarget(ViewManagerBase *, QString clientName); + virtual ~AudioCallbackRecordTarget(); + + virtual std::string getClientName() const override { return m_clientName; } + + virtual int getApplicationSampleRate() const override; + virtual int getApplicationChannelCount() const override; + + virtual void setSystemRecordBlockSize(int) override; + virtual void setSystemRecordSampleRate(int) override; + virtual void setSystemRecordLatency(int) override; + virtual void setSystemRecordChannelCount(int) override; + + virtual void putSamples(const float *const *samples, int nchannels, int nframes) override; + + virtual void setInputLevels(float peakLeft, float peakRight) override; + + virtual void audioProcessingOverload() override { } + + QString getRecordContainerFolder(); + QString getRecordFolder(); + + virtual bool isRecording() const override { return m_recording; } + virtual sv_frame_t getRecordDuration() const override { return m_frameCount; } + + virtual bool getInputLevels(float &left, float &right) override; + + WritableWaveFileModel *startRecording(); // caller takes ownership of model + void stopRecording(); + +signals: + void recordStatusChanged(bool recording); + void recordDurationChanged(sv_frame_t, sv_samplerate_t); // emitted occasionally + void recordCompleted(); + +protected slots: + void modelAboutToBeDeleted(); + +private: + ViewManagerBase *m_viewManager; + std::string m_clientName; + bool m_recording; + sv_samplerate_t m_recordSampleRate; + int m_recordChannelCount; + sv_frame_t m_frameCount; + QString m_audioFileName; + WritableWaveFileModel *m_model; + float m_inputLeft; + float m_inputRight; + QMutex m_mutex; +}; + +#endif diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioRecordTarget.cpp --- a/audio/AudioRecordTarget.cpp Wed Jan 04 14:22:39 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. See the file - COPYING included with this distribution for more information. -*/ - -#include "AudioRecordTarget.h" - -#include "base/ViewManagerBase.h" -#include "base/TempDirectory.h" - -#include "data/model/WritableWaveFileModel.h" - -#include - -AudioRecordTarget::AudioRecordTarget(ViewManagerBase *manager, - QString clientName) : - m_viewManager(manager), - m_clientName(clientName.toUtf8().data()), - m_recording(false), - m_recordSampleRate(44100), - m_recordChannelCount(2), - m_frameCount(0), - m_model(0) -{ -} - -AudioRecordTarget::~AudioRecordTarget() -{ - QMutexLocker locker(&m_mutex); -} - -int -AudioRecordTarget::getApplicationSampleRate() const -{ - return 0; // don't care -} - -int -AudioRecordTarget::getApplicationChannelCount() const -{ - return m_recordChannelCount; -} - -void -AudioRecordTarget::setSystemRecordBlockSize(int) -{ -} - -void -AudioRecordTarget::setSystemRecordSampleRate(int n) -{ - m_recordSampleRate = n; -} - -void -AudioRecordTarget::setSystemRecordLatency(int) -{ -} - -void -AudioRecordTarget::setSystemRecordChannelCount(int c) -{ - m_recordChannelCount = c; -} - -void -AudioRecordTarget::putSamples(const float *const *samples, int, int nframes) -{ - bool secChanged = false; - sv_frame_t frameToEmit = 0; - - { - QMutexLocker locker(&m_mutex); //!!! bad here - if (!m_recording) return; - - m_model->addSamples(samples, nframes); - - sv_frame_t priorFrameCount = m_frameCount; - m_frameCount += nframes; - - RealTime priorRT = RealTime::frame2RealTime - (priorFrameCount, m_recordSampleRate); - RealTime postRT = RealTime::frame2RealTime - (m_frameCount, m_recordSampleRate); - - secChanged = (postRT.sec > priorRT.sec); - if (secChanged) { - m_model->updateModel(); //!!! v bad here - frameToEmit = m_frameCount; - } - } - - if (secChanged) { - emit recordDurationChanged(frameToEmit, m_recordSampleRate); - } -} - -void -AudioRecordTarget::setInputLevels(float left, float right) -{ - cerr << "AudioRecordTarget::setInputLevels(" << left << "," << right << ")" - << endl; -} - -void -AudioRecordTarget::modelAboutToBeDeleted() -{ - QMutexLocker locker(&m_mutex); - if (sender() == m_model) { - m_model = 0; - m_recording = false; - } -} - -QString -AudioRecordTarget::getRecordContainerFolder() -{ - QDir parent(TempDirectory::getInstance()->getContainingPath()); - QString subdirname("recorded"); - - if (!parent.mkpath(subdirname)) { - SVCERR << "ERROR: AudioRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; - return ""; - } else { - return parent.filePath(subdirname); - } -} - -QString -AudioRecordTarget::getRecordFolder() -{ - QDir parent(getRecordContainerFolder()); - QDateTime now = QDateTime::currentDateTime(); - QString subdirname = QString("%1").arg(now.toString("yyyyMMdd")); - - if (!parent.mkpath(subdirname)) { - SVCERR << "ERROR: AudioRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; - return ""; - } else { - return parent.filePath(subdirname); - } -} - -WritableWaveFileModel * -AudioRecordTarget::startRecording() -{ - { - QMutexLocker locker(&m_mutex); - - if (m_recording) { - SVCERR << "WARNING: AudioRecordTarget::startRecording: We are already recording" << endl; - return 0; - } - - m_model = 0; - m_frameCount = 0; - - QString folder = getRecordFolder(); - if (folder == "") return 0; - QDir recordedDir(folder); - - QDateTime now = QDateTime::currentDateTime(); - - // Don't use QDateTime::toString(Qt::ISODate) as the ":" character - // isn't permitted in filenames on Windows - QString nowString = now.toString("yyyyMMdd-HHmmss-zzz"); - - QString filename = tr("recorded-%1.wav").arg(nowString); - QString label = tr("Recorded %1").arg(nowString); - - m_audioFileName = recordedDir.filePath(filename); - - m_model = new WritableWaveFileModel(m_recordSampleRate, - m_recordChannelCount, - m_audioFileName); - - if (!m_model->isOK()) { - SVCERR << "ERROR: AudioRecordTarget::startRecording: Recording failed" - << endl; - //!!! and throw? - delete m_model; - m_model = 0; - return 0; - } - - m_model->setObjectName(label); - m_recording = true; - } - - emit recordStatusChanged(true); - return m_model; -} - -void -AudioRecordTarget::stopRecording() -{ - { - QMutexLocker locker(&m_mutex); - if (!m_recording) { - SVCERR << "WARNING: AudioRecordTarget::startRecording: Not recording" << endl; - return; - } - - m_model->writeComplete(); - m_model = 0; - m_recording = false; - } - - emit recordStatusChanged(false); - emit recordCompleted(); -} - - diff -r 9fb190c6521b -r b3c35447ef31 audio/AudioRecordTarget.h --- a/audio/AudioRecordTarget.h Wed Jan 04 14:22:39 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef AUDIO_RECORD_TARGET_H -#define AUDIO_RECORD_TARGET_H - -#include - -#include - -#include -#include - -#include "base/BaseTypes.h" - -class ViewManagerBase; -class WritableWaveFileModel; - -class AudioRecordTarget : public QObject, - public breakfastquay::ApplicationRecordTarget -{ - Q_OBJECT - -public: - AudioRecordTarget(ViewManagerBase *, QString clientName); - virtual ~AudioRecordTarget(); - - virtual std::string getClientName() const override { return m_clientName; } - - virtual int getApplicationSampleRate() const override; - virtual int getApplicationChannelCount() const override; - - virtual void setSystemRecordBlockSize(int) override; - virtual void setSystemRecordSampleRate(int) override; - virtual void setSystemRecordLatency(int) override; - virtual void setSystemRecordChannelCount(int) override; - - virtual void putSamples(const float *const *samples, int nchannels, int nframes) override; - - virtual void setInputLevels(float peakLeft, float peakRight) override; - - virtual void audioProcessingOverload() override { } - - QString getRecordContainerFolder(); - QString getRecordFolder(); - - bool isRecording() const { return m_recording; } - WritableWaveFileModel *startRecording(); // caller takes ownership - void stopRecording(); - -signals: - void recordStatusChanged(bool recording); - void recordDurationChanged(sv_frame_t, sv_samplerate_t); // emitted occasionally - void recordCompleted(); - -protected slots: - void modelAboutToBeDeleted(); - -private: - ViewManagerBase *m_viewManager; - std::string m_clientName; - bool m_recording; - sv_samplerate_t m_recordSampleRate; - int m_recordChannelCount; - sv_frame_t m_frameCount; - QString m_audioFileName; - WritableWaveFileModel *m_model; - QMutex m_mutex; -}; - -#endif diff -r 9fb190c6521b -r b3c35447ef31 files.pri --- a/files.pri Wed Jan 04 14:22:39 2017 +0000 +++ b/files.pri Wed Jan 04 16:03:12 2017 +0000 @@ -1,7 +1,7 @@ SVAPP_HEADERS += \ audio/AudioCallbackPlaySource.h \ - audio/AudioRecordTarget.h \ + audio/AudioCallbackRecordTarget.h \ audio/AudioGenerator.h \ audio/ClipMixer.h \ audio/ContinuousSynth.h \ @@ -15,7 +15,7 @@ SVAPP_SOURCES += \ audio/AudioCallbackPlaySource.cpp \ - audio/AudioRecordTarget.cpp \ + audio/AudioCallbackRecordTarget.cpp \ audio/AudioGenerator.cpp \ audio/ClipMixer.cpp \ audio/ContinuousSynth.cpp \ diff -r 9fb190c6521b -r b3c35447ef31 framework/MainWindowBase.cpp --- a/framework/MainWindowBase.cpp Wed Jan 04 14:22:39 2017 +0000 +++ b/framework/MainWindowBase.cpp Wed Jan 04 16:03:12 2017 +0000 @@ -48,7 +48,7 @@ #include "widgets/InteractiveFileFinder.h" #include "audio/AudioCallbackPlaySource.h" -#include "audio/AudioRecordTarget.h" +#include "audio/AudioCallbackRecordTarget.h" #include "audio/PlaySpeedRangeMapper.h" #include "data/fileio/DataFileReaderFactory.h" @@ -231,11 +231,12 @@ connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), this, SLOT(paneDeleteButtonClicked(Pane *))); - m_playSource = new AudioCallbackPlaySource(m_viewManager, - QApplication::applicationName()); + m_playSource = new AudioCallbackPlaySource + (m_viewManager, QApplication::applicationName()); + if (m_soundOptions & WithAudioInput) { - m_recordTarget = new AudioRecordTarget(m_viewManager, - QApplication::applicationName()); + m_recordTarget = new AudioCallbackRecordTarget + (m_viewManager, QApplication::applicationName()); connect(m_recordTarget, SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)), this, @@ -251,8 +252,8 @@ connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()), this, SLOT(audioTimeStretchMultiChannelDisabled())); - connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), - this, SLOT(outputLevelsChanged(float, float))); + connect(m_viewManager, SIGNAL(monitoringLevelsChanged(float, float)), + this, SLOT(monitoringLevelsChanged(float, float))); connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)), this, SLOT(playbackFrameChanged(sv_frame_t))); diff -r 9fb190c6521b -r b3c35447ef31 framework/MainWindowBase.h --- a/framework/MainWindowBase.h Wed Jan 04 14:22:39 2017 +0000 +++ b/framework/MainWindowBase.h Wed Jan 04 16:03:12 2017 +0000 @@ -46,7 +46,7 @@ class WaveformLayer; class WaveFileModel; class AudioCallbackPlaySource; -class AudioRecordTarget; +class AudioCallbackRecordTarget; class CommandHistory; class QMenu; class AudioDial; @@ -258,7 +258,7 @@ virtual void globalCentreFrameChanged(sv_frame_t); virtual void viewCentreFrameChanged(View *, sv_frame_t); virtual void viewZoomLevelChanged(View *, int, bool); - virtual void outputLevelsChanged(float, float) = 0; + virtual void monitoringLevelsChanged(float, float) = 0; virtual void recordDurationChanged(sv_frame_t, sv_samplerate_t); virtual void currentPaneChanged(Pane *); @@ -348,7 +348,7 @@ SoundOptions m_soundOptions; AudioCallbackPlaySource *m_playSource; - AudioRecordTarget *m_recordTarget; + AudioCallbackRecordTarget *m_recordTarget; breakfastquay::ResamplerWrapper *m_resamplerWrapper; breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this... breakfastquay::SystemAudioIO *m_audioIO; // ... and this exists