changeset 618:636a5908cf81 import-audio-data

Merge from default branch
author Chris Cannam
date Tue, 04 Sep 2018 13:19:43 +0100
parents 6cdf2afebfd7 (current diff) e98a42e94d90 (diff)
children 0e95c30d6680
files framework/MainWindowBase.cpp
diffstat 5 files changed, 124 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/audio/AudioCallbackRecordTarget.cpp	Thu Jun 28 15:29:26 2018 +0100
+++ b/audio/AudioCallbackRecordTarget.cpp	Tue Sep 04 13:19:43 2018 +0100
@@ -22,6 +22,10 @@
 #include <QDir>
 #include <QTimer>
 
+//#define DEBUG_AUDIO_CALLBACK_RECORD_TARGET 1
+
+static const int recordUpdateTimeout = 200; // ms
+
 AudioCallbackRecordTarget::AudioCallbackRecordTarget(ViewManagerBase *manager,
                                                      QString clientName) :
     m_viewManager(manager),
@@ -138,7 +142,10 @@
 void
 AudioCallbackRecordTarget::updateModel()
 {
-    bool secChanged = false;
+#ifdef DEBUG_AUDIO_CALLBACK_RECORD_TARGET
+    cerr << "AudioCallbackRecordTarget::updateModel" << endl;
+#endif
+    
     sv_frame_t frameToEmit = 0;
 
     int nframes = 0;
@@ -149,9 +156,19 @@
     }
 
     if (nframes == 0) {
+#ifdef DEBUG_AUDIO_CALLBACK_RECORD_TARGET
+        cerr << "AudioCallbackRecordTarget::updateModel: no frames available" << endl;
+#endif 
+        if (m_recording) {
+            QTimer::singleShot(recordUpdateTimeout, this, SLOT(updateModel()));
+        }    
         return;
     }
 
+#ifdef DEBUG_AUDIO_CALLBACK_RECORD_TARGET
+    cerr << "AudioCallbackRecordTarget::updateModel: have " << nframes << " frames" << endl;
+#endif
+
     float **samples = new float *[m_recordChannelCount];
     for (int c = 0; c < m_recordChannelCount; ++c) {
         samples[c] = new float[nframes];
@@ -165,27 +182,14 @@
     }
     delete[] samples;
     
-    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();
-        frameToEmit = m_frameCount;
-    }
-
-    if (secChanged) {
-        emit recordDurationChanged(frameToEmit, m_recordSampleRate);
-    }
+    m_model->updateModel();
+    frameToEmit = m_frameCount;
+    emit recordDurationChanged(frameToEmit, m_recordSampleRate);
 
     if (m_recording) {
-        QTimer::singleShot(1000, this, SLOT(updateModel()));
+        QTimer::singleShot(recordUpdateTimeout, this, SLOT(updateModel()));
     }    
 }
 
@@ -291,7 +295,7 @@
 
     emit recordStatusChanged(true);
 
-    QTimer::singleShot(1000, this, SLOT(updateModel()));
+    QTimer::singleShot(recordUpdateTimeout, this, SLOT(updateModel()));
     
     return m_model;
 }
--- a/audio/AudioGenerator.cpp	Thu Jun 28 15:29:26 2018 +0100
+++ b/audio/AudioGenerator.cpp	Tue Sep 04 13:19:43 2018 +0100
@@ -141,7 +141,7 @@
         ClipMixer *mixer = makeClipMixerFor(model);
         if (mixer) {
             QMutexLocker locker(&m_mutex);
-            m_clipMixerMap[model] = mixer;
+            m_clipMixerMap[model->getId()] = mixer;
             return willPlay;
         }
     }
@@ -150,7 +150,7 @@
         ContinuousSynth *synth = makeSynthFor(model);
         if (synth) {
             QMutexLocker locker(&m_mutex);
-            m_continuousSynthMap[model] = synth;
+            m_continuousSynthMap[model->getId()] = synth;
             return willPlay;
         }
     }
@@ -169,12 +169,14 @@
         return;
     }
 
-    if (m_clipMixerMap.find(model) == m_clipMixerMap.end()) return;
+    if (m_clipMixerMap.find(model->getId()) == m_clipMixerMap.end()) {
+        return;
+    }
 
     ClipMixer *mixer = makeClipMixerFor(model);
     if (mixer) {
         QMutexLocker locker(&m_mutex);
-        m_clipMixerMap[model] = mixer;
+        m_clipMixerMap[model->getId()] = mixer;
     }
 }
 
@@ -279,10 +281,12 @@
 
     QMutexLocker locker(&m_mutex);
 
-    if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
+    if (m_clipMixerMap.find(sodm->getId()) == m_clipMixerMap.end()) {
+        return;
+    }
 
-    ClipMixer *mixer = m_clipMixerMap[sodm];
-    m_clipMixerMap.erase(sodm);
+    ClipMixer *mixer = m_clipMixerMap[sodm->getId()];
+    m_clipMixerMap.erase(sodm->getId());
     delete mixer;
 }
 
@@ -307,7 +311,8 @@
     cerr << "AudioGenerator::reset()" << endl;
 #endif
 
-    for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
+    for (ClipMixerMap::iterator i = m_clipMixerMap.begin();
+         i != m_clipMixerMap.end(); ++i) {
         if (i->second) {
             i->second->reset();
         }
@@ -356,8 +361,10 @@
 }
 
 sv_frame_t
-AudioGenerator::mixModel(Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
-                         float **buffer, sv_frame_t fadeIn, sv_frame_t fadeOut)
+AudioGenerator::mixModel(Model *model,
+                         sv_frame_t startFrame, sv_frame_t frameCount,
+                         float **buffer,
+                         sv_frame_t fadeIn, sv_frame_t fadeOut)
 {
     if (m_sourceSampleRate == 0) {
         cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << endl;
@@ -520,7 +527,7 @@
                              sv_frame_t startFrame, sv_frame_t frames,
                              float **buffer, float gain, float pan)
 {
-    ClipMixer *clipMixer = m_clipMixerMap[model];
+    ClipMixer *clipMixer = m_clipMixerMap[model->getId()];
     if (!clipMixer) return 0;
 
     int blocks = int(frames / m_processingBlockSize);
@@ -548,7 +555,7 @@
     ClipMixer::NoteStart on;
     ClipMixer::NoteEnd off;
 
-    NoteOffSet &noteOffs = m_noteOffs[model];
+    NoteOffSet &noteOffs = m_noteOffs[model->getId()];
 
     float **bufferIndexes = new float *[m_targetChannelCount];
 
@@ -566,6 +573,24 @@
         std::vector<ClipMixer::NoteStart> starts;
         std::vector<ClipMixer::NoteEnd> ends;
 
+        while (noteOffs.begin() != noteOffs.end() &&
+               noteOffs.begin()->onFrame > reqStart) {
+
+            // We must have jumped back in time, as there is a
+            // note-off pending for a note that hasn't begun yet. Emit
+            // the note-off now and discard
+
+            off.frameOffset = 0;
+            off.frequency = noteOffs.begin()->frequency;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+            cerr << "mixModel [clip]: adding rewind-caused note-off at frame offset 0 frequency " << off.frequency << endl;
+#endif
+
+            ends.push_back(off);
+            noteOffs.erase(noteOffs.begin());
+        }
+        
         for (NoteList::const_iterator ni = notes.begin();
              ni != notes.end(); ++ni) {
 
@@ -593,9 +618,9 @@
             }
 
             while (noteOffs.begin() != noteOffs.end() &&
-                   noteOffs.begin()->frame <= noteFrame) {
+                   noteOffs.begin()->offFrame <= noteFrame) {
 
-                sv_frame_t eventFrame = noteOffs.begin()->frame;
+                sv_frame_t eventFrame = noteOffs.begin()->offFrame;
                 if (eventFrame < reqStart) eventFrame = reqStart;
 
                 off.frameOffset = eventFrame - reqStart;
@@ -620,13 +645,14 @@
             
             starts.push_back(on);
             noteOffs.insert
-                (NoteOff(on.frequency, noteFrame + noteDuration));
+                (NoteOff(on.frequency, noteFrame + noteDuration, noteFrame));
         }
 
         while (noteOffs.begin() != noteOffs.end() &&
-               noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
+               noteOffs.begin()->offFrame <=
+               reqStart + m_processingBlockSize) {
 
-            sv_frame_t eventFrame = noteOffs.begin()->frame;
+            sv_frame_t eventFrame = noteOffs.begin()->offFrame;
             if (eventFrame < reqStart) eventFrame = reqStart;
 
             off.frameOffset = eventFrame - reqStart;
@@ -660,7 +686,7 @@
                                         float gain, 
                                         float pan)
 {
-    ContinuousSynth *synth = m_continuousSynthMap[model];
+    ContinuousSynth *synth = m_continuousSynthMap[model->getId()];
     if (!synth) return 0;
 
     // only type we support here at the moment
--- a/audio/AudioGenerator.h	Thu Jun 28 15:29:26 2018 +0100
+++ b/audio/AudioGenerator.h	Tue Sep 04 13:19:43 2018 +0100
@@ -13,10 +13,9 @@
     COPYING included with this distribution for more information.
 */
 
-#ifndef _AUDIO_GENERATOR_H_
-#define _AUDIO_GENERATOR_H_
+#ifndef SV_AUDIO_GENERATOR_H
+#define SV_AUDIO_GENERATOR_H
 
-class Model;
 class NoteModel;
 class FlexiNoteModel;
 class DenseTimeValueModel;
@@ -33,6 +32,7 @@
 #include <vector>
 
 #include "base/BaseTypes.h"
+#include "data/model/Model.h"
 
 class AudioGenerator : public QObject
 {
@@ -81,8 +81,12 @@
     /**
      * Mix a single model into an output buffer.
      */
-    virtual sv_frame_t mixModel(Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
-                            float **buffer, sv_frame_t fadeIn = 0, sv_frame_t fadeOut = 0);
+    virtual sv_frame_t mixModel(Model *model,
+                                sv_frame_t startFrame,
+                                sv_frame_t frameCount,
+                                float **buffer,
+                                sv_frame_t fadeIn = 0,
+                                sv_frame_t fadeOut = 0);
 
     /**
      * Specify that only the given set of models should be played.
@@ -108,25 +112,40 @@
 
     struct NoteOff {
 
-        NoteOff(float _freq, sv_frame_t _frame) : frequency(_freq), frame(_frame) { }
+        NoteOff(float _freq, sv_frame_t _offFrame, sv_frame_t _onFrame) :
+            frequency(_freq), offFrame(_offFrame), onFrame(_onFrame) { }
 
         float frequency;
-        sv_frame_t frame;
+        sv_frame_t offFrame;
+
+        // This is the frame at which the note whose note-off appears
+        // here began. It is used to determine when we should silence
+        // a note because the playhead has jumped back in time - if
+        // the current frame for rendering is earlier than this one,
+        // then we should end and discard the note
+        //
+        sv_frame_t onFrame;
 
         struct Comparator {
             bool operator()(const NoteOff &n1, const NoteOff &n2) const {
-                return n1.frame < n2.frame;
+                if (n1.offFrame != n2.offFrame) {
+                    return n1.offFrame < n2.offFrame;
+                } else if (n1.onFrame != n2.onFrame) {
+                    return n1.onFrame < n2.onFrame;
+                } else {
+                    return n1.frequency < n2.frequency;
+                }
             }
         };
     };
 
 
-    typedef std::map<const Model *, ClipMixer *> ClipMixerMap;
+    typedef std::map<const ModelId, ClipMixer *> ClipMixerMap;
 
     typedef std::multiset<NoteOff, NoteOff::Comparator> NoteOffSet;
-    typedef std::map<const Model *, NoteOffSet> NoteOffMap;
+    typedef std::map<const ModelId, NoteOffSet> NoteOffMap;
 
-    typedef std::map<const Model *, ContinuousSynth *> ContinuousSynthMap;
+    typedef std::map<const ModelId, ContinuousSynth *> ContinuousSynthMap;
 
     QMutex m_mutex;
 
--- a/framework/Document.h	Thu Jun 28 15:29:26 2018 +0100
+++ b/framework/Document.h	Tue Sep 04 13:19:43 2018 +0100
@@ -13,8 +13,8 @@
     COPYING included with this distribution for more information.
 */
 
-#ifndef _DOCUMENT_H_
-#define _DOCUMENT_H_
+#ifndef SV_DOCUMENT_H
+#define SV_DOCUMENT_H
 
 #include "layer/LayerFactory.h"
 #include "transform/Transform.h"
--- a/framework/MainWindowBase.cpp	Thu Jun 28 15:29:26 2018 +0100
+++ b/framework/MainWindowBase.cpp	Tue Sep 04 13:19:43 2018 +0100
@@ -2469,6 +2469,7 @@
             createCallbackIO(m_recordTarget, m_resamplerWrapper,
                              preference, errorString);
         if (m_audioIO) {
+            SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
             m_audioIO->suspend(); // start in suspended state
             m_playSource->setSystemPlaybackTarget(m_audioIO);
         } else {
@@ -2484,6 +2485,7 @@
             createCallbackPlayTarget(m_resamplerWrapper,
                                      preference, errorString);
         if (m_playTarget) {
+            SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
             m_playTarget->suspend(); // start in suspended state
             m_playSource->setSystemPlaybackTarget(m_playTarget);
         }
@@ -2561,7 +2563,15 @@
 void
 MainWindowBase::audioChannelCountIncreased(int)
 {
+    SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
     recreateAudioIO();
+
+    if (m_recordTarget &&
+        m_recordTarget->isRecording() &&
+        m_audioIO) {
+        SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
+        m_audioIO->resume();
+    }
 }
 
 WaveFileModel *
@@ -3039,7 +3049,7 @@
     }
 
     if (!m_audioIO) {
-        cerr << "MainWindowBase::record: about to create audio IO" << endl;
+        SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
         createAudioIO();
     }
 
@@ -3078,7 +3088,7 @@
 
     if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
     
-    SVDEBUG << "MainWindowBase::record: about to resume" << endl;
+    SVCERR << "MainWindowBase::record: about to resume" << endl;
     m_audioIO->resume();
 
     WritableWaveFileModel *model = m_recordTarget->startRecording();
@@ -3092,12 +3102,15 @@
     }
 
     if (!model->isOK()) {
+        SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
         m_recordTarget->stopRecording();
         m_audioIO->suspend();
         if (action) action->setChecked(false);
         delete model;
         return;
     }
+
+    SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
     
     PlayParameterRepository::getInstance()->addPlayable(model);
 
@@ -3111,6 +3124,7 @@
         if (templateName != "") {
             FileOpenStatus tplStatus = openSessionTemplate(templateName);
             if (tplStatus == FileOpenCancelled) {
+                SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
                 m_recordTarget->stopRecording();
                 m_audioIO->suspend();
                 PlayParameterRepository::getInstance()->removePlayable(model);
@@ -3183,7 +3197,7 @@
     updateMenuStates();
     m_recentFiles.addFile(model->getLocation());
     currentPaneChanged(m_paneStack->getCurrentPane());
-
+    
     emit audioFileLoaded();
 }
 
@@ -3424,6 +3438,8 @@
     
     m_playSource->stop();
 
+    SVCERR << "MainWindowBase::stop: suspending" << endl;
+    
     if (m_audioIO) m_audioIO->suspend();
     else if (m_playTarget) m_playTarget->suspend();
     
@@ -4062,13 +4078,18 @@
         QProcess::execute("c:/windows/explorer.exe", args);
 #else
         args << path;
-        QProcess::execute(
+        QProcess process;
+        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+        env.insert("LD_LIBRARY_PATH", "");
+        process.setProcessEnvironment(env);
+        process.start(
 #if defined Q_OS_MAC
             "/usr/bin/open",
 #else
             "/usr/bin/xdg-open",
 #endif
             args);
+        process.waitForFinished();
 #endif
     }
 }