comparison audioio/AudioGenerator.cpp @ 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
comparison
equal deleted inserted replaced
307:6eb15c3aee0a 308:289d65722123
143 143
144 ClipMixer *mixer = makeClipMixerFor(model); 144 ClipMixer *mixer = makeClipMixerFor(model);
145 if (mixer) { 145 if (mixer) {
146 QMutexLocker locker(&m_mutex); 146 QMutexLocker locker(&m_mutex);
147 m_clipMixerMap[model] = mixer; 147 m_clipMixerMap[model] = mixer;
148 return true; 148 }
149 } 149 }
150 } 150
151 /*!!! 151 /*!!!
152 void 152 void
153 AudioGenerator::playPluginConfigurationChanged(const Playable *playable, 153 AudioGenerator::playPluginConfigurationChanged(const Playable *playable,
154 QString configurationXml) 154 QString configurationXml)
155 { 155 {
194 PlayParameterRepository::getInstance()->getPlayParameters(playable); 194 PlayParameterRepository::getInstance()->getPlayParameters(playable);
195 if (parameters) { 195 if (parameters) {
196 sampleId = parameters->getPlaySampleId(); 196 sampleId = parameters->getPlaySampleId();
197 } 197 }
198 198
199 std::cerr << "AudioGenerator::loadPluginFor(" << model << "): sample id = " << sampleId << std::endl; 199 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << sampleId << std::endl;
200 200
201 if (sampleId == "") { 201 if (sampleId == "") {
202 SVDEBUG << "AudioGenerator::loadPluginFor(" << model << "): no sample, skipping" << endl; 202 SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl;
203 return 0; 203 return 0;
204 } 204 }
205 205
206 206 ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
207 207 m_sourceSampleRate,
208 208 m_processingBlockSize);
209 209
210 210 float clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0f); // required
211 RealTimePluginInstance *plugin = loadPlugin(pluginId, ""); 211
212 if (!plugin) return 0; 212 QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(sampleId);
213 213
214 std::cerr << "AudioGenerator::loadPluginFor(" << model << "): loaded plugin " 214 if (!mixer->loadClipData(clipPath, clipF0)) {
215 << plugin << std::endl; 215 delete mixer;
216
217 if (configurationXml != "") {
218 PluginXml(plugin).setParametersFromXml(configurationXml);
219 setSampleDir(plugin);
220 }
221
222 configurationXml = PluginXml(plugin).toXmlString();
223
224 if (parameters) {
225 parameters->setPlaySampleId(pluginId);
226 parameters->setPlayPluginConfiguration(configurationXml);
227 }
228
229 return plugin;
230 }
231
232 RealTimePluginInstance *
233 AudioGenerator::loadPlugin(QString pluginId, QString program)
234 {
235 RealTimePluginFactory *factory =
236 RealTimePluginFactory::instanceFor(pluginId);
237
238 if (!factory) {
239 cerr << "Failed to get plugin factory" << endl;
240 return 0;
241 }
242
243 RealTimePluginInstance *instance =
244 factory->instantiatePlugin
245 (pluginId, 0, 0, m_sourceSampleRate, m_processingBlockSize, m_targetChannelCount);
246
247 if (!instance) {
248 cerr << "Failed to instantiate plugin " << pluginId << endl;
249 return 0; 216 return 0;
250 } 217 }
251 218
252 setSampleDir(instance); 219 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): loaded clip " << sampleId << std::endl;
253 220
254 for (unsigned int i = 0; i < instance->getParameterCount(); ++i) { 221 return mixer;
255 instance->setParameterValue(i, instance->getParameterDefault(i)); 222 }
256 } 223
257 std::string defaultProgram = instance->getProgram(0, 0);
258 if (defaultProgram != "") {
259 cerr << "first selecting default program " << defaultProgram << endl;
260 instance->selectProgram(defaultProgram);
261 }
262 if (program != "") {
263 cerr << "now selecting desired program " << program << endl;
264 instance->selectProgram(program.toStdString());
265 }
266 instance->setIdealChannelCount(m_targetChannelCount); // reset!
267
268 return instance;
269 }
270 */
271 void 224 void
272 AudioGenerator::removeModel(Model *model) 225 AudioGenerator::removeModel(Model *model)
273 { 226 {
274 SparseOneDimensionalModel *sodm = 227 SparseOneDimensionalModel *sodm =
275 dynamic_cast<SparseOneDimensionalModel *>(model); 228 dynamic_cast<SparseOneDimensionalModel *>(model);
276 if (!sodm) return; // nothing to do 229 if (!sodm) return; // nothing to do
277 230
278 QMutexLocker locker(&m_mutex); 231 QMutexLocker locker(&m_mutex);
279 232
280 if (m_synthMap.find(sodm) == m_synthMap.end()) return; 233 if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
281 234
282 //!!! RealTimePluginInstance *instance = m_synthMap[sodm]; 235 ClipMixer *mixer = m_clipMixerMap[sodm];
283 // m_synthMap.erase(sodm); 236 m_clipMixerMap.erase(sodm);
284 delete instance; 237 delete mixer;
285 } 238 }
286 239
287 void 240 void
288 AudioGenerator::clearModels() 241 AudioGenerator::clearModels()
289 { 242 {
290 QMutexLocker locker(&m_mutex); 243 QMutexLocker locker(&m_mutex);
291 /*!!! 244
292 while (!m_synthMap.empty()) { 245 while (!m_clipMixerMap.empty()) {
293 RealTimePluginInstance *instance = m_synthMap.begin()->second; 246 ClipMixer *mixer = m_clipMixerMap.begin()->second;
294 m_synthMap.erase(m_synthMap.begin()); 247 m_clipMixerMap.erase(m_clipMixerMap.begin());
295 delete instance; 248 delete mixer;
296 } 249 }
297 */
298 } 250 }
299 251
300 void 252 void
301 AudioGenerator::reset() 253 AudioGenerator::reset()
302 { 254 {
303 QMutexLocker locker(&m_mutex); 255 QMutexLocker locker(&m_mutex);
304 /*!!! 256
305 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 257 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
306 if (i->second) { 258 if (i->second) {
307 i->second->silence(); 259 i->second->reset();
308 i->second->discardEvents(); 260 }
309 } 261 }
310 }
311 */
312 262
313 m_noteOffs.clear(); 263 m_noteOffs.clear();
314 } 264 }
315 265
316 void 266 void
321 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl; 271 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl;
322 272
323 QMutexLocker locker(&m_mutex); 273 QMutexLocker locker(&m_mutex);
324 m_targetChannelCount = targetChannelCount; 274 m_targetChannelCount = targetChannelCount;
325 275
326 /*!!! 276 for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
327 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { 277 if (i->second) i->second->setChannelCount(targetChannelCount);
328 if (i->second) i->second->setIdealChannelCount(targetChannelCount); 278 }
329 }
330 */
331 } 279 }
332 280
333 size_t 281 size_t
334 AudioGenerator::getBlockSize() const 282 AudioGenerator::getBlockSize() const
335 { 283 {
513 size_t startFrame, size_t frames, 461 size_t startFrame, size_t frames,
514 float **buffer, float gain, float pan, 462 float **buffer, float gain, float pan,
515 size_t /* fadeIn */, 463 size_t /* fadeIn */,
516 size_t /* fadeOut */) 464 size_t /* fadeOut */)
517 { 465 {
518 RealTimePluginInstance *plugin = m_synthMap[model]; 466 ClipMixer *clipMixer = m_clipMixerMap[model];
519 if (!plugin) return 0; 467 if (!clipMixer) return 0;
520 468
521 size_t latency = plugin->getLatency();
522 size_t blocks = frames / m_processingBlockSize; 469 size_t blocks = frames / m_processingBlockSize;
523 470
524 //!!! hang on -- the fact that the audio callback play source's 471 //!!! hang on -- the fact that the audio callback play source's
525 //buffer is a multiple of the plugin's buffer size doesn't mean 472 //buffer is a multiple of the plugin's buffer size doesn't mean
526 //that we always get called for a multiple of it here (because it 473 //that we always get called for a multiple of it here (because it
536 #ifdef DEBUG_AUDIO_GENERATOR 483 #ifdef DEBUG_AUDIO_GENERATOR
537 cout << "mixModel [synthetic note]: frames " << frames 484 cout << "mixModel [synthetic note]: frames " << frames
538 << ", blocks " << blocks << endl; 485 << ", blocks " << blocks << endl;
539 #endif 486 #endif
540 487
541 snd_seq_event_t onEv; 488 ClipMixer::NoteStart on;
542 onEv.type = SND_SEQ_EVENT_NOTEON; 489 ClipMixer::NoteEnd off;
543 onEv.data.note.channel = 0; 490
544
545 snd_seq_event_t offEv;
546 offEv.type = SND_SEQ_EVENT_NOTEOFF;
547 offEv.data.note.channel = 0;
548 offEv.data.note.velocity = 0;
549
550 NoteOffSet &noteOffs = m_noteOffs[model]; 491 NoteOffSet &noteOffs = m_noteOffs[model];
492
493 float **bufferIndexes = new float *[m_targetChannelCount];
551 494
552 for (size_t i = 0; i < blocks; ++i) { 495 for (size_t i = 0; i < blocks; ++i) {
553 496
554 size_t reqStart = startFrame + i * m_processingBlockSize; 497 size_t reqStart = startFrame + i * m_processingBlockSize;
555 498
556 NoteList notes; 499 NoteList notes;
557 NoteExportable *exportable = dynamic_cast<NoteExportable *>(model); 500 NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
558 if (exportable) { 501 if (exportable) {
559 notes = exportable->getNotes(reqStart + latency, 502 notes = exportable->getNotes(reqStart,
560 reqStart + latency + m_processingBlockSize); 503 reqStart + m_processingBlockSize);
561 } 504 }
562 505
563 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime 506 std::vector<ClipMixer::NoteStart> starts;
564 (startFrame + i * m_processingBlockSize, m_sourceSampleRate); 507 std::vector<ClipMixer::NoteEnd> ends;
565 508
566 for (NoteList::const_iterator ni = notes.begin(); 509 for (NoteList::const_iterator ni = notes.begin();
567 ni != notes.end(); ++ni) { 510 ni != notes.end(); ++ni) {
568 511
569 size_t noteFrame = ni->start; 512 size_t noteFrame = ni->start;
570 513
571 if (noteFrame >= latency) noteFrame -= latency;
572
573 if (noteFrame < reqStart || 514 if (noteFrame < reqStart ||
574 noteFrame >= reqStart + m_processingBlockSize) continue; 515 noteFrame >= reqStart + m_processingBlockSize) continue;
575 516
576 while (noteOffs.begin() != noteOffs.end() && 517 while (noteOffs.begin() != noteOffs.end() &&
577 noteOffs.begin()->frame <= noteFrame) { 518 noteOffs.begin()->frame <= noteFrame) {
578 519
579 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 520 size_t eventFrame = noteOffs.begin()->frame;
580 (noteOffs.begin()->frame, m_sourceSampleRate); 521 if (eventFrame < reqStart) eventFrame = reqStart;
581 522
582 offEv.data.note.note = noteOffs.begin()->pitch; 523 off.frameOffset = eventFrame - reqStart;
583 524 off.frequency = noteOffs.begin()->frequency;
584 #ifdef DEBUG_AUDIO_GENERATOR 525
585 cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 526 #ifdef DEBUG_AUDIO_GENERATOR
586 #endif 527 cerr << "mixModel [synthetic]: adding note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
587 528 #endif
588 plugin->sendEvent(eventTime, &offEv); 529
530 ends.push_back(off);
589 noteOffs.erase(noteOffs.begin()); 531 noteOffs.erase(noteOffs.begin());
590 } 532 }
591 533
592 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 534 on.frameOffset = noteFrame - reqStart;
593 (noteFrame, m_sourceSampleRate); 535 on.frequency = ni->getFrequency();
536 on.level = float(ni->velocity) / 127.0;
537 on.pan = pan;
538
539 #ifdef DEBUG_AUDIO_GENERATOR
540 cout << "mixModel [synthetic]: adding note at frame " << noteFrame << ", frame offset " << on.frameOffset << " frequency " << on.frequency << endl;
541 #endif
594 542
595 if (ni->isMidiPitchQuantized) { 543 starts.push_back(on);
596 onEv.data.note.note = ni->midiPitch;
597 } else {
598 #ifdef DEBUG_AUDIO_GENERATOR
599 cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl;
600 #endif
601 onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency);
602 }
603
604 onEv.data.note.velocity = ni->velocity;
605
606 plugin->sendEvent(eventTime, &onEv);
607
608 #ifdef DEBUG_AUDIO_GENERATOR
609 cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_processingBlockSize) << ", resulting time " << eventTime << endl;
610 #endif
611
612 noteOffs.insert 544 noteOffs.insert
613 (NoteOff(onEv.data.note.note, noteFrame + ni->duration)); 545 (NoteOff(on.frequency, noteFrame + ni->duration));
614 } 546 }
615 547
616 while (noteOffs.begin() != noteOffs.end() && 548 while (noteOffs.begin() != noteOffs.end() &&
617 noteOffs.begin()->frame <= 549 noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
618 startFrame + i * m_processingBlockSize + m_processingBlockSize) { 550
619 551 size_t eventFrame = noteOffs.begin()->frame;
620 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 552 if (eventFrame < reqStart) eventFrame = reqStart;
621 (noteOffs.begin()->frame, m_sourceSampleRate); 553
622 554 off.frameOffset = eventFrame - reqStart;
623 offEv.data.note.note = noteOffs.begin()->pitch; 555 off.frequency = noteOffs.begin()->frequency;
624 556
625 #ifdef DEBUG_AUDIO_GENERATOR 557 #ifdef DEBUG_AUDIO_GENERATOR
626 cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl; 558 cerr << "mixModel [synthetic]: adding leftover note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
627 #endif 559 #endif
628 560
629 plugin->sendEvent(eventTime, &offEv); 561 ends.push_back(off);
630 noteOffs.erase(noteOffs.begin()); 562 noteOffs.erase(noteOffs.begin());
631 } 563 }
632
633 plugin->run(blockTime);
634 float **outs = plugin->getAudioOutputBuffers();
635 564
636 for (size_t c = 0; c < m_targetChannelCount; ++c) { 565 for (size_t c = 0; c < m_targetChannelCount; ++c) {
637 #ifdef DEBUG_AUDIO_GENERATOR 566 bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
638 cout << "mixModel [synthetic]: adding " << m_processingBlockSize << " samples from plugin output " << c << endl; 567 }
639 #endif 568
640 569 clipMixer->mix(bufferIndexes, gain, starts, ends);
641 size_t sourceChannel = (c % plugin->getAudioOutputCount()); 570 }
642 571
643 float channelGain = gain; 572 delete[] bufferIndexes;
644 if (pan != 0.0) {
645 if (c == 0) {
646 if (pan > 0.0) channelGain *= 1.0 - pan;
647 } else {
648 if (pan < 0.0) channelGain *= pan + 1.0;
649 }
650 }
651
652 for (size_t j = 0; j < m_processingBlockSize; ++j) {
653 buffer[c][i * m_processingBlockSize + j] +=
654 channelGain * outs[sourceChannel][j];
655 }
656 }
657 }
658 573
659 return got; 574 return got;
660 } 575 }