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 {