comparison audioio/AudioGenerator.cpp @ 350:aebee52e86b3

Merge from branch tony_integration
author Chris Cannam
date Wed, 14 May 2014 09:54:46 +0100
parents 8d7f39df44ed
children 0876ea394902
comparison
equal deleted inserted replaced
330:46b24009ce7a 350:aebee52e86b3
20 #include "base/PlayParameterRepository.h" 20 #include "base/PlayParameterRepository.h"
21 #include "base/Pitch.h" 21 #include "base/Pitch.h"
22 #include "base/Exceptions.h" 22 #include "base/Exceptions.h"
23 23
24 #include "data/model/NoteModel.h" 24 #include "data/model/NoteModel.h"
25 #include "data/model/FlexiNoteModel.h"
25 #include "data/model/DenseTimeValueModel.h" 26 #include "data/model/DenseTimeValueModel.h"
27 #include "data/model/SparseTimeValueModel.h"
26 #include "data/model/SparseOneDimensionalModel.h" 28 #include "data/model/SparseOneDimensionalModel.h"
27 29 #include "data/model/NoteData.h"
28 #include "plugin/RealTimePluginFactory.h" 30
29 #include "plugin/RealTimePluginInstance.h" 31 #include "ClipMixer.h"
30 #include "plugin/PluginIdentifier.h" 32 #include "ContinuousSynth.h"
31 #include "plugin/PluginXml.h"
32 #include "plugin/api/alsa/seq_event.h"
33 33
34 #include <iostream> 34 #include <iostream>
35 #include <cmath> 35 #include <cmath>
36 36
37 #include <QDir> 37 #include <QDir>
38 #include <QFile> 38 #include <QFile>
39 39
40 const size_t 40 const size_t
41 AudioGenerator::m_pluginBlockSize = 2048; 41 AudioGenerator::m_processingBlockSize = 1024;
42 42
43 QString 43 QString
44 AudioGenerator::m_sampleDir = ""; 44 AudioGenerator::m_sampleDir = "";
45 45
46 //#define DEBUG_AUDIO_GENERATOR 1 46 //#define DEBUG_AUDIO_GENERATOR 1
47 47
48 AudioGenerator::AudioGenerator() : 48 AudioGenerator::AudioGenerator() :
49 m_sourceSampleRate(0), 49 m_sourceSampleRate(0),
50 m_targetChannelCount(1), 50 m_targetChannelCount(1),
51 m_waveType(0),
51 m_soloing(false) 52 m_soloing(false)
52 { 53 {
53 initialiseSampleDir(); 54 initialiseSampleDir();
54 55
55 connect(PlayParameterRepository::getInstance(), 56 connect(PlayParameterRepository::getInstance(),
56 SIGNAL(playPluginIdChanged(const Playable *, QString)), 57 SIGNAL(playClipIdChanged(const Playable *, QString)),
57 this, 58 this,
58 SLOT(playPluginIdChanged(const Playable *, QString))); 59 SLOT(playClipIdChanged(const Playable *, QString)));
59
60 connect(PlayParameterRepository::getInstance(),
61 SIGNAL(playPluginConfigurationChanged(const Playable *, QString)),
62 this,
63 SLOT(playPluginConfigurationChanged(const Playable *, QString)));
64 } 60 }
65 61
66 AudioGenerator::~AudioGenerator() 62 AudioGenerator::~AudioGenerator()
67 { 63 {
68 #ifdef DEBUG_AUDIO_GENERATOR 64 #ifdef DEBUG_AUDIO_GENERATOR
123 m_sourceSampleRate = model->getSampleRate(); 119 m_sourceSampleRate = model->getSampleRate();
124 return true; 120 return true;
125 } 121 }
126 } 122 }
127 123
128 RealTimePluginInstance *plugin = loadPluginFor(model); 124 if (usesClipMixer(model)) {
129 if (plugin) { 125 ClipMixer *mixer = makeClipMixerFor(model);
130 QMutexLocker locker(&m_mutex); 126 if (mixer) {
131 m_synthMap[model] = plugin; 127 QMutexLocker locker(&m_mutex);
132 return true; 128 m_clipMixerMap[model] = mixer;
129 return true;
130 }
131 }
132
133 if (usesContinuousSynth(model)) {
134 ContinuousSynth *synth = makeSynthFor(model);
135 if (synth) {
136 QMutexLocker locker(&m_mutex);
137 m_continuousSynthMap[model] = synth;
138 return true;
139 }
133 } 140 }
134 141
135 return false; 142 return false;
136 } 143 }
137 144
138 void 145 void
139 AudioGenerator::playPluginIdChanged(const Playable *playable, QString) 146 AudioGenerator::playClipIdChanged(const Playable *playable, QString)
140 { 147 {
141 const Model *model = dynamic_cast<const Model *>(playable); 148 const Model *model = dynamic_cast<const Model *>(playable);
142 if (!model) { 149 if (!model) {
143 cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable " 150 cerr << "WARNING: AudioGenerator::playClipIdChanged: playable "
144 << playable << " is not a supported model type" 151 << playable << " is not a supported model type"
145 << endl; 152 << endl;
146 return; 153 return;
147 } 154 }
148 155
149 if (m_synthMap.find(model) == m_synthMap.end()) return; 156 if (m_clipMixerMap.find(model) == m_clipMixerMap.end()) return;
150 157
151 RealTimePluginInstance *plugin = loadPluginFor(model); 158 ClipMixer *mixer = makeClipMixerFor(model);
152 if (plugin) { 159 if (mixer) {
153 QMutexLocker locker(&m_mutex); 160 QMutexLocker locker(&m_mutex);
154 delete m_synthMap[model]; 161 m_clipMixerMap[model] = mixer;
155 m_synthMap[model] = plugin; 162 }
156 } 163 }
157 } 164
158 165 bool
159 void 166 AudioGenerator::usesClipMixer(const Model *model)
160 AudioGenerator::playPluginConfigurationChanged(const Playable *playable, 167 {
161 QString configurationXml) 168 bool clip =
162 { 169 (qobject_cast<const SparseOneDimensionalModel *>(model) ||
163 // SVDEBUG << "AudioGenerator::playPluginConfigurationChanged" << endl; 170 qobject_cast<const NoteModel *>(model) ||
164 171 qobject_cast<const FlexiNoteModel *>(model));
165 const Model *model = dynamic_cast<const Model *>(playable); 172 return clip;
166 if (!model) { 173 }
167 cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable " 174
168 << playable << " is not a supported model type" 175 bool
169 << endl; 176 AudioGenerator::wantsQuieterClips(const Model *model)
170 return; 177 {
171 } 178 // basically, anything that usually has sustain (like notes) or
172 179 // often has multiple sounds at once (like notes) wants to use a
173 if (m_synthMap.find(model) == m_synthMap.end()) { 180 // quieter level than simple click tracks
174 SVDEBUG << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << endl; 181 bool does =
175 return; 182 (qobject_cast<const NoteModel *>(model) ||
176 } 183 qobject_cast<const FlexiNoteModel *>(model));
177 184 return does;
178 RealTimePluginInstance *plugin = m_synthMap[model]; 185 }
179 if (plugin) { 186
180 PluginXml(plugin).setParametersFromXml(configurationXml); 187 bool
181 } 188 AudioGenerator::usesContinuousSynth(const Model *model)
182 } 189 {
183 190 bool cont =
184 void 191 (qobject_cast<const SparseTimeValueModel *>(model));
185 AudioGenerator::setSampleDir(RealTimePluginInstance *plugin) 192 return cont;
186 { 193 }
187 if (m_sampleDir != "") { 194
188 plugin->configure("sampledir", m_sampleDir.toStdString()); 195 ClipMixer *
189 } 196 AudioGenerator::makeClipMixerFor(const Model *model)
190 } 197 {
191 198 QString clipId;
192 RealTimePluginInstance *
193 AudioGenerator::loadPluginFor(const Model *model)
194 {
195 QString pluginId, configurationXml;
196 199
197 const Playable *playable = model; 200 const Playable *playable = model;
198 if (!playable || !playable->canPlay()) return 0; 201 if (!playable || !playable->canPlay()) return 0;
199 202
200 PlayParameters *parameters = 203 PlayParameters *parameters =
201 PlayParameterRepository::getInstance()->getPlayParameters(playable); 204 PlayParameterRepository::getInstance()->getPlayParameters(playable);
202 if (parameters) { 205 if (parameters) {
203 pluginId = parameters->getPlayPluginId(); 206 clipId = parameters->getPlayClipId();
204 configurationXml = parameters->getPlayPluginConfiguration(); 207 }
205 } 208
206 209 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << clipId << std::endl;
207 if (pluginId == "") return 0; 210
208 211 if (clipId == "") {
209 RealTimePluginInstance *plugin = loadPlugin(pluginId, ""); 212 SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl;
210 if (!plugin) return 0;
211
212 if (configurationXml != "") {
213 PluginXml(plugin).setParametersFromXml(configurationXml);
214 setSampleDir(plugin);
215 }
216
217 configurationXml = PluginXml(plugin).toXmlString();
218
219 if (parameters) {
220 parameters->setPlayPluginId(pluginId);
221 parameters->setPlayPluginConfiguration(configurationXml);
222 }
223
224 return plugin;
225 }
226
227 RealTimePluginInstance *
228 AudioGenerator::loadPlugin(QString pluginId, QString program)
229 {
230 RealTimePluginFactory *factory =
231 RealTimePluginFactory::instanceFor(pluginId);
232
233 if (!factory) {
234 cerr << "Failed to get plugin factory" << endl;
235 return 0;
236 }
237
238 RealTimePluginInstance *instance =
239 factory->instantiatePlugin
240 (pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
241
242 if (!instance) {
243 cerr << "Failed to instantiate plugin " << pluginId << endl;
244 return 0; 213 return 0;
245 } 214 }
246 215
247 setSampleDir(instance); 216 ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
248 217 m_sourceSampleRate,
249 for (unsigned int i = 0; i < instance->getParameterCount(); ++i) { 218 m_processingBlockSize);
250 instance->setParameterValue(i, instance->getParameterDefault(i)); 219
251 } 220 float clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0f); // required
252 std::string defaultProgram = instance->getProgram(0, 0); 221
253 if (defaultProgram != "") { 222 QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId);
254 // cerr << "first selecting default program " << defaultProgram << endl; 223
255 instance->selectProgram(defaultProgram); 224 float level = wantsQuieterClips(model) ? 0.5 : 1.0;
256 } 225 if (!mixer->loadClipData(clipPath, clipF0, level)) {
257 if (program != "") { 226 delete mixer;
258 // cerr << "now selecting desired program " << program << endl; 227 return 0;
259 instance->selectProgram(program.toStdString()); 228 }
260 } 229
261 instance->setIdealChannelCount(m_targetChannelCount); // reset! 230 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): loaded clip " << clipId << std::endl;
262 231
263 return instance; 232 return mixer;
233 }
234
235 ContinuousSynth *
236 AudioGenerator::makeSynthFor(const Model *model)
237 {
238 const Playable *playable = model;
239 if (!playable || !playable->canPlay()) return 0;
240
241 ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount,
242 m_sourceSampleRate,
243 m_processingBlockSize,
244 m_waveType);
245
246 std::cerr << "AudioGenerator::makeSynthFor(" << model << "): created synth" << std::endl;
247
248 return synth;
264 } 249 }
265 250
266 void 251 void
267 AudioGenerator::removeModel(Model *model) 252 AudioGenerator::removeModel(Model *model)
268 { 253 {
270 dynamic_cast<SparseOneDimensionalModel *>(model); 255 dynamic_cast<SparseOneDimensionalModel *>(model);
271 if (!sodm) return; // nothing to do 256 if (!sodm) return; // nothing to do
272 257
273 QMutexLocker locker(&m_mutex); 258 QMutexLocker locker(&m_mutex);
274 259
275 if (m_synthMap.find(sodm) == m_synthMap.end()) return; 260 if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
276 261
277 RealTimePluginInstance *instance = m_synthMap[sodm]; 262 ClipMixer *mixer = m_clipMixerMap[sodm];
278 m_synthMap.erase(sodm); 263 m_clipMixerMap.erase(sodm);
279 delete instance; 264 delete mixer;
280 } 265 }
281 266
282 void 267 void
283 AudioGenerator::clearModels() 268 AudioGenerator::clearModels()
284 { 269 {
285 QMutexLocker locker(&m_mutex); 270 QMutexLocker locker(&m_mutex);
286 while (!m_synthMap.empty()) { 271
287 RealTimePluginInstance *instance = m_synthMap.begin()->second; 272 while (!m_clipMixerMap.empty()) {
288 m_synthMap.erase(m_synthMap.begin()); 273 ClipMixer *mixer = m_clipMixerMap.begin()->second;
289 delete instance; 274 m_clipMixerMap.erase(m_clipMixerMap.begin());
275 delete mixer;
290 } 276 }
291 } 277 }
292 278
293 void 279 void
294 AudioGenerator::reset() 280 AudioGenerator::reset()
295 { 281 {
296 QMutexLocker locker(&m_mutex); 282 QMutexLocker locker(&m_mutex);
297 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 283
284 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
298 if (i->second) { 285 if (i->second) {
299 i->second->silence(); 286 i->second->reset();
300 i->second->discardEvents();
301 } 287 }
302 } 288 }
303 289
304 m_noteOffs.clear(); 290 m_noteOffs.clear();
305 } 291 }
312 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl; 298 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl;
313 299
314 QMutexLocker locker(&m_mutex); 300 QMutexLocker locker(&m_mutex);
315 m_targetChannelCount = targetChannelCount; 301 m_targetChannelCount = targetChannelCount;
316 302
317 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 303 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
318 if (i->second) i->second->setIdealChannelCount(targetChannelCount); 304 if (i->second) i->second->setChannelCount(targetChannelCount);
319 } 305 }
320 } 306 }
321 307
322 size_t 308 size_t
323 AudioGenerator::getBlockSize() const 309 AudioGenerator::getBlockSize() const
324 { 310 {
325 return m_pluginBlockSize; 311 return m_processingBlockSize;
326 } 312 }
327 313
328 void 314 void
329 AudioGenerator::setSoloModelSet(std::set<Model *> s) 315 AudioGenerator::setSoloModelSet(std::set<Model *> s)
330 { 316 {
385 if (dtvm) { 371 if (dtvm) {
386 return mixDenseTimeValueModel(dtvm, startFrame, frameCount, 372 return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
387 buffer, gain, pan, fadeIn, fadeOut); 373 buffer, gain, pan, fadeIn, fadeOut);
388 } 374 }
389 375
390 bool synthetic = 376 if (usesClipMixer(model)) {
391 (qobject_cast<SparseOneDimensionalModel *>(model) || 377 return mixClipModel(model, startFrame, frameCount,
392 qobject_cast<NoteModel *>(model)); 378 buffer, gain, pan);
393 379 }
394 if (synthetic) { 380
395 return mixSyntheticNoteModel(model, startFrame, frameCount, 381 if (usesContinuousSynth(model)) {
396 buffer, gain, pan, fadeIn, fadeOut); 382 return mixContinuousSynthModel(model, startFrame, frameCount,
397 } 383 buffer, gain, pan);
384 }
385
386 std::cerr << "AudioGenerator::mixModel: WARNING: Model " << model << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl;
398 387
399 return frameCount; 388 return frameCount;
400 } 389 }
401 390
402 size_t 391 size_t
493 482
494 return got; 483 return got;
495 } 484 }
496 485
497 size_t 486 size_t
498 AudioGenerator::mixSyntheticNoteModel(Model *model, 487 AudioGenerator::mixClipModel(Model *model,
499 size_t startFrame, size_t frames, 488 size_t startFrame, size_t frames,
500 float **buffer, float gain, float pan, 489 float **buffer, float gain, float pan)
501 size_t /* fadeIn */, 490 {
502 size_t /* fadeOut */) 491 ClipMixer *clipMixer = m_clipMixerMap[model];
503 { 492 if (!clipMixer) return 0;
504 RealTimePluginInstance *plugin = m_synthMap[model]; 493
505 if (!plugin) return 0; 494 size_t blocks = frames / m_processingBlockSize;
506
507 size_t latency = plugin->getLatency();
508 size_t blocks = frames / m_pluginBlockSize;
509 495
496 //!!! todo: the below -- it matters
497
510 //!!! hang on -- the fact that the audio callback play source's 498 //!!! hang on -- the fact that the audio callback play source's
511 //buffer is a multiple of the plugin's buffer size doesn't mean 499 //buffer is a multiple of the plugin's buffer size doesn't mean
512 //that we always get called for a multiple of it here (because it 500 //that we always get called for a multiple of it here (because it
513 //also depends on the JACK block size). how should we ensure that 501 //also depends on the JACK block size). how should we ensure that
514 //all models write the same amount in to the mix, and that we 502 //all models write the same amount in to the mix, and that we
515 //always have a multiple of the plugin buffer size? I guess this 503 //always have a multiple of the plugin buffer size? I guess this
516 //class has to be queryable for the plugin buffer size & the 504 //class has to be queryable for the plugin buffer size & the
517 //callback play source has to use that as a multiple for all the 505 //callback play source has to use that as a multiple for all the
518 //calls to mixModel 506 //calls to mixModel
519 507
520 size_t got = blocks * m_pluginBlockSize; 508 size_t got = blocks * m_processingBlockSize;
521 509
522 #ifdef DEBUG_AUDIO_GENERATOR 510 #ifdef DEBUG_AUDIO_GENERATOR
523 cout << "mixModel [synthetic note]: frames " << frames 511 cout << "mixModel [clip]: frames " << frames
524 << ", blocks " << blocks << endl; 512 << ", blocks " << blocks << endl;
525 #endif 513 #endif
526 514
527 snd_seq_event_t onEv; 515 ClipMixer::NoteStart on;
528 onEv.type = SND_SEQ_EVENT_NOTEON; 516 ClipMixer::NoteEnd off;
529 onEv.data.note.channel = 0; 517
530
531 snd_seq_event_t offEv;
532 offEv.type = SND_SEQ_EVENT_NOTEOFF;
533 offEv.data.note.channel = 0;
534 offEv.data.note.velocity = 0;
535
536 NoteOffSet &noteOffs = m_noteOffs[model]; 518 NoteOffSet &noteOffs = m_noteOffs[model];
537 519
520 float **bufferIndexes = new float *[m_targetChannelCount];
521
538 for (size_t i = 0; i < blocks; ++i) { 522 for (size_t i = 0; i < blocks; ++i) {
539 523
540 size_t reqStart = startFrame + i * m_pluginBlockSize; 524 size_t reqStart = startFrame + i * m_processingBlockSize;
541 525
542 NoteList notes = getNotes(model, 526 NoteList notes;
543 reqStart + latency, 527 NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
544 reqStart + latency + m_pluginBlockSize); 528 if (exportable) {
545 529 notes = exportable->getNotes(reqStart,
546 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime 530 reqStart + m_processingBlockSize);
547 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); 531 }
532
533 std::vector<ClipMixer::NoteStart> starts;
534 std::vector<ClipMixer::NoteEnd> ends;
548 535
549 for (NoteList::const_iterator ni = notes.begin(); 536 for (NoteList::const_iterator ni = notes.begin();
550 ni != notes.end(); ++ni) { 537 ni != notes.end(); ++ni) {
551 538
552 size_t noteFrame = ni->start; 539 size_t noteFrame = ni->start;
553 540
554 if (noteFrame >= latency) noteFrame -= latency;
555
556 if (noteFrame < reqStart || 541 if (noteFrame < reqStart ||
557 noteFrame >= reqStart + m_pluginBlockSize) continue; 542 noteFrame >= reqStart + m_processingBlockSize) continue;
558 543
559 while (noteOffs.begin() != noteOffs.end() && 544 while (noteOffs.begin() != noteOffs.end() &&
560 noteOffs.begin()->frame <= noteFrame) { 545 noteOffs.begin()->frame <= noteFrame) {
561 546
562 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 547 size_t eventFrame = noteOffs.begin()->frame;
563 (noteOffs.begin()->frame, m_sourceSampleRate); 548 if (eventFrame < reqStart) eventFrame = reqStart;
564 549
565 offEv.data.note.note = noteOffs.begin()->pitch; 550 off.frameOffset = eventFrame - reqStart;
566 551 off.frequency = noteOffs.begin()->frequency;
567 #ifdef DEBUG_AUDIO_GENERATOR 552
568 cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 553 #ifdef DEBUG_AUDIO_GENERATOR
569 #endif 554 cerr << "mixModel [clip]: adding note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
570 555 #endif
571 plugin->sendEvent(eventTime, &offEv); 556
557 ends.push_back(off);
572 noteOffs.erase(noteOffs.begin()); 558 noteOffs.erase(noteOffs.begin());
573 } 559 }
574 560
575 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 561 on.frameOffset = noteFrame - reqStart;
576 (noteFrame, m_sourceSampleRate); 562 on.frequency = ni->getFrequency();
563 on.level = float(ni->velocity) / 127.0;
564 on.pan = pan;
565
566 #ifdef DEBUG_AUDIO_GENERATOR
567 cout << "mixModel [clip]: adding note at frame " << noteFrame << ", frame offset " << on.frameOffset << " frequency " << on.frequency << ", level " << on.level << endl;
568 #endif
577 569
578 if (ni->isMidiPitchQuantized) { 570 starts.push_back(on);
579 onEv.data.note.note = ni->midiPitch; 571 noteOffs.insert
580 } else { 572 (NoteOff(on.frequency, noteFrame + ni->duration));
581 #ifdef DEBUG_AUDIO_GENERATOR 573 }
582 cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl; 574
583 #endif 575 while (noteOffs.begin() != noteOffs.end() &&
584 onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency); 576 noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
577
578 size_t eventFrame = noteOffs.begin()->frame;
579 if (eventFrame < reqStart) eventFrame = reqStart;
580
581 off.frameOffset = eventFrame - reqStart;
582 off.frequency = noteOffs.begin()->frequency;
583
584 #ifdef DEBUG_AUDIO_GENERATOR
585 cerr << "mixModel [clip]: adding leftover note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
586 #endif
587
588 ends.push_back(off);
589 noteOffs.erase(noteOffs.begin());
590 }
591
592 for (size_t c = 0; c < m_targetChannelCount; ++c) {
593 bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
594 }
595
596 clipMixer->mix(bufferIndexes, gain, starts, ends);
597 }
598
599 delete[] bufferIndexes;
600
601 return got;
602 }
603
604 size_t
605 AudioGenerator::mixContinuousSynthModel(Model *model,
606 size_t startFrame,
607 size_t frames,
608 float **buffer,
609 float gain,
610 float pan)
611 {
612 ContinuousSynth *synth = m_continuousSynthMap[model];
613 if (!synth) return 0;
614
615 // only type we support here at the moment
616 SparseTimeValueModel *stvm = qobject_cast<SparseTimeValueModel *>(model);
617 if (stvm->getScaleUnits() != "Hz") return 0;
618
619 size_t blocks = frames / m_processingBlockSize;
620
621 //!!! todo: see comment in mixClipModel
622
623 size_t got = blocks * m_processingBlockSize;
624
625 #ifdef DEBUG_AUDIO_GENERATOR
626 cout << "mixModel [synth]: frames " << frames
627 << ", blocks " << blocks << endl;
628 #endif
629
630 float **bufferIndexes = new float *[m_targetChannelCount];
631
632 for (size_t i = 0; i < blocks; ++i) {
633
634 size_t reqStart = startFrame + i * m_processingBlockSize;
635
636 for (size_t c = 0; c < m_targetChannelCount; ++c) {
637 bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
638 }
639
640 SparseTimeValueModel::PointList points =
641 stvm->getPoints(reqStart, reqStart + m_processingBlockSize);
642
643 // by default, repeat last frequency
644 float f0 = 0.f;
645
646 // go straight to the last freq that is genuinely in this range
647 for (SparseTimeValueModel::PointList::const_iterator itr = points.end();
648 itr != points.begin(); ) {
649 --itr;
650 if (itr->frame >= reqStart &&
651 itr->frame < reqStart + m_processingBlockSize) {
652 f0 = itr->value;
653 break;
585 } 654 }
586 655 }
587 onEv.data.note.velocity = ni->velocity; 656
588 657 // if we found no such frequency and the next point is further
589 plugin->sendEvent(eventTime, &onEv); 658 // away than twice the model resolution, go silent (same
590 659 // criterion TimeValueLayer uses for ending a discrete curve
591 #ifdef DEBUG_AUDIO_GENERATOR 660 // segment)
592 cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << endl; 661 if (f0 == 0.f) {
593 #endif 662 SparseTimeValueModel::PointList nextPoints =
594 663 stvm->getNextPoints(reqStart + m_processingBlockSize);
595 noteOffs.insert 664 if (nextPoints.empty() ||
596 (NoteOff(onEv.data.note.note, noteFrame + ni->duration)); 665 nextPoints.begin()->frame > reqStart + 2 * stvm->getResolution()) {
597 } 666 f0 = -1.f;
598 667 }
599 while (noteOffs.begin() != noteOffs.end() && 668 }
600 noteOffs.begin()->frame <= 669
601 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { 670 // cerr << "f0 = " << f0 << endl;
602 671
603 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 672 synth->mix(bufferIndexes,
604 (noteOffs.begin()->frame, m_sourceSampleRate); 673 gain,
605 674 pan,
606 offEv.data.note.note = noteOffs.begin()->pitch; 675 f0);
607 676 }
608 #ifdef DEBUG_AUDIO_GENERATOR 677
609 cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 678 delete[] bufferIndexes;
610 #endif
611
612 plugin->sendEvent(eventTime, &offEv);
613 noteOffs.erase(noteOffs.begin());
614 }
615
616 plugin->run(blockTime);
617 float **outs = plugin->getAudioOutputBuffers();
618
619 for (size_t c = 0; c < m_targetChannelCount; ++c) {
620 #ifdef DEBUG_AUDIO_GENERATOR
621 cout << "mixModel [synthetic]: adding " << m_pluginBlockSize << " samples from plugin output " << c << endl;
622 #endif
623
624 size_t sourceChannel = (c % plugin->getAudioOutputCount());
625
626 float channelGain = gain;
627 if (pan != 0.0) {
628 if (c == 0) {
629 if (pan > 0.0) channelGain *= 1.0 - pan;
630 } else {
631 if (pan < 0.0) channelGain *= pan + 1.0;
632 }
633 }
634
635 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
636 buffer[c][i * m_pluginBlockSize + j] +=
637 channelGain * outs[sourceChannel][j];
638 }
639 }
640 }
641 679
642 return got; 680 return got;
643 } 681 }
644 682
645 AudioGenerator::NoteList
646 AudioGenerator::getNotes(Model *model,
647 size_t startFrame,
648 size_t endFrame)
649 {
650 NoteList notes;
651
652 SparseOneDimensionalModel *sodm =
653 qobject_cast<SparseOneDimensionalModel *>(model);
654
655 if (sodm) {
656
657 SparseOneDimensionalModel::PointList points =
658 sodm->getPoints(startFrame, endFrame);
659
660 for (SparseOneDimensionalModel::PointList::iterator pli =
661 points.begin(); pli != points.end(); ++pli) {
662
663 notes.push_back
664 (NoteData(pli->frame,
665 m_sourceSampleRate / 6, // arbitrary short duration
666 64, // default pitch
667 100)); // default velocity
668 }
669
670 return notes;
671 }
672
673 NoteModel *nm = qobject_cast<NoteModel *>(model);
674
675 if (nm) {
676
677 NoteModel::PointList points =
678 nm->getPoints(startFrame, endFrame);
679
680 for (NoteModel::PointList::iterator pli =
681 points.begin(); pli != points.end(); ++pli) {
682
683 size_t duration = pli->duration;
684 if (duration == 0 || duration == 1) {
685 duration = m_sourceSampleRate / 20;
686 }
687
688 int pitch = lrintf(pli->value);
689
690 int velocity = 100;
691 if (pli->level > 0.f && pli->level <= 1.f) {
692 velocity = lrintf(pli->level * 127);
693 }
694
695 NoteData note(pli->frame,
696 duration,
697 pitch,
698 velocity);
699
700 if (nm->getScaleUnits() == "Hz") {
701 note.frequency = pli->value;
702 note.isMidiPitchQuantized = false;
703 }
704
705 notes.push_back(note);
706 }
707
708 return notes;
709 }
710
711 return notes;
712 }
713