Skip to content

Commit 2fe8bdb

Browse files
authored
Optimize FLAC decoder (#23)
1 parent 760fabe commit 2fe8bdb

File tree

21 files changed

+7021
-822
lines changed

21 files changed

+7021
-822
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,6 @@ dkms.conf
6060
.vscode
6161
build/
6262
dist/
63+
64+
flac_to_wav
65+
!flac_to_wav/

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.15)
22

33
# Common source files
44
set(srcs
5-
src/decode/flac_decoder.cpp
5+
src/decode/flac/flac_decoder.cpp
6+
src/decode/flac/flac_lpc.cpp
7+
src/decode/flac/flac_crc.cpp
68
src/decode/mp3_decoder.cpp
79
src/decode/wav_decoder.cpp
810
src/dsp/dsps_add_s16_ansi.c
@@ -27,6 +29,8 @@ if(ESP_PLATFORM)
2729
src/dsp/dsps_dotprod_f32_aes3.S
2830
src/dsp/dsps_dotprod_f32_m_ae32.S
2931
src/dsp/dsps_mulc_s16_ae32.S
32+
src/decode/flac/flac_lpc_32_asm.S
33+
src/decode/flac/flac_lpc_64_asm.S
3034
)
3135

3236
# Build as an ESP-IDF component
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(flac_to_wav)
3+
4+
set(CMAKE_CXX_STANDARD 11)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
# Add the esp-audio-libs as a subdirectory (going up two levels to the root)
8+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/esp-audio-libs)
9+
10+
# Create the executable
11+
add_executable(flac_to_wav src/flac_to_wav.cpp)
12+
13+
# Output the binary to the project root directory instead of build/
14+
set_target_properties(flac_to_wav PROPERTIES
15+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
16+
)
17+
18+
# Link with esp-audio-libs
19+
target_link_libraries(flac_to_wav PRIVATE esp-audio-libs)
20+
21+
# Include directories
22+
target_include_directories(flac_to_wav PRIVATE
23+
${CMAKE_CURRENT_SOURCE_DIR}/../../include
24+
${CMAKE_CURRENT_SOURCE_DIR}/include
25+
)
26+
27+
# Add optimization flags
28+
target_compile_options(flac_to_wav PRIVATE -O2)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# FLAC to WAV Converter Example
2+
3+
This example demonstrates how to use the esp-audio-libs FLAC decoder to convert FLAC files to WAV format.
4+
5+
## Overview
6+
7+
The `flac_to_wav` program:
8+
- Reads a FLAC file
9+
- Decodes it using the esp-audio-libs FLAC decoder
10+
- Writes the decoded audio as a WAV file
11+
- Supports all bit depths (8, 12, 16, 20, 24, 32 bits)
12+
- Handles various channel configurations (mono to 8 channels)
13+
- Verifies decoded audio against the MD5 signature in the FLAC header
14+
- Displays file information and verification results
15+
16+
## Building
17+
18+
### Prerequisites
19+
20+
- CMake 3.10 or later
21+
- A C++11 compatible compiler (gcc, clang, etc.)
22+
- Make or Ninja build system
23+
24+
### Build Steps
25+
26+
```bash
27+
# From the flac_to_wav directory
28+
cmake -B build
29+
cmake --build build
30+
```
31+
32+
The compiled binary will be placed in the project directory as `flac_to_wav`.
33+
34+
### Build Options
35+
36+
You can specify a different build type:
37+
38+
```bash
39+
# Debug build with symbols
40+
cmake -B build -DCMAKE_BUILD_TYPE=Debug
41+
cmake --build build
42+
43+
# Release build with optimizations (default)
44+
cmake -B build -DCMAKE_BUILD_TYPE=Release
45+
cmake --build build
46+
```
47+
48+
## Usage
49+
50+
```bash
51+
./flac_to_wav <input.flac> <output.wav>
52+
```
53+
54+
### Example
55+
56+
```bash
57+
# Convert a FLAC file to WAV
58+
./flac_to_wav song.flac song.wav
59+
60+
# The program will display information about the file:
61+
# - Sample rate
62+
# - Number of channels
63+
# - Bit depth
64+
# - Total samples
65+
# - Metadata blocks found
66+
#
67+
# After conversion, it will verify the decoded audio:
68+
# - Expected MD5 (from FLAC header)
69+
# - Computed MD5 (from decoded audio)
70+
# - Verification result (PASS/FAIL)
71+
```
72+
73+
### MD5 Verification
74+
75+
The program automatically verifies the decoded audio by computing an MD5 hash and comparing it with the signature stored in the FLAC file's STREAMINFO header. This ensures bit-perfect decoding.
76+
77+
**Supported for all bit depths:**
78+
- Byte-aligned: 8, 16, 24, 32 bits
79+
- Non-byte-aligned: 12, 15, 20 bits (properly packed per FLAC spec)
80+
81+
Example output:
82+
```
83+
=== MD5 Verification ===
84+
Expected MD5: ac3c581ce17991866b0dcdea3b9dfd43
85+
Computed MD5: ac3c581ce17991866b0dcdea3b9dfd43
86+
Result: PASS - MD5 signatures match!
87+
```
88+
89+
## Testing
90+
91+
A comprehensive test suite is available to validate the FLAC decoder against the official FLAC test files. See [TESTING.md](TESTING.md) for details on:
92+
- Setting up test files
93+
- Running the test suite
94+
- Interpreting test results
95+
- Current decoder capabilities
96+
97+
## Performance
98+
99+
On a typical modern desktop CPU:
100+
- **16-bit stereo @ 44.1kHz**: ~100-200x realtime (decoding a 3-minute song takes <1 second)
101+
- **24-bit stereo @ 96kHz**: ~80-150x realtime
102+
103+
Performance on ESP32-S3 @ 240MHz:
104+
- **16-bit stereo @ 48kHz**: ~30x realtime (~3-4% CPU)
105+
- **24-bit stereo @ 48kHz**: ~20x realtime (~5% CPU)
106+
107+
## Output Format
108+
109+
The converter produces standard WAV files:
110+
- **8-bit, 16-bit**: PCM format (format code 1)
111+
- **12-bit, 20-bit, 24-bit, 32-bit**: WAVE_FORMAT_EXTENSIBLE (format code 0xFFFE)
112+
- **Byte order**: Little-endian (standard for WAV)
113+
- **Bit alignment**: Non-byte-aligned bit depths are zero-padded in the LSB
114+
115+
Example: 12-bit samples are stored in 16-bit containers (2 bytes):
116+
```
117+
Original 12-bit value: 0x123
118+
Stored in WAV: 0x1230 (left-shifted by 4, LSBs = 0)
119+
```
120+
121+
## Further Reading
122+
123+
- [FLAC Format Specification](https://xiph.org/flac/format.html)
124+
- [FLAC Decoder Implementation Details](../../src/decode/flac/README.md)
125+
- [FLAC Test Files Repository](https://github.com/ietf-wg-cellar/flac-test-files)
126+
- [RFC 9639: FLAC Specification](https://www.rfc-editor.org/rfc/rfc9639.html)
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# FLAC Decoder Test Suite
2+
3+
This document describes how to test the esp-audio-libs FLAC decoder using the official FLAC test suite.
4+
5+
## Overview
6+
7+
The test suite validates the FLAC decoder against the official FLAC test files. It performs validation using:
8+
1. **MD5 verification** (primary): Decoded audio MD5 matches the signature in the FLAC header
9+
2. **ffmpeg comparison** (secondary): Cross-validation against ffmpeg's output
10+
11+
## Prerequisites
12+
13+
1. **Python 3** with standard library
14+
2. **ffmpeg** (for reference decoding)
15+
3. **FLAC test files** from the official test suite
16+
17+
## Setting Up Test Files
18+
19+
Download the official FLAC test suite:
20+
21+
```bash
22+
# From the flac_to_wav directory
23+
git clone https://github.com/ietf-wg-cellar/flac-test-files.git
24+
```
25+
26+
This creates a `flac-test-files` directory in `flac_to_wav`.
27+
28+
Expected directory structure:
29+
```
30+
esp-audio-libs/
31+
├── host_examples/
32+
│ └── flac_to_wav/
33+
│ ├── flac-test-files/ # Test suite (cloned from GitHub)
34+
│ │ ├── subset/ # Standard FLAC files
35+
│ │ ├── uncommon/ # Edge cases
36+
│ │ └── faulty/ # Invalid files
37+
│ ├── build/
38+
│ ├── flac_to_wav.cpp
39+
│ ├── test_flac_decoder.py
40+
│ ├── README.md
41+
│ └── TESTING.md (this file)
42+
```
43+
44+
## Running Tests
45+
46+
```bash
47+
# Build the decoder first
48+
cmake -B build
49+
cmake --build build
50+
51+
# Run the test suite
52+
python3 test_flac_decoder.py
53+
```
54+
55+
## Understanding Test Results
56+
57+
The test script will:
58+
1. Decode each FLAC file using our decoder
59+
2. Verify the MD5 hash computed during decoding against the FLAC header signature
60+
3. Cross-validate against ffmpeg output (for additional confidence)
61+
4. Generate a detailed report
62+
63+
**Test output example:**
64+
```
65+
Testing subset files (64 files)...
66+
[1/64] Testing 01 - blocksize 4096.flac... ✓ PASS
67+
[2/64] Testing 02 - blocksize 4608.flac... ✓ PASS
68+
...
69+
```
70+
71+
**Results are saved to:**
72+
- `test_results/test_report.txt` - Human-readable report
73+
- `test_results/test_report.json` - Machine-readable JSON report
74+
- `test_results/<category>/our_decoder/` - WAV files from our decoder
75+
- `test_results/<category>/ffmpeg/` - WAV files from ffmpeg
76+
77+
## Test Categories
78+
79+
The test suite includes three categories:
80+
81+
### 1. subset (64 files)
82+
Standard FLAC files using streaming subset features (should all pass)
83+
- Various bit depths (8, 12, 16, 20, 24, 32 bits)
84+
- All bit depths supported with proper MD5 verification
85+
- Non-byte-aligned depths (12, 15, 20 bits) are correctly packed per FLAC spec for MD5 computation
86+
- Various sample rates (22.05kHz to 384kHz)
87+
- Various block sizes (16 to 16384 samples)
88+
- Various channel configurations (mono to 8 channels)
89+
- Files with large metadata blocks
90+
- Variable block size files
91+
92+
### 2. uncommon (11 files)
93+
Files with uncommon or exotic features of the FLAC format
94+
- Uncommon bit depths (15 or 32 bits) - passes
95+
- Uncommon sample rate (768kHz) - passes
96+
- Uncommon block size (65535) or rice partition orders (15) - passes
97+
- Changes to streaming settings (sample rate, number of channels, bit depth) - fails (not supported)
98+
- Files starting with frame header or unparseable data - fails (requires STREAMINFO)
99+
100+
### 3. faulty (11 files)
101+
Files with invalid data or corruption
102+
- Expected to fail gracefully if unable to decode
103+
- Some files may be accepted if the error is in metadata we don't validate
104+
105+
## Interpreting Test Results
106+
107+
### Primary Test Results (MD5-based)
108+
109+
**✓ PASS - MD5 verified**
110+
- Computed MD5 matches header signature
111+
- ✅ Cryptographically verified bit-perfect decode
112+
- Most reliable test result
113+
114+
**✓ PASS - MD5 verified + matches ffmpeg**
115+
- MD5 matches header signature
116+
- Also matches ffmpeg output
117+
- ✅ Highest confidence result (dual validation)
118+
119+
**✗ FAIL - MD5 mismatch**
120+
- Computed MD5 does not match header signature
121+
- ❌ Decoder bug, needs investigation
122+
123+
**✓ PASS - Matches ffmpeg (no MD5 in file)**
124+
- No MD5 signature available in FLAC header
125+
- Falls back to ffmpeg comparison
126+
- ✅ Correct decode (secondary validation)
127+
128+
### Other Results
129+
130+
**? FAIL - Decoder failed but ffmpeg succeeded**
131+
- Our decoder rejected the file
132+
- May indicate missing feature support or decoder bug
133+
- ⚠️ Needs investigation
134+
135+
**? EXPECTED - Both decoders failed**
136+
- Both our decoder and ffmpeg rejected the file
137+
- ✅ Correct behavior for invalid files
138+
139+
**? UNKNOWN - No MD5 available and ffmpeg failed**
140+
- Cannot validate output
141+
- ⚠️ Manual inspection needed
142+
143+
## Troubleshooting Tests
144+
145+
**"Error: flac_to_wav not found"**
146+
```bash
147+
# Build the decoder first
148+
cmake -B build
149+
cmake --build build
150+
```
151+
152+
**"Error: Test files not found"**
153+
```bash
154+
# Clone the test files repository
155+
cd ..
156+
git clone https://github.com/ietf-wg-cellar/flac-test-files.git
157+
cd flac_to_wav
158+
```
159+
160+
**"Error: ffmpeg not found"**
161+
```bash
162+
# On macOS:
163+
brew install ffmpeg
164+
165+
# On Ubuntu/Debian:
166+
sudo apt-get install ffmpeg
167+
168+
# On Windows:
169+
# Download from https://ffmpeg.org/download.html
170+
```
171+
172+
**Test suite runs but all tests fail**
173+
- Check that `flac_to_wav` is executable
174+
- Try running manually: `./flac_to_wav "flac-test-files/subset/01 - blocksize 4096.flac" test.wav`
175+
- Check console output for error messages
176+
177+
## Current Test Results
178+
179+
As of the latest run, the decoder achieves:
180+
- **73/86 tests passing (84%)**
181+
- **All 64 subset files pass** (100%) - all with MD5 verification
182+
- Known limitations:
183+
- Files with changing stream parameters (sample rate, channels, bit depth) mid-stream
184+
- Files without STREAMINFO metadata block
185+
186+
## Customizing Tests
187+
188+
Edit `test_flac_decoder.py` to:
189+
- Test specific file categories: Modify `TEST_CATEGORIES` list
190+
- Change timeout for slow systems: Adjust `timeout=` parameter in `run_command()`
191+
- Test against different reference decoder: Replace ffmpeg commands
192+
- Add custom validation logic
193+
194+
## Further Reading
195+
196+
- [FLAC Format Specification](https://xiph.org/flac/format.html)
197+
- [FLAC Test Files Repository](https://github.com/ietf-wg-cellar/flac-test-files)
198+
- [RFC 9639: FLAC Specification](https://www.rfc-editor.org/rfc/rfc9639.html)

0 commit comments

Comments
 (0)