Mercurial > hg > svapp
changeset 550:c732251237b1 bqresample
Merge from branch 3.0-integration
author | Chris Cannam |
---|---|
date | Wed, 07 Dec 2016 12:04:41 +0000 |
parents | ec189ad4d38f (current diff) 4de547a5905c (diff) |
children | b9d8c7a690d6 |
files | audio/AudioCallbackPlaySource.cpp audio/AudioCallbackPlaySource.h |
diffstat | 4 files changed, 71 insertions(+), 208 deletions(-) [+] |
line wrap: on
line diff
--- a/audio/AudioCallbackPlaySource.cpp Wed Dec 07 11:52:26 2016 +0000 +++ b/audio/AudioCallbackPlaySource.cpp Wed Dec 07 12:04:41 2016 +0000 @@ -77,8 +77,7 @@ m_stretcherInputCount(0), m_stretcherInputs(0), m_stretcherInputSizes(0), - m_fillThread(0), - m_resampler(0) + m_fillThread(0) { m_viewManager->setAudioPlaySource(this); @@ -230,20 +229,19 @@ } if (buffersChanged || srChanged) { - if (m_resampler) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cerr << "AudioCallbackPlaySource::addModel: Buffers or sample rate changed, deleting existing resampler" << endl; -#endif - delete m_resampler; - m_resampler = 0; - } + + // There are more channels than there were before, or the + // source sample rate has changed + + //!!! + } rebuildRangeLists(); m_mutex.unlock(); - initialiseResampler(); + //!!! m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); @@ -301,13 +299,6 @@ m_models.erase(model); if (m_models.empty()) { - if (m_resampler) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cerr << "AudioCallbackPlaySource::removeModel: No models left, deleting resampler" << endl; -#endif - delete m_resampler; - m_resampler = 0; - } m_sourceSampleRate = 0; } @@ -344,14 +335,6 @@ m_models.clear(); - if (m_resampler) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cerr << "AudioCallbackPlaySource::clearModels: Deleting resampler" << endl; -#endif - delete m_resampler; - m_resampler = 0; - } - m_lastModelEndFrame = 0; m_sourceSampleRate = 0; @@ -472,9 +455,6 @@ if (rb) rb->reset(); } } - if (m_resampler) { - m_resampler->reset(); - } m_mutex.unlock(); @@ -947,7 +927,6 @@ bool first = (m_targetSampleRate == 0); m_targetSampleRate = sr; - initialiseResampler(); if (first && (m_stretchRatio != 1.f)) { // couldn't create a stretcher before because we had no sample @@ -957,34 +936,8 @@ } void -AudioCallbackPlaySource::initialiseResampler() +AudioCallbackPlaySource::setSystemPlaybackChannelCount(int c) { - m_mutex.lock(); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cerr << "AudioCallbackPlaySource::initialiseResampler(): from " - << getSourceSampleRate() << " to " << getTargetSampleRate() << endl; -#endif - - if (m_resampler) { - delete m_resampler; - m_resampler = 0; - } - - if (getSourceSampleRate() != getTargetSampleRate()) { - - m_resampler = new breakfastquay::Resampler - (breakfastquay::Resampler::FastestTolerable, - getTargetChannelCount()); - - m_mutex.unlock(); - - emit sampleRateMismatch(getSourceSampleRate(), - getTargetSampleRate(), - true); - } else { - m_mutex.unlock(); - } } void @@ -1373,16 +1326,9 @@ cout << "buffered to " << f << " already" << endl; #endif - bool resample = (getSourceSampleRate() != getTargetSampleRate()); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << endl; -#endif - int channels = getTargetChannelCount(); sv_frame_t orig = space; - sv_frame_t got = 0; static float **bufferPtrs = 0; static int bufferPtrCount = 0; @@ -1395,160 +1341,61 @@ sv_frame_t generatorBlockSize = m_audioGenerator->getBlockSize(); - if (resample && !m_resampler) { - throw std::logic_error("Sample rates differ, but no resampler available!"); + // space must be a multiple of generatorBlockSize + sv_frame_t reqSpace = space; + space = (reqSpace / generatorBlockSize) * generatorBlockSize; + if (space == 0) { +#ifdef DEBUG_AUDIO_PLAY_SOURCE + cout << "requested fill of " << reqSpace + << " is less than generator block size of " + << generatorBlockSize << ", leaving it" << endl; +#endif + return false; } - if (resample && m_resampler) { + if (tmpSize < channels * space) { + delete[] tmp; + tmp = new float[channels * space]; + tmpSize = channels * space; + } - double ratio = - double(getTargetSampleRate()) / double(getSourceSampleRate()); - orig = sv_frame_t(double(orig) / ratio + 0.1); + for (int c = 0; c < channels; ++c) { - // orig must be a multiple of generatorBlockSize - orig = (orig / generatorBlockSize) * generatorBlockSize; - if (orig == 0) return false; + bufferPtrs[c] = tmp + c * space; + + for (int i = 0; i < space; ++i) { + tmp[c * space + i] = 0.0f; + } + } - sv_frame_t work = std::max(orig, space); + sv_frame_t got = mixModels(f, space, bufferPtrs); // also modifies f - // We only allocate one buffer, but we use it in two halves. - // We place the non-interleaved values in the second half of - // the buffer (orig samples for channel 0, orig samples for - // channel 1 etc), and then interleave them into the first - // half of the buffer. Then we resample back into the second - // half (interleaved) and de-interleave the results back to - // the start of the buffer for insertion into the ringbuffers. - // What a faff -- especially as we've already de-interleaved - // the audio data from the source file elsewhere before we - // even reach this point. - - if (tmpSize < channels * work * 2) { - delete[] tmp; - tmp = new float[channels * work * 2]; - tmpSize = channels * work * 2; - } + for (int c = 0; c < channels; ++c) { - float *nonintlv = tmp + channels * work; - float *intlv = tmp; - float *srcout = tmp + channels * work; - - for (int c = 0; c < channels; ++c) { - for (int i = 0; i < orig; ++i) { - nonintlv[channels * i + c] = 0.0f; - } - } + RingBuffer<float> *wb = getWriteRingBuffer(c); + if (wb) { + int actual = wb->write(bufferPtrs[c], int(got)); +#ifdef DEBUG_AUDIO_PLAY_SOURCE + cout << "Wrote " << actual << " samples for ch " << c << ", now " + << wb->getReadSpace() << " to read" + << endl; +#endif + if (actual < got) { + cerr << "WARNING: Buffer overrun in channel " << c + << ": wrote " << actual << " of " << got + << " samples" << endl; + } + } + } - for (int c = 0; c < channels; ++c) { - bufferPtrs[c] = nonintlv + c * orig; - } - - got = mixModels(f, orig, bufferPtrs); // also modifies f - - // and interleave into first half - for (int c = 0; c < channels; ++c) { - for (int i = 0; i < got; ++i) { - float sample = nonintlv[c * got + i]; - intlv[channels * i + c] = sample; - } - } - - sv_frame_t toCopy = m_resampler->resampleInterleaved - (intlv, srcout, got, ratio, false); - - SRC_DATA data; - data.data_in = intlv; - data.data_out = srcout; - data.input_frames = long(got); - data.output_frames = long(work); - data.src_ratio = ratio; - data.end_of_input = 0; - - int err = src_process(m_converter, &data); - - sv_frame_t toCopy = sv_frame_t(double(got) * ratio + 0.1); - - if (err) { - cerr - << "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: " - << src_strerror(err) << endl; - //!!! Then what? - } else { - got = data.input_frames_used; - toCopy = data.output_frames_gen; -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "Resampled " << got << " frames to " << toCopy << " frames" << endl; -#endif - } - - for (int c = 0; c < channels; ++c) { - for (int i = 0; i < toCopy; ++i) { - tmp[i] = srcout[channels * i + c]; - } - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) wb->write(tmp, int(toCopy)); - } - - m_writeBufferFill = f; - if (readWriteEqual) m_readBufferFill = f; - - } else { - - // space must be a multiple of generatorBlockSize - sv_frame_t reqSpace = space; - space = (reqSpace / generatorBlockSize) * generatorBlockSize; - if (space == 0) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "requested fill of " << reqSpace - << " is less than generator block size of " - << generatorBlockSize << ", leaving it" << endl; -#endif - return false; - } - - if (tmpSize < channels * space) { - delete[] tmp; - tmp = new float[channels * space]; - tmpSize = channels * space; - } - - for (int c = 0; c < channels; ++c) { - - bufferPtrs[c] = tmp + c * space; - - for (int i = 0; i < space; ++i) { - tmp[c * space + i] = 0.0f; - } - } - - sv_frame_t got = mixModels(f, space, bufferPtrs); // also modifies f - - for (int c = 0; c < channels; ++c) { - - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) { - int actual = wb->write(bufferPtrs[c], int(got)); -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "Wrote " << actual << " samples for ch " << c << ", now " - << wb->getReadSpace() << " to read" - << endl; -#endif - if (actual < got) { - cerr << "WARNING: Buffer overrun in channel " << c - << ": wrote " << actual << " of " << got - << " samples" << endl; - } - } - } - - m_writeBufferFill = f; - if (readWriteEqual) m_readBufferFill = f; + m_writeBufferFill = f; + if (readWriteEqual) m_readBufferFill = f; #ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "Read buffer fill is now " << m_readBufferFill << endl; + cout << "Read buffer fill is now " << m_readBufferFill << endl; #endif - //!!! 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 - } + //!!! 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 return true; }
--- a/audio/AudioCallbackPlaySource.h Wed Dec 07 11:52:26 2016 +0000 +++ b/audio/AudioCallbackPlaySource.h Wed Dec 07 12:04:41 2016 +0000 @@ -168,6 +168,13 @@ virtual sv_samplerate_t getTargetSampleRate() const; /** + * Indicate how many channels the target audio device was opened + * with. Note that the target device does channel mixing in the + * case where our requested channel count does not match its. + */ + void setSystemPlaybackChannelCount(int); + + /** * Set the current output levels for metering (for call from the * target) */ @@ -393,8 +400,6 @@ QMutex m_mutex; QWaitCondition m_condition; FillThread *m_fillThread; - breakfastquay::Resampler *m_resampler; - void initialiseResampler(); }; #endif
--- a/audio/AudioRecordTarget.cpp Wed Dec 07 11:52:26 2016 +0000 +++ b/audio/AudioRecordTarget.cpp Wed Dec 07 12:04:41 2016 +0000 @@ -27,6 +27,7 @@ m_clientName(clientName.toUtf8().data()), m_recording(false), m_recordSampleRate(44100), + m_recordChannelCount(2), m_frameCount(0), m_model(0) { @@ -54,6 +55,12 @@ } void +AudioRecordTarget::setSystemRecordChannelCount(int c) +{ + m_recordChannelCount = c; +} + +void AudioRecordTarget::putSamples(int nframes, float **samples) { bool secChanged = false; @@ -153,7 +160,9 @@ m_audioFileName = recordedDir.filePath(filename); - m_model = new WritableWaveFileModel(m_recordSampleRate, 2, m_audioFileName); + m_model = new WritableWaveFileModel(m_recordSampleRate, + m_recordChannelCount, + m_audioFileName); if (!m_model->isOK()) { cerr << "ERROR: AudioRecordTarget::startRecording: Recording failed"
--- a/audio/AudioRecordTarget.h Wed Dec 07 11:52:26 2016 +0000 +++ b/audio/AudioRecordTarget.h Wed Dec 07 12:04:41 2016 +0000 @@ -44,6 +44,7 @@ virtual void setSystemRecordBlockSize(int); virtual void setSystemRecordSampleRate(int); virtual void setSystemRecordLatency(int); + virtual void setSystemRecordChannelCount(int); virtual void putSamples(int nframes, float **samples); @@ -71,6 +72,7 @@ std::string m_clientName; bool m_recording; sv_samplerate_t m_recordSampleRate; + int m_recordChannelCount; sv_frame_t m_frameCount; QString m_audioFileName; WritableWaveFileModel *m_model;