arduino-audio-tools
ResampleStream.h
1 #pragma once
2 
3 #include "AudioTools/AudioIO.h"
4 
5 namespace audio_tools {
6 
14 template <class T>
16  public:
22  void begin(T *transform, Stream *source) {
23  TRACED();
24  active = true;
25  p_stream = source;
26  p_transform = transform;
27  if (transform == nullptr) {
28  LOGE("transform is NULL");
29  active = false;
30  }
31  if (p_stream == nullptr) {
32  LOGE("p_stream is NULL");
33  active = false;
34  }
35  byte_factor = getByteFactor();
36  }
37 
38  size_t readBytes(uint8_t *data, size_t byteCount) {
39  LOGD("TransformationReader::readBytes: %d", (int)byteCount);
40  if (!active) {
41  LOGE("inactive");
42  return 0;
43  }
44  if (p_stream == nullptr) {
45  LOGE("p_stream is NULL");
46  return 0;
47  }
48  int read_size = byte_factor * byteCount;
49  LOGD("factor %f -> buffer %d bytes", byte_factor, read_size);
50  buffer.resize(read_size);
51  int read_eff = p_stream->readBytes(buffer.data(), read_size);
52  // stop when there is not data
53  if (read_eff==0) return 0;
54  // provide result
55  Print *tmp = setupOutput(data, byteCount);
56  p_transform->write(buffer.data(), read_eff);
57  restoreOutput(tmp);
58  return print_to_array.totalBytesWritten();
59  }
60 
61  int availableForWrite() { return print_to_array.availableForWrite(); }
62 
63  protected:
65  public:
66  void begin(uint8_t *array, size_t data_len) {
67  TRACED();
68  p_data = array;
69  max_len = data_len;
70  pos = 0;
71  }
72 
73  int availableForWrite() override { return max_len; }
74 
75  size_t write(const uint8_t *data, size_t byteCount) override {
76  LOGD("AdapterPrintToArray::write: %d (%d)", (int) byteCount, (int) pos);
77  if (pos + byteCount > max_len) return 0;
78  memcpy(p_data + pos, data, byteCount);
79 
80  pos += byteCount;
81  return byteCount;
82  }
83 
84  int totalBytesWritten() { return pos; }
85 
86  protected:
87  uint8_t *p_data;
88  size_t max_len;
89  size_t pos = 0;
90  } print_to_array;
91  float byte_factor = 0.0f;
92  Stream *p_stream = nullptr;
93  Vector<uint8_t> buffer{0}; // we allocate memory only when needed
94  T *p_transform = nullptr;
95  bool active = false;
96 
101  Print *setupOutput(uint8_t *data, size_t byteCount) {
102  Print *result = p_transform->getPrint();
103  p_transform->setStream(print_to_array);
104  print_to_array.begin(data, byteCount);
105 
106  return result;
107  }
110  void restoreOutput(Print *out) {
111  if (out) p_transform->setStream(*out);
112  }
113 
114  float getByteFactor(){
115  buffer.resize(64);
116  float input_size = 8.0;
117  Print *tmp = setupOutput(buffer.data(), 64);
118  p_transform->write(buffer.data(), input_size);
119  restoreOutput(tmp);
120  float output_size = print_to_array.totalBytesWritten();
121  float factor = input_size/output_size;
122  LOGI("input_size: %f / output_size: %f / factor (eff): %f", input_size, output_size, factor);
123  return factor;
124  }
125 };
126 
134  public:
135  void setupReader() {
136  assert(getStream() != nullptr);
137  reader.begin(this, getStream());
138  }
139 
140  virtual void setStream(Stream &stream) {
141  p_stream = &stream;
142  p_print = &stream;
143  }
144 
145  virtual void setStream(Print &print) { p_print = &print; }
146 
147  virtual Print *getPrint() { return p_print; }
148 
149  virtual Stream *getStream() { return p_stream; }
150 
151  size_t readBytes(uint8_t *data, size_t size) override {
152  LOGD("ReformatBaseStream::readBytes: %d", (int)size);
153  return reader.readBytes(data, size);
154  }
155 
156  int available() override {
157  return DEFAULT_BUFFER_SIZE; // reader.availableForWrite();
158  }
159 
160  int availableForWrite() override {
161  return DEFAULT_BUFFER_SIZE; // reader.availableForWrite();
162  }
163 
164  protected:
166  Stream *p_stream = nullptr;
167  Print *p_print = nullptr;
168  float factor;
169 };
170 
176 struct ResampleConfig : public AudioInfo {
177  float step_size = 1.0f;
179  int to_sample_rate = 0;
180  int buffer_size = DEFAULT_BUFFER_SIZE;
181 };
182 
192  public:
193  ResampleStream() = default;
194 
196  ResampleStream(Print &out) { setStream(out); }
200  setAudioInfo(out.audioInfo());
201  setStream(out);
202  }
203 
205  ResampleStream(Stream &io) { setStream(io); }
206 
210  setAudioInfo(io.audioInfo());
211  setStream(io);
212  }
213 
216  ResampleConfig cfg;
217  cfg.copyFrom(audioInfo());
218  return cfg;
219  }
220 
221  bool begin(ResampleConfig cfg) {
222  LOGI("begin step_size: %f", cfg.step_size);
223  setAudioInfo(cfg);
224  to_sample_rate = cfg.to_sample_rate;
225  out_buffer.resize(cfg.buffer_size);
226 
227  setupLastSamples(cfg);
228  setStepSize(cfg.step_size);
229  is_first = true;
230  // step_dirty = true;
231  bytes_per_frame = info.bits_per_sample / 8 * info.channels;
232 
233  // setup reader: e.g. if step size is 2 we need to double the input data
234  // reader.begin(this, step_size, p_stream);
235  if (p_stream != nullptr) {
236  setupReader();
237  }
238 
239  return true;
240  }
241 
242  bool begin(AudioInfo from, int toRate) {
243  ResampleConfig rcfg;
244  rcfg.copyFrom(from);
245  rcfg.to_sample_rate = toRate;
246  rcfg.step_size = getStepSize(from.sample_rate, toRate);
247  return begin(rcfg);
248  }
249 
250  bool begin(AudioInfo info, float step) {
251  ResampleConfig rcfg;
252  rcfg.copyFrom(info);
253  rcfg.step_size = step;
254  return begin(rcfg);
255  }
256 
257  void setAudioInfo(AudioInfo info) override {
258  AudioStream::setAudioInfo(info);
259  // update the step size if a fixed to_sample_rate has been defined
260  if (to_sample_rate != 0) {
261  setStepSize(getStepSize(info.sample_rate, to_sample_rate));
262  }
263  }
264 
266  void setStepSize(float step) {
267  LOGI("setStepSize: %f", step);
268  step_size = step;
269  }
270 
273  float getStepSize(float sampleRateFrom, float sampleRateTo) {
274  return sampleRateFrom / sampleRateTo;
275  }
276 
278  float getStepSize() { return step_size; }
279 
280  // int availableForWrite() override { return p_print->availableForWrite(); }
281 
282  size_t write(const uint8_t *buffer, size_t bytes) override {
283  LOGD("ResampleStream::write: %d", (int)bytes);
284  size_t written;
285  switch (info.bits_per_sample) {
286  case 16:
287  return write<int16_t>(p_print, buffer, bytes, written);
288  case 24:
289  return write<int24_t>(p_print, buffer, bytes, written);
290  case 32:
291  return write<int32_t>(p_print, buffer, bytes, written);
292  default:
293  TRACEE();
294  }
295  return 0;
296  }
297 
299  void setBuffered(bool active){
300  is_buffer_active = active;
301  }
302 
304  void flush() override {
305  if (p_out!=nullptr && !out_buffer.isEmpty()){
306  TRACED();
307  p_out->write(out_buffer.data(), out_buffer.available());
308  out_buffer.reset();
309  }
310  }
311 
312  protected:
313  Vector<uint8_t> last_samples{0};
314  float idx = 0;
315  bool is_first = true;
316  float step_size = 1.0;
317  int to_sample_rate = 0;
318  int bytes_per_frame = 0;
319  TransformationReader<ResampleStream> reader;
320  // optional buffering
321  bool is_buffer_active = false;
322  SingleBuffer<uint8_t> out_buffer{0};
323  Print *p_out=nullptr;
324 
327  int bytes_per_sample = cfg.bits_per_sample / 8;
328  int last_samples_size = cfg.channels * bytes_per_sample;
329  last_samples.resize(last_samples_size);
330  memset(last_samples.data(), 0, last_samples_size);
331  }
332 
334  template <typename T>
335  size_t write(Print *p_out, const uint8_t *buffer, size_t bytes,
336  size_t &written) {
337  this->p_out = p_out;
338  if (step_size == 1.0) {
339  return p_print->write(buffer, bytes);
340  }
341  // prevent npe
342  if (info.channels == 0) {
343  LOGE("channels is 0");
344  return 0;
345  }
346  T *data = (T *)buffer;
347  int samples = bytes / sizeof(T);
348  size_t frames = samples / info.channels;
349  written = 0;
350 
351  // avoid noise if audio does not start with 0
352  if (is_first) {
353  is_first = false;
354  setupLastSamples<T>(data, 0);
355  }
356 
357  T frame[info.channels];
358  size_t frame_size = sizeof(frame);
359 
360  // process all samples
361  while (idx < frames) {
362  for (int ch = 0; ch < info.channels; ch++) {
363  T result = getValue<T>(data, idx, ch);
364  frame[ch] = result;
365  }
366 
367  if (is_buffer_active){
368  // if buffer is full we send it to output
369  if (out_buffer.availableForWrite()<frame_size){
370  flush();
371  }
372 
373  // we use a buffer to minimize the number of output calls
374  written += out_buffer.writeArray((const uint8_t *)&frame, frame_size);
375  } else {
376  if (p_out->availableForWrite() < frame_size) {
377  TRACEE();
378  }
379  written += p_out->write((const uint8_t *)&frame, frame_size);
380  }
381 
382  idx += step_size;
383  }
384 
385  flush();
386 
387  // save last samples
388  setupLastSamples<T>(data, frames - 1);
389  idx -= frames;
390  // returns requested bytes to avoid rewriting of processed bytes
391  return bytes;
392  }
393 
394 
396  template <typename T>
397  T getValue(T *data, float frame_idx, int channel) {
398  // interpolate value
399  int frame_idx1 = frame_idx;
400  int frame_idx0 = frame_idx1 - 1;
401  T val0 = lookup<T>(data, frame_idx0, channel);
402  T val1 = lookup<T>(data, frame_idx1, channel);
403 
404  float result = mapFloat(frame_idx, frame_idx1, frame_idx0, val0, val1);
405  return (float)round(result);
406  }
407 
408  // lookup value for indicated frame & channel: index starts with -1;
409  template <typename T>
410  T lookup(T *data, int frame, int channel) {
411  if (frame >= 0) {
412  return data[frame * info.channels + channel];
413  } else {
414  // index -1
415  T *pt_last_samples = (T *)last_samples.data();
416  return pt_last_samples[channel];
417  }
418  }
419  // store last samples to provide values for index -1
420  template <typename T>
421  void setupLastSamples(T *data, int frame) {
422  for (int ch = 0; ch < info.channels; ch++) {
423  T *pt_last_samples = (T *)last_samples.data();
424  pt_last_samples[ch] = data[(frame * info.channels) + ch];
425  }
426  }
427 };
428 
429 } // namespace audio_tools
Base class for Output Adpapters.
Definition: AudioIO.h:11
Abstract Audio Ouptut class.
Definition: AudioOutput.h:22
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: AudioStreams.h:47
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:61
Definition: NoArduino.h:51
Base class for chained converting streams.
Definition: ResampleStream.h:133
Dynamic Resampling. We can use a variable factor to speed up or slow down the playback.
Definition: ResampleStream.h:191
size_t write(Print *p_out, const uint8_t *buffer, size_t bytes, size_t &written)
Writes the buffer to p_print after resampling.
Definition: ResampleStream.h:335
void flush() override
When buffering is active, writes the buffered audio to the output.
Definition: ResampleStream.h:304
float getStepSize(float sampleRateFrom, float sampleRateTo)
Definition: ResampleStream.h:273
ResampleStream(Print &out)
Support for resampling via write.
Definition: ResampleStream.h:196
ResampleStream(AudioOutput &out)
Definition: ResampleStream.h:199
void setBuffered(bool active)
Activates buffering to avoid small incremental writes.
Definition: ResampleStream.h:299
void setStepSize(float step)
influence the sample rate
Definition: ResampleStream.h:266
float getStepSize()
Returns the actual step size.
Definition: ResampleStream.h:278
void setupLastSamples(AudioInfo cfg)
Sets up the buffer for the rollover samples.
Definition: ResampleStream.h:326
ResampleStream(Stream &io)
Support for resampling via write and read.
Definition: ResampleStream.h:205
ResampleConfig defaultConfig()
Provides the default configuraiton.
Definition: ResampleStream.h:215
T getValue(T *data, float frame_idx, int channel)
get the interpolated value for indicated (float) index value
Definition: ResampleStream.h:397
ResampleStream(AudioStream &io)
Definition: ResampleStream.h:209
T * data()
Provides address of actual data.
Definition: Buffers.h:240
int available() override
provides the number of entries that are available to read
Definition: Buffers.h:207
int availableForWrite() override
provides the number of entries that are available to write
Definition: Buffers.h:212
void reset() override
clears the buffer
Definition: Buffers.h:242
Definition: NoArduino.h:114
ConverterStream Helper class which implements the converting readBytes with the help of write.
Definition: ResampleStream.h:15
void begin(T *transform, Stream *source)
setup of the TransformationReader class
Definition: ResampleStream.h:22
Print * setupOutput(uint8_t *data, size_t byteCount)
Definition: ResampleStream.h:101
void restoreOutput(Print *out)
restores the original output in the converter class
Definition: ResampleStream.h:110
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:9
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max)
Similar to Arduino map function but using floats.
Definition: AudioTypes.h:341
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:43
void copyFrom(AudioInfo info)
Same as set.
Definition: AudioTypes.h:78
Optional Configuration object. The critical information is the channels and the step_size....
Definition: ResampleStream.h:176
int to_sample_rate
Optional fixed target sample rate.
Definition: ResampleStream.h:179