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