Mercurial > hg > svapp
comparison audio/AudioCallbackPlaySource.cpp @ 738:48001ed9143b audio-source-refactor
Introduce TimeStretchWrapper; some work towards making the AudioCallbackPlaySource not actually try to be an ApplicationPlaybackSource itself but only return one that is constructed from wrappers that it controls the lifespan of
author | Chris Cannam |
---|---|
date | Wed, 18 Mar 2020 12:51:41 +0000 |
parents | 497d80d3b9c4 |
children | ddfac001b543 |
comparison
equal
deleted
inserted
replaced
737:497d80d3b9c4 | 738:48001ed9143b |
---|---|
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" | |
19 | 20 |
20 #include "data/model/Model.h" | 21 #include "data/model/Model.h" |
21 #include "base/ViewManagerBase.h" | 22 #include "base/ViewManagerBase.h" |
22 #include "base/PlayParameterRepository.h" | 23 #include "base/PlayParameterRepository.h" |
23 #include "base/Preferences.h" | 24 #include "base/Preferences.h" |
29 | 30 |
30 #include "bqaudioio/SystemPlaybackTarget.h" | 31 #include "bqaudioio/SystemPlaybackTarget.h" |
31 #include "bqaudioio/ResamplerWrapper.h" | 32 #include "bqaudioio/ResamplerWrapper.h" |
32 | 33 |
33 #include "bqvec/VectorOps.h" | 34 #include "bqvec/VectorOps.h" |
34 | |
35 #include <rubberband/RubberBandStretcher.h> | |
36 using namespace RubberBand; | |
37 | 35 |
38 using breakfastquay::v_zero_channels; | 36 using breakfastquay::v_zero_channels; |
39 | 37 |
40 #include <iostream> | 38 #include <iostream> |
41 #include <cassert> | 39 #include <cassert> |
76 m_auditioningPlugin(nullptr), | 74 m_auditioningPlugin(nullptr), |
77 m_auditioningPluginBypassed(false), | 75 m_auditioningPluginBypassed(false), |
78 m_auditioningPluginFailed(false), | 76 m_auditioningPluginFailed(false), |
79 m_playStartFrame(0), | 77 m_playStartFrame(0), |
80 m_playStartFramePassed(false), | 78 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), | 79 m_fillThread(nullptr), |
89 m_resamplerWrapper(nullptr) | 80 m_resamplerWrapper(nullptr), |
81 m_timeStretchWrapper(nullptr) | |
90 { | 82 { |
91 m_viewManager->setAudioPlaySource(this); | 83 m_viewManager->setAudioPlaySource(this); |
92 | 84 |
93 connect(m_viewManager, SIGNAL(selectionChanged()), | 85 connect(m_viewManager, SIGNAL(selectionChanged()), |
94 this, SLOT(selectionChanged())); | 86 this, SLOT(selectionChanged())); |
133 | 125 |
134 delete m_writeBuffers; | 126 delete m_writeBuffers; |
135 | 127 |
136 delete m_audioGenerator; | 128 delete m_audioGenerator; |
137 | 129 |
138 for (int i = 0; i < m_stretcherInputCount; ++i) { | |
139 delete[] m_stretcherInputs[i]; | |
140 } | |
141 delete[] m_stretcherInputSizes; | |
142 delete[] m_stretcherInputs; | |
143 | |
144 delete m_timeStretcher; | |
145 delete m_monoStretcher; | |
146 | |
147 m_bufferScavenger.scavenge(true); | 130 m_bufferScavenger.scavenge(true); |
148 m_pluginScavenger.scavenge(true); | 131 m_pluginScavenger.scavenge(true); |
149 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 132 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
150 SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl; | 133 SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl; |
151 #endif | 134 #endif |
135 } | |
136 | |
137 breakfastquay::ApplicationPlaybackSource * | |
138 AudioCallbackPlaySource::getApplicationPlaybackSource() | |
139 { | |
140 QMutexLocker locker(&m_mutex); | |
141 | |
142 if (m_timeStretchWrapper) { | |
143 return m_timeStretchWrapper; | |
144 } | |
145 | |
146 checkWrappers(); | |
147 return m_timeStretchWrapper; | |
148 } | |
149 | |
150 void | |
151 AudioCallbackPlaySource::checkWrappers() | |
152 { | |
153 // to be called only with m_mutex held | |
154 | |
155 if (!m_resamplerWrapper) { | |
156 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(this); | |
157 } | |
158 if (!m_timeStretchWrapper) { | |
159 m_timeStretchWrapper = new TimeStretchWrapper(m_resamplerWrapper); | |
160 } | |
152 } | 161 } |
153 | 162 |
154 void | 163 void |
155 AudioCallbackPlaySource::addModel(ModelId modelId) | 164 AudioCallbackPlaySource::addModel(ModelId modelId) |
156 { | 165 { |
248 if (willPlay) clearRingBuffers(true); | 257 if (willPlay) clearRingBuffers(true); |
249 } | 258 } |
250 | 259 |
251 if (srChanged) { | 260 if (srChanged) { |
252 | 261 |
253 SVCERR << "AudioCallbackPlaySource: Source rate changed" << endl; | 262 checkWrappers(); |
254 | 263 |
255 if (m_resamplerWrapper) { | 264 SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " |
256 SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " | 265 << m_sourceSampleRate << ", updating resampler wrapper" |
257 << m_sourceSampleRate << ", updating resampler wrapper" << endl; | 266 << endl; |
258 m_resamplerWrapper->changeApplicationSampleRate | 267 m_resamplerWrapper->changeApplicationSampleRate |
259 (int(round(m_sourceSampleRate))); | 268 (int(round(m_sourceSampleRate))); |
260 m_resamplerWrapper->reset(); | 269 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 } | 270 } |
272 | 271 |
273 rebuildRangeLists(); | 272 rebuildRangeLists(); |
274 | 273 |
275 m_mutex.unlock(); | 274 m_mutex.unlock(); |
481 // we're just re-seeking. | 480 // we're just re-seeking. |
482 // NO -- we can end up playing some first -- always reset here | 481 // NO -- we can end up playing some first -- always reset here |
483 | 482 |
484 m_mutex.lock(); | 483 m_mutex.lock(); |
485 | 484 |
486 if (m_timeStretcher) { | 485 if (m_timeStretchWrapper) { |
487 m_timeStretcher->reset(); | 486 m_timeStretchWrapper->reset(); |
488 } | |
489 if (m_monoStretcher) { | |
490 m_monoStretcher->reset(); | |
491 } | 487 } |
492 | 488 |
493 m_readBufferFill = m_writeBufferFill = startFrame; | 489 m_readBufferFill = m_writeBufferFill = startFrame; |
494 if (m_readBuffers) { | 490 if (m_readBuffers) { |
495 for (int c = 0; c < getTargetChannelCount(); ++c) { | 491 for (int c = 0; c < getTargetChannelCount(); ++c) { |
603 if (ap && !m_auditioningPluginBypassed) { | 599 if (ap && !m_auditioningPluginBypassed) { |
604 m_auditioningPluginBypassed = true; | 600 m_auditioningPluginBypassed = true; |
605 emit audioOverloadPluginDisabled(); | 601 emit audioOverloadPluginDisabled(); |
606 return; | 602 return; |
607 } | 603 } |
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; | |
616 } | |
617 } | 604 } |
618 | 605 |
619 void | 606 void |
620 AudioCallbackPlaySource::setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *target) | 607 AudioCallbackPlaySource::setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *target) |
621 { | 608 { |
609 //!!! This should go, we should be using the ApplicationPlaybackSource callbacks | |
610 | |
622 if (target == nullptr) { | 611 if (target == nullptr) { |
623 // reset target-related facts and figures | 612 // reset target-related facts and figures |
624 m_deviceSampleRate = 0; | 613 m_deviceSampleRate = 0; |
625 m_deviceChannelCount = 0; | 614 m_deviceChannelCount = 0; |
626 } | 615 } |
627 m_target = target; | 616 m_target = target; |
628 } | |
629 | |
630 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 } | 617 } |
639 | 618 |
640 void | 619 void |
641 AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) | 620 AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) |
642 { | 621 { |
734 | 713 |
735 bool looping = m_viewManager->getPlayLoopMode(); | 714 bool looping = m_viewManager->getPlayLoopMode(); |
736 | 715 |
737 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); | 716 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); |
738 | 717 |
718 /*!!! | |
739 sv_frame_t stretchlat = 0; | 719 sv_frame_t stretchlat = 0; |
740 double timeRatio = 1.0; | 720 double timeRatio = 1.0; |
741 | 721 |
742 if (m_timeStretcher) { | 722 if (m_timeStretcher) { |
743 stretchlat = m_timeStretcher->getLatency(); | 723 stretchlat = m_timeStretcher->getLatency(); |
744 timeRatio = m_timeStretcher->getTimeRatio(); | 724 timeRatio = m_timeStretcher->getTimeRatio(); |
745 } | 725 } |
746 | 726 |
747 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate); | 727 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate); |
748 | 728 */ |
749 // When the target has just requested a block from us, the last | 729 // When the target has just requested a block from us, the last |
750 // sample it obtained was our buffer fill frame count minus the | 730 // sample it obtained was our buffer fill frame count minus the |
751 // amount of read space (converted back to source sample rate) | 731 // amount of read space (converted back to source sample rate) |
752 // remaining now. That sample is not expected to be played until | 732 // remaining now. That sample is not expected to be played until |
753 // the target's play latency has elapsed. By the time the | 733 // the target's play latency has elapsed. By the time the |
782 lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate); | 762 lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate); |
783 } | 763 } |
784 | 764 |
785 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); | 765 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); |
786 | 766 |
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 | 767 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
794 cout << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl; | 768 cout << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl; |
795 #endif | 769 #endif |
796 | 770 |
797 // Normally the range lists should contain at least one item each | 771 // Normally the range lists should contain at least one item each |
803 } | 777 } |
804 | 778 |
805 if (m_rangeStarts.empty()) { | 779 if (m_rangeStarts.empty()) { |
806 // this code is only used in case of error in rebuildRangeLists | 780 // this code is only used in case of error in rebuildRangeLists |
807 RealTime playing_t = bufferedto_t | 781 RealTime playing_t = bufferedto_t |
808 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 782 //!!! - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t |
783 - latency_t - lastretrieved_t - inbuffer_t | |
809 + sincerequest_t; | 784 + sincerequest_t; |
810 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | 785 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; |
811 sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); | 786 sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); |
812 return m_viewManager->alignPlaybackFrameToReference(frame); | 787 return m_viewManager->alignPlaybackFrameToReference(frame); |
813 } | 788 } |
829 } | 804 } |
830 | 805 |
831 RealTime playing_t = bufferedto_t; | 806 RealTime playing_t = bufferedto_t; |
832 | 807 |
833 playing_t = playing_t | 808 playing_t = playing_t |
834 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 809 //!!! - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t |
810 - latency_t - lastretrieved_t - inbuffer_t | |
835 + sincerequest_t; | 811 + sincerequest_t; |
836 | 812 |
837 // This rather gross little hack is used to ensure that latency | 813 // This rather gross little hack is used to ensure that latency |
838 // compensation doesn't result in the playback pointer appearing | 814 // compensation doesn't result in the playback pointer appearing |
839 // to start earlier than the actual playback does. It doesn't | 815 // to start earlier than the actual playback does. It doesn't |
848 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate); | 824 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate); |
849 if (playing_t < playstart_t) { | 825 if (playing_t < playstart_t) { |
850 // cout << "playing_t " << playing_t << " < playstart_t " | 826 // cout << "playing_t " << playing_t << " < playstart_t " |
851 // << playstart_t << endl; | 827 // << playstart_t << endl; |
852 if (/*!!! sincerequest_t > RealTime::zeroTime && */ | 828 if (/*!!! sincerequest_t > RealTime::zeroTime && */ |
853 m_playStartedAt + latency_t + stretchlat_t < | 829 //!!! m_playStartedAt + latency_t + stretchlat_t < |
830 m_playStartedAt + latency_t < | |
854 RealTime::fromSeconds(currentTime)) { | 831 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; | 832 // 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; | 833 m_playStartFramePassed = true; |
857 } else { | 834 } else { |
858 playing_t = playstart_t; | 835 playing_t = playstart_t; |
1067 } | 1044 } |
1068 | 1045 |
1069 void | 1046 void |
1070 AudioCallbackPlaySource::setTimeStretch(double factor) | 1047 AudioCallbackPlaySource::setTimeStretch(double factor) |
1071 { | 1048 { |
1072 m_stretchRatio = factor; | 1049 checkWrappers(); |
1073 | 1050 |
1074 int rate = int(getSourceSampleRate()); | 1051 m_timeStretchWrapper->setTimeStretchRatio(factor); |
1075 if (!rate) return; // have to make our stretcher later | 1052 |
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)); | 1053 emit activity(tr("Change time-stretch factor to %1").arg(factor)); |
1102 } | 1054 } |
1103 | 1055 |
1104 int | 1056 int |
1105 AudioCallbackPlaySource::getSourceSamples(float *const *buffer, | 1057 AudioCallbackPlaySource::getSourceSamples(float *const *buffer, |
1200 } | 1152 } |
1201 } | 1153 } |
1202 | 1154 |
1203 if (count == 0) return 0; | 1155 if (count == 0) return 0; |
1204 | 1156 |
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) { | 1157 if (m_target) { |
1232 m_lastRetrievedBlockSize = count; | 1158 m_lastRetrievedBlockSize = count; |
1233 m_lastRetrievalTimestamp = m_target->getCurrentTime(); | 1159 m_lastRetrievalTimestamp = m_target->getCurrentTime(); |
1234 } | 1160 } |
1235 | 1161 |
1236 if (!ts || ratio == 1.f) { | 1162 int got = 0; |
1237 | |
1238 int got = 0; | |
1239 | 1163 |
1240 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1164 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
1241 cout << "channels == " << channels << endl; | 1165 cout << "channels == " << channels << endl; |
1242 #endif | 1166 #endif |
1243 | 1167 |
1244 for (int ch = 0; ch < channels; ++ch) { | 1168 for (int ch = 0; ch < channels; ++ch) { |
1245 | 1169 |
1246 RingBuffer<float> *rb = getReadRingBuffer(ch); | 1170 RingBuffer<float> *rb = getReadRingBuffer(ch); |
1247 | 1171 |
1248 if (rb) { | 1172 if (rb) { |
1249 | 1173 |
1250 // this is marginally more likely to leave our channels in | 1174 // this is marginally more likely to leave our channels in |
1251 // sync after a processing failure than just passing "count": | 1175 // sync after a processing failure than just passing "count": |
1252 sv_frame_t request = count; | 1176 sv_frame_t request = count; |
1253 if (ch > 0) request = got; | 1177 if (ch > 0) request = got; |
1254 | 1178 |
1255 got = rb->read(buffer[ch], int(request)); | 1179 got = rb->read(buffer[ch], int(request)); |
1256 | 1180 |
1257 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1181 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
1258 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; | 1182 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; |
1259 #endif | 1183 #endif |
1260 } | 1184 } |
1261 | 1185 |
1262 for (int ch = 0; ch < channels; ++ch) { | 1186 for (int ch = 0; ch < channels; ++ch) { |
1263 for (int i = got; i < count; ++i) { | 1187 for (int i = got; i < count; ++i) { |
1264 buffer[ch][i] = 0.0; | 1188 buffer[ch][i] = 0.0; |
1265 } | 1189 } |
1266 } | 1190 } |
1267 } | 1191 } |
1268 | 1192 |
1269 applyAuditioningEffect(count, buffer); | 1193 applyAuditioningEffect(count, buffer); |
1270 | 1194 |
1271 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1195 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1272 cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl; | 1196 cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl; |
1273 #endif | 1197 #endif |
1274 | 1198 |
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(); | 1199 m_condition.wakeAll(); |
1363 | 1200 |
1364 return count; | 1201 return got; |
1365 } | 1202 } |
1366 | 1203 |
1367 void | 1204 void |
1368 AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float *const *buffers) | 1205 AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float *const *buffers) |
1369 { | 1206 { |