Mercurial > hg > svapp
comparison audioio/AudioCallbackPlaySource.cpp @ 91:9fc4b256c283
* PortAudio driver: do not specify frames per buffer, let PA decide
* Remove old non-RubberBand time stretcher -- it doesn't work with varying
buffer sizes such as the PA driver may now be using
* Rewrite getCurrentPlayingFrame for greater correctness when using long
buffer sizes (interpolating according to audio stream timestamp)
* Several changes to make the timestretch management RT safe(r)
author | Chris Cannam |
---|---|
date | Fri, 08 Feb 2008 17:51:15 +0000 |
parents | ae2627ac7db2 |
children | 792bca285459 |
comparison
equal
deleted
inserted
replaced
90:db267a315058 | 91:9fc4b256c283 |
---|---|
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 | 28 |
29 #ifdef HAVE_RUBBERBAND | 29 #include "AudioCallbackPlayTarget.h" |
30 | |
30 #include <rubberband/RubberBandStretcher.h> | 31 #include <rubberband/RubberBandStretcher.h> |
31 using namespace RubberBand; | 32 using namespace RubberBand; |
32 #else | |
33 #include "PhaseVocoderTimeStretcher.h" | |
34 #endif | |
35 | 33 |
36 #include <iostream> | 34 #include <iostream> |
37 #include <cassert> | 35 #include <cassert> |
38 | 36 |
39 //#define DEBUG_AUDIO_PLAY_SOURCE 1 | 37 //#define DEBUG_AUDIO_PLAY_SOURCE 1 |
54 m_sourceChannelCount(0), | 52 m_sourceChannelCount(0), |
55 m_blockSize(1024), | 53 m_blockSize(1024), |
56 m_sourceSampleRate(0), | 54 m_sourceSampleRate(0), |
57 m_targetSampleRate(0), | 55 m_targetSampleRate(0), |
58 m_playLatency(0), | 56 m_playLatency(0), |
57 m_target(0), | |
58 m_lastRetrievalTimestamp(0.0), | |
59 m_lastRetrievedBlockSize(0), | |
59 m_playing(false), | 60 m_playing(false), |
60 m_exiting(false), | 61 m_exiting(false), |
61 m_lastModelEndFrame(0), | 62 m_lastModelEndFrame(0), |
62 m_outputLeft(0.0), | 63 m_outputLeft(0.0), |
63 m_outputRight(0.0), | 64 m_outputRight(0.0), |
64 m_auditioningPlugin(0), | 65 m_auditioningPlugin(0), |
65 m_auditioningPluginBypassed(false), | 66 m_auditioningPluginBypassed(false), |
66 m_timeStretcher(0), | 67 m_timeStretcher(0), |
68 m_stretchRatio(1.0), | |
69 m_stretcherInputCount(0), | |
70 m_stretcherInputs(0), | |
71 m_stretcherInputSizes(0), | |
67 m_fillThread(0), | 72 m_fillThread(0), |
68 m_converter(0), | 73 m_converter(0), |
69 m_crapConverter(0), | 74 m_crapConverter(0), |
70 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) | 75 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) |
71 { | 76 { |
105 | 110 |
106 delete m_writeBuffers; | 111 delete m_writeBuffers; |
107 | 112 |
108 delete m_audioGenerator; | 113 delete m_audioGenerator; |
109 | 114 |
115 for (size_t i = 0; i < m_stretcherInputCount; ++i) { | |
116 delete[] m_stretcherInputs[i]; | |
117 } | |
118 delete[] m_stretcherInputSizes; | |
119 delete[] m_stretcherInputs; | |
120 | |
110 m_bufferScavenger.scavenge(true); | 121 m_bufferScavenger.scavenge(true); |
111 m_pluginScavenger.scavenge(true); | 122 m_pluginScavenger.scavenge(true); |
112 #ifndef HAVE_RUBBERBAND | |
113 m_timeStretcherScavenger.scavenge(true); | |
114 #endif | |
115 } | 123 } |
116 | 124 |
117 void | 125 void |
118 AudioCallbackPlaySource::addModel(Model *model) | 126 AudioCallbackPlaySource::addModel(Model *model) |
119 { | 127 { |
369 // The fill thread will automatically empty its buffers before | 377 // The fill thread will automatically empty its buffers before |
370 // starting again if we have not so far been playing, but not if | 378 // starting again if we have not so far been playing, but not if |
371 // we're just re-seeking. | 379 // we're just re-seeking. |
372 | 380 |
373 m_mutex.lock(); | 381 m_mutex.lock(); |
382 if (m_timeStretcher) { | |
383 m_timeStretcher->reset(); | |
384 } | |
374 if (m_playing) { | 385 if (m_playing) { |
375 m_readBufferFill = m_writeBufferFill = startFrame; | 386 m_readBufferFill = m_writeBufferFill = startFrame; |
376 if (m_readBuffers) { | 387 if (m_readBuffers) { |
377 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 388 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
378 RingBuffer<float> *rb = getReadRingBuffer(c); | 389 RingBuffer<float> *rb = getReadRingBuffer(c); |
389 m_mutex.unlock(); | 400 m_mutex.unlock(); |
390 | 401 |
391 m_audioGenerator->reset(); | 402 m_audioGenerator->reset(); |
392 | 403 |
393 bool changed = !m_playing; | 404 bool changed = !m_playing; |
405 m_lastRetrievalTimestamp = 0; | |
394 m_playing = true; | 406 m_playing = true; |
395 m_condition.wakeAll(); | 407 m_condition.wakeAll(); |
396 if (changed) emit playStatusChanged(m_playing); | 408 if (changed) emit playStatusChanged(m_playing); |
397 } | 409 } |
398 | 410 |
400 AudioCallbackPlaySource::stop() | 412 AudioCallbackPlaySource::stop() |
401 { | 413 { |
402 bool changed = m_playing; | 414 bool changed = m_playing; |
403 m_playing = false; | 415 m_playing = false; |
404 m_condition.wakeAll(); | 416 m_condition.wakeAll(); |
417 m_lastRetrievalTimestamp = 0; | |
405 if (changed) emit playStatusChanged(m_playing); | 418 if (changed) emit playStatusChanged(m_playing); |
406 } | 419 } |
407 | 420 |
408 void | 421 void |
409 AudioCallbackPlaySource::selectionChanged() | 422 AudioCallbackPlaySource::selectionChanged() |
450 emit audioOverloadPluginDisabled(); | 463 emit audioOverloadPluginDisabled(); |
451 } | 464 } |
452 } | 465 } |
453 | 466 |
454 void | 467 void |
455 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 468 AudioCallbackPlaySource::setTarget(AudioCallbackPlayTarget *target, size_t size) |
456 { | 469 { |
470 m_target = target; | |
457 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 471 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; |
458 assert(size < m_ringBufferSize); | 472 assert(size < m_ringBufferSize); |
459 m_blockSize = size; | 473 m_blockSize = size; |
460 } | 474 } |
461 | 475 |
479 } | 493 } |
480 | 494 |
481 size_t | 495 size_t |
482 AudioCallbackPlaySource::getCurrentPlayingFrame() | 496 AudioCallbackPlaySource::getCurrentPlayingFrame() |
483 { | 497 { |
498 // This method attempts to estimate which audio sample frame is | |
499 // "currently coming through the speakers". | |
500 | |
484 bool resample = false; | 501 bool resample = false; |
485 double ratio = 1.0; | 502 double resampleRatio = 1.0; |
486 | 503 |
487 if (getSourceSampleRate() != getTargetSampleRate()) { | 504 // We resample when filling the ring buffer, and time-stretch when |
488 resample = true; | 505 // draining it. The buffer contains data at the "target rate" and |
489 ratio = double(getSourceSampleRate()) / double(getTargetSampleRate()); | 506 // the latency provided by the target is also at the target rate. |
490 } | 507 // Because of the multiple rates involved, we do the actual |
491 | 508 // calculation using RealTime instead. |
492 size_t readSpace = 0; | 509 |
510 size_t sourceRate = getSourceSampleRate(); | |
511 size_t targetRate = getTargetSampleRate(); | |
512 | |
513 if (sourceRate == 0 || targetRate == 0) return 0; | |
514 | |
515 size_t inbuffer = 0; // at target rate | |
516 | |
493 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 517 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
494 RingBuffer<float> *rb = getReadRingBuffer(c); | 518 RingBuffer<float> *rb = getReadRingBuffer(c); |
495 if (rb) { | 519 if (rb) { |
496 size_t spaceHere = rb->getReadSpace(); | 520 size_t here = rb->getReadSpace(); |
497 if (c == 0 || spaceHere < readSpace) readSpace = spaceHere; | 521 if (c == 0 || here < inbuffer) inbuffer = here; |
498 } | 522 } |
499 } | 523 } |
500 | 524 |
501 if (resample) { | 525 size_t readBufferFill = m_readBufferFill; |
502 readSpace = size_t(readSpace * ratio + 0.1); | 526 size_t lastRetrievedBlockSize = m_lastRetrievedBlockSize; |
503 } | 527 double lastRetrievalTimestamp = m_lastRetrievalTimestamp; |
504 | 528 double currentTime = 0.0; |
505 size_t latency = m_playLatency; | 529 if (m_target) currentTime = m_target->getCurrentTime(); |
506 if (resample) latency = size_t(m_playLatency * ratio + 0.1); | 530 |
507 | 531 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); |
508 #ifdef HAVE_RUBBERBAND | 532 |
533 size_t latency = m_playLatency; // at target rate | |
534 RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); | |
535 | |
536 size_t stretchlat = 0; | |
537 double timeRatio = 1.0; | |
538 | |
509 if (m_timeStretcher) { | 539 if (m_timeStretcher) { |
510 latency += m_timeStretcher->getLatency(); | 540 stretchlat = m_timeStretcher->getLatency(); |
511 } | 541 timeRatio = m_timeStretcher->getTimeRatio(); |
512 #else | 542 } |
513 PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher; | 543 |
514 if (timeStretcher) { | 544 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate); |
515 latency += timeStretcher->getProcessingLatency(); | 545 |
516 } | 546 // When the target has just requested a block from us, the last |
517 #endif | 547 // sample it obtained was our buffer fill frame count minus the |
518 | 548 // amount of read space (converted back to source sample rate) |
519 latency += readSpace; | 549 // remaining now. That sample is not expected to be played until |
520 size_t bufferedFrame = m_readBufferFill; | 550 // the target's play latency has elapsed. By the time the |
551 // following block is requested, that sample will be at the | |
552 // target's play latency minus the last requested block size away | |
553 // from being played. | |
554 | |
555 RealTime sincerequest_t = RealTime::zeroTime; | |
556 RealTime lastretrieved_t = RealTime::zeroTime; | |
557 | |
558 if (m_target && lastRetrievalTimestamp != 0.0) { | |
559 | |
560 lastretrieved_t = RealTime::frame2RealTime | |
561 (lastRetrievedBlockSize, targetRate); | |
562 | |
563 // calculate number of frames at target rate that have elapsed | |
564 // since the end of the last call to getSourceSamples | |
565 | |
566 double elapsed = currentTime - lastRetrievalTimestamp; | |
567 | |
568 if (elapsed > 0.0) { | |
569 sincerequest_t = RealTime::fromSeconds(elapsed); | |
570 } | |
571 | |
572 } else { | |
573 | |
574 lastretrieved_t = RealTime::frame2RealTime | |
575 (getTargetBlockSize(), targetRate); | |
576 } | |
577 | |
578 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate); | |
579 | |
580 if (timeRatio != 1.0) { | |
581 lastretrieved_t = lastretrieved_t / timeRatio; | |
582 sincerequest_t = sincerequest_t / timeRatio; | |
583 } | |
521 | 584 |
522 bool looping = m_viewManager->getPlayLoopMode(); | 585 bool looping = m_viewManager->getPlayLoopMode(); |
523 bool constrained = (m_viewManager->getPlaySelectionMode() && | 586 bool constrained = (m_viewManager->getPlaySelectionMode() && |
524 !m_viewManager->getSelections().empty()); | 587 !m_viewManager->getSelections().empty()); |
525 | 588 |
526 size_t framePlaying = bufferedFrame; | 589 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
527 | 590 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; |
528 if (looping && !constrained) { | 591 #endif |
529 while (framePlaying < latency) framePlaying += m_lastModelEndFrame; | 592 |
530 } | 593 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); |
531 | |
532 if (framePlaying > latency) framePlaying -= latency; | |
533 else framePlaying = 0; | |
534 | |
535 // std::cerr << "framePlaying = " << framePlaying << " -> reference "; | |
536 | |
537 framePlaying = m_viewManager->alignPlaybackFrameToReference(framePlaying); | |
538 | |
539 // std::cerr << framePlaying << std::endl; | |
540 | |
541 if (!constrained) { | |
542 if (!looping && framePlaying > m_lastModelEndFrame) { | |
543 framePlaying = m_lastModelEndFrame; | |
544 stop(); | |
545 } | |
546 return framePlaying; | |
547 } | |
548 | |
549 bufferedFrame = m_viewManager->alignPlaybackFrameToReference(bufferedFrame); | |
550 | 594 |
551 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 595 MultiSelection::SelectionList selections = m_viewManager->getSelections(); |
552 MultiSelection::SelectionList::const_iterator i; | 596 MultiSelection::SelectionList::const_iterator i; |
553 | 597 |
554 // i = selections.begin(); | 598 // these could be cached from one call to the next, if the |
555 // size_t rangeStart = i->getStartFrame(); | 599 // selection has not changed... but some of the work would still |
556 | 600 // need to be done because the playback model may have changed |
557 i = selections.end(); | 601 |
558 --i; | 602 std::vector<RealTime> rangeStarts; |
559 size_t rangeEnd = i->getEndFrame(); | 603 std::vector<RealTime> rangeDurations; |
560 | 604 |
561 for (i = selections.begin(); i != selections.end(); ++i) { | 605 int inRange = 0; |
562 if (i->contains(bufferedFrame)) break; | 606 int index = 0; |
563 } | 607 |
564 | 608 if (constrained) { |
565 size_t f = bufferedFrame; | 609 |
566 | 610 for (i = selections.begin(); i != selections.end(); ++i) { |
567 // std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; | 611 |
568 | 612 RealTime start = |
569 if (i == selections.end()) { | 613 (RealTime::frame2RealTime |
570 --i; | 614 (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
571 if (i->getEndFrame() + latency < f) { | 615 sourceRate)); |
572 // std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; | 616 RealTime duration = |
573 | 617 (RealTime::frame2RealTime |
574 if (!looping && (framePlaying > rangeEnd)) { | 618 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) - |
575 // std::cout << "STOPPING" << std::endl; | 619 m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
576 stop(); | 620 sourceRate)); |
577 return rangeEnd; | 621 |
578 } else { | 622 rangeStarts.push_back(start); |
579 return framePlaying; | 623 rangeDurations.push_back(duration); |
580 } | 624 |
581 } else { | 625 if (bufferedto_t >= start) { |
582 // std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | 626 inRange = index; |
583 latency -= (f - i->getEndFrame()); | 627 } |
584 f = i->getEndFrame(); | 628 |
585 } | 629 ++index; |
586 } | 630 } |
587 | 631 } |
588 // std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | 632 |
589 | 633 if (rangeStarts.empty()) { |
590 while (latency > 0) { | 634 rangeStarts.push_back(RealTime::zeroTime); |
591 size_t offset = f - i->getStartFrame(); | 635 rangeDurations.push_back(end); |
592 if (offset >= latency) { | 636 } |
593 if (f > latency) { | 637 |
594 framePlaying = f - latency; | 638 if (inRange >= rangeStarts.size()) inRange = rangeStarts.size()-1; |
595 } else { | 639 |
596 framePlaying = 0; | 640 RealTime playing_t = bufferedto_t - rangeStarts[inRange]; |
597 } | 641 |
598 break; | 642 playing_t = playing_t |
599 } else { | 643 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t |
600 if (i == selections.begin()) { | 644 + sincerequest_t; |
601 if (looping) { | 645 |
602 i = selections.end(); | 646 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
603 } | 647 std::cerr << "playing_t as offset into range " << inRange << " (with start = " << rangeStarts[inRange] << ") = " << playing_t << std::endl; |
604 } | 648 #endif |
605 latency -= offset; | 649 |
606 --i; | 650 while (playing_t < RealTime::zeroTime) { |
607 f = i->getEndFrame(); | 651 |
608 } | 652 if (inRange == 0) { |
609 } | 653 if (looping) { |
610 | 654 inRange = rangeStarts.size() - 1; |
611 return framePlaying; | 655 } else { |
656 break; | |
657 } | |
658 } else { | |
659 --inRange; | |
660 } | |
661 | |
662 playing_t = playing_t + rangeDurations[inRange]; | |
663 } | |
664 | |
665 playing_t = playing_t + rangeStarts[inRange]; | |
666 | |
667 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
668 std::cerr << " playing time: " << playing_t << std::endl; | |
669 #endif | |
670 | |
671 if (!looping) { | |
672 if (inRange == rangeStarts.size()-1 && | |
673 playing_t >= rangeStarts[inRange] + rangeDurations[inRange]) { | |
674 stop(); | |
675 } | |
676 } | |
677 | |
678 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | |
679 | |
680 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | |
681 return m_viewManager->alignPlaybackFrameToReference(frame); | |
612 } | 682 } |
613 | 683 |
614 void | 684 void |
615 AudioCallbackPlaySource::setOutputLevels(float left, float right) | 685 AudioCallbackPlaySource::setOutputLevels(float left, float right) |
616 { | 686 { |
756 { | 826 { |
757 return m_sourceSampleRate; | 827 return m_sourceSampleRate; |
758 } | 828 } |
759 | 829 |
760 void | 830 void |
761 AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono) | 831 AudioCallbackPlaySource::setTimeStretch(float factor) |
762 { | 832 { |
763 #ifdef HAVE_RUBBERBAND | 833 m_stretchRatio = factor; |
764 if (m_timeStretcher) { | 834 |
765 m_timeStretchRatioMutex.lock(); | 835 if (m_timeStretcher || (factor == 1.f)) { |
766 m_timeStretcher->setTimeRatio(factor); | 836 // stretch ratio will be set in next process call if appropriate |
767 m_timeStretchRatioMutex.unlock(); | |
768 return; | 837 return; |
769 } else { | 838 } else { |
839 m_stretcherInputCount = getTargetChannelCount(); | |
770 RubberBandStretcher *stretcher = new RubberBandStretcher | 840 RubberBandStretcher *stretcher = new RubberBandStretcher |
771 (getTargetSampleRate(), | 841 (getTargetSampleRate(), |
772 getTargetChannelCount(), | 842 m_stretcherInputCount, |
773 RubberBandStretcher::OptionProcessRealTime, | 843 RubberBandStretcher::OptionProcessRealTime, |
774 factor); | 844 factor); |
845 m_stretcherInputs = new float *[m_stretcherInputCount]; | |
846 m_stretcherInputSizes = new size_t[m_stretcherInputCount]; | |
847 for (size_t c = 0; c < m_stretcherInputCount; ++c) { | |
848 m_stretcherInputSizes[c] = 16384; | |
849 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
850 } | |
775 m_timeStretcher = stretcher; | 851 m_timeStretcher = stretcher; |
776 return; | 852 return; |
777 } | 853 } |
778 #else | |
779 // Avoid locks -- create, assign, mark old one for scavenging | |
780 // later (as a call to getSourceSamples may still be using it) | |
781 | |
782 PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher; | |
783 | |
784 size_t channels = getTargetChannelCount(); | |
785 if (mono) channels = 1; | |
786 | |
787 if (existingStretcher && | |
788 existingStretcher->getRatio() == factor && | |
789 existingStretcher->getSharpening() == sharpen && | |
790 existingStretcher->getChannelCount() == channels) { | |
791 return; | |
792 } | |
793 | |
794 if (factor != 1) { | |
795 | |
796 if (existingStretcher && | |
797 existingStretcher->getSharpening() == sharpen && | |
798 existingStretcher->getChannelCount() == channels) { | |
799 existingStretcher->setRatio(factor); | |
800 return; | |
801 } | |
802 | |
803 PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher | |
804 (getTargetSampleRate(), | |
805 channels, | |
806 factor, | |
807 sharpen, | |
808 getTargetBlockSize()); | |
809 | |
810 m_timeStretcher = newStretcher; | |
811 | |
812 } else { | |
813 m_timeStretcher = 0; | |
814 } | |
815 | |
816 if (existingStretcher) { | |
817 m_timeStretcherScavenger.claim(existingStretcher); | |
818 } | |
819 #endif | |
820 } | 854 } |
821 | 855 |
822 size_t | 856 size_t |
823 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) | 857 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) |
824 { | 858 { |
858 } | 892 } |
859 } | 893 } |
860 | 894 |
861 if (count == 0) return 0; | 895 if (count == 0) return 0; |
862 | 896 |
863 #ifdef HAVE_RUBBERBAND | |
864 RubberBandStretcher *ts = m_timeStretcher; | 897 RubberBandStretcher *ts = m_timeStretcher; |
865 float ratio = ts ? ts->getTimeRatio() : 1.f; | 898 float ratio = ts ? ts->getTimeRatio() : 1.f; |
866 #else | 899 |
867 PhaseVocoderTimeStretcher *ts = m_timeStretcher; | 900 if (ratio != m_stretchRatio) { |
868 float ratio = ts ? ts->getRatio() : 1.f; | 901 if (!ts) { |
869 #endif | 902 std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << std::endl; |
903 m_stretchRatio = 1.f; | |
904 } else { | |
905 ts->setTimeRatio(m_stretchRatio); | |
906 } | |
907 } | |
908 | |
909 if (m_target) { | |
910 m_lastRetrievedBlockSize = count; | |
911 m_lastRetrievalTimestamp = m_target->getCurrentTime(); | |
912 } | |
870 | 913 |
871 if (!ts || ratio == 1.f) { | 914 if (!ts || ratio == 1.f) { |
872 | 915 |
873 size_t got = 0; | 916 size_t got = 0; |
874 | 917 |
898 } | 941 } |
899 | 942 |
900 applyAuditioningEffect(count, buffer); | 943 applyAuditioningEffect(count, buffer); |
901 | 944 |
902 m_condition.wakeAll(); | 945 m_condition.wakeAll(); |
946 | |
903 return got; | 947 return got; |
904 } | 948 } |
905 | 949 |
906 size_t channels = getTargetChannelCount(); | 950 size_t channels = getTargetChannelCount(); |
907 | |
908 #ifdef HAVE_RUBBERBAND | |
909 bool mix = false; | |
910 #else | |
911 bool mix = (channels > 1 && ts->getChannelCount() == 1); | |
912 #endif | |
913 | |
914 size_t available; | 951 size_t available; |
915 | |
916 int warned = 0; | 952 int warned = 0; |
917 | 953 size_t fedToStretcher = 0; |
918 // We want output blocks of e.g. 1024 (probably fixed, certainly | 954 |
919 // bounded). We can provide input blocks of any size (unbounded) | 955 // The input block for a given output is approx output / ratio, |
920 // at the timestretcher's request. The input block for a given | 956 // but we can't predict it exactly, for an adaptive timestretcher. |
921 // output is approx output / ratio, but we can't predict it | 957 |
922 // exactly, for an adaptive timestretcher. The stretcher will | |
923 // need some additional buffer space. See the time stretcher code | |
924 // and comments. | |
925 | |
926 #ifdef HAVE_RUBBERBAND | |
927 m_timeStretchRatioMutex.lock(); | |
928 while ((available = ts->available()) < count) { | 958 while ((available = ts->available()) < count) { |
929 #else | |
930 while ((available = ts->getAvailableOutputSamples()) < count) { | |
931 #endif | |
932 | 959 |
933 size_t reqd = lrintf((count - available) / ratio); | 960 size_t reqd = lrintf((count - available) / ratio); |
934 #ifdef HAVE_RUBBERBAND | |
935 reqd = std::max(reqd, ts->getSamplesRequired()); | 961 reqd = std::max(reqd, ts->getSamplesRequired()); |
936 #else | |
937 reqd = std::max(reqd, ts->getRequiredInputSamples()); | |
938 #endif | |
939 if (reqd == 0) reqd = 1; | 962 if (reqd == 0) reqd = 1; |
940 | 963 |
941 float *ib[channels]; | |
942 | |
943 size_t got = reqd; | 964 size_t got = reqd; |
944 | 965 |
945 if (mix) { | 966 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
946 for (size_t c = 0; c < channels; ++c) { | 967 std::cerr << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << std::endl; |
947 if (c == 0) ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 968 #endif |
948 else ib[c] = 0; | 969 |
949 RingBuffer<float> *rb = getReadRingBuffer(c); | 970 for (size_t c = 0; c < channels; ++c) { |
950 if (rb) { | 971 if (c >= m_stretcherInputCount) continue; |
951 size_t gotHere; | 972 if (reqd > m_stretcherInputSizes[c]) { |
952 if (c > 0) gotHere = rb->readAdding(ib[0], got); | 973 if (c == 0) { |
953 else gotHere = rb->read(ib[0], got); | 974 std::cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << std::endl; |
954 if (gotHere < got) got = gotHere; | |
955 } | 975 } |
976 delete[] m_stretcherInputs[c]; | |
977 m_stretcherInputSizes[c] = reqd * 2; | |
978 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
956 } | 979 } |
957 } else { | 980 } |
958 for (size_t c = 0; c < channels; ++c) { | 981 |
959 ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 982 for (size_t c = 0; c < channels; ++c) { |
960 RingBuffer<float> *rb = getReadRingBuffer(c); | 983 if (c >= m_stretcherInputCount) continue; |
961 if (rb) { | 984 RingBuffer<float> *rb = getReadRingBuffer(c); |
962 size_t gotHere = rb->read(ib[c], got); | 985 if (rb) { |
963 if (gotHere < got) got = gotHere; | 986 size_t gotHere = rb->read(m_stretcherInputs[c], got); |
987 if (gotHere < got) got = gotHere; | |
988 | |
989 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
990 if (c == 0) { | |
991 std::cerr << "feeding stretcher: got " << gotHere | |
992 << ", " << rb->getReadSpace() << " remain" << std::endl; | |
964 } | 993 } |
994 #endif | |
995 | |
996 } else { | |
997 std::cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << std::endl; | |
965 } | 998 } |
966 } | 999 } |
967 | 1000 |
968 if (got < reqd) { | 1001 if (got < reqd) { |
969 std::cerr << "WARNING: Read underrun in playback (" | 1002 std::cerr << "WARNING: Read underrun in playback (" |
970 << got << " < " << reqd << ")" << std::endl; | 1003 << got << " < " << reqd << ")" << std::endl; |
971 } | 1004 } |
972 | 1005 |
973 #ifdef HAVE_RUBBERBAND | 1006 ts->process(m_stretcherInputs, got, false); |
974 ts->process(ib, got, false); | 1007 |
975 #else | 1008 fedToStretcher += got; |
976 ts->putInput(ib, got); | |
977 #endif | |
978 | |
979 for (size_t c = 0; c < channels; ++c) { | |
980 delete[] ib[c]; | |
981 } | |
982 | 1009 |
983 if (got == 0) break; | 1010 if (got == 0) break; |
984 | 1011 |
985 #ifdef HAVE_RUBBERBAND | |
986 if (ts->available() == available) { | 1012 if (ts->available() == available) { |
987 #else | |
988 if (ts->getAvailableOutputSamples() == available) { | |
989 #endif | |
990 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; | 1013 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; |
991 if (++warned == 5) break; | 1014 if (++warned == 5) break; |
992 } | 1015 } |
993 } | 1016 } |
994 | 1017 |
995 #ifdef HAVE_RUBBERBAND | |
996 ts->retrieve(buffer, count); | 1018 ts->retrieve(buffer, count); |
997 m_timeStretchRatioMutex.unlock(); | |
998 #else | |
999 ts->getOutput(buffer, count); | |
1000 #endif | |
1001 | |
1002 if (mix) { | |
1003 for (size_t c = 1; c < channels; ++c) { | |
1004 for (size_t i = 0; i < count; ++i) { | |
1005 buffer[c][i] = buffer[0][i] / channels; | |
1006 } | |
1007 } | |
1008 for (size_t i = 0; i < count; ++i) { | |
1009 buffer[0][i] /= channels; | |
1010 } | |
1011 } | |
1012 | 1019 |
1013 applyAuditioningEffect(count, buffer); | 1020 applyAuditioningEffect(count, buffer); |
1014 | 1021 |
1015 m_condition.wakeAll(); | 1022 m_condition.wakeAll(); |
1016 | 1023 |
1182 data.src_ratio = ratio; | 1189 data.src_ratio = ratio; |
1183 data.end_of_input = 0; | 1190 data.end_of_input = 0; |
1184 | 1191 |
1185 int err = 0; | 1192 int err = 0; |
1186 | 1193 |
1187 #ifdef HAVE_RUBBERBAND | |
1188 if (m_timeStretcher && m_timeStretcher->getTimeRatio() < 0.4) { | 1194 if (m_timeStretcher && m_timeStretcher->getTimeRatio() < 0.4) { |
1189 #else | |
1190 if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) { | |
1191 #endif | |
1192 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1195 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1193 std::cout << "Using crappy converter" << std::endl; | 1196 std::cout << "Using crappy converter" << std::endl; |
1194 #endif | 1197 #endif |
1195 err = src_process(m_crapConverter, &data); | 1198 err = src_process(m_crapConverter, &data); |
1196 } else { | 1199 } else { |
1225 | 1228 |
1226 } else { | 1229 } else { |
1227 | 1230 |
1228 // space must be a multiple of generatorBlockSize | 1231 // space must be a multiple of generatorBlockSize |
1229 space = (space / generatorBlockSize) * generatorBlockSize; | 1232 space = (space / generatorBlockSize) * generatorBlockSize; |
1230 if (space == 0) return false; | 1233 if (space == 0) { |
1234 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
1235 std::cout << "requested fill is less than generator block size of " | |
1236 << generatorBlockSize << ", leaving it" << std::endl; | |
1237 #endif | |
1238 return false; | |
1239 } | |
1231 | 1240 |
1232 if (tmpSize < channels * space) { | 1241 if (tmpSize < channels * space) { |
1233 delete[] tmp; | 1242 delete[] tmp; |
1234 tmp = new float[channels * space]; | 1243 tmp = new float[channels * space]; |
1235 tmpSize = channels * space; | 1244 tmpSize = channels * space; |
1511 while (!s.m_exiting) { | 1520 while (!s.m_exiting) { |
1512 | 1521 |
1513 s.unifyRingBuffers(); | 1522 s.unifyRingBuffers(); |
1514 s.m_bufferScavenger.scavenge(); | 1523 s.m_bufferScavenger.scavenge(); |
1515 s.m_pluginScavenger.scavenge(); | 1524 s.m_pluginScavenger.scavenge(); |
1516 #ifndef HAVE_RUBBERBAND | |
1517 s.m_timeStretcherScavenger.scavenge(); | |
1518 #endif | |
1519 | 1525 |
1520 if (work && s.m_playing && s.getSourceSampleRate()) { | 1526 if (work && s.m_playing && s.getSourceSampleRate()) { |
1521 | 1527 |
1522 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1528 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1523 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; | 1529 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; |