Mercurial > hg > svapp
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 ¬eOffs = 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 &);