arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
ResampleStream.h
1#pragma once
2
3#include "AudioTools/CoreAudio/AudioIO.h"
4
5#if USE_PRINT_FLUSH
6# define PRINT_FLUSH_OVERRIDE override
7#else
8# define PRINT_FLUSH_OVERRIDE
9#endif
10
11namespace audio_tools {
12
18struct ResampleConfig : public AudioInfo {
19 float step_size = 1.0f;
22 int buffer_size = DEFAULT_BUFFER_SIZE;
23};
24
34 public:
35 ResampleStream() = default;
36
38 ResampleStream(Print &out) { setOutput(out); }
43 setOutput(out);
44 }
45
48
55
59 cfg.copyFrom(audioInfo());
60 return cfg;
61 }
62
63 bool begin(ResampleConfig cfg) {
64 LOGI("begin step_size: %f", cfg.step_size);
65 //is_output_notify = false;
66 to_sample_rate = cfg.to_sample_rate;
67 out_buffer.resize(cfg.buffer_size);
68
70 setStepSize(cfg.step_size);
71 is_first = true;
72 idx = 0;
73 // step_dirty = true;
74 bytes_per_frame = info.bits_per_sample / 8 * info.channels;
75
76 setupReader();
77
78 setAudioInfo(cfg);
79
80 return true;
81 }
82
83 bool begin(AudioInfo from, AudioInfo to) {
84 if (from.bits_per_sample != to.bits_per_sample){
85 LOGE("invalid bits_per_sample: %d", (int) to.bits_per_sample);
86 return false;
87 }
88 if (from.channels != to.channels){
89 LOGE("invalid channels: %d", (int) to.channels);
90 return false;
91 }
92 return begin(from, (sample_rate_t)to.sample_rate);
93 }
94
95 bool begin(AudioInfo from, int toRate) {
96 return begin(from, (sample_rate_t)toRate);
97 }
98
99 bool begin(AudioInfo from, sample_rate_t toRate) {
100 ResampleConfig rcfg;
101 rcfg.copyFrom(from);
102 rcfg.to_sample_rate = toRate;
103 rcfg.step_size = getStepSize(from.sample_rate, toRate);
104 return begin(rcfg);
105 }
106
107 virtual bool begin(AudioInfo info) {
108 if (to_sample_rate != 0) return begin(info, to_sample_rate);
109 return begin(info, step_size);
110 }
111
112 bool begin() override {
113 return begin(audioInfo());
114 }
115
116 bool begin(AudioInfo info, float step) {
117 ResampleConfig rcfg;
118 rcfg.copyFrom(info);
119 step_size = step;
120 return begin(rcfg);
121 }
122
123 void setAudioInfo(AudioInfo newInfo) override {
124 // update the step size if a fixed to_sample_rate has been defined
125 if (to_sample_rate != 0) {
126 setStepSize(getStepSize(newInfo.sample_rate, to_sample_rate));
127 }
128 // notify about changes
129 LOGI("-> ResampleStream:")
131 }
132
134 AudioInfo out = audioInfo();
135 if (to_sample_rate != 0) {
136 out.sample_rate = to_sample_rate;
137 } else {
138 out.sample_rate = out.sample_rate * step_size;
139 }
140 return out;
141 }
142
144 void setStepSize(float step) {
145 LOGI("setStepSize: %f", step);
146 step_size = step;
147 }
148
149 void setTargetSampleRate(int rate) { to_sample_rate = rate; }
150
153 float getStepSize(float sampleRateFrom, float sampleRateTo) {
154 return sampleRateFrom / sampleRateTo;
155 }
156
158 float getStepSize() { return step_size; }
159
160 // int availableForWrite() override { return p_print->availableForWrite(); }
161
162 size_t write(const uint8_t *data, size_t len) override {
163 LOGD("ResampleStream::write: %d", (int)len);
164 //addNotifyOnFirstWrite();
165 size_t written = 0;
166 switch (info.bits_per_sample) {
167 case 16:
168 return write<int16_t>(p_print, data, len, written);
169 case 24:
170 return write<int24_t>(p_print, data, len, written);
171 case 32:
172 return write<int32_t>(p_print, data, len, written);
173 default:
174 TRACEE();
175 }
176 return 0;
177 }
178
180 void setBuffered(bool active) { is_buffer_active = active; }
181
183 void flush() PRINT_FLUSH_OVERRIDE {
184 if (p_out != nullptr && !out_buffer.isEmpty()) {
185 TRACED();
186#if USE_PRINT_FLUSH
187 p_out->flush();
188#endif
189 int rc = p_out->write(out_buffer.data(), out_buffer.available());
190 if (rc != out_buffer.available()) {
191 LOGE("write error %d vs %d", rc, out_buffer.available());
192 }
193 out_buffer.reset();
194 }
195 }
196
197 float getByteFactor() override { return 1.0f / step_size; }
198
199 protected:
200 Vector<uint8_t> last_samples{0};
201 float idx = 0;
202 bool is_first = true;
203 float step_size = 1.0;
204 int to_sample_rate = 0;
205 int bytes_per_frame = 0;
206 // optional buffering
207 bool is_buffer_active = USE_RESAMPLE_BUFFER;
208 SingleBuffer<uint8_t> out_buffer{0};
209 Print *p_out = nullptr;
210
213 int bytes_per_sample = cfg.bits_per_sample / 8;
214 int last_samples_size = cfg.channels * bytes_per_sample;
215 last_samples.resize(last_samples_size);
216 memset(last_samples.data(), 0, last_samples_size);
217 }
218
220 template <typename T>
221 size_t write(Print *p_out, const uint8_t *buffer, size_t bytes,
222 size_t &written) {
223 this->p_out = p_out;
224 if (step_size == 1.0f) {
225 written = p_out->write(buffer, bytes);
226 return written;
227 }
228 // prevent npe
229 if (info.channels == 0) {
230 LOGE("channels is 0");
231 return 0;
232 }
233 T *data = (T *)buffer;
234 int samples = bytes / sizeof(T);
235 size_t frames = samples / info.channels;
236 written = 0;
237
238 // avoid noise if audio does not start with 0
239 if (is_first) {
240 is_first = false;
241 setupLastSamples<T>(data, 0);
242 }
243
244 T frame[info.channels];
245 size_t frame_size = sizeof(frame);
246
247 // process all samples
248 while (idx < frames - 1) {
249 for (int ch = 0; ch < info.channels; ch++) {
250 T result = getValue<T>(data, idx, ch);
251 frame[ch] = result;
252 }
253
254 if (is_buffer_active) {
255 // if buffer is full we send it to output
256 if (out_buffer.availableForWrite() <= frame_size) {
257 flush();
258 }
259
260 // we use a buffer to minimize the number of output calls
261 int tmp_written =
262 out_buffer.writeArray((const uint8_t *)&frame, frame_size);
263 written += tmp_written;
264 if (frame_size != tmp_written) {
265 TRACEE();
266 }
267 } else {
268 int tmp = p_out->write((const uint8_t *)&frame, frame_size);
269 written += tmp;
270 if (tmp != frame_size) {
271 LOGE("Failed to write %d bytes: %d", (int)frame_size, tmp);
272 }
273 }
274
275 idx += step_size;
276 }
277
278 flush();
279
280 // save last samples to be made available at index position -1;
281 setupLastSamples<T>(data, frames - 1);
282 idx -= frames;
283
284 if (bytes != (written * step_size)) {
285 LOGD("write: %d vs %d", (int)bytes, (int)written);
286 }
287
288 // returns requested bytes to avoid rewriting of processed bytes
289 return frames * info.channels * sizeof(T);
290 }
291
293 template <typename T>
294 T getValue(T *data, float frame_idx, int channel) {
295 // interpolate value
296 int frame_idx0 = frame_idx;
297 // e.g. index -0.5 should be determined from -1 to 0 range
298 if (frame_idx0 == 0 && frame_idx < 0) frame_idx0 = -1;
299 int frame_idx1 = frame_idx0 + 1;
300 T val0 = lookup<T>(data, frame_idx0, channel);
301 T val1 = lookup<T>(data, frame_idx1, channel);
302
303 float result = mapT<float>(frame_idx, frame_idx0, frame_idx1, val0, val1);
304 LOGD("getValue idx: %d:%d / val: %d:%d / %f -> %f", frame_idx0, frame_idx1,
305 (int)val0, (int)val1, frame_idx, result)
306 return (float)round(result);
307 }
308
310 template <typename T>
311 T lookup(T *data, int frame, int channel) {
312 if (frame >= 0) {
313 return data[frame * info.channels + channel];
314 } else {
315 // index -1 (get last sample from previos run)
316 T *pt_last_samples = (T *)last_samples.data();
317 return pt_last_samples[channel];
318 }
319 }
321 template <typename T>
322 void setupLastSamples(T *data, int frame) {
323 for (int ch = 0; ch < info.channels; ch++) {
324 T *pt_last_samples = (T *)last_samples.data();
325 pt_last_samples[ch] = data[(frame * info.channels) + ch];
326 LOGD("setupLastSamples ch:%d - %d", ch, (int)pt_last_samples[ch])
327 }
328 }
329};
330
331} // namespace audio_tools
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition AudioOutput.h:59
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:119
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:127
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition BaseStream.h:150
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
Definition NoArduino.h:62
Base class for chained converting streams.
Definition AudioIO.h:156
virtual void setStream(Stream &stream) override
Defines/Changes the input & output.
Definition AudioIO.h:158
Dynamic Resampling. We can use a variable factor to speed up or slow down the playback.
Definition ResampleStream.h:33
size_t write(Print *p_out, const uint8_t *buffer, size_t bytes, size_t &written)
Writes the buffer to defined output after resampling.
Definition ResampleStream.h:221
void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition ResampleStream.h:123
float getStepSize(float sampleRateFrom, float sampleRateTo)
Definition ResampleStream.h:153
ResampleStream(Print &out)
Support for resampling via write.
Definition ResampleStream.h:38
ResampleStream(AudioOutput &out)
Definition ResampleStream.h:41
void setBuffered(bool active)
Activates buffering to avoid small incremental writes.
Definition ResampleStream.h:180
void setStepSize(float step)
influence the sample rate
Definition ResampleStream.h:144
float getStepSize()
Returns the actual step size.
Definition ResampleStream.h:158
AudioInfo audioInfoOut() override
Definition ResampleStream.h:133
void setupLastSamples(T *data, int frame)
store last samples to provide values for index -1
Definition ResampleStream.h:322
void flush()
When buffering is active, writes the buffered audio to the output.
Definition ResampleStream.h:183
void setupLastSamples(AudioInfo cfg)
Sets up the buffer for the rollover samples.
Definition ResampleStream.h:212
ResampleStream(Stream &io)
Support for resampling via write and read.
Definition ResampleStream.h:47
ResampleConfig defaultConfig()
Provides the default configuraiton.
Definition ResampleStream.h:57
T getValue(T *data, float frame_idx, int channel)
get the interpolated value for indicated (float) index value
Definition ResampleStream.h:294
ResampleStream(AudioStream &io)
Definition ResampleStream.h:51
T lookup(T *data, int frame, int channel)
lookup value for indicated frame & channel: index starts with -1;
Definition ResampleStream.h:311
int available() override
provides the number of entries that are available to read
Definition Buffers.h:227
int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:232
T * data()
Provides address of actual data.
Definition Buffers.h:261
void reset() override
clears the buffer
Definition Buffers.h:263
Definition NoArduino.h:142
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:103
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59
Optional Configuration object. The critical information is the channels and the step_size....
Definition ResampleStream.h:18
int to_sample_rate
Optional fixed target sample rate.
Definition ResampleStream.h:21