Mercurial > hg > svapp
changeset 3:75c3ea1c3a32
* Add play-selection and looping modes. Looping seems to work OK, but
the plain play-selection is miscalculating current frame number to
feed back to the GUI.
* Cache selection rectanges wherever possible in View::paintEvent.
author | Chris Cannam |
---|---|
date | Tue, 24 Jan 2006 16:20:58 +0000 |
parents | df5923e33d01 |
children | 5865094175ea |
files | audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h |
diffstat | 2 files changed, 221 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp Thu Jan 12 13:45:27 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Tue Jan 24 16:20:58 2006 +0000 @@ -47,6 +47,13 @@ while (m_buffers.size() < 20) m_buffers.push_back(0); m_viewManager->setAudioPlaySource(this); + + connect(m_viewManager, SIGNAL(selectionChanged()), + this, SLOT(selectionChanged())); + connect(m_viewManager, SIGNAL(playLoopModeChanged()), + this, SLOT(playLoopModeChanged())); + connect(m_viewManager, SIGNAL(playSelectionModeChanged()), + this, SLOT(playSelectionModeChanged())); } AudioCallbackPlaySource::~AudioCallbackPlaySource() @@ -214,6 +221,40 @@ } void +AudioCallbackPlaySource::selectionChanged() +{ + if (m_viewManager->getPlaySelectionMode()) { + m_mutex.lock(); + for (size_t c = 0; c < m_bufferCount; ++c) { + getRingBuffer(c).reset(); + } + m_mutex.unlock(); + } +} + +void +AudioCallbackPlaySource::playLoopModeChanged() +{ + m_mutex.lock(); + for (size_t c = 0; c < m_bufferCount; ++c) { + getRingBuffer(c).reset(); + } + m_mutex.unlock(); +} + +void +AudioCallbackPlaySource::playSelectionModeChanged() +{ + if (!m_viewManager->getSelections().empty()) { + m_mutex.lock(); + for (size_t c = 0; c < m_bufferCount; ++c) { + getRingBuffer(c).reset(); + } + m_mutex.unlock(); + } +} + +void AudioCallbackPlaySource::setTargetBlockSize(size_t size) { std::cerr << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; @@ -263,13 +304,6 @@ readSpace = size_t(readSpace * ratio + 0.1); } - size_t lastRequestedFrame = 0; - if (m_bufferedToFrame > readSpace) { - lastRequestedFrame = m_bufferedToFrame - readSpace; - } - - size_t framePlaying = lastRequestedFrame; - size_t latency = m_playLatency; if (resample) latency = size_t(m_playLatency * ratio + 0.1); @@ -278,15 +312,65 @@ latency += timeStretcher->getStretcher(0)->getProcessingLatency(); } - if (framePlaying > latency) { - framePlaying = framePlaying - latency; - } else { - framePlaying = 0; + latency += readSpace; + size_t bufferedFrame = m_bufferedToFrame; + + size_t framePlaying = bufferedFrame; + if (framePlaying > latency) framePlaying -= latency; + else framePlaying = 0; + + if (!m_viewManager->getPlaySelectionMode()) { + return framePlaying; } -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "getCurrentPlayingFrame: readSpace " << readSpace << ", lastRequestedFrame " << lastRequestedFrame << ", framePlaying " << framePlaying << ", latency " << latency << std::endl; -#endif + ViewManager::SelectionList selections = m_viewManager->getSelections(); + if (selections.empty()) { + return framePlaying; + } + + ViewManager::SelectionList::const_iterator i; + + 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; + + if (i == selections.end()) { + --i; + if (i->getEndFrame() + latency < f) { + return framePlaying; + } else { + 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; + + while (latency > 0) { + size_t offset = f - i->getStartFrame(); + if (offset >= latency) { + if (f > latency) { + framePlaying = f - latency; + } else { + framePlaying = 0; + } + break; + } else { + if (i == selections.begin()) { + if (m_viewManager->getPlayLoopMode()) { + i = selections.end(); + } + } + latency -= offset; + --i; + f = i->getEndFrame(); + } + } return framePlaying; } @@ -610,18 +694,12 @@ } } - for (std::set<Model *>::iterator mi = m_models.begin(); - mi != m_models.end(); ++mi) { + for (size_t c = 0; c < channels; ++c) { + bufferPtrs[c] = nonintlv + c * orig; + } - for (size_t c = 0; c < channels; ++c) { - bufferPtrs[c] = nonintlv + c * orig; - } - - size_t gotHere = m_audioGenerator->mixModel - (*mi, f, orig, bufferPtrs); - - got = std::max(got, gotHere); - } + bool ended = !mixModels(f, orig, bufferPtrs); + got = orig; // and interleave into first half for (size_t c = 0; c < channels; ++c) { @@ -664,6 +742,8 @@ } getRingBuffer(c).write(tmp, toCopy); } + + m_bufferedToFrame = f; } else { @@ -680,22 +760,17 @@ for (size_t c = 0; c < channels; ++c) { bufferPtrs[c] = tmp + c * space; - + for (size_t i = 0; i < space; ++i) { tmp[c * space + i] = 0.0f; } } - for (std::set<Model *>::iterator mi = m_models.begin(); - mi != m_models.end(); ++mi) { - - got = m_audioGenerator->mixModel - (*mi, f, space, bufferPtrs); - } + bool ended = !mixModels(f, space, bufferPtrs); for (size_t c = 0; c < channels; ++c) { - got = getRingBuffer(c).write(bufferPtrs[c], space); + getRingBuffer(c).write(bufferPtrs[c], space); #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cerr << "Wrote " << got << " frames for ch " << c << ", now " @@ -703,10 +778,115 @@ << std::endl; #endif } + + m_bufferedToFrame = f; + //!!! how do we know when ended? need to mark up a fully-buffered flag and check this if we find the buffers empty in getSourceSamples } +} + +bool +AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers) +{ + size_t processed = 0; + size_t chunkStart = frame; + size_t chunkSize = count; + size_t nextChunkStart = chunkStart + chunkSize; - m_bufferedToFrame = f + got; -} + bool useSelection = (m_viewManager->getPlaySelectionMode() && + !m_viewManager->getSelections().empty()); + + static float **chunkBufferPtrs = 0; + static size_t chunkBufferPtrCount = 0; + size_t channels = getSourceChannelCount(); + +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; +#endif + + if (chunkBufferPtrCount < channels) { + if (chunkBufferPtrs) delete[] chunkBufferPtrs; + chunkBufferPtrs = new float *[channels]; + chunkBufferPtrCount = channels; + } + + for (size_t c = 0; c < channels; ++c) { + chunkBufferPtrs[c] = buffers[c]; + } + + while (processed < count) { + + chunkSize = count - processed; + nextChunkStart = chunkStart + chunkSize; + + if (useSelection) { + + Selection selection = + m_viewManager->getContainingSelection(chunkStart, true); + + if (selection.isEmpty()) { + if (m_viewManager->getPlayLoopMode()) { + selection = *m_viewManager->getSelections().begin(); + chunkStart = selection.getStartFrame(); + } + } + + if (selection.isEmpty()) { + + chunkSize = 0; + nextChunkStart = chunkStart; + + } else { + + if (chunkStart < selection.getStartFrame()) { + chunkStart = selection.getStartFrame(); + } + + nextChunkStart = std::min(chunkStart + chunkSize, + selection.getEndFrame()); + + chunkSize = nextChunkStart - chunkStart; + } + } + + if (!chunkSize) { +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "Ending selection playback at " << nextChunkStart << std::endl; +#endif + // We need to maintain full buffers so that the other + // thread can tell where it's got to in the playback -- so + // return the full amount here + frame = frame + count; + return false; + } + +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; +#endif + + size_t got = 0; + + for (std::set<Model *>::iterator mi = m_models.begin(); + mi != m_models.end(); ++mi) { + + got = m_audioGenerator->mixModel(*mi, chunkStart, + chunkSize, chunkBufferPtrs); + } + + for (size_t c = 0; c < channels; ++c) { + chunkBufferPtrs[c] += chunkSize; + } + + processed += chunkSize; + chunkStart = nextChunkStart; + } + +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "Returning selection playback at " << nextChunkStart << std::endl; +#endif + + frame = nextChunkStart; + return true; +} void AudioCallbackPlaySource::AudioCallbackPlaySourceFillThread::run()
--- a/audioio/AudioCallbackPlaySource.h Thu Jan 12 13:45:27 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Tue Jan 24 16:20:58 2006 +0000 @@ -167,6 +167,11 @@ /// Just a warning void sampleRateMismatch(size_t requested, size_t available); +protected slots: + void selectionChanged(); + void playLoopModeChanged(); + void playSelectionModeChanged(); + protected: ViewManager *m_viewManager; AudioGenerator *m_audioGenerator; @@ -218,6 +223,7 @@ Scavenger<TimeStretcherData> m_timeStretcherScavenger; void fillBuffers(); // Called from fill thread, m_playing true, mutex held + bool mixModels(size_t &frame, size_t count, float **buffers); class AudioCallbackPlaySourceFillThread : public QThread {