### Local setup commands for Pico SDK 2.3.0 Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/README.md Clones and prepares the Pico SDK, examples, and extras repositories required for local builds. Run these in the same level as the project folder. ```bash > git clone -b 2.3.0 https://github.com/raspberrypi/pico-sdk.git > cd pico-sdk > git submodule update -i > cd .. > git clone -b sdk-2.3.0 https://github.com/raspberrypi/pico-examples.git > > git clone -b sdk-2.3.0 https://github.com/raspberrypi/pico-extras.git > > git clone -b main https://github.com/elehobica/pico_audio_i2s_32b.git ``` -------------------------------- ### Example custom connection with audio_complete_connection Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows how to define a custom connection struct with function pointers and complete it with audio_complete_connection. The example uses a mono_to_stereo_consumer_take for consumer_pool_take and default implementations for the other operations, then creates a consumer pool and completes the connection. ```c struct buffer_copying_on_consumer_take_connection conn = { .core = { .consumer_pool_take = mono_to_stereo_consumer_take, .consumer_pool_give = consumer_pool_give_buffer_default, .producer_pool_take = producer_pool_take_buffer_default, .producer_pool_give = producer_pool_give_buffer_default, } }; audio_buffer_pool_t *consumer = audio_new_consumer_pool(&consumer_format, 2, 256); audio_complete_connection(&conn.core, producer_pool, consumer); ``` -------------------------------- ### audio_i2s_config_t initialization example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/configuration.md Example initialization of the audio_i2s_config_t struct from the sine_wave sample. Uses the default macros for data_pin and clock_pin_base, and assigns DMA channels 0 and 1 with PIO state machine 0. ```c static audio_i2s_config_t i2s_config = { .data_pin = PICO_AUDIO_I2S_DATA_PIN, .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, .dma_channel0 = 0, .dma_channel1 = 1, .pio_sm = 0 }; ``` -------------------------------- ### Manual buffer configuration example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows how to allocate a buffer with audio_new_buffer, fill it with silence, set sample_count, and give it to the consumer via give_audio_buffer. ```c audio_buffer_t *ab = audio_new_buffer(&producer_format, 1156); int32_t *samples = (int32_t *) ab->buffer->bytes; for (uint i = 0; i < ab->max_sample_count; i++) { samples[i*2+0] = 0; // left samples[i*2+1] = 0; // right } ab->sample_count = ab->max_sample_count; give_audio_buffer(pool, ab); // make it available to the consumer ``` -------------------------------- ### audio_i2s_connect example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Creates a producer pool with 3 buffers of 1156 samples and connects it using audio_i2s_connect. Asserts success. ```c audio_buffer_pool_t *producer_pool = audio_new_producer_pool(&producer_format, 3, 1156); bool __unused ok = audio_i2s_connect(producer_pool); assert(ok); ``` -------------------------------- ### audio_i2s_connect_extra advanced options examples Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Two examples using audio_i2s_connect_extra: the first uses a built-in copy-on-take connection with 4 buffers of 512 samples; the second supplies a custom connection vtable with 2 buffers of 256 samples. Both assert success. ```c // 4 consumer buffers of 512 samples, copy-on-take connection bool ok = audio_i2s_connect_extra(producer_pool, false, 4, 512, NULL); assert(ok); ``` ```c // Or supply your own connection vtable: static struct buffer_copying_on_consumer_take_connection my_conn = { .core = { .consumer_pool_take = my_take, .consumer_pool_give = consumer_pool_give_buffer_default, .producer_pool_take = producer_pool_take_buffer_default, .producer_pool_give = producer_pool_give_buffer_default, } }; bool ok2 = audio_i2s_connect_extra(producer_pool, false, 2, 256, &my_conn.core); ``` -------------------------------- ### Example: creating a producer pool Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows how to define an audio format and buffer format, then create a producer pool with 3 buffers of 1156 samples each. Uses AUDIO_PCM_FORMAT_S32 and stereo channels. ```c static audio_format_t audio_format = { .sample_freq = 44100, .pcm_format = AUDIO_PCM_FORMAT_S32, .channel_count = AUDIO_CHANNEL_STEREO }; static audio_buffer_format_t producer_format = { .format = &audio_format, .sample_stride = 8 }; audio_buffer_pool_t *ap = audio_new_producer_pool(&producer_format, 3, 1156); // 3 buffers, 1156 samples each ``` -------------------------------- ### Example: Stereo S16 to Stereo S32 conversion Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/sample_conversion.md Example usage of converting_copy for widening stereo S16 to stereo S32, used for 32-bit DAC output from 16-bit sources. ```cpp // Stereo S16 -> Stereo S32 (widening, used for 32-bit DAC output from 16-bit sources) converting_copy, Stereo>::copy( (int32_t *) dest, (const int16_t *) src, sample_count); ``` -------------------------------- ### Example usage of consumer_pool_take as a connection slot Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/sample_conversion.md Shows how to use consumer_pool_take directly as the consumer_pool_take slot of a connection. The example creates a buffer_copying_on_consumer_take_connection and calls consumer_pool_take with Stereo for both ToFmt and FromFmt, passing true for block. ```cpp // Use directly as the consumer_pool_take slot of a connection: static struct buffer_copying_on_consumer_take_connection conn; audio_buffer_t *buf = consumer_pool_take, Stereo>((audio_connection_t *) &conn, true); ``` -------------------------------- ### Basic audio_i2s_setup example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Basic usage of audio_i2s_setup with a 44.1 kHz stereo S32 format. It defines the audio format, producer format, and I2S configuration, then calls audio_i2s_setup in app_init and panics if the return is NULL. ```c #include "pico/audio.h" #include "pico/audio_i2s.h" static audio_format_t audio_format = { .sample_freq = 44100, .pcm_format = AUDIO_PCM_FORMAT_S32, .channel_count = AUDIO_CHANNEL_STEREO }; static audio_buffer_format_t producer_format = { .format = &audio_format, .sample_stride = 8 // 4 bytes * 2 channels }; static audio_i2s_config_t i2s_config = { .data_pin = PICO_AUDIO_I2S_DATA_PIN, // 18 .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, // 16 .dma_channel0 = 0, .dma_channel1 = 1, .pio_sm = 0 }; void app_init(void) { const audio_format_t *out_format = audio_i2s_setup(&audio_format, &audio_format, &i2s_config); if (!out_format) { panic("PicoAudio: Unable to open audio device.\n"); } } ``` -------------------------------- ### sample_converter::convert_sample Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/sample_conversion.md Documentation for the sample_converter template's convert_sample static member function, including all specializations and an example. ```APIDOC ## `sample_converter::convert_sample` ### Description Converts a single audio sample from one format to another. The primary template is unusable as-is; only the listed specializations provide a `convert_sample` member. ### Method Static member function ### Signature `static typename ToFmt::sample_t convert_sample(const typename FromFmt::sample_t &sample);` ### Parameters - **sample** (const typename FromFmt::sample_t&) - Required - The input sample to convert. ### Return Value - **typename ToFmt::sample_t** - The converted sample in the target format. ### Specializations | ToFmt | FromFmt | Conversion expression | |---|---|---| | `Fmt` (identical) | `Fmt` | `return sample;` (identity) | | `FmtS16` | `FmtU16` | `sample ^ 0x8000u` | | `FmtS16` | `FmtS8` | `sample << 8u` | | `FmtS16` | `FmtU8` | `(sample << 8u) ^ 0x8000u` | | `FmtU16` | `FmtS8` | `(sample << 8u) ^ 0x8000u` | | `FmtU16` | `FmtU8` | `sample << 8u` | | `FmtU16` | `FmtS16` | `sample ^ 0x8000u` | | `FmtS8` | `FmtU16` | `(sample ^ 0x8000u) >> 8u` | | `FmtS8` | `FmtU8` | `sample ^ 0x80` | | `FmtS8` | `FmtS16` | `sample >> 8u` | | `FmtU8` | `FmtU16` | `sample >> 8u` | | `FmtU8` | `FmtS8` | `sample ^ 0x80` | | `FmtU8` | `FmtS16` | `(sample ^ 0x8000u) >> 8u` | ### Example ```cpp #include "pico/sample_conversion.h" // convert one unsigned 16-bit sample to signed 16-bit uint16_t u = 0x8000u; int16_t s = sample_converter::convert_sample(u); // 0 (midpoint) ``` ``` -------------------------------- ### audio_i2s_connect_thru usage example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Creates a producer pool and connects it using audio_i2s_connect_thru with a NULL connection, which uses the built-in copy-on-take connection. ```c audio_buffer_pool_t *pool = audio_new_producer_pool(&producer_format, 3, 1156); audio_i2s_connect_thru(pool, NULL); // 2 buffers, 256 samples each, copy on take ``` -------------------------------- ### audio_i2s_end usage example Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Shows how to properly deinitialize the I2S pipeline by first disabling playback with audio_i2s_set_enabled(false) and then calling audio_i2s_end() to free all resources. ```c void app_deinit(void) { audio_i2s_set_enabled(false); // stop DMA + IRQ first audio_i2s_end(); // free everything } ``` -------------------------------- ### Example basic produce loop with take_audio_buffer and give_audio_buffer Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows a non-blocking take_audio_buffer call, filling the buffer with interleaved stereo samples, setting sample_count, and giving it back. If take returns NULL, the function returns early. ```c void decode() { audio_buffer_t *buffer = take_audio_buffer(ap, false); // non-blocking if (buffer == NULL) return; int32_t *samples = (int32_t *) buffer->buffer->bytes; for (uint i = 0; i < buffer->max_sample_count; i++) { samples[i*2+0] = value0; // L samples[i*2+1] = value1; // R } buffer->sample_count = buffer->max_sample_count; give_audio_buffer(ap, buffer); } ``` -------------------------------- ### Example error handling for non-blocking take returning NULL Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows how to handle a NULL return from take_audio_buffer when block is false. The comment explains that the I2S driver will play silence for that DMA slot. ```c audio_buffer_t *buffer = take_audio_buffer(ap, false); if (buffer == NULL) { // No free producer buffer right now; skip this callback iteration. // The I2S driver will play silence for this DMA slot instead. return; } // ... fill and give back ... ``` -------------------------------- ### Call producer_pool_blocking_give with Stereo Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/sample_conversion.md Example call to producer_pool_blocking_give with Stereo formats, blocking until the producer buffer is copied into the consumer pool. The connection is passed as &pg_conn.core and the full producer buffer as full_producer_buffer. ```cpp // Transfer one producer buffer into the consumer pool, blocking until copied: producer_pool_blocking_give, Stereo>(&pg_conn.core, full_producer_buffer); ``` -------------------------------- ### Upsample 22050 Hz buffer to 44100 Hz using audio_upsample Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Example call to audio_upsample with a step of 0x800 (2048/4096) to achieve 2x oversampling. The step must be less than 0x1000; otherwise results are undefined. ```c // Upsample a 22050 Hz buffer to 44100 Hz (step = 0x800, i.e. 1/2 input sample per output sample). static const uint32_t UPSAMPLE_STEP = 0x800; // 2048 / 4096 -> 2x oversample audio_upsample(input_buf, output_buf, 1156, UPSAMPLE_STEP); ``` -------------------------------- ### Direct use of sample_converter::convert_sample Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/sample_conversion.md Direct usage example converting a single unsigned 16-bit sample to signed 16-bit using sample_converter::convert_sample. The input 0x8000u maps to the midpoint value 0. ```cpp #include "pico/sample_conversion.h" // convert one unsigned 16-bit sample to signed 16-bit uint16_t u = 0x8000u; int16_t s = sample_converter::convert_sample(u); // 0 (midpoint) ``` -------------------------------- ### Enable or disable I2S playback Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Starts or stops I2S playback. Passing true starts playback, false stops it. In CORE1 mode, it waits for Core 1 responses and may panic if Core 1 does not respond within the timeout. ```c audio_i2s_set_enabled(true); // start playback // ... application main loop ... audio_i2s_set_enabled(false); // stop playback ``` -------------------------------- ### audio_i2s_setup Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Configures the I2S audio interface with the given input and output audio formats and configuration. It stores the format pointers, sets up PIO pins, claims a PIO state machine, allocates a silence buffer, and configures DMA channels for audio output. ```APIDOC ## audio_i2s_setup ### Description Configures the I2S audio interface with the given input and output audio formats and configuration. It stores the format pointers, sets up PIO pins, claims a PIO state machine, allocates a silence buffer, and configures DMA channels for audio output. ### Signature ```c const audio_format_t *audio_i2s_setup(const audio_format_t *i2s_input_audio_format, const audio_format_t *i2s_output_audio_format, const audio_i2s_config_t *config); ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|---------|---------|-------------| | `i2s_input_audio_format` | `const audio_format_t *` | yes | — | Format of the audio the application produces (also stored as the sample-conversion reference) | | `i2s_output_audio_format` | `const audio_format_t *` | yes | — | Format actually emitted on the I2S bus; `channel_count` must be `AUDIO_CHANNEL_STEREO`, `pcm_format` must be `AUDIO_PCM_FORMAT_S16` or `AUDIO_PCM_FORMAT_S32` (asserted) | | `config` | `const audio_i2s_config_t *` | yes | — | GPIO pins, DMA channel pair and PIO state machine (see [configuration.md](../configuration.md)) | ### Return Type `const audio_format_t *` — a pointer to the stored `*i2s_output_audio_format` (the saved `_i2s_output_audio_format`), non-NULL on success. The sample application treats a NULL return as fatal (`panic("PicoAudio: Unable to open audio device.\n")`), although the current implementation always returns the output format pointer. ### Behavior 1. Stores both format pointers in file-scope statics `_i2s_input_audio_format` / `_i2s_output_audio_format`. 2. Configures `config->data_pin` and `config->clock_pin_base` / `+1` as PIO pins (`GPIO_FUNC_PIOx`). 3. Claims `config->pio_sm` on the selected PIO, adds the `audio_i2s` program, and calls `audio_i2s_program_init()`. 4. Allocates a silence buffer (`PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH * 4` bytes). 5. Configures DMA channels `config->dma_channel0` / `config->dma_channel1` (chained to each other, DREQ `DREQ_PIOx_TX0 + sm`). Transfer size is derived from the output format: 32-bit for S16/S32 stereo output (`DMA_SIZE_32`); the mono paths hit `assert(false)`. ### Throws / Fatal Conditions | Condition | Mechanism | Location | |---|---|---| | `i2s_output_audio_format->channel_count != AUDIO_CHANNEL_STEREO` | `assert` | `src/audio_i2s.c:183` | | `pcm_format` not S16 or S32 | `assert` | `src/audio_i2s.c:184` | | Output channel_count is `AUDIO_CHANNEL_MONO` (any PCM format) | `assert(false)` in DMA-size switch | `src/audio_i2s.c:207-220` | ### Example (basic) ```c #include "pico/audio.h" #include "pico/audio_i2s.h" static audio_format_t audio_format = { .sample_freq = 44100, .pcm_format = AUDIO_PCM_FORMAT_S32, .channel_count = AUDIO_CHANNEL_STEREO }; static audio_buffer_format_t producer_format = { .format = &audio_format, .sample_stride = 8 // 4 bytes * 2 channels }; static audio_i2s_config_t i2s_config = { .data_pin = PICO_AUDIO_I2S_DATA_PIN, // 18 .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, // 16 .dma_channel0 = 0, .dma_channel1 = 1, .pio_sm = 0 }; void app_init(void) { const audio_format_t *out_format = audio_i2s_setup(&audio_format, &audio_format, &i2s_config); if (!out_format) { panic("PicoAudio: Unable to open audio device.\n"); } } ``` ### Example (error handling) ```c // The driver asserts internally; user-visible failure is NULL return. const audio_format_t *out_format = audio_i2s_setup(&in_fmt, &out_fmt, &cfg); if (!out_format) { panic("PicoAudio: Unable to open audio device.\n"); } // Passing a mono or U8/U16 output format aborts via assert(false) in // audio_i2s_setup() — only stereo S16/S32 output is supported. ``` ``` -------------------------------- ### audio_i2s_setup error handling Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Error-handling pattern for audio_i2s_setup. The driver asserts internally on invalid output formats, so the user-visible failure is a NULL return, which should trigger a panic. Only stereo S16/S32 output is supported; mono or U8/U16 formats cause an assert(false). ```c // The driver asserts internally; user-visible failure is NULL return. const audio_format_t *out_format = audio_i2s_setup(&in_fmt, &out_fmt, &cfg); if (!out_format) { panic("PicoAudio: Unable to open audio device.\n"); } // Passing a mono or U8/U16 output format aborts via assert(false) in // audio_i2s_setup() — only stereo S16/S32 output is supported. ``` -------------------------------- ### Catch failures in consumer code with audio_i2s_setup, audio_i2s_connect, take_audio_buffer Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/errors.md Shows the only non-fatal error signals from the library: NULL from audio_i2s_setup() and bool from audio_i2s_connect(). Non-blocking take_audio_buffer() may return NULL by contract, which is not an error. All other failures assert or panic inside the library; build with NDEBUG to strip asserts, keep hardware config valid, and never double-queue a buffer. ```c // 1. NULL/bool return checks (the only non-fatal signals the library gives back) const audio_format_t *out = audio_i2s_setup(&audio_format, &audio_format, &i2s_config); if (!out) { panic("PicoAudio: Unable to open audio device.\n"); // your own handler } bool ok = audio_i2s_connect(producer_pool); if (!ok) { // never happens on the current code path (always returns true), but check anyway } // 2. Non-blocking takes can return NULL by contract (not an error) audio_buffer_t *buffer = take_audio_buffer(ap, false); if (buffer == NULL) { return; // skip this callback; the driver plays silence } // 3. Everything else asserts/panics inside the library: // - build with NDEBUG to strip asserts if you want only panics // - keep hardware config valid (stereo S16/S32 output, DMA IRQ/PIO in {0,1}) // - never double-queue a buffer (ab->next must stay NULL) ``` -------------------------------- ### Docker build commands for Pico 2 I2S DAC Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/README.md Commands to clone, navigate, and build the firmware inside a Docker container. Use the current directory for the sample, and specify 'pico' or 'pico2' to build only the respective target. Outputs are 'build/xxxx.uf2' for Pico and 'build2/xxxx.uf2' for Pico 2. ```bash $ git clone -b main https://github.com/elehobica/pico_audio_i2s_32b.git $ cd pico_audio_i2s_32b/samples/xxxx $ ../build_docker.sh # build both targets (default) $ ../build_docker.sh pico # build only Pico -> build/xxxx.uf2 $ ../build_docker.sh pico2 # build only Pico 2 -> build2/xxxx.uf2 ``` -------------------------------- ### audio_complete_connection Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Completes an audio connection by wiring a producer and consumer pool into the connection, asserting their types. This function is used to finalize a custom connection setup. ```APIDOC ## audio_complete_connection ### Description Completes an audio connection by wiring a producer and consumer pool into the connection, asserting their types. This function is used to finalize a custom connection setup. ### Signature ```c void audio_complete_connection(audio_connection_t *connection, audio_buffer_pool_t *producer, audio_buffer_pool_t *consumer); ``` ### Parameters - **connection** (audio_connection_t*) - Required - Pointer to the connection to complete. - **producer** (audio_buffer_pool_t*) - Required - Pointer to the producer pool. Must have type `ac_producer`. - **consumer** (audio_buffer_pool_t*) - Required - Pointer to the consumer pool. Must have type `ac_consumer`. ### Behavior Asserts that `producer->type == ac_producer` and `consumer->type == ac_consumer`, then wires the connection into both pools and sets the pool pointers on the connection. ### Example ```c struct buffer_copying_on_consumer_take_connection conn = { .core = { .consumer_pool_take = mono_to_stereo_consumer_take, .consumer_pool_give = consumer_pool_give_buffer_default, .producer_pool_take = producer_pool_take_buffer_default, .producer_pool_give = producer_pool_give_buffer_default, } }; audio_buffer_pool_t *consumer = audio_new_consumer_pool(&consumer_format, 2, 256); audio_complete_connection(&conn.core, producer_pool, consumer); ``` ``` -------------------------------- ### User-defined i2s_callback_func in extern C Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Example user-defined i2s_callback_func from the sample (samples/sine_wave_i2s_32b/sine_wave.cpp:271-277). It is wrapped in extern "C" for C++ applications and calls decode() when decode_flg is set. ```c extern "C" { void i2s_callback_func() { if (decode_flg) { decode(); } } } ``` -------------------------------- ### Wire mono S16 producer to stereo consumer with mono_to_stereo_consumer_take Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Shows how to set up an audio_connection_t with the built-in mono-to-stereo converter. The producer_pool_take and give use the default buffer functions, while the consumer_pool_take is set to mono_to_stereo_consumer_take. The connection is completed with audio_complete_connection, passing the mono producer and stereo consumer. ```c // Wire a mono S16 producer to a stereo consumer using the built-in converter: audio_connection_t conn = { .producer_pool_take = producer_pool_take_buffer_default, .producer_pool_give = producer_pool_give_buffer_default, .consumer_pool_take = mono_to_stereo_consumer_take, .consumer_pool_give = consumer_pool_give_buffer_default, }; audio_complete_connection(&conn, mono_producer, stereo_consumer); ``` -------------------------------- ### audio_i2s_program_init C helper function Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/audio_i2s_pio.md Static inline helper to initialize a PIO state machine for I2S audio output. It configures pins, shift registers, and FIFO, primes the bit counter, and jumps to the entry point. Requires the PIO program to be added to the instruction memory first. ```c static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint res_bits) { pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); sm_config_set_out_pins(&sm_config, data_pin, 1); sm_config_set_sideset_pins(&sm_config, clock_pin_base); sm_config_set_out_shift(&sm_config, false, true, 32); sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX); pio_sm_init(pio, sm, offset, &sm_config); uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); pio_sm_set_pins(pio, sm, 0); // clear pins pio_sm_clear_fifos(pio, sm); pio_sm_drain_tx_fifo(pio, sm); // set resolution to ISR (use as config value) pio_sm_set_enabled(pio, sm, false); pio_sm_put_blocking(pio, sm, res_bits - 2); // res_bits should be 32, 16 or 8 pio_sm_exec(pio, sm, pio_encode_pull(false, false)); pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); pio_sm_set_enabled(pio, sm, true); pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); } ``` -------------------------------- ### audio_i2s_program_init Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/audio_i2s_pio.md Initializes the I2S PIO state machine with the given pins and resolution. Configures output pins, side-set pins, FIFO join, and primes the resolution value into the state machine. ```APIDOC ## audio_i2s_program_init ### Description Initializes the I2S PIO state machine for audio output. Configures the state machine with the specified data and clock pins, sets up the FIFO, and primes the resolution value. ### Method C function (static inline) ### Signature c static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint res_bits) ### Parameters - **pio** (PIO) - Required - The PIO instance (`pio0` or `pio1`) - **sm** (uint) - Required - State machine index (0-3) - **offset** (uint) - Required - Instruction-memory offset where `audio_i2s_program` was added (`pio_add_program` return) - **data_pin** (uint) - Required - GPIO for serial data out (SDO) - **clock_pin_base** (uint) - Required - GPIO base for the two side-set pins: `clock_pin_base` = BCLK, `clock_pin_base + 1` = LRCLK - **res_bits** (uint) - Required - Resolution in bits (32, 16 or 8 per source comment); written to the FIFO as `res_bits - 2` and loaded into X at entry ### Configuration performed - 1 out pin, 2 side-set pins, right-shift out with autopull at 32 bits, TX-only FIFO join - Output pin directions set for `data_pin` and both clock pins; pins cleared - FIFOs cleared and drained - X (per-sample bit counter) primed through ISR: `res_bits - 2` is pushed and drawn into ISR, then `mov x, isr` - Program counter jumped to `entry_point` ### Return Value None (void) ``` -------------------------------- ### audio_init_buffer signature Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Initializes an already-allocated audio_buffer_t: sets format, allocates buffer via pico_buffer_alloc, sets max_sample_count, and resets sample_count to 0. ```c void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count); ``` -------------------------------- ### audio_i2s_setup function signature Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio_i2s.md Function signature for audio_i2s_setup, which configures the I2S audio output. It stores the input and output audio formats, configures GPIO pins, claims a PIO state machine, allocates a silence buffer, and sets up two chained DMA channels. The output format must be stereo with S16 or S32 PCM format, otherwise an assertion fails. ```c const audio_format_t *audio_i2s_setup(const audio_format_t *i2s_input_audio_format, const audio_format_t *i2s_output_audio_format, const audio_i2s_config_t *config); ``` -------------------------------- ### audio_new_buffer signature Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Allocates and zero-initializes an audio_buffer_t and delegates to audio_init_buffer. Returns a heap-allocated buffer or NULL if allocation fails. ```c audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count); ``` -------------------------------- ### Define pico_audio_i2s_32b interface library in CMake Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/CMakeLists.txt CMake definition for the pico_audio_i2s_32b interface library. It generates the PIO header from audio_i2s.pio, adds audio_i2s.c as a source, links required libraries (pico_stdlib, pico_multicore, hardware_dma, hardware_pio, hardware_irq, pico_audio_32b), and sets the include directory to src/include. The guard prevents redefinition if the target already exists. ```cmake if (NOT TARGET pico_audio_i2s_32b) add_library(pico_audio_i2s_32b INTERFACE) pico_generate_pio_header(pico_audio_i2s_32b ${CMAKE_CURRENT_LIST_DIR}/src/audio_i2s.pio ) target_sources(pico_audio_i2s_32b INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src/audio_i2s.c ) target_link_libraries(pico_audio_i2s_32b INTERFACE pico_stdlib pico_multicore hardware_dma hardware_pio hardware_irq pico_audio_32b ) target_include_directories(pico_audio_i2s_32b INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src/include ) endif() ``` -------------------------------- ### audio_init_buffer Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Initializes an already-allocated audio_buffer_t by setting format, allocating the underlying buffer, and resetting sample_count to zero. ```APIDOC ## audio_init_buffer ### Description Initializes an already-allocated `audio_buffer_t`: sets `format`, allocates `buffer = pico_buffer_alloc(buffer_sample_count * format->sample_stride)`, sets `max_sample_count` and resets `sample_count = 0`. ### Function Signature ```c void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count); ``` ### Parameters - **audio_buffer** (`audio_buffer_t *`) - Required - Pointer to the buffer to initialize - **format** (`audio_buffer_format_t *`) - Required - Buffer format - **buffer_sample_count** (`int`) - Required - Number of samples for the buffer ### Example (manual buffer configuration) ```c audio_buffer_t *ab = audio_new_buffer(&producer_format, 1156); int32_t *samples = (int32_t *) ab->buffer->bytes; for (uint i = 0; i < ab->max_sample_count; i++) { samples[i*2+0] = 0; // left samples[i*2+1] = 0; // right } ab->sample_count = ab->max_sample_count; give_audio_buffer(pool, ab); // make it available to the consumer ``` ``` -------------------------------- ### Windows build commands for I2S DAC Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/README.md Build commands for Windows using Developer Command Prompt for VS 2022. The command includes cmake and nmake. Use the -DPICO_PLATFORM=rp2350 and -DPICO_BOARD=pico2 options for Raspberry Pi Pico 2. ```bash > cd pico_audio_i2s_32b\samples\xxxx > mkdir build && cd build > cmake -G "NMake Makefiles" .. ; (for Raspberry Pi Pico 1 series) > cmake -G "NMake Makefiles" -DPICO_PLATFORM=rp2350 -DPICO_BOARD=pico2 .. ; (for Raspberry Pi Pico 2) > nmake ``` -------------------------------- ### audio_new_buffer Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/pico_audio.md Allocates and zero-initializes an audio_buffer_t and delegates to audio_init_buffer. Returns a heap-allocated buffer or NULL if allocation fails. ```APIDOC ## audio_new_buffer ### Description Allocates and zero-initializes an `audio_buffer_t` and delegates to `audio_init_buffer`. Returns a heap-allocated buffer (or NULL if allocation fails). ### Function Signature ```c audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count); ``` ### Parameters - **format** (`audio_buffer_format_t *`) - Required - Buffer format - **buffer_sample_count** (`int`) - Required - Number of samples for the buffer ### Returns - `audio_buffer_t *` - Pointer to the allocated buffer, or NULL if allocation fails. ``` -------------------------------- ### Linux build commands for I2S DAC Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/README.md Build commands for Linux using cmake and make. Use -DPICO_PLATFORM=rp2350 and -DPICO_BOARD=pico2 for Raspberry Pi Pico 2. Outputs are '*.uf2' files. ```bash $ cd pico_audio_i2s_32b/samples/xxxx $ mkdir build && cd build $ cmake .. # (for Raspberry Pi Pico 1 series) $ cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=pico2 .. # (for Raspberry Pi Pico 2) $ make -j4 ``` -------------------------------- ### audio_i2s PIO program (src/audio_i2s.pio) Source: https://github.com/elehobica/pico_audio_i2s_32b/blob/main/_autodocs/api-reference/audio_i2s_pio.md The complete audio_i2s PIO program. It shifts 32-bit FIFO words out one bit at a time using two interleaved bit loops (bitloop1 for WS=1, bitloop0 for WS=0) and a public entry_point that reloads X from ISR. Requires autopull with threshold 32 and side-set of 2 pins (bit 0 = BCLK, bit 1 = LRCLK). ```pio .program audio_i2s .side_set 2 ; /--- LRCLK ; |/-- BCLK bitloop1: ; || out pins, 1 side 0b00 jmp x-- bitloop1 side 0b01 out pins, 1 side 0b10 mov x, isr side 0b11 bitloop0: out pins, 1 side 0b10 jmp x-- bitloop0 side 0b11 out pins, 1 side 0b00 public entry_point: mov x, isr side 0b01 ```