annotate 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
rev   line source
Chris@738 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@738 2
Chris@738 3 /*
Chris@738 4 Sonic Visualiser
Chris@738 5 An audio file viewer and annotation editor.
Chris@738 6 Centre for Digital Music, Queen Mary, University of London.
Chris@738 7
Chris@738 8 This program is free software; you can redistribute it and/or
Chris@738 9 modify it under the terms of the GNU General Public License as
Chris@738 10 published by the Free Software Foundation; either version 2 of the
Chris@738 11 License, or (at your option) any later version. See the file
Chris@738 12 COPYING included with this distribution for more information.
Chris@738 13 */
Chris@738 14
Chris@738 15 #include "TimeStretchWrapper.h"
Chris@738 16
Chris@738 17 #include <rubberband/RubberBandStretcher.h>
Chris@738 18
Chris@738 19 #include "base/Debug.h"
Chris@738 20
Chris@738 21 using namespace RubberBand;
Chris@738 22 using namespace std;
Chris@738 23
Chris@738 24 TimeStretchWrapper::TimeStretchWrapper(ApplicationPlaybackSource *source) :
Chris@738 25 m_source(source),
Chris@738 26 m_stretcher(nullptr),
Chris@738 27 m_timeRatio(1.0),
Chris@738 28 m_stretcherInputSize(16384),
Chris@738 29 m_channelCount(0),
Chris@738 30 m_sampleRate(0)
Chris@738 31 {
Chris@738 32 }
Chris@738 33
Chris@738 34 TimeStretchWrapper::~TimeStretchWrapper()
Chris@738 35 {
Chris@738 36 delete m_stretcher;
Chris@738 37 }
Chris@738 38
Chris@738 39 void
Chris@738 40 TimeStretchWrapper::setTimeStretchRatio(double ratio)
Chris@738 41 {
Chris@738 42 lock_guard<mutex> guard(m_mutex);
Chris@738 43
Chris@738 44 SVDEBUG << "TimeStretchWrapper::setTimeStretchRatio: setting ratio to "
Chris@738 45 << ratio << " (was " << m_timeRatio << ")" << endl;
Chris@738 46
Chris@738 47 m_timeRatio = ratio;
Chris@738 48
Chris@738 49 // Stretcher will be updated by checkStretcher() from next call to
Chris@738 50 // getSourceSamples()
Chris@738 51 }
Chris@738 52
Chris@738 53 void
Chris@738 54 TimeStretchWrapper::reset()
Chris@738 55 {
Chris@738 56 lock_guard<mutex> guard(m_mutex);
Chris@738 57
Chris@738 58 if (m_stretcher) {
Chris@738 59 m_stretcher->reset();
Chris@738 60 }
Chris@738 61 }
Chris@738 62
Chris@738 63 int
Chris@738 64 TimeStretchWrapper::getSourceSamples(float *const *samples,
Chris@738 65 int nchannels, int nframes)
Chris@738 66 {
Chris@738 67 checkStretcher();
Chris@738 68
Chris@738 69 lock_guard<mutex> guard(m_mutex);
Chris@738 70
Chris@738 71 static int warnings = 0;
Chris@738 72 if (nchannels != m_channelCount) {
Chris@738 73 if (warnings >= 0) {
Chris@738 74 SVCERR << "WARNING: getSourceSamples called for a number of channels different from that set with setSystemPlaybackChannelCount ("
Chris@738 75 << nchannels << " vs " << m_channelCount << ")" << endl;
Chris@738 76 if (++warnings == 6) {
Chris@738 77 SVCERR << "(further warnings will be suppressed)" << endl;
Chris@738 78 warnings = -1;
Chris@738 79 }
Chris@738 80 }
Chris@738 81 return 0;
Chris@738 82 }
Chris@738 83
Chris@738 84 if (!m_stretcher) {
Chris@738 85 return m_source->getSourceSamples(samples, nchannels, nframes);
Chris@738 86 }
Chris@738 87
Chris@738 88 sv_frame_t available;
Chris@738 89 sv_frame_t fedToStretcher = 0;
Chris@738 90
Chris@738 91 vector<float *> inputPtrs(m_channelCount, nullptr);
Chris@738 92 for (int i = 0; i < m_channelCount; ++i) {
Chris@738 93 inputPtrs[i] = m_inputs[i].data();
Chris@738 94 }
Chris@738 95
Chris@738 96 // The input block for a given output is approx output / ratio,
Chris@738 97 // but we can't predict it exactly, for an adaptive timestretcher.
Chris@738 98
Chris@738 99 while ((available = m_stretcher->available()) < nframes) {
Chris@738 100
Chris@738 101 sv_frame_t reqd = sv_frame_t
Chris@738 102 (ceil(double(nframes - available) / m_timeRatio));
Chris@738 103 reqd = std::max(reqd, sv_frame_t(m_stretcher->getSamplesRequired()));
Chris@738 104 reqd = std::min(reqd, m_stretcherInputSize);
Chris@738 105 if (reqd == 0) reqd = 1;
Chris@738 106
Chris@738 107 int got = m_source->getSourceSamples
Chris@738 108 (inputPtrs.data(), nchannels, reqd);
Chris@738 109
Chris@738 110 if (got <= 0) {
Chris@738 111 SVCERR << "WARNING: Failed to obtain any source samples at all"
Chris@738 112 << endl;
Chris@738 113 return 0;
Chris@738 114 }
Chris@738 115
Chris@738 116 m_stretcher->process
Chris@738 117 (inputPtrs.data(), size_t(got), false);
Chris@738 118 }
Chris@738 119
Chris@738 120 return int(m_stretcher->retrieve(samples, nframes));
Chris@738 121 }
Chris@738 122
Chris@738 123 void
Chris@738 124 TimeStretchWrapper::checkStretcher()
Chris@738 125 {
Chris@738 126 lock_guard<mutex> guard(m_mutex);
Chris@738 127
Chris@738 128 if (m_timeRatio == 1.0 || !m_channelCount || !m_sampleRate) {
Chris@738 129 SVDEBUG << "TimeStretchWrapper::checkStretcher: m_timeRatio = "
Chris@738 130 << m_timeRatio << ", m_channelCount = " << m_channelCount
Chris@738 131 << ", m_sampleRate = " << m_sampleRate
Chris@738 132 << ", no need for stretcher" << endl;
Chris@738 133 if (m_stretcher) {
Chris@738 134 SVDEBUG << "(Deleting existing one)" << endl;
Chris@738 135 delete m_stretcher;
Chris@738 136 m_stretcher = nullptr;
Chris@738 137 }
Chris@738 138 return;
Chris@738 139 }
Chris@738 140
Chris@738 141 if (m_stretcher) {
Chris@738 142 SVDEBUG << "TimeStretchWrapper::checkStretcher: setting stretcher ratio to " << m_timeRatio << endl;
Chris@738 143 m_stretcher->setTimeRatio(m_timeRatio);
Chris@738 144 return;
Chris@738 145 }
Chris@738 146
Chris@738 147 SVDEBUG << "TimeStretchWrapper::checkStretcher: creating stretcher with ratio " << m_timeRatio << endl;
Chris@738 148
Chris@738 149 m_stretcher = new RubberBandStretcher
Chris@738 150 (m_sampleRate,
Chris@738 151 m_channelCount,
Chris@738 152 RubberBandStretcher::OptionProcessRealTime,
Chris@738 153 m_timeRatio);
Chris@738 154
Chris@738 155 m_inputs.resize(m_channelCount);
Chris@738 156 for (auto &v: m_inputs) {
Chris@738 157 v.resize(m_stretcherInputSize);
Chris@738 158 }
Chris@738 159 }
Chris@738 160
Chris@738 161 void
Chris@738 162 TimeStretchWrapper::setSystemPlaybackChannelCount(int count)
Chris@738 163 {
Chris@738 164 {
Chris@738 165 lock_guard<mutex> guard(m_mutex);
Chris@738 166 if (m_channelCount != count) {
Chris@738 167 delete m_stretcher;
Chris@738 168 m_stretcher = nullptr;
Chris@738 169 }
Chris@738 170 m_channelCount = count;
Chris@738 171 }
Chris@738 172 m_source->setSystemPlaybackChannelCount(count);
Chris@738 173 }
Chris@738 174
Chris@738 175 void
Chris@738 176 TimeStretchWrapper::setSystemPlaybackSampleRate(int rate)
Chris@738 177 {
Chris@738 178 {
Chris@738 179 lock_guard<mutex> guard(m_mutex);
Chris@738 180 if (m_sampleRate != rate) {
Chris@738 181 delete m_stretcher;
Chris@738 182 m_stretcher = nullptr;
Chris@738 183 }
Chris@738 184 m_sampleRate = rate;
Chris@738 185 }
Chris@738 186 m_source->setSystemPlaybackSampleRate(rate);
Chris@738 187 }
Chris@738 188
Chris@738 189 std::string
Chris@738 190 TimeStretchWrapper::getClientName() const
Chris@738 191 {
Chris@738 192 return m_source->getClientName();
Chris@738 193 }
Chris@738 194
Chris@738 195 int
Chris@738 196 TimeStretchWrapper::getApplicationSampleRate() const
Chris@738 197 {
Chris@738 198 return m_source->getApplicationSampleRate();
Chris@738 199 }
Chris@738 200
Chris@738 201 int
Chris@738 202 TimeStretchWrapper::getApplicationChannelCount() const
Chris@738 203 {
Chris@738 204 return m_source->getApplicationChannelCount();
Chris@738 205 }
Chris@738 206
Chris@738 207 void
Chris@738 208 TimeStretchWrapper::setSystemPlaybackBlockSize(int)
Chris@738 209 {
Chris@738 210 }
Chris@738 211
Chris@738 212 void
Chris@738 213 TimeStretchWrapper::setSystemPlaybackLatency(int latency)
Chris@738 214 {
Chris@738 215 m_source->setSystemPlaybackLatency(latency);
Chris@738 216 }
Chris@738 217
Chris@738 218 void
Chris@738 219 TimeStretchWrapper::setOutputLevels(float left, float right)
Chris@738 220 {
Chris@738 221 m_source->setOutputLevels(left, right);
Chris@738 222 }
Chris@738 223
Chris@738 224 void
Chris@738 225 TimeStretchWrapper::audioProcessingOverload()
Chris@738 226 {
Chris@738 227 m_source->audioProcessingOverload();
Chris@738 228 }