Mercurial > hg > svapp
changeset 554:4be200469a9c 3.0-integration
Merge from branch bqresample
author | Chris Cannam |
---|---|
date | Fri, 09 Dec 2016 18:01:55 +0000 |
parents | 4de547a5905c (current diff) 2a1e9e017484 (diff) |
children | 2683a8ca36ea |
files | |
diffstat | 4 files changed, 150 insertions(+), 79 deletions(-) [+] |
line wrap: on
line diff
--- a/audio/AudioCallbackPlaySource.cpp Tue Dec 06 15:59:06 2016 +0000 +++ b/audio/AudioCallbackPlaySource.cpp Fri Dec 09 18:01:55 2016 +0000 @@ -28,6 +28,7 @@ #include "plugin/RealTimePluginInstance.h" #include "bqaudioio/SystemPlaybackTarget.h" +#include "bqaudioio/ResamplerWrapper.h" #include <rubberband/RubberBandStretcher.h> using namespace RubberBand; @@ -53,7 +54,7 @@ m_sourceChannelCount(0), m_blockSize(1024), m_sourceSampleRate(0), - m_targetSampleRate(0), + m_deviceSampleRate(0), m_playLatency(0), m_target(0), m_lastRetrievalTimestamp(0.0), @@ -77,7 +78,8 @@ m_stretcherInputCount(0), m_stretcherInputs(0), m_stretcherInputSizes(0), - m_fillThread(0) + m_fillThread(0), + m_resamplerWrapper(0) { m_viewManager->setAudioPlaySource(this); @@ -228,13 +230,26 @@ if (willPlay) clearRingBuffers(true); } - if (buffersChanged || srChanged) { + if (srChanged) { - // There are more channels than there were before, or the - // source sample rate has changed + SVCERR << "AudioCallbackPlaySource: Source rate changed" << endl; - //!!! + if (m_resamplerWrapper) { + SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " + << m_sourceSampleRate << ", updating resampler wrapper" << endl; + m_resamplerWrapper->changeApplicationSampleRate + (int(round(m_sourceSampleRate))); + m_resamplerWrapper->reset(); + } + delete m_timeStretcher; + delete m_monoStretcher; + m_timeStretcher = 0; + m_monoStretcher = 0; + + if (m_stretchRatio != 1.f) { + setTimeStretch(m_stretchRatio); + } } rebuildRangeLists(); @@ -538,7 +553,7 @@ } void -AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n) +AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName ) { } @@ -573,6 +588,16 @@ } void +AudioCallbackPlaySource::setResamplerWrapper(breakfastquay::ResamplerWrapper *w) +{ + m_resamplerWrapper = w; + if (m_resamplerWrapper && m_sourceSampleRate != 0) { + m_resamplerWrapper->changeApplicationSampleRate + (int(round(m_sourceSampleRate))); + } +} + +void AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) { cout << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl; @@ -618,12 +643,12 @@ // This method attempts to estimate which audio sample frame is // "currently coming through the speakers". - sv_samplerate_t targetRate = getTargetSampleRate(); + sv_samplerate_t deviceRate = getDeviceSampleRate(); sv_frame_t latency = m_playLatency; // at target rate RealTime latency_t = RealTime::zeroTime; - if (targetRate != 0) { - latency_t = RealTime::frame2RealTime(latency, targetRate); + if (deviceRate != 0) { + latency_t = RealTime::frame2RealTime(latency, deviceRate); } return getCurrentFrame(latency_t); @@ -638,16 +663,18 @@ sv_frame_t AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) { - // We resample when filling the ring buffer, and time-stretch when - // draining it. The buffer contains data at the "target rate" and - // the latency provided by the target is also at the target rate. - // Because of the multiple rates involved, we do the actual - // calculation using RealTime instead. + // The ring buffers contain data at the source sample rate and all + // processing (including time stretching) happens at this + // rate. Resampling only happens after the audio data leaves this + // class. + + // (But because historically more than one sample rate could have + // been involved here, we do latency calculations using RealTime + // values instead of samples.) - sv_samplerate_t sourceRate = getSourceSampleRate(); - sv_samplerate_t targetRate = getTargetSampleRate(); + sv_samplerate_t rate = getSourceSampleRate(); - if (sourceRate == 0 || targetRate == 0) return 0; + if (rate == 0) return 0; int inbuffer = 0; // at target rate @@ -667,7 +694,7 @@ bool looping = m_viewManager->getPlayLoopMode(); - RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); + RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); sv_frame_t stretchlat = 0; double timeRatio = 1.0; @@ -677,7 +704,7 @@ timeRatio = m_timeStretcher->getTimeRatio(); } - RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate); + RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate); // When the target has just requested a block from us, the last // sample it obtained was our buffer fill frame count minus the @@ -695,8 +722,7 @@ m_trustworthyTimestamps && lastRetrievalTimestamp != 0.0) { - lastretrieved_t = RealTime::frame2RealTime - (lastRetrievedBlockSize, targetRate); + lastretrieved_t = RealTime::frame2RealTime(lastRetrievedBlockSize, rate); // calculate number of frames at target rate that have elapsed // since the end of the last call to getSourceSamples @@ -713,11 +739,10 @@ } else { - lastretrieved_t = RealTime::frame2RealTime - (getTargetBlockSize(), targetRate); + lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate); } - RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate); + RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); if (timeRatio != 1.0) { lastretrieved_t = lastretrieved_t / timeRatio; @@ -743,7 +768,7 @@ - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t + sincerequest_t; if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; - sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate); + sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); return m_viewManager->alignPlaybackFrameToReference(frame); } @@ -780,8 +805,7 @@ // duration of playback! if (!m_playStartFramePassed) { - RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, - sourceRate); + RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate); if (playing_t < playstart_t) { // cerr << "playing_t " << playing_t << " < playstart_t " // << playstart_t << endl; @@ -839,7 +863,7 @@ if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; - sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate); + sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); if (m_lastCurrentFrame > 0 && !looping) { if (frame < m_lastCurrentFrame) { @@ -924,19 +948,11 @@ void AudioCallbackPlaySource::setSystemPlaybackSampleRate(int sr) { - bool first = (m_targetSampleRate == 0); - - m_targetSampleRate = sr; - - if (first && (m_stretchRatio != 1.f)) { - // couldn't create a stretcher before because we had no sample - // rate: make one now - setTimeStretch(m_stretchRatio); - } + m_deviceSampleRate = sr; } void -AudioCallbackPlaySource::setSystemPlaybackChannelCount(int c) +AudioCallbackPlaySource::setSystemPlaybackChannelCount(int) { } @@ -969,10 +985,9 @@ } sv_samplerate_t -AudioCallbackPlaySource::getTargetSampleRate() const +AudioCallbackPlaySource::getDeviceSampleRate() const { - if (m_targetSampleRate) return m_targetSampleRate; - else return getSourceSampleRate(); + return m_deviceSampleRate; } int @@ -999,19 +1014,20 @@ { m_stretchRatio = factor; - if (!getTargetSampleRate()) return; // have to make our stretcher later + int rate = int(getSourceSampleRate()); + if (!rate) return; // have to make our stretcher later if (m_timeStretcher || (factor == 1.0)) { // stretch ratio will be set in next process call if appropriate } else { m_stretcherInputCount = getTargetChannelCount(); RubberBandStretcher *stretcher = new RubberBandStretcher - (int(getTargetSampleRate()), + (rate, m_stretcherInputCount, RubberBandStretcher::OptionProcessRealTime, factor); RubberBandStretcher *monoStretcher = new RubberBandStretcher - (int(getTargetSampleRate()), + (rate, 1, RubberBandStretcher::OptionProcessRealTime, factor); @@ -1308,6 +1324,9 @@ return false; } + // space is now the number of samples that can be written on each + // channel's write ringbuffer + sv_frame_t f = m_writeBufferFill; bool readWriteEqual = (m_readBuffers == m_writeBuffers); @@ -1325,8 +1344,6 @@ int channels = getTargetChannelCount(); - sv_frame_t orig = space; - static float **bufferPtrs = 0; static int bufferPtrCount = 0;
--- a/audio/AudioCallbackPlaySource.h Tue Dec 06 15:59:06 2016 +0000 +++ b/audio/AudioCallbackPlaySource.h Fri Dec 09 18:01:55 2016 +0000 @@ -39,6 +39,10 @@ class RubberBandStretcher; } +namespace breakfastquay { + class ResamplerWrapper; +} + class Model; class ViewManagerBase; class AudioGenerator; @@ -121,6 +125,11 @@ virtual void setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *); /** + * Set the resampler wrapper, if one is in use. + */ + virtual void setResamplerWrapper(breakfastquay::ResamplerWrapper *); + + /** * Set the block size of the target audio device. This should be * called by the target class. */ @@ -136,7 +145,7 @@ /** * Set the playback latency of the target audio device, in frames - * at the target sample rate. This is the difference between the + * at the device sample rate. This is the difference between the * frame currently "leaving the speakers" and the last frame (or * highest last frame across all channels) requested via * getSamples(). The default is zero. @@ -161,7 +170,7 @@ * Return the sample rate set by the target audio device (or the * source sample rate if the target hasn't set one). */ - virtual sv_samplerate_t getTargetSampleRate() const; + sv_samplerate_t getDeviceSampleRate() const; /** * Indicate how many channels the target audio device was opened @@ -194,30 +203,45 @@ * 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. + * + * override from AudioPlaySource */ - int getTargetChannelCount() const; + virtual int getTargetChannelCount() const override; /** * ApplicationPlaybackSource equivalent of the above. + * + * override from breakfastquay::ApplicationPlaybackSource */ - virtual int getApplicationChannelCount() const { + virtual int getApplicationChannelCount() const override { return getTargetChannelCount(); } /** - * 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. + * Get the actual sample rate of the source material (the main + * model). This may safely be called from a realtime thread. + * Returns 0 if there is no source yet available. + * + * When this changes, the AudioCallbackPlaySource notifies its + * ResamplerWrapper of the new sample rate so that it can resample + * correctly on the way to the device (which is opened at a fixed + * rate, see getApplicationSampleRate). */ - virtual sv_samplerate_t getSourceSampleRate() const; + virtual sv_samplerate_t getSourceSampleRate() const override; /** - * ApplicationPlaybackSource equivalent of the above. + * ApplicationPlaybackSource interface method: get the sample rate + * at which the application wants the device to be opened. We + * always allow the device to open at its default rate, and then + * we resample if the audio is at a different rate. This avoids + * having to close and re-open the device to obtain consistent + * behaviour for consecutive sessions with different source rates. */ - virtual int getApplicationSampleRate() const { - return int(round(getSourceSampleRate())); + virtual int getApplicationSampleRate() const override { + return 0; } /** @@ -311,7 +335,7 @@ int m_sourceChannelCount; sv_frame_t m_blockSize; sv_samplerate_t m_sourceSampleRate; - sv_samplerate_t m_targetSampleRate; + sv_samplerate_t m_deviceSampleRate; sv_frame_t m_playLatency; breakfastquay::SystemPlaybackTarget *m_target; double m_lastRetrievalTimestamp; @@ -396,6 +420,7 @@ QMutex m_mutex; QWaitCondition m_condition; FillThread *m_fillThread; + breakfastquay::ResamplerWrapper *m_resamplerWrapper; // I don't own this }; #endif
--- a/framework/MainWindowBase.cpp Tue Dec 06 15:59:06 2016 +0000 +++ b/framework/MainWindowBase.cpp Fri Dec 09 18:01:55 2016 +0000 @@ -76,6 +76,7 @@ #include <bqaudioio/SystemPlaybackTarget.h> #include <bqaudioio/SystemAudioIO.h> #include <bqaudioio/AudioFactory.h> +#include <bqaudioio/ResamplerWrapper.h> #include <QApplication> #include <QMessageBox> @@ -141,6 +142,7 @@ m_soundOptions(options), m_playSource(0), m_recordTarget(0), + m_resamplerWrapper(0), m_playTarget(0), m_audioIO(0), m_oscQueue(0), @@ -303,6 +305,7 @@ delete m_playTarget; // Then delete the Application objects. + delete m_resamplerWrapper; delete m_playSource; delete m_recordTarget; @@ -1399,7 +1402,9 @@ if (Preferences::getInstance()->getFixedSampleRate() != 0) { rate = Preferences::getInstance()->getFixedSampleRate(); } else if (Preferences::getInstance()->getResampleOnLoad()) { - rate = m_playSource->getSourceSampleRate(); + if (getMainModel()) { + rate = getMainModel()->getSampleRate(); + } } ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate); @@ -2158,7 +2163,9 @@ if (getMainModel()) { rate = getMainModel()->getSampleRate(); } else if (Preferences::getInstance()->getResampleOnLoad()) { - rate = m_playSource->getSourceSampleRate(); + if (getMainModel()) { + rate = getMainModel()->getSampleRate(); + } } RDFImporter importer @@ -2300,27 +2307,49 @@ if (!(m_soundOptions & WithAudioOutput)) return; - //!!! how to handle preferences -/* QSettings settings; settings.beginGroup("Preferences"); - QString targetName = settings.value("audio-target", "").toString(); + QString implementation = settings.value + ("audio-target", "").toString(); + QString suffix; + if (implementation != "") suffix = "-" + implementation; + QString recordDevice = settings.value + ("audio-record-device" + suffix, "").toString(); + QString playbackDevice = settings.value + ("audio-playback-device" + suffix, "").toString(); settings.endGroup(); - AudioTargetFactory *factory = AudioTargetFactory::getInstance(); - - factory->setDefaultCallbackTarget(targetName); -*/ - + + if (implementation == "auto") { + implementation = ""; + } + + breakfastquay::AudioFactory::Preference preference; + preference.implementation = implementation.toStdString(); + preference.recordDevice = recordDevice.toStdString(); + preference.playbackDevice = playbackDevice.toStdString(); + + SVCERR << "createAudioIO: Preferred implementation = \"" + << preference.implementation << "\"" << endl; + SVCERR << "createAudioIO: Preferred playback device = \"" + << preference.playbackDevice << "\"" << endl; + SVCERR << "createAudioIO: Preferred record device = \"" + << preference.recordDevice << "\"" << endl; + + if (!m_resamplerWrapper) { + m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource); + m_playSource->setResamplerWrapper(m_resamplerWrapper); + } + if (m_soundOptions & WithAudioInput) { m_audioIO = breakfastquay::AudioFactory:: - createCallbackIO(m_recordTarget, m_playSource); + createCallbackIO(m_recordTarget, m_resamplerWrapper, preference); if (m_audioIO) { m_audioIO->suspend(); // start in suspended state m_playSource->setSystemPlaybackTarget(m_audioIO); } } else { m_playTarget = breakfastquay::AudioFactory:: - createCallbackPlayTarget(m_playSource); + createCallbackPlayTarget(m_resamplerWrapper, preference); if (m_playTarget) { m_playTarget->suspend(); // start in suspended state m_playSource->setSystemPlaybackTarget(m_playTarget); @@ -2329,22 +2358,20 @@ if (!m_playTarget && !m_audioIO) { emit hideSplash(); - -// if (factory->isAutoCallbackTarget(targetName)) { + if (implementation == "") { QMessageBox::warning (this, tr("Couldn't open audio device"), tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"), QMessageBox::Ok); -/* } else { QMessageBox::warning (this, tr("Couldn't open audio device"), tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>") - .arg(factory->getCallbackTargetDescription(targetName)), + .arg(breakfastquay::AudioFactory:: + getImplementationDescription(implementation.toStdString()) + .c_str()), QMessageBox::Ok); } -*/ - return; } }
--- a/framework/MainWindowBase.h Tue Dec 06 15:59:06 2016 +0000 +++ b/framework/MainWindowBase.h Fri Dec 09 18:01:55 2016 +0000 @@ -65,8 +65,9 @@ class AlignmentModel; namespace breakfastquay { -class SystemPlaybackTarget; -class SystemAudioIO; + class SystemPlaybackTarget; + class SystemAudioIO; + class ResamplerWrapper; } /** @@ -343,6 +344,7 @@ AudioCallbackPlaySource *m_playSource; AudioRecordTarget *m_recordTarget; + breakfastquay::ResamplerWrapper *m_resamplerWrapper; breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this... breakfastquay::SystemAudioIO *m_audioIO; // ... and this exists