Mercurial > hg > svapp
diff audio/TimeStretchWrapper.cpp @ 738:48001ed9143b audio-source-refactor
Introduce TimeStretchWrapper; some work towards making the AudioCallbackPlaySource not actually try to be an ApplicationPlaybackSource itself but only return one that is constructed from wrappers that it controls the lifespan of
author | Chris Cannam |
---|---|
date | Wed, 18 Mar 2020 12:51:41 +0000 |
parents | |
children | ddfac001b543 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/TimeStretchWrapper.cpp Wed Mar 18 12:51:41 2020 +0000 @@ -0,0 +1,228 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "TimeStretchWrapper.h" + +#include <rubberband/RubberBandStretcher.h> + +#include "base/Debug.h" + +using namespace RubberBand; +using namespace std; + +TimeStretchWrapper::TimeStretchWrapper(ApplicationPlaybackSource *source) : + m_source(source), + m_stretcher(nullptr), + m_timeRatio(1.0), + m_stretcherInputSize(16384), + m_channelCount(0), + m_sampleRate(0) +{ +} + +TimeStretchWrapper::~TimeStretchWrapper() +{ + delete m_stretcher; +} + +void +TimeStretchWrapper::setTimeStretchRatio(double ratio) +{ + lock_guard<mutex> guard(m_mutex); + + SVDEBUG << "TimeStretchWrapper::setTimeStretchRatio: setting ratio to " + << ratio << " (was " << m_timeRatio << ")" << endl; + + m_timeRatio = ratio; + + // Stretcher will be updated by checkStretcher() from next call to + // getSourceSamples() +} + +void +TimeStretchWrapper::reset() +{ + lock_guard<mutex> guard(m_mutex); + + if (m_stretcher) { + m_stretcher->reset(); + } +} + +int +TimeStretchWrapper::getSourceSamples(float *const *samples, + int nchannels, int nframes) +{ + checkStretcher(); + + lock_guard<mutex> guard(m_mutex); + + static int warnings = 0; + if (nchannels != m_channelCount) { + if (warnings >= 0) { + SVCERR << "WARNING: getSourceSamples called for a number of channels different from that set with setSystemPlaybackChannelCount (" + << nchannels << " vs " << m_channelCount << ")" << endl; + if (++warnings == 6) { + SVCERR << "(further warnings will be suppressed)" << endl; + warnings = -1; + } + } + return 0; + } + + if (!m_stretcher) { + return m_source->getSourceSamples(samples, nchannels, nframes); + } + + sv_frame_t available; + sv_frame_t fedToStretcher = 0; + + vector<float *> inputPtrs(m_channelCount, nullptr); + for (int i = 0; i < m_channelCount; ++i) { + inputPtrs[i] = m_inputs[i].data(); + } + + // The input block for a given output is approx output / ratio, + // but we can't predict it exactly, for an adaptive timestretcher. + + while ((available = m_stretcher->available()) < nframes) { + + sv_frame_t reqd = sv_frame_t + (ceil(double(nframes - available) / m_timeRatio)); + reqd = std::max(reqd, sv_frame_t(m_stretcher->getSamplesRequired())); + reqd = std::min(reqd, m_stretcherInputSize); + if (reqd == 0) reqd = 1; + + int got = m_source->getSourceSamples + (inputPtrs.data(), nchannels, reqd); + + if (got <= 0) { + SVCERR << "WARNING: Failed to obtain any source samples at all" + << endl; + return 0; + } + + m_stretcher->process + (inputPtrs.data(), size_t(got), false); + } + + return int(m_stretcher->retrieve(samples, nframes)); +} + +void +TimeStretchWrapper::checkStretcher() +{ + lock_guard<mutex> guard(m_mutex); + + if (m_timeRatio == 1.0 || !m_channelCount || !m_sampleRate) { + SVDEBUG << "TimeStretchWrapper::checkStretcher: m_timeRatio = " + << m_timeRatio << ", m_channelCount = " << m_channelCount + << ", m_sampleRate = " << m_sampleRate + << ", no need for stretcher" << endl; + if (m_stretcher) { + SVDEBUG << "(Deleting existing one)" << endl; + delete m_stretcher; + m_stretcher = nullptr; + } + return; + } + + if (m_stretcher) { + SVDEBUG << "TimeStretchWrapper::checkStretcher: setting stretcher ratio to " << m_timeRatio << endl; + m_stretcher->setTimeRatio(m_timeRatio); + return; + } + + SVDEBUG << "TimeStretchWrapper::checkStretcher: creating stretcher with ratio " << m_timeRatio << endl; + + m_stretcher = new RubberBandStretcher + (m_sampleRate, + m_channelCount, + RubberBandStretcher::OptionProcessRealTime, + m_timeRatio); + + m_inputs.resize(m_channelCount); + for (auto &v: m_inputs) { + v.resize(m_stretcherInputSize); + } +} + +void +TimeStretchWrapper::setSystemPlaybackChannelCount(int count) +{ + { + lock_guard<mutex> guard(m_mutex); + if (m_channelCount != count) { + delete m_stretcher; + m_stretcher = nullptr; + } + m_channelCount = count; + } + m_source->setSystemPlaybackChannelCount(count); +} + +void +TimeStretchWrapper::setSystemPlaybackSampleRate(int rate) +{ + { + lock_guard<mutex> guard(m_mutex); + if (m_sampleRate != rate) { + delete m_stretcher; + m_stretcher = nullptr; + } + m_sampleRate = rate; + } + m_source->setSystemPlaybackSampleRate(rate); +} + +std::string +TimeStretchWrapper::getClientName() const +{ + return m_source->getClientName(); +} + +int +TimeStretchWrapper::getApplicationSampleRate() const +{ + return m_source->getApplicationSampleRate(); +} + +int +TimeStretchWrapper::getApplicationChannelCount() const +{ + return m_source->getApplicationChannelCount(); +} + +void +TimeStretchWrapper::setSystemPlaybackBlockSize(int) +{ +} + +void +TimeStretchWrapper::setSystemPlaybackLatency(int latency) +{ + m_source->setSystemPlaybackLatency(latency); +} + +void +TimeStretchWrapper::setOutputLevels(float left, float right) +{ + m_source->setOutputLevels(left, right); +} + +void +TimeStretchWrapper::audioProcessingOverload() +{ + m_source->audioProcessingOverload(); +}