arduino-audio-tools
CodecADPCMXQ.h
1 #pragma once
2 #include "AudioCodecs/AudioEncoded.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 
9 namespace audio_tools {
10 
11 enum 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 
25 class ADPCMDecoderXQ : public AudioDecoder {
26  public:
27  ADPCMDecoderXQ() {
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 void *input_buffer, size_t length) {
89  uint8_t *input_buffer8 = (uint8_t *)input_buffer;
90  LOGD("write: %d", (int)length);
91  for (int j = 0; j < length; 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 length;
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 
134 class ADPCMEncoderXQ : public AudioEncoder {
135  public:
136  ADPCMEncoderXQ() {
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 void *in_ptr, size_t in_size) override {
194  LOGD("write: %d", (int)in_size);
195  int16_t *input_buffer = (int16_t *)in_ptr;
196  pcm_block_size = samples_per_block * info.channels;
197  for (int j = 0; j < in_size / 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 in_size;
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
Docoding of encoded audio into PCM data.
Definition: AudioEncoded.h:18
Encoding of PCM data.
Definition: AudioEncoded.h:88
Definition: NoArduino.h:58
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:57