arduino-audio-tools
CodecAPTX.h
Go to the documentation of this file.
1 
11 #pragma once
12 #include "AudioConfig.h"
13 #include "AudioTools/AudioCodecs/AudioCodecsBase.h"
14 #include "openaptx.h"
15 
16 namespace audio_tools {
17 
26 class APTXDecoder : public AudioDecoder {
27  public:
28  APTXDecoder(bool isHd = false) {
29  is_hd = isHd;
30  info.sample_rate = 44100;
31  info.channels = 2;
32  info.bits_per_sample = isHd ? 24 : 16;
33  }
34 
35  bool begin() override {
36  TRACEI();
37  ctx = aptx_init(is_hd);
38  is_first_write = true;
39  notifyAudioChange(info);
40  return ctx != nullptr;
41  }
42 
43  void end() override {
44  TRACEI();
45  bool dropped = aptx_decode_sync_finish(ctx);
46  aptx_finish(ctx);
47  ctx = nullptr;
48  }
49 
50  virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
51 
52  operator bool() { return ctx != nullptr; }
53 
54  virtual size_t write(const uint8_t *data, size_t len) {
55  LOGI("write: %d", len);
56  bool is_ok = true;
57  size_t dropped;
58  int synced;
59 
60  if (is_first_write) {
61  is_first_write = false;
62  if (!checkPrefix(data, len)) {
63  return 0;
64  }
65  }
66 
67  output_buffer.resize(len * 10);
68  memset(output_buffer.data(), 0, output_buffer.size());
69  processed = aptx_decode_sync(ctx, (const uint8_t *)data, len,
70  output_buffer.data(), output_buffer.size(),
71  &written, &synced, &dropped);
72 
73  checkSync(synced, dropped, is_ok);
74 
75  // If we have not decoded all supplied samples then decoding unrecoverable
76  // failed
77  if (processed != len) {
78  LOGE("aptX decoding reqested: %d eff: %d", len, processed);
79  is_ok = false;
80  }
81 
82  writeData(written, is_ok);
83 
84  return is_ok ? len : 0;
85  }
86 
87  protected:
88  struct aptx_context *ctx = nullptr;
89  Print *p_print = nullptr;
90  bool is_first_write = true;
91  Vector<uint8_t> output_buffer;
92  bool is_hd;
93  size_t processed;
94  size_t written;
95  bool syncing;
96 
98  void writeData(size_t written, bool &is_ok) {
99  if (written > 0) {
100  int samples = written / 3;
101  LOGI("written: %d", written);
102  LOGI("samples: %d", samples);
103  int24_t *p_int24 = (int24_t *)output_buffer.data();
104  int16_t *p_int16 = (int16_t *)output_buffer.data();
105  for (int j = 0; j < samples; j++) {
106  p_int16[j] = p_int24[j].getAndScale16();
107  }
108 
109  if (p_print->write((uint8_t *)output_buffer.data(), samples * 2) !=
110  samples * 2) {
111  LOGE("aptX decoding failed to write decoded data");
112  is_ok = false;
113  }
114  }
115  }
116 
118  void checkSync(bool synced, bool dropped, bool &is_ok) {
119  /* Check all possible states of synced, syncing and dropped status */
120  if (!synced) {
121  if (!syncing) {
122  LOGE("aptX decoding failed, synchronizing");
123  syncing = true;
124  is_ok = false;
125  }
126  if (dropped) {
127  LOGE("aptX synchronization successful, dropped %lu byte%s",
128  (unsigned long)dropped, (dropped != 1) ? "s" : "");
129  syncing = false;
130  is_ok = true;
131  }
132  if (!syncing) {
133  LOGE("aptX decoding failed, synchronizing");
134  syncing = true;
135  is_ok = false;
136  }
137  } else {
138  if (dropped) {
139  if (!syncing) LOGE("aptX decoding failed, synchronizing");
140  LOGE("aptX synchronization successful, dropped %lu byte%s",
141  (unsigned long)dropped, (dropped != 1) ? "s" : "");
142  syncing = false;
143  is_ok = false;
144  } else if (syncing) {
145  LOGI("aptX synchronization successful");
146  syncing = false;
147  is_ok = true;
148  }
149  }
150  }
151 
153  bool checkPrefix(const void *input_buffer, size_t length) {
154  bool result = true;
155  if (length >= 4 && memcmp(input_buffer, "\x4b\xbf\x4b\xbf", 4) == 0) {
156  if (is_hd) {
157  LOGE("aptX audio stream (not aptX HD)");
158  result = false;
159  }
160  } else if (length >= 6 &&
161  memcmp(input_buffer, "\x73\xbe\xff\x73\xbe\xff", 6) == 0) {
162  if (!is_hd) {
163  LOGE("aptX HD audio stream");
164  result = false;
165  }
166  } else {
167  if (length >= 4 && memcmp(input_buffer, "\x6b\xbf\x6b\xbf", 4) == 0) {
168  LOGE("standard aptX audio stream - not supported");
169  result = false;
170  } else {
171  LOGE("No aptX nor aptX HD audio stream");
172  result = false;
173  }
174  }
175  return result;
176  }
177 };
178 
187 class APTXEncoder : public AudioEncoder {
188  public:
189  APTXEncoder(bool isHd = false) {
190  is_hd = isHd;
191  info.sample_rate = 44100;
192  info.channels = 2;
193  info.bits_per_sample = isHd ? 24 : 16;
194  }
195 
196  bool begin() {
197  TRACEI();
198  input_buffer.resize(4 * 2);
199  output_buffer.resize(100 * (is_hd ? 6 : 4));
200 
201  LOGI("input_buffer.size: %d", input_buffer.size());
202  LOGI("output_buffer.size: %d", output_buffer.size());
203  LOGI("is_hd: %s", is_hd ? "true" : "false");
204  ctx = aptx_init(is_hd);
205  return ctx!=nullptr;
206  }
207 
208  virtual void end() {
209  TRACEI();
210  if (ctx != nullptr) {
211  size_t output_written = 0;
212  aptx_encode_finish(ctx, output_buffer.data(), output_buffer.size(),
213  &output_written);
214  if (output_written > 0) {
215  // write result to final output
216  int written = p_print->write((const uint8_t *)output_buffer.data(),
217  output_written);
218  if (written != output_written) {
219  LOGE("write requested: %d eff: %d", output_written, written);
220  }
221  }
222  aptx_finish(ctx);
223  ctx = nullptr;
224  }
225  }
226 
227  virtual const char *mime() { return "audio/aptx"; }
228 
229  virtual void setAudioInfo(AudioInfo info) {
231  switch (info.bits_per_sample) {
232  case 16:
233  is_hd = false;
234  break;
235  case 24:
236  is_hd = true;
237  break;
238  default:
239  LOGE("invalid bits_per_sample: %d", info.bits_per_sample);
240  }
241  }
242 
243  virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
244 
245  operator bool() { return ctx != nullptr; }
246 
247  virtual size_t write(const uint8_t *data, size_t len) {
248  LOGI("write: %d", len);
249  if (ctx == nullptr) return 0;
250  size_t output_written = 0;
251 
252  // process all bytes
253  int16_t *in_ptr16 = (int16_t *)data;
254  int in_samples = len / 2;
255  for (int j = 0; j < in_samples; j++) {
256  input_buffer[input_pos++].setAndScale16(in_ptr16[j]);
257 
258  // if input_buffer is full we encode
259  if (input_pos >= input_buffer.size()) {
260  size_t result = aptx_encode(
261  ctx, (const uint8_t *)input_buffer.data(), input_buffer.size() * 3,
262  output_buffer.data() + output_pos,
263  output_buffer.size() - output_pos, &output_written);
264 
265  output_pos += output_written;
266 
267  if (result != input_buffer.size() * 3) {
268  LOGW("encode requested: %d, eff: %d", input_buffer.size() * 3,
269  result);
270  }
271 
272  // if output buffer is full we write the result
273  if (output_pos + output_pos >= output_buffer.size()) {
274  int written =
275  p_print->write((const uint8_t *)output_buffer.data(), output_pos);
276  if (written != output_pos) {
277  LOGE("write requested: %d eff: %d", output_pos, written);
278  }
279  // restart at beginning of output buffer
280  output_pos = 0;
281  }
282  // restart at beginning of input buffer
283  input_pos = 0;
284  }
285  }
286 
287  return len;
288  }
289 
290  protected:
291  bool is_hd;
292  Vector<int24_t> input_buffer{4 * 2};
293  Vector<uint8_t> output_buffer;
294  int input_pos = 0;
295  int output_pos = 0;
296  Print *p_print = nullptr;
297  struct aptx_context *ctx = nullptr;
298 };
299 
300 } // namespace audio_tools
Decoder for OpenAptx. Depends on https://github.com/pschatzmann/libopenaptx.
Definition: CodecAPTX.h:26
virtual void setOutput(Print &out_stream)
Defines where the decoded result is written to.
Definition: CodecAPTX.h:50
void checkSync(bool synced, bool dropped, bool &is_ok)
Checks the syncronization.
Definition: CodecAPTX.h:118
bool checkPrefix(const void *input_buffer, size_t length)
Checks the prefix of the received data.
Definition: CodecAPTX.h:153
void writeData(size_t written, bool &is_ok)
Converts the data to 16 bit and writes it to final output.
Definition: CodecAPTX.h:98
Encoder for OpenAptx - Depends on https://github.com/pschatzmann/libopenaptx.
Definition: CodecAPTX.h:187
virtual void setAudioInfo(AudioInfo info)
Defines the sample rate, number of channels and bits per sample.
Definition: CodecAPTX.h:229
virtual const char * mime()
Provides the mime type of the encoded result.
Definition: CodecAPTX.h:227
Docoding of encoded audio into PCM data.
Definition: AudioCodecsBase.h:16
Encoding of PCM data.
Definition: AudioCodecsBase.h:84
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition: AudioCodecsBase.h:93
Definition: NoArduino.h:58
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition: Int24_4bytes_t.h:16
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
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