changeset 235:1fcee2a1c03e sonification

Add queueExampleNote method to AudioCallbackPlaySource, providing a way to add a note to a special model which is mixed in outside of the normal playback flow. Currently mixing & generation happen in the play thread, which doesn't work very well -- buffer pending.
author Chris Cannam
date Fri, 24 Jun 2011 16:44:02 +0100
parents a98f1638c5ec
children 4d1501b27075
files audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/AudioGenerator.cpp audioio/AudioGenerator.h
diffstat 4 files changed, 132 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp	Fri Jun 24 15:39:00 2011 +0100
+++ b/audioio/AudioCallbackPlaySource.cpp	Fri Jun 24 16:44:02 2011 +0100
@@ -24,7 +24,9 @@
 #include "data/model/DenseTimeValueModel.h"
 #include "data/model/WaveFileModel.h"
 #include "data/model/SparseOneDimensionalModel.h"
+#include "data/model/NoteModel.h"
 #include "plugin/RealTimePluginInstance.h"
+#include "base/Debug.h"
 
 #include "AudioCallbackPlayTarget.h"
 
@@ -69,6 +71,8 @@
     m_auditioningPluginBypassed(false),
     m_playStartFrame(0),
     m_playStartFramePassed(false),
+    m_exampleNotes(0),
+    m_examplePlaybackFrame(0),
     m_timeStretcher(0),
     m_monoStretcher(0),
     m_stretchRatio(1.0),
@@ -115,6 +119,8 @@
     }
 
     clearModels();
+
+    delete m_exampleNotes;
     
     if (m_readBuffers != m_writeBuffers) {
 	delete m_readBuffers;
@@ -990,6 +996,39 @@
 }
 
 void
+AudioCallbackPlaySource::queueExampleNote(int midiPitch)
+{
+    SVDEBUG << "AudioCallbackPlaySource::queueExampleNote " << midiPitch << endl;
+    
+    size_t rate = getTargetSampleRate();
+    if (!rate) return;
+
+    Note n(m_examplePlaybackFrame,
+           midiPitch,
+           rate / 2, // half a second
+           0,
+           "");
+
+    NoteModel *newNoteModel = 0;
+
+    if (!m_exampleNotes) {
+        // do this outside mutex -- adding the playable and the model
+        // both call back on us into functions that need to lock
+        newNoteModel = new NoteModel(rate, 1, false);
+        PlayParameterRepository::getInstance()->addPlayable(newNoteModel);
+        m_audioGenerator->addModel(newNoteModel);
+        m_exampleNotes = newNoteModel;
+    }
+
+    m_mutex.lock();
+    m_exampleNotes->addPoint(n);
+    m_mutex.unlock();
+
+    SVDEBUG << "AudioCallbackPlaySource::queueExampleNote: Added note at frame "
+            << n.frame << endl;
+}
+
+void
 AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s)
 {
     m_audioGenerator->setSoloModelSet(s);
@@ -1062,6 +1101,52 @@
 }
 
 size_t
+AudioCallbackPlaySource::mixExampleModel(size_t count, float **buffer)
+{
+    SVDEBUG << "AudioCallbackPlaySource::mixExampleModel" << endl;
+
+    if (!m_exampleNotes || m_exampleNotes->isEmpty()) {
+        return 0;
+    }
+
+    SVDEBUG << "AudioCallbackPlaySource::mixExampleModel: Model non-empty; m_examplePlaybackFrame is " << m_examplePlaybackFrame << " and count " << count << endl;
+
+    QMutexLocker locker(&m_mutex);
+
+    size_t n = 0;
+
+    n = m_audioGenerator->mixModel(m_exampleNotes,
+                                   m_examplePlaybackFrame,
+                                   count,
+                                   buffer,
+                                   0,
+                                   0);
+
+    m_examplePlaybackFrame += n;
+
+    // prune notes that have finished
+    while (1) {
+        const NoteModel::PointList &points = m_exampleNotes->getPoints();
+        if (!points.empty()) {
+            NoteModel::Point p(*points.begin());
+            if (p.frame + p.duration < m_examplePlaybackFrame) {
+                m_exampleNotes->deletePoint(p);
+                continue;
+            }
+        }
+        break;
+    }
+
+    SVDEBUG << "AudioCallbackPlaySource::mixExampleModel: done, got "
+            << n << " frames for new m_examplePlaybackFrame of "
+            << m_examplePlaybackFrame << ", "
+            << m_exampleNotes->getPoints().size() << " queued notes remain"
+            << endl;
+
+    return n;
+}
+
+size_t
 AudioCallbackPlaySource::getSourceSamples(size_t ucount, float **buffer)
 {
     int count = ucount;
@@ -1075,7 +1160,7 @@
 		buffer[ch][i] = 0.0;
 	    }
 	}
-	return 0;
+	return mixExampleModel(ucount, buffer);
     }
 
 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
@@ -1180,6 +1265,8 @@
 #endif
 
         m_condition.wakeAll();
+        
+        (void)mixExampleModel(got, buffer);
 
 	return got;
     }
@@ -1273,6 +1360,8 @@
 
     m_condition.wakeAll();
 
+    (void)mixExampleModel(count, buffer);
+
     return count;
 }
 
--- a/audioio/AudioCallbackPlaySource.h	Fri Jun 24 15:39:00 2011 +0100
+++ b/audioio/AudioCallbackPlaySource.h	Fri Jun 24 16:44:02 2011 +0100
@@ -38,6 +38,7 @@
 }
 
 class Model;
+class NoteModel;
 class ViewManagerBase;
 class AudioGenerator;
 class PlayParameters;
@@ -227,6 +228,12 @@
     void setAuditioningEffect(Auditionable *plugin);
 
     /**
+     * Request spontaneous playback of a single short note of the
+     * given pitch.
+     */
+    void queueExampleNote(int midiPitch);
+
+    /**
      * Specify that only the given set of models should be played.
      */
     void setSoloModelSet(std::set<Model *>s);
@@ -305,6 +312,8 @@
     size_t                            m_playStartFrame;
     bool                              m_playStartFramePassed;
     RealTime                          m_playStartedAt;
+    NoteModel                        *m_exampleNotes;
+    size_t                            m_examplePlaybackFrame;
 
     RingBuffer<float> *getWriteRingBuffer(size_t c) {
 	if (m_writeBuffers && c < m_writeBuffers->size()) {
@@ -345,6 +354,11 @@
     // frame argument passed in, in the case of looping).
     size_t mixModels(size_t &frame, size_t count, float **buffers);
 
+    // Called from getSourceSamples, thus in play thread rather than
+    // fill thread.  Return the number of frames written, which will
+    // be count or fewer.
+    size_t mixExampleModel(size_t count, float **buffers);
+
     // Called from getSourceSamples.
     void applyAuditioningEffect(size_t count, float **buffers);
 
--- a/audioio/AudioGenerator.cpp	Fri Jun 24 15:39:00 2011 +0100
+++ b/audioio/AudioGenerator.cpp	Fri Jun 24 16:44:02 2011 +0100
@@ -501,7 +501,8 @@
 AudioGenerator::Notes
 AudioGenerator::getNotesFromModel(Model *model,
                                   size_t startFrame,
-                                  size_t frameCount)
+                                  size_t frameCount,
+                                  size_t latency)
 {
     Notes notes;
 
@@ -516,11 +517,17 @@
     if (sodm) {
 
 	SparseOneDimensionalModel::PointList points =
-	    sodm->getPoints(startFrame, startFrame + frameCount);
+	    sodm->getPoints(startFrame + latency,
+                            startFrame + frameCount + latency);
         
 	for (SparseOneDimensionalModel::PointList::iterator pli =
 		 points.begin(); pli != points.end(); ++pli) {
-            n.frame = pli->frame;
+            size_t frame = pli->frame;
+            if (frame > latency) frame -= latency;
+            if (frame < startFrame || frame >= startFrame + frameCount) {
+                continue;
+            }
+            n.frame = frame;
             notes.push_back(n);
         }
     }
@@ -531,12 +538,19 @@
     if (nm) {
 
 	NoteModel::PointList points =
-	    nm->getPoints(startFrame, startFrame + frameCount);
+	    nm->getPoints(startFrame + latency,
+                          startFrame + frameCount + latency);
         
 	for (NoteModel::PointList::iterator pli =
 		 points.begin(); pli != points.end(); ++pli) {
 
-            n.frame = pli->frame;
+            size_t frame = pli->frame;
+            if (frame > latency) frame -= latency;
+            if (frame < startFrame || frame >= startFrame + frameCount) {
+                continue;
+            }
+
+            n.frame = frame;
             n.duration = pli->duration;
             if (n.duration == 1) n.duration = m_sourceSampleRate / 20;
 
@@ -565,7 +579,12 @@
                                size_t /* fadeOut */)
 {
     RealTimePluginInstance *plugin = m_synthMap[model];
-    if (!plugin) return 0;
+    if (!plugin) {
+        SVDEBUG << "AudioGenerator::mixSparseModel: No plugin" << endl;
+        return 0;
+    }
+
+    SVDEBUG << "AudioGenerator::mixSparseModel: Have plugin" << endl;
 
     size_t latency = plugin->getLatency();
     size_t blocks = frames / m_pluginBlockSize;
@@ -608,17 +627,11 @@
 	    (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
 
         Notes notes = getNotesFromModel
-            (model, reqStart + latency, m_pluginBlockSize);
+            (model, reqStart, m_pluginBlockSize, latency);
         
         for (Notes::const_iterator ni = notes.begin(); ni != notes.end(); ++ni) {
 
             size_t frame = ni->frame;
-            if (frame > latency) frame -= latency;
-
-            if (frame < reqStart ||
-                frame >= reqStart + m_pluginBlockSize) {
-                continue;
-            }
 
 	    while (noteOffs.begin() != noteOffs.end() &&
 		   noteOffs.begin()->frame <= frame) {
--- a/audioio/AudioGenerator.h	Fri Jun 24 15:39:00 2011 +0100
+++ b/audioio/AudioGenerator.h	Fri Jun 24 16:44:02 2011 +0100
@@ -138,7 +138,8 @@
     static void setSampleDir(RealTimePluginInstance *plugin);
 
     virtual Notes getNotesFromModel
-    (Model *model, size_t startFrame, size_t frameCount);
+    (Model *model, size_t startFrame, size_t frameCount, 
+     size_t latency);
 
     virtual size_t mixDenseTimeValueModel
     (DenseTimeValueModel *model, size_t startFrame, size_t frameCount,