arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
CodecADPCMXQ.h
1#pragma once
2#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
3#include "adpcm-lib.h" // https://github.com/pschatzmann/arduino-adpcm-xq
4
5#define DEFAULT_NOISE_SHAPING NOISE_SHAPING_OFF
6#define DEFAULT_LOOKAHEAD 0
7#define DEFAULT_BLOCKSIZE_POW2 0
8
9namespace audio_tools {
10
11enum class ADPCMNoiseShaping {
12 AD_NOISE_SHAPING_OFF = 0, // flat noise (no shaping)
13 AD_NOISE_SHAPING_STATIC = 1, // first-order highpass shaping
14 AD_NOISE_SHAPING_DYNAMIC = 2
15};
16
26 public:
28 info.sample_rate = 44100;
29 info.channels = 2;
30 info.bits_per_sample = 16;
31 }
32
34 void setBlockSizePower(int pow) {
35 if (pow >= 8 && pow >= 15) {
36 block_size_pow2 = pow;
37 }
38 }
39
41 void setLookahead(int value) {
42 if (value <= 8) {
43 lookahead = value;
44 }
45 }
46
48 void setNoiseShaping(ADPCMNoiseShaping ns) { noise_shaping = (int)ns; }
49
50 bool begin() override {
51 TRACEI();
52 current_byte = 0;
53 if (adpcm_cnxt == nullptr) {
54 adpcm_cnxt = adpcm_create_context(info.channels, lookahead, noise_shaping,
55 initial_deltas);
56
57 if (block_size_pow2)
58 block_size = 1 << block_size_pow2;
59 else
60 block_size = 256 * info.channels *
61 (info.sample_rate < 11000 ? 1 : info.sample_rate / 11000);
62
63 samples_per_block =
64 (block_size - info.channels * 4) * (info.channels ^ 3) + 1;
65
66 pcm_block.resize(samples_per_block * info.channels);
67 adpcm_block.resize(block_size);
68 }
69
70 notifyAudioChange(info);
71 return true;
72 }
73
74 void end() override {
75 TRACEI();
76 if (adpcm_cnxt != nullptr) {
77 adpcm_free_context(adpcm_cnxt);
78 adpcm_cnxt = nullptr;
79 }
80 pcm_block.resize(0);
81 adpcm_block.resize(0);
82 }
83
84 virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
85
86 operator bool() override { return adpcm_cnxt != nullptr; }
87
88 virtual size_t write(const uint8_t *data, size_t len) {
89 uint8_t *input_buffer8 = (uint8_t *)data;
90 LOGD("write: %d", (int)len);
91 for (int j = 0; j < len; j++) {
92 adpcm_block[current_byte++] = input_buffer8[j];
93 if (current_byte == block_size) {
94 decode(current_byte);
95 current_byte = 0;
96 }
97 }
98 return len;
99 }
100
101 protected:
102 int current_byte = 0;
103 void *adpcm_cnxt = nullptr;
104 Vector<int16_t> pcm_block;
105 Vector<uint8_t> adpcm_block;
106 int32_t initial_deltas[2] = {0};
107 Print *p_print = nullptr;
108 int samples_per_block = 0, lookahead = DEFAULT_LOOKAHEAD,
109 noise_shaping = (int)DEFAULT_NOISE_SHAPING,
110 block_size_pow2 = DEFAULT_BLOCKSIZE_POW2, block_size = 0;
111
112 bool decode(int this_block_adpcm_samples) {
113 int result = adpcm_decode_block(pcm_block.data(), adpcm_block.data(),
114 block_size, info.channels);
115 if (result != samples_per_block) {
116 LOGE("adpcm_decode_block: %d instead %d", result,
117 this_block_adpcm_samples);
118 return false;
119 }
120 int write_size = samples_per_block * info.channels * 2;
121 p_print->write((uint8_t *)pcm_block.data(), write_size);
122 return true;
123 }
124};
125
135 public:
137 info.sample_rate = 44100;
138 info.channels = 2;
139 info.bits_per_sample = 16;
140 }
141
143 void setBlockSizePower(int pow) {
144 if (pow >= 8 && pow >= 15) {
145 block_size_pow2 = pow;
146 }
147 }
148
150 void setLookahead(int value) {
151 if (value <= 8) {
152 lookahead = value;
153 }
154 }
155
157 void setNoiseShaping(ADPCMNoiseShaping ns) { noise_shaping = (int)ns; }
158
159 bool begin() override {
160 TRACEI();
161
162 if (block_size_pow2)
163 block_size = 1 << block_size_pow2;
164 else
165 block_size = 256 * info.channels *
166 (info.sample_rate < 11000 ? 1 : info.sample_rate / 11000);
167
168 samples_per_block =
169 (block_size - info.channels * 4) * (info.channels ^ 3) + 1;
170
171 pcm_block.resize(samples_per_block * info.channels);
172 adpcm_block.resize(block_size);
173 current_sample = 0;
174 return true;
175 }
176
177 void end() override {
178 TRACEI();
179 if (adpcm_cnxt != nullptr) {
180 adpcm_free_context(adpcm_cnxt);
181 adpcm_cnxt = nullptr;
182 }
183 pcm_block.resize(0);
184 adpcm_block.resize(0);
185 }
186
187 const char *mime() override { return "audio/adpcm"; }
188
189 void setOutput(Print &out_stream) override { p_print = &out_stream; }
190
191 operator bool() override { return adpcm_cnxt != nullptr; }
192
193 size_t write(const uint8_t *data, size_t len) override {
194 LOGD("write: %d", (int)len);
195 int16_t *input_buffer = (int16_t *)data;
196 pcm_block_size = samples_per_block * info.channels;
197 for (int j = 0; j < len / 2; j++) {
198 pcm_block[current_sample++] = input_buffer[j];
199 if (current_sample == samples_per_block * info.channels) {
200 encode();
201 current_sample = 0;
202 }
203 }
204 return len;
205 }
206
207 protected:
208 int current_sample = 0;
209 void *adpcm_cnxt = nullptr;
210 Vector<int16_t> pcm_block;
211 Vector<uint8_t> adpcm_block;
212 Print *p_print = nullptr;
213 int samples_per_block = 0, lookahead = DEFAULT_LOOKAHEAD,
214 noise_shaping = (int)DEFAULT_NOISE_SHAPING,
215 block_size_pow2 = DEFAULT_BLOCKSIZE_POW2, block_size = 0, pcm_block_size;
216 bool is_first = true;
217
218 bool encode() {
219 // if this is the first block, compute a decaying average (in reverse) so
220 // that we can let the encoder know what kind of initial deltas to expect
221 // (helps initializing index)
222
223 if (adpcm_cnxt == nullptr) {
224 is_first = false;
225 int32_t average_deltas[2];
226
227 average_deltas[0] = average_deltas[1] = 0;
228
229 for (int i = samples_per_block * info.channels; i -= info.channels;) {
230 average_deltas[0] -= average_deltas[0] >> 3;
231 average_deltas[0] +=
232 abs((int32_t)pcm_block[i] - pcm_block[i - info.channels]);
233
234 if (info.channels == 2) {
235 average_deltas[1] -= average_deltas[1] >> 3;
236 average_deltas[1] +=
237 abs((int32_t)pcm_block[i - 1] - pcm_block[i + 1]);
238 }
239 }
240
241 average_deltas[0] >>= 3;
242 average_deltas[1] >>= 3;
243
244 adpcm_cnxt = adpcm_create_context(info.channels, lookahead, noise_shaping,
245 average_deltas);
246 }
247
248 size_t num_bytes;
249 adpcm_encode_block(adpcm_cnxt, adpcm_block.data(), &num_bytes,
250 pcm_block.data(), samples_per_block);
251
252 if (num_bytes != block_size) {
253 LOGE(
254 "adpcm_encode_block() did not return expected value "
255 "(expected %d, got %d)!\n",
256 block_size, (int)num_bytes);
257 return false;
258 }
259
260 p_print->write(adpcm_block.data(), block_size);
261 return true;
262 }
263};
264
265} // namespace audio_tools
266
Decoder for ADPCM-XQ. Depends on https://github.com/pschatzmann/arduino-adpcm-xq.
Definition CodecADPCMXQ.h:25
virtual void setOutput(Print &out_stream)
Defines where the decoded result is written to.
Definition CodecADPCMXQ.h:84
void setNoiseShaping(ADPCMNoiseShaping ns)
Defines the noise shaping.
Definition CodecADPCMXQ.h:48
void setBlockSizePower(int pow)
set bocksizes as 2^pow: range from 8 to 15
Definition CodecADPCMXQ.h:34
void setLookahead(int value)
Set look ahead bytes from 0 to 8.
Definition CodecADPCMXQ.h:41
Encoder for ADPCM-XQ - Depends on https://github.com/pschatzmann/arduino-adpcm-xq.
Definition CodecADPCMXQ.h:134
void setNoiseShaping(ADPCMNoiseShaping ns)
Defines the noise shaping.
Definition CodecADPCMXQ.h:157
const char * mime() override
Provides the mime type of the encoded result.
Definition CodecADPCMXQ.h:187
void setBlockSizePower(int pow)
set bocksizes as 2^pow: range from 8 to 15
Definition CodecADPCMXQ.h:143
void setLookahead(int value)
Set look ahead bytes from 0 to 8.
Definition CodecADPCMXQ.h:150
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Encoding of PCM data.
Definition AudioCodecsBase.h:90
Definition NoArduino.h:62
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
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