# RPi Pico WAV Player Audio File Format Limitations: A Complete Technical Guide

> Discover RPi Pico WAV Player audio format limitations. Learn supported PCM WAV file specs: bit depth, channels, headers, and sample rates up to 192 kHz for your projects.

- Repository: [Elehobica/rpi_pico_wav_player](https://github.com/elehobica/rpi_pico_wav_player)
- Tags: technical-guide
- Published: 2026-03-01

---

**The RPi Pico WAV Player only supports uncompressed PCM WAV files with 16, 24, or 32-bit integer depth, mono or stereo channels, and standard RIFF headers up to 192 kHz sample rates.**

The `elehobica/rpi_pico_wav_player` repository implements a lightweight audio player for Raspberry Pi Pico that deliberately restricts format support to standard WAV containers. Understanding these limitations is essential for preparing compatible audio libraries and troubleshooting playback failures.

## What Audio Formats Are Supported?

The player implements a narrow compatibility window optimized for the RP2040's constrained resources.

### Supported WAV Specifications

The decoder recognizes only **uncompressed PCM audio** with the following parameters:

- **Container**: Standard RIFF/WAV (not RF64 or BWF)
- **Format Tag**: `FMT_PCM` (value 1) for integer PCM; `FMT_FLOAT` (value 3) is parsed but not decoded
- **Bit Depth**: 16-bit, 24-bit, or 32-bit integer samples
- **Channels**: Mono (1) or Stereo (2) only
- **Sample Rates**: Up to 192 kHz (hardware limited by I²S/PCM5102 DAC configuration)

Any deviation from these specifications results in the file being excluded from the UI or decoded as silence.

## How Format Restrictions Are Enforced in Code

The limitation is not merely documentation—it is hardcoded across multiple layers of the architecture.

### File Extension Filtering in file_menu_FatFs.c

Before any audio processing occurs, the file browser filters directory contents. In [`lib/file_menu/file_menu_FatFs.c`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/file_menu/file_menu_FatFs.c) at lines 99-107, the function `file_menu_match_ext()` implements a strict string comparison:

```c
// From lib/file_menu/file_menu_FatFs.c
bool file_menu_match_ext(const char *filename) {
    const char *ext = strrchr(filename, '.');
    if (ext == NULL) return false;
    // Only returns true for .wav or .WAV extensions
    return (strcasecmp(ext, ".wav") == 0);
}

```

Consequently, MP3, FLAC, OGG, AAC, and other formats never appear in the player's file menu.

### Header Parsing in PlayWav.cpp

Once a `.wav` file is selected, `PlayWav::skipToDataChunk()` in [`lib/PlayAudio/PlayWav.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/PlayAudio/PlayWav.cpp) (lines 44-66) parses the RIFF structure. This function extracts critical metadata from the `fmt` chunk:

- **Format tag** (`format`): Must be 1 (PCM) or 3 (Float) to proceed
- **Channels** (`channels`): Stored but assumed ≤ 2 in downstream processing
- **Sample rate** (`sampleRate`): Validated against hardware capabilities
- **Bits per sample** (`bitsPerSample`): Determines decoder branch
- **Block alignment** (`blockBytes`): Used for buffer calculations

The parser does not implement extended `fmt` sub-chunks or `WAVE_FORMAT_EXTENSIBLE` GUIDs. If the header contains compression codes like ADPCM (0x11) or MPEG (0x50), the player treats them as unknown and will likely produce silence or fail to initialize the audio stream.

### Decoder Limitations in the Audio Pipeline

The actual audio decoding occurs in `PlayWav::decode()`, where a switch statement (lines 104-112) handles sample conversion:

```cpp
// Simplified from lib/PlayAudio/PlayWav.cpp
int32_t PlayWav::decode(uint8_t *in_buf, int32_t *out_buf, size_t samples) {
    switch (bitsPerSample) {
        case 16: // Convert int16 to int32
        case 24: // Convert packed 24-bit to int32
        case 32: // Pass through int32
            // ... PCM processing ...
            break;
        default:
            return 0; // Silence for unsupported bit depths
    }
    // Channel handling: assumes max 2 channels
    for (int j = 0; j < 2; j++) {
        out_buf[j] = (j < channels) ? sample : sample; // Duplicate mono
    }
}

```

**Critical constraints visible in this code:**
- **No floating-point decoding**: While `FMT_FLOAT` (value 3) is recognized in the header, the decode switch lacks a case for float conversion, resulting in zeroed output buffers.
- **Hardcoded stereo output**: The channel loop `for (int j = 0; j < 2; j++)` always writes two samples. Mono files work because the sample is duplicated, but multi-channel files (5.1 surround, etc.) will have channels truncated or misaligned.
- **Integer PCM only**: Only 16, 24, and 32-bit integer samples reach the DAC. Other bit depths (8-bit, 20-bit) return silence.

## Unsupported Formats and Why They Fail

Understanding the failure modes helps diagnose why specific files produce no sound.

### Compressed Audio (MP3, FLAC, AAC)

Lossy and lossless compressed formats require decompression algorithms (codecs) that would exceed the RP2040's limited RAM and CPU budget. The player architecture delegates format support to the UI layer's extension filter, so these files never reach the decoder. Attempting to rename a `.mp3` to `.wav` will result in a RIFF header parse failure in `skipToDataChunk()`.

### Floating-Point PCM

Professional audio workflows often use 32-bit float WAVs for headroom. While the header parser recognizes format tag 3 (`WAVE_FORMAT_IEEE_FLOAT`), the `decode()` function lacks the conversion logic to normalize float values to the integer range expected by the PCM5102 DAC. The result is a valid playback session with silent output.

### Multi-Channel Audio Beyond Stereo

Surround sound files (4.0, 5.1, 7.1) contain more than two channels. The decoder's fixed `for (int j = 0; j < 2; j++)` loop in `PlayWav::decode()` assumes a maximum of two interleaved samples per frame. Additional channels in the file stream cause channel data to be misinterpreted as audio samples, resulting in white noise or decoding errors.

### Extended WAV Variants (RF64, BWF)

The Broadcast Wave Format (BWF) and RF64 extensions support files larger than 4GB and embed metadata chunks. The parser in `skipToDataChunk()` searches for the `data` chunk offset using standard RIFF structure assumptions. It does not handle the `ds64` chunk required for RF64 or parse the `bext` chunk. Large files will not have their data pointers correctly resolved, causing playback failure or crashes.

## Code Examples: Working Within the Limitations

### Playing a Compatible WAV File

The following snippet demonstrates proper initialization of a WAV player with a verified compatible file:

```cpp
#include "PlayAudio/PlayAudio.h"
#include "PlayAudio/PlayWav.h"

int main() {
    // Initialize the audio subsystem and I2S interface
    PlayAudio::initialize();

    // Instantiate WAV player - only accepts standard PCM files
    PlayWav player;
    
    // This will succeed if the file meets all constraints:
    // - Extension: .wav
    // - Format: PCM (1) or Float (3) header
    // - Bit depth: 16, 24, or 32-bit integer
    // - Channels: 1 or 2
    player.play("music/01_Track.wav");
}

```

The `play()` method internally calls `skipToDataChunk()` to validate the RIFF header before streaming begins.

### Handling Unsupported File Types

When integrating file selection logic, you can verify extension compatibility before attempting playback:

```cpp
#include "file_menu/file_menu_FatFs.h"

void processFile(const char* filename) {
    // Check if file matches supported extension
    if (!file_menu_match_ext(filename)) {
        // This filters out MP3, FLAC, OGG, etc.
        printf("Skipping unsupported file: %s\n", filename);
        return;
    }
    
    // Proceed with WAV playback
    PlayWav player;
    player.play(filename);
}

```

This pattern prevents the decoder from attempting to parse non-WAV headers, saving processing cycles and avoiding undefined behavior.

## Summary

The RPi Pico WAV Player enforces strict audio format constraints at multiple architectural levels:

- **File Extension**: Only `.wav` and `.WAV` files appear in the UI, filtered by `file_menu_match_ext()` in [`lib/file_menu/file_menu_FatFs.c`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/lib/file_menu/file_menu_FatFs.c)
- **Container Format**: Standard RIFF headers only; RF64 and BWF extensions are unsupported
- **Codec**: Uncompressed PCM integer data only; compressed codecs and IEEE float formats are parsed but decode to silence
- **Bit Depth**: 16-bit, 24-bit, or 32-bit integer PCM
- **Channels**: Mono or stereo only; multi-channel surround files are not handled
- **Sample Rate**: Up to 192 kHz, limited by I²S hardware configuration

Any deviation from these specifications results in the file being hidden from the file browser or producing silent/invalid output.

## Frequently Asked Questions

### Can the RPi Pico WAV Player play MP3 files?

No. The player cannot decode MP3 or any compressed audio format. The file menu explicitly filters for `.wav` extensions in `file_menu_match_ext()`, preventing MP3 files from appearing in the browser. Even if renamed to `.wav`, the RIFF header parser would reject the invalid structure.

### What is the maximum bit depth supported by the RPi Pico WAV Player?

The decoder supports **16-bit, 24-bit, and 32-bit integer PCM**. These are handled in `PlayWav::decode()` where samples are zero-padded or sign-extended to 32-bit integers for the DAC. 8-bit PCM and 32-bit floating-point formats are not supported and will play as silence.

### Why does my 32-bit float WAV file play silence?

While the header parser recognizes the `WAVE_FORMAT_IEEE_FLOAT` (format tag 3) in `skipToDataChunk()`, the `decode()` function lacks a conversion case for floating-point samples. The switch statement only handles integer bit depths (16, 24, 32), causing float files to fall through to the default case that returns zeroed buffers.

### Is there a file size limit for WAV files on the RPi Pico WAV Player?

The player uses standard RIFF structure parsing that assumes 32-bit chunk sizes, limiting files to approximately 4 GB. The parser does not implement the RF64 extension required for larger files. Additionally, practical limits depend on SD card read speeds; high-bitrate files (24-bit/192kHz stereo) require sustained throughput that may cause buffer underruns on slower cards.