comparison audioio/AudioGenerator.cpp @ 328:28c17ce7a6e9 tony_integration

Merge from tonioni branch
author Chris Cannam
date Tue, 28 Jan 2014 15:02:15 +0000
parents 5c69d40a0e30
children 0e4332efcc7d
comparison
equal deleted inserted replaced
304:c837368b1faf 328:28c17ce7a6e9
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::usesContinuousSynth(const Model *model)
170 return; 177 {
171 } 178 bool cont =
172 179 (qobject_cast<const SparseTimeValueModel *>(model));
173 if (m_synthMap.find(model) == m_synthMap.end()) { 180 return cont;
174 SVDEBUG << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << endl; 181 }
175 return; 182
176 } 183 ClipMixer *
177 184 AudioGenerator::makeClipMixerFor(const Model *model)
178 RealTimePluginInstance *plugin = m_synthMap[model]; 185 {
179 if (plugin) { 186 QString clipId;
180 PluginXml(plugin).setParametersFromXml(configurationXml);
181 }
182 }
183
184 void
185 AudioGenerator::setSampleDir(RealTimePluginInstance *plugin)
186 {
187 if (m_sampleDir != "") {
188 plugin->configure("sampledir", m_sampleDir.toStdString());
189 }
190 }
191
192 RealTimePluginInstance *
193 AudioGenerator::loadPluginFor(const Model *model)
194 {
195 QString pluginId, configurationXml;
196 187
197 const Playable *playable = model; 188 const Playable *playable = model;
198 if (!playable || !playable->canPlay()) return 0; 189 if (!playable || !playable->canPlay()) return 0;
199 190
200 PlayParameters *parameters = 191 PlayParameters *parameters =
201 PlayParameterRepository::getInstance()->getPlayParameters(playable); 192 PlayParameterRepository::getInstance()->getPlayParameters(playable);
202 if (parameters) { 193 if (parameters) {
203 pluginId = parameters->getPlayPluginId(); 194 clipId = parameters->getPlayClipId();
204 configurationXml = parameters->getPlayPluginConfiguration(); 195 }
205 } 196
206 197 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << clipId << std::endl;
207 if (pluginId == "") return 0; 198
208 199 if (clipId == "") {
209 RealTimePluginInstance *plugin = loadPlugin(pluginId, ""); 200 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; 201 return 0;
245 } 202 }
246 203
247 setSampleDir(instance); 204 ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
248 205 m_sourceSampleRate,
249 for (unsigned int i = 0; i < instance->getParameterCount(); ++i) { 206 m_processingBlockSize);
250 instance->setParameterValue(i, instance->getParameterDefault(i)); 207
251 } 208 float clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0f); // required
252 std::string defaultProgram = instance->getProgram(0, 0); 209
253 if (defaultProgram != "") { 210 QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId);
254 // cerr << "first selecting default program " << defaultProgram << endl; 211
255 instance->selectProgram(defaultProgram); 212 if (!mixer->loadClipData(clipPath, clipF0)) {
256 } 213 delete mixer;
257 if (program != "") { 214 return 0;
258 // cerr << "now selecting desired program " << program << endl; 215 }
259 instance->selectProgram(program.toStdString()); 216
260 } 217 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): loaded clip " << clipId << std::endl;
261 instance->setIdealChannelCount(m_targetChannelCount); // reset! 218
262 219 return mixer;
263 return instance; 220 }
221
222 ContinuousSynth *
223 AudioGenerator::makeSynthFor(const Model *model)
224 {
225 const Playable *playable = model;
226 if (!playable || !playable->canPlay()) return 0;
227
228 ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount,
229 m_sourceSampleRate,
230 m_processingBlockSize,
231 m_waveType);
232
233 std::cerr << "AudioGenerator::makeSynthFor(" << model << "): created synth" << std::endl;
234
235 return synth;
264 } 236 }
265 237
266 void 238 void
267 AudioGenerator::removeModel(Model *model) 239 AudioGenerator::removeModel(Model *model)
268 { 240 {
270 dynamic_cast<SparseOneDimensionalModel *>(model); 242 dynamic_cast<SparseOneDimensionalModel *>(model);
271 if (!sodm) return; // nothing to do 243 if (!sodm) return; // nothing to do
272 244
273 QMutexLocker locker(&m_mutex); 245 QMutexLocker locker(&m_mutex);
274 246
275 if (m_synthMap.find(sodm) == m_synthMap.end()) return; 247 if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
276 248
277 RealTimePluginInstance *instance = m_synthMap[sodm]; 249 ClipMixer *mixer = m_clipMixerMap[sodm];
278 m_synthMap.erase(sodm); 250 m_clipMixerMap.erase(sodm);
279 delete instance; 251 delete mixer;
280 } 252 }
281 253
282 void 254 void
283 AudioGenerator::clearModels() 255 AudioGenerator::clearModels()
284 { 256 {
285 QMutexLocker locker(&m_mutex); 257 QMutexLocker locker(&m_mutex);
286 while (!m_synthMap.empty()) { 258
287 RealTimePluginInstance *instance = m_synthMap.begin()->second; 259 while (!m_clipMixerMap.empty()) {
288 m_synthMap.erase(m_synthMap.begin()); 260 ClipMixer *mixer = m_clipMixerMap.begin()->second;
289 delete instance; 261 m_clipMixerMap.erase(m_clipMixerMap.begin());
262 delete mixer;
290 } 263 }
291 } 264 }
292 265
293 void 266 void
294 AudioGenerator::reset() 267 AudioGenerator::reset()
295 { 268 {
296 QMutexLocker locker(&m_mutex); 269 QMutexLocker locker(&m_mutex);
297 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 270
271 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
298 if (i->second) { 272 if (i->second) {
299 i->second->silence(); 273 i->second->reset();
300 i->second->discardEvents();
301 } 274 }
302 } 275 }
303 276
304 m_noteOffs.clear(); 277 m_noteOffs.clear();
305 } 278 }
312 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl; 285 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl;
313 286
314 QMutexLocker locker(&m_mutex); 287 QMutexLocker locker(&m_mutex);
315 m_targetChannelCount = targetChannelCount; 288 m_targetChannelCount = targetChannelCount;
316 289
317 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 290 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
318 if (i->second) i->second->setIdealChannelCount(targetChannelCount); 291 if (i->second) i->second->setChannelCount(targetChannelCount);
319 } 292 }
320 } 293 }
321 294
322 size_t 295 size_t
323 AudioGenerator::getBlockSize() const 296 AudioGenerator::getBlockSize() const
324 { 297 {
325 return m_pluginBlockSize; 298 return m_processingBlockSize;
326 } 299 }
327 300
328 void 301 void
329 AudioGenerator::setSoloModelSet(std::set<Model *> s) 302 AudioGenerator::setSoloModelSet(std::set<Model *> s)
330 { 303 {
385 if (dtvm) { 358 if (dtvm) {
386 return mixDenseTimeValueModel(dtvm, startFrame, frameCount, 359 return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
387 buffer, gain, pan, fadeIn, fadeOut); 360 buffer, gain, pan, fadeIn, fadeOut);
388 } 361 }
389 362
390 bool synthetic = 363 if (usesClipMixer(model)) {
391 (qobject_cast<SparseOneDimensionalModel *>(model) || 364 return mixClipModel(model, startFrame, frameCount,
392 qobject_cast<NoteModel *>(model)); 365 buffer, gain, pan);
393 366 }
394 if (synthetic) { 367
395 return mixSyntheticNoteModel(model, startFrame, frameCount, 368 if (usesContinuousSynth(model)) {
396 buffer, gain, pan, fadeIn, fadeOut); 369 return mixContinuousSynthModel(model, startFrame, frameCount,
397 } 370 buffer, gain, pan);
371 }
372
373 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 374
399 return frameCount; 375 return frameCount;
400 } 376 }
401 377
402 size_t 378 size_t
493 469
494 return got; 470 return got;
495 } 471 }
496 472
497 size_t 473 size_t
498 AudioGenerator::mixSyntheticNoteModel(Model *model, 474 AudioGenerator::mixClipModel(Model *model,
499 size_t startFrame, size_t frames, 475 size_t startFrame, size_t frames,
500 float **buffer, float gain, float pan, 476 float **buffer, float gain, float pan)
501 size_t /* fadeIn */, 477 {
502 size_t /* fadeOut */) 478 ClipMixer *clipMixer = m_clipMixerMap[model];
503 { 479 if (!clipMixer) return 0;
504 RealTimePluginInstance *plugin = m_synthMap[model]; 480
505 if (!plugin) return 0; 481 size_t blocks = frames / m_processingBlockSize;
506
507 size_t latency = plugin->getLatency();
508 size_t blocks = frames / m_pluginBlockSize;
509 482
483 //!!! todo: the below -- it matters
484
510 //!!! hang on -- the fact that the audio callback play source's 485 //!!! 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 486 //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 487 //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 488 //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 489 //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 490 //always have a multiple of the plugin buffer size? I guess this
516 //class has to be queryable for the plugin buffer size & the 491 //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 492 //callback play source has to use that as a multiple for all the
518 //calls to mixModel 493 //calls to mixModel
519 494
520 size_t got = blocks * m_pluginBlockSize; 495 size_t got = blocks * m_processingBlockSize;
521 496
522 #ifdef DEBUG_AUDIO_GENERATOR 497 #ifdef DEBUG_AUDIO_GENERATOR
523 cout << "mixModel [synthetic note]: frames " << frames 498 cout << "mixModel [clip]: frames " << frames
524 << ", blocks " << blocks << endl; 499 << ", blocks " << blocks << endl;
525 #endif 500 #endif
526 501
527 snd_seq_event_t onEv; 502 ClipMixer::NoteStart on;
528 onEv.type = SND_SEQ_EVENT_NOTEON; 503 ClipMixer::NoteEnd off;
529 onEv.data.note.channel = 0; 504
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]; 505 NoteOffSet &noteOffs = m_noteOffs[model];
537 506
507 float **bufferIndexes = new float *[m_targetChannelCount];
508
538 for (size_t i = 0; i < blocks; ++i) { 509 for (size_t i = 0; i < blocks; ++i) {
539 510
540 size_t reqStart = startFrame + i * m_pluginBlockSize; 511 size_t reqStart = startFrame + i * m_processingBlockSize;
541 512
542 NoteList notes = getNotes(model, 513 NoteList notes;
543 reqStart + latency, 514 NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
544 reqStart + latency + m_pluginBlockSize); 515 if (exportable) {
545 516 notes = exportable->getNotes(reqStart,
546 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime 517 reqStart + m_processingBlockSize);
547 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); 518 }
519
520 std::vector<ClipMixer::NoteStart> starts;
521 std::vector<ClipMixer::NoteEnd> ends;
548 522
549 for (NoteList::const_iterator ni = notes.begin(); 523 for (NoteList::const_iterator ni = notes.begin();
550 ni != notes.end(); ++ni) { 524 ni != notes.end(); ++ni) {
551 525
552 size_t noteFrame = ni->start; 526 size_t noteFrame = ni->start;
553 527
554 if (noteFrame >= latency) noteFrame -= latency;
555
556 if (noteFrame < reqStart || 528 if (noteFrame < reqStart ||
557 noteFrame >= reqStart + m_pluginBlockSize) continue; 529 noteFrame >= reqStart + m_processingBlockSize) continue;
558 530
559 while (noteOffs.begin() != noteOffs.end() && 531 while (noteOffs.begin() != noteOffs.end() &&
560 noteOffs.begin()->frame <= noteFrame) { 532 noteOffs.begin()->frame <= noteFrame) {
561 533
562 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 534 size_t eventFrame = noteOffs.begin()->frame;
563 (noteOffs.begin()->frame, m_sourceSampleRate); 535 if (eventFrame < reqStart) eventFrame = reqStart;
564 536
565 offEv.data.note.note = noteOffs.begin()->pitch; 537 off.frameOffset = eventFrame - reqStart;
566 538 off.frequency = noteOffs.begin()->frequency;
567 #ifdef DEBUG_AUDIO_GENERATOR 539
568 cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 540 #ifdef DEBUG_AUDIO_GENERATOR
569 #endif 541 cerr << "mixModel [clip]: adding note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
570 542 #endif
571 plugin->sendEvent(eventTime, &offEv); 543
544 ends.push_back(off);
572 noteOffs.erase(noteOffs.begin()); 545 noteOffs.erase(noteOffs.begin());
573 } 546 }
574 547
575 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 548 on.frameOffset = noteFrame - reqStart;
576 (noteFrame, m_sourceSampleRate); 549 on.frequency = ni->getFrequency();
550 on.level = float(ni->velocity) / 127.0;
551 on.pan = pan;
552
553 #ifdef DEBUG_AUDIO_GENERATOR
554 cout << "mixModel [clip]: adding note at frame " << noteFrame << ", frame offset " << on.frameOffset << " frequency " << on.frequency << endl;
555 #endif
577 556
578 if (ni->isMidiPitchQuantized) { 557 starts.push_back(on);
579 onEv.data.note.note = ni->midiPitch; 558 noteOffs.insert
580 } else { 559 (NoteOff(on.frequency, noteFrame + ni->duration));
581 #ifdef DEBUG_AUDIO_GENERATOR 560 }
582 cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl; 561
583 #endif 562 while (noteOffs.begin() != noteOffs.end() &&
584 onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency); 563 noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
564
565 size_t eventFrame = noteOffs.begin()->frame;
566 if (eventFrame < reqStart) eventFrame = reqStart;
567
568 off.frameOffset = eventFrame - reqStart;
569 off.frequency = noteOffs.begin()->frequency;
570
571 #ifdef DEBUG_AUDIO_GENERATOR
572 cerr << "mixModel [clip]: adding leftover note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
573 #endif
574
575 ends.push_back(off);
576 noteOffs.erase(noteOffs.begin());
577 }
578
579 for (size_t c = 0; c < m_targetChannelCount; ++c) {
580 bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
581 }
582
583 clipMixer->mix(bufferIndexes, gain, starts, ends);
584 }
585
586 delete[] bufferIndexes;
587
588 return got;
589 }
590
591 size_t
592 AudioGenerator::mixContinuousSynthModel(Model *model,
593 size_t startFrame,
594 size_t frames,
595 float **buffer,
596 float gain,
597 float pan)
598 {
599 ContinuousSynth *synth = m_continuousSynthMap[model];
600 if (!synth) return 0;
601
602 // only type we support here at the moment
603 SparseTimeValueModel *stvm = qobject_cast<SparseTimeValueModel *>(model);
604 if (stvm->getScaleUnits() != "Hz") return 0;
605
606 size_t blocks = frames / m_processingBlockSize;
607
608 //!!! todo: see comment in mixClipModel
609
610 size_t got = blocks * m_processingBlockSize;
611
612 #ifdef DEBUG_AUDIO_GENERATOR
613 cout << "mixModel [synth]: frames " << frames
614 << ", blocks " << blocks << endl;
615 #endif
616
617 float **bufferIndexes = new float *[m_targetChannelCount];
618
619 for (size_t i = 0; i < blocks; ++i) {
620
621 size_t reqStart = startFrame + i * m_processingBlockSize;
622
623 for (size_t c = 0; c < m_targetChannelCount; ++c) {
624 bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
625 }
626
627 SparseTimeValueModel::PointList points =
628 stvm->getPoints(reqStart, reqStart + m_processingBlockSize);
629
630 // by default, repeat last frequency
631 float f0 = 0.f;
632
633 // go straight to the last freq that is genuinely in this range
634 for (SparseTimeValueModel::PointList::const_iterator itr = points.end();
635 itr != points.begin(); ) {
636 --itr;
637 if (itr->frame >= reqStart &&
638 itr->frame < reqStart + m_processingBlockSize) {
639 f0 = itr->value;
640 break;
585 } 641 }
586 642 }
587 onEv.data.note.velocity = ni->velocity; 643
588 644 // if we found no such frequency and the next point is further
589 plugin->sendEvent(eventTime, &onEv); 645 // away than twice the model resolution, go silent (same
590 646 // criterion TimeValueLayer uses for ending a discrete curve
591 #ifdef DEBUG_AUDIO_GENERATOR 647 // segment)
592 cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << endl; 648 if (f0 == 0.f) {
593 #endif 649 SparseTimeValueModel::PointList nextPoints =
594 650 stvm->getNextPoints(reqStart + m_processingBlockSize);
595 noteOffs.insert 651 if (nextPoints.empty() ||
596 (NoteOff(onEv.data.note.note, noteFrame + ni->duration)); 652 nextPoints.begin()->frame > reqStart + 2 * stvm->getResolution()) {
597 } 653 f0 = -1.f;
598 654 }
599 while (noteOffs.begin() != noteOffs.end() && 655 }
600 noteOffs.begin()->frame <= 656
601 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { 657 // cerr << "f0 = " << f0 << endl;
602 658
603 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 659 synth->mix(bufferIndexes,
604 (noteOffs.begin()->frame, m_sourceSampleRate); 660 gain,
605 661 pan,
606 offEv.data.note.note = noteOffs.begin()->pitch; 662 f0);
607 663 }
608 #ifdef DEBUG_AUDIO_GENERATOR 664
609 cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 665 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 666
642 return got; 667 return got;
643 } 668 }
644 669
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