I2S — Digital Audio
One controller per instance — the harvested i2s@ devicetree node (I2S0/I2S1 on the
ESP32 family) — addressed by index and configured entirely at construction: sample rate,
channels, bit depth, block size. Samples are 16-bit; one block per call.
I2S
An audio stream with the PWM discipline: the verbs own their setup. The first write() configures and starts the TX direction, the first read() the RX — a write-only program (a tone to an amplifier) never touches the RX engine at all.
Example first
import { I2S } from '@typecad/hal';
const audio = new I2S(0, { hz: 16000 }); // I2S0, 16 kHz
// one block of 16-bit samples; a stereo frame is two entries (L, R)
audio.write([1000, -1000, 2000, -2000, 0, 0]);
const first = audio.read(); // starts RX on first use
const second = audio.readAt(1); // any element of the last block| Constructor option | Type | Meaning |
|---|---|---|
| instance | number | Controller index — 0 is the board’s first (I2S0 on the ESP32 family) |
| hz | number | Sample rate (default 16000) |
| channels | number | 1 mono / 2 stereo (default 2) |
| bits | number | Bits per sample (16) |
| blockFrames | number | Block granularity in frames — one write/read is one block (default 64) |
All methods
| Method | What it does |
|---|---|
| write(samples) | Send one block — up to blockFrames × channels 16-bit samples; short writes zero-pad. First call configures and starts TX |
| read() | Receive one block; returns the first sample. First call configures and starts RX |
| readAt(index) | Element index of the last received block (0 .. blockFrames × channels − 1) — read() must have run first |
the one-jumper bench test
The hardware loopback: bridge the board's data-out pad to its data-in pad and the write path reads back through the read path — no codec, no amplifier. The suite runs exactly this on real silicon: a tone block written, DMA-clocked out and back, and asserted sample by sample.
Example first
import { I2S, Time } from '@typecad/hal';
// ONE JUMPER: bridge the board's data-out pad to data-in
// (esp32s3_devkitC i2s0_default: O_SD GPIO38 → I_SD GPIO41)
const audio = new I2S(0, { hz: 16000, channels: 2, blockFrames: 16 });
audio.write([1000, -1000, 2000, -2000]);
Time.sleep(500); // the DMA clocks the block out and back
const s0 = audio.read(); // 1000
const s1 = audio.readAt(1); // -1000 — the block, intactOn the esp32s3_devkitC (i2s0_default), the jumper is O_SD (GPIO38) to I_SD
(GPIO41). The settle sleep is honest: the DMA needs time to clock the block out and back
through the wire.