Mercurial > hg > svapp
comparison audioio/AudioCallbackPlaySource.cpp @ 4:5865094175ea
* Fix update and play limits for play-selection mode when not looping
* Fix playback in loop mode when no selection -- but the GUI update for
this is still wrong on the flyback
* Various fixes and improvements to making selections, particularly during
playback
* Draw selection under non-opaque non-scrollable layers, so as to improve
cacheing
* Show selection limits as text when drawing selection
* Allow user to find missing audio files when loading session
* Cross-fade selections when in play-selection mode -- mostly. We don't
cross-fade on a processing block boundary, and unfortunately with short
selections the selection boundary is quite likely to coincide with a block
boundary.
author | Chris Cannam |
---|---|
date | Wed, 25 Jan 2006 17:46:28 +0000 |
parents | 75c3ea1c3a32 |
children | 2edc0757ca75 |
comparison
equal
deleted
inserted
replaced
3:75c3ea1c3a32 | 4:5865094175ea |
---|---|
33 m_targetSampleRate(0), | 33 m_targetSampleRate(0), |
34 m_playLatency(0), | 34 m_playLatency(0), |
35 m_playing(false), | 35 m_playing(false), |
36 m_exiting(false), | 36 m_exiting(false), |
37 m_bufferedToFrame(0), | 37 m_bufferedToFrame(0), |
38 m_lastModelEndFrame(0), | |
38 m_outputLeft(0.0), | 39 m_outputLeft(0.0), |
39 m_outputRight(0.0), | 40 m_outputRight(0.0), |
40 m_slowdownCounter(0), | 41 m_slowdownCounter(0), |
41 m_timeStretcher(0), | 42 m_timeStretcher(0), |
42 m_fillThread(0), | 43 m_fillThread(0), |
73 AudioCallbackPlaySource::addModel(Model *model) | 74 AudioCallbackPlaySource::addModel(Model *model) |
74 { | 75 { |
75 m_mutex.lock(); | 76 m_mutex.lock(); |
76 | 77 |
77 m_models.insert(model); | 78 m_models.insert(model); |
79 if (model->getEndFrame() > m_lastModelEndFrame) { | |
80 m_lastModelEndFrame = model->getEndFrame(); | |
81 } | |
78 | 82 |
79 bool buffersChanged = false, srChanged = false; | 83 bool buffersChanged = false, srChanged = false; |
80 | 84 |
81 if (m_sourceSampleRate == 0) { | 85 if (m_sourceSampleRate == 0) { |
82 | 86 |
162 m_converter = 0; | 166 m_converter = 0; |
163 } | 167 } |
164 m_sourceSampleRate = 0; | 168 m_sourceSampleRate = 0; |
165 } | 169 } |
166 | 170 |
171 size_t lastEnd = 0; | |
172 for (std::set<Model *>::const_iterator i = m_models.begin(); | |
173 i != m_models.end(); ++i) { | |
174 if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame(); | |
175 } | |
176 m_lastModelEndFrame = lastEnd; | |
177 | |
167 m_audioGenerator->removeModel(model); | 178 m_audioGenerator->removeModel(model); |
168 | 179 |
169 m_mutex.unlock(); | 180 m_mutex.unlock(); |
170 } | 181 } |
171 | 182 |
179 if (m_converter) { | 190 if (m_converter) { |
180 src_delete(m_converter); | 191 src_delete(m_converter); |
181 m_converter = 0; | 192 m_converter = 0; |
182 } | 193 } |
183 | 194 |
195 m_lastModelEndFrame = 0; | |
196 | |
184 m_audioGenerator->clearModels(); | 197 m_audioGenerator->clearModels(); |
185 | 198 |
186 m_sourceSampleRate = 0; | 199 m_sourceSampleRate = 0; |
187 | 200 |
188 m_mutex.unlock(); | 201 m_mutex.unlock(); |
189 } | 202 } |
190 | 203 |
191 void | 204 void |
192 AudioCallbackPlaySource::play(size_t startFrame) | 205 AudioCallbackPlaySource::play(size_t startFrame) |
193 { | 206 { |
207 if (m_viewManager->getPlaySelectionMode()) { | |
208 ViewManager::SelectionList selections = m_viewManager->getSelections(); | |
209 ViewManager::SelectionList::iterator i = selections.begin(); | |
210 if (i != selections.end()) { | |
211 if (startFrame < i->getStartFrame()) { | |
212 startFrame = i->getStartFrame(); | |
213 } else { | |
214 ViewManager::SelectionList::iterator j = selections.end(); | |
215 --j; | |
216 if (startFrame >= j->getEndFrame()) { | |
217 startFrame = i->getStartFrame(); | |
218 } | |
219 } | |
220 } | |
221 } | |
222 | |
194 // The fill thread will automatically empty its buffers before | 223 // The fill thread will automatically empty its buffers before |
195 // starting again if we have not so far been playing, but not if | 224 // starting again if we have not so far been playing, but not if |
196 // we're just re-seeking. | 225 // we're just re-seeking. |
197 | 226 |
198 if (m_playing) { | 227 if (m_playing) { |
209 | 238 |
210 m_audioGenerator->reset(); | 239 m_audioGenerator->reset(); |
211 | 240 |
212 m_playing = true; | 241 m_playing = true; |
213 m_condition.wakeAll(); | 242 m_condition.wakeAll(); |
243 emit playStatusChanged(m_playing); | |
214 } | 244 } |
215 | 245 |
216 void | 246 void |
217 AudioCallbackPlaySource::stop() | 247 AudioCallbackPlaySource::stop() |
218 { | 248 { |
219 m_playing = false; | 249 m_playing = false; |
220 m_condition.wakeAll(); | 250 m_condition.wakeAll(); |
251 emit playStatusChanged(m_playing); | |
221 } | 252 } |
222 | 253 |
223 void | 254 void |
224 AudioCallbackPlaySource::selectionChanged() | 255 AudioCallbackPlaySource::selectionChanged() |
225 { | 256 { |
227 m_mutex.lock(); | 258 m_mutex.lock(); |
228 for (size_t c = 0; c < m_bufferCount; ++c) { | 259 for (size_t c = 0; c < m_bufferCount; ++c) { |
229 getRingBuffer(c).reset(); | 260 getRingBuffer(c).reset(); |
230 } | 261 } |
231 m_mutex.unlock(); | 262 m_mutex.unlock(); |
263 m_condition.wakeAll(); | |
232 } | 264 } |
233 } | 265 } |
234 | 266 |
235 void | 267 void |
236 AudioCallbackPlaySource::playLoopModeChanged() | 268 AudioCallbackPlaySource::playLoopModeChanged() |
237 { | 269 { |
238 m_mutex.lock(); | |
239 for (size_t c = 0; c < m_bufferCount; ++c) { | |
240 getRingBuffer(c).reset(); | |
241 } | |
242 m_mutex.unlock(); | |
243 } | 270 } |
244 | 271 |
245 void | 272 void |
246 AudioCallbackPlaySource::playSelectionModeChanged() | 273 AudioCallbackPlaySource::playSelectionModeChanged() |
247 { | 274 { |
249 m_mutex.lock(); | 276 m_mutex.lock(); |
250 for (size_t c = 0; c < m_bufferCount; ++c) { | 277 for (size_t c = 0; c < m_bufferCount; ++c) { |
251 getRingBuffer(c).reset(); | 278 getRingBuffer(c).reset(); |
252 } | 279 } |
253 m_mutex.unlock(); | 280 m_mutex.unlock(); |
281 m_condition.wakeAll(); | |
254 } | 282 } |
255 } | 283 } |
256 | 284 |
257 void | 285 void |
258 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 286 AudioCallbackPlaySource::setTargetBlockSize(size_t size) |
315 latency += readSpace; | 343 latency += readSpace; |
316 size_t bufferedFrame = m_bufferedToFrame; | 344 size_t bufferedFrame = m_bufferedToFrame; |
317 | 345 |
318 size_t framePlaying = bufferedFrame; | 346 size_t framePlaying = bufferedFrame; |
319 if (framePlaying > latency) framePlaying -= latency; | 347 if (framePlaying > latency) framePlaying -= latency; |
320 else framePlaying = 0; | 348 else { |
349 //!!! Not right | |
350 if (m_viewManager->getPlayLoopMode() && | |
351 !m_viewManager->getPlaySelectionMode()) { | |
352 framePlaying += m_lastModelEndFrame; | |
353 if (framePlaying > latency) framePlaying -= latency; | |
354 else framePlaying = 0; | |
355 } | |
356 } | |
321 | 357 |
322 if (!m_viewManager->getPlaySelectionMode()) { | 358 if (!m_viewManager->getPlaySelectionMode()) { |
323 return framePlaying; | 359 return framePlaying; |
324 } | 360 } |
325 | 361 |
328 return framePlaying; | 364 return framePlaying; |
329 } | 365 } |
330 | 366 |
331 ViewManager::SelectionList::const_iterator i; | 367 ViewManager::SelectionList::const_iterator i; |
332 | 368 |
369 i = selections.begin(); | |
370 size_t rangeStart = i->getStartFrame(); | |
371 | |
372 i = selections.end(); | |
373 --i; | |
374 size_t rangeEnd = i->getEndFrame(); | |
375 | |
333 for (i = selections.begin(); i != selections.end(); ++i) { | 376 for (i = selections.begin(); i != selections.end(); ++i) { |
334 if (i->contains(bufferedFrame)) break; | 377 if (i->contains(bufferedFrame)) break; |
335 } | 378 } |
336 | 379 |
337 size_t f = bufferedFrame; | 380 size_t f = bufferedFrame; |
338 | 381 |
339 std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << std::endl; | 382 // std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; |
340 | 383 |
341 if (i == selections.end()) { | 384 if (i == selections.end()) { |
342 --i; | 385 --i; |
343 if (i->getEndFrame() + latency < f) { | 386 if (i->getEndFrame() + latency < f) { |
344 return framePlaying; | 387 // std::cerr << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; |
388 | |
389 if (!m_viewManager->getPlayLoopMode() && (framePlaying > rangeEnd)) { | |
390 // std::cerr << "STOPPING" << std::endl; | |
391 stop(); | |
392 return rangeEnd; | |
393 } else { | |
394 return framePlaying; | |
395 } | |
345 } else { | 396 } else { |
346 std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | 397 // std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; |
347 latency -= (f - i->getEndFrame()); | 398 latency -= (f - i->getEndFrame()); |
348 f = i->getEndFrame(); | 399 f = i->getEndFrame(); |
349 } | 400 } |
350 } | 401 } |
351 | 402 |
352 std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | 403 // std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; |
353 | 404 |
354 while (latency > 0) { | 405 while (latency > 0) { |
355 size_t offset = f - i->getStartFrame(); | 406 size_t offset = f - i->getStartFrame(); |
356 if (offset >= latency) { | 407 if (offset >= latency) { |
357 if (f > latency) { | 408 if (f > latency) { |
613 size_t spaceHere = getRingBuffer(c).getWriteSpace(); | 664 size_t spaceHere = getRingBuffer(c).getWriteSpace(); |
614 if (c == 0 || spaceHere < space) space = spaceHere; | 665 if (c == 0 || spaceHere < space) space = spaceHere; |
615 } | 666 } |
616 | 667 |
617 if (space == 0) return; | 668 if (space == 0) return; |
618 | 669 |
619 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 670 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
620 std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; | 671 std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; |
621 #endif | 672 #endif |
622 | 673 |
623 size_t f = m_bufferedToFrame; | 674 size_t f = m_bufferedToFrame; |
816 while (processed < count) { | 867 while (processed < count) { |
817 | 868 |
818 chunkSize = count - processed; | 869 chunkSize = count - processed; |
819 nextChunkStart = chunkStart + chunkSize; | 870 nextChunkStart = chunkStart + chunkSize; |
820 | 871 |
872 size_t fadeIn = 0, fadeOut = 0; | |
873 | |
821 if (useSelection) { | 874 if (useSelection) { |
822 | 875 |
823 Selection selection = | 876 Selection selection = |
824 m_viewManager->getContainingSelection(chunkStart, true); | 877 m_viewManager->getContainingSelection(chunkStart, true); |
825 | 878 |
826 if (selection.isEmpty()) { | 879 if (selection.isEmpty()) { |
827 if (m_viewManager->getPlayLoopMode()) { | 880 if (m_viewManager->getPlayLoopMode()) { |
828 selection = *m_viewManager->getSelections().begin(); | 881 selection = *m_viewManager->getSelections().begin(); |
829 chunkStart = selection.getStartFrame(); | 882 chunkStart = selection.getStartFrame(); |
883 fadeIn = 50; | |
830 } | 884 } |
831 } | 885 } |
832 | 886 |
833 if (selection.isEmpty()) { | 887 if (selection.isEmpty()) { |
834 | 888 |
837 | 891 |
838 } else { | 892 } else { |
839 | 893 |
840 if (chunkStart < selection.getStartFrame()) { | 894 if (chunkStart < selection.getStartFrame()) { |
841 chunkStart = selection.getStartFrame(); | 895 chunkStart = selection.getStartFrame(); |
896 fadeIn = 50; | |
842 } | 897 } |
843 | 898 |
844 nextChunkStart = std::min(chunkStart + chunkSize, | 899 nextChunkStart = chunkStart + chunkSize; |
845 selection.getEndFrame()); | 900 |
901 if (nextChunkStart > selection.getEndFrame()) { | |
902 nextChunkStart = selection.getEndFrame(); | |
903 fadeOut = 50; | |
904 } | |
846 | 905 |
847 chunkSize = nextChunkStart - chunkStart; | 906 chunkSize = nextChunkStart - chunkStart; |
848 } | 907 } |
908 | |
909 } else if (m_viewManager->getPlayLoopMode() && | |
910 m_lastModelEndFrame > 0) { | |
911 | |
912 if (chunkStart >= m_lastModelEndFrame) { | |
913 chunkStart = 0; | |
914 } | |
915 if (chunkSize > m_lastModelEndFrame - chunkStart) { | |
916 chunkSize = m_lastModelEndFrame - chunkStart; | |
917 } | |
918 nextChunkStart = chunkStart + chunkSize; | |
849 } | 919 } |
850 | 920 |
851 if (!chunkSize) { | 921 if (!chunkSize) { |
852 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 922 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
853 std::cerr << "Ending selection playback at " << nextChunkStart << std::endl; | 923 std::cerr << "Ending selection playback at " << nextChunkStart << std::endl; |
863 std::cerr << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; | 933 std::cerr << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; |
864 #endif | 934 #endif |
865 | 935 |
866 size_t got = 0; | 936 size_t got = 0; |
867 | 937 |
938 if (chunkSize < 100) { | |
939 fadeIn = 0; | |
940 fadeOut = 0; | |
941 } else if (chunkSize < 300) { | |
942 if (fadeIn > 0) fadeIn = 10; | |
943 if (fadeOut > 0) fadeOut = 10; | |
944 } | |
945 | |
946 if (fadeIn > 0) { | |
947 if (processed * 2 < fadeIn) { | |
948 fadeIn = processed * 2; | |
949 } | |
950 } | |
951 | |
952 if (fadeOut > 0) { | |
953 if ((count - processed) * 2 < fadeOut) { | |
954 fadeOut = (count - processed) * 2; | |
955 } | |
956 } | |
957 | |
868 for (std::set<Model *>::iterator mi = m_models.begin(); | 958 for (std::set<Model *>::iterator mi = m_models.begin(); |
869 mi != m_models.end(); ++mi) { | 959 mi != m_models.end(); ++mi) { |
870 | 960 |
871 got = m_audioGenerator->mixModel(*mi, chunkStart, | 961 got = m_audioGenerator->mixModel(*mi, chunkStart, |
872 chunkSize, chunkBufferPtrs); | 962 chunkSize, chunkBufferPtrs, |
963 fadeIn, fadeOut); | |
873 } | 964 } |
874 | 965 |
875 for (size_t c = 0; c < channels; ++c) { | 966 for (size_t c = 0; c < channels; ++c) { |
876 chunkBufferPtrs[c] += chunkSize; | 967 chunkBufferPtrs[c] += chunkSize; |
877 } | 968 } |
909 if (s.getSourceSampleRate() > 0) { | 1000 if (s.getSourceSampleRate() > 0) { |
910 ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0; | 1001 ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0; |
911 } | 1002 } |
912 | 1003 |
913 if (!s.m_playing) ms *= 10; | 1004 if (!s.m_playing) ms *= 10; |
914 | 1005 ms = ms / 8; |
915 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1006 |
916 std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms/4 << "ms..." << std::endl; | 1007 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
917 #endif | 1008 std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl; |
918 | 1009 #endif |
919 s.m_condition.wait(&s.m_mutex, size_t(ms / 4)); | 1010 |
1011 s.m_condition.wait(&s.m_mutex, size_t(ms)); | |
920 | 1012 |
921 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1013 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
922 std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; | 1014 std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; |
923 #endif | 1015 #endif |
924 | 1016 |