Mercurial > hg > svapp
comparison audioio/AudioCallbackPlaySource.cpp @ 100:22bf057ea151 1.2-stable
* merge from trunk (1.2 ended up being tracked from trunk, but we may want
this branch for fixes later)
author | Chris Cannam |
---|---|
date | Wed, 27 Feb 2008 10:32:45 +0000 |
parents | ae2627ac7db2 |
children |
comparison
equal
deleted
inserted
replaced
71:a8acc7841d70 | 100:22bf057ea151 |
---|---|
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), |
67 m_playStartFrame(0), | |
68 m_playStartFramePassed(false), | |
66 m_timeStretcher(0), | 69 m_timeStretcher(0), |
70 m_stretchRatio(1.0), | |
71 m_stretcherInputCount(0), | |
72 m_stretcherInputs(0), | |
73 m_stretcherInputSizes(0), | |
67 m_fillThread(0), | 74 m_fillThread(0), |
68 m_converter(0), | 75 m_converter(0), |
69 m_crapConverter(0), | 76 m_crapConverter(0), |
70 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) | 77 m_resampleQuality(Preferences::getInstance()->getResampleQuality()) |
71 { | 78 { |
105 | 112 |
106 delete m_writeBuffers; | 113 delete m_writeBuffers; |
107 | 114 |
108 delete m_audioGenerator; | 115 delete m_audioGenerator; |
109 | 116 |
117 for (size_t i = 0; i < m_stretcherInputCount; ++i) { | |
118 delete[] m_stretcherInputs[i]; | |
119 } | |
120 delete[] m_stretcherInputSizes; | |
121 delete[] m_stretcherInputs; | |
122 | |
110 m_bufferScavenger.scavenge(true); | 123 m_bufferScavenger.scavenge(true); |
111 m_pluginScavenger.scavenge(true); | 124 m_pluginScavenger.scavenge(true); |
112 #ifndef HAVE_RUBBERBAND | |
113 m_timeStretcherScavenger.scavenge(true); | |
114 #endif | |
115 } | 125 } |
116 | 126 |
117 void | 127 void |
118 AudioCallbackPlaySource::addModel(Model *model) | 128 AudioCallbackPlaySource::addModel(Model *model) |
119 { | 129 { |
234 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) | 244 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) |
235 { | 245 { |
236 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 246 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
237 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; | 247 std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; |
238 #endif | 248 #endif |
239 if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; | 249 if (endFrame > m_lastModelEndFrame) { |
250 m_lastModelEndFrame = endFrame; | |
251 rebuildRangeLists(); | |
252 } | |
240 } | 253 } |
241 | 254 |
242 void | 255 void |
243 AudioCallbackPlaySource::removeModel(Model *model) | 256 AudioCallbackPlaySource::removeModel(Model *model) |
244 { | 257 { |
302 m_sourceSampleRate = 0; | 315 m_sourceSampleRate = 0; |
303 | 316 |
304 m_mutex.unlock(); | 317 m_mutex.unlock(); |
305 | 318 |
306 m_audioGenerator->clearModels(); | 319 m_audioGenerator->clearModels(); |
320 | |
321 clearRingBuffers(); | |
307 } | 322 } |
308 | 323 |
309 void | 324 void |
310 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) | 325 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) |
311 { | 326 { |
312 if (!haveLock) m_mutex.lock(); | 327 if (!haveLock) m_mutex.lock(); |
328 | |
329 rebuildRangeLists(); | |
313 | 330 |
314 if (count == 0) { | 331 if (count == 0) { |
315 if (m_writeBuffers) count = m_writeBuffers->size(); | 332 if (m_writeBuffers) count = m_writeBuffers->size(); |
316 } | 333 } |
317 | 334 |
318 size_t sf = m_readBufferFill; | 335 m_writeBufferFill = getCurrentBufferedFrame(); |
319 RingBuffer<float> *rb = getReadRingBuffer(0); | |
320 if (rb) { | |
321 //!!! This is incorrect if we're in a non-contiguous selection | |
322 //Same goes for all related code (subtracting the read space | |
323 //from the fill frame to try to establish where the effective | |
324 //pre-resample/timestretch read pointer is) | |
325 size_t rs = rb->getReadSpace(); | |
326 if (rs < sf) sf -= rs; | |
327 else sf = 0; | |
328 } | |
329 m_writeBufferFill = sf; | |
330 | 336 |
331 if (m_readBuffers != m_writeBuffers) { | 337 if (m_readBuffers != m_writeBuffers) { |
332 delete m_writeBuffers; | 338 delete m_writeBuffers; |
333 } | 339 } |
334 | 340 |
350 AudioCallbackPlaySource::play(size_t startFrame) | 356 AudioCallbackPlaySource::play(size_t startFrame) |
351 { | 357 { |
352 if (m_viewManager->getPlaySelectionMode() && | 358 if (m_viewManager->getPlaySelectionMode() && |
353 !m_viewManager->getSelections().empty()) { | 359 !m_viewManager->getSelections().empty()) { |
354 | 360 |
361 std::cerr << "AudioCallbackPlaySource::play: constraining frame " << startFrame << " to selection = "; | |
362 | |
355 startFrame = m_viewManager->constrainFrameToSelection(startFrame); | 363 startFrame = m_viewManager->constrainFrameToSelection(startFrame); |
364 | |
365 std::cerr << startFrame << std::endl; | |
356 | 366 |
357 } else { | 367 } else { |
358 if (startFrame >= m_lastModelEndFrame) { | 368 if (startFrame >= m_lastModelEndFrame) { |
359 startFrame = 0; | 369 startFrame = 0; |
360 } | 370 } |
369 // The fill thread will automatically empty its buffers before | 379 // The fill thread will automatically empty its buffers before |
370 // starting again if we have not so far been playing, but not if | 380 // starting again if we have not so far been playing, but not if |
371 // we're just re-seeking. | 381 // we're just re-seeking. |
372 | 382 |
373 m_mutex.lock(); | 383 m_mutex.lock(); |
384 if (m_timeStretcher) { | |
385 m_timeStretcher->reset(); | |
386 } | |
374 if (m_playing) { | 387 if (m_playing) { |
388 std::cerr << "playing already, resetting" << std::endl; | |
375 m_readBufferFill = m_writeBufferFill = startFrame; | 389 m_readBufferFill = m_writeBufferFill = startFrame; |
376 if (m_readBuffers) { | 390 if (m_readBuffers) { |
377 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 391 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
378 RingBuffer<float> *rb = getReadRingBuffer(c); | 392 RingBuffer<float> *rb = getReadRingBuffer(c); |
393 std::cerr << "reset ring buffer for channel " << c << std::endl; | |
379 if (rb) rb->reset(); | 394 if (rb) rb->reset(); |
380 } | 395 } |
381 } | 396 } |
382 if (m_converter) src_reset(m_converter); | 397 if (m_converter) src_reset(m_converter); |
383 if (m_crapConverter) src_reset(m_crapConverter); | 398 if (m_crapConverter) src_reset(m_crapConverter); |
388 } | 403 } |
389 m_mutex.unlock(); | 404 m_mutex.unlock(); |
390 | 405 |
391 m_audioGenerator->reset(); | 406 m_audioGenerator->reset(); |
392 | 407 |
408 m_playStartFrame = startFrame; | |
409 m_playStartFramePassed = false; | |
410 m_playStartedAt = RealTime::zeroTime; | |
411 if (m_target) { | |
412 m_playStartedAt = RealTime::fromSeconds(m_target->getCurrentTime()); | |
413 } | |
414 | |
393 bool changed = !m_playing; | 415 bool changed = !m_playing; |
416 m_lastRetrievalTimestamp = 0; | |
394 m_playing = true; | 417 m_playing = true; |
395 m_condition.wakeAll(); | 418 m_condition.wakeAll(); |
396 if (changed) emit playStatusChanged(m_playing); | 419 if (changed) emit playStatusChanged(m_playing); |
397 } | 420 } |
398 | 421 |
400 AudioCallbackPlaySource::stop() | 423 AudioCallbackPlaySource::stop() |
401 { | 424 { |
402 bool changed = m_playing; | 425 bool changed = m_playing; |
403 m_playing = false; | 426 m_playing = false; |
404 m_condition.wakeAll(); | 427 m_condition.wakeAll(); |
428 m_lastRetrievalTimestamp = 0; | |
405 if (changed) emit playStatusChanged(m_playing); | 429 if (changed) emit playStatusChanged(m_playing); |
406 } | 430 } |
407 | 431 |
408 void | 432 void |
409 AudioCallbackPlaySource::selectionChanged() | 433 AudioCallbackPlaySource::selectionChanged() |
450 emit audioOverloadPluginDisabled(); | 474 emit audioOverloadPluginDisabled(); |
451 } | 475 } |
452 } | 476 } |
453 | 477 |
454 void | 478 void |
455 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 479 AudioCallbackPlaySource::setTarget(AudioCallbackPlayTarget *target, size_t size) |
456 { | 480 { |
481 m_target = target; | |
457 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 482 // std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; |
458 assert(size < m_ringBufferSize); | 483 assert(size < m_ringBufferSize); |
459 m_blockSize = size; | 484 m_blockSize = size; |
460 } | 485 } |
461 | 486 |
479 } | 504 } |
480 | 505 |
481 size_t | 506 size_t |
482 AudioCallbackPlaySource::getCurrentPlayingFrame() | 507 AudioCallbackPlaySource::getCurrentPlayingFrame() |
483 { | 508 { |
509 // This method attempts to estimate which audio sample frame is | |
510 // "currently coming through the speakers". | |
511 | |
512 size_t targetRate = getTargetSampleRate(); | |
513 size_t latency = m_playLatency; // at target rate | |
514 RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); | |
515 | |
516 return getCurrentFrame(latency_t); | |
517 } | |
518 | |
519 size_t | |
520 AudioCallbackPlaySource::getCurrentBufferedFrame() | |
521 { | |
522 return getCurrentFrame(RealTime::zeroTime); | |
523 } | |
524 | |
525 size_t | |
526 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) | |
527 { | |
484 bool resample = false; | 528 bool resample = false; |
485 double ratio = 1.0; | 529 double resampleRatio = 1.0; |
486 | 530 |
487 if (getSourceSampleRate() != getTargetSampleRate()) { | 531 // We resample when filling the ring buffer, and time-stretch when |
488 resample = true; | 532 // draining it. The buffer contains data at the "target rate" and |
489 ratio = double(getSourceSampleRate()) / double(getTargetSampleRate()); | 533 // the latency provided by the target is also at the target rate. |
490 } | 534 // Because of the multiple rates involved, we do the actual |
491 | 535 // calculation using RealTime instead. |
492 size_t readSpace = 0; | 536 |
537 size_t sourceRate = getSourceSampleRate(); | |
538 size_t targetRate = getTargetSampleRate(); | |
539 | |
540 if (sourceRate == 0 || targetRate == 0) return 0; | |
541 | |
542 size_t inbuffer = 0; // at target rate | |
543 | |
493 for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 544 for (size_t c = 0; c < getTargetChannelCount(); ++c) { |
494 RingBuffer<float> *rb = getReadRingBuffer(c); | 545 RingBuffer<float> *rb = getReadRingBuffer(c); |
495 if (rb) { | 546 if (rb) { |
496 size_t spaceHere = rb->getReadSpace(); | 547 size_t here = rb->getReadSpace(); |
497 if (c == 0 || spaceHere < readSpace) readSpace = spaceHere; | 548 if (c == 0 || here < inbuffer) inbuffer = here; |
498 } | 549 } |
499 } | 550 } |
500 | 551 |
501 if (resample) { | 552 size_t readBufferFill = m_readBufferFill; |
502 readSpace = size_t(readSpace * ratio + 0.1); | 553 size_t lastRetrievedBlockSize = m_lastRetrievedBlockSize; |
503 } | 554 double lastRetrievalTimestamp = m_lastRetrievalTimestamp; |
504 | 555 double currentTime = 0.0; |
505 size_t latency = m_playLatency; | 556 if (m_target) currentTime = m_target->getCurrentTime(); |
506 if (resample) latency = size_t(m_playLatency * ratio + 0.1); | 557 |
507 | 558 RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); |
508 #ifdef HAVE_RUBBERBAND | 559 |
560 size_t stretchlat = 0; | |
561 double timeRatio = 1.0; | |
562 | |
509 if (m_timeStretcher) { | 563 if (m_timeStretcher) { |
510 latency += m_timeStretcher->getLatency(); | 564 stretchlat = m_timeStretcher->getLatency(); |
511 } | 565 timeRatio = m_timeStretcher->getTimeRatio(); |
512 #else | 566 } |
513 PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher; | 567 |
514 if (timeStretcher) { | 568 RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate); |
515 latency += timeStretcher->getProcessingLatency(); | 569 |
516 } | 570 // When the target has just requested a block from us, the last |
517 #endif | 571 // sample it obtained was our buffer fill frame count minus the |
518 | 572 // amount of read space (converted back to source sample rate) |
519 latency += readSpace; | 573 // remaining now. That sample is not expected to be played until |
520 size_t bufferedFrame = m_readBufferFill; | 574 // the target's play latency has elapsed. By the time the |
575 // following block is requested, that sample will be at the | |
576 // target's play latency minus the last requested block size away | |
577 // from being played. | |
578 | |
579 RealTime sincerequest_t = RealTime::zeroTime; | |
580 RealTime lastretrieved_t = RealTime::zeroTime; | |
581 | |
582 if (m_target && lastRetrievalTimestamp != 0.0) { | |
583 | |
584 lastretrieved_t = RealTime::frame2RealTime | |
585 (lastRetrievedBlockSize, targetRate); | |
586 | |
587 // calculate number of frames at target rate that have elapsed | |
588 // since the end of the last call to getSourceSamples | |
589 | |
590 double elapsed = currentTime - lastRetrievalTimestamp; | |
591 | |
592 if (elapsed > 0.0) { | |
593 sincerequest_t = RealTime::fromSeconds(elapsed); | |
594 } | |
595 | |
596 } else { | |
597 | |
598 lastretrieved_t = RealTime::frame2RealTime | |
599 (getTargetBlockSize(), targetRate); | |
600 } | |
601 | |
602 RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate); | |
603 | |
604 if (timeRatio != 1.0) { | |
605 lastretrieved_t = lastretrieved_t / timeRatio; | |
606 sincerequest_t = sincerequest_t / timeRatio; | |
607 } | |
521 | 608 |
522 bool looping = m_viewManager->getPlayLoopMode(); | 609 bool looping = m_viewManager->getPlayLoopMode(); |
523 bool constrained = (m_viewManager->getPlaySelectionMode() && | 610 |
524 !m_viewManager->getSelections().empty()); | 611 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
525 | 612 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; |
526 size_t framePlaying = bufferedFrame; | 613 #endif |
527 | 614 |
528 if (looping && !constrained) { | 615 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); |
529 while (framePlaying < latency) framePlaying += m_lastModelEndFrame; | 616 |
530 } | 617 // Normally the range lists should contain at least one item each |
531 | 618 // -- if playback is unconstrained, that item should report the |
532 if (framePlaying > latency) framePlaying -= latency; | 619 // entire source audio duration. |
533 else framePlaying = 0; | 620 |
534 | 621 if (m_rangeStarts.empty()) { |
535 // std::cerr << "framePlaying = " << framePlaying << " -> reference "; | 622 rebuildRangeLists(); |
536 | 623 } |
537 framePlaying = m_viewManager->alignPlaybackFrameToReference(framePlaying); | 624 |
538 | 625 if (m_rangeStarts.empty()) { |
539 // std::cerr << framePlaying << std::endl; | 626 // this code is only used in case of error in rebuildRangeLists |
627 RealTime playing_t = bufferedto_t | |
628 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | |
629 + sincerequest_t; | |
630 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | |
631 return m_viewManager->alignPlaybackFrameToReference(frame); | |
632 } | |
633 | |
634 int inRange = 0; | |
635 int index = 0; | |
636 | |
637 for (size_t i = 0; i < m_rangeStarts.size(); ++i) { | |
638 if (bufferedto_t >= m_rangeStarts[i]) { | |
639 inRange = index; | |
640 } else { | |
641 break; | |
642 } | |
643 ++index; | |
644 } | |
645 | |
646 if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1; | |
647 | |
648 RealTime playing_t = bufferedto_t; | |
649 | |
650 playing_t = playing_t | |
651 - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | |
652 + sincerequest_t; | |
653 | |
654 // This rather gross little hack is used to ensure that latency | |
655 // compensation doesn't result in the playback pointer appearing | |
656 // to start earlier than the actual playback does. It doesn't | |
657 // work properly (hence the bail-out in the middle) because if we | |
658 // are playing a relatively short looped region, the playing time | |
659 // estimated from the buffer fill frame may have wrapped around | |
660 // the region boundary and end up being much smaller than the | |
661 // theoretical play start frame, perhaps even for the entire | |
662 // duration of playback! | |
663 | |
664 if (!m_playStartFramePassed) { | |
665 RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, | |
666 sourceRate); | |
667 if (playing_t < playstart_t) { | |
668 // std::cerr << "playing_t " << playing_t << " < playstart_t " | |
669 // << playstart_t << std::endl; | |
670 if (sincerequest_t > RealTime::zeroTime && | |
671 m_playStartedAt + latency_t + stretchlat_t < | |
672 RealTime::fromSeconds(currentTime)) { | |
673 // std::cerr << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << std::endl; | |
674 m_playStartFramePassed = true; | |
675 } else { | |
676 playing_t = playstart_t; | |
677 } | |
678 } else { | |
679 m_playStartFramePassed = true; | |
680 } | |
681 } | |
682 | |
683 playing_t = playing_t - m_rangeStarts[inRange]; | |
684 | |
685 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
686 std::cerr << "playing_t as offset into range " << inRange << " (with start = " << m_rangeStarts[inRange] << ") = " << playing_t << std::endl; | |
687 #endif | |
688 | |
689 while (playing_t < RealTime::zeroTime) { | |
690 | |
691 if (inRange == 0) { | |
692 if (looping) { | |
693 inRange = m_rangeStarts.size() - 1; | |
694 } else { | |
695 break; | |
696 } | |
697 } else { | |
698 --inRange; | |
699 } | |
700 | |
701 playing_t = playing_t + m_rangeDurations[inRange]; | |
702 } | |
703 | |
704 playing_t = playing_t + m_rangeStarts[inRange]; | |
705 | |
706 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
707 std::cerr << " playing time: " << playing_t << std::endl; | |
708 #endif | |
709 | |
710 if (!looping) { | |
711 if (inRange == m_rangeStarts.size()-1 && | |
712 playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) { | |
713 std::cerr << "Not looping, inRange " << inRange << " == rangeStarts.size()-1, playing_t " << playing_t << " >= m_rangeStarts[inRange] " << m_rangeStarts[inRange] << " + m_rangeDurations[inRange] " << m_rangeDurations[inRange] << " -- stopping" << std::endl; | |
714 stop(); | |
715 } | |
716 } | |
717 | |
718 if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | |
719 | |
720 size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | |
721 return m_viewManager->alignPlaybackFrameToReference(frame); | |
722 } | |
723 | |
724 void | |
725 AudioCallbackPlaySource::rebuildRangeLists() | |
726 { | |
727 bool constrained = (m_viewManager->getPlaySelectionMode()); | |
728 | |
729 m_rangeStarts.clear(); | |
730 m_rangeDurations.clear(); | |
731 | |
732 size_t sourceRate = getSourceSampleRate(); | |
733 if (sourceRate == 0) return; | |
734 | |
735 RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); | |
736 if (end == RealTime::zeroTime) return; | |
540 | 737 |
541 if (!constrained) { | 738 if (!constrained) { |
542 if (!looping && framePlaying > m_lastModelEndFrame) { | 739 m_rangeStarts.push_back(RealTime::zeroTime); |
543 framePlaying = m_lastModelEndFrame; | 740 m_rangeDurations.push_back(end); |
544 stop(); | 741 return; |
545 } | 742 } |
546 return framePlaying; | |
547 } | |
548 | |
549 bufferedFrame = m_viewManager->alignPlaybackFrameToReference(bufferedFrame); | |
550 | 743 |
551 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 744 MultiSelection::SelectionList selections = m_viewManager->getSelections(); |
552 MultiSelection::SelectionList::const_iterator i; | 745 MultiSelection::SelectionList::const_iterator i; |
553 | 746 |
554 // i = selections.begin(); | 747 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
555 // size_t rangeStart = i->getStartFrame(); | 748 std::cerr << "AudioCallbackPlaySource::rebuildRangeLists" << std::endl; |
556 | 749 #endif |
557 i = selections.end(); | 750 |
558 --i; | 751 if (!selections.empty()) { |
559 size_t rangeEnd = i->getEndFrame(); | 752 |
560 | 753 for (i = selections.begin(); i != selections.end(); ++i) { |
561 for (i = selections.begin(); i != selections.end(); ++i) { | 754 |
562 if (i->contains(bufferedFrame)) break; | 755 RealTime start = |
563 } | 756 (RealTime::frame2RealTime |
564 | 757 (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
565 size_t f = bufferedFrame; | 758 sourceRate)); |
566 | 759 RealTime duration = |
567 // std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; | 760 (RealTime::frame2RealTime |
568 | 761 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) - |
569 if (i == selections.end()) { | 762 m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), |
570 --i; | 763 sourceRate)); |
571 if (i->getEndFrame() + latency < f) { | 764 |
572 // std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; | 765 m_rangeStarts.push_back(start); |
573 | 766 m_rangeDurations.push_back(duration); |
574 if (!looping && (framePlaying > rangeEnd)) { | 767 } |
575 // std::cout << "STOPPING" << std::endl; | 768 } else { |
576 stop(); | 769 m_rangeStarts.push_back(RealTime::zeroTime); |
577 return rangeEnd; | 770 m_rangeDurations.push_back(end); |
578 } else { | 771 } |
579 return framePlaying; | 772 |
580 } | 773 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
581 } else { | 774 std::cerr << "Now have " << m_rangeStarts.size() << " play ranges" << std::endl; |
582 // std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | 775 #endif |
583 latency -= (f - i->getEndFrame()); | |
584 f = i->getEndFrame(); | |
585 } | |
586 } | |
587 | |
588 // std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | |
589 | |
590 while (latency > 0) { | |
591 size_t offset = f - i->getStartFrame(); | |
592 if (offset >= latency) { | |
593 if (f > latency) { | |
594 framePlaying = f - latency; | |
595 } else { | |
596 framePlaying = 0; | |
597 } | |
598 break; | |
599 } else { | |
600 if (i == selections.begin()) { | |
601 if (looping) { | |
602 i = selections.end(); | |
603 } | |
604 } | |
605 latency -= offset; | |
606 --i; | |
607 f = i->getEndFrame(); | |
608 } | |
609 } | |
610 | |
611 return framePlaying; | |
612 } | 776 } |
613 | 777 |
614 void | 778 void |
615 AudioCallbackPlaySource::setOutputLevels(float left, float right) | 779 AudioCallbackPlaySource::setOutputLevels(float left, float right) |
616 { | 780 { |
756 { | 920 { |
757 return m_sourceSampleRate; | 921 return m_sourceSampleRate; |
758 } | 922 } |
759 | 923 |
760 void | 924 void |
761 AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono) | 925 AudioCallbackPlaySource::setTimeStretch(float factor) |
762 { | 926 { |
763 #ifdef HAVE_RUBBERBAND | 927 m_stretchRatio = factor; |
764 if (m_timeStretcher) { | 928 |
765 m_timeStretchRatioMutex.lock(); | 929 if (m_timeStretcher || (factor == 1.f)) { |
766 m_timeStretcher->setTimeRatio(factor); | 930 // stretch ratio will be set in next process call if appropriate |
767 m_timeStretchRatioMutex.unlock(); | |
768 return; | 931 return; |
769 } else { | 932 } else { |
933 m_stretcherInputCount = getTargetChannelCount(); | |
770 RubberBandStretcher *stretcher = new RubberBandStretcher | 934 RubberBandStretcher *stretcher = new RubberBandStretcher |
771 (getTargetSampleRate(), | 935 (getTargetSampleRate(), |
772 getTargetChannelCount(), | 936 m_stretcherInputCount, |
773 RubberBandStretcher::OptionProcessRealTime, | 937 RubberBandStretcher::OptionProcessRealTime, |
774 factor); | 938 factor); |
939 m_stretcherInputs = new float *[m_stretcherInputCount]; | |
940 m_stretcherInputSizes = new size_t[m_stretcherInputCount]; | |
941 for (size_t c = 0; c < m_stretcherInputCount; ++c) { | |
942 m_stretcherInputSizes[c] = 16384; | |
943 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
944 } | |
775 m_timeStretcher = stretcher; | 945 m_timeStretcher = stretcher; |
776 return; | 946 return; |
777 } | 947 } |
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 } | 948 } |
821 | 949 |
822 size_t | 950 size_t |
823 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) | 951 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) |
824 { | 952 { |
858 } | 986 } |
859 } | 987 } |
860 | 988 |
861 if (count == 0) return 0; | 989 if (count == 0) return 0; |
862 | 990 |
863 #ifdef HAVE_RUBBERBAND | |
864 RubberBandStretcher *ts = m_timeStretcher; | 991 RubberBandStretcher *ts = m_timeStretcher; |
865 float ratio = ts ? ts->getTimeRatio() : 1.f; | 992 float ratio = ts ? ts->getTimeRatio() : 1.f; |
866 #else | 993 |
867 PhaseVocoderTimeStretcher *ts = m_timeStretcher; | 994 if (ratio != m_stretchRatio) { |
868 float ratio = ts ? ts->getRatio() : 1.f; | 995 if (!ts) { |
869 #endif | 996 std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << std::endl; |
997 m_stretchRatio = 1.f; | |
998 } else { | |
999 ts->setTimeRatio(m_stretchRatio); | |
1000 } | |
1001 } | |
1002 | |
1003 if (m_target) { | |
1004 m_lastRetrievedBlockSize = count; | |
1005 m_lastRetrievalTimestamp = m_target->getCurrentTime(); | |
1006 } | |
870 | 1007 |
871 if (!ts || ratio == 1.f) { | 1008 if (!ts || ratio == 1.f) { |
872 | 1009 |
873 size_t got = 0; | 1010 size_t got = 0; |
874 | 1011 |
898 } | 1035 } |
899 | 1036 |
900 applyAuditioningEffect(count, buffer); | 1037 applyAuditioningEffect(count, buffer); |
901 | 1038 |
902 m_condition.wakeAll(); | 1039 m_condition.wakeAll(); |
1040 | |
903 return got; | 1041 return got; |
904 } | 1042 } |
905 | 1043 |
906 size_t channels = getTargetChannelCount(); | 1044 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; | 1045 size_t available; |
915 | |
916 int warned = 0; | 1046 int warned = 0; |
917 | 1047 size_t fedToStretcher = 0; |
918 // We want output blocks of e.g. 1024 (probably fixed, certainly | 1048 |
919 // bounded). We can provide input blocks of any size (unbounded) | 1049 // The input block for a given output is approx output / ratio, |
920 // at the timestretcher's request. The input block for a given | 1050 // but we can't predict it exactly, for an adaptive timestretcher. |
921 // output is approx output / ratio, but we can't predict it | 1051 |
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) { | 1052 while ((available = ts->available()) < count) { |
929 #else | |
930 while ((available = ts->getAvailableOutputSamples()) < count) { | |
931 #endif | |
932 | 1053 |
933 size_t reqd = lrintf((count - available) / ratio); | 1054 size_t reqd = lrintf((count - available) / ratio); |
934 #ifdef HAVE_RUBBERBAND | |
935 reqd = std::max(reqd, ts->getSamplesRequired()); | 1055 reqd = std::max(reqd, ts->getSamplesRequired()); |
936 #else | |
937 reqd = std::max(reqd, ts->getRequiredInputSamples()); | |
938 #endif | |
939 if (reqd == 0) reqd = 1; | 1056 if (reqd == 0) reqd = 1; |
940 | 1057 |
941 float *ib[channels]; | |
942 | |
943 size_t got = reqd; | 1058 size_t got = reqd; |
944 | 1059 |
945 if (mix) { | 1060 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING |
946 for (size_t c = 0; c < channels; ++c) { | 1061 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 | 1062 #endif |
948 else ib[c] = 0; | 1063 |
949 RingBuffer<float> *rb = getReadRingBuffer(c); | 1064 for (size_t c = 0; c < channels; ++c) { |
950 if (rb) { | 1065 if (c >= m_stretcherInputCount) continue; |
951 size_t gotHere; | 1066 if (reqd > m_stretcherInputSizes[c]) { |
952 if (c > 0) gotHere = rb->readAdding(ib[0], got); | 1067 if (c == 0) { |
953 else gotHere = rb->read(ib[0], got); | 1068 std::cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << std::endl; |
954 if (gotHere < got) got = gotHere; | |
955 } | 1069 } |
1070 delete[] m_stretcherInputs[c]; | |
1071 m_stretcherInputSizes[c] = reqd * 2; | |
1072 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | |
956 } | 1073 } |
957 } else { | 1074 } |
958 for (size_t c = 0; c < channels; ++c) { | 1075 |
959 ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 1076 for (size_t c = 0; c < channels; ++c) { |
960 RingBuffer<float> *rb = getReadRingBuffer(c); | 1077 if (c >= m_stretcherInputCount) continue; |
961 if (rb) { | 1078 RingBuffer<float> *rb = getReadRingBuffer(c); |
962 size_t gotHere = rb->read(ib[c], got); | 1079 if (rb) { |
963 if (gotHere < got) got = gotHere; | 1080 size_t gotHere = rb->read(m_stretcherInputs[c], got); |
1081 if (gotHere < got) got = gotHere; | |
1082 | |
1083 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | |
1084 if (c == 0) { | |
1085 std::cerr << "feeding stretcher: got " << gotHere | |
1086 << ", " << rb->getReadSpace() << " remain" << std::endl; | |
964 } | 1087 } |
1088 #endif | |
1089 | |
1090 } else { | |
1091 std::cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << std::endl; | |
965 } | 1092 } |
966 } | 1093 } |
967 | 1094 |
968 if (got < reqd) { | 1095 if (got < reqd) { |
969 std::cerr << "WARNING: Read underrun in playback (" | 1096 std::cerr << "WARNING: Read underrun in playback (" |
970 << got << " < " << reqd << ")" << std::endl; | 1097 << got << " < " << reqd << ")" << std::endl; |
971 } | 1098 } |
972 | 1099 |
973 #ifdef HAVE_RUBBERBAND | 1100 ts->process(m_stretcherInputs, got, false); |
974 ts->process(ib, got, false); | 1101 |
975 #else | 1102 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 | 1103 |
983 if (got == 0) break; | 1104 if (got == 0) break; |
984 | 1105 |
985 #ifdef HAVE_RUBBERBAND | |
986 if (ts->available() == available) { | 1106 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; | 1107 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; | 1108 if (++warned == 5) break; |
992 } | 1109 } |
993 } | 1110 } |
994 | 1111 |
995 #ifdef HAVE_RUBBERBAND | |
996 ts->retrieve(buffer, count); | 1112 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 | 1113 |
1013 applyAuditioningEffect(count, buffer); | 1114 applyAuditioningEffect(count, buffer); |
1014 | 1115 |
1015 m_condition.wakeAll(); | 1116 m_condition.wakeAll(); |
1016 | 1117 |
1182 data.src_ratio = ratio; | 1283 data.src_ratio = ratio; |
1183 data.end_of_input = 0; | 1284 data.end_of_input = 0; |
1184 | 1285 |
1185 int err = 0; | 1286 int err = 0; |
1186 | 1287 |
1187 #ifdef HAVE_RUBBERBAND | |
1188 if (m_timeStretcher && m_timeStretcher->getTimeRatio() < 0.4) { | 1288 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 | 1289 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1193 std::cout << "Using crappy converter" << std::endl; | 1290 std::cout << "Using crappy converter" << std::endl; |
1194 #endif | 1291 #endif |
1195 err = src_process(m_crapConverter, &data); | 1292 err = src_process(m_crapConverter, &data); |
1196 } else { | 1293 } else { |
1225 | 1322 |
1226 } else { | 1323 } else { |
1227 | 1324 |
1228 // space must be a multiple of generatorBlockSize | 1325 // space must be a multiple of generatorBlockSize |
1229 space = (space / generatorBlockSize) * generatorBlockSize; | 1326 space = (space / generatorBlockSize) * generatorBlockSize; |
1230 if (space == 0) return false; | 1327 if (space == 0) { |
1328 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
1329 std::cout << "requested fill is less than generator block size of " | |
1330 << generatorBlockSize << ", leaving it" << std::endl; | |
1331 #endif | |
1332 return false; | |
1333 } | |
1231 | 1334 |
1232 if (tmpSize < channels * space) { | 1335 if (tmpSize < channels * space) { |
1233 delete[] tmp; | 1336 delete[] tmp; |
1234 tmp = new float[channels * space]; | 1337 tmp = new float[channels * space]; |
1235 tmpSize = channels * space; | 1338 tmpSize = channels * space; |
1511 while (!s.m_exiting) { | 1614 while (!s.m_exiting) { |
1512 | 1615 |
1513 s.unifyRingBuffers(); | 1616 s.unifyRingBuffers(); |
1514 s.m_bufferScavenger.scavenge(); | 1617 s.m_bufferScavenger.scavenge(); |
1515 s.m_pluginScavenger.scavenge(); | 1618 s.m_pluginScavenger.scavenge(); |
1516 #ifndef HAVE_RUBBERBAND | |
1517 s.m_timeStretcherScavenger.scavenge(); | |
1518 #endif | |
1519 | 1619 |
1520 if (work && s.m_playing && s.getSourceSampleRate()) { | 1620 if (work && s.m_playing && s.getSourceSampleRate()) { |
1521 | 1621 |
1522 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 1622 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
1523 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; | 1623 std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; |