Skip to content

Commit 78c3c73

Browse files
committed
refactor common code. Use bitmath instead of loop
1 parent 2c16984 commit 78c3c73

3 files changed

Lines changed: 86 additions & 106 deletions

File tree

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

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "py/runtime.h"
1313
#include "shared-bindings/audioi2sin/I2SIn.h"
1414
#include "shared-bindings/microcontroller/Pin.h"
15+
#include "shared-module/audioi2sin/__init__.h"
1516

1617
#include "driver/i2s_std.h"
1718

@@ -142,61 +143,15 @@ static inline uint32_t i2sin_read_raw(const uint8_t *src, uint8_t in_depth) {
142143
return v;
143144
}
144145

145-
// Convert `raw` from `in_depth` to the explicitly-requested `out_depth`
146-
// (sign-preserving for signed) and write it to `buffer` at sample index `idx`.
147-
// This path runs only when the caller set `output_bit_depth`, i.e. asked for a
148-
// real bit-depth rescale: upscaling bit-replicates so full-scale input maps to
149-
// full-scale output (e.g. 16-bit 0xFFFF -> 24-bit 0xFFFFFF) and downscaling
150-
// arithmetic-shifts right. This is distinct from the implicit container width
151-
// set by the array typecode (e.g. a 24-bit sample carried in a 32-bit 'i'
152-
// slot), which only sign-extends and is handled by the default path.
153-
// Output element size: 1 byte at 8, 2 bytes at 16, 4 bytes at 24 or 32.
146+
// Normalize `raw` from `in_depth` for this port's wire format, then hand off to
147+
// the shared converter to rescale `in_depth` -> `out_depth` and write it to
148+
// `buffer` at sample index `idx`. The depth conversion + unsigned-WAV flip +
149+
// container store live in `shared_audioi2sin_write_converted` (shared with other
150+
// ports); see that helper for the upscale/downscale semantics.
154151
static inline void i2sin_write_converted(void *buffer, uint32_t idx,
155152
uint32_t raw, uint8_t in_depth, uint8_t out_depth, bool samples_signed) {
156153
int32_t s = i2sin_normalize_signed(raw, in_depth);
157-
int32_t shifted;
158-
if (out_depth > in_depth) {
159-
// Explicit upscale: bit-replicate the input across the wider output so
160-
// that full-scale input maps to full-scale output (e.g. 16-bit 0xFFFF
161-
// -> 24-bit 0xFFFFFF), filling out_depth bits rather than leaving the
162-
// new low bits zero.
163-
uint32_t in_mask = (in_depth >= 32) ? 0xffffffffu : ((1u << in_depth) - 1u);
164-
uint32_t in_bits = (uint32_t)s & in_mask;
165-
uint32_t result = 0;
166-
int remaining = out_depth;
167-
while (remaining > 0) {
168-
int take = (remaining >= (int)in_depth) ? (int)in_depth : remaining;
169-
result = (result << take) | (in_bits >> (in_depth - take));
170-
remaining -= take;
171-
}
172-
// Sign-extend the out_depth-bit result up to int32 so a 24-bit value in
173-
// a 32-bit container decodes correctly; harmless when out_depth == 32.
174-
shifted = (int32_t)(result << (32 - out_depth)) >> (32 - out_depth);
175-
} else if (out_depth == in_depth) {
176-
shifted = s;
177-
} else {
178-
shifted = s >> (in_depth - out_depth);
179-
}
180-
uint32_t u = (uint32_t)shifted;
181-
if (!samples_signed) {
182-
if (out_depth >= 32) {
183-
u ^= 0x80000000u;
184-
} else {
185-
uint32_t mask = (1u << out_depth) - 1u;
186-
u = (u & mask) ^ (1u << (out_depth - 1));
187-
}
188-
}
189-
switch (out_depth) {
190-
case 8:
191-
((uint8_t *)buffer)[idx] = (uint8_t)(u & 0xffu);
192-
break;
193-
case 16:
194-
((uint16_t *)buffer)[idx] = (uint16_t)(u & 0xffffu);
195-
break;
196-
default: // 24 or 32
197-
((uint32_t *)buffer)[idx] = u;
198-
break;
199-
}
154+
shared_audioi2sin_write_converted(buffer, idx, s, in_depth, out_depth, samples_signed);
200155
}
201156

202157
// I2S delivers signed PCM. When samples_signed is false, XOR each sample with

‎ports/raspberrypi/common-hal/audioi2sin/I2SIn.c‎

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "common-hal/audioi2sin/I2SIn.h"
1515
#include "shared-bindings/audioi2sin/I2SIn.h"
1616
#include "shared-bindings/microcontroller/Pin.h"
17+
#include "shared-module/audioi2sin/__init__.h"
1718
#include "bindings/rp2pio/StateMachine.h"
1819
#include "supervisor/port.h"
1920

@@ -397,17 +398,12 @@ static inline int32_t i2sin_normalize_signed(uint32_t raw, uint8_t in_depth,
397398
return (int16_t)(raw & 0xffffu);
398399
}
399400

400-
// Write `raw` (input-depth bits, just-read FIFO sample) to `buffer` at sample
401-
// index `idx`, converting from `in_depth` to the explicitly-requested
402-
// `out_depth` (sign-preserving for signed) and (if needed) flipping the sign
403-
// bit for the unsigned-WAV convention. This path runs only when the caller set
404-
// `output_bit_depth`, i.e. asked for a real bit-depth rescale: upscaling
405-
// bit-replicates so full-scale input maps to full-scale output (e.g. 16-bit
406-
// 0xFFFF -> 24-bit 0xFFFFFF) and downscaling arithmetic-shifts right. This is
407-
// distinct from the implicit container width set by the array typecode (e.g. a
408-
// 24-bit sample carried in a 32-bit 'i' slot), which only sign-extends and is
409-
// handled by the default `movesign24` path. Output element size follows
410-
// `out_depth`: 1 byte at 8, 2 bytes at 16, 4 bytes at 24 or 32.
401+
// Normalize `raw` (input-depth bits, just-read FIFO sample) for this port's wire
402+
// format, then hand off to the shared converter to rescale `in_depth` ->
403+
// `out_depth` and write it to `buffer` at sample index `idx`. The depth
404+
// conversion + unsigned-WAV flip + container store live in
405+
// `shared_audioi2sin_write_converted` (shared with other ports); see that helper
406+
// for the upscale/downscale semantics.
411407
//
412408
// For signed 24-bit output, the int32 slot holds the sign-extended value
413409
// (range -2^23 .. 2^23-1) — unlike the default `output_bit_depth=bit_depth=24`
@@ -417,49 +413,7 @@ static inline void i2sin_write_converted(void *buffer, uint32_t idx,
417413
uint32_t raw, uint8_t in_depth, uint8_t out_depth,
418414
bool samples_signed, bool left_justified) {
419415
int32_t s = i2sin_normalize_signed(raw, in_depth, left_justified);
420-
int32_t shifted;
421-
if (out_depth > in_depth) {
422-
// Explicit upscale: bit-replicate the input across the wider output so
423-
// that full-scale input maps to full-scale output (e.g. 16-bit 0xFFFF
424-
// -> 24-bit 0xFFFFFF), filling out_depth bits rather than leaving the
425-
// new low bits zero.
426-
uint32_t in_mask = (in_depth >= 32) ? 0xffffffffu : ((1u << in_depth) - 1u);
427-
uint32_t in_bits = (uint32_t)s & in_mask;
428-
uint32_t result = 0;
429-
int remaining = out_depth;
430-
while (remaining > 0) {
431-
int take = (remaining >= (int)in_depth) ? (int)in_depth : remaining;
432-
result = (result << take) | (in_bits >> (in_depth - take));
433-
remaining -= take;
434-
}
435-
// Sign-extend the out_depth-bit result up to int32 so a 24-bit value in
436-
// a 32-bit container decodes correctly; harmless when out_depth == 32.
437-
shifted = (int32_t)(result << (32 - out_depth)) >> (32 - out_depth);
438-
} else if (out_depth == in_depth) {
439-
shifted = s;
440-
} else {
441-
shifted = s >> (in_depth - out_depth);
442-
}
443-
uint32_t u = (uint32_t)shifted;
444-
if (!samples_signed) {
445-
if (out_depth >= 32) {
446-
u ^= 0x80000000u;
447-
} else {
448-
uint32_t mask = (1u << out_depth) - 1u;
449-
u = (u & mask) ^ (1u << (out_depth - 1));
450-
}
451-
}
452-
switch (out_depth) {
453-
case 8:
454-
((uint8_t *)buffer)[idx] = (uint8_t)(u & 0xffu);
455-
break;
456-
case 16:
457-
((uint16_t *)buffer)[idx] = (uint16_t)(u & 0xffffu);
458-
break;
459-
default: // 24 or 32
460-
((uint32_t *)buffer)[idx] = u;
461-
break;
462-
}
416+
shared_audioi2sin_write_converted(buffer, idx, s, in_depth, out_depth, samples_signed);
463417
}
464418

465419
uint32_t common_hal_audioi2sin_i2sin_record_to_buffer(audioi2sin_i2sin_obj_t *self,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
#include <stdbool.h>
10+
#include <stdint.h>
11+
12+
// Rescale an already-normalized signed sample and write it to `buffer` at sample
13+
// index `idx`. `s` is the sign-extended int32 value of the sample at `in_depth`
14+
// scale (the per-port `i2sin_normalize_signed` produces it from the raw FIFO
15+
// word, handling wire-format details like left-justification). This helper does
16+
// the depth conversion + unsigned-WAV flip + container store, which is identical
17+
// across ports.
18+
//
19+
// This path runs only when the caller set `output_bit_depth`, i.e. asked for a
20+
// real bit-depth rescale: upscaling bit-replicates so full-scale input maps to
21+
// full-scale output (e.g. 16-bit 0xFFFF -> 24-bit 0xFFFFFF) and downscaling
22+
// arithmetic-shifts right. This is distinct from the implicit container width
23+
// set by the array typecode (e.g. a 24-bit sample carried in a 32-bit 'i' slot),
24+
// which only sign-extends and is handled by the default (non-converting) path.
25+
// Output element size follows `out_depth`: 1 byte at 8, 2 bytes at 16, 4 bytes
26+
// at 24 or 32.
27+
static inline void shared_audioi2sin_write_converted(void *buffer, uint32_t idx,
28+
int32_t s, uint8_t in_depth, uint8_t out_depth, bool samples_signed) {
29+
int32_t shifted;
30+
if (out_depth > in_depth) {
31+
// Explicit upscale: left-justify the in_depth-bit sample at the top of a
32+
// 32-bit word and replicate the pattern downward so full-scale input
33+
// maps to full-scale output (e.g. 16-bit 0xFFFF -> 24-bit 0xFFFFFF). Two
34+
// steps cover every supported ratio (widest is 8 -> 32); the guard skips
35+
// the second step (and the resulting shift-by->=32 UB) when one suffices.
36+
uint32_t top = (uint32_t)s << (32 - in_depth);
37+
top |= top >> in_depth;
38+
if (in_depth * 2 < out_depth) {
39+
top |= top >> (in_depth * 2);
40+
}
41+
// Arithmetic-shift the high out_depth bits down, sign-extending to int32
42+
// so a 24-bit value in a 32-bit slot decodes correctly (no-op at 32).
43+
shifted = (int32_t)top >> (32 - out_depth);
44+
} else if (out_depth == in_depth) {
45+
shifted = s;
46+
} else {
47+
shifted = s >> (in_depth - out_depth);
48+
}
49+
uint32_t u = (uint32_t)shifted;
50+
if (!samples_signed) {
51+
// I2S delivers signed PCM; flip the sign bit to match the WAV unsigned
52+
// convention, at the output bit width.
53+
if (out_depth >= 32) {
54+
u ^= 0x80000000u;
55+
} else {
56+
uint32_t mask = (1u << out_depth) - 1u;
57+
u = (u & mask) ^ (1u << (out_depth - 1));
58+
}
59+
}
60+
switch (out_depth) {
61+
case 8:
62+
((uint8_t *)buffer)[idx] = (uint8_t)(u & 0xffu);
63+
break;
64+
case 16:
65+
((uint16_t *)buffer)[idx] = (uint16_t)(u & 0xffffu);
66+
break;
67+
default: // 24 or 32
68+
((uint32_t *)buffer)[idx] = u;
69+
break;
70+
}
71+
}

0 commit comments

Comments
 (0)