Mercurial > hg > svapp
comparison audio/AudioCallbackPlaySource.cpp @ 559:7b115a6505b8 3.0-integration
Handle increases in the overall channel count by closing and reopening the audio device.
author | Chris Cannam |
---|---|
date | Tue, 13 Dec 2016 12:03:48 +0000 |
parents | 206d65e2b69a |
children | 3c846b06c518 |
comparison
equal
deleted
inserted
replaced
558:206d65e2b69a | 559:7b115a6505b8 |
---|---|
28 #include "plugin/RealTimePluginInstance.h" | 28 #include "plugin/RealTimePluginInstance.h" |
29 | 29 |
30 #include "bqaudioio/SystemPlaybackTarget.h" | 30 #include "bqaudioio/SystemPlaybackTarget.h" |
31 #include "bqaudioio/ResamplerWrapper.h" | 31 #include "bqaudioio/ResamplerWrapper.h" |
32 | 32 |
33 #include "bqvec/VectorOps.h" | |
34 | |
33 #include <rubberband/RubberBandStretcher.h> | 35 #include <rubberband/RubberBandStretcher.h> |
34 using namespace RubberBand; | 36 using namespace RubberBand; |
37 | |
38 using breakfastquay::v_zero_channels; | |
35 | 39 |
36 #include <iostream> | 40 #include <iostream> |
37 #include <cassert> | 41 #include <cassert> |
38 | 42 |
39 //#define DEBUG_AUDIO_PLAY_SOURCE 1 | 43 //#define DEBUG_AUDIO_PLAY_SOURCE 1 |
53 m_bufferScavenger(1), | 57 m_bufferScavenger(1), |
54 m_sourceChannelCount(0), | 58 m_sourceChannelCount(0), |
55 m_blockSize(1024), | 59 m_blockSize(1024), |
56 m_sourceSampleRate(0), | 60 m_sourceSampleRate(0), |
57 m_deviceSampleRate(0), | 61 m_deviceSampleRate(0), |
62 m_deviceChannelCount(0), | |
58 m_playLatency(0), | 63 m_playLatency(0), |
59 m_target(0), | 64 m_target(0), |
60 m_lastRetrievalTimestamp(0.0), | 65 m_lastRetrievalTimestamp(0.0), |
61 m_lastRetrievedBlockSize(0), | 66 m_lastRetrievedBlockSize(0), |
62 m_trustworthyTimestamps(true), | 67 m_trustworthyTimestamps(true), |
156 m_models.insert(model); | 161 m_models.insert(model); |
157 if (model->getEndFrame() > m_lastModelEndFrame) { | 162 if (model->getEndFrame() > m_lastModelEndFrame) { |
158 m_lastModelEndFrame = model->getEndFrame(); | 163 m_lastModelEndFrame = model->getEndFrame(); |
159 } | 164 } |
160 | 165 |
161 bool buffersChanged = false, srChanged = false; | 166 bool buffersIncreased = false, srChanged = false; |
162 | 167 |
163 int modelChannels = 1; | 168 int modelChannels = 1; |
164 ReadOnlyWaveFileModel *rowfm = qobject_cast<ReadOnlyWaveFileModel *>(model); | 169 ReadOnlyWaveFileModel *rowfm = qobject_cast<ReadOnlyWaveFileModel *>(model); |
165 if (rowfm) modelChannels = rowfm->getChannelCount(); | 170 if (rowfm) modelChannels = rowfm->getChannelCount(); |
166 if (modelChannels > m_sourceChannelCount) { | 171 if (modelChannels > m_sourceChannelCount) { |
223 } | 228 } |
224 } | 229 } |
225 | 230 |
226 if (!m_writeBuffers || (int)m_writeBuffers->size() < getTargetChannelCount()) { | 231 if (!m_writeBuffers || (int)m_writeBuffers->size() < getTargetChannelCount()) { |
227 clearRingBuffers(true, getTargetChannelCount()); | 232 clearRingBuffers(true, getTargetChannelCount()); |
228 buffersChanged = true; | 233 buffersIncreased = true; |
229 } else { | 234 } else { |
230 if (willPlay) clearRingBuffers(true); | 235 if (willPlay) clearRingBuffers(true); |
231 } | 236 } |
232 | 237 |
233 if (srChanged) { | 238 if (srChanged) { |
254 | 259 |
255 rebuildRangeLists(); | 260 rebuildRangeLists(); |
256 | 261 |
257 m_mutex.unlock(); | 262 m_mutex.unlock(); |
258 | 263 |
259 //!!! | 264 m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); |
265 | |
266 if (buffersIncreased) { | |
267 SVDEBUG << "AudioCallbackPlaySource::addModel: Number of buffers increased, signalling channelCountIncreased" << endl; | |
268 emit channelCountIncreased(); | |
269 } | |
260 | 270 |
261 m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); | |
262 | |
263 if (!m_fillThread) { | 271 if (!m_fillThread) { |
264 m_fillThread = new FillThread(*this); | 272 m_fillThread = new FillThread(*this); |
265 m_fillThread->start(); | 273 m_fillThread->start(); |
266 } | 274 } |
267 | 275 |
268 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 276 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
269 cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s)" << endl; | 277 SVDEBUG << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s)" << endl; |
270 #endif | 278 #endif |
271 | 279 |
272 connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), | 280 connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), |
273 this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); | 281 this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); |
274 | 282 |
275 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 283 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
276 cout << "AudioCallbackPlaySource::addModel: awakening thread" << endl; | 284 cout << "AudioCallbackPlaySource::addModel: awakening thread" << endl; |
277 #endif | 285 #endif |
278 | 286 |
279 m_condition.wakeAll(); | 287 m_condition.wakeAll(); |
280 } | 288 } |
281 | 289 |
282 void | 290 void |
283 AudioCallbackPlaySource::modelChangedWithin(sv_frame_t | 291 AudioCallbackPlaySource::modelChangedWithin(sv_frame_t |
578 } | 586 } |
579 | 587 |
580 void | 588 void |
581 AudioCallbackPlaySource::setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *target) | 589 AudioCallbackPlaySource::setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *target) |
582 { | 590 { |
591 if (target == 0) { | |
592 // reset target-related facts and figures | |
593 m_deviceSampleRate = 0; | |
594 m_deviceChannelCount = 0; | |
595 } | |
583 m_target = target; | 596 m_target = target; |
584 } | 597 } |
585 | 598 |
586 void | 599 void |
587 AudioCallbackPlaySource::setResamplerWrapper(breakfastquay::ResamplerWrapper *w) | 600 AudioCallbackPlaySource::setResamplerWrapper(breakfastquay::ResamplerWrapper *w) |
946 { | 959 { |
947 m_deviceSampleRate = sr; | 960 m_deviceSampleRate = sr; |
948 } | 961 } |
949 | 962 |
950 void | 963 void |
951 AudioCallbackPlaySource::setSystemPlaybackChannelCount(int) | 964 AudioCallbackPlaySource::setSystemPlaybackChannelCount(int count) |
952 { | 965 { |
966 m_deviceChannelCount = count; | |
953 } | 967 } |
954 | 968 |
955 void | 969 void |
956 AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a) | 970 AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a) |
957 { | 971 { |
995 int | 1009 int |
996 AudioCallbackPlaySource::getTargetChannelCount() const | 1010 AudioCallbackPlaySource::getTargetChannelCount() const |
997 { | 1011 { |
998 if (m_sourceChannelCount < 2) return 2; | 1012 if (m_sourceChannelCount < 2) return 2; |
999 return m_sourceChannelCount; | 1013 return m_sourceChannelCount; |
1014 } | |
1015 | |
1016 int | |
1017 AudioCallbackPlaySource::getDeviceChannelCount() const | |
1018 { | |
1019 return m_deviceChannelCount; | |
1000 } | 1020 } |
1001 | 1021 |
1002 sv_samplerate_t | 1022 sv_samplerate_t |
1003 AudioCallbackPlaySource::getSourceSampleRate() const | 1023 AudioCallbackPlaySource::getSourceSampleRate() const |
1004 { | 1024 { |
1039 | 1059 |
1040 emit activity(tr("Change time-stretch factor to %1").arg(factor)); | 1060 emit activity(tr("Change time-stretch factor to %1").arg(factor)); |
1041 } | 1061 } |
1042 | 1062 |
1043 int | 1063 int |
1044 AudioCallbackPlaySource::getSourceSamples(int count, float **buffer) | 1064 AudioCallbackPlaySource::getSourceSamples(float *const *buffer, |
1045 { | 1065 int requestedChannels, |
1066 int count) | |
1067 { | |
1068 // In principle, the target will handle channel mapping in cases | |
1069 // where our channel count differs from the device's. But that | |
1070 // only holds if our channel count doesn't change -- i.e. if | |
1071 // getApplicationChannelCount() always returns the same value as | |
1072 // it did when the target was created, and if this function always | |
1073 // returns that number of channels. | |
1074 // | |
1075 // Unfortunately that can't hold for us -- we always have at least | |
1076 // 2 channels but if the user opens a new main model with more | |
1077 // channels than that (and more than the last main model) then our | |
1078 // target channel count necessarily gets increased. | |
1079 // | |
1080 // We have: | |
1081 // | |
1082 // getSourceChannelCount() -> number of channels available to | |
1083 // provide from real model data | |
1084 // | |
1085 // getTargetChannelCount() -> number we will actually provide; | |
1086 // same as getSourceChannelCount() except that it is always at | |
1087 // least 2 | |
1088 // | |
1089 // getDeviceChannelCount() -> number the device will emit, usually | |
1090 // equal to the value of getTargetChannelCount() at the time the | |
1091 // device was initialised, unless the device could not provide | |
1092 // that number | |
1093 // | |
1094 // requestedChannels -> number the device is expecting from us, | |
1095 // always equal to the value of getTargetChannelCount() at the | |
1096 // time the device was initialised | |
1097 // | |
1098 // If the requested channel count is at least the target channel | |
1099 // count, then we go ahead and provide the target channels as | |
1100 // expected. We just zero any spare channels. | |
1101 // | |
1102 // If the requested channel count is smaller than the target | |
1103 // channel count, then we don't know what to do and we provide | |
1104 // nothing. This shouldn't happen as long as management is on the | |
1105 // ball -- we emit channelCountIncreased() when the target channel | |
1106 // count increases, and whatever code "owns" the driver should | |
1107 // have reopened the audio device when it got that signal. But | |
1108 // there's a race condition there, which we accommodate with this | |
1109 // check. | |
1110 | |
1111 int channels = getTargetChannelCount(); | |
1112 | |
1046 if (!m_playing) { | 1113 if (!m_playing) { |
1047 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1114 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
1048 SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Not playing" << endl; | 1115 SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Not playing" << endl; |
1049 #endif | 1116 #endif |
1050 for (int ch = 0; ch < getTargetChannelCount(); ++ch) { | 1117 v_zero_channels(buffer, requestedChannels, count); |
1051 for (int i = 0; i < count; ++i) { | |
1052 buffer[ch][i] = 0.0; | |
1053 } | |
1054 } | |
1055 return 0; | 1118 return 0; |
1119 } | |
1120 if (requestedChannels < channels) { | |
1121 SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Not enough device channels (" << requestedChannels << ", need " << channels << "); hoping device is about to be reopened" << endl; | |
1122 v_zero_channels(buffer, requestedChannels, count); | |
1123 return 0; | |
1124 } | |
1125 if (requestedChannels > channels) { | |
1126 v_zero_channels(buffer + channels, requestedChannels - channels, count); | |
1056 } | 1127 } |
1057 | 1128 |
1058 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1129 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
1059 SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Playing" << endl; | 1130 SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Playing" << endl; |
1060 #endif | 1131 #endif |
1061 | 1132 |
1062 // Ensure that all buffers have at least the amount of data we | 1133 // Ensure that all buffers have at least the amount of data we |
1063 // need -- else reduce the size of our requests correspondingly | 1134 // need -- else reduce the size of our requests correspondingly |
1064 | 1135 |
1065 for (int ch = 0; ch < getTargetChannelCount(); ++ch) { | 1136 for (int ch = 0; ch < channels; ++ch) { |
1066 | 1137 |
1067 RingBuffer<float> *rb = getReadRingBuffer(ch); | 1138 RingBuffer<float> *rb = getReadRingBuffer(ch); |
1068 | 1139 |
1069 if (!rb) { | 1140 if (!rb) { |
1070 cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " | 1141 cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " |
1123 | 1194 |
1124 if (!ts || ratio == 1.f) { | 1195 if (!ts || ratio == 1.f) { |
1125 | 1196 |
1126 int got = 0; | 1197 int got = 0; |
1127 | 1198 |
1128 cerr << "getTargetChannelCount() == " << getTargetChannelCount() << endl; | 1199 cerr << "channels == " << channels << endl; |
1129 | 1200 |
1130 for (int ch = 0; ch < getTargetChannelCount(); ++ch) { | 1201 for (int ch = 0; ch < channels; ++ch) { |
1131 | 1202 |
1132 RingBuffer<float> *rb = getReadRingBuffer(ch); | 1203 RingBuffer<float> *rb = getReadRingBuffer(ch); |
1133 | 1204 |
1134 if (rb) { | 1205 if (rb) { |
1135 | 1206 |
1143 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 1214 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
1144 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; | 1215 cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; |
1145 #endif | 1216 #endif |
1146 } | 1217 } |
1147 | 1218 |
1148 for (int ch = 0; ch < getTargetChannelCount(); ++ch) { | 1219 for (int ch = 0; ch < channels; ++ch) { |
1149 for (int i = got; i < count; ++i) { | 1220 for (int i = got; i < count; ++i) { |
1150 buffer[ch][i] = 0.0; | 1221 buffer[ch][i] = 0.0; |
1151 } | 1222 } |
1152 } | 1223 } |
1153 } | 1224 } |
1161 m_condition.wakeAll(); | 1232 m_condition.wakeAll(); |
1162 | 1233 |
1163 return got; | 1234 return got; |
1164 } | 1235 } |
1165 | 1236 |
1166 int channels = getTargetChannelCount(); | |
1167 sv_frame_t available; | 1237 sv_frame_t available; |
1168 sv_frame_t fedToStretcher = 0; | 1238 sv_frame_t fedToStretcher = 0; |
1169 int warned = 0; | 1239 int warned = 0; |
1170 | 1240 |
1171 // The input block for a given output is approx output / ratio, | 1241 // The input block for a given output is approx output / ratio, |
1236 } | 1306 } |
1237 } | 1307 } |
1238 | 1308 |
1239 ts->retrieve(buffer, size_t(count)); | 1309 ts->retrieve(buffer, size_t(count)); |
1240 | 1310 |
1241 for (int c = stretchChannels; c < getTargetChannelCount(); ++c) { | 1311 v_zero_channels(buffer + stretchChannels, channels - stretchChannels, count); |
1242 for (int i = 0; i < count; ++i) { | |
1243 buffer[c][i] = buffer[0][i]; | |
1244 } | |
1245 } | |
1246 | 1312 |
1247 applyAuditioningEffect(count, buffer); | 1313 applyAuditioningEffect(count, buffer); |
1248 | 1314 |
1249 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1315 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1250 cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl; | 1316 cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl; |
1254 | 1320 |
1255 return count; | 1321 return count; |
1256 } | 1322 } |
1257 | 1323 |
1258 void | 1324 void |
1259 AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float **buffers) | 1325 AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float *const *buffers) |
1260 { | 1326 { |
1261 if (m_auditioningPluginBypassed) return; | 1327 if (m_auditioningPluginBypassed) return; |
1262 RealTimePluginInstance *plugin = m_auditioningPlugin; | 1328 RealTimePluginInstance *plugin = m_auditioningPlugin; |
1263 if (!plugin) return; | 1329 if (!plugin) return; |
1264 | 1330 |