1
1
#pragma once
2
2
3
3
#include " AudioTools/AudioStreams.h"
4
+
5
+ namespace audio_tools {
6
+
4
7
/* *
5
- * @brief A simple implementation which changes the sample rate by the indicated factor.
8
+ * @brief A simple implementation which changes the sample rate by the indicated integer factor.
6
9
* To downlample we calculate the avarage of n (=factor) samples. To upsample we interpolate
7
10
* the missing samples. If the indicated factor is positive we upsample if it is negative
8
11
* we downsample.
9
- *
10
- * @tparam T
12
+ * @author Phil Schatzmann
13
+ * @copyright GPLv3
14
+ * @tparam T data type of audio data
11
15
*/
12
16
template <typename T>
13
17
class Resample : public AudioStreamX {
14
18
public:
19
+ /* *
20
+ * @brief Construct a new Resample object
21
+ * call setOut and begin to setup the required parameters
22
+ */
23
+ Resample () = default ;
15
24
/* *
16
25
* @brief Construct a new Converter Resample object
17
26
*
18
27
* @param channels number of channels (default 2)
19
28
* @param factor use a negative value to downsample
20
29
*/
21
30
Resample (Print &out, int channels=2 , int factor=2 ){
31
+ setOut (out);
32
+ begin (channels, factor);
33
+ }
34
+ /* *
35
+ * @brief Construct a new Resample object
36
+ *
37
+ * @param in
38
+ * @param channels
39
+ * @param factor
40
+ */
41
+ Resample (Stream &in, int channels=2 , int factor=2 ){
42
+ setIn (in);
43
+ begin (channels, factor);
44
+ }
45
+
46
+ void begin (int channels=2 , int factor=2 ) {
22
47
this ->channels = channels;
23
48
this ->factor = factor;
49
+ }
50
+
51
+ void setOut (Print &out){
24
52
this ->p_out = &out;
25
53
}
26
54
27
- Resample (Stream &in, int channels=2 , int factor=2 ){
28
- this ->channels = channels;
29
- this ->factor = factor;
55
+ void setIn (Stream &in){
30
56
this ->p_out = ∈
31
57
this ->p_in = ∈
32
58
}
@@ -88,7 +114,6 @@ class Resample : public AudioStreamX {
88
114
return byte_count;
89
115
}
90
116
91
-
92
117
protected:
93
118
Print *p_out=nullptr ;
94
119
Stream *p_in=nullptr ;
@@ -170,5 +195,152 @@ class Resample : public AudioStreamX {
170
195
T* p_data (int frame_pos, int channel, T*start){
171
196
return frame_pos>=0 ? start+(frame_pos*channels)+channel : last_end+channel;
172
197
}
198
+ };
199
+
200
+ enum ResamplePrecision { Low, Medium, High, VeryHigh};
201
+
202
+ /* *
203
+ * @brief Class to determine a combination of upsample and downsample rates to achieve any ratio
204
+ * @author Phil Schatzmann
205
+ * @copyright GPLv3
206
+ */
207
+ class ResampleParameterEstimator {
208
+ public:
209
+
210
+ ResampleParameterEstimator () = default ;
211
+
212
+ ResampleParameterEstimator (int fromRate, int toRate, ResamplePrecision precision = Medium){
213
+ begin (fromRate, toRate, precision);
214
+ }
215
+
216
+ void begin (int fromRate, int toRate, ResamplePrecision precision = Medium){
217
+ this ->from_rate = fromRate;
218
+ this ->to_rate = toRate;
219
+ this ->precision = precision;
220
+ // update result values
221
+ calculate ();
222
+ }
223
+
224
+ // / prposed factor for upsampling
225
+ int factor () {
226
+ return fact;
227
+ }
228
+
229
+ // / propose divisor for downsampling
230
+ int divisor () {
231
+ return div;
232
+ }
233
+
234
+ // / original sample rate
235
+ int fromRate (){
236
+ return from_rate;
237
+ }
238
+
239
+ // / target sample rate
240
+ int toRate (){
241
+ return to_rate;
242
+ }
243
+
244
+ // / effective target sample rate by upsampling and then downsampling at different factors
245
+ float toRateEffective () {
246
+ return to_rate_eff;
247
+ }
248
+
249
+ // / same as factor
250
+ int upsample () {
251
+ return factor ();
252
+ }
253
+
254
+ // / same as division but provides negative number to indicate that we need to downsample
255
+ int downsample () {
256
+ return - divisor ();
257
+ }
258
+
259
+ // / Determines a supported downsampling write size
260
+ size_t supportedSize (size_t len){
261
+ return (len / div) * div;
262
+ }
263
+
264
+ protected:
265
+ int fact=0 , div=0 ;
266
+ int from_rate=0 , to_rate=0 ;
267
+ float diff=10000000.0 ;
268
+ float to_rate_eff=0 ;
269
+ int div_array[28 ] = {1 , 2 , 3 , 5 , 7 , 10 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 , 59 , 61 , 67 , 71 , 73 , 79 , 83 , 89 , 97 , 100 };
270
+ int limits[4 ] = {6 , 12 , 18 , 27 };
271
+ int precision=1 ;
272
+
273
+ // find the parameters with the lowest difference
274
+ void calculate () {
275
+ int limit_max = limits[precision];
276
+ for (int j=0 ;j<limit_max;j++){
277
+ int tmp_div = div_array[j];
278
+ int tmp_fact = rintf (static_cast <float >(to_rate) * tmp_div / from_rate);
279
+ float tmp_diff = static_cast <float >(to_rate) - (static_cast <float >(from_rate) * tmp_fact / tmp_div);
280
+ LOGD (" div: %d, fact %d -> diff: %f" , tmp_div,tmp_fact,tmp_diff);
281
+ if (abs (tmp_diff)<abs (diff)){
282
+ fact = tmp_fact;
283
+ div = tmp_div;
284
+ diff = tmp_diff;
285
+ if (diff==0.0 ){
286
+ break ;
287
+ }
288
+ }
289
+ }
290
+ to_rate_eff = static_cast <float >(from_rate) * fact / div;
291
+ LOGI (" div: %d, fact %d -> rate: %d, rate_eff: %f" , div,fact,to_rate, to_rate_eff);
292
+ }
293
+ };
294
+
295
+ /* *
296
+ * @brief Stream class which can be used to resample between different sample rates.
297
+ * @author Phil Schatzmann
298
+ * @copyright GPLv3
299
+ * @tparam T data type of audio data
300
+ */
301
+ template <typename T>
302
+ class ResampleStream : public AudioStreamX {
303
+ public:
304
+ ResampleStream (Print &out, ResamplePrecision precision = Medium){
305
+ this ->precision = precision;
306
+ up.setOut (down); // we upsample first
307
+ down.setOut (out); // so that we can downsample to the requested rate
308
+ }
309
+
310
+ ResampleStream (Stream &in, ResamplePrecision precision = Medium){
311
+ this ->precision = precision;
312
+ up.seIn (down); // we upsample first
313
+ down.setIn (in); // so that we can downsample to the requested rate
314
+ }
315
+
316
+ // / Defines the channels and sample rates
317
+ void begin (int channels, int fromRate, int toRate){
318
+ calc.begin (fromRate, toRate, precision);
319
+ up.begin (channels, calc.upsample ());
320
+ down.begin (channels, calc.downsample ());
321
+ }
322
+
323
+ // / Determines the number of bytes which are available for write
324
+ int availableForWrite () override { return up.availableForWrite (); }
325
+
326
+ // / Writes the data up or downsampled to the final destination
327
+ size_t write (const uint8_t *src, size_t byte_count) override {
328
+ return up.write (src, byte_count);
329
+ }
330
+ // / Determines the available bytes from the final source stream
331
+ int available () override { up.available (); }
332
+
333
+ // / Reads the up/downsampled bytes
334
+ size_t readBytes (uint8_t *src, size_t byte_count) override {
335
+ return up.readBytes (src, byte_count);
336
+ }
337
+
338
+ protected:
339
+ ResampleParameterEstimator calc;
340
+ Resample<T> up;
341
+ Resample<T> down;
342
+ ResamplePrecision precision;
343
+
344
+ };
173
345
174
- };
346
+ } // namespace
0 commit comments