Mercurial > hg > svapp
comparison audioio/AudioCallbackPlaySource.cpp @ 101:89a689720ee9 spectrogram-cache-rejig
* Merge from trunk
author | Chris Cannam |
---|---|
date | Wed, 27 Feb 2008 11:59:42 +0000 |
parents | eb596ef12041 |
children |
comparison
equal
deleted
inserted
replaced
59:bf1a53489ccc | 101:89a689720ee9 |
---|---|
23 #include "base/Preferences.h" | 23 #include "base/Preferences.h" |
24 #include "data/model/DenseTimeValueModel.h" | 24 #include "data/model/DenseTimeValueModel.h" |
25 #include "data/model/WaveFileModel.h" | 25 #include "data/model/WaveFileModel.h" |
26 #include "data/model/SparseOneDimensionalModel.h" | 26 #include "data/model/SparseOneDimensionalModel.h" |
27 #include "plugin/RealTimePluginInstance.h" | 27 #include "plugin/RealTimePluginInstance.h" |
28 #include "PhaseVocoderTimeStretcher.h" | 28 |
29 #include "AudioCallbackPlayTarget.h" | |
30 | |
31 #include <rubberband/RubberBandStretcher.h> | |
32 using namespace RubberBand; | |
29 | 33 |
30 #include <iostream> | 34 #include <iostream> |
31 #include <cassert> | 35 #include <cassert> |
32 | 36 |
33 //#define DEBUG_AUDIO_PLAY_SOURCE 1 | 37 //#define DEBUG_AUDIO_PLAY_SOURCE 1 |
48 m_sourceChannelCount(0), | 52 m_sourceChannelCount(0), |
49 m_blockSize(1024), | 53 m_blockSize(1024), |
50 m_sourceSampleRate(0), | 54 m_sourceSampleRate(0), |
51 m_targetSampleRate(0), | 55 m_targetSampleRate(0), |
52 m_playLatency(0), | 56 m_playLatency(0), |
57 m_target(0), | |
58 m_lastRetrievalTimestamp(0.0), | |
59 m_lastRetrievedBlockSize(0), | |
53 m_playing(false), | 60 m_playing(false), |
54 m_exiting(false), | 61 m_exiting(false), |
55 m_lastModelEndFrame(0), | 62 m_lastModelEndFrame(0), |
56 m_outputLeft(0.0), | 63 m_outputLeft(0.0), |
57 m_outputRight(0.0), | 64 m_outputRight(0.0), |
58 m_auditioningPlugin(0), | 65 m_auditioningPlugin(0), |
59 m_auditioningPluginBypassed(false), | 66 m_auditioningPluginBypassed(false), |
67 m_playStartFrame(0), | |
68 m_playStartFramePassed(false), | |
60 m_timeStretcher(0), | 69 m_timeStretcher(0), |
70 m_stretchRatio(1.0), | |
71 m_stretcherInputCount(0), | |
72 m_stretcherInputs(0), | |
73 m_stretcherInputSizes(0), | |
61 m_fillThread(0), | 74 m_fillThread(0), |
62 m_converter(0), | 75 m_converter(0), |
63 m_crapConverter(0), | 76 m_crapConverter(0), |
64 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) | 77 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) |
65 { | 78 { |
99 | 112 |
100 delete m_writeBuffers; | 113 delete m_writeBuffers; |
101 | 114 |
102 delete m_audioGenerator; | 115 delete m_audioGenerator; |
103 | 116 |
117 for (size_t i = 0; i < m_stretcherInputCount; ++i) { | |
118 delete[] m_stretcherInputs[i]; | |
119 } | |
120 delete[] m_stretcherInputSizes; | |
121 delete[] m_stretcherInputs; | |
122 | |
104 m_bufferScavenger.scavenge(true); | 123 m_bufferScavenger.scavenge(true); |
105 m_pluginScavenger.scavenge(true); | 124 m_pluginScavenger.scavenge(true); |
106 m_timeStretcherScavenger.scavenge(true); | |
107 } | 125 } |
108 | 126 |
109 void | 127 void |
110 AudioCallbackPlaySource::addModel(Model *model) | 128 AudioCallbackPlaySource::addModel(Model *model) |
111 { | 129 { |
226 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) | 244 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) |
227 { | 245 { |
228 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 246 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
229 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; | 247 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; |
230 #endif | 248 #endif |
231 if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; | 249 if (endFrame > m_lastModelEndFrame) { |
250 m_lastModelEndFrame = endFrame; | |
251 rebuildRangeLists(); | |
252 } | |
232 } | 253 } |
233 | 254 |
234 void | 255 void |
235 AudioCallbackPlaySource::removeModel(Model *model) | 256 AudioCallbackPlaySource::removeModel(Model *model) |
236 { | 257 { |
294 m_sourceSampleRate = 0; | 315 m_sourceSampleRate = 0; |
295 | 316 |
296 m_mutex.unlock(); | 317 m_mutex.unlock(); |
297 | 318 |
298 m_audioGenerator->clearModels(); | 319 m_audioGenerator->clearModels(); |
320 | |
321 clearRingBuffers(); | |
299 } | 322 } |
300 | 323 |
301 void | 324 void |
302 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) | 325 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) |
303 { | 326 { |
304 if (!haveLock) m_mutex.lock(); | 327 if (!haveLock) m_mutex.lock(); |
328 | |
329 rebuildRangeLists(); | |
305 | 330 |
306 if (count == 0) { | 331 if (count == 0) { |
307 if (m_writeBuffers) count = m_writeBuffers->size(); | 332 if (m_writeBuffers) count = m_writeBuffers->size(); |
308 } | 333 } |
309 | 334 |
310 size_t sf = m_readBufferFill; | 335 m_writeBufferFill = getCurrentBufferedFrame(); |
311 RingBuffer<float> *rb = getReadRingBuffer(0); | |
312 if (rb) { | |
313 //!!! This is incorrect if we're in a non-contiguous selection | |
314 //Same goes for all related code (subtracting the read space | |
315 //from the fill frame to try to establish where the effective | |
316 //pre-resample/timestretch read pointer is) | |
317 size_t rs = rb->getReadSpace(); | |
318 if (rs < sf) sf -= rs; | |
319 else sf = 0; | |
320 } | |
321 m_writeBufferFill = sf; | |
322 | 336 |
323 if (m_readBuffers != m_writeBuffers) { | 337 if (m_readBuffers != m_writeBuffers) { |
324 delete m_writeBuffers; | 338 delete m_writeBuffers; |
325 } | 339 } |
326 | 340 |
341 void | 355 void |
342 AudioCallbackPlaySource::play(size_t startFrame) | 356 AudioCallbackPlaySource::play(size_t startFrame) |
343 { | 357 { |
344 if (m_viewManager->getPlaySelectionMode() && | 358 if (m_viewManager->getPlaySelectionMode() && |
345 !m_viewManager->getSelections().empty()) { | 359 !m_viewManager->getSelections().empty()) { |
346 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 360 |
347 MultiSelection::SelectionList::iterator i = selections.begin(); | 361 std::cerr << "AudioCallbackPlaySource::play: constraining frame " << startFrame << " to selection = "; |
348 if (i != selections.end()) { | 362 |
349 if (startFrame < i->getStartFrame()) { | 363 startFrame = m_viewManager->constrainFrameToSelection(startFrame); |
350 startFrame = i->getStartFrame(); | 364 |
351 } else { | 365 std::cerr << startFrame << std::endl; |
352 MultiSelection::SelectionList::iterator j = selections.end(); | 366 |
353 --j; | |
354 if (startFrame >= j->getEndFrame()) { | |
355 startFrame = i->getStartFrame(); | |
356 } | |
357 } | |
358 } | |
359 } else { | 367 } else { |
360 if (startFrame >= m_lastModelEndFrame) { | 368 if (startFrame >= m_lastModelEndFrame) { |
361 startFrame = 0; | 369 startFrame = 0; |
362 } | 370 } |
363 } | 371 } |
364 | 372 |
373 std::cerr << "play(" << startFrame << ") -> playback model "; | |
374 | |
375 startFrame = m_viewManager->alignReferenceToPlaybackFrame(startFrame); | |
376 | |
377 std::cerr << startFrame << std::endl; | |
378 | |
365 // The fill thread will automatically empty its buffers before | 379 // The fill thread will automatically empty its buffers before |
366 // starting again if we have not so far been playing, but not if | 380 // starting again if we have not so far been playing, but not if |
367 // we're just re-seeking. | 381 // we're just re-seeking. |
368 | 382 |
369 m_mutex.lock(); | 383 m_mutex.lock(); |
384 if (m_timeStretcher) { | |
385 m_timeStretcher->reset(); | |
386 } | |
370 if (m_playing) { | 387 if (m_playing) { |
388 std::cerr << "playing already, resetting" << std::endl; | |
371 m_readBufferFill = m_writeBufferFill = startFrame; | 389 m_readBufferFill = m_writeBufferFill = startFrame; |
372 if (m_readBuffers) { | 390 if (m_readBuffers) { |
373 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 391 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
374 RingBuffer<float> *rb = getReadRingBuffer(c); | 392 RingBuffer<float> *rb = getReadRingBuffer(c); |
393 std::cerr << "reset ring buffer for channel " << c << std::endl; | |
375 if (rb) rb->reset(); | 394 if (rb) rb->reset(); |
376 } | 395 } |
377 } | 396 } |
378 if (m_converter) src_reset(m_converter); | 397 if (m_converter) src_reset(m_converter); |
379 if (m_crapConverter) src_reset(m_crapConverter); | 398 if (m_crapConverter) src_reset(m_crapConverter); |
384 } | 403 } |
385 m_mutex.unlock(); | 404 m_mutex.unlock(); |
386 | 405 |
387 m_audioGenerator->reset(); | 406 m_audioGenerator->reset(); |
388 | 407 |
408 m_playStartFrame = startFrame; | |
409 m_playStartFramePassed = false; | |
410 m_playStartedAt = RealTime::zeroTime; | |
411 if (m_target) { | |
412 m_playStartedAt = RealTime::fromSeconds(m_target->getCurrentTime()); | |
413 } | |
414 | |
389 bool changed = !m_playing; | 415 bool changed = !m_playing; |
416 m_lastRetrievalTimestamp = 0; | |
390 m_playing = true; | 417 m_playing = true; |
391 m_condition.wakeAll(); | 418 m_condition.wakeAll(); |
392 if (changed) emit playStatusChanged(m_playing); | 419 if (changed) emit playStatusChanged(m_playing); |
393 } | 420 } |
394 | 421 |
396 AudioCallbackPlaySource::stop() | 423 AudioCallbackPlaySource::stop() |
397 { | 424 { |
398 bool changed = m_playing; | 425 bool changed = m_playing; |
399 m_playing = false; | 426 m_playing = false; |
400 m_condition.wakeAll(); | 427 m_condition.wakeAll(); |
428 m_lastRetrievalTimestamp = 0; | |
401 if (changed) emit playStatusChanged(m_playing); | 429 if (changed) emit playStatusChanged(m_playing); |
402 } | 430 } |
403 | 431 |
404 void | 432 void |
405 AudioCallbackPlaySource::selectionChanged() | 433 AudioCallbackPlaySource::selectionChanged() |
446 emit audioOverloadPluginDisabled(); | 474 emit audioOverloadPluginDisabled(); |
447 } | 475 } |
448 } | 476 } |
449 | 477 |
450 void | 478 void |
451 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 479 AudioCallbackPlaySource::setTarget(AudioCallbackPlayTarget *target, size_t size) |
452 { | 480 { |
481 m_target = target; | |
453 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 482 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; |
454 assert(size < m_ringBufferSize); | 483 assert(size < m_ringBufferSize); |
455 m_blockSize = size; | 484 m_blockSize = size; |
456 } | 485 } |
457 | 486 |
475 } | 504 } |
476 | 505 |
477 size_t | 506 size_t |
478 AudioCallbackPlaySource::getCurrentPlayingFrame() | 507 AudioCallbackPlaySource::getCurrentPlayingFrame() |
479 { | 508 { |
509 // This method attempts to estimate which audio sample frame is | |
510 // "currently coming through the speakers". | |
511 | |
512 size_t targetRate = getTargetSampleRate(); | |
513 size_t latency = m_playLatency; // at target rate | |
514 RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); | |
515 | |
516 return getCurrentFrame(latency_t); | |
517 } | |
518 | |
519 size_t | |
520 AudioCallbackPlaySource::getCurrentBufferedFrame() | |
521 { | |
522 return getCurrentFrame(RealTime::zeroTime); | |
523 } | |
524 | |
525 size_t | |
526 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) | |
527 { | |
480 bool resample = false; | 528 bool resample = false; |
481 double ratio = 1.0; | 529 double resampleRatio = 1.0; |
482 | 530 |
483 if (getSourceSampleRate() != getTargetSampleRate()) { | 531 // We resample when filling the ring buffer, and time-stretch when |
484 resample = true; | 532 // draining it. The buffer contains data at the "target rate" and |
485 ratio = double(getSourceSampleRate()) / double(getTargetSampleRate()); | 533 // the latency provided by the target is also at the target rate. |
486 } | 534 // Because of the multiple rates involved, we do the actual |
487 | 535 // calculation using RealTime instead. |
488 size_t readSpace = 0; | 536 |
537 size_t sourceRate = getSourceSampleRate(); | |
538 size_t targetRate = getTargetSampleRate(); | |
539 | |
540 if (sourceRate == 0 || targetRate == 0) return 0; | |
541 | |
542 size_t inbuffer = 0; // at target rate | |
543 | |
489 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 544 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
490 RingBuffer<float> *rb = getReadRingBuffer(c); | 545 RingBuffer<float> *rb = getReadRingBuffer(c); |
491 if (rb) { | 546 if (rb) { |
492 size_t spaceHere = rb->getReadSpace(); | 547 size_t here = rb->getReadSpace(); |
493 if (c == 0 || spaceHere < readSpace) readSpace = spaceHere; | 548 if (c == 0 || here < inbuffer) inbuffer = here; |
494 } | 549 } |
495 } | 550 } |
496 | 551 |
497 if (resample) { | 552 size_t readBufferFill = m_readBufferFill; |
498 readSpace = size_t(readSpace * ratio + 0.1); | 553 size_t lastRetrievedBlockSize = m_lastRetrievedBlockSize; |
499 } | 554 double lastRetrievalTimestamp = m_lastRetrievalTimestamp; |
500 | 555 double currentTime = 0.0; |
501 size_t latency = m_playLatency; | 556 if (m_target) currentTime = m_target->getCurrentTime(); |
502 if (resample) latency = size_t(m_playLatency * ratio + 0.1); | 557 |
503 | 558 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); |
504 PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher; | 559 |
505 if (timeStretcher) { | 560 size_t stretchlat = 0; |
506 latency += timeStretcher->getProcessingLatency(); | 561 double timeRatio = 1.0; |
507 } | 562 |
508 | 563 if (m_timeStretcher) { |
509 latency += readSpace; | 564 stretchlat = m_timeStretcher->getLatency(); |
510 size_t bufferedFrame = m_readBufferFill; | 565 timeRatio = m_timeStretcher->getTimeRatio(); |
566 } | |
567 | |
568 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate); | |
569 | |
570 // When the target has just requested a block from us, the last | |
571 // sample it obtained was our buffer fill frame count minus the | |
572 // amount of read space (converted back to source sample rate) | |
573 // remaining now. That sample is not expected to be played until | |
574 // the target's play latency has elapsed. By the time the | |
575 // following block is requested, that sample will be at the | |
576 // target's play latency minus the last requested block size away | |
577 // from being played. | |
578 | |
579 RealTime sincerequest_t = RealTime::zeroTime; | |
580 RealTime lastretrieved_t = RealTime::zeroTime; | |
581 | |
582 if (m_target && lastRetrievalTimestamp != 0.0) { | |
583 | |
584 lastretrieved_t = RealTime::frame2RealTime | |
585 (lastRetrievedBlockSize, targetRate); | |
586 | |
587 // calculate number of frames at target rate that have elapsed | |
588 // since the end of the last call to getSourceSamples | |
589 | |
590 double elapsed = currentTime - lastRetrievalTimestamp; | |
591 | |
592 if (elapsed > 0.0) { | |
593 sincerequest_t = RealTime::fromSeconds(elapsed); | |
594 } | |
595 | |
596 } else { | |
597 | |
598 lastretrieved_t = RealTime::frame2RealTime | |
599 (getTargetBlockSize(), targetRate); | |
600 } | |
601 | |
602 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate); | |
603 | |
604 if (timeRatio != 1.0) { | |
605 lastretrieved_t = lastretrieved_t / timeRatio; | |
606 sincerequest_t = sincerequest_t / timeRatio; | |
607 } | |
511 | 608 |
512 bool looping = m_viewManager->getPlayLoopMode(); | 609 bool looping = m_viewManager->getPlayLoopMode(); |
513 bool constrained = (m_viewManager->getPlaySelectionMode() && | 610 |
514 !m_viewManager->getSelections().empty()); | 611 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
515 | 612 std::cerr << "\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: " << lastretrieved_t << std::endl; |
516 size_t framePlaying = bufferedFrame; | 613 #endif |
517 | 614 |
518 if (looping && !constrained) { | 615 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); |
519 while (framePlaying < latency) framePlaying += m_lastModelEndFrame; | 616 |
520 } | 617 // Normally the range lists should contain at least one item each |
521 | 618 // -- if playback is unconstrained, that item should report the |
522 if (framePlaying > latency) framePlaying -= latency; | 619 // entire source audio duration. |
523 else framePlaying = 0; | 620 |
621 if (m_rangeStarts.empty()) { | |
622 rebuildRangeLists(); | |
623 } | |
624 | |
625 if (m_rangeStarts.empty()) { | |
626 // this code is only used in case of error in rebuildRangeLists | |
627 RealTime playing_t = bufferedto_t | |
628 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | |
629 + sincerequest_t; | |
630 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | |
631 return m_viewManager->alignPlaybackFrameToReference(frame); | |
632 } | |
633 | |
634 int inRange = 0; | |
635 int index = 0; | |
636 | |
637 for (size_t i = 0; i < m_rangeStarts.size(); ++i) { | |
638 if (bufferedto_t >= m_rangeStarts[i]) { | |
639 inRange = index; | |
640 } else { | |
641 break; | |
642 } | |
643 ++index; | |
644 } | |
645 | |
646 if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1; | |
647 | |
648 RealTime playing_t = bufferedto_t; | |
649 | |
650 playing_t = playing_t | |
651 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | |
652 + sincerequest_t; | |
653 | |
654 // This rather gross little hack is used to ensure that latency | |
655 // compensation doesn't result in the playback pointer appearing | |
656 // to start earlier than the actual playback does. It doesn't | |
657 // work properly (hence the bail-out in the middle) because if we | |
658 // are playing a relatively short looped region, the playing time | |
659 // estimated from the buffer fill frame may have wrapped around | |
660 // the region boundary and end up being much smaller than the | |
661 // theoretical play start frame, perhaps even for the entire | |
662 // duration of playback! | |
663 | |
664 if (!m_playStartFramePassed) { | |
665 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, | |
666 sourceRate); | |
667 if (playing_t < playstart_t) { | |
668 // std::cerr << "playing_t " << playing_t << " < playstart_t " | |
669 // << playstart_t << std::endl; | |
670 if (sincerequest_t > RealTime::zeroTime && | |
671 m_playStartedAt + latency_t + stretchlat_t < | |
672 RealTime::fromSeconds(currentTime)) { | |
673 // std::cerr << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << std::endl; | |
674 m_playStartFramePassed = true; | |
675 } else { | |
676 playing_t = playstart_t; | |
677 } | |
678 } else { | |
679 m_playStartFramePassed = true; | |
680 } | |
681 } | |
682 | |
683 playing_t = playing_t - m_rangeStarts[inRange]; | |
684 | |
685 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
686 std::cerr << "playing_t as offset into range " << inRange << " (with start = " << m_rangeStarts[inRange] << ") = " << playing_t << std::endl; | |
687 #endif | |
688 | |
689 while (playing_t < RealTime::zeroTime) { | |
690 | |
691 if (inRange == 0) { | |
692 if (looping) { | |
693 inRange = m_rangeStarts.size() - 1; | |
694 } else { | |
695 break; | |
696 } | |
697 } else { | |
698 --inRange; | |
699 } | |
700 | |
701 playing_t = playing_t + m_rangeDurations[inRange]; | |
702 } | |
703 | |
704 playing_t = playing_t + m_rangeStarts[inRange]; | |
705 | |
706 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
707 std::cerr << " playing time: " << playing_t << std::endl; | |
708 #endif | |
709 | |
710 if (!looping) { | |
711 if (inRange == m_rangeStarts.size()-1 && | |
712 playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) { | |
713 std::cerr << "Not looping, inRange " << inRange << " == rangeStarts.size()-1, playing_t " << playing_t << " >= m_rangeStarts[inRange] " << m_rangeStarts[inRange] << " + m_rangeDurations[inRange] " << m_rangeDurations[inRange] << " -- stopping" << std::endl; | |
714 stop(); | |
715 } | |
716 } | |
717 | |
718 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | |
719 | |
720 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | |
721 return m_viewManager->alignPlaybackFrameToReference(frame); | |
722 } | |
723 | |
724 void | |
725 AudioCallbackPlaySource::rebuildRangeLists() | |
726 { | |
727 bool constrained = (m_viewManager->getPlaySelectionMode()); | |
728 | |
729 m_rangeStarts.clear(); | |
730 m_rangeDurations.clear(); | |
731 | |
732 size_t sourceRate = getSourceSampleRate(); | |
733 if (sourceRate == 0) return; | |
734 | |
735 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); | |
736 if (end == RealTime::zeroTime) return; | |
524 | 737 |
525 if (!constrained) { | 738 if (!constrained) { |
526 if (!looping && framePlaying > m_lastModelEndFrame) { | 739 m_rangeStarts.push_back(RealTime::zeroTime); |
527 framePlaying = m_lastModelEndFrame; | 740 m_rangeDurations.push_back(end); |
528 stop(); | 741 return; |
529 } | |
530 return framePlaying; | |
531 } | 742 } |
532 | 743 |
533 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 744 MultiSelection::SelectionList selections = m_viewManager->getSelections(); |
534 MultiSelection::SelectionList::const_iterator i; | 745 MultiSelection::SelectionList::const_iterator i; |
535 | 746 |
536 // i = selections.begin(); | 747 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
537 // size_t rangeStart = i->getStartFrame(); | 748 std::cerr << "AudioCallbackPlaySource::rebuildRangeLists" << std::endl; |
538 | 749 #endif |
539 i = selections.end(); | 750 |
540 --i; | 751 if (!selections.empty()) { |
541 size_t rangeEnd = i->getEndFrame(); | 752 |
542 | 753 for (i = selections.begin(); i != selections.end(); ++i) { |
543 for (i = selections.begin(); i != selections.end(); ++i) { | 754 |
544 if (i->contains(bufferedFrame)) break; | 755 RealTime start = |
545 } | 756 (RealTime::frame2RealTime |
546 | 757 (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
547 size_t f = bufferedFrame; | 758 sourceRate)); |
548 | 759 RealTime duration = |
549 // std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; | 760 (RealTime::frame2RealTime |
550 | 761 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) - |
551 if (i == selections.end()) { | 762 m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
552 --i; | 763 sourceRate)); |
553 if (i->getEndFrame() + latency < f) { | 764 |
554 // std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; | 765 m_rangeStarts.push_back(start); |
555 | 766 m_rangeDurations.push_back(duration); |
556 if (!looping && (framePlaying > rangeEnd)) { | 767 } |
557 // std::cout << "STOPPING" << std::endl; | 768 } else { |
558 stop(); | 769 m_rangeStarts.push_back(RealTime::zeroTime); |
559 return rangeEnd; | 770 m_rangeDurations.push_back(end); |
560 } else { | 771 } |
561 return framePlaying; | 772 |
562 } | 773 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
563 } else { | 774 std::cerr << "Now have " << m_rangeStarts.size() << " play ranges" << std::endl; |
564 // std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | 775 #endif |
565 latency -= (f - i->getEndFrame()); | |
566 f = i->getEndFrame(); | |
567 } | |
568 } | |
569 | |
570 // std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | |
571 | |
572 while (latency > 0) { | |
573 size_t offset = f - i->getStartFrame(); | |
574 if (offset >= latency) { | |
575 if (f > latency) { | |
576 framePlaying = f - latency; | |
577 } else { | |
578 framePlaying = 0; | |
579 } | |
580 break; | |
581 } else { | |
582 if (i == selections.begin()) { | |
583 if (looping) { | |
584 i = selections.end(); | |
585 } | |
586 } | |
587 latency -= offset; | |
588 --i; | |
589 f = i->getEndFrame(); | |
590 } | |
591 } | |
592 | |
593 return framePlaying; | |
594 } | 776 } |
595 | 777 |
596 void | 778 void |
597 AudioCallbackPlaySource::setOutputLevels(float left, float right) | 779 AudioCallbackPlaySource::setOutputLevels(float left, float right) |
598 { | 780 { |
738 { | 920 { |
739 return m_sourceSampleRate; | 921 return m_sourceSampleRate; |
740 } | 922 } |
741 | 923 |
742 void | 924 void |
743 AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono) | 925 AudioCallbackPlaySource::setTimeStretch(float factor) |
744 { | 926 { |
745 // Avoid locks -- create, assign, mark old one for scavenging | 927 m_stretchRatio = factor; |
746 // later (as a call to getSourceSamples may still be using it) | 928 |
747 | 929 if (m_timeStretcher || (factor == 1.f)) { |
748 PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher; | 930 // stretch ratio will be set in next process call if appropriate |
749 | 931 return; |
750 size_t channels = getTargetChannelCount(); | |
751 if (mono) channels = 1; | |
752 | |
753 if (existingStretcher && | |
754 existingStretcher->getRatio() == factor && | |
755 existingStretcher->getSharpening() == sharpen && | |
756 existingStretcher->getChannelCount() == channels) { | |
757 return; | |
758 } | |
759 | |
760 if (factor != 1) { | |
761 | |
762 if (existingStretcher && | |
763 existingStretcher->getSharpening() == sharpen && | |
764 existingStretcher->getChannelCount() == channels) { | |
765 existingStretcher->setRatio(factor); | |
766 return; | |
767 } | |
768 | |
769 PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher | |
770 (getTargetSampleRate(), | |
771 channels, | |
772 factor, | |
773 sharpen, | |
774 getTargetBlockSize()); | |
775 | |
776 m_timeStretcher = newStretcher; | |
777 | |
778 } else { | 932 } else { |
779 m_timeStretcher = 0; | 933 m_stretcherInputCount = getTargetChannelCount(); |
780 } | 934 RubberBandStretcher *stretcher = new RubberBandStretcher |
781 | 935 (getTargetSampleRate(), |
782 if (existingStretcher) { | 936 m_stretcherInputCount, |
783 m_timeStretcherScavenger.claim(existingStretcher); | 937 RubberBandStretcher::OptionProcessRealTime, |
938 factor); | |
939 m_stretcherInputs = new float *[m_stretcherInputCount]; | |
940 m_stretcherInputSizes = new size_t[m_stretcherInputCount]; | |
941 for (size_t c = 0; c < m_stretcherInputCount; ++c) { | |
942 m_stretcherInputSizes[c] = 16384; | |
943 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
944 } | |
945 m_timeStretcher = stretcher; | |
946 return; | |
784 } | 947 } |
785 } | 948 } |
786 | 949 |
787 size_t | 950 size_t |
788 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) | 951 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) |
823 } | 986 } |
824 } | 987 } |
825 | 988 |
826 if (count == 0) return 0; | 989 if (count == 0) return 0; |
827 | 990 |
828 PhaseVocoderTimeStretcher *ts = m_timeStretcher; | 991 RubberBandStretcher *ts = m_timeStretcher; |
829 | 992 float ratio = ts ? ts->getTimeRatio() : 1.f; |
830 if (!ts || ts->getRatio() == 1) { | 993 |
994 if (ratio != m_stretchRatio) { | |
995 if (!ts) { | |
996 std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << std::endl; | |
997 m_stretchRatio = 1.f; | |
998 } else { | |
999 ts->setTimeRatio(m_stretchRatio); | |
1000 } | |
1001 } | |
1002 | |
1003 if (m_target) { | |
1004 m_lastRetrievedBlockSize = count; | |
1005 m_lastRetrievalTimestamp = m_target->getCurrentTime(); | |
1006 } | |
1007 | |
1008 if (!ts || ratio == 1.f) { | |
831 | 1009 |
832 size_t got = 0; | 1010 size_t got = 0; |
833 | 1011 |
834 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 1012 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { |
835 | 1013 |
857 } | 1035 } |
858 | 1036 |
859 applyAuditioningEffect(count, buffer); | 1037 applyAuditioningEffect(count, buffer); |
860 | 1038 |
861 m_condition.wakeAll(); | 1039 m_condition.wakeAll(); |
1040 | |
862 return got; | 1041 return got; |
863 } | 1042 } |
864 | 1043 |
865 float ratio = ts->getRatio(); | |
866 | |
867 // std::cout << "ratio = " << ratio << std::endl; | |
868 | |
869 size_t channels = getTargetChannelCount(); | 1044 size_t channels = getTargetChannelCount(); |
870 bool mix = (channels > 1 && ts->getChannelCount() == 1); | |
871 | |
872 size_t available; | 1045 size_t available; |
873 | |
874 int warned = 0; | 1046 int warned = 0; |
875 | 1047 size_t fedToStretcher = 0; |
876 // We want output blocks of e.g. 1024 (probably fixed, certainly | 1048 |
877 // bounded). We can provide input blocks of any size (unbounded) | 1049 // The input block for a given output is approx output / ratio, |
878 // at the timestretcher's request. The input block for a given | 1050 // but we can't predict it exactly, for an adaptive timestretcher. |
879 // output is approx output / ratio, but we can't predict it | 1051 |
880 // exactly, for an adaptive timestretcher. The stretcher will | 1052 while ((available = ts->available()) < count) { |
881 // need some additional buffer space. See the time stretcher code | |
882 // and comments. | |
883 | |
884 while ((available = ts->getAvailableOutputSamples()) < count) { | |
885 | 1053 |
886 size_t reqd = lrintf((count - available) / ratio); | 1054 size_t reqd = lrintf((count - available) / ratio); |
887 reqd = std::max(reqd, ts->getRequiredInputSamples()); | 1055 reqd = std::max(reqd, ts->getSamplesRequired()); |
888 if (reqd == 0) reqd = 1; | 1056 if (reqd == 0) reqd = 1; |
889 | 1057 |
890 float *ib[channels]; | |
891 | |
892 size_t got = reqd; | 1058 size_t got = reqd; |
893 | 1059 |
894 if (mix) { | 1060 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
895 for (size_t c = 0; c < channels; ++c) { | 1061 std::cerr << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << std::endl; |
896 if (c == 0) ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 1062 #endif |
897 else ib[c] = 0; | 1063 |
898 RingBuffer<float> *rb = getReadRingBuffer(c); | 1064 for (size_t c = 0; c < channels; ++c) { |
899 if (rb) { | 1065 if (c >= m_stretcherInputCount) continue; |
900 size_t gotHere; | 1066 if (reqd > m_stretcherInputSizes[c]) { |
901 if (c > 0) gotHere = rb->readAdding(ib[0], got); | 1067 if (c == 0) { |
902 else gotHere = rb->read(ib[0], got); | 1068 std::cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << std::endl; |
903 if (gotHere < got) got = gotHere; | |
904 } | 1069 } |
1070 delete[] m_stretcherInputs[c]; | |
1071 m_stretcherInputSizes[c] = reqd * 2; | |
1072 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
905 } | 1073 } |
906 } else { | 1074 } |
907 for (size_t c = 0; c < channels; ++c) { | 1075 |
908 ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 1076 for (size_t c = 0; c < channels; ++c) { |
909 RingBuffer<float> *rb = getReadRingBuffer(c); | 1077 if (c >= m_stretcherInputCount) continue; |
910 if (rb) { | 1078 RingBuffer<float> *rb = getReadRingBuffer(c); |
911 size_t gotHere = rb->read(ib[c], got); | 1079 if (rb) { |
912 if (gotHere < got) got = gotHere; | 1080 size_t gotHere = rb->read(m_stretcherInputs[c], got); |
1081 if (gotHere < got) got = gotHere; | |
1082 | |
1083 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
1084 if (c == 0) { | |
1085 std::cerr << "feeding stretcher: got " << gotHere | |
1086 << ", " << rb->getReadSpace() << " remain" << std::endl; | |
913 } | 1087 } |
1088 #endif | |
1089 | |
1090 } else { | |
1091 std::cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << std::endl; | |
914 } | 1092 } |
915 } | 1093 } |
916 | 1094 |
917 if (got < reqd) { | 1095 if (got < reqd) { |
918 std::cerr << "WARNING: Read underrun in playback (" | 1096 std::cerr << "WARNING: Read underrun in playback (" |
919 << got << " < " << reqd << ")" << std::endl; | 1097 << got << " < " << reqd << ")" << std::endl; |
920 } | 1098 } |
921 | 1099 |
922 ts->putInput(ib, got); | 1100 ts->process(m_stretcherInputs, got, false); |
923 | 1101 |
924 for (size_t c = 0; c < channels; ++c) { | 1102 fedToStretcher += got; |
925 delete[] ib[c]; | |
926 } | |
927 | 1103 |
928 if (got == 0) break; | 1104 if (got == 0) break; |
929 | 1105 |
930 if (ts->getAvailableOutputSamples() == available) { | 1106 if (ts->available() == available) { |
931 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; | 1107 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; |
932 if (++warned == 5) break; | 1108 if (++warned == 5) break; |
933 } | 1109 } |
934 } | 1110 } |
935 | 1111 |
936 ts->getOutput(buffer, count); | 1112 ts->retrieve(buffer, count); |
937 | |
938 if (mix) { | |
939 for (size_t c = 1; c < channels; ++c) { | |
940 for (size_t i = 0; i < count; ++i) { | |
941 buffer[c][i] = buffer[0][i] / channels; | |
942 } | |
943 } | |
944 for (size_t i = 0; i < count; ++i) { | |
945 buffer[0][i] /= channels; | |
946 } | |
947 } | |
948 | 1113 |
949 applyAuditioningEffect(count, buffer); | 1114 applyAuditioningEffect(count, buffer); |
950 | 1115 |
951 m_condition.wakeAll(); | 1116 m_condition.wakeAll(); |
952 | 1117 |
1118 data.src_ratio = ratio; | 1283 data.src_ratio = ratio; |
1119 data.end_of_input = 0; | 1284 data.end_of_input = 0; |
1120 | 1285 |
1121 int err = 0; | 1286 int err = 0; |
1122 | 1287 |
1123 if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) { | 1288 if (m_timeStretcher && m_timeStretcher->getTimeRatio() < 0.4) { |
1124 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1289 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1125 std::cout << "Using crappy converter" << std::endl; | 1290 std::cout << "Using crappy converter" << std::endl; |
1126 #endif | 1291 #endif |
1127 err = src_process(m_crapConverter, &data); | 1292 err = src_process(m_crapConverter, &data); |
1128 } else { | 1293 } else { |
1157 | 1322 |
1158 } else { | 1323 } else { |
1159 | 1324 |
1160 // space must be a multiple of generatorBlockSize | 1325 // space must be a multiple of generatorBlockSize |
1161 space = (space / generatorBlockSize) * generatorBlockSize; | 1326 space = (space / generatorBlockSize) * generatorBlockSize; |
1162 if (space == 0) return false; | 1327 if (space == 0) { |
1328 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
1329 std::cout << "requested fill is less than generator block size of " | |
1330 << generatorBlockSize << ", leaving it" << std::endl; | |
1331 #endif | |
1332 return false; | |
1333 } | |
1163 | 1334 |
1164 if (tmpSize < channels * space) { | 1335 if (tmpSize < channels * space) { |
1165 delete[] tmp; | 1336 delete[] tmp; |
1166 tmp = new float[channels * space]; | 1337 tmp = new float[channels * space]; |
1167 tmpSize = channels * space; | 1338 tmpSize = channels * space; |
1243 selectionSize = 0; | 1414 selectionSize = 0; |
1244 | 1415 |
1245 size_t fadeIn = 0, fadeOut = 0; | 1416 size_t fadeIn = 0, fadeOut = 0; |
1246 | 1417 |
1247 if (constrained) { | 1418 if (constrained) { |
1419 | |
1420 size_t rChunkStart = | |
1421 m_viewManager->alignPlaybackFrameToReference(chunkStart); | |
1248 | 1422 |
1249 Selection selection = | 1423 Selection selection = |
1250 m_viewManager->getContainingSelection(chunkStart, true); | 1424 m_viewManager->getContainingSelection(rChunkStart, true); |
1251 | 1425 |
1252 if (selection.isEmpty()) { | 1426 if (selection.isEmpty()) { |
1253 if (looping) { | 1427 if (looping) { |
1254 selection = *m_viewManager->getSelections().begin(); | 1428 selection = *m_viewManager->getSelections().begin(); |
1255 chunkStart = selection.getStartFrame(); | 1429 chunkStart = m_viewManager->alignReferenceToPlaybackFrame |
1430 (selection.getStartFrame()); | |
1256 fadeIn = 50; | 1431 fadeIn = 50; |
1257 } | 1432 } |
1258 } | 1433 } |
1259 | 1434 |
1260 if (selection.isEmpty()) { | 1435 if (selection.isEmpty()) { |
1262 chunkSize = 0; | 1437 chunkSize = 0; |
1263 nextChunkStart = chunkStart; | 1438 nextChunkStart = chunkStart; |
1264 | 1439 |
1265 } else { | 1440 } else { |
1266 | 1441 |
1267 selectionSize = | 1442 size_t sf = m_viewManager->alignReferenceToPlaybackFrame |
1268 selection.getEndFrame() - | 1443 (selection.getStartFrame()); |
1269 selection.getStartFrame(); | 1444 size_t ef = m_viewManager->alignReferenceToPlaybackFrame |
1270 | 1445 (selection.getEndFrame()); |
1271 if (chunkStart < selection.getStartFrame()) { | 1446 |
1272 chunkStart = selection.getStartFrame(); | 1447 selectionSize = ef - sf; |
1448 | |
1449 if (chunkStart < sf) { | |
1450 chunkStart = sf; | |
1273 fadeIn = 50; | 1451 fadeIn = 50; |
1274 } | 1452 } |
1275 | 1453 |
1276 nextChunkStart = chunkStart + chunkSize; | 1454 nextChunkStart = chunkStart + chunkSize; |
1277 | 1455 |
1278 if (nextChunkStart >= selection.getEndFrame()) { | 1456 if (nextChunkStart >= ef) { |
1279 nextChunkStart = selection.getEndFrame(); | 1457 nextChunkStart = ef; |
1280 fadeOut = 50; | 1458 fadeOut = 50; |
1281 } | 1459 } |
1282 | 1460 |
1283 chunkSize = nextChunkStart - chunkStart; | 1461 chunkSize = nextChunkStart - chunkStart; |
1284 } | 1462 } |
1436 while (!s.m_exiting) { | 1614 while (!s.m_exiting) { |
1437 | 1615 |
1438 s.unifyRingBuffers(); | 1616 s.unifyRingBuffers(); |
1439 s.m_bufferScavenger.scavenge(); | 1617 s.m_bufferScavenger.scavenge(); |
1440 s.m_pluginScavenger.scavenge(); | 1618 s.m_pluginScavenger.scavenge(); |
1441 s.m_timeStretcherScavenger.scavenge(); | |
1442 | 1619 |
1443 if (work && s.m_playing && s.getSourceSampleRate()) { | 1620 if (work && s.m_playing && s.getSourceSampleRate()) { |
1444 | 1621 |
1445 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1622 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1446 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; | 1623 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; |