arduino-audio-tools
CodecBase64.h
1 
2 #pragma once
3 
4 #include "AudioTools/AudioCodecs/AudioCodecsBase.h"
5 
6 namespace audio_tools {
7 
8 
9 enum Base46Logic { NoCR, CRforFrame, CRforWrite };
10 static char encoding_table[] = {
11  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
12  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
13  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
14  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
15  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
16 static int mod_table[] = {0, 2, 1};
17 
18 static const int B64index[256] = {
19  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21  0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
22  58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
23  7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
24  25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
25  37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
26 
37 class DecoderBase64 : public AudioDecoder {
38  public:
43  DecoderBase64() { TRACED(); }
44 
51  TRACED();
52  setOutput(out);
53  }
54 
56  void setOutput(Print &out) override { p_print = &out; }
57 
59  void setNewLine(Base46Logic logic) { newline_logic = logic; }
60 
61  bool begin() override {
62  TRACED();
63  is_valid = newline_logic == NoCR;
64  active = true;
65  return true;
66  }
67 
68  void end() override {
69  TRACED();
70  // deconde ramaining bytes
71  int len = buffer.available();
72  uint8_t tmp[len];
73  buffer.readArray(tmp, len);
74  decodeLine(tmp, len);
75 
76  active = false;
77  buffer.resize(0);
78  }
79 
80  size_t write(const uint8_t *data, size_t len) override {
81  if (p_print == nullptr) return 0;
82  TRACED();
83  addToBuffer((uint8_t *)data, len);
84  int decode_size = 4; // maybe we should increase this ?
85  while (buffer.available() >= decode_size) {
86  uint8_t tmp[decode_size];
87  buffer.readArray(tmp, decode_size);
88  decodeLine(tmp, decode_size);
89  }
90 
91  return len;
92  }
93 
94  operator bool() override { return active; }
95 
96  protected:
97  bool active = false;
98  bool is_valid = false;
99  Base46Logic newline_logic = CRforFrame;
100  Vector<uint8_t> result;
101  RingBuffer<uint8_t> buffer{1500};
102  AudioInfo info;
103 
104  void decodeLine(uint8_t *data, size_t byteCount) {
105  LOGD("decode: %d", (int)byteCount);
106  int len = byteCount;
107 
108  unsigned char *p = (unsigned char *)data;
109  int pad = len > 0 && (len % 4 || p[len - 1] == '=');
110  const size_t L = ((len + 3) / 4 - pad) * 4;
111  result.resize(L / 4 * 3 + pad);
112  memset(result.data(), 0, result.size());
113 
114  for (size_t i = 0, j = 0; i < L; i += 4) {
115  int32_t n = static_cast<int32_t>(B64index[p[i]]) << 18 | B64index[p[i + 1]] << 12 |
116  B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
117  result[j++] = n >> 16;
118  result[j++] = n >> 8 & 0xFF;
119  result[j++] = n & 0xFF;
120  }
121  if (pad) {
122  int32_t n = static_cast<int32_t>(B64index[p[L]]) << 18 | B64index[p[L + 1]] << 12;
123  result[result.size() - 1] = n >> 16;
124 
125  if (len > L + 2 && p[L + 2] != '=') {
126  n |= B64index[p[L + 2]] << 6;
127  result.push_back(n >> 8 & 0xFF);
128  }
129  }
130  writeBlocking(p_print, result.data(), result.size());
131  }
132 
133  void addToBuffer(uint8_t *data, size_t len) {
134  TRACED();
135  if (buffer.size() < len) {
136  buffer.resize(len);
137  }
138  // syncronize to find a valid start position
139  int start = 0;
140  if (!is_valid) {
141  for (int j = 0; j < len; j++) {
142  if (data[j] == '\n') {
143  start = j;
144  is_valid = true;
145  break;
146  }
147  }
148  }
149 
150  if (is_valid) {
151  // remove white space
152  for (int j = start; j < len; j++) {
153  if (!isspace(data[j])) {
154  buffer.write(data[j]);
155  } else if (data[j] == '\n') {
156  int offset = buffer.available() % 4;
157  if (offset > 0) {
158  LOGW("Resync %d (-%d)...", buffer.available(), offset);
159  uint8_t tmp[4];
160  buffer.readArray(tmp, offset);
161  }
162  }
163  }
164  }
165  LOGD("buffer: %d, is_valid: %s", buffer.available(),
166  is_valid ? "true" : "false");
167  }
168 };
169 
170 
181 class EncoderBase64 : public AudioEncoder {
182  public:
183  // Empty Conbuffeructor - the output buffeream must be provided with begin()
184  EncoderBase64() {}
185 
186  // Conbuffeructor providing the output buffeream
187  EncoderBase64(Print &out) { p_print = &out; }
188 
190  void setOutput(Print &out_buffeream) override {
191  p_print = &out_buffeream;
192  }
193 
195  const char *mime() override { return "text/base64"; }
196 
198  void setNewLine(Base46Logic flag) { newline_logic = flag; }
199 
201  virtual bool begin() override {
202  is_open = true;
203  frame_size = info.bits_per_sample * info.channels / 8;
204  if (newline_logic != NoCR) {
205  if (frame_size==0){
206  LOGW("AudioInfo not defined");
207  // assume frame size
208  frame_size = 4;
209  }
210  p_print->write('\n');
211  flush();
212  }
213  return true;
214  }
215 
217  void end() override { is_open = false; }
218 
220  virtual size_t write(const uint8_t *data, size_t len) override {
221  LOGD("EncoderBase64::write: %d", (int)len);
222 
223  switch (newline_logic) {
224  case NoCR:
225  case CRforWrite:
226  encodeLine(data, len);
227  break;
228  case CRforFrame: {
229  int frames = len / frame_size;
230  int open = len;
231  int offset = 0;
232  while (open > 0) {
233  int write_size = min(frame_size, open);
234  encodeLine(data + offset, write_size);
235  open -= write_size;
236  offset += write_size;
237  }
238  break;
239  }
240  }
241 
242  return len;
243  }
244 
245  operator bool() override { return is_open; }
246 
247  bool isOpen() { return is_open; }
248 
249  protected:
250  Print *p_print = nullptr;
251  bool is_open;
252  Base46Logic newline_logic = CRforFrame;
253  Vector<uint8_t> ret;
254  AudioInfo info;
255  int frame_size;
256 
257  void flush() {
258 #if defined(ESP32)
259 # if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(3, 3, 5)
260  p_print->flush();
261 # endif
262 #else
263  p_print->flush();
264 #endif
265  }
266 
267  void encodeLine(const uint8_t *data, size_t input_length) {
268  LOGD("EncoderBase64::encodeLine: %d", (int)input_length);
269  int output_length = 4 * ((input_length + 2) / 3);
270  if (ret.size() < output_length + 1) {
271  ret.resize(output_length + 1);
272  }
273 
274  for (int i = 0, j = 0; i < input_length;) {
275  uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
276  uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
277  uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
278 
279  uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
280 
281  ret[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
282  ret[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
283  ret[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
284  ret[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
285  }
286 
287  for (int i = 0; i < mod_table[input_length % 3]; i++)
288  ret[output_length - 1 - i] = '=';
289 
290  // add a new line to the end
291  if (newline_logic != NoCR) {
292  ret[output_length] = '\n';
293  output_length++;
294  }
295 
296  writeBlocking(p_print, ret.data(), output_length);
297  flush();
298  }
299 };
300 
301 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioCodecsBase.h:16
Encoding of PCM data.
Definition: AudioCodecsBase.h:84
virtual int readArray(T data[], int len)
reads multiple values
Definition: Buffers.h:41
DecoderBase64 - Converts a Base64 encoded Stream into the original data stream. Decoding only gives a...
Definition: CodecBase64.h:37
void setOutput(Print &out) override
Defines the output Stream.
Definition: CodecBase64.h:56
DecoderBase64()
Constructor for a new DecoderBase64 object.
Definition: CodecBase64.h:43
DecoderBase64(Print &out)
Constructor for a new DecoderBase64 object.
Definition: CodecBase64.h:50
void setNewLine(Base46Logic logic)
We expect new lines to delimit the individual lines.
Definition: CodecBase64.h:59
EncoderBase64s - Encodes the input data into a Base64 string. By default each audio frame is followed...
Definition: CodecBase64.h:181
virtual size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as RAW.
Definition: CodecBase64.h:220
void setOutput(Print &out_buffeream) override
Defines the output Stream.
Definition: CodecBase64.h:190
virtual bool begin() override
starts the processing using the actual RAWAudioInfo
Definition: CodecBase64.h:201
const char * mime() override
Provides "text/base64".
Definition: CodecBase64.h:195
void end() override
stops the processing
Definition: CodecBase64.h:217
void setNewLine(Base46Logic flag)
We add a new line after each write.
Definition: CodecBase64.h:198
Definition: NoArduino.h:58
virtual int available()
provides the number of entries that are available to read
Definition: Buffers.h:366
virtual bool write(T data)
write add an entry to the buffer
Definition: Buffers.h:347
virtual size_t size()
Returns the maximum capacity of the buffer.
Definition: Buffers.h:383
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868