comparison audioio/AudioCallbackPlaySource.cpp @ 16:3715efc38f95

* substantial enhancements to time stretcher: -- use putInput/getOutput methods to ensure the audio source always feeds it enough input, avoiding underruns due to rounding error -- add a percussion detector and an optional "Sharpen" toggle to the main window, which invokes a very basic variable speed timestretcher
author Chris Cannam
date Wed, 13 Sep 2006 17:17:42 +0000
parents cc566264c935
children 80126455d169
comparison
equal deleted inserted replaced
15:cc566264c935 16:3715efc38f95
49 m_playing(false), 49 m_playing(false),
50 m_exiting(false), 50 m_exiting(false),
51 m_lastModelEndFrame(0), 51 m_lastModelEndFrame(0),
52 m_outputLeft(0.0), 52 m_outputLeft(0.0),
53 m_outputRight(0.0), 53 m_outputRight(0.0),
54 m_slowdownCounter(0),
55 m_timeStretcher(0), 54 m_timeStretcher(0),
56 m_fillThread(0), 55 m_fillThread(0),
57 m_converter(0) 56 m_converter(0)
58 { 57 {
59 m_viewManager->setAudioPlaySource(this); 58 m_viewManager->setAudioPlaySource(this);
425 readSpace = size_t(readSpace * ratio + 0.1); 424 readSpace = size_t(readSpace * ratio + 0.1);
426 } 425 }
427 426
428 size_t latency = m_playLatency; 427 size_t latency = m_playLatency;
429 if (resample) latency = size_t(m_playLatency * ratio + 0.1); 428 if (resample) latency = size_t(m_playLatency * ratio + 0.1);
430 429
431 TimeStretcherData *timeStretcher = m_timeStretcher; 430 PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher;
432 if (timeStretcher) { 431 if (timeStretcher) {
433 latency += timeStretcher->getStretcher(0)->getProcessingLatency(); 432 latency += timeStretcher->getProcessingLatency();
434 } 433 }
435 434
436 latency += readSpace; 435 latency += readSpace;
437 size_t bufferedFrame = m_readBufferFill; 436 size_t bufferedFrame = m_readBufferFill;
438 437
586 AudioCallbackPlaySource::getSourceSampleRate() const 585 AudioCallbackPlaySource::getSourceSampleRate() const
587 { 586 {
588 return m_sourceSampleRate; 587 return m_sourceSampleRate;
589 } 588 }
590 589
591 AudioCallbackPlaySource::TimeStretcherData::TimeStretcherData(size_t channels, 590 void
592 float factor, 591 AudioCallbackPlaySource::setSlowdownFactor(float factor, bool sharpen)
593 size_t blockSize) :
594 m_factor(factor),
595 m_blockSize(blockSize)
596 {
597 // std::cerr << "TimeStretcherData::TimeStretcherData(" << channels << ", " << factor << ", " << blockSize << ")" << std::endl;
598
599 for (size_t ch = 0; ch < channels; ++ch) {
600
601 m_stretcher[ch] = new PhaseVocoderTimeStretcher(factor, blockSize);
602 // 128),
603 // (blockSize/2) / factor),
604 // new float[lrintf(blockSize * factor)]);
605 }
606 }
607
608 AudioCallbackPlaySource::TimeStretcherData::~TimeStretcherData()
609 {
610 // std::cerr << "TimeStretcherData::~TimeStretcherData" << std::endl;
611
612 while (!m_stretcher.empty()) {
613 delete m_stretcher.begin()->second;
614 // delete[] m_stretcher.begin()->second.second;
615 m_stretcher.erase(m_stretcher.begin());
616 }
617 // delete m_stretchInputBuffer;
618 }
619
620 PhaseVocoderTimeStretcher *
621 AudioCallbackPlaySource::TimeStretcherData::getStretcher(size_t channel)
622 {
623 return m_stretcher[channel];
624 }
625 /*
626 float *
627 AudioCallbackPlaySource::TimeStretcherData::getOutputBuffer(size_t channel)
628 {
629 return m_stretcher[channel].second;
630 }
631
632 float *
633 AudioCallbackPlaySource::TimeStretcherData::getInputBuffer()
634 {
635 return m_stretchInputBuffer;
636 }
637
638 void
639 AudioCallbackPlaySource::TimeStretcherData::run(size_t channel)
640 {
641 getStretcher(channel)->process(getInputBuffer(),
642 getOutputBuffer(channel),
643 m_blockSize);
644 }
645 */
646 void
647 AudioCallbackPlaySource::setSlowdownFactor(float factor)
648 { 592 {
649 // Avoid locks -- create, assign, mark old one for scavenging 593 // Avoid locks -- create, assign, mark old one for scavenging
650 // later (as a call to getSourceSamples may still be using it) 594 // later (as a call to getSourceSamples may still be using it)
651 595
652 TimeStretcherData *existingStretcher = m_timeStretcher; 596 PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher;
653 597
654 if (existingStretcher && existingStretcher->getFactor() == factor) { 598 if (existingStretcher &&
599 existingStretcher->getRatio() == factor &&
600 existingStretcher->getSharpening() == sharpen) {
655 return; 601 return;
656 } 602 }
657 603
658 if (factor != 1) { 604 if (factor != 1) {
659 TimeStretcherData *newStretcher = new TimeStretcherData 605 PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher
660 (getTargetChannelCount(), factor, 606 (getTargetChannelCount(),
661 // factor > 1 ? getTargetBlockSize() : getTargetBlockSize() / factor); 607 factor,
662 //!!! doesn't work if the block size > getTargetBlockSize(), but it 608 sharpen,
663 // should be made to
664 // getTargetBlockSize());
665 lrintf(getTargetBlockSize() / factor)); 609 lrintf(getTargetBlockSize() / factor));
666 m_slowdownCounter = 0;
667 m_timeStretcher = newStretcher; 610 m_timeStretcher = newStretcher;
668 } else { 611 } else {
669 m_timeStretcher = 0; 612 m_timeStretcher = 0;
670 } 613 }
671 614
684 } 627 }
685 } 628 }
686 return 0; 629 return 0;
687 } 630 }
688 631
689 TimeStretcherData *timeStretcher = m_timeStretcher; 632 PhaseVocoderTimeStretcher *ts = m_timeStretcher;
690 633
691 if (!timeStretcher || timeStretcher->getFactor() == 1) { 634 if (!ts || ts->getRatio() == 1) {
692 635
693 size_t got = 0; 636 size_t got = 0;
694 637
695 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { 638 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
696 639
719 662
720 m_condition.wakeAll(); 663 m_condition.wakeAll();
721 return got; 664 return got;
722 } 665 }
723 666
667 float ratio = ts->getRatio();
668
669 // std::cout << "ratio = " << ratio << std::endl;
670
671 size_t available;
672
673 while ((available = ts->getAvailableOutputSamples()) < count) {
674
675 size_t reqd = lrintf((count - available) / ratio);
676 reqd = std::max(reqd, ts->getRequiredInputSamples());
677 if (reqd == 0) reqd = 1;
678
679 size_t channels = getTargetChannelCount();
680
681 float *ib[channels];
682
683 size_t got = reqd;
684
685 for (size_t c = 0; c < channels; ++c) {
686 ib[c] = new float[reqd]; //!!! fix -- this is a rt function
687 RingBuffer<float> *rb = getReadRingBuffer(c);
688 if (rb) {
689 size_t gotHere = rb->read(ib[c], got);
690 if (gotHere < got) got = gotHere;
691 }
692 }
693
694 if (got < reqd) {
695 std::cerr << "WARNING: Read underrun in playback ("
696 << got << " < " << reqd << ")" << std::endl;
697 }
698
699 ts->putInput(ib, got);
700
701 for (size_t c = 0; c < channels; ++c) {
702 delete[] ib[c];
703 }
704
705 if (got == 0) break;
706
707 if (ts->getAvailableOutputSamples() == available) {
708 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples" << std::endl;
709 break;
710 }
711 }
712
713 ts->getOutput(buffer, count);
714
715
724 /*!!! 716 /*!!!
725 if (m_slowdownCounter == 0) { 717 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
726 718
727 size_t got = 0; 719 RingBuffer<float> *rb = getReadRingBuffer(ch);
728 float *ib = timeStretcher->getInputBuffer(); 720
729 721 if (rb) {
730 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { 722
731 723 float ratio = ts->getRatio();
732 RingBuffer<float> *rb = getReadRingBuffer(ch); 724
733 725 // std::cout << "ratio = " << ratio << std::endl;
734 if (rb) { 726
735 727 size_t available;
736 size_t request = count; 728
737 if (ch > 0) request = got; // see above 729 while ((available = ts->getAvailableOutputSamples()) < count) {
738 got = rb->read(buffer[ch], request); 730
731 size_t reqd = lrintf((count - available) / ratio);
732 reqd = std::max(reqd, ts->getRequiredInputSamples());
733 if (reqd == 0) reqd = 1;
734
735 float ib[reqd];
736 size_t got = rb->read(ib, reqd);
739 737
740 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING 738 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
741 std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << ", running time stretcher" << std::endl; 739 std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << " (reqd=" << reqd << ", count=" << count << ", ratio=" << ratio << ", got*ratio=" << got * ratio << "), running time stretcher" << std::endl;
742 #endif 740 #endif
743 741
744 for (size_t i = 0; i < count; ++i) { 742 if (got < reqd) {
745 ib[i] = buffer[ch][i]; 743 std::cerr << "WARNING: Read underrun in playback ("
746 } 744 << got << " < " << reqd << ")" << std::endl;
747 745 }
748 timeStretcher->run(ch); 746
749 } 747 ts->putInput(ib, got);
750 } 748
751 749 if (got == 0) break;
752 } else if (m_slowdownCounter >= timeStretcher->getFactor()) { 750
753 // reset this in case the factor has changed leaving the 751 if (ts->getAvailableOutputSamples() == available) {
754 // counter out of range 752 std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples" << std::endl;
755 m_slowdownCounter = 0; 753 break;
756 } 754 }
757 755 }
758 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { 756
759 757 ts->getOutput(buffer[ch], count);
760 float *ob = timeStretcher->getOutputBuffer(ch); 758 }
761
762 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
763 std::cerr << "AudioCallbackPlaySource::getSamples: Copying from (" << (m_slowdownCounter * count) << "," << count << ") to buffer" << std::endl;
764 #endif
765
766 for (size_t i = 0; i < count; ++i) {
767 buffer[ch][i] = ob[m_slowdownCounter * count + i];
768 }
769 } 759 }
770 */ 760 */
771 761 m_condition.wakeAll();
772 for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { 762
773
774 RingBuffer<float> *rb = getReadRingBuffer(ch);
775
776 if (rb) {
777
778 float ratio = timeStretcher->getStretcher(ch)->getRatio();
779 size_t request = lrintf(count / ratio);
780 // if (ch > 0) request = got; // see above
781
782 float *ib = new float[request]; //!!!
783
784 size_t got = rb->read(ib, request);
785
786 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
787 std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << " (count=" << count << ", ratio=" << timeStretcher->getStretcher(ch)->getRatio() << ", got*ratio=" << got * ratio << "), running time stretcher" << std::endl;
788 #endif
789
790 timeStretcher->getStretcher(ch)->process(ib, buffer[ch], request);
791
792 delete[] ib;
793
794 // for (size_t i = 0; i < count; ++i) {
795 // ib[i] = buffer[ch][i];
796 // }
797
798 // timeStretcher->run(ch);
799
800
801
802 }
803 }
804
805
806
807 //!!! if (m_slowdownCounter == 0) m_condition.wakeAll();
808 // m_slowdownCounter = (m_slowdownCounter + 1) % timeStretcher->getFactor();
809 return count; 763 return count;
810 } 764 }
811 765
812 // Called from fill thread, m_playing true, mutex held 766 // Called from fill thread, m_playing true, mutex held
813 bool 767 bool