Mercurial > hg > svapp
comparison audio/AudioCallbackPlaySource.cpp @ 746:771ec060c1d2
Merge from branch audio-source-refactor. Pull out auditioning effect wrapper and time stretch wrapper from play source; corresponding changes to plugin memory management etc.
| author | Chris Cannam |
|---|---|
| date | Fri, 03 Apr 2020 12:14:05 +0100 |
| parents | 75390b1ebd2c 1c6c10cc5f73 |
| children | e7c77c366360 |
comparison
equal
deleted
inserted
replaced
| 743:7b1d30af4b38 | 746:771ec060c1d2 |
|---|---|
| 14 */ | 14 */ |
| 15 | 15 |
| 16 #include "AudioCallbackPlaySource.h" | 16 #include "AudioCallbackPlaySource.h" |
| 17 | 17 |
| 18 #include "AudioGenerator.h" | 18 #include "AudioGenerator.h" |
| 19 #include "TimeStretchWrapper.h" | |
| 20 #include "EffectWrapper.h" | |
| 19 | 21 |
| 20 #include "data/model/Model.h" | 22 #include "data/model/Model.h" |
| 21 #include "base/ViewManagerBase.h" | 23 #include "base/ViewManagerBase.h" |
| 22 #include "base/PlayParameterRepository.h" | 24 #include "base/PlayParameterRepository.h" |
| 23 #include "base/Preferences.h" | 25 #include "base/Preferences.h" |
| 29 | 31 |
| 30 #include "bqaudioio/SystemPlaybackTarget.h" | 32 #include "bqaudioio/SystemPlaybackTarget.h" |
| 31 #include "bqaudioio/ResamplerWrapper.h" | 33 #include "bqaudioio/ResamplerWrapper.h" |
| 32 | 34 |
| 33 #include "bqvec/VectorOps.h" | 35 #include "bqvec/VectorOps.h" |
| 34 | |
| 35 #include <rubberband/RubberBandStretcher.h> | |
| 36 using namespace RubberBand; | |
| 37 | 36 |
| 38 using breakfastquay::v_zero_channels; | 37 using breakfastquay::v_zero_channels; |
| 39 | 38 |
| 40 #include <iostream> | 39 #include <iostream> |
| 41 #include <cassert> | 40 #include <cassert> |
| 71 m_lastModelEndFrame(0), | 70 m_lastModelEndFrame(0), |
| 72 m_ringBufferSize(DEFAULT_RING_BUFFER_SIZE), | 71 m_ringBufferSize(DEFAULT_RING_BUFFER_SIZE), |
| 73 m_outputLeft(0.0), | 72 m_outputLeft(0.0), |
| 74 m_outputRight(0.0), | 73 m_outputRight(0.0), |
| 75 m_levelsSet(false), | 74 m_levelsSet(false), |
| 76 m_auditioningPlugin(nullptr), | |
| 77 m_auditioningPluginBypassed(false), | |
| 78 m_auditioningPluginFailed(false), | |
| 79 m_playStartFrame(0), | 75 m_playStartFrame(0), |
| 80 m_playStartFramePassed(false), | 76 m_playStartFramePassed(false), |
| 81 m_timeStretcher(nullptr), | |
| 82 m_monoStretcher(nullptr), | |
| 83 m_stretchRatio(1.0), | |
| 84 m_stretchMono(false), | |
| 85 m_stretcherInputCount(0), | |
| 86 m_stretcherInputs(nullptr), | |
| 87 m_stretcherInputSizes(nullptr), | |
| 88 m_fillThread(nullptr), | 77 m_fillThread(nullptr), |
| 89 m_resamplerWrapper(nullptr) | 78 m_resamplerWrapper(nullptr), |
| 79 m_timeStretchWrapper(nullptr), | |
| 80 m_auditioningEffectWrapper(nullptr) | |
| 90 { | 81 { |
| 91 m_viewManager->setAudioPlaySource(this); | 82 m_viewManager->setAudioPlaySource(this); |
| 92 | 83 |
| 93 connect(m_viewManager, SIGNAL(selectionChanged()), | 84 connect(m_viewManager, SIGNAL(selectionChanged()), |
| 94 this, SLOT(selectionChanged())); | 85 this, SLOT(selectionChanged())); |
| 133 | 124 |
| 134 delete m_writeBuffers; | 125 delete m_writeBuffers; |
| 135 | 126 |
| 136 delete m_audioGenerator; | 127 delete m_audioGenerator; |
| 137 | 128 |
| 138 for (int i = 0; i < m_stretcherInputCount; ++i) { | 129 delete m_timeStretchWrapper; |
| 139 delete[] m_stretcherInputs[i]; | 130 delete m_auditioningEffectWrapper; |
| 140 } | 131 delete m_resamplerWrapper; |
| 141 delete[] m_stretcherInputSizes; | |
| 142 delete[] m_stretcherInputs; | |
| 143 | |
| 144 delete m_timeStretcher; | |
| 145 delete m_monoStretcher; | |
| 146 | 132 |
| 147 m_bufferScavenger.scavenge(true); | 133 m_bufferScavenger.scavenge(true); |
| 148 m_pluginScavenger.scavenge(true); | 134 m_pluginScavenger.scavenge(true); |
| 149 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 135 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
| 150 SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl; | 136 SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl; |
| 151 #endif | 137 #endif |
| 138 } | |
| 139 | |
| 140 breakfastquay::ApplicationPlaybackSource * | |
| 141 AudioCallbackPlaySource::getApplicationPlaybackSource() | |
| 142 { | |
| 143 QMutexLocker locker(&m_mutex); | |
| 144 | |
| 145 if (m_timeStretchWrapper) { | |
| 146 return m_timeStretchWrapper; | |
| 147 } | |
| 148 | |
| 149 checkWrappers(); | |
| 150 return m_timeStretchWrapper; | |
| 151 } | |
| 152 | |
| 153 void | |
| 154 AudioCallbackPlaySource::checkWrappers() | |
| 155 { | |
| 156 // to be called only with m_mutex held | |
| 157 | |
| 158 if (!m_resamplerWrapper) { | |
| 159 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(this); | |
| 160 } | |
| 161 if (!m_auditioningEffectWrapper) { | |
| 162 m_auditioningEffectWrapper = new EffectWrapper(m_resamplerWrapper); | |
| 163 } | |
| 164 if (!m_timeStretchWrapper) { | |
| 165 m_timeStretchWrapper = new TimeStretchWrapper(m_auditioningEffectWrapper); | |
| 166 } | |
| 152 } | 167 } |
| 153 | 168 |
| 154 void | 169 void |
| 155 AudioCallbackPlaySource::addModel(ModelId modelId) | 170 AudioCallbackPlaySource::addModel(ModelId modelId) |
| 156 { | 171 { |
| 248 if (willPlay) clearRingBuffers(true); | 263 if (willPlay) clearRingBuffers(true); |
| 249 } | 264 } |
| 250 | 265 |
| 251 if (srChanged) { | 266 if (srChanged) { |
| 252 | 267 |
| 253 SVCERR << "AudioCallbackPlaySource: Source rate changed" << endl; | 268 checkWrappers(); |
| 254 | 269 |
| 255 if (m_resamplerWrapper) { | 270 SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " |
| 256 SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " | 271 << m_sourceSampleRate << ", updating resampler wrapper" |
| 257 << m_sourceSampleRate << ", updating resampler wrapper" << endl; | 272 << endl; |
| 258 m_resamplerWrapper->changeApplicationSampleRate | 273 m_resamplerWrapper->changeApplicationSampleRate |
| 259 (int(round(m_sourceSampleRate))); | 274 (int(round(m_sourceSampleRate))); |
| 260 m_resamplerWrapper->reset(); | 275 m_resamplerWrapper->reset(); |
| 261 } | |
| 262 | |
| 263 delete m_timeStretcher; | |
| 264 delete m_monoStretcher; | |
| 265 m_timeStretcher = nullptr; | |
| 266 m_monoStretcher = nullptr; | |
| 267 | |
| 268 if (m_stretchRatio != 1.f) { | |
| 269 setTimeStretch(m_stretchRatio); | |
| 270 } | |
| 271 } | 276 } |
| 272 | 277 |
| 273 rebuildRangeLists(); | 278 rebuildRangeLists(); |
| 274 | 279 |
| 275 m_mutex.unlock(); | 280 m_mutex.unlock(); |
| 481 // we're just re-seeking. | 486 // we're just re-seeking. |
| 482 // NO -- we can end up playing some first -- always reset here | 487 // NO -- we can end up playing some first -- always reset here |
| 483 | 488 |
| 484 m_mutex.lock(); | 489 m_mutex.lock(); |
| 485 | 490 |
| 486 if (m_timeStretcher) { | 491 if (m_timeStretchWrapper) { |
| 487 m_timeStretcher->reset(); | 492 m_timeStretchWrapper->reset(); |
| 488 } | |
| 489 if (m_monoStretcher) { | |
| 490 m_monoStretcher->reset(); | |
| 491 } | 493 } |
| 492 | 494 |
| 493 m_readBufferFill = m_writeBufferFill = startFrame; | 495 m_readBufferFill = m_writeBufferFill = startFrame; |
| 494 if (m_readBuffers) { | 496 if (m_readBuffers) { |
| 495 for (int c = 0; c < getTargetChannelCount(); ++c) { | 497 for (int c = 0; c < getTargetChannelCount(); ++c) { |
| 597 { | 599 { |
| 598 SVCERR << "Audio processing overload!" << endl; | 600 SVCERR << "Audio processing overload!" << endl; |
| 599 | 601 |
| 600 if (!m_playing) return; | 602 if (!m_playing) return; |
| 601 | 603 |
| 602 RealTimePluginInstance *ap = m_auditioningPlugin; | 604 if (m_auditioningEffectWrapper && |
| 603 if (ap && !m_auditioningPluginBypassed) { | 605 !m_auditioningEffectWrapper->isBypassed()) { |
| 604 m_auditioningPluginBypassed = true; | 606 m_auditioningEffectWrapper->setBypassed(true); |
| 605 emit audioOverloadPluginDisabled(); | 607 emit audioOverloadPluginDisabled(); |
| 606 return; | |
| 607 } | |
| 608 | |
| 609 if (m_timeStretcher && | |
| 610 m_timeStretcher->getTimeRatio() < 1.0 && | |
| 611 m_stretcherInputCount > 1 && | |
| 612 m_monoStretcher && !m_stretchMono) { | |
| 613 m_stretchMono = true; | |
| 614 emit audioTimeStretchMultiChannelDisabled(); | |
| 615 return; | 608 return; |
| 616 } | 609 } |
| 617 } | 610 } |
| 618 | 611 |
| 619 void | 612 void |
| 626 } | 619 } |
| 627 m_target = target; | 620 m_target = target; |
| 628 } | 621 } |
| 629 | 622 |
| 630 void | 623 void |
| 631 AudioCallbackPlaySource::setResamplerWrapper(breakfastquay::ResamplerWrapper *w) | |
| 632 { | |
| 633 m_resamplerWrapper = w; | |
| 634 if (m_resamplerWrapper && m_sourceSampleRate != 0) { | |
| 635 m_resamplerWrapper->changeApplicationSampleRate | |
| 636 (int(round(m_sourceSampleRate))); | |
| 637 } | |
| 638 } | |
| 639 | |
| 640 void | |
| 641 AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) | 624 AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) |
| 642 { | 625 { |
| 643 cout << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl; | 626 SVDEBUG << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl; |
| 644 if (size != 0) { | 627 if (size != 0) { |
| 645 m_blockSize = size; | 628 m_blockSize = size; |
| 646 } | 629 } |
| 647 if (size * 4 > m_ringBufferSize) { | 630 if (size * 4 > m_ringBufferSize) { |
| 648 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 631 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
| 649 cout << "AudioCallbackPlaySource::setTarget: Buffer size " | 632 SVCERR << "AudioCallbackPlaySource::setTarget: Buffer size " |
| 650 << size << " > a quarter of ring buffer size " | 633 << size << " > a quarter of ring buffer size " |
| 651 << m_ringBufferSize << ", calling for more ring buffer" | 634 << m_ringBufferSize << ", calling for more ring buffer" |
| 652 << endl; | 635 << endl; |
| 653 #endif | 636 #endif |
| 654 m_ringBufferSize = size * 4; | 637 m_ringBufferSize = size * 4; |
| 655 if (m_writeBuffers && !m_writeBuffers->empty()) { | 638 if (m_writeBuffers && !m_writeBuffers->empty()) { |
| 656 clearRingBuffers(); | 639 clearRingBuffers(); |
| 657 } | 640 } |
| 702 | 685 |
| 703 sv_frame_t | 686 sv_frame_t |
| 704 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) | 687 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) |
| 705 { | 688 { |
| 706 // The ring buffers contain data at the source sample rate and all | 689 // The ring buffers contain data at the source sample rate and all |
| 707 // processing (including time stretching) happens at this | 690 // processing here happens at this rate. Resampling only happens |
| 708 // rate. Resampling only happens after the audio data leaves this | 691 // after the audio data leaves this class. |
| 709 // class. | |
| 710 | 692 |
| 711 // (But because historically more than one sample rate could have | 693 // (But because historically more than one sample rate could have |
| 712 // been involved here, we do latency calculations using RealTime | 694 // been involved here, we do latency calculations using RealTime |
| 713 // values instead of samples.) | 695 // values instead of samples.) |
| 714 | 696 |
| 733 if (m_target) currentTime = m_target->getCurrentTime(); | 715 if (m_target) currentTime = m_target->getCurrentTime(); |
| 734 | 716 |
| 735 bool looping = m_viewManager->getPlayLoopMode(); | 717 bool looping = m_viewManager->getPlayLoopMode(); |
| 736 | 718 |
| 737 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); | 719 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); |
| 738 | |
| 739 sv_frame_t stretchlat = 0; | |
| 740 double timeRatio = 1.0; | |
| 741 | |
| 742 if (m_timeStretcher) { | |
| 743 stretchlat = m_timeStretcher->getLatency(); | |
| 744 timeRatio = m_timeStretcher->getTimeRatio(); | |
| 745 } | |
| 746 | |
| 747 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate); | |
| 748 | 720 |
| 749 // When the target has just requested a block from us, the last | 721 // When the target has just requested a block from us, the last |
| 750 // sample it obtained was our buffer fill frame count minus the | 722 // sample it obtained was our buffer fill frame count minus the |
| 751 // amount of read space (converted back to source sample rate) | 723 // amount of read space (converted back to source sample rate) |
| 752 // remaining now. That sample is not expected to be played until | 724 // remaining now. That sample is not expected to be played until |
| 782 lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate); | 754 lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate); |
| 783 } | 755 } |
| 784 | 756 |
| 785 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); | 757 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); |
| 786 | 758 |
| 787 if (timeRatio != 1.0) { | |
| 788 lastretrieved_t = lastretrieved_t / timeRatio; | |
| 789 sincerequest_t = sincerequest_t / timeRatio; | |
| 790 latency_t = latency_t / timeRatio; | |
| 791 } | |
| 792 | |
| 793 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 759 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
| 794 cout << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl; | 760 cout << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl; |
| 795 #endif | 761 #endif |
| 796 | 762 |
| 797 // Normally the range lists should contain at least one item each | 763 // Normally the range lists should contain at least one item each |
| 803 } | 769 } |
| 804 | 770 |
| 805 if (m_rangeStarts.empty()) { | 771 if (m_rangeStarts.empty()) { |
| 806 // this code is only used in case of error in rebuildRangeLists | 772 // this code is only used in case of error in rebuildRangeLists |
| 807 RealTime playing_t = bufferedto_t | 773 RealTime playing_t = bufferedto_t |
| 808 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 774 - latency_t - lastretrieved_t - inbuffer_t |
| 809 + sincerequest_t; | 775 + sincerequest_t; |
| 810 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | 776 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; |
| 811 sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); | 777 sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); |
| 812 return m_viewManager->alignPlaybackFrameToReference(frame); | 778 return m_viewManager->alignPlaybackFrameToReference(frame); |
| 813 } | 779 } |
| 829 } | 795 } |
| 830 | 796 |
| 831 RealTime playing_t = bufferedto_t; | 797 RealTime playing_t = bufferedto_t; |
| 832 | 798 |
| 833 playing_t = playing_t | 799 playing_t = playing_t |
| 834 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 800 - latency_t - lastretrieved_t - inbuffer_t |
| 835 + sincerequest_t; | 801 + sincerequest_t; |
| 836 | 802 |
| 837 // This rather gross little hack is used to ensure that latency | 803 // This rather gross little hack is used to ensure that latency |
| 838 // compensation doesn't result in the playback pointer appearing | 804 // compensation doesn't result in the playback pointer appearing |
| 839 // to start earlier than the actual playback does. It doesn't | 805 // to start earlier than the actual playback does. It doesn't |
| 847 if (!m_playStartFramePassed) { | 813 if (!m_playStartFramePassed) { |
| 848 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate); | 814 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate); |
| 849 if (playing_t < playstart_t) { | 815 if (playing_t < playstart_t) { |
| 850 // cout << "playing_t " << playing_t << " < playstart_t " | 816 // cout << "playing_t " << playing_t << " < playstart_t " |
| 851 // << playstart_t << endl; | 817 // << playstart_t << endl; |
| 852 if (/*!!! sincerequest_t > RealTime::zeroTime && */ | 818 if (m_playStartedAt + latency_t < |
| 853 m_playStartedAt + latency_t + stretchlat_t < | |
| 854 RealTime::fromSeconds(currentTime)) { | 819 RealTime::fromSeconds(currentTime)) { |
| 855 // cout << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << endl; | 820 // cout << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << endl; |
| 856 m_playStartFramePassed = true; | 821 m_playStartFramePassed = true; |
| 857 } else { | 822 } else { |
| 858 playing_t = playstart_t; | 823 playing_t = playstart_t; |
| 1001 { | 966 { |
| 1002 m_deviceChannelCount = count; | 967 m_deviceChannelCount = count; |
| 1003 } | 968 } |
| 1004 | 969 |
| 1005 void | 970 void |
| 1006 AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a) | 971 AudioCallbackPlaySource::setAuditioningEffect(std::shared_ptr<Auditionable> a) |
| 1007 { | 972 { |
| 1008 RealTimePluginInstance *plugin = dynamic_cast<RealTimePluginInstance *>(a); | 973 SVDEBUG << "AudioCallbackPlaySource::setAuditioningEffect(" << a << ")" |
| 974 << endl; | |
| 975 | |
| 976 auto plugin = std::dynamic_pointer_cast<RealTimePluginInstance>(a); | |
| 1009 if (a && !plugin) { | 977 if (a && !plugin) { |
| 1010 SVCERR << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << endl; | 978 SVCERR << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << endl; |
| 1011 } | 979 } |
| 1012 | 980 |
| 1013 m_mutex.lock(); | 981 m_mutex.lock(); |
| 1014 m_auditioningPlugin = plugin; | 982 m_auditioningEffectWrapper->setEffect(plugin); |
| 1015 m_auditioningPluginBypassed = false; | 983 m_auditioningEffectWrapper->setBypassed(false); |
| 1016 m_auditioningPluginFailed = false; | |
| 1017 m_mutex.unlock(); | 984 m_mutex.unlock(); |
| 1018 | 985 |
| 1019 SVDEBUG << "AudioCallbackPlaySource::setAuditioningEffect: set plugin to " | 986 SVDEBUG << "AudioCallbackPlaySource::setAuditioningEffect: set plugin to " |
| 1020 << plugin << " and bypassed to " << m_auditioningPluginBypassed | 987 << plugin << endl; |
| 1021 << endl; | |
| 1022 } | 988 } |
| 1023 | 989 |
| 1024 void | 990 void |
| 1025 AudioCallbackPlaySource::setSoloModelSet(std::set<ModelId> s) | 991 AudioCallbackPlaySource::setSoloModelSet(std::set<ModelId> s) |
| 1026 { | 992 { |
| 1067 } | 1033 } |
| 1068 | 1034 |
| 1069 void | 1035 void |
| 1070 AudioCallbackPlaySource::setTimeStretch(double factor) | 1036 AudioCallbackPlaySource::setTimeStretch(double factor) |
| 1071 { | 1037 { |
| 1072 m_stretchRatio = factor; | 1038 checkWrappers(); |
| 1073 | 1039 |
| 1074 int rate = int(getSourceSampleRate()); | 1040 m_timeStretchWrapper->setTimeStretchRatio(factor); |
| 1075 if (!rate) return; // have to make our stretcher later | 1041 |
| 1076 | |
| 1077 if (m_timeStretcher || (factor == 1.0)) { | |
| 1078 // stretch ratio will be set in next process call if appropriate | |
| 1079 } else { | |
| 1080 m_stretcherInputCount = getTargetChannelCount(); | |
| 1081 RubberBandStretcher *stretcher = new RubberBandStretcher | |
| 1082 (rate, | |
| 1083 m_stretcherInputCount, | |
| 1084 RubberBandStretcher::OptionProcessRealTime, | |
| 1085 factor); | |
| 1086 RubberBandStretcher *monoStretcher = new RubberBandStretcher | |
| 1087 (rate, | |
| 1088 1, | |
| 1089 RubberBandStretcher::OptionProcessRealTime, | |
| 1090 factor); | |
| 1091 m_stretcherInputs = new float *[m_stretcherInputCount]; | |
| 1092 m_stretcherInputSizes = new sv_frame_t[m_stretcherInputCount]; | |
| 1093 for (int c = 0; c < m_stretcherInputCount; ++c) { | |
| 1094 m_stretcherInputSizes[c] = 16384; | |
| 1095 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
| 1096 } | |
| 1097 m_monoStretcher = monoStretcher; | |
| 1098 m_timeStretcher = stretcher; | |
| 1099 } | |
| 1100 | |
| 1101 emit activity(tr("Change time-stretch factor to %1").arg(factor)); | 1042 emit activity(tr("Change time-stretch factor to %1").arg(factor)); |
| 1102 } | 1043 } |
| 1103 | 1044 |
| 1104 int | 1045 int |
| 1105 AudioCallbackPlaySource::getSourceSamples(float *const *buffer, | 1046 AudioCallbackPlaySource::getSourceSamples(float *const *buffer, |
| 1200 } | 1141 } |
| 1201 } | 1142 } |
| 1202 | 1143 |
| 1203 if (count == 0) return 0; | 1144 if (count == 0) return 0; |
| 1204 | 1145 |
| 1205 RubberBandStretcher *ts = m_timeStretcher; | |
| 1206 RubberBandStretcher *ms = m_monoStretcher; | |
| 1207 | |
| 1208 double ratio = ts ? ts->getTimeRatio() : 1.0; | |
| 1209 | |
| 1210 if (ratio != m_stretchRatio) { | |
| 1211 if (!ts) { | |
| 1212 SVCERR << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << endl; | |
| 1213 m_stretchRatio = 1.0; | |
| 1214 } else { | |
| 1215 ts->setTimeRatio(m_stretchRatio); | |
| 1216 if (ms) ms->setTimeRatio(m_stretchRatio); | |
| 1217 if (m_stretchRatio >= 1.0) m_stretchMono = false; | |
| 1218 } | |
| 1219 } | |
| 1220 | |
| 1221 int stretchChannels = m_stretcherInputCount; | |
| 1222 if (m_stretchMono) { | |
| 1223 if (ms) { | |
| 1224 ts = ms; | |
| 1225 stretchChannels = 1; | |
| 1226 } else { | |
| 1227 m_stretchMono = false; | |
| 1228 } | |
| 1229 } | |
| 1230 | |
| 1231 if (m_target) { | 1146 if (m_target) { |
| 1232 m_lastRetrievedBlockSize = count; | 1147 m_lastRetrievedBlockSize = count; |
| 1233 m_lastRetrievalTimestamp = m_target->getCurrentTime(); | 1148 m_lastRetrievalTimestamp = m_target->getCurrentTime(); |
| 1234 } | 1149 } |
| 1235 | 1150 |
| 1236 if (!ts || ratio == 1.f) { | 1151 int got = 0; |
| 1237 | |
| 1238 int got = 0; | |
| 1239 | 1152 |
| 1240 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1153 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
| 1241 cout << "channels == " << channels << endl; | 1154 cout << "channels == " << channels << endl; |
| 1242 #endif | 1155 #endif |
| 1243 | 1156 |
| 1244 for (int ch = 0; ch < channels; ++ch) { | 1157 for (int ch = 0; ch < channels; ++ch) { |
| 1245 | 1158 |
| 1246 RingBuffer<float> *rb = getReadRingBuffer(ch); | 1159 RingBuffer<float> *rb = getReadRingBuffer(ch); |
| 1247 | 1160 |
| 1248 if (rb) { | 1161 if (rb) { |
| 1249 | 1162 |
| 1250 // this is marginally more likely to leave our channels in | 1163 // this is marginally more likely to leave our channels in |
| 1251 // sync after a processing failure than just passing "count": | 1164 // sync after a processing failure than just passing "count": |
| 1252 sv_frame_t request = count; | 1165 sv_frame_t request = count; |
| 1253 if (ch > 0) request = got; | 1166 if (ch > 0) request = got; |
| 1254 | 1167 |
| 1255 got = rb->read(buffer[ch], int(request)); | 1168 got = rb->read(buffer[ch], int(request)); |
| 1256 | 1169 |
| 1257 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1170 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
| 1258 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; | 1171 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; |
| 1259 #endif | 1172 #endif |
| 1260 } | 1173 } |
| 1261 | 1174 |
| 1262 for (int ch = 0; ch < channels; ++ch) { | 1175 for (int ch = 0; ch < channels; ++ch) { |
| 1263 for (int i = got; i < count; ++i) { | 1176 for (int i = got; i < count; ++i) { |
| 1264 buffer[ch][i] = 0.0; | 1177 buffer[ch][i] = 0.0; |
| 1265 } | 1178 } |
| 1266 } | 1179 } |
| 1267 } | 1180 } |
| 1268 | |
| 1269 applyAuditioningEffect(count, buffer); | |
| 1270 | 1181 |
| 1271 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1182 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
| 1272 cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl; | 1183 cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl; |
| 1273 #endif | 1184 #endif |
| 1274 | 1185 |
| 1275 m_condition.wakeAll(); | |
| 1276 | |
| 1277 return got; | |
| 1278 } | |
| 1279 | |
| 1280 sv_frame_t available; | |
| 1281 sv_frame_t fedToStretcher = 0; | |
| 1282 int warned = 0; | |
| 1283 | |
| 1284 // The input block for a given output is approx output / ratio, | |
| 1285 // but we can't predict it exactly, for an adaptive timestretcher. | |
| 1286 | |
| 1287 while ((available = ts->available()) < count) { | |
| 1288 | |
| 1289 sv_frame_t reqd = lrint(double(count - available) / ratio); | |
| 1290 reqd = std::max(reqd, sv_frame_t(ts->getSamplesRequired())); | |
| 1291 if (reqd == 0) reqd = 1; | |
| 1292 | |
| 1293 sv_frame_t got = reqd; | |
| 1294 | |
| 1295 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
| 1296 cout << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << endl; | |
| 1297 #endif | |
| 1298 | |
| 1299 for (int c = 0; c < channels; ++c) { | |
| 1300 if (c >= m_stretcherInputCount) continue; | |
| 1301 if (reqd > m_stretcherInputSizes[c]) { | |
| 1302 if (c == 0) { | |
| 1303 SVDEBUG << "NOTE: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << endl; | |
| 1304 } | |
| 1305 delete[] m_stretcherInputs[c]; | |
| 1306 m_stretcherInputSizes[c] = reqd * 2; | |
| 1307 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
| 1308 } | |
| 1309 } | |
| 1310 | |
| 1311 for (int c = 0; c < channels; ++c) { | |
| 1312 if (c >= m_stretcherInputCount) continue; | |
| 1313 RingBuffer<float> *rb = getReadRingBuffer(c); | |
| 1314 if (rb) { | |
| 1315 sv_frame_t gotHere; | |
| 1316 if (stretchChannels == 1 && c > 0) { | |
| 1317 gotHere = rb->readAdding(m_stretcherInputs[0], int(got)); | |
| 1318 } else { | |
| 1319 gotHere = rb->read(m_stretcherInputs[c], int(got)); | |
| 1320 } | |
| 1321 if (gotHere < got) got = gotHere; | |
| 1322 | |
| 1323 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
| 1324 if (c == 0) { | |
| 1325 cout << "feeding stretcher: got " << gotHere | |
| 1326 << ", " << rb->getReadSpace() << " remain" << endl; | |
| 1327 } | |
| 1328 #endif | |
| 1329 | |
| 1330 } else { | |
| 1331 SVCERR << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << endl; | |
| 1332 } | |
| 1333 } | |
| 1334 | |
| 1335 if (got < reqd) { | |
| 1336 SVCERR << "WARNING: Read underrun in playback (" | |
| 1337 << got << " < " << reqd << ")" << endl; | |
| 1338 } | |
| 1339 | |
| 1340 ts->process(m_stretcherInputs, size_t(got), false); | |
| 1341 | |
| 1342 fedToStretcher += got; | |
| 1343 | |
| 1344 if (got == 0) break; | |
| 1345 | |
| 1346 if (ts->available() == available) { | |
| 1347 SVCERR << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << endl; | |
| 1348 if (++warned == 5) break; | |
| 1349 } | |
| 1350 } | |
| 1351 | |
| 1352 ts->retrieve(buffer, size_t(count)); | |
| 1353 | |
| 1354 v_zero_channels(buffer + stretchChannels, channels - stretchChannels, count); | |
| 1355 | |
| 1356 applyAuditioningEffect(count, buffer); | |
| 1357 | |
| 1358 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
| 1359 cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl; | |
| 1360 #endif | |
| 1361 | |
| 1362 m_condition.wakeAll(); | 1186 m_condition.wakeAll(); |
| 1363 | 1187 |
| 1364 return count; | 1188 return got; |
| 1365 } | 1189 } |
| 1366 | |
| 1367 void | |
| 1368 AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float *const *buffers) | |
| 1369 { | |
| 1370 if (m_auditioningPluginBypassed) return; | |
| 1371 RealTimePluginInstance *plugin = m_auditioningPlugin; | |
| 1372 if (!plugin) return; | |
| 1373 | |
| 1374 if ((int)plugin->getAudioInputCount() != getTargetChannelCount()) { | |
| 1375 if (!m_auditioningPluginFailed) { | |
| 1376 SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " | |
| 1377 << "Can't run plugin: plugin input count " | |
| 1378 << plugin->getAudioInputCount() | |
| 1379 << " != our channel count " << getTargetChannelCount() | |
| 1380 << " (future errors for this plugin will be suppressed)" | |
| 1381 << endl; | |
| 1382 m_auditioningPluginFailed = true; | |
| 1383 } | |
| 1384 return; | |
| 1385 } | |
| 1386 if ((int)plugin->getAudioOutputCount() != getTargetChannelCount()) { | |
| 1387 if (!m_auditioningPluginFailed) { | |
| 1388 SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " | |
| 1389 << "Can't run plugin: plugin output count " | |
| 1390 << plugin->getAudioOutputCount() | |
| 1391 << " != our channel count " << getTargetChannelCount() | |
| 1392 << " (future errors for this plugin will be suppressed)" | |
| 1393 << endl; | |
| 1394 m_auditioningPluginFailed = true; | |
| 1395 } | |
| 1396 return; | |
| 1397 } | |
| 1398 if ((int)plugin->getBufferSize() < count) { | |
| 1399 if (!m_auditioningPluginFailed) { | |
| 1400 SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " | |
| 1401 << "Can't run plugin: plugin buffer size " | |
| 1402 << plugin->getBufferSize() | |
| 1403 << " < our block size " << count | |
| 1404 << " (future errors for this plugin will be suppressed)" | |
| 1405 << endl; | |
| 1406 m_auditioningPluginFailed = true; | |
| 1407 } | |
| 1408 return; | |
| 1409 } | |
| 1410 | |
| 1411 float **ib = plugin->getAudioInputBuffers(); | |
| 1412 float **ob = plugin->getAudioOutputBuffers(); | |
| 1413 | |
| 1414 for (int c = 0; c < getTargetChannelCount(); ++c) { | |
| 1415 for (int i = 0; i < count; ++i) { | |
| 1416 ib[c][i] = buffers[c][i]; | |
| 1417 } | |
| 1418 } | |
| 1419 | |
| 1420 plugin->run(Vamp::RealTime::zeroTime, int(count)); | |
| 1421 | |
| 1422 for (int c = 0; c < getTargetChannelCount(); ++c) { | |
| 1423 for (int i = 0; i < count; ++i) { | |
| 1424 buffers[c][i] = ob[c][i]; | |
| 1425 } | |
| 1426 } | |
| 1427 } | |
| 1428 | 1190 |
| 1429 // Called from fill thread, m_playing true, mutex held | 1191 // Called from fill thread, m_playing true, mutex held |
| 1430 bool | 1192 bool |
| 1431 AudioCallbackPlaySource::fillBuffers() | 1193 AudioCallbackPlaySource::fillBuffers() |
| 1432 { | 1194 { |
