Mercurial > hg > svapp
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 } |