changeset 308:289d65722123 tonioni

More work on mixing and file i/o for sample stuff
author Chris Cannam
date Tue, 07 Jan 2014 15:50:04 +0000
parents 6eb15c3aee0a
children 71050ffd0141
files audioio/AudioGenerator.cpp audioio/AudioGenerator.h audioio/ClipMixer.cpp audioio/ClipMixer.h framework/SVFileReader.cpp framework/SVFileReader.h
diffstat 6 files changed, 117 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/audioio/AudioGenerator.cpp	Tue Jan 07 13:10:35 2014 +0000
+++ b/audioio/AudioGenerator.cpp	Tue Jan 07 15:50:04 2014 +0000
@@ -145,9 +145,9 @@
     if (mixer) {
         QMutexLocker locker(&m_mutex);
         m_clipMixerMap[model] = mixer;
-        return true;
     }
 }
+
 /*!!!
 void
 AudioGenerator::playPluginConfigurationChanged(const Playable *playable,
@@ -196,78 +196,31 @@
         sampleId = parameters->getPlaySampleId();
     }
 
-    std::cerr << "AudioGenerator::loadPluginFor(" << model << "): sample id = " << sampleId << std::endl;
+    std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << sampleId << std::endl;
 
     if (sampleId == "") {
-        SVDEBUG << "AudioGenerator::loadPluginFor(" << model << "): no sample, skipping" << endl;
+        SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl;
         return 0;
     }
 
+    ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
+                                     m_sourceSampleRate,
+                                     m_processingBlockSize);
 
+    float clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0f); // required
 
+    QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(sampleId);
 
-
-
-    RealTimePluginInstance *plugin = loadPlugin(pluginId, "");
-    if (!plugin) return 0;
-
-    std::cerr << "AudioGenerator::loadPluginFor(" << model << "): loaded plugin "
-              << plugin << std::endl;
-
-    if (configurationXml != "") {
-        PluginXml(plugin).setParametersFromXml(configurationXml);
-        setSampleDir(plugin);
-    }
-
-    configurationXml = PluginXml(plugin).toXmlString();
-
-    if (parameters) {
-        parameters->setPlaySampleId(pluginId);
-        parameters->setPlayPluginConfiguration(configurationXml);
-    }
-
-    return plugin;
-}
-
-RealTimePluginInstance *
-AudioGenerator::loadPlugin(QString pluginId, QString program)
-{
-    RealTimePluginFactory *factory =
-	RealTimePluginFactory::instanceFor(pluginId);
-    
-    if (!factory) {
-	cerr << "Failed to get plugin factory" << endl;
-	return 0;
-    }
-	
-    RealTimePluginInstance *instance =
-	factory->instantiatePlugin
-	(pluginId, 0, 0, m_sourceSampleRate, m_processingBlockSize, m_targetChannelCount);
-
-    if (!instance) {
-	cerr << "Failed to instantiate plugin " << pluginId << endl;
+    if (!mixer->loadClipData(clipPath, clipF0)) {
+        delete mixer;
         return 0;
     }
 
-    setSampleDir(instance);
+    std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): loaded clip " << sampleId << std::endl;
 
-    for (unsigned int i = 0; i < instance->getParameterCount(); ++i) {
-        instance->setParameterValue(i, instance->getParameterDefault(i));
-    }
-    std::string defaultProgram = instance->getProgram(0, 0);
-    if (defaultProgram != "") {
-        cerr << "first selecting default program " << defaultProgram << endl;
-        instance->selectProgram(defaultProgram);
-    }
-    if (program != "") {
-        cerr << "now selecting desired program " << program << endl;
-        instance->selectProgram(program.toStdString());
-    }
-    instance->setIdealChannelCount(m_targetChannelCount); // reset!
+    return mixer;
+}
 
-    return instance;
-}
-*/
 void
 AudioGenerator::removeModel(Model *model)
 {
@@ -277,38 +230,35 @@
 
     QMutexLocker locker(&m_mutex);
 
-    if (m_synthMap.find(sodm) == m_synthMap.end()) return;
+    if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
 
-//!!!    RealTimePluginInstance *instance = m_synthMap[sodm];
-//    m_synthMap.erase(sodm);
-    delete instance;
+    ClipMixer *mixer = m_clipMixerMap[sodm];
+    m_clipMixerMap.erase(sodm);
+    delete mixer;
 }
 
 void
 AudioGenerator::clearModels()
 {
     QMutexLocker locker(&m_mutex);
-/*!!!
-    while (!m_synthMap.empty()) {
-	RealTimePluginInstance *instance = m_synthMap.begin()->second;
-	m_synthMap.erase(m_synthMap.begin());
-	delete instance;
+
+    while (!m_clipMixerMap.empty()) {
+        ClipMixer *mixer = m_clipMixerMap.begin()->second;
+	m_clipMixerMap.erase(m_clipMixerMap.begin());
+	delete mixer;
     }
-*/
 }    
 
 void
 AudioGenerator::reset()
 {
     QMutexLocker locker(&m_mutex);
-/*!!!
-    for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
+
+    for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
 	if (i->second) {
-	    i->second->silence();
-	    i->second->discardEvents();
+	    i->second->reset();
 	}
     }
-*/
 
     m_noteOffs.clear();
 }
@@ -323,11 +273,9 @@
     QMutexLocker locker(&m_mutex);
     m_targetChannelCount = targetChannelCount;
 
-/*!!!
-    for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
-	if (i->second) i->second->setIdealChannelCount(targetChannelCount);
+    for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
+	if (i->second) i->second->setChannelCount(targetChannelCount);
     }
-*/
 }
 
 size_t
@@ -515,10 +463,9 @@
                                       size_t /* fadeIn */,
                                       size_t /* fadeOut */)
 {
-    RealTimePluginInstance *plugin = m_synthMap[model];
-    if (!plugin) return 0;
+    ClipMixer *clipMixer = m_clipMixerMap[model];
+    if (!clipMixer) return 0;
 
-    size_t latency = plugin->getLatency();
     size_t blocks = frames / m_processingBlockSize;
     
     //!!! hang on -- the fact that the audio callback play source's
@@ -538,17 +485,13 @@
 	      << ", blocks " << blocks << endl;
 #endif
 
-    snd_seq_event_t onEv;
-    onEv.type = SND_SEQ_EVENT_NOTEON;
-    onEv.data.note.channel = 0;
+    ClipMixer::NoteStart on;
+    ClipMixer::NoteEnd off;
 
-    snd_seq_event_t offEv;
-    offEv.type = SND_SEQ_EVENT_NOTEOFF;
-    offEv.data.note.channel = 0;
-    offEv.data.note.velocity = 0;
-    
     NoteOffSet &noteOffs = m_noteOffs[model];
 
+    float **bufferIndexes = new float *[m_targetChannelCount];
+
     for (size_t i = 0; i < blocks; ++i) {
 
 	size_t reqStart = startFrame + i * m_processingBlockSize;
@@ -556,105 +499,77 @@
         NoteList notes;
         NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
         if (exportable) {
-            notes = exportable->getNotes(reqStart + latency,
-                                         reqStart + latency + m_processingBlockSize);
+            notes = exportable->getNotes(reqStart,
+                                         reqStart + m_processingBlockSize);
         }
 
-        Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
-	    (startFrame + i * m_processingBlockSize, m_sourceSampleRate);
+        std::vector<ClipMixer::NoteStart> starts;
+        std::vector<ClipMixer::NoteEnd> ends;
 
 	for (NoteList::const_iterator ni = notes.begin();
              ni != notes.end(); ++ni) {
 
 	    size_t noteFrame = ni->start;
 
-	    if (noteFrame >= latency) noteFrame -= latency;
-
 	    if (noteFrame < reqStart ||
 		noteFrame >= reqStart + m_processingBlockSize) continue;
 
 	    while (noteOffs.begin() != noteOffs.end() &&
 		   noteOffs.begin()->frame <= noteFrame) {
 
-                Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		    (noteOffs.begin()->frame, m_sourceSampleRate);
+                size_t eventFrame = noteOffs.begin()->frame;
+                if (eventFrame < reqStart) eventFrame = reqStart;
 
-		offEv.data.note.note = noteOffs.begin()->pitch;
+                off.frameOffset = eventFrame - reqStart;
+                off.frequency = noteOffs.begin()->frequency;
 
 #ifdef DEBUG_AUDIO_GENERATOR
-		cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl;
+		cerr << "mixModel [synthetic]: adding note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
 #endif
 
-		plugin->sendEvent(eventTime, &offEv);
+                ends.push_back(off);
 		noteOffs.erase(noteOffs.begin());
 	    }
 
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(noteFrame, m_sourceSampleRate);
-	    
-            if (ni->isMidiPitchQuantized) {
-                onEv.data.note.note = ni->midiPitch;
-            } else {
-#ifdef DEBUG_AUDIO_GENERATOR
-                cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl;
-#endif
-                onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency);
-            }
-
-            onEv.data.note.velocity = ni->velocity;
-
-	    plugin->sendEvent(eventTime, &onEv);
+            on.frameOffset = noteFrame - reqStart;
+            on.frequency = ni->getFrequency();
+            on.level = float(ni->velocity) / 127.0;
+            on.pan = pan;
 
 #ifdef DEBUG_AUDIO_GENERATOR
-	    cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_processingBlockSize) << ", resulting time " << eventTime << endl;
+	    cout << "mixModel [synthetic]: adding note at frame " << noteFrame << ", frame offset " << on.frameOffset << " frequency " << on.frequency << endl;
 #endif
 	    
+            starts.push_back(on);
 	    noteOffs.insert
-                (NoteOff(onEv.data.note.note, noteFrame + ni->duration));
+                (NoteOff(on.frequency, noteFrame + ni->duration));
 	}
 
 	while (noteOffs.begin() != noteOffs.end() &&
-	       noteOffs.begin()->frame <=
-	       startFrame + i * m_processingBlockSize + m_processingBlockSize) {
+	       noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
 
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(noteOffs.begin()->frame, m_sourceSampleRate);
+            size_t eventFrame = noteOffs.begin()->frame;
+            if (eventFrame < reqStart) eventFrame = reqStart;
 
-	    offEv.data.note.note = noteOffs.begin()->pitch;
+            off.frameOffset = eventFrame - reqStart;
+            off.frequency = noteOffs.begin()->frequency;
 
 #ifdef DEBUG_AUDIO_GENERATOR
-            cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl;
+            cerr << "mixModel [synthetic]: adding leftover note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
 #endif
 
-	    plugin->sendEvent(eventTime, &offEv);
-	    noteOffs.erase(noteOffs.begin());
+            ends.push_back(off);
+            noteOffs.erase(noteOffs.begin());
 	}
-	
-	plugin->run(blockTime);
-	float **outs = plugin->getAudioOutputBuffers();
 
 	for (size_t c = 0; c < m_targetChannelCount; ++c) {
-#ifdef DEBUG_AUDIO_GENERATOR
-	    cout << "mixModel [synthetic]: adding " << m_processingBlockSize << " samples from plugin output " << c << endl;
-#endif
+            bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
+        }
 
-	    size_t sourceChannel = (c % plugin->getAudioOutputCount());
+        clipMixer->mix(bufferIndexes, gain, starts, ends);
+    }
 
-	    float channelGain = gain;
-	    if (pan != 0.0) {
-		if (c == 0) {
-		    if (pan > 0.0) channelGain *= 1.0 - pan;
-		} else {
-		    if (pan < 0.0) channelGain *= pan + 1.0;
-		}
-	    }
-
-	    for (size_t j = 0; j < m_processingBlockSize; ++j) {
-		buffer[c][i * m_processingBlockSize + j] +=
-		    channelGain * outs[sourceChannel][j];
-	    }
-	}
-    }
+    delete[] bufferIndexes;
 
     return got;
 }
--- a/audioio/AudioGenerator.h	Tue Jan 07 13:10:35 2014 +0000
+++ b/audioio/AudioGenerator.h	Tue Jan 07 15:50:04 2014 +0000
@@ -104,9 +104,9 @@
 
     struct NoteOff {
 
-        NoteOff(int _p, size_t _f) : pitch(_p), frame(_f) { }
+        NoteOff(float _freq, size_t _frame) : frequency(_freq), frame(_frame) { }
 
-	int pitch;
+        float frequency;
 	size_t frame;
 
 	struct Comparator {
--- a/audioio/ClipMixer.cpp	Tue Jan 07 13:10:35 2014 +0000
+++ b/audioio/ClipMixer.cpp	Tue Jan 07 15:50:04 2014 +0000
@@ -32,6 +32,12 @@
     delete[] m_clipData;
 }
 
+void
+ClipMixer::setChannelCount(int channels)
+{
+    m_channels = channels;
+}
+
 bool
 ClipMixer::loadClipData(QString path, float f0)
 {
@@ -49,8 +55,8 @@
     info.format = 0;
     file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
     if (!file) {
-	cerr << "ClipMixer::loadClipData: Failed to open file "
-             << path << ": " << sf_strerror(file) << endl;
+	cerr << "ClipMixer::loadClipData: Failed to open file path \""
+             << path << "\": " << sf_strerror(file) << endl;
 	return false;
     }
 
@@ -86,7 +92,14 @@
 }
 
 void
+ClipMixer::reset()
+{
+    //!!!
+}
+
+void
 ClipMixer::mix(float **toBuffers, 
+               float gain,
                std::vector<NoteStart> newNotes, 
                std::vector<NoteEnd> endingNotes)
 {
--- a/audioio/ClipMixer.h	Tue Jan 07 13:10:35 2014 +0000
+++ b/audioio/ClipMixer.h	Tue Jan 07 15:50:04 2014 +0000
@@ -30,13 +30,16 @@
     ClipMixer(int channels, int sampleRate, int blockSize);
     ~ClipMixer();
 
+    void setChannelCount(int channels);
+    
     bool loadClipData(QString clipFilePath, float clipF0);
 
+    void reset(); // discarding any playing notes
+
     //!!! what can we find in common with the NoteData type and
     //!!! AudioGenerator's NoteOff?
 
     struct NoteStart {
-	int id; // unique to match note end
 	int frameOffset; // in current processing block
 	float frequency; // Hz
 	float level; // volume in range (0,1]
@@ -44,11 +47,12 @@
     };
 
     struct NoteEnd {
-	int id; // matching note start
+        float frequency; // matching note start
 	int frameOffset; // in current processing block
     };
 
     void mix(float **toBuffers, 
+             float gain,
 	     std::vector<NoteStart> newNotes, 
 	     std::vector<NoteEnd> endingNotes);
 
--- a/framework/SVFileReader.cpp	Tue Jan 07 13:10:35 2014 +0000
+++ b/framework/SVFileReader.cpp	Tue Jan 07 15:50:04 2014 +0000
@@ -1271,8 +1271,8 @@
         float gain = attributes.value("gain").toFloat(&ok);
         if (ok) parameters->setPlayGain(gain);
         
-        QString pluginId = attributes.value("pluginId");
-        if (pluginId != "") parameters->setPlayPluginId(pluginId);
+        QString sampleId = attributes.value("sampleId");
+        if (sampleId != "") parameters->setPlaySampleId(sampleId);
         
         m_currentPlayParameters = parameters;
 
@@ -1291,17 +1291,26 @@
 bool
 SVFileReader::readPlugin(const QXmlAttributes &attributes)
 {
-    if (m_currentDerivedModelId < 0 && !m_currentPlayParameters) {
+    if (m_currentDerivedModelId >= 0) {
+        return readPluginForTransform(attributes);
+    } else if (m_currentPlayParameters) {
+        return readPluginForPlayback(attributes);
+    } else {
         cerr << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << endl;
         return false;
     }
+}
 
-    if (!m_currentPlayParameters && m_currentTransformIsNewStyle) {
+bool
+SVFileReader::readPluginForTransform(const QXmlAttributes &attributes)
+{
+    if (m_currentTransformIsNewStyle) {
+        // Not needed, we have the transform element instead
         return true;
     }
 
     QString configurationXml = "<plugin";
-    
+
     for (int i = 0; i < attributes.length(); ++i) {
         configurationXml += QString(" %1=\"%2\"")
             .arg(attributes.qName(i))
@@ -1310,12 +1319,21 @@
 
     configurationXml += "/>";
 
-    if (m_currentPlayParameters) {
-        m_currentPlayParameters->setPlayPluginConfiguration(configurationXml);
-    } else {
-        TransformFactory::getInstance()->
-            setParametersFromPluginConfigurationXml(m_currentTransform,
-                                                    configurationXml);
+    TransformFactory::getInstance()->
+        setParametersFromPluginConfigurationXml(m_currentTransform,
+                                                configurationXml);
+    return true;
+}
+
+bool
+SVFileReader::readPluginForPlayback(const QXmlAttributes &attributes)
+{
+    // Obsolete but supported for compatibility
+
+    QString ident = attributes.value("identifier");
+    if (ident == "sample_player") {
+        QString sampleId = attributes.value("program");
+        if (sampleId != "") m_currentPlayParameters->setPlaySampleId(sampleId);
     }
 
     return true;
--- a/framework/SVFileReader.h	Tue Jan 07 13:10:35 2014 +0000
+++ b/framework/SVFileReader.h	Tue Jan 07 15:50:04 2014 +0000
@@ -222,6 +222,8 @@
     bool readDerivation(const QXmlAttributes &);
     bool readPlayParameters(const QXmlAttributes &);
     bool readPlugin(const QXmlAttributes &);
+    bool readPluginForTransform(const QXmlAttributes &);
+    bool readPluginForPlayback(const QXmlAttributes &);
     bool readTransform(const QXmlAttributes &);
     bool readParameter(const QXmlAttributes &);
     bool readSelection(const QXmlAttributes &);