diff audio/AudioCallbackRecordTarget.cpp @ 574:b3c35447ef31 3.0-integration

Wire up record monitoring
author Chris Cannam
date Wed, 04 Jan 2017 16:03:12 +0000
parents audio/AudioRecordTarget.cpp@9fb190c6521b
children c2e27ad7f408
line wrap: on
line diff
--- /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 <QDir>
+
+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();
+}
+
+