comparison audioio/AudioCallbackPlaySource.cpp @ 93:737b373246b5

* Further fixes to the handling of playback frame and buffered frame counts
author Chris Cannam
date Mon, 11 Feb 2008 12:46:39 +0000
parents 792bca285459
children 9cc9862333bd
comparison
equal deleted inserted replaced
92:792bca285459 93:737b373246b5
242 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) 242 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame)
243 { 243 {
244 #ifdef DEBUG_AUDIO_PLAY_SOURCE 244 #ifdef DEBUG_AUDIO_PLAY_SOURCE
245 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; 245 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl;
246 #endif 246 #endif
247 if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; 247 if (endFrame > m_lastModelEndFrame) {
248 m_lastModelEndFrame = endFrame;
249 }
248 } 250 }
249 251
250 void 252 void
251 AudioCallbackPlaySource::removeModel(Model *model) 253 AudioCallbackPlaySource::removeModel(Model *model)
252 { 254 {
310 m_sourceSampleRate = 0; 312 m_sourceSampleRate = 0;
311 313
312 m_mutex.unlock(); 314 m_mutex.unlock();
313 315
314 m_audioGenerator->clearModels(); 316 m_audioGenerator->clearModels();
317
318 clearRingBuffers();
315 } 319 }
316 320
317 void 321 void
318 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) 322 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count)
319 { 323 {
320 if (!haveLock) m_mutex.lock(); 324 if (!haveLock) m_mutex.lock();
325
326 rebuildRangeLists();
321 327
322 if (count == 0) { 328 if (count == 0) {
323 if (m_writeBuffers) count = m_writeBuffers->size(); 329 if (m_writeBuffers) count = m_writeBuffers->size();
324 } 330 }
325 331
326 size_t sf = m_readBufferFill; 332 m_writeBufferFill = getCurrentBufferedFrame();
327 RingBuffer<float> *rb = getReadRingBuffer(0);
328 if (rb) {
329 //!!! This is incorrect if we're in a non-contiguous selection
330 //Same goes for all related code (subtracting the read space
331 //from the fill frame to try to establish where the effective
332 //pre-resample/timestretch read pointer is)
333 size_t rs = rb->getReadSpace();
334 if (rs < sf) sf -= rs;
335 else sf = 0;
336 }
337 m_writeBufferFill = sf;
338 333
339 if (m_readBuffers != m_writeBuffers) { 334 if (m_readBuffers != m_writeBuffers) {
340 delete m_writeBuffers; 335 delete m_writeBuffers;
341 } 336 }
342 337
381 m_mutex.lock(); 376 m_mutex.lock();
382 if (m_timeStretcher) { 377 if (m_timeStretcher) {
383 m_timeStretcher->reset(); 378 m_timeStretcher->reset();
384 } 379 }
385 if (m_playing) { 380 if (m_playing) {
381 std::cerr << "playing already, resetting" << std::endl;
386 m_readBufferFill = m_writeBufferFill = startFrame; 382 m_readBufferFill = m_writeBufferFill = startFrame;
387 if (m_readBuffers) { 383 if (m_readBuffers) {
388 for (size_t c = 0; c < getTargetChannelCount(); ++c) { 384 for (size_t c = 0; c < getTargetChannelCount(); ++c) {
389 RingBuffer<float> *rb = getReadRingBuffer(c); 385 RingBuffer<float> *rb = getReadRingBuffer(c);
386 std::cerr << "reset ring buffer for channel " << c << std::endl;
390 if (rb) rb->reset(); 387 if (rb) rb->reset();
391 } 388 }
392 } 389 }
393 if (m_converter) src_reset(m_converter); 390 if (m_converter) src_reset(m_converter);
394 if (m_crapConverter) src_reset(m_crapConverter); 391 if (m_crapConverter) src_reset(m_crapConverter);
496 AudioCallbackPlaySource::getCurrentPlayingFrame() 493 AudioCallbackPlaySource::getCurrentPlayingFrame()
497 { 494 {
498 // This method attempts to estimate which audio sample frame is 495 // This method attempts to estimate which audio sample frame is
499 // "currently coming through the speakers". 496 // "currently coming through the speakers".
500 497
498 size_t targetRate = getTargetSampleRate();
499 size_t latency = m_playLatency; // at target rate
500 RealTime latency_t = RealTime::frame2RealTime(latency, targetRate);
501
502 return getCurrentFrame(latency_t);
503 }
504
505 size_t
506 AudioCallbackPlaySource::getCurrentBufferedFrame()
507 {
508 return getCurrentFrame(RealTime::zeroTime);
509 }
510
511 size_t
512 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t)
513 {
501 bool resample = false; 514 bool resample = false;
502 double resampleRatio = 1.0; 515 double resampleRatio = 1.0;
503 516
504 // We resample when filling the ring buffer, and time-stretch when 517 // We resample when filling the ring buffer, and time-stretch when
505 // draining it. The buffer contains data at the "target rate" and 518 // draining it. The buffer contains data at the "target rate" and
527 double lastRetrievalTimestamp = m_lastRetrievalTimestamp; 540 double lastRetrievalTimestamp = m_lastRetrievalTimestamp;
528 double currentTime = 0.0; 541 double currentTime = 0.0;
529 if (m_target) currentTime = m_target->getCurrentTime(); 542 if (m_target) currentTime = m_target->getCurrentTime();
530 543
531 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); 544 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate);
532
533 size_t latency = m_playLatency; // at target rate
534 RealTime latency_t = RealTime::frame2RealTime(latency, targetRate);
535 545
536 size_t stretchlat = 0; 546 size_t stretchlat = 0;
537 double timeRatio = 1.0; 547 double timeRatio = 1.0;
538 548
539 if (m_timeStretcher) { 549 if (m_timeStretcher) {
581 lastretrieved_t = lastretrieved_t / timeRatio; 591 lastretrieved_t = lastretrieved_t / timeRatio;
582 sincerequest_t = sincerequest_t / timeRatio; 592 sincerequest_t = sincerequest_t / timeRatio;
583 } 593 }
584 594
585 bool looping = m_viewManager->getPlayLoopMode(); 595 bool looping = m_viewManager->getPlayLoopMode();
586 bool constrained = (m_viewManager->getPlaySelectionMode() &&
587 !m_viewManager->getSelections().empty());
588 596
589 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING 597 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
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; 598 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;
591 #endif 599 #endif
592 600
593 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); 601 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate);
594 602
603 // Normally the range lists should contain at least one item each
604 // -- if playback is unconstrained, that item should report the
605 // entire source audio duration.
606
607 if (m_rangeStarts.empty()) {
608 rebuildRangeLists();
609 }
610
611 if (m_rangeStarts.empty()) {
612 // this code is only used in case of error in rebuildRangeLists
613 RealTime playing_t = bufferedto_t
614 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
615 + sincerequest_t;
616 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
617 return m_viewManager->alignPlaybackFrameToReference(frame);
618 }
619
620 int inRange = 0;
621 int index = 0;
622
623 for (size_t i = 0; i < m_rangeStarts.size(); ++i) {
624 if (bufferedto_t >= m_rangeStarts[i]) {
625 inRange = index;
626 } else {
627 break;
628 }
629 ++index;
630 }
631
632 if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1;
633
634 RealTime playing_t = bufferedto_t - m_rangeStarts[inRange];
635
636 playing_t = playing_t
637 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
638 + sincerequest_t;
639
640 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
641 std::cerr << "playing_t as offset into range " << inRange << " (with start = " << m_rangeStarts[inRange] << ") = " << playing_t << std::endl;
642 #endif
643
644 while (playing_t < RealTime::zeroTime) {
645
646 if (inRange == 0) {
647 if (looping) {
648 inRange = m_rangeStarts.size() - 1;
649 } else {
650 break;
651 }
652 } else {
653 --inRange;
654 }
655
656 playing_t = playing_t + m_rangeDurations[inRange];
657 }
658
659 playing_t = playing_t + m_rangeStarts[inRange];
660
661 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
662 std::cerr << " playing time: " << playing_t << std::endl;
663 #endif
664
665 if (!looping) {
666 if (inRange == m_rangeStarts.size()-1 &&
667 playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) {
668 stop();
669 }
670 }
671
672 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
673
674 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
675 return m_viewManager->alignPlaybackFrameToReference(frame);
676 }
677
678 void
679 AudioCallbackPlaySource::rebuildRangeLists()
680 {
681 bool constrained = (m_viewManager->getPlaySelectionMode());
682
683 m_rangeStarts.clear();
684 m_rangeDurations.clear();
685
686 size_t sourceRate = getSourceSampleRate();
687 if (sourceRate == 0) return;
688
689 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate);
690 if (end == RealTime::zeroTime) return;
691
692 if (!constrained) {
693 m_rangeStarts.push_back(RealTime::zeroTime);
694 m_rangeDurations.push_back(end);
695 return;
696 }
697
595 MultiSelection::SelectionList selections = m_viewManager->getSelections(); 698 MultiSelection::SelectionList selections = m_viewManager->getSelections();
596 MultiSelection::SelectionList::const_iterator i; 699 MultiSelection::SelectionList::const_iterator i;
597 700
598 // These could be cached from one call to the next, if the 701 #ifdef DEBUG_AUDIO_PLAY_SOURCE
599 // selection has not changed... but some of the work would still 702 std::cerr << "AudioCallbackPlaySource::rebuildRangeLists" << std::endl;
600 // need to be done because the playback model may have changed. 703 #endif
601 704
602 // Currently, we know that this method is only ever called from a 705 if (!selections.empty()) {
603 // single thread (the GUI thread), so we could be nasty and
604 // maintain these as statics to avoid re-creating them...
605
606 std::vector<RealTime> rangeStarts;
607 std::vector<RealTime> rangeDurations;
608
609 int inRange = 0;
610 int index = 0;
611
612 if (constrained) {
613 706
614 for (i = selections.begin(); i != selections.end(); ++i) { 707 for (i = selections.begin(); i != selections.end(); ++i) {
615 708
616 RealTime start = 709 RealTime start =
617 (RealTime::frame2RealTime 710 (RealTime::frame2RealTime
621 (RealTime::frame2RealTime 714 (RealTime::frame2RealTime
622 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) - 715 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) -
623 m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), 716 m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()),
624 sourceRate)); 717 sourceRate));
625 718
626 rangeStarts.push_back(start); 719 m_rangeStarts.push_back(start);
627 rangeDurations.push_back(duration); 720 m_rangeDurations.push_back(duration);
628 721 }
629 if (bufferedto_t >= start) { 722 } else {
630 inRange = index; 723 m_rangeStarts.push_back(RealTime::zeroTime);
631 } 724 m_rangeDurations.push_back(end);
632 725 }
633 ++index; 726
634 } 727 #ifdef DEBUG_AUDIO_PLAY_SOURCE
635 } 728 std::cerr << "Now have " << m_rangeStarts.size() << " play ranges" << std::endl;
636 729 #endif
637 if (rangeStarts.empty()) {
638 rangeStarts.push_back(RealTime::zeroTime);
639 rangeDurations.push_back(end);
640 }
641
642 if (inRange >= rangeStarts.size()) inRange = rangeStarts.size()-1;
643
644 RealTime playing_t = bufferedto_t - rangeStarts[inRange];
645
646 playing_t = playing_t
647 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
648 + sincerequest_t;
649
650 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
651 std::cerr << "playing_t as offset into range " << inRange << " (with start = " << rangeStarts[inRange] << ") = " << playing_t << std::endl;
652 #endif
653
654 while (playing_t < RealTime::zeroTime) {
655
656 if (inRange == 0) {
657 if (looping) {
658 inRange = rangeStarts.size() - 1;
659 } else {
660 break;
661 }
662 } else {
663 --inRange;
664 }
665
666 playing_t = playing_t + rangeDurations[inRange];
667 }
668
669 playing_t = playing_t + rangeStarts[inRange];
670
671 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
672 std::cerr << " playing time: " << playing_t << std::endl;
673 #endif
674
675 if (!looping) {
676 if (inRange == rangeStarts.size()-1 &&
677 playing_t >= rangeStarts[inRange] + rangeDurations[inRange]) {
678 stop();
679 }
680 }
681
682 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
683
684 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
685 return m_viewManager->alignPlaybackFrameToReference(frame);
686 } 730 }
687 731
688 void 732 void
689 AudioCallbackPlaySource::setOutputLevels(float left, float right) 733 AudioCallbackPlaySource::setOutputLevels(float left, float right)
690 { 734 {