Mercurial > hg > svapp
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 \