Mercurial > hg > svapp
changeset 13:89bb89894ad6
* As previous commit
author | Chris Cannam |
---|---|
date | Fri, 17 Feb 2006 18:11:08 +0000 |
parents | 29b38a641d43 |
children | 0f7997490bae |
files | audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/AudioGenerator.cpp audioio/AudioGenerator.h audioio/AudioJACKTarget.cpp |
diffstat | 5 files changed, 225 insertions(+), 57 deletions(-) [+] |
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp Fri Feb 17 18:04:26 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Fri Feb 17 18:11:08 2006 +0000 @@ -22,6 +22,7 @@ #include <cassert> //#define DEBUG_AUDIO_PLAY_SOURCE 1 +//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 //const size_t AudioCallbackPlaySource::m_ringBufferSize = 102400; const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071; @@ -31,6 +32,9 @@ m_audioGenerator(new AudioGenerator(manager)), m_readBuffers(0), m_writeBuffers(0), + m_readBufferFill(0), + m_writeBufferFill(0), + m_bufferScavenger(1), m_sourceChannelCount(0), m_blockSize(1024), m_sourceSampleRate(0), @@ -38,7 +42,6 @@ m_playLatency(0), m_playing(false), m_exiting(false), - m_bufferedToFrame(0), m_lastModelEndFrame(0), m_outputLeft(0.0), m_outputRight(0.0), @@ -119,12 +122,8 @@ std::cerr << "Adding model with " << modelChannels << " channels " << std::endl; - if (!m_writeBuffers || m_writeBuffers->size() < modelChannels) { - m_audioGenerator->setTargetChannelCount(modelChannels); - } - - if (!m_writeBuffers || (m_writeBuffers->size() < modelChannels)) { - clearRingBuffers(true, modelChannels); + if (!m_writeBuffers || (m_writeBuffers->size() < getTargetChannelCount())) { + clearRingBuffers(true, getTargetChannelCount()); buffersChanged = true; } else { if (canPlay) clearRingBuffers(true); @@ -139,6 +138,8 @@ m_mutex.unlock(); + m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); + if (!m_fillThread) { m_fillThread = new AudioCallbackPlaySourceFillThread(*this); m_fillThread->start(); @@ -214,6 +215,19 @@ if (m_writeBuffers) count = m_writeBuffers->size(); } + size_t sf = m_readBufferFill; + RingBuffer<float> *rb = getReadRingBuffer(0); + if (rb) { + //!!! This is incorrect if we're in a non-contiguous selection + //Same goes for all related code (subtracting the read space + //from the fill frame to try to establish where the effective + //pre-resample/timestretch read pointer is) + size_t rs = rb->getReadSpace(); + if (rs < sf) sf -= rs; + else sf = 0; + } + m_writeBufferFill = sf; + if (m_readBuffers != m_writeBuffers) { delete m_writeBuffers; } @@ -263,9 +277,9 @@ m_mutex.lock(); if (m_playing) { - m_bufferedToFrame = startFrame; + m_readBufferFill = m_writeBufferFill = startFrame; if (m_readBuffers) { - for (size_t c = 0; c < getSourceChannelCount(); ++c) { + for (size_t c = 0; c < getTargetChannelCount(); ++c) { RingBuffer<float> *rb = getReadRingBuffer(c); if (rb) rb->reset(); } @@ -273,7 +287,7 @@ if (m_converter) src_reset(m_converter); } else { if (m_converter) src_reset(m_converter); - m_bufferedToFrame = startFrame; + m_readBufferFill = m_writeBufferFill = startFrame; } m_mutex.unlock(); @@ -359,7 +373,7 @@ } size_t readSpace = 0; - for (size_t c = 0; c < getSourceChannelCount(); ++c) { + for (size_t c = 0; c < getTargetChannelCount(); ++c) { RingBuffer<float> *rb = getReadRingBuffer(c); if (rb) { size_t spaceHere = rb->getReadSpace(); @@ -380,7 +394,7 @@ } latency += readSpace; - size_t bufferedFrame = m_bufferedToFrame; + size_t bufferedFrame = m_readBufferFill; bool looping = m_viewManager->getPlayLoopMode(); bool constrained = (m_viewManager->getPlaySelectionMode() && @@ -489,7 +503,8 @@ if (getSourceSampleRate() != getTargetSampleRate()) { int err = 0; - m_converter = src_new(SRC_SINC_BEST_QUALITY, m_sourceChannelCount, &err); + m_converter = src_new(SRC_SINC_BEST_QUALITY, + getTargetChannelCount(), &err); if (!m_converter) { std::cerr << "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: " @@ -514,6 +529,13 @@ } size_t +AudioCallbackPlaySource::getTargetChannelCount() const +{ + if (m_sourceChannelCount < 2) return 2; + return m_sourceChannelCount; +} + +size_t AudioCallbackPlaySource::getSourceSampleRate() const { return m_sourceSampleRate; @@ -590,7 +612,7 @@ if (factor > 1) { TimeStretcherData *newStretcher = new TimeStretcherData - (getSourceChannelCount(), factor, getTargetBlockSize()); + (getTargetChannelCount(), factor, getTargetBlockSize()); m_slowdownCounter = 0; m_timeStretcher = newStretcher; } else { @@ -606,7 +628,7 @@ AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) { if (!m_playing) { - for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) { + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { for (size_t i = 0; i < count; ++i) { buffer[ch][i] = 0.0; } @@ -620,7 +642,7 @@ size_t got = 0; - for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) { + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { RingBuffer<float> *rb = getReadRingBuffer(ch); @@ -638,7 +660,7 @@ #endif } - for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) { + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { for (size_t i = got; i < count; ++i) { buffer[ch][i] = 0.0; } @@ -654,7 +676,7 @@ size_t got = 0; double *ib = timeStretcher->getInputBuffer(); - for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) { + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { RingBuffer<float> *rb = getReadRingBuffer(ch); @@ -682,7 +704,7 @@ m_slowdownCounter = 0; } - for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) { + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { double *ob = timeStretcher->getOutputBuffer(ch); @@ -708,7 +730,7 @@ static size_t tmpSize = 0; size_t space = 0; - for (size_t c = 0; c < getSourceChannelCount(); ++c) { + for (size_t c = 0; c < getTargetChannelCount(); ++c) { RingBuffer<float> *wb = getWriteRingBuffer(c); if (wb) { size_t spaceHere = wb->getWriteSpace(); @@ -718,12 +740,14 @@ if (space == 0) return false; + size_t f = m_writeBufferFill; + + bool readWriteEqual = (m_readBuffers == m_writeBuffers); + #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; #endif - size_t f = m_bufferedToFrame; - #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "buffered to " << f << " already" << std::endl; #endif @@ -734,7 +758,8 @@ std::cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << std::endl; #endif - size_t channels = getSourceChannelCount(); + size_t channels = getTargetChannelCount(); + size_t orig = space; size_t got = 0; @@ -843,8 +868,9 @@ if (wb) wb->write(tmp, toCopy); } - m_bufferedToFrame = f; - + m_writeBufferFill = f; + if (readWriteEqual) m_readBufferFill = f; + } else { // space must be a multiple of generatorBlockSize @@ -881,7 +907,9 @@ #endif } - m_bufferedToFrame = f; + m_writeBufferFill = f; + if (readWriteEqual) m_readBufferFill = 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 } @@ -903,7 +931,7 @@ static float **chunkBufferPtrs = 0; static size_t chunkBufferPtrCount = 0; - size_t channels = getSourceChannelCount(); + size_t channels = getTargetChannelCount(); #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cerr << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; @@ -1041,6 +1069,68 @@ } void +AudioCallbackPlaySource::unifyRingBuffers() +{ + if (m_readBuffers == m_writeBuffers) return; + + // only unify if there will be something to read + for (size_t c = 0; c < getTargetChannelCount(); ++c) { + RingBuffer<float> *wb = getWriteRingBuffer(c); + if (wb) { + if (wb->getReadSpace() < m_blockSize * 2) { + if ((m_writeBufferFill + m_blockSize * 2) < + m_lastModelEndFrame) { + // OK, we don't have enough and there's more to + // read -- don't unify until we can do better + return; + } + } + break; + } + } + + size_t rf = m_readBufferFill; + RingBuffer<float> *rb = getReadRingBuffer(0); + if (rb) { + size_t rs = rb->getReadSpace(); + //!!! incorrect when in non-contiguous selection, see comments elsewhere +// std::cerr << "rs = " << rs << std::endl; + if (rs < rf) rf -= rs; + else rf = 0; + } + + //std::cerr << "m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << std::endl; + + size_t wf = m_writeBufferFill; + size_t skip = 0; + for (size_t c = 0; c < getTargetChannelCount(); ++c) { + RingBuffer<float> *wb = getWriteRingBuffer(c); + if (wb) { + if (c == 0) { + + size_t wrs = wb->getReadSpace(); +// std::cerr << "wrs = " << wrs << std::endl; + + if (wrs < wf) wf -= wrs; + else wf = 0; +// std::cerr << "wf = " << wf << std::endl; + + if (wf < rf) skip = rf - wf; + if (skip == 0) break; + } + +// std::cerr << "skipping " << skip << std::endl; + wb->skip(skip); + } + } + + m_bufferScavenger.claim(m_readBuffers); + m_readBuffers = m_writeBuffers; + m_readBufferFill = m_writeBufferFill; + std::cerr << "unified" << std::endl; +} + +void AudioCallbackPlaySource::AudioCallbackPlaySourceFillThread::run() { AudioCallbackPlaySource &s(m_source); @@ -1056,12 +1146,7 @@ while (!s.m_exiting) { - if (s.m_readBuffers != s.m_writeBuffers) { - s.m_bufferScavenger.claim(s.m_readBuffers); - s.m_readBuffers = s.m_writeBuffers; - std::cerr << "unified" << std::endl; - } - + s.unifyRingBuffers(); s.m_bufferScavenger.scavenge(); s.m_timeStretcherScavenger.scavenge(); @@ -1100,7 +1185,7 @@ #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl; #endif - for (size_t c = 0; c < s.getSourceChannelCount(); ++c) { + for (size_t c = 0; c < s.getTargetChannelCount(); ++c) { RingBuffer<float> *rb = s.getReadRingBuffer(c); if (rb) rb->reset(); }
--- a/audioio/AudioCallbackPlaySource.h Fri Feb 17 18:04:26 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Fri Feb 17 18:11:08 2006 +0000 @@ -140,13 +140,23 @@ virtual bool getOutputLevels(float &left, float &right); /** - * Get the number of channels of audio that will be available. + * Get the number of channels of audio that in the source models. * This may safely be called from a realtime thread. Returns 0 if * there is no source yet available. */ size_t getSourceChannelCount() const; /** + * Get the number of channels of audio that will be provided + * to the play target. This may be more than the source channel + * count: for example, a mono source will provide 2 channels + * after pan. + * This may safely be called from a realtime thread. Returns 0 if + * there is no source yet available. + */ + size_t getTargetChannelCount() const; + + /** * Get the actual sample rate of the source material. This may * safely be called from a realtime thread. Returns 0 if there is * no source yet available. @@ -193,6 +203,8 @@ std::set<Model *> m_models; RingBufferVector *m_readBuffers; RingBufferVector *m_writeBuffers; + size_t m_readBufferFill; + size_t m_writeBufferFill; Scavenger<RingBufferVector> m_bufferScavenger; size_t m_sourceChannelCount; size_t m_blockSize; @@ -201,7 +213,6 @@ size_t m_playLatency; bool m_playing; bool m_exiting; - size_t m_bufferedToFrame; size_t m_lastModelEndFrame; static const size_t m_ringBufferSize; float m_outputLeft; @@ -225,6 +236,7 @@ } void clearRingBuffers(bool haveLock = false, size_t count = 0); + void unifyRingBuffers(); class TimeStretcherData {
--- a/audioio/AudioGenerator.cpp Fri Feb 17 18:04:26 2006 +0000 +++ b/audioio/AudioGenerator.cpp Fri Feb 17 18:11:08 2006 +0000 @@ -41,6 +41,18 @@ } bool +AudioGenerator::canPlay(const Model *model) +{ + if (dynamic_cast<const DenseTimeValueModel *>(model) || + dynamic_cast<const SparseOneDimensionalModel *>(model) || + dynamic_cast<const NoteModel *>(model)) { + return true; + } else { + return false; + } +} + +bool AudioGenerator::addModel(Model *model) { if (m_sourceSampleRate == 0) { @@ -176,6 +188,10 @@ void AudioGenerator::setTargetChannelCount(size_t targetChannelCount) { + if (m_targetChannelCount == targetChannelCount) return; + + std::cerr << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << std::endl; + QMutexLocker locker(&m_mutex); m_targetChannelCount = targetChannelCount; @@ -251,28 +267,47 @@ } size_t got = 0; + size_t prevChannel = 999; - for (size_t c = 0; c < m_targetChannelCount && c < dtvm->getChannelCount(); ++c) { + for (size_t c = 0; c < m_targetChannelCount; ++c) { - if (startFrame >= fadeIn/2) { - got = dtvm->getValues - (c, startFrame - fadeIn/2, startFrame + frames + fadeOut/2, - channelBuffer); - } else { - size_t missing = fadeIn/2 - startFrame; - got = dtvm->getValues - (c, 0, startFrame + frames + fadeOut/2, - channelBuffer + missing); - } + size_t sourceChannel = (c % dtvm->getChannelCount()); + +// std::cerr << "mixing channel " << c << " from source channel " << sourceChannel << std::endl; + + float channelGain = gain; + if (pan != 0.0) { + if (c == 0) { + if (pan > 0.0) channelGain *= 1.0 - pan; + } else { + if (pan < 0.0) channelGain *= pan + 1.0; + } + } + + if (prevChannel != sourceChannel) { + if (startFrame >= fadeIn/2) { + got = dtvm->getValues + (sourceChannel, + startFrame - fadeIn/2, startFrame + frames + fadeOut/2, + channelBuffer); + } else { + size_t missing = fadeIn/2 - startFrame; + got = dtvm->getValues + (sourceChannel, + 0, startFrame + frames + fadeOut/2, + channelBuffer + missing); + } + } + prevChannel = sourceChannel; for (size_t i = 0; i < fadeIn/2; ++i) { float *back = buffer[c]; back -= fadeIn/2; - back[i] += (gain * channelBuffer[i] * i) / fadeIn; + back[i] += (channelGain * channelBuffer[i] * i) / fadeIn; } for (size_t i = 0; i < frames + fadeOut/2; ++i) { - float mult = gain; + float mult = channelGain; if (i < fadeIn/2) { mult = (mult * i) / fadeIn; } @@ -402,13 +437,25 @@ plugin->run(blockTime); float **outs = plugin->getAudioOutputBuffers(); - for (size_t c = 0; c < m_targetChannelCount && c < plugin->getAudioOutputCount(); ++c) { + for (size_t c = 0; c < m_targetChannelCount; ++c) { #ifdef DEBUG_AUDIO_GENERATOR std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; #endif + size_t sourceChannel = (c % plugin->getAudioOutputCount()); + + float channelGain = gain; + if (pan != 0.0) { + if (c == 0) { + if (pan > 0.0) channelGain *= 1.0 - pan; + } else { + if (pan < 0.0) channelGain *= pan + 1.0; + } + } + for (size_t j = 0; j < m_pluginBlockSize; ++j) { - buffer[c][i * m_pluginBlockSize + j] += gain * outs[c][j]; + buffer[c][i * m_pluginBlockSize + j] += + channelGain * outs[sourceChannel][j]; } } } @@ -536,13 +583,25 @@ plugin->run(blockTime); float **outs = plugin->getAudioOutputBuffers(); - for (size_t c = 0; c < m_targetChannelCount && c < plugin->getAudioOutputCount(); ++c) { + for (size_t c = 0; c < m_targetChannelCount; ++c) { #ifdef DEBUG_AUDIO_GENERATOR std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; #endif + size_t sourceChannel = (c % plugin->getAudioOutputCount()); + + float channelGain = gain; + if (pan != 0.0) { + if (c == 0) { + if (pan > 0.0) channelGain *= 1.0 - pan; + } else { + if (pan < 0.0) channelGain *= pan + 1.0; + } + } + for (size_t j = 0; j < m_pluginBlockSize; ++j) { - buffer[c][i * m_pluginBlockSize + j] += gain * outs[c][j]; + buffer[c][i * m_pluginBlockSize + j] += + channelGain * outs[sourceChannel][j]; } } }
--- a/audioio/AudioGenerator.h Fri Feb 17 18:04:26 2006 +0000 +++ b/audioio/AudioGenerator.h Fri Feb 17 18:11:08 2006 +0000 @@ -29,10 +29,19 @@ virtual ~AudioGenerator(); /** - * Add a data model to be played from and initialise any - * necessary audio generation code. - * Returns true if the model is of a type that we know how to play. - * (The model will be added regardless.) + * Return true if the given model is of a type that we generally + * know how to play. This doesn't guarantee that a specific + * AudioGenerator will actually produce sounds for it (for + * example, it may turn out that a vital plugin is missing). + */ + static bool canPlay(const Model *model); + + /** + * Add a data model to be played from and initialise any necessary + * audio generation code. Returns true if the model will be + * played. (The return value test here is stricter than that for + * canPlay, above.) The model will be added regardless of the + * return value. */ virtual bool addModel(Model *model);
--- a/audioio/AudioJACKTarget.cpp Fri Feb 17 18:04:26 2006 +0000 +++ b/audioio/AudioJACKTarget.cpp Fri Feb 17 18:11:08 2006 +0000 @@ -83,6 +83,9 @@ size_t channels = m_source->getSourceChannelCount(); + // Because we offer pan, we always want at least 2 channels + if (channels < 2) channels = 2; + if (channels == m_outputs.size() || !m_client) { m_mutex.unlock(); return;