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; |
