# HG changeset patch # User Chris Cannam # Date 1138211188 0 # Node ID 5865094175ea5da93f6caaa96229bb118bfcddd3 # Parent 75c3ea1c3a32ae0844a6d5a1d518aaf96507e5ed * Fix update and play limits for play-selection mode when not looping * Fix playback in loop mode when no selection -- but the GUI update for this is still wrong on the flyback * Various fixes and improvements to making selections, particularly during playback * Draw selection under non-opaque non-scrollable layers, so as to improve cacheing * Show selection limits as text when drawing selection * Allow user to find missing audio files when loading session * Cross-fade selections when in play-selection mode -- mostly. We don't cross-fade on a processing block boundary, and unfortunately with short selections the selection boundary is quite likely to coincide with a block boundary. diff -r 75c3ea1c3a32 -r 5865094175ea audioio/AudioCallbackPlaySource.cpp --- a/audioio/AudioCallbackPlaySource.cpp Tue Jan 24 16:20:58 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Wed Jan 25 17:46:28 2006 +0000 @@ -35,6 +35,7 @@ m_playing(false), m_exiting(false), m_bufferedToFrame(0), + m_lastModelEndFrame(0), m_outputLeft(0.0), m_outputRight(0.0), m_slowdownCounter(0), @@ -75,6 +76,9 @@ m_mutex.lock(); m_models.insert(model); + if (model->getEndFrame() > m_lastModelEndFrame) { + m_lastModelEndFrame = model->getEndFrame(); + } bool buffersChanged = false, srChanged = false; @@ -164,6 +168,13 @@ m_sourceSampleRate = 0; } + size_t lastEnd = 0; + for (std::set::const_iterator i = m_models.begin(); + i != m_models.end(); ++i) { + if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame(); + } + m_lastModelEndFrame = lastEnd; + m_audioGenerator->removeModel(model); m_mutex.unlock(); @@ -181,6 +192,8 @@ m_converter = 0; } + m_lastModelEndFrame = 0; + m_audioGenerator->clearModels(); m_sourceSampleRate = 0; @@ -191,6 +204,22 @@ void AudioCallbackPlaySource::play(size_t startFrame) { + if (m_viewManager->getPlaySelectionMode()) { + ViewManager::SelectionList selections = m_viewManager->getSelections(); + ViewManager::SelectionList::iterator i = selections.begin(); + if (i != selections.end()) { + if (startFrame < i->getStartFrame()) { + startFrame = i->getStartFrame(); + } else { + ViewManager::SelectionList::iterator j = selections.end(); + --j; + if (startFrame >= j->getEndFrame()) { + startFrame = i->getStartFrame(); + } + } + } + } + // The fill thread will automatically empty its buffers before // starting again if we have not so far been playing, but not if // we're just re-seeking. @@ -211,6 +240,7 @@ m_playing = true; m_condition.wakeAll(); + emit playStatusChanged(m_playing); } void @@ -218,6 +248,7 @@ { m_playing = false; m_condition.wakeAll(); + emit playStatusChanged(m_playing); } void @@ -229,17 +260,13 @@ getRingBuffer(c).reset(); } m_mutex.unlock(); + m_condition.wakeAll(); } } void AudioCallbackPlaySource::playLoopModeChanged() { - m_mutex.lock(); - for (size_t c = 0; c < m_bufferCount; ++c) { - getRingBuffer(c).reset(); - } - m_mutex.unlock(); } void @@ -251,6 +278,7 @@ getRingBuffer(c).reset(); } m_mutex.unlock(); + m_condition.wakeAll(); } } @@ -317,7 +345,15 @@ size_t framePlaying = bufferedFrame; if (framePlaying > latency) framePlaying -= latency; - else framePlaying = 0; + else { + //!!! Not right + if (m_viewManager->getPlayLoopMode() && + !m_viewManager->getPlaySelectionMode()) { + framePlaying += m_lastModelEndFrame; + if (framePlaying > latency) framePlaying -= latency; + else framePlaying = 0; + } + } if (!m_viewManager->getPlaySelectionMode()) { return framePlaying; @@ -330,26 +366,41 @@ ViewManager::SelectionList::const_iterator i; + i = selections.begin(); + size_t rangeStart = i->getStartFrame(); + + i = selections.end(); + --i; + size_t rangeEnd = i->getEndFrame(); + for (i = selections.begin(); i != selections.end(); ++i) { if (i->contains(bufferedFrame)) break; } size_t f = bufferedFrame; - std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << std::endl; +// std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; if (i == selections.end()) { --i; if (i->getEndFrame() + latency < f) { - return framePlaying; +// std::cerr << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; + + if (!m_viewManager->getPlayLoopMode() && (framePlaying > rangeEnd)) { +// std::cerr << "STOPPING" << std::endl; + stop(); + return rangeEnd; + } else { + return framePlaying; + } } else { - std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; +// std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; latency -= (f - i->getEndFrame()); f = i->getEndFrame(); } } - std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; +// std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; while (latency > 0) { size_t offset = f - i->getStartFrame(); @@ -615,7 +666,7 @@ } if (space == 0) return; - + #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; #endif @@ -818,6 +869,8 @@ chunkSize = count - processed; nextChunkStart = chunkStart + chunkSize; + size_t fadeIn = 0, fadeOut = 0; + if (useSelection) { Selection selection = @@ -827,6 +880,7 @@ if (m_viewManager->getPlayLoopMode()) { selection = *m_viewManager->getSelections().begin(); chunkStart = selection.getStartFrame(); + fadeIn = 50; } } @@ -839,13 +893,29 @@ if (chunkStart < selection.getStartFrame()) { chunkStart = selection.getStartFrame(); + fadeIn = 50; } - nextChunkStart = std::min(chunkStart + chunkSize, - selection.getEndFrame()); + nextChunkStart = chunkStart + chunkSize; + + if (nextChunkStart > selection.getEndFrame()) { + nextChunkStart = selection.getEndFrame(); + fadeOut = 50; + } chunkSize = nextChunkStart - chunkStart; } + + } else if (m_viewManager->getPlayLoopMode() && + m_lastModelEndFrame > 0) { + + if (chunkStart >= m_lastModelEndFrame) { + chunkStart = 0; + } + if (chunkSize > m_lastModelEndFrame - chunkStart) { + chunkSize = m_lastModelEndFrame - chunkStart; + } + nextChunkStart = chunkStart + chunkSize; } if (!chunkSize) { @@ -865,11 +935,32 @@ size_t got = 0; + if (chunkSize < 100) { + fadeIn = 0; + fadeOut = 0; + } else if (chunkSize < 300) { + if (fadeIn > 0) fadeIn = 10; + if (fadeOut > 0) fadeOut = 10; + } + + if (fadeIn > 0) { + if (processed * 2 < fadeIn) { + fadeIn = processed * 2; + } + } + + if (fadeOut > 0) { + if ((count - processed) * 2 < fadeOut) { + fadeOut = (count - processed) * 2; + } + } + for (std::set::iterator mi = m_models.begin(); mi != m_models.end(); ++mi) { got = m_audioGenerator->mixModel(*mi, chunkStart, - chunkSize, chunkBufferPtrs); + chunkSize, chunkBufferPtrs, + fadeIn, fadeOut); } for (size_t c = 0; c < channels; ++c) { @@ -911,12 +1002,13 @@ } if (!s.m_playing) ms *= 10; + ms = ms / 8; #ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms/4 << "ms..." << std::endl; + std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl; #endif - s.m_condition.wait(&s.m_mutex, size_t(ms / 4)); + s.m_condition.wait(&s.m_mutex, size_t(ms)); #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; diff -r 75c3ea1c3a32 -r 5865094175ea audioio/AudioCallbackPlaySource.h --- a/audioio/AudioCallbackPlaySource.h Tue Jan 24 16:20:58 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Wed Jan 25 17:46:28 2006 +0000 @@ -164,6 +164,8 @@ signals: void modelReplaced(); + void playStatusChanged(bool isPlaying); + /// Just a warning void sampleRateMismatch(size_t requested, size_t available); @@ -186,6 +188,7 @@ bool m_playing; bool m_exiting; size_t m_bufferedToFrame; + size_t m_lastModelEndFrame; static const size_t m_ringBufferSize; float m_outputLeft; float m_outputRight; @@ -222,7 +225,10 @@ TimeStretcherData *m_timeStretcher; Scavenger m_timeStretcherScavenger; - void fillBuffers(); // Called from fill thread, m_playing true, mutex held + // Called from fill thread, m_playing true, mutex held + void fillBuffers(); + + // Called from fillBuffers bool mixModels(size_t &frame, size_t count, float **buffers); class AudioCallbackPlaySourceFillThread : public QThread diff -r 75c3ea1c3a32 -r 5865094175ea audioio/AudioGenerator.cpp --- a/audioio/AudioGenerator.cpp Tue Jan 24 16:20:58 2006 +0000 +++ b/audioio/AudioGenerator.cpp Wed Jan 25 17:46:28 2006 +0000 @@ -150,7 +150,7 @@ size_t AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount, - float **buffer) + float **buffer, size_t fadeIn, size_t fadeOut) { if (m_sourceSampleRate == 0) { std::cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << std::endl; @@ -169,14 +169,14 @@ DenseTimeValueModel *dtvm = dynamic_cast(model); if (dtvm) { return mixDenseTimeValueModel(dtvm, startFrame, frameCount, - buffer, gain, pan); + buffer, gain, pan, fadeIn, fadeOut); } SparseOneDimensionalModel *sodm = dynamic_cast (model); if (sodm) { return mixSparseOneDimensionalModel(sodm, startFrame, frameCount, - buffer, gain, pan); + buffer, gain, pan, fadeIn, fadeOut); } return frameCount; @@ -185,23 +185,43 @@ size_t AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm, size_t startFrame, size_t frames, - float **buffer, float gain, float pan) + float **buffer, float gain, float pan, + size_t fadeIn, size_t fadeOut) { static float *channelBuffer = 0; static size_t channelBufSiz = 0; - if (channelBufSiz < frames) { + size_t totalFrames = frames + fadeIn/2 + fadeOut/2; + + if (channelBufSiz < totalFrames) { delete[] channelBuffer; - channelBuffer = new float[frames]; - channelBufSiz = frames; + channelBuffer = new float[totalFrames]; + channelBufSiz = totalFrames; } size_t got = 0; for (size_t c = 0; c < m_targetChannelCount && c < dtvm->getChannelCount(); ++c) { - got = dtvm->getValues(c, startFrame, startFrame + frames, channelBuffer); - for (size_t i = 0; i < frames; ++i) { - buffer[c][i] += gain * channelBuffer[i]; + + got = dtvm->getValues + (c, startFrame - fadeIn/2, startFrame + frames + fadeOut/2, + channelBuffer); + + for (size_t i = 0; i < fadeIn/2; ++i) { + float *back = buffer[c]; + back -= fadeIn/2; + back[i] += (gain * channelBuffer[i] * i) / fadeIn; + } + + for (size_t i = 0; i < frames + fadeOut/2; ++i) { + float mult = gain; + if (i < fadeIn/2) { + mult = (mult * i) / fadeIn; + } + if (i > frames - fadeOut/2) { + mult = (mult * ((frames + fadeOut/2) - i)) / fadeOut; + } + buffer[c][i] += mult * channelBuffer[i]; } } @@ -211,7 +231,9 @@ size_t AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm, size_t startFrame, size_t frames, - float **buffer, float gain, float pan) + float **buffer, float gain, float pan, + size_t /* fadeIn */, + size_t /* fadeOut */) { RealTimePluginInstance *plugin = m_synthMap[sodm]; if (!plugin) return 0; diff -r 75c3ea1c3a32 -r 5865094175ea audioio/AudioGenerator.h --- a/audioio/AudioGenerator.h Tue Jan 24 16:20:58 2006 +0000 +++ b/audioio/AudioGenerator.h Wed Jan 25 17:46:28 2006 +0000 @@ -63,7 +63,7 @@ * Mix a single model into an output buffer. */ virtual size_t mixModel(Model *model, size_t startFrame, size_t frameCount, - float **buffer); + float **buffer, size_t fadeIn = 0, size_t fadeOut = 0); protected: ViewManager *m_viewManager; @@ -93,11 +93,11 @@ virtual size_t mixDenseTimeValueModel (DenseTimeValueModel *model, size_t startFrame, size_t frameCount, - float **buffer, float gain, float pan); + float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut); virtual size_t mixSparseOneDimensionalModel (SparseOneDimensionalModel *model, size_t startFrame, size_t frameCount, - float **buffer, float gain, float pan); + float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut); static const size_t m_pluginBlockSize; };