Mercurial > hg > svapp
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 { |