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 &noteOffs = m_noteOffs[sodm]; 550 NoteOffSet &noteOffs = 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 &noteOffs = 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