arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
CodecAPTX.h
Go to the documentation of this file.
1
11#pragma once
12#include "AudioToolsConfig.h"
13#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
14#include "openaptx.h"
15
16namespace audio_tools {
17
26class 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();
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
187class 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 const char * mime()
Provides the mime type of the encoded result.
Definition CodecAPTX.h:227
virtual void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition CodecAPTX.h:229
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Encoding of PCM data.
Definition AudioCodecsBase.h:90
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:99
Definition NoArduino.h:62
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 AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
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