Skip to content

Commit 3728298

Browse files
committed
output_bit_depth argument
1 parent 4bfcd6c commit 3728298

7 files changed

Lines changed: 331 additions & 69 deletions

File tree

locale/circuitpython.pot

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ msgstr ""
186186
msgid "%q must be 1 when %q is True"
187187
msgstr ""
188188

189+
#: shared-bindings/audioi2sin/I2SIn.c
190+
msgid "%q must be 8, 16, 24, or 32"
191+
msgstr ""
192+
189193
#: py/argcheck.c shared-bindings/gifio/GifWriter.c
190194
#: shared-module/gifio/OnDiskGif.c
191195
msgid "%q must be <= %d"
@@ -1695,10 +1699,6 @@ msgstr ""
16951699
msgid "Ok"
16961700
msgstr ""
16971701

1698-
#: ports/raspberrypi/common-hal/audioi2sin/I2SIn.c
1699-
msgid "Only 16, 24, or 32 bit depth supported."
1700-
msgstr ""
1701-
17021702
#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c
17031703
#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c
17041704
#, c-format
@@ -2702,6 +2702,10 @@ msgstr ""
27022702
msgid "binary op %q not implemented"
27032703
msgstr ""
27042704

2705+
#: ports/raspberrypi/common-hal/audioi2sin/I2SIn.c
2706+
msgid "bit_depth must be 16, 24, or 32"
2707+
msgstr ""
2708+
27052709
#: ports/espressif/common-hal/audiobusio/PDMIn.c
27062710
#: ports/espressif/common-hal/audioi2sin/I2SIn.c
27072711
msgid "bit_depth must be 8, 16, 24, or 32."

ports/espressif/common-hal/audioi2sin/I2SIn.c

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self,
2121
const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select,
2222
const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock,
23-
uint32_t sample_rate, uint8_t bit_depth, bool mono, bool left_justified,
24-
bool samples_signed) {
23+
uint32_t sample_rate, uint8_t bit_depth, uint8_t output_bit_depth,
24+
bool mono, bool left_justified, bool samples_signed) {
2525

2626
if (bit_depth != 8 && bit_depth != 16 && bit_depth != 24 && bit_depth != 32) {
2727
mp_raise_ValueError(MP_ERROR_TEXT("bit_depth must be 8, 16, 24, or 32."));
@@ -66,6 +66,7 @@ void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self,
6666
self->mclk = main_clock;
6767
self->sample_rate = sample_rate;
6868
self->bit_depth = bit_depth;
69+
self->output_bit_depth = output_bit_depth;
6970
self->mono = mono;
7071
self->samples_signed = samples_signed;
7172

@@ -113,6 +114,72 @@ void common_hal_audioi2sin_i2sin_deinit(audioi2sin_i2sin_obj_t *self) {
113114
self->mclk = NULL;
114115
}
115116

117+
// Sign-extend a raw I2S sample (in the low `in_depth` bits of `raw`) to a
118+
// canonical int32 value.
119+
static inline int32_t i2sin_normalize_signed(uint32_t raw, uint8_t in_depth) {
120+
if (in_depth == 32) {
121+
return (int32_t)raw;
122+
}
123+
if (in_depth == 24) {
124+
uint32_t sign_bit = 0x800000u;
125+
return (int32_t)((raw ^ sign_bit) - sign_bit);
126+
}
127+
if (in_depth == 16) {
128+
return (int16_t)(raw & 0xffffu);
129+
}
130+
return (int8_t)(raw & 0xffu);
131+
}
132+
133+
// Read a single sample from the DMA scratch at the given byte offset for the
134+
// configured input bit depth.
135+
static inline uint32_t i2sin_read_raw(const uint8_t *src, uint8_t in_depth) {
136+
if (in_depth == 8) {
137+
return (uint32_t)(*src);
138+
}
139+
if (in_depth == 16) {
140+
uint16_t v;
141+
memcpy(&v, src, sizeof(v));
142+
return v;
143+
}
144+
uint32_t v;
145+
memcpy(&v, src, sizeof(v));
146+
return v;
147+
}
148+
149+
// Convert `raw` from `in_depth` to `out_depth` (shift-only semantics, sign-
150+
// preserving for signed) and write it to `buffer` at sample index `idx`.
151+
// Output element size: 1 byte at 8, 2 bytes at 16, 4 bytes at 24 or 32.
152+
static inline void i2sin_write_converted(void *buffer, uint32_t idx,
153+
uint32_t raw, uint8_t in_depth, uint8_t out_depth, bool samples_signed) {
154+
int32_t s = i2sin_normalize_signed(raw, in_depth);
155+
int32_t shifted;
156+
if (out_depth >= in_depth) {
157+
shifted = (int32_t)((uint32_t)s << (out_depth - in_depth));
158+
} else {
159+
shifted = s >> (in_depth - out_depth);
160+
}
161+
uint32_t u = (uint32_t)shifted;
162+
if (!samples_signed) {
163+
if (out_depth >= 32) {
164+
u ^= 0x80000000u;
165+
} else {
166+
uint32_t mask = (1u << out_depth) - 1u;
167+
u = (u & mask) ^ (1u << (out_depth - 1));
168+
}
169+
}
170+
switch (out_depth) {
171+
case 8:
172+
((uint8_t *)buffer)[idx] = (uint8_t)(u & 0xffu);
173+
break;
174+
case 16:
175+
((uint16_t *)buffer)[idx] = (uint16_t)(u & 0xffffu);
176+
break;
177+
default: // 24 or 32
178+
((uint32_t *)buffer)[idx] = u;
179+
break;
180+
}
181+
}
182+
116183
// I2S delivers signed PCM. When samples_signed is false, XOR each sample with
117184
// the sign bit for its width to convert to unsigned PCM (WAV convention).
118185
static void i2sin_convert_to_unsigned(void *buffer, uint32_t samples,
@@ -158,6 +225,49 @@ uint32_t common_hal_audioi2sin_i2sin_record_to_buffer(audioi2sin_i2sin_obj_t *se
158225
element_size = 4;
159226
}
160227

228+
if (self->output_bit_depth != self->bit_depth) {
229+
// Bit-depth conversion path: always read at input width into scratch,
230+
// convert each sample into the user's buffer at output width.
231+
const uint8_t in_depth = self->bit_depth;
232+
const uint8_t out_depth = self->output_bit_depth;
233+
const bool samples_signed = self->samples_signed;
234+
uint8_t scratch[256];
235+
const size_t in_frame_bytes = 2 * element_size;
236+
const size_t scratch_frames = sizeof(scratch) / in_frame_bytes;
237+
uint32_t produced = 0;
238+
while (produced < length) {
239+
size_t want_frames;
240+
if (self->mono) {
241+
want_frames = length - produced;
242+
} else {
243+
want_frames = (length - produced + 1) / 2;
244+
}
245+
if (want_frames > scratch_frames) {
246+
want_frames = scratch_frames;
247+
}
248+
size_t got_bytes = 0;
249+
esp_err_t err = i2s_channel_read(self->rx_chan, scratch,
250+
want_frames * in_frame_bytes, &got_bytes, portMAX_DELAY);
251+
CHECK_ESP_RESULT(err);
252+
size_t got_frames = got_bytes / in_frame_bytes;
253+
for (size_t i = 0; i < got_frames && produced < length; i++) {
254+
const uint8_t *frame = scratch + i * in_frame_bytes;
255+
uint32_t left_raw = i2sin_read_raw(frame, in_depth);
256+
i2sin_write_converted(buffer, produced++, left_raw,
257+
in_depth, out_depth, samples_signed);
258+
if (!self->mono && produced < length) {
259+
uint32_t right_raw = i2sin_read_raw(frame + element_size, in_depth);
260+
i2sin_write_converted(buffer, produced++, right_raw,
261+
in_depth, out_depth, samples_signed);
262+
}
263+
}
264+
if (got_frames < want_frames) {
265+
break;
266+
}
267+
}
268+
return produced;
269+
}
270+
161271
uint32_t produced;
162272
if (!self->mono) {
163273
size_t result = 0;
@@ -206,6 +316,10 @@ uint8_t common_hal_audioi2sin_i2sin_get_bit_depth(audioi2sin_i2sin_obj_t *self)
206316
return self->bit_depth;
207317
}
208318

319+
uint8_t common_hal_audioi2sin_i2sin_get_output_bit_depth(audioi2sin_i2sin_obj_t *self) {
320+
return self->output_bit_depth;
321+
}
322+
209323
uint32_t common_hal_audioi2sin_i2sin_get_sample_rate(audioi2sin_i2sin_obj_t *self) {
210324
return self->sample_rate;
211325
}

ports/espressif/common-hal/audioi2sin/I2SIn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct {
2323
const mcu_pin_obj_t *mclk;
2424
uint32_t sample_rate;
2525
uint8_t bit_depth;
26+
uint8_t output_bit_depth;
2627
bool mono;
2728
bool samples_signed;
2829
} audioi2sin_i2sin_obj_t;

0 commit comments

Comments
 (0)