# HG changeset patch # User Chris Cannam # Date 1308930242 -3600 # Node ID 1fcee2a1c03e66e56a82847ba3459996972d3e81 # Parent a98f1638c5ec12a59c1c9004bfd83769db7bd5a5 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. diff -r a98f1638c5ec -r 1fcee2a1c03e audioio/AudioCallbackPlaySource.cpp --- 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 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; } diff -r a98f1638c5ec -r 1fcee2a1c03e audioio/AudioCallbackPlaySource.h --- 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::sets); @@ -305,6 +312,8 @@ size_t m_playStartFrame; bool m_playStartFramePassed; RealTime m_playStartedAt; + NoteModel *m_exampleNotes; + size_t m_examplePlaybackFrame; RingBuffer *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); diff -r a98f1638c5ec -r 1fcee2a1c03e audioio/AudioGenerator.cpp --- 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) { diff -r a98f1638c5ec -r 1fcee2a1c03e audioio/AudioGenerator.h --- 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,