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