Chris@738: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@738: Chris@738: /* Chris@738: Sonic Visualiser Chris@738: An audio file viewer and annotation editor. Chris@738: Centre for Digital Music, Queen Mary, University of London. Chris@738: Chris@738: This program is free software; you can redistribute it and/or Chris@738: modify it under the terms of the GNU General Public License as Chris@738: published by the Free Software Foundation; either version 2 of the Chris@738: License, or (at your option) any later version. See the file Chris@738: COPYING included with this distribution for more information. Chris@738: */ Chris@738: Chris@738: #include "TimeStretchWrapper.h" Chris@738: Chris@738: #include Chris@738: Chris@738: #include "base/Debug.h" Chris@738: Chris@738: using namespace RubberBand; Chris@738: using namespace std; Chris@738: Chris@738: TimeStretchWrapper::TimeStretchWrapper(ApplicationPlaybackSource *source) : Chris@738: m_source(source), Chris@738: m_stretcher(nullptr), Chris@738: m_timeRatio(1.0), Chris@738: m_stretcherInputSize(16384), Chris@738: m_channelCount(0), Chris@738: m_sampleRate(0) Chris@738: { Chris@738: } Chris@738: Chris@738: TimeStretchWrapper::~TimeStretchWrapper() Chris@738: { Chris@738: delete m_stretcher; Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::setTimeStretchRatio(double ratio) Chris@738: { Chris@738: lock_guard guard(m_mutex); Chris@738: Chris@738: SVDEBUG << "TimeStretchWrapper::setTimeStretchRatio: setting ratio to " Chris@738: << ratio << " (was " << m_timeRatio << ")" << endl; Chris@738: Chris@738: m_timeRatio = ratio; Chris@738: Chris@738: // Stretcher will be updated by checkStretcher() from next call to Chris@738: // getSourceSamples() Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::reset() Chris@738: { Chris@738: lock_guard guard(m_mutex); Chris@738: Chris@738: if (m_stretcher) { Chris@738: m_stretcher->reset(); Chris@738: } Chris@738: } Chris@738: Chris@738: int Chris@738: TimeStretchWrapper::getSourceSamples(float *const *samples, Chris@738: int nchannels, int nframes) Chris@738: { Chris@738: checkStretcher(); Chris@738: Chris@738: lock_guard guard(m_mutex); Chris@738: Chris@738: static int warnings = 0; Chris@738: if (nchannels != m_channelCount) { Chris@738: if (warnings >= 0) { Chris@738: SVCERR << "WARNING: getSourceSamples called for a number of channels different from that set with setSystemPlaybackChannelCount (" Chris@738: << nchannels << " vs " << m_channelCount << ")" << endl; Chris@738: if (++warnings == 6) { Chris@738: SVCERR << "(further warnings will be suppressed)" << endl; Chris@738: warnings = -1; Chris@738: } Chris@738: } Chris@738: return 0; Chris@738: } Chris@738: Chris@738: if (!m_stretcher) { Chris@738: return m_source->getSourceSamples(samples, nchannels, nframes); Chris@738: } Chris@738: Chris@738: vector inputPtrs(m_channelCount, nullptr); Chris@738: for (int i = 0; i < m_channelCount; ++i) { Chris@738: inputPtrs[i] = m_inputs[i].data(); Chris@738: } Chris@738: Chris@738: // The input block for a given output is approx output / ratio, Chris@738: // but we can't predict it exactly, for an adaptive timestretcher. Chris@738: Chris@740: sv_frame_t available; Chris@740: Chris@738: while ((available = m_stretcher->available()) < nframes) { Chris@738: Chris@740: int reqd = int(ceil(double(nframes - available) / m_timeRatio)); Chris@740: reqd = std::max(reqd, int(m_stretcher->getSamplesRequired())); Chris@738: reqd = std::min(reqd, m_stretcherInputSize); Chris@738: if (reqd == 0) reqd = 1; Chris@738: Chris@738: int got = m_source->getSourceSamples Chris@738: (inputPtrs.data(), nchannels, reqd); Chris@738: Chris@738: if (got <= 0) { Chris@738: SVCERR << "WARNING: Failed to obtain any source samples at all" Chris@738: << endl; Chris@738: return 0; Chris@738: } Chris@738: Chris@738: m_stretcher->process Chris@738: (inputPtrs.data(), size_t(got), false); Chris@738: } Chris@738: Chris@738: return int(m_stretcher->retrieve(samples, nframes)); Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::checkStretcher() Chris@738: { Chris@738: lock_guard guard(m_mutex); Chris@738: Chris@738: if (m_timeRatio == 1.0 || !m_channelCount || !m_sampleRate) { Chris@738: if (m_stretcher) { Chris@740: SVDEBUG << "TimeStretchWrapper::checkStretcher: m_timeRatio = " Chris@740: << m_timeRatio << ", m_channelCount = " << m_channelCount Chris@740: << ", m_sampleRate = " << m_sampleRate Chris@740: << ", deleting existing stretcher" << endl; Chris@738: delete m_stretcher; Chris@738: m_stretcher = nullptr; Chris@738: } Chris@738: return; Chris@738: } Chris@738: Chris@738: if (m_stretcher) { Chris@738: SVDEBUG << "TimeStretchWrapper::checkStretcher: setting stretcher ratio to " << m_timeRatio << endl; Chris@738: m_stretcher->setTimeRatio(m_timeRatio); Chris@738: return; Chris@738: } Chris@738: Chris@738: SVDEBUG << "TimeStretchWrapper::checkStretcher: creating stretcher with ratio " << m_timeRatio << endl; Chris@738: Chris@738: m_stretcher = new RubberBandStretcher Chris@740: (size_t(round(m_sampleRate)), Chris@738: m_channelCount, Chris@738: RubberBandStretcher::OptionProcessRealTime, Chris@738: m_timeRatio); Chris@738: Chris@738: m_inputs.resize(m_channelCount); Chris@738: for (auto &v: m_inputs) { Chris@738: v.resize(m_stretcherInputSize); Chris@738: } Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::setSystemPlaybackChannelCount(int count) Chris@738: { Chris@738: { Chris@738: lock_guard guard(m_mutex); Chris@738: if (m_channelCount != count) { Chris@738: delete m_stretcher; Chris@738: m_stretcher = nullptr; Chris@738: } Chris@738: m_channelCount = count; Chris@738: } Chris@738: m_source->setSystemPlaybackChannelCount(count); Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::setSystemPlaybackSampleRate(int rate) Chris@738: { Chris@738: { Chris@738: lock_guard guard(m_mutex); Chris@738: if (m_sampleRate != rate) { Chris@738: delete m_stretcher; Chris@738: m_stretcher = nullptr; Chris@738: } Chris@738: m_sampleRate = rate; Chris@738: } Chris@738: m_source->setSystemPlaybackSampleRate(rate); Chris@738: } Chris@738: Chris@738: std::string Chris@738: TimeStretchWrapper::getClientName() const Chris@738: { Chris@738: return m_source->getClientName(); Chris@738: } Chris@738: Chris@738: int Chris@738: TimeStretchWrapper::getApplicationSampleRate() const Chris@738: { Chris@738: return m_source->getApplicationSampleRate(); Chris@738: } Chris@738: Chris@738: int Chris@738: TimeStretchWrapper::getApplicationChannelCount() const Chris@738: { Chris@738: return m_source->getApplicationChannelCount(); Chris@738: } Chris@738: Chris@738: void Chris@739: TimeStretchWrapper::setSystemPlaybackBlockSize(int sz) Chris@738: { Chris@739: SVDEBUG << "NOTE: TimeStretchWrapper::setSystemPlaybackBlockSize called " Chris@739: << "with size = " << sz << "; not passing to wrapped source, as " Chris@739: << "actual block size will vary" << endl; Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::setSystemPlaybackLatency(int latency) Chris@738: { Chris@738: m_source->setSystemPlaybackLatency(latency); Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::setOutputLevels(float left, float right) Chris@738: { Chris@738: m_source->setOutputLevels(left, right); Chris@738: } Chris@738: Chris@738: void Chris@738: TimeStretchWrapper::audioProcessingOverload() Chris@738: { Chris@738: m_source->audioProcessingOverload(); Chris@738: }