Mercurial > hg > svapp
comparison audioio/AudioGenerator.cpp @ 277:e647e880e711 tonioni
Merge from the default branch
author | Chris Cannam |
---|---|
date | Mon, 15 Apr 2013 17:38:30 +0100 |
parents | 64724dbc5f05 6c6950bd7d53 |
children | 7be7b43b7264 |
comparison
equal
deleted
inserted
replaced
276:64724dbc5f05 | 277:e647e880e711 |
---|---|
396 } | 396 } |
397 | 397 |
398 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 398 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> |
399 (model); | 399 (model); |
400 if (sodm) { | 400 if (sodm) { |
401 return mixSparseOneDimensionalModel(sodm, startFrame, frameCount, | 401 return mixSyntheticNoteModel(model, startFrame, frameCount, |
402 buffer, gain, pan, fadeIn, fadeOut); | 402 buffer, gain, pan, fadeIn, fadeOut); |
403 } | 403 } |
404 | 404 |
405 NoteModel *nm = dynamic_cast<NoteModel *>(model); | 405 NoteModel *nm = dynamic_cast<NoteModel *>(model); |
406 if (nm) { | 406 if (nm) { |
407 return mixNoteModel(nm, startFrame, frameCount, | 407 return mixSyntheticNoteModel(model, startFrame, frameCount, |
408 buffer, gain, pan, fadeIn, fadeOut); | 408 buffer, gain, pan, fadeIn, fadeOut); |
409 } | 409 } |
410 | 410 |
411 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; | 411 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; |
412 | 412 |
413 return frameCount; | 413 return frameCount; |
507 | 507 |
508 return got; | 508 return got; |
509 } | 509 } |
510 | 510 |
511 size_t | 511 size_t |
512 AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm, | 512 AudioGenerator::mixSyntheticNoteModel(Model *model, |
513 size_t startFrame, size_t frames, | 513 size_t startFrame, size_t frames, |
514 float **buffer, float gain, float pan, | 514 float **buffer, float gain, float pan, |
515 size_t /* fadeIn */, | 515 size_t /* fadeIn */, |
516 size_t /* fadeOut */) | 516 size_t /* fadeOut */) |
517 { | 517 { |
518 RealTimePluginInstance *plugin = m_synthMap[sodm]; | 518 RealTimePluginInstance *plugin = m_synthMap[model]; |
519 if (!plugin) return 0; | 519 if (!plugin) return 0; |
520 | 520 |
521 size_t latency = plugin->getLatency(); | 521 size_t latency = plugin->getLatency(); |
522 size_t blocks = frames / m_pluginBlockSize; | 522 size_t blocks = frames / m_pluginBlockSize; |
523 | 523 |
532 //calls to mixModel | 532 //calls to mixModel |
533 | 533 |
534 size_t got = blocks * m_pluginBlockSize; | 534 size_t got = blocks * m_pluginBlockSize; |
535 | 535 |
536 #ifdef DEBUG_AUDIO_GENERATOR | 536 #ifdef DEBUG_AUDIO_GENERATOR |
537 std::cout << "mixModel [sparse]: frames " << frames | 537 std::cout << "mixModel [synthetic note]: frames " << frames |
538 << ", blocks " << blocks << std::endl; | 538 << ", blocks " << blocks << std::endl; |
539 #endif | 539 #endif |
540 | 540 |
541 snd_seq_event_t onEv; | 541 snd_seq_event_t onEv; |
542 onEv.type = SND_SEQ_EVENT_NOTEON; | 542 onEv.type = SND_SEQ_EVENT_NOTEON; |
543 onEv.data.note.channel = 0; | 543 onEv.data.note.channel = 0; |
544 onEv.data.note.note = 64; | |
545 onEv.data.note.velocity = 100; | |
546 | 544 |
547 snd_seq_event_t offEv; | 545 snd_seq_event_t offEv; |
548 offEv.type = SND_SEQ_EVENT_NOTEOFF; | 546 offEv.type = SND_SEQ_EVENT_NOTEOFF; |
549 offEv.data.note.channel = 0; | 547 offEv.data.note.channel = 0; |
550 offEv.data.note.velocity = 0; | 548 offEv.data.note.velocity = 0; |
551 | 549 |
552 NoteOffSet ¬eOffs = m_noteOffs[sodm]; | 550 NoteOffSet ¬eOffs = m_noteOffs[model]; |
553 | 551 |
554 for (size_t i = 0; i < blocks; ++i) { | 552 for (size_t i = 0; i < blocks; ++i) { |
555 | 553 |
556 size_t reqStart = startFrame + i * m_pluginBlockSize; | 554 size_t reqStart = startFrame + i * m_pluginBlockSize; |
557 | 555 |
558 SparseOneDimensionalModel::PointList points = | 556 NoteList notes = getNotes(model, |
559 sodm->getPoints(reqStart + latency, | 557 reqStart + latency, |
560 reqStart + latency + m_pluginBlockSize); | 558 reqStart + latency + m_pluginBlockSize); |
561 | 559 |
562 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime | 560 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime |
563 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); | 561 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); |
564 | 562 |
565 for (SparseOneDimensionalModel::PointList::iterator pli = | 563 for (NoteList::const_iterator ni = notes.begin(); |
566 points.begin(); pli != points.end(); ++pli) { | 564 ni != notes.end(); ++ni) { |
567 | 565 |
568 size_t pliFrame = pli->frame; | 566 size_t noteFrame = ni->start; |
569 | 567 |
570 if (pliFrame >= latency) pliFrame -= latency; | 568 if (noteFrame >= latency) noteFrame -= latency; |
571 | 569 |
572 if (pliFrame < reqStart || | 570 if (noteFrame < reqStart || |
573 pliFrame >= reqStart + m_pluginBlockSize) continue; | 571 noteFrame >= reqStart + m_pluginBlockSize) continue; |
574 | 572 |
575 while (noteOffs.begin() != noteOffs.end() && | 573 while (noteOffs.begin() != noteOffs.end() && |
576 noteOffs.begin()->frame <= pliFrame) { | 574 noteOffs.begin()->frame <= noteFrame) { |
577 | 575 |
578 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | 576 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime |
579 (noteOffs.begin()->frame, m_sourceSampleRate); | 577 (noteOffs.begin()->frame, m_sourceSampleRate); |
580 | 578 |
581 offEv.data.note.note = noteOffs.begin()->pitch; | 579 offEv.data.note.note = noteOffs.begin()->pitch; |
582 | 580 |
583 #ifdef DEBUG_AUDIO_GENERATOR | 581 #ifdef DEBUG_AUDIO_GENERATOR |
584 std::cerr << "mixModel [sparse]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; | 582 std::cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; |
585 #endif | 583 #endif |
586 | 584 |
587 plugin->sendEvent(eventTime, &offEv); | 585 plugin->sendEvent(eventTime, &offEv); |
588 noteOffs.erase(noteOffs.begin()); | 586 noteOffs.erase(noteOffs.begin()); |
589 } | 587 } |
590 | 588 |
591 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | 589 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime |
592 (pliFrame, m_sourceSampleRate); | 590 (noteFrame, m_sourceSampleRate); |
593 | 591 |
592 if (ni->isMidiPitchQuantized) { | |
593 onEv.data.note.note = ni->midiPitch; | |
594 } else { | |
595 #ifdef DEBUG_AUDIO_GENERATOR | |
596 std::cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << std::endl; | |
597 #endif | |
598 onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency); | |
599 } | |
600 | |
601 onEv.data.note.velocity = ni->velocity; | |
602 | |
594 plugin->sendEvent(eventTime, &onEv); | 603 plugin->sendEvent(eventTime, &onEv); |
595 | 604 |
596 #ifdef DEBUG_AUDIO_GENERATOR | 605 #ifdef DEBUG_AUDIO_GENERATOR |
597 std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; | 606 std::cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; |
598 #endif | 607 #endif |
599 | 608 |
600 size_t duration = 7000; // frames [for now] | 609 noteOffs.insert |
601 NoteOff noff; | 610 (NoteOff(onEv.data.note.note, noteFrame + ni->duration)); |
602 noff.pitch = onEv.data.note.note; | |
603 noff.frame = pliFrame + duration; | |
604 noteOffs.insert(noff); | |
605 } | 611 } |
606 | 612 |
607 while (noteOffs.begin() != noteOffs.end() && | 613 while (noteOffs.begin() != noteOffs.end() && |
608 noteOffs.begin()->frame <= | 614 noteOffs.begin()->frame <= |
609 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { | 615 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { |
612 (noteOffs.begin()->frame, m_sourceSampleRate); | 618 (noteOffs.begin()->frame, m_sourceSampleRate); |
613 | 619 |
614 offEv.data.note.note = noteOffs.begin()->pitch; | 620 offEv.data.note.note = noteOffs.begin()->pitch; |
615 | 621 |
616 #ifdef DEBUG_AUDIO_GENERATOR | 622 #ifdef DEBUG_AUDIO_GENERATOR |
617 std::cerr << "mixModel [sparse]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; | 623 std::cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; |
618 #endif | 624 #endif |
619 | 625 |
620 plugin->sendEvent(eventTime, &offEv); | 626 plugin->sendEvent(eventTime, &offEv); |
621 noteOffs.erase(noteOffs.begin()); | 627 noteOffs.erase(noteOffs.begin()); |
622 } | 628 } |
624 plugin->run(blockTime); | 630 plugin->run(blockTime); |
625 float **outs = plugin->getAudioOutputBuffers(); | 631 float **outs = plugin->getAudioOutputBuffers(); |
626 | 632 |
627 for (size_t c = 0; c < m_targetChannelCount; ++c) { | 633 for (size_t c = 0; c < m_targetChannelCount; ++c) { |
628 #ifdef DEBUG_AUDIO_GENERATOR | 634 #ifdef DEBUG_AUDIO_GENERATOR |
629 std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; | 635 std::cout << "mixModel [synthetic]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; |
630 #endif | 636 #endif |
631 | 637 |
632 size_t sourceChannel = (c % plugin->getAudioOutputCount()); | 638 size_t sourceChannel = (c % plugin->getAudioOutputCount()); |
633 | 639 |
634 float channelGain = gain; | 640 float channelGain = gain; |
648 } | 654 } |
649 | 655 |
650 return got; | 656 return got; |
651 } | 657 } |
652 | 658 |
653 | 659 AudioGenerator::NoteList |
654 //!!! mucho duplication with above -- refactor | 660 AudioGenerator::getNotes(Model *model, |
655 size_t | 661 size_t startFrame, |
656 AudioGenerator::mixNoteModel(NoteModel *nm, | 662 size_t endFrame) |
657 size_t startFrame, size_t frames, | 663 { |
658 float **buffer, float gain, float pan, | 664 NoteList notes; |
659 size_t /* fadeIn */, | 665 |
660 size_t /* fadeOut */) | 666 SparseOneDimensionalModel *sodm = |
661 { | 667 qobject_cast<SparseOneDimensionalModel *>(model); |
662 RealTimePluginInstance *plugin = m_synthMap[nm]; | 668 |
663 if (!plugin) return 0; | 669 if (sodm) { |
664 | 670 |
665 size_t latency = plugin->getLatency(); | 671 SparseOneDimensionalModel::PointList points = |
666 size_t blocks = frames / m_pluginBlockSize; | 672 sodm->getPoints(startFrame, endFrame); |
667 | 673 |
668 //!!! hang on -- the fact that the audio callback play source's | 674 for (SparseOneDimensionalModel::PointList::iterator pli = |
669 //buffer is a multiple of the plugin's buffer size doesn't mean | 675 points.begin(); pli != points.end(); ++pli) { |
670 //that we always get called for a multiple of it here (because it | 676 |
671 //also depends on the JACK block size). how should we ensure that | 677 notes.push_back |
672 //all models write the same amount in to the mix, and that we | 678 (NoteData(pli->frame, |
673 //always have a multiple of the plugin buffer size? I guess this | 679 m_sourceSampleRate / 6, // arbitrary short duration |
674 //class has to be queryable for the plugin buffer size & the | 680 64, // default pitch |
675 //callback play source has to use that as a multiple for all the | 681 100)); // default velocity |
676 //calls to mixModel | 682 } |
677 | 683 |
678 size_t got = blocks * m_pluginBlockSize; | 684 return notes; |
679 | 685 } |
680 #ifdef DEBUG_AUDIO_GENERATOR | 686 |
681 Vamp::RealTime startTime = Vamp::RealTime::frame2RealTime | 687 NoteModel *nm = qobject_cast<NoteModel *>(model); |
682 (startFrame, m_sourceSampleRate); | 688 |
683 | 689 if (nm) { |
684 std::cout << "mixModel [note]: frames " << frames << " from " << startFrame | 690 |
685 << " (time " << startTime << "), blocks " << blocks << std::endl; | |
686 #endif | |
687 | |
688 snd_seq_event_t onEv; | |
689 onEv.type = SND_SEQ_EVENT_NOTEON; | |
690 onEv.data.note.channel = 0; | |
691 onEv.data.note.note = 64; | |
692 onEv.data.note.velocity = 100; | |
693 | |
694 snd_seq_event_t offEv; | |
695 offEv.type = SND_SEQ_EVENT_NOTEOFF; | |
696 offEv.data.note.channel = 0; | |
697 offEv.data.note.velocity = 0; | |
698 | |
699 NoteOffSet ¬eOffs = m_noteOffs[nm]; | |
700 | |
701 for (size_t i = 0; i < blocks; ++i) { | |
702 | |
703 size_t reqStart = startFrame + i * m_pluginBlockSize; | |
704 | |
705 NoteModel::PointList points = | 691 NoteModel::PointList points = |
706 nm->getPoints(reqStart + latency, | 692 nm->getPoints(startFrame, endFrame); |
707 reqStart + latency + m_pluginBlockSize); | 693 |
708 | 694 for (NoteModel::PointList::iterator pli = |
709 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime | |
710 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); | |
711 | |
712 for (NoteModel::PointList::iterator pli = | |
713 points.begin(); pli != points.end(); ++pli) { | 695 points.begin(); pli != points.end(); ++pli) { |
714 | 696 |
715 size_t pliFrame = pli->frame; | |
716 | |
717 if (pliFrame >= latency) pliFrame -= latency; | |
718 | |
719 if (pliFrame < reqStart || | |
720 pliFrame >= reqStart + m_pluginBlockSize) continue; | |
721 | |
722 while (noteOffs.begin() != noteOffs.end() && | |
723 noteOffs.begin()->frame <= pliFrame) { | |
724 | |
725 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | |
726 (noteOffs.begin()->frame, m_sourceSampleRate); | |
727 | |
728 offEv.data.note.note = noteOffs.begin()->pitch; | |
729 | |
730 #ifdef DEBUG_AUDIO_GENERATOR | |
731 std::cerr << "mixModel [note]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; | |
732 #endif | |
733 | |
734 plugin->sendEvent(eventTime, &offEv); | |
735 noteOffs.erase(noteOffs.begin()); | |
736 } | |
737 | |
738 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | |
739 (pliFrame, m_sourceSampleRate); | |
740 | |
741 if (nm->getScaleUnits() == "Hz") { | |
742 onEv.data.note.note = Pitch::getPitchForFrequency(pli->value); | |
743 } else { | |
744 onEv.data.note.note = lrintf(pli->value); | |
745 } | |
746 | |
747 if (pli->level > 0.f && pli->level <= 1.f) { | |
748 onEv.data.note.velocity = lrintf(pli->level * 127); | |
749 } else { | |
750 onEv.data.note.velocity = 100; | |
751 } | |
752 | |
753 plugin->sendEvent(eventTime, &onEv); | |
754 | |
755 #ifdef DEBUG_AUDIO_GENERATOR | |
756 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; | |
757 #endif | |
758 | |
759 size_t duration = pli->duration; | 697 size_t duration = pli->duration; |
760 if (duration == 0 || duration == 1) { | 698 if (duration == 0 || duration == 1) { |
761 duration = m_sourceSampleRate / 20; | 699 duration = m_sourceSampleRate / 20; |
762 } | 700 } |
763 NoteOff noff; | 701 |
764 noff.pitch = onEv.data.note.note; | 702 int pitch = lrintf(pli->value); |
765 noff.frame = pliFrame + duration; | 703 |
766 noteOffs.insert(noff); | 704 int velocity = 100; |
767 | 705 if (pli->level > 0.f && pli->level <= 1.f) { |
768 #ifdef DEBUG_AUDIO_GENERATOR | 706 velocity = lrintf(pli->level * 127); |
769 std::cout << "mixModel [note]: recording note off at " << noff.frame << std::endl; | 707 } |
770 #endif | 708 |
771 } | 709 NoteData note(pli->frame, |
772 | 710 duration, |
773 while (noteOffs.begin() != noteOffs.end() && | 711 pitch, |
774 noteOffs.begin()->frame <= | 712 velocity); |
775 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { | 713 |
776 | 714 if (nm->getScaleUnits() == "Hz") { |
777 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime | 715 note.frequency = pli->value; |
778 (noteOffs.begin()->frame, m_sourceSampleRate); | 716 note.isMidiPitchQuantized = false; |
779 | 717 } |
780 offEv.data.note.note = noteOffs.begin()->pitch; | 718 |
781 | 719 notes.push_back(note); |
782 #ifdef DEBUG_AUDIO_GENERATOR | 720 } |
783 std::cerr << "mixModel [note]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl; | 721 |
784 #endif | 722 return notes; |
785 | 723 } |
786 plugin->sendEvent(eventTime, &offEv); | 724 |
787 noteOffs.erase(noteOffs.begin()); | 725 return notes; |
788 } | 726 } |
789 | 727 |
790 plugin->run(blockTime); | |
791 float **outs = plugin->getAudioOutputBuffers(); | |
792 | |
793 for (size_t c = 0; c < m_targetChannelCount; ++c) { | |
794 #ifdef DEBUG_AUDIO_GENERATOR | |
795 std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; | |
796 #endif | |
797 | |
798 size_t sourceChannel = (c % plugin->getAudioOutputCount()); | |
799 | |
800 float channelGain = gain; | |
801 if (pan != 0.0) { | |
802 if (c == 0) { | |
803 if (pan > 0.0) channelGain *= 1.0 - pan; | |
804 } else { | |
805 if (pan < 0.0) channelGain *= pan + 1.0; | |
806 } | |
807 } | |
808 | |
809 for (size_t j = 0; j < m_pluginBlockSize; ++j) { | |
810 buffer[c][i * m_pluginBlockSize + j] += | |
811 channelGain * outs[sourceChannel][j]; | |
812 } | |
813 } | |
814 } | |
815 | |
816 return got; | |
817 } | |
818 |