comparison audioio/AudioGenerator.cpp @ 234:a98f1638c5ec sonification

Refactor mixNoteModel and mixSparseOneDimensionalModel into a single mixSparseModel -- attempting to clear the decks a bit for asynchronous example-note playing
author Chris Cannam
date Fri, 24 Jun 2011 15:39:00 +0100
parents 8aace2d9f1c2
children 1fcee2a1c03e
comparison
equal deleted inserted replaced
233:8aace2d9f1c2 234:a98f1638c5ec
388 } 388 }
389 389
390 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> 390 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
391 (model); 391 (model);
392 if (sodm) { 392 if (sodm) {
393 return mixSparseOneDimensionalModel(sodm, startFrame, frameCount, 393 return mixSparseModel(sodm, startFrame, frameCount,
394 buffer, gain, pan, fadeIn, fadeOut); 394 buffer, gain, pan, fadeIn, fadeOut);
395 } 395 }
396 396
397 NoteModel *nm = dynamic_cast<NoteModel *>(model); 397 NoteModel *nm = dynamic_cast<NoteModel *>(model);
398 if (nm) { 398 if (nm) {
399 return mixNoteModel(nm, startFrame, frameCount, 399 return mixSparseModel(nm, startFrame, frameCount,
400 buffer, gain, pan, fadeIn, fadeOut); 400 buffer, gain, pan, fadeIn, fadeOut);
401 } 401 }
402 402
403 return frameCount; 403 return frameCount;
404 } 404 }
405 405
496 } 496 }
497 497
498 return got; 498 return got;
499 } 499 }
500 500
501 AudioGenerator::Notes
502 AudioGenerator::getNotesFromModel(Model *model,
503 size_t startFrame,
504 size_t frameCount)
505 {
506 Notes notes;
507
508 Note n;
509 n.pitch = 64;
510 n.duration = 0;
511 n.velocity = 100;
512
513 SparseOneDimensionalModel *sodm =
514 qobject_cast<SparseOneDimensionalModel *>(model);
515
516 if (sodm) {
517
518 SparseOneDimensionalModel::PointList points =
519 sodm->getPoints(startFrame, startFrame + frameCount);
520
521 for (SparseOneDimensionalModel::PointList::iterator pli =
522 points.begin(); pli != points.end(); ++pli) {
523 n.frame = pli->frame;
524 notes.push_back(n);
525 }
526 }
527
528 NoteModel *nm =
529 qobject_cast<NoteModel *>(model);
530
531 if (nm) {
532
533 NoteModel::PointList points =
534 nm->getPoints(startFrame, startFrame + frameCount);
535
536 for (NoteModel::PointList::iterator pli =
537 points.begin(); pli != points.end(); ++pli) {
538
539 n.frame = pli->frame;
540 n.duration = pli->duration;
541 if (n.duration == 1) n.duration = m_sourceSampleRate / 20;
542
543 if (nm->getScaleUnits() == "Hz") {
544 n.pitch = Pitch::getPitchForFrequency(pli->value);
545 } else {
546 n.pitch = lrintf(pli->value);
547 }
548
549 if (pli->level > 0.f && pli->level <= 1.f) {
550 n.velocity = lrintf(pli->level * 127);
551 }
552
553 notes.push_back(n);
554 }
555 }
556
557 return notes;
558 }
559
501 size_t 560 size_t
502 AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm, 561 AudioGenerator::mixSparseModel(Model *model,
503 size_t startFrame, size_t frames, 562 size_t startFrame, size_t frames,
504 float **buffer, float gain, float pan, 563 float **buffer, float gain, float pan,
505 size_t /* fadeIn */, 564 size_t /* fadeIn */,
506 size_t /* fadeOut */) 565 size_t /* fadeOut */)
507 { 566 {
508 RealTimePluginInstance *plugin = m_synthMap[sodm]; 567 RealTimePluginInstance *plugin = m_synthMap[model];
509 if (!plugin) return 0; 568 if (!plugin) return 0;
510 569
511 size_t latency = plugin->getLatency(); 570 size_t latency = plugin->getLatency();
512 size_t blocks = frames / m_pluginBlockSize; 571 size_t blocks = frames / m_pluginBlockSize;
513 572
529 #endif 588 #endif
530 589
531 snd_seq_event_t onEv; 590 snd_seq_event_t onEv;
532 onEv.type = SND_SEQ_EVENT_NOTEON; 591 onEv.type = SND_SEQ_EVENT_NOTEON;
533 onEv.data.note.channel = 0; 592 onEv.data.note.channel = 0;
534 onEv.data.note.note = 64;
535 onEv.data.note.velocity = 100;
536 593
537 snd_seq_event_t offEv; 594 snd_seq_event_t offEv;
538 offEv.type = SND_SEQ_EVENT_NOTEOFF; 595 offEv.type = SND_SEQ_EVENT_NOTEOFF;
539 offEv.data.note.channel = 0; 596 offEv.data.note.channel = 0;
540 offEv.data.note.velocity = 0; 597 offEv.data.note.velocity = 0;
541 598
542 NoteOffSet &noteOffs = m_noteOffs[sodm]; 599 NoteOffSet &noteOffs = m_noteOffs[model];
600
601 int halfSecond = 0.5 * m_sourceSampleRate;
543 602
544 for (size_t i = 0; i < blocks; ++i) { 603 for (size_t i = 0; i < blocks; ++i) {
545 604
546 size_t reqStart = startFrame + i * m_pluginBlockSize; 605 size_t reqStart = startFrame + i * m_pluginBlockSize;
547
548 SparseOneDimensionalModel::PointList points =
549 sodm->getPoints(reqStart + latency,
550 reqStart + latency + m_pluginBlockSize);
551 606
552 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime 607 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
553 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); 608 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
554 609
555 for (SparseOneDimensionalModel::PointList::iterator pli = 610 Notes notes = getNotesFromModel
556 points.begin(); pli != points.end(); ++pli) { 611 (model, reqStart + latency, m_pluginBlockSize);
557 612
558 size_t pliFrame = pli->frame; 613 for (Notes::const_iterator ni = notes.begin(); ni != notes.end(); ++ni) {
559 614
560 if (pliFrame >= latency) pliFrame -= latency; 615 size_t frame = ni->frame;
561 616 if (frame > latency) frame -= latency;
562 if (pliFrame < reqStart || 617
563 pliFrame >= reqStart + m_pluginBlockSize) continue; 618 if (frame < reqStart ||
619 frame >= reqStart + m_pluginBlockSize) {
620 continue;
621 }
564 622
565 while (noteOffs.begin() != noteOffs.end() && 623 while (noteOffs.begin() != noteOffs.end() &&
566 noteOffs.begin()->frame <= pliFrame) { 624 noteOffs.begin()->frame <= frame) {
567 625
568 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 626 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
569 (noteOffs.begin()->frame, m_sourceSampleRate); 627 (noteOffs.begin()->frame, m_sourceSampleRate);
570 628
571 offEv.data.note.note = noteOffs.begin()->pitch; 629 offEv.data.note.note = noteOffs.begin()->pitch;
572 630
573 #ifdef DEBUG_AUDIO_GENERATOR 631 #ifdef DEBUG_AUDIO_GENERATOR
574 std::cerr << "mixModel [sparse]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; 632 std::cerr << "mixModel: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
575 #endif 633 #endif
576 634
577 plugin->sendEvent(eventTime, &offEv); 635 plugin->sendEvent(eventTime, &offEv);
578 noteOffs.erase(noteOffs.begin()); 636 noteOffs.erase(noteOffs.begin());
579 } 637 }
580 638
581 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime 639 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
582 (pliFrame, m_sourceSampleRate); 640 (frame, m_sourceSampleRate);
583 641
642 onEv.data.note.note = ni->pitch;
643 onEv.data.note.velocity = ni->velocity;
644
584 plugin->sendEvent(eventTime, &onEv); 645 plugin->sendEvent(eventTime, &onEv);
585 646
586 #ifdef DEBUG_AUDIO_GENERATOR 647 #ifdef DEBUG_AUDIO_GENERATOR
587 std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; 648 std::cerr << "mixModel: point at frame " << frame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
588 #endif 649 #endif
589 650
590 size_t duration = 7000; // frames [for now] 651 size_t duration = ni->duration;
652 if (duration == 0) duration = halfSecond;
653
591 NoteOff noff; 654 NoteOff noff;
592 noff.pitch = onEv.data.note.note; 655 noff.pitch = onEv.data.note.note;
593 noff.frame = pliFrame + duration; 656 noff.frame = frame + duration;
594 noteOffs.insert(noff); 657 noteOffs.insert(noff);
595 } 658 }
596 659
597 while (noteOffs.begin() != noteOffs.end() && 660 while (noteOffs.begin() != noteOffs.end() &&
598 noteOffs.begin()->frame <= 661 noteOffs.begin()->frame <=
638 } 701 }
639 702
640 return got; 703 return got;
641 } 704 }
642 705
643
644 //!!! mucho duplication with above -- refactor
645 size_t
646 AudioGenerator::mixNoteModel(NoteModel *nm,
647 size_t startFrame, size_t frames,
648 float **buffer, float gain, float pan,
649 size_t /* fadeIn */,
650 size_t /* fadeOut */)
651 {
652 RealTimePluginInstance *plugin = m_synthMap[nm];
653 if (!plugin) return 0;
654
655 size_t latency = plugin->getLatency();
656 size_t blocks = frames / m_pluginBlockSize;
657
658 //!!! hang on -- the fact that the audio callback play source's
659 //buffer is a multiple of the plugin's buffer size doesn't mean
660 //that we always get called for a multiple of it here (because it
661 //also depends on the JACK block size). how should we ensure that
662 //all models write the same amount in to the mix, and that we
663 //always have a multiple of the plugin buffer size? I guess this
664 //class has to be queryable for the plugin buffer size & the
665 //callback play source has to use that as a multiple for all the
666 //calls to mixModel
667
668 size_t got = blocks * m_pluginBlockSize;
669
670 #ifdef DEBUG_AUDIO_GENERATOR
671 Vamp::RealTime startTime = Vamp::RealTime::frame2RealTime
672 (startFrame, m_sourceSampleRate);
673
674 std::cout << "mixModel [note]: frames " << frames << " from " << startFrame
675 << " (time " << startTime << "), blocks " << blocks << std::endl;
676 #endif
677
678 snd_seq_event_t onEv;
679 onEv.type = SND_SEQ_EVENT_NOTEON;
680 onEv.data.note.channel = 0;
681 onEv.data.note.note = 64;
682 onEv.data.note.velocity = 100;
683
684 snd_seq_event_t offEv;
685 offEv.type = SND_SEQ_EVENT_NOTEOFF;
686 offEv.data.note.channel = 0;
687 offEv.data.note.velocity = 0;
688
689 NoteOffSet &noteOffs = m_noteOffs[nm];
690
691 for (size_t i = 0; i < blocks; ++i) {
692
693 size_t reqStart = startFrame + i * m_pluginBlockSize;
694
695 NoteModel::PointList points =
696 nm->getPoints(reqStart + latency,
697 reqStart + latency + m_pluginBlockSize);
698
699 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
700 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
701
702 for (NoteModel::PointList::iterator pli =
703 points.begin(); pli != points.end(); ++pli) {
704
705 size_t pliFrame = pli->frame;
706
707 if (pliFrame >= latency) pliFrame -= latency;
708
709 if (pliFrame < reqStart ||
710 pliFrame >= reqStart + m_pluginBlockSize) continue;
711
712 while (noteOffs.begin() != noteOffs.end() &&
713 noteOffs.begin()->frame <= pliFrame) {
714
715 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
716 (noteOffs.begin()->frame, m_sourceSampleRate);
717
718 offEv.data.note.note = noteOffs.begin()->pitch;
719
720 #ifdef DEBUG_AUDIO_GENERATOR
721 std::cerr << "mixModel [note]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
722 #endif
723
724 plugin->sendEvent(eventTime, &offEv);
725 noteOffs.erase(noteOffs.begin());
726 }
727
728 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
729 (pliFrame, m_sourceSampleRate);
730
731 if (nm->getScaleUnits() == "Hz") {
732 onEv.data.note.note = Pitch::getPitchForFrequency(pli->value);
733 } else {
734 onEv.data.note.note = lrintf(pli->value);
735 }
736
737 if (pli->level > 0.f && pli->level <= 1.f) {
738 onEv.data.note.velocity = lrintf(pli->level * 127);
739 } else {
740 onEv.data.note.velocity = 100;
741 }
742
743 plugin->sendEvent(eventTime, &onEv);
744
745 #ifdef DEBUG_AUDIO_GENERATOR
746 std::cout << "mixModel [note]: point at frame " << pliFrame << ", pitch " << (int)onEv.data.note.note << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
747 #endif
748
749 size_t duration = pli->duration;
750 if (duration == 0 || duration == 1) {
751 duration = m_sourceSampleRate / 20;
752 }
753 NoteOff noff;
754 noff.pitch = onEv.data.note.note;
755 noff.frame = pliFrame + duration;
756 noteOffs.insert(noff);
757
758 #ifdef DEBUG_AUDIO_GENERATOR
759 std::cout << "mixModel [note]: recording note off at " << noff.frame << std::endl;
760 #endif
761 }
762
763 while (noteOffs.begin() != noteOffs.end() &&
764 noteOffs.begin()->frame <=
765 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
766
767 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
768 (noteOffs.begin()->frame, m_sourceSampleRate);
769
770 offEv.data.note.note = noteOffs.begin()->pitch;
771
772 #ifdef DEBUG_AUDIO_GENERATOR
773 std::cerr << "mixModel [note]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
774 #endif
775
776 plugin->sendEvent(eventTime, &offEv);
777 noteOffs.erase(noteOffs.begin());
778 }
779
780 plugin->run(blockTime);
781 float **outs = plugin->getAudioOutputBuffers();
782
783 for (size_t c = 0; c < m_targetChannelCount; ++c) {
784 #ifdef DEBUG_AUDIO_GENERATOR
785 std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
786 #endif
787
788 size_t sourceChannel = (c % plugin->getAudioOutputCount());
789
790 float channelGain = gain;
791 if (pan != 0.0) {
792 if (c == 0) {
793 if (pan > 0.0) channelGain *= 1.0 - pan;
794 } else {
795 if (pan < 0.0) channelGain *= pan + 1.0;
796 }
797 }
798
799 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
800 buffer[c][i * m_pluginBlockSize + j] +=
801 channelGain * outs[sourceChannel][j];
802 }
803 }
804 }
805
806 return got;
807 }
808