| # https://www.fisheyecollaborative.org/fish-sounds/pempheris-schomburgkii
 | 
 | $ ffmpeg -i pempheris.wav -f u8 -ar 22050 -ac 1 fishy.raw
 | 
 | $ xxd -i fishy.raw > audio_data.h
 | 
 | 
 | 
 | # CMakelist.txt
 | 
 | cmake_minimum_required(VERSION 3.13)
 | 
 | include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
 | 
 | project(myapp C CXX ASM)
 | 
 | set(CMAKE_C_STANDARD 11)
 | 
 | set(CMAKE_CXX_STANDARD 17)
 | 
 | pico_sdk_init()
 | 
 | add_executable(${PROJECT_NAME} main.c)
 | 
 | pico_add_extra_outputs(${PROJECT_NAME})
 | 
 | # Link necessary libraries
 | 
 | target_link_libraries(${PROJECT_NAME} 
 | 
 |     pico_stdlib
 | 
 |     hardware_pwm
 | 
 |     hardware_clocks
 | 
 | )
 | 
 | pico_enable_stdio_usb(${PROJECT_NAME} 1)
 | 
 | pico_enable_stdio_uart(${PROJECT_NAME} 0)
 | 
 | 
 | 
 | # main.c
 | 
 | //GPIO 16 → 100-470µF capacitor (+) → Speaker (+)
 | 
 | //Speaker (-) → GND
 | 
 | #include <stdio.h>
 | 
 | #include <pico/stdlib.h>
 | 
 | #include <hardware/pwm.h>
 | 
 | #include <hardware/clocks.h>
 | 
 | #include <hardware/timer.h>
 | 
 | #define AUDIO_PIN     16
 | 
 | #define SAMPLE_RATE   22050   // Hz
 | 
 | #define PWM_FREQ      100000  // PWM carrier frequency
 | 
 | // Example audio data (replace with your PCM data)
 | 
 | const uint8_t audio_data[] = {
 | 
 |     // example start on 128 (0): 128, 150, 170, 190, 210, 190, 170, 150, 128, 100, 80, 60, 40, 60, 80, 100
 | 
 | };
 | 
 | const uint32_t audio_data_len = sizeof(audio_data);
 | 
 | // Playback state
 | 
 | volatile uint32_t sample_index = 0;
 | 
 | repeating_timer_t audio_timer;
 | 
 | void init_pwm_audio(uint gpio) {
 | 
 |     gpio_set_function(gpio, GPIO_FUNC_PWM);
 | 
 |     uint slice = pwm_gpio_to_slice_num(gpio);
 | 
 |     pwm_config cfg = pwm_get_default_config();
 | 
 |     float clk_hz = clock_get_hz(clk_sys);
 | 
 |     // Calculate divider for ~100kHz PWM carrier
 | 
 |     float div = clk_hz / (PWM_FREQ * 256.0f);
 | 
 |     pwm_config_set_clkdiv(&cfg, div);
 | 
 |     pwm_config_set_wrap(&cfg, 255);
 | 
 |     pwm_init(slice, &cfg, true);
 | 
 |     pwm_set_gpio_level(gpio, 128);
 | 
 | }
 | 
 | // Timer callback to update one sample
 | 
 | bool audio_callback(repeating_timer_t *t) {
 | 
 |     if (sample_index >= audio_data_len) {
 | 
 |         pwm_set_gpio_level(AUDIO_PIN, 128); // silence
 | 
 |         return false; // stop timer
 | 
 |     }
 | 
 |     pwm_set_gpio_level(AUDIO_PIN, audio_data[sample_index++]);
 | 
 |     return true; // continue
 | 
 | }
 | 
 | void play_audio(uint gpio, const uint8_t *samples, uint32_t num_samples, uint32_t sample_rate) {
 | 
 |     sample_index = 0;
 | 
 |     // Compute interval in microseconds
 | 
 |     double interval_us = 1e6 / (double)sample_rate;
 | 
 |     // Start repeating timer
 | 
 |     add_repeating_timer_us(-interval_us, audio_callback, NULL, &audio_timer);
 | 
 |     // Wait until playback finishes
 | 
 |     while (audio_timer.alarm_id != 0 && sample_index < num_samples) {
 | 
 |         tight_loop_contents();
 | 
 |     }
 | 
 | }
 | 
 | void play_tone(uint gpio, uint16_t frequency, uint32_t duration_ms) {
 | 
 |     uint32_t period_us = 1000000 / frequency;
 | 
 |     uint32_t half = period_us / 2;
 | 
 |     uint32_t cycles = (duration_ms * 1000) / period_us;
 | 
 |     for (uint32_t i = 0; i < cycles; i++) {
 | 
 |         pwm_set_gpio_level(gpio, 255);
 | 
 |         sleep_us(half);
 | 
 |         pwm_set_gpio_level(gpio, 0);
 | 
 |         sleep_us(half);
 | 
 |     }
 | 
 |     pwm_set_gpio_level(gpio, 128);
 | 
 | }
 | 
 | int main() {
 | 
 |     stdio_init_all();
 | 
 |     printf("RP2040 PCM Audio Player using Timer\n");
 | 
 |     init_pwm_audio(AUDIO_PIN);
 | 
 |     sleep_ms(1000);
 | 
 |     while (true) {
 | 
 |         printf("Playing tone...\n");
 | 
 |         play_tone(AUDIO_PIN, 440, 300);
 | 
 |         sleep_ms(500);
 | 
 |         printf("Playing PCM audio...\n");
 | 
 |         play_audio(AUDIO_PIN, audio_data, audio_data_len, SAMPLE_RATE);
 | 
 |         sleep_ms(1000);
 | 
 |     }
 | 
 |     return 0;
 | 
 | }
 |