changeset 305:9716c75499ef tonioni

Toward using a sample mixer (with arbitrary frequency target) instead of dssi player plugin
author Chris Cannam
date Tue, 07 Jan 2014 10:58:10 +0000
parents b6358ba5ebc6
children 64382d824bfe
files audioio/AudioGenerator.cpp audioio/AudioGenerator.h audioio/ClipMixer.cpp audioio/ClipMixer.h svapp.pro
diffstat 5 files changed, 205 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/audioio/AudioGenerator.cpp	Wed Dec 04 19:40:24 2013 +0000
+++ b/audioio/AudioGenerator.cpp	Tue Jan 07 10:58:10 2014 +0000
@@ -27,12 +27,6 @@
 #include "data/model/SparseOneDimensionalModel.h"
 #include "data/model/NoteData.h"
 
-#include "plugin/RealTimePluginFactory.h"
-#include "plugin/RealTimePluginInstance.h"
-#include "plugin/PluginIdentifier.h"
-#include "plugin/PluginXml.h"
-#include "plugin/api/alsa/seq_event.h"
-
 #include <iostream>
 #include <cmath>
 
@@ -40,7 +34,7 @@
 #include <QFile>
 
 const size_t
-AudioGenerator::m_pluginBlockSize = 2048;
+AudioGenerator::m_processingBlockSize = 2048;
 
 QString
 AudioGenerator::m_sampleDir = "";
@@ -55,14 +49,9 @@
     initialiseSampleDir();
 
     connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginIdChanged(const Playable *, QString)),
+            SIGNAL(playSampleIdChanged(const Playable *, QString)),
             this,
-            SLOT(playPluginIdChanged(const Playable *, QString)));
-
-    connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginConfigurationChanged(const Playable *, QString)),
-            this,
-            SLOT(playPluginConfigurationChanged(const Playable *, QString)));
+            SLOT(playSampleIdChanged(const Playable *, QString)));
 }
 
 AudioGenerator::~AudioGenerator()
@@ -126,38 +115,39 @@
 	    return true;
 	}
     }
-
+/*!!!
     RealTimePluginInstance *plugin = loadPluginFor(model);
     if (plugin) {
         QMutexLocker locker(&m_mutex);
         m_synthMap[model] = plugin;
         return true;
     }
-
+*/
     return false;
 }
 
 void
-AudioGenerator::playPluginIdChanged(const Playable *playable, QString)
+AudioGenerator::playSampleIdChanged(const Playable *playable, QString)
 {
     const Model *model = dynamic_cast<const Model *>(playable);
     if (!model) {
-        cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
+        cerr << "WARNING: AudioGenerator::playSampleIdChanged: playable "
                   << playable << " is not a supported model type"
                   << endl;
         return;
     }
 
     if (m_synthMap.find(model) == m_synthMap.end()) return;
-    
+/*!!!    
     RealTimePluginInstance *plugin = loadPluginFor(model);
     if (plugin) {
         QMutexLocker locker(&m_mutex);
         delete m_synthMap[model];
         m_synthMap[model] = plugin;
     }
+*/
 }
-
+/*!!!
 void
 AudioGenerator::playPluginConfigurationChanged(const Playable *playable,
                                                QString configurationXml)
@@ -166,7 +156,7 @@
 
     const Model *model = dynamic_cast<const Model *>(playable);
     if (!model) {
-        cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
+        cerr << "WARNING: AudioGenerator::playSampleIdChanged: playable "
                   << playable << " is not a supported model type"
                   << endl;
         return;
@@ -202,7 +192,7 @@
     PlayParameters *parameters =
 	PlayParameterRepository::getInstance()->getPlayParameters(playable);
     if (parameters) {
-        pluginId = parameters->getPlayPluginId();
+        pluginId = parameters->getPlaySampleId();
         configurationXml = parameters->getPlayPluginConfiguration();
     }
 
@@ -227,7 +217,7 @@
     configurationXml = PluginXml(plugin).toXmlString();
 
     if (parameters) {
-        parameters->setPlayPluginId(pluginId);
+        parameters->setPlaySampleId(pluginId);
         parameters->setPlayPluginConfiguration(configurationXml);
     }
 
@@ -247,7 +237,7 @@
 	
     RealTimePluginInstance *instance =
 	factory->instantiatePlugin
-	(pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
+	(pluginId, 0, 0, m_sourceSampleRate, m_processingBlockSize, m_targetChannelCount);
 
     if (!instance) {
 	cerr << "Failed to instantiate plugin " << pluginId << endl;
@@ -272,7 +262,7 @@
 
     return instance;
 }
-
+*/
 void
 AudioGenerator::removeModel(Model *model)
 {
@@ -284,8 +274,8 @@
 
     if (m_synthMap.find(sodm) == m_synthMap.end()) return;
 
-    RealTimePluginInstance *instance = m_synthMap[sodm];
-    m_synthMap.erase(sodm);
+//!!!    RealTimePluginInstance *instance = m_synthMap[sodm];
+//    m_synthMap.erase(sodm);
     delete instance;
 }
 
@@ -293,23 +283,27 @@
 AudioGenerator::clearModels()
 {
     QMutexLocker locker(&m_mutex);
+/*!!!
     while (!m_synthMap.empty()) {
 	RealTimePluginInstance *instance = m_synthMap.begin()->second;
 	m_synthMap.erase(m_synthMap.begin());
 	delete instance;
     }
+*/
 }    
 
 void
 AudioGenerator::reset()
 {
     QMutexLocker locker(&m_mutex);
+/*!!!
     for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
 	if (i->second) {
 	    i->second->silence();
 	    i->second->discardEvents();
 	}
     }
+*/
 
     m_noteOffs.clear();
 }
@@ -324,15 +318,17 @@
     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);
     }
+*/
 }
 
 size_t
 AudioGenerator::getBlockSize() const
 {
-    return m_pluginBlockSize;
+    return m_processingBlockSize;
 }
 
 void
@@ -518,7 +514,7 @@
     if (!plugin) return 0;
 
     size_t latency = plugin->getLatency();
-    size_t blocks = frames / m_pluginBlockSize;
+    size_t blocks = frames / m_processingBlockSize;
     
     //!!! hang on -- the fact that the audio callback play source's
     //buffer is a multiple of the plugin's buffer size doesn't mean
@@ -530,7 +526,7 @@
     //callback play source has to use that as a multiple for all the
     //calls to mixModel
 
-    size_t got = blocks * m_pluginBlockSize;
+    size_t got = blocks * m_processingBlockSize;
 
 #ifdef DEBUG_AUDIO_GENERATOR
     cout << "mixModel [synthetic note]: frames " << frames
@@ -550,17 +546,17 @@
 
     for (size_t i = 0; i < blocks; ++i) {
 
-	size_t reqStart = startFrame + i * m_pluginBlockSize;
+	size_t reqStart = startFrame + i * m_processingBlockSize;
 
         NoteList notes;
         NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
         if (exportable) {
             notes = exportable->getNotes(reqStart + latency,
-                                         reqStart + latency + m_pluginBlockSize);
+                                         reqStart + latency + m_processingBlockSize);
         }
 
         Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
-	    (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
+	    (startFrame + i * m_processingBlockSize, m_sourceSampleRate);
 
 	for (NoteList::const_iterator ni = notes.begin();
              ni != notes.end(); ++ni) {
@@ -570,7 +566,7 @@
 	    if (noteFrame >= latency) noteFrame -= latency;
 
 	    if (noteFrame < reqStart ||
-		noteFrame >= reqStart + m_pluginBlockSize) continue;
+		noteFrame >= reqStart + m_processingBlockSize) continue;
 
 	    while (noteOffs.begin() != noteOffs.end() &&
 		   noteOffs.begin()->frame <= noteFrame) {
@@ -605,7 +601,7 @@
 	    plugin->sendEvent(eventTime, &onEv);
 
 #ifdef DEBUG_AUDIO_GENERATOR
-	    cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << endl;
+	    cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_processingBlockSize) << ", resulting time " << eventTime << endl;
 #endif
 	    
 	    noteOffs.insert
@@ -614,7 +610,7 @@
 
 	while (noteOffs.begin() != noteOffs.end() &&
 	       noteOffs.begin()->frame <=
-	       startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
+	       startFrame + i * m_processingBlockSize + m_processingBlockSize) {
 
             Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
 		(noteOffs.begin()->frame, m_sourceSampleRate);
@@ -634,7 +630,7 @@
 
 	for (size_t c = 0; c < m_targetChannelCount; ++c) {
 #ifdef DEBUG_AUDIO_GENERATOR
-	    cout << "mixModel [synthetic]: adding " << m_pluginBlockSize << " samples from plugin output " << c << endl;
+	    cout << "mixModel [synthetic]: adding " << m_processingBlockSize << " samples from plugin output " << c << endl;
 #endif
 
 	    size_t sourceChannel = (c % plugin->getAudioOutputCount());
@@ -648,8 +644,8 @@
 		}
 	    }
 
-	    for (size_t j = 0; j < m_pluginBlockSize; ++j) {
-		buffer[c][i * m_pluginBlockSize + j] +=
+	    for (size_t j = 0; j < m_processingBlockSize; ++j) {
+		buffer[c][i * m_processingBlockSize + j] +=
 		    channelGain * outs[sourceChannel][j];
 	    }
 	}
--- a/audioio/AudioGenerator.h	Wed Dec 04 19:40:24 2013 +0000
+++ b/audioio/AudioGenerator.h	Tue Jan 07 10:58:10 2014 +0000
@@ -21,7 +21,6 @@
 class FlexiNoteModel;
 class DenseTimeValueModel;
 class SparseOneDimensionalModel;
-class RealTimePluginInstance;
 class Playable;
 
 #include <QObject>
@@ -58,7 +57,7 @@
     virtual void clearModels();
 
     /**
-     * Reset playback, clearing plugins and the like.
+     * Reset playback, clearing buffers and the like.
      */
     virtual void reset();
 
@@ -93,12 +92,11 @@
     virtual void clearSoloModelSet();
 
 protected slots:
-    void playPluginIdChanged(const Playable *, QString);
-    void playPluginConfigurationChanged(const Playable *, QString);
+    void playSampleIdChanged(const Playable *, QString);
 
 protected:
-    size_t       m_sourceSampleRate;
-    size_t       m_targetChannelCount;
+    size_t m_sourceSampleRate;
+    size_t m_targetChannelCount;
 
     bool m_soloing;
     std::set<Model *> m_soloModelSet;
@@ -117,20 +115,22 @@
 	};
     };
 
-    typedef std::map<const Model *, RealTimePluginInstance *> PluginMap;
+//!!!    typedef std::map<const Model *, RealTimePluginInstance *> PluginMap;
 
     typedef std::multiset<NoteOff, NoteOff::Comparator> NoteOffSet;
     typedef std::map<const Model *, NoteOffSet> NoteOffMap;
 
     QMutex m_mutex;
-    PluginMap m_synthMap;
+//!!!    PluginMap m_synthMap;
     NoteOffMap m_noteOffs;
     static QString m_sampleDir;
 
+/*!!!
     virtual RealTimePluginInstance *loadPluginFor(const Model *model);
     virtual RealTimePluginInstance *loadPlugin(QString id, QString program);
+*/
     static void initialiseSampleDir();
-    static void setSampleDir(RealTimePluginInstance *plugin);
+//!!!    static void setSampleDir(RealTimePluginInstance *plugin);
 
     virtual size_t mixDenseTimeValueModel
     (DenseTimeValueModel *model, size_t startFrame, size_t frameCount,
@@ -140,7 +140,7 @@
     (Model *model, size_t startFrame, size_t frameCount,
      float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut);
     
-    static const size_t m_pluginBlockSize;
+    static const size_t m_processingBlockSize;
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audioio/ClipMixer.cpp	Tue Jan 07 10:58:10 2014 +0000
@@ -0,0 +1,86 @@
+/* -*- 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 file copyright 2006 Chris Cannam, 2006-2014 QMUL.
+    
+    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 "ClipMixer.h"
+
+#include <sndfile.h>
+
+#include "base/Debug.h"
+
+ClipMixer::ClipMixer(int channels, int sampleRate, int blockSize) :
+    m_channels(channels),
+    m_sampleRate(sampleRate),
+    m_blockSize(blockSize),
+    m_clipData(0)
+{
+}
+
+ClipMixer::~ClipMixer()
+{
+    delete[] m_clipData;
+}
+
+bool
+ClipMixer::loadClipData(QString path, float f0)
+{
+    if (m_clipData) {
+        cerr << "ClipPlayer::loadClipData: Already have clip loaded" << endl;
+        return false;
+    }
+
+    SF_INFO info;
+    SNDFILE *file;
+    int sampleCount = 0;
+    float *tmpFrames;
+    size_t i;
+
+    info.format = 0;
+    file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
+    if (!file) {
+	cerr << "ClipPlayer::loadClipData: Failed to open file "
+             << path << ": " << sf_strerror(file) << endl;
+	return false;
+    }
+
+    tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
+    if (!tmpFrames) {
+        cerr << "ClipPlayer::loadClipData: malloc(" << info.frames * info.channels * sizeof(float) << ") failed" << endl;
+        return false;
+    }
+
+    sf_readf_float(file, tmpFrames, info.frames);
+    sf_close(file);
+
+    m_clipData = (float *)malloc(info.frames * sizeof(float));
+    if (!m_clipData) {
+        cerr << "ClipPlayer::loadClipData: malloc(" << info.frames * sizeof(float) << ") failed" << endl;
+	free(tmpFrames);
+	return false;
+    }
+
+    for (i = 0; i < info.frames; ++i) {
+	int j;
+	m_clipData[i] = 0.0f;
+	for (j = 0; j < info.channels; ++j) {
+	    m_clipData[i] += tmpFrames[i * info.channels + j];
+	}
+    }
+
+    free(tmpFrames);
+
+    m_clipLength = info.frames;
+    m_clipF0 = f0;
+    m_clipRate = info.samplerate;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audioio/ClipMixer.h	Tue Jan 07 10:58:10 2014 +0000
@@ -0,0 +1,69 @@
+/* -*- 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 file copyright 2006 Chris Cannam, 2006-2014 QMUL.
+    
+    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.
+*/
+
+#ifndef _CLIP_MIXER_H_
+#define _CLIP_MIXER_H_
+
+#include <QString>
+#include <vector>
+
+/**
+ * Mix in synthetic notes produced by resampling a prerecorded
+ * clip. That is, this is a sampler.
+ */
+
+class ClipMixer
+{
+public:
+    ClipMixer(int channels, int sampleRate, int blockSize);
+    ~ClipMixer();
+
+    bool loadClipData(QString clipFilePath, float clipF0);
+
+    //!!! 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]
+	float pan; // range [-1,1]
+    };
+
+    struct NoteEnd {
+	int id; // matching note start
+	int frameOffset; // in current processing block
+    };
+
+    void mix(float **toBuffers, 
+	     std::vector<NoteStart> newNotes, 
+	     std::vector<NoteEnd> endingNotes);
+
+private:
+    int m_channels;
+    int m_sampleRate;
+    int m_blockSize;
+
+    QString m_clipPath;
+
+    float *m_clipData;
+    int m_clipLength;
+    float m_clipF0;
+    float m_clipRate;
+};
+
+
+#endif
--- a/svapp.pro	Wed Dec 04 19:40:24 2013 +0000
+++ b/svapp.pro	Tue Jan 07 10:58:10 2014 +0000
@@ -35,10 +35,15 @@
            audioio/AudioPortAudioTarget.h \
            audioio/AudioPulseAudioTarget.h \
            audioio/AudioTargetFactory.h \
+           audioio/ClipMixer.h \
            audioio/PlaySpeedRangeMapper.h
+
+
+#!!! todo: move ClipMixer down 
 SOURCES += audioio/AudioCallbackPlaySource.cpp \
            audioio/AudioCallbackPlayTarget.cpp \
            audioio/AudioCoreAudioTarget.cpp \
+           audioio/ClipMixer.cpp \
            audioio/AudioGenerator.cpp \
            audioio/AudioJACKTarget.cpp \
            audioio/AudioPortAudioTarget.cpp \