Mercurial > hg > svapp
comparison audioio/AudioGenerator.cpp @ 239:4d1501b27075 integration_library
Merge from branch "sonification"
author | mathieub <mathieu.barthet@eecs.qmul.ac.uk> |
---|---|
date | Mon, 25 Jul 2011 17:57:59 +0100 |
parents | 1fcee2a1c03e |
children | a99de38af73f |
comparison
equal
deleted
inserted
replaced
237:1ebd8e13262d | 239:4d1501b27075 |
---|---|
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 size_t latency) | |
506 { | |
507 Notes notes; | |
508 | |
509 Note n; | |
510 n.pitch = 64; | |
511 n.duration = 0; | |
512 n.velocity = 100; | |
513 | |
514 SparseOneDimensionalModel *sodm = | |
515 qobject_cast<SparseOneDimensionalModel *>(model); | |
516 | |
517 if (sodm) { | |
518 | |
519 SparseOneDimensionalModel::PointList points = | |
520 sodm->getPoints(startFrame + latency, | |
521 startFrame + frameCount + latency); | |
522 | |
523 for (SparseOneDimensionalModel::PointList::iterator pli = | |
524 points.begin(); pli != points.end(); ++pli) { | |
525 size_t frame = pli->frame; | |
526 if (frame > latency) frame -= latency; | |
527 if (frame < startFrame || frame >= startFrame + frameCount) { | |
528 continue; | |
529 } | |
530 n.frame = frame; | |
531 notes.push_back(n); | |
532 } | |
533 } | |
534 | |
535 NoteModel *nm = | |
536 qobject_cast<NoteModel *>(model); | |
537 | |
538 if (nm) { | |
539 | |
540 NoteModel::PointList points = | |
541 nm->getPoints(startFrame + latency, | |
542 startFrame + frameCount + latency); | |
543 | |
544 for (NoteModel::PointList::iterator pli = | |
545 points.begin(); pli != points.end(); ++pli) { | |
546 | |
547 size_t frame = pli->frame; | |
548 if (frame > latency) frame -= latency; | |
549 if (frame < startFrame || frame >= startFrame + frameCount) { | |
550 continue; | |
551 } | |
552 | |
553 n.frame = frame; | |
554 n.duration = pli->duration; | |
555 if (n.duration == 1) n.duration = m_sourceSampleRate / 20; | |
556 | |
557 if (nm->getScaleUnits() == "Hz") { | |
558 n.pitch = Pitch::getPitchForFrequency(pli->value); | |
559 } else { | |
560 n.pitch = lrintf(pli->value); | |
561 } | |
562 | |
563 if (pli->level > 0.f && pli->level <= 1.f) { | |
564 n.velocity = lrintf(pli->level * 127); | |
565 } | |
566 | |
567 notes.push_back(n); | |
568 } | |
569 } | |
570 | |
571 return notes; | |
572 } | |
573 | |
501 size_t | 574 size_t |
502 AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm, | 575 AudioGenerator::mixSparseModel(Model *model, |
503 size_t startFrame, size_t frames, | 576 size_t startFrame, size_t frames, |
504 float **buffer, float gain, float pan, | 577 float **buffer, float gain, float pan, |
505 size_t /* fadeIn */, | 578 size_t /* fadeIn */, |
506 size_t /* fadeOut */) | 579 size_t /* fadeOut */) |
507 { | 580 { |
508 RealTimePluginInstance *plugin = m_synthMap[sodm]; | 581 RealTimePluginInstance *plugin = m_synthMap[model]; |
509 if (!plugin) return 0; | 582 if (!plugin) { |
583 SVDEBUG << "AudioGenerator::mixSparseModel: No plugin" << endl; | |
584 return 0; | |
585 } | |
586 | |
587 SVDEBUG << "AudioGenerator::mixSparseModel: Have plugin" << endl; | |
510 | 588 |
511 size_t latency = plugin->getLatency(); | 589 size_t latency = plugin->getLatency(); |
512 size_t blocks = frames / m_pluginBlockSize; | 590 size_t blocks = frames / m_pluginBlockSize; |
513 | 591 |
514 //!!! hang on -- the fact that the audio callback play source's | 592 //!!! hang on -- the fact that the audio callback play source's |
529 #endif | 607 #endif |
530 | 608 |
531 snd_seq_event_t onEv; | 609 snd_seq_event_t onEv; |
532 onEv.type = SND_SEQ_EVENT_NOTEON; | 610 onEv.type = SND_SEQ_EVENT_NOTEON; |
533 onEv.data.note.channel = 0; | 611 onEv.data.note.channel = 0; |
534 onEv.data.note.note = 64; | |
535 onEv.data.note.velocity = 100; | |
536 | 612 |
537 snd_seq_event_t offEv; | 613 snd_seq_event_t offEv; |
538 offEv.type = SND_SEQ_EVENT_NOTEOFF; | 614 offEv.type = SND_SEQ_EVENT_NOTEOFF; |
539 offEv.data.note.channel = 0; | 615 offEv.data.note.channel = 0; |
540 offEv.data.note.velocity = 0; | 616 offEv.data.note.velocity = 0; |
541 | 617 |
542 NoteOffSet ¬eOffs = m_noteOffs[sodm]; | 618 NoteOffSet ¬eOffs = m_noteOffs[model]; |
619 | |
620 int halfSecond = 0.5 * m_sourceSampleRate; | |
543 | 621 |
544 for (size_t i = 0; i < blocks; ++i) { | 622 for (size_t i = 0; i < blocks; ++i) { |
545 | 623 |
546 size_t reqStart = startFrame + i * m_pluginBlockSize; | 624 size_t reqStart = startFrame + i * m_pluginBlockSize; |
547 | |
548 SparseOneDimensionalModel::PointList points = | |
549 sodm->getPoints(reqStart + latency, | |
550 reqStart + latency + m_pluginBlockSize); | |
551 | 625 |
552 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime | 626 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime |
553 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); | 627 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); |
554 | 628 |
555 for (SparseOneDimensionalModel::PointList::iterator pli = | 629 Notes notes = getNotesFromModel |
556 points.begin(); pli != points.end(); ++pli) { | 630 (model, reqStart, m_pluginBlockSize, latency); |
557 | 631 |
558 size_t pliFrame = pli->frame; | 632 for (Notes::const_iterator ni = notes.begin(); ni != notes.end(); ++ni) { |
559 | 633 |
560 if (pliFrame >= latency) pliFrame -= latency; | 634 size_t frame = ni->frame; |
561 | |
562 if (pliFrame < reqStart || | |
563 pliFrame >= reqStart + m_pluginBlockSize) continue; | |
564 | 635 |
565 while (noteOffs.begin() != noteOffs.end() && | 636 while (noteOffs.begin() != noteOffs.end() && |
566 noteOffs.begin()->frame <= pliFrame) { | 637 noteOffs.begin()->frame <= frame) { |
567 | 638 |
568 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | 639 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime |
569 (noteOffs.begin()->frame, m_sourceSampleRate); | 640 (noteOffs.begin()->frame, m_sourceSampleRate); |
570 | 641 |
571 offEv.data.note.note = noteOffs.begin()->pitch; | 642 offEv.data.note.note = noteOffs.begin()->pitch; |
572 | 643 |
573 #ifdef DEBUG_AUDIO_GENERATOR | 644 #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; | 645 std::cerr << "mixModel: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; |
575 #endif | 646 #endif |
576 | 647 |
577 plugin->sendEvent(eventTime, &offEv); | 648 plugin->sendEvent(eventTime, &offEv); |
578 noteOffs.erase(noteOffs.begin()); | 649 noteOffs.erase(noteOffs.begin()); |
579 } | 650 } |
580 | 651 |
581 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | 652 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime |
582 (pliFrame, m_sourceSampleRate); | 653 (frame, m_sourceSampleRate); |
583 | 654 |
655 onEv.data.note.note = ni->pitch; | |
656 onEv.data.note.velocity = ni->velocity; | |
657 | |
584 plugin->sendEvent(eventTime, &onEv); | 658 plugin->sendEvent(eventTime, &onEv); |
585 | 659 |
586 #ifdef DEBUG_AUDIO_GENERATOR | 660 #ifdef DEBUG_AUDIO_GENERATOR |
587 std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; | 661 std::cerr << "mixModel: point at frame " << frame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; |
588 #endif | 662 #endif |
589 | 663 |
590 size_t duration = 7000; // frames [for now] | 664 size_t duration = ni->duration; |
665 if (duration == 0) duration = halfSecond; | |
666 | |
591 NoteOff noff; | 667 NoteOff noff; |
592 noff.pitch = onEv.data.note.note; | 668 noff.pitch = onEv.data.note.note; |
593 noff.frame = pliFrame + duration; | 669 noff.frame = frame + duration; |
594 noteOffs.insert(noff); | 670 noteOffs.insert(noff); |
595 } | 671 } |
596 | 672 |
597 while (noteOffs.begin() != noteOffs.end() && | 673 while (noteOffs.begin() != noteOffs.end() && |
598 noteOffs.begin()->frame <= | 674 noteOffs.begin()->frame <= |
638 } | 714 } |
639 | 715 |
640 return got; | 716 return got; |
641 } | 717 } |
642 | 718 |
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 |