Mercurial > hg > svapp
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 ¬eOffs = m_noteOffs[sodm]; | 599 NoteOffSet ¬eOffs = 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 ¬eOffs = 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 |