summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/BACKLOG.md10
-rw-r--r--doc/TOOLS_REFERENCE.md14
-rw-r--r--tools/specplay.cc212
-rw-r--r--tools/specplay_README.md164
4 files changed, 0 insertions, 400 deletions
diff --git a/doc/BACKLOG.md b/doc/BACKLOG.md
index 68995a7..1aa911d 100644
--- a/doc/BACKLOG.md
+++ b/doc/BACKLOG.md
@@ -6,16 +6,6 @@ This file contains low-priority tasks and ideas that have not yet been triaged f
## Audio Tools
-### Task #64: specplay Enhancements
-Extend audio analysis tool with new features:
-- **Priority 1**: Spectral visualization (ASCII art), waveform display, frequency analysis, dynamic range
-- **Priority 2**: Diff mode (compare .wav vs .spec), batch mode (CSV report, find clipping)
-- **Priority 3**: WAV export (.spec → .wav), normalization
-- **Priority 4**: Spectral envelope, harmonic analysis, onset detection
-- **Priority 5**: Interactive mode (seek, loop, volume control)
-
-See `tools/specplay_README.md` for detailed feature list.
-
### Task #65: Data-Driven Tempo Control
Move tempo variation from code to data files.
diff --git a/doc/TOOLS_REFERENCE.md b/doc/TOOLS_REFERENCE.md
index f99d213..6315a17 100644
--- a/doc/TOOLS_REFERENCE.md
+++ b/doc/TOOLS_REFERENCE.md
@@ -43,20 +43,6 @@ cmake --build build -j4
---
-## specplay (Diagnostic)
-
-```bash
-# Analyze .spec file
-./build/specplay input.spec
-
-# Or analyze .wav file
-./build/specplay input.wav
-```
-
-Output: Peak, RMS, clipping detection.
-
----
-
## Code Coverage (macOS)
```bash
diff --git a/tools/specplay.cc b/tools/specplay.cc
deleted file mode 100644
index 9fa9355..0000000
--- a/tools/specplay.cc
+++ /dev/null
@@ -1,212 +0,0 @@
-// Standalone tool to play .spec or .wav files for debugging
-// Usage: ./specplay <file.spec|file.wav>
-
-#include "audio/dct.h"
-#include "audio/window.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define MINIAUDIO_IMPLEMENTATION
-#include "miniaudio.h"
-
-struct PlaybackState {
- float* pcm_data;
- size_t num_samples;
- size_t playback_pos;
-};
-
-void audio_callback(ma_device* device, void* output, const void* input,
- ma_uint32 frame_count) {
- PlaybackState* state = (PlaybackState*)device->pUserData;
- float* out = (float*)output;
-
- for (ma_uint32 i = 0; i < frame_count; i++) {
- if (state->playback_pos < state->num_samples) {
- float sample = state->pcm_data[state->playback_pos++];
- // Clamp to [-1, 1] and warn if clipping
- if (sample > 1.0f || sample < -1.0f) {
- fprintf(stderr, "[CLIP at sample %zu: %.3f]\n", state->playback_pos - 1,
- sample);
- sample = (sample > 1.0f) ? 1.0f : -1.0f;
- }
- out[i * 2] = sample; // Left
- out[i * 2 + 1] = sample; // Right (mono)
- } else {
- out[i * 2] = 0.0f;
- out[i * 2 + 1] = 0.0f;
- }
- }
-}
-
-float* load_spec(const char* path, size_t* out_num_samples) {
- FILE* f = fopen(path, "rb");
- if (!f) {
- fprintf(stderr, "Failed to open %s\n", path);
- return nullptr;
- }
-
- // Read SpecHeader
- struct SpecHeader {
- char magic[4];
- int32_t version;
- int32_t dct_size;
- int32_t num_frames;
- };
-
- SpecHeader header;
- if (fread(&header, sizeof(SpecHeader), 1, f) != 1) {
- fprintf(stderr, "Failed to read SpecHeader\n");
- fclose(f);
- return nullptr;
- }
-
- // Validate header
- if (memcmp(header.magic, "SPEC", 4) != 0) {
- fprintf(stderr, "Invalid magic bytes (expected 'SPEC')\n");
- fclose(f);
- return nullptr;
- }
-
- printf("Loading .spec: version=%d, dct_size=%d, frames=%d\n", header.version,
- header.dct_size, header.num_frames);
-
- uint32_t num_frames = header.num_frames;
-
- // Read spectral data
- size_t spec_size = num_frames * DCT_SIZE;
- float* spec_data = (float*)malloc(spec_size * sizeof(float));
- if (fread(spec_data, sizeof(float), spec_size, f) != spec_size) {
- fprintf(stderr, "Failed to read spectral data\n");
- free(spec_data);
- fclose(f);
- return nullptr;
- }
- fclose(f);
-
- // Convert to PCM via IDCT
- *out_num_samples = spec_size;
- float* pcm_data = (float*)malloc(*out_num_samples * sizeof(float));
-
- for (uint32_t frame = 0; frame < num_frames; frame++) {
- const float* spectral_frame = spec_data + (frame * DCT_SIZE);
- float* time_frame = pcm_data + (frame * DCT_SIZE);
- idct_512(spectral_frame, time_frame);
- }
-
- free(spec_data);
-
- // Analyze PCM statistics
- float peak = 0.0f, rms_sum = 0.0f;
- for (size_t i = 0; i < *out_num_samples; i++) {
- float abs_val = fabsf(pcm_data[i]);
- if (abs_val > peak)
- peak = abs_val;
- rms_sum += pcm_data[i] * pcm_data[i];
- }
- float rms = sqrtf(rms_sum / *out_num_samples);
-
- printf("PCM stats: Peak=%.3f, RMS=%.3f\n", peak, rms);
- if (peak > 1.0f) {
- printf("[WARNING] Peak exceeds 1.0! Will clip during playback.\n");
- }
-
- return pcm_data;
-}
-
-float* load_wav(const char* path, size_t* out_num_samples) {
- ma_decoder decoder;
- ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 1, 32000);
-
- if (ma_decoder_init_file(path, &config, &decoder) != MA_SUCCESS) {
- fprintf(stderr, "Failed to open WAV file: %s\n", path);
- return nullptr;
- }
-
- ma_uint64 frame_count;
- ma_decoder_get_length_in_pcm_frames(&decoder, &frame_count);
- *out_num_samples = (size_t)frame_count;
-
- float* pcm_data = (float*)malloc(*out_num_samples * sizeof(float));
- ma_decoder_read_pcm_frames(&decoder, pcm_data, frame_count, nullptr);
- ma_decoder_uninit(&decoder);
-
- printf("Loaded .wav: %zu samples\n", *out_num_samples);
-
- // Analyze PCM statistics
- float peak = 0.0f, rms_sum = 0.0f;
- for (size_t i = 0; i < *out_num_samples; i++) {
- float abs_val = fabsf(pcm_data[i]);
- if (abs_val > peak)
- peak = abs_val;
- rms_sum += pcm_data[i] * pcm_data[i];
- }
- float rms = sqrtf(rms_sum / *out_num_samples);
-
- printf("PCM stats: Peak=%.3f, RMS=%.3f\n", peak, rms);
-
- return pcm_data;
-}
-
-int main(int argc, char** argv) {
- if (argc != 2) {
- fprintf(stderr, "Usage: %s <file.spec|file.wav>\n", argv[0]);
- return 1;
- }
-
- const char* path = argv[1];
- const char* ext = strrchr(path, '.');
-
- PlaybackState state = {};
-
- if (ext && strcmp(ext, ".spec") == 0) {
- state.pcm_data = load_spec(path, &state.num_samples);
- } else if (ext && (strcmp(ext, ".wav") == 0 || strcmp(ext, ".aif") == 0)) {
- state.pcm_data = load_wav(path, &state.num_samples);
- } else {
- fprintf(stderr, "Unknown file type: %s\n", path);
- return 1;
- }
-
- if (!state.pcm_data) {
- fprintf(stderr, "Failed to load audio\n");
- return 1;
- }
-
- printf("Playing %.2f seconds... Press Ctrl+C to stop.\n",
- (float)state.num_samples / 32000.0f);
-
- // Initialize miniaudio
- ma_device_config device_config =
- ma_device_config_init(ma_device_type_playback);
- device_config.playback.format = ma_format_f32;
- device_config.playback.channels = 2;
- device_config.sampleRate = 32000;
- device_config.dataCallback = audio_callback;
- device_config.pUserData = &state;
-
- ma_device device;
- if (ma_device_init(NULL, &device_config, &device) != MA_SUCCESS) {
- fprintf(stderr, "Failed to initialize audio device\n");
- free(state.pcm_data);
- return 1;
- }
-
- if (ma_device_start(&device) != MA_SUCCESS) {
- fprintf(stderr, "Failed to start audio device\n");
- ma_device_uninit(&device);
- free(state.pcm_data);
- return 1;
- }
-
- // Wait for playback to finish
- while (state.playback_pos < state.num_samples) {
- ma_sleep(100);
- }
-
- ma_device_uninit(&device);
- free(state.pcm_data);
-
- printf("Playback complete.\n");
- return 0;
-}
diff --git a/tools/specplay_README.md b/tools/specplay_README.md
deleted file mode 100644
index 72d9ec1..0000000
--- a/tools/specplay_README.md
+++ /dev/null
@@ -1,164 +0,0 @@
-# specplay - Audio Analysis & Playback Tool
-
-Standalone diagnostic tool for analyzing and playing .spec spectrogram files and .wav audio files.
-
-## Usage
-
-```bash
-./build/specplay <file.spec|file.wav>
-```
-
-## Features
-
-### Current (v1.0)
-- ✅ Plays .spec files via IDCT synthesis (matches demo playback exactly)
-- ✅ Plays .wav files for comparison
-- ✅ Reports PCM statistics:
- - **Peak level**: Maximum absolute sample value (detects clipping if > 1.0)
- - **RMS level**: Root-mean-square energy (loudness measure)
-- ✅ Real-time clipping detection during playback with sample position
-- ✅ Validates .spec file format (magic bytes, version, DCT size)
-
-### Example Output
-```
-Loading .spec: version=1, dct_size=512, frames=68
-PCM stats: Peak=0.403, RMS=0.058
-Playing 1.09 seconds... Press Ctrl+C to stop.
-Playback complete.
-```
-
-## Use Cases
-
-1. **Debugging Audio Issues**
- - Quickly identify which samples have clipping (Peak > 1.0)
- - Compare .wav source vs .spec output to detect analysis artifacts
- - Verify spectrogram regeneration after DCT changes
-
-2. **Quality Assurance**
- - Batch test all .spec files for clipping before committing
- - Measure loudness consistency across samples (RMS levels)
- - Validate spectrograms match expected characteristics
-
-3. **Development Workflow**
- - Test individual samples without running full demo
- - A/B compare different spectrogram generation parameters
- - Verify procedural note generation output
-
-## Technical Details
-
-- **Sample Rate**: 32kHz (matches demo audio engine)
-- **Format**: Mono (duplicated to stereo for playback)
-- **IDCT**: Uses same `idct_512()` function as demo (bit-exact)
-- **Clamping**: Clipping detected but samples clamped to [-1, 1] for playback
-
-## Future Enhancement Ideas
-
-### Priority 1: Analysis Features
-- [ ] **Spectral visualization**: ASCII art frequency plot
-- [ ] **Waveform display**: Time-domain amplitude graph
-- [ ] **Frequency analysis**: Dominant frequency, spectral centroid
-- [ ] **Dynamic range**: Measure headroom, suggest normalization
-- [ ] **Duration info**: Show seconds, samples, frames
-
-### Priority 2: Comparison Tools
-- [ ] **Diff mode**: `specplay --compare file.wav file.spec`
- - Side-by-side PCM stats
- - RMS error, peak difference
- - Correlation coefficient
-- [ ] **Batch mode**: `specplay --batch assets/final/*.spec`
- - Generate CSV report of all stats
- - Sort by peak level (find clipping candidates)
- - Find outliers (unusually loud/quiet samples)
-
-### Priority 3: Export Features
-- [ ] **WAV export**: `specplay file.spec --export output.wav`
- - Convert .spec → .wav for external analysis
- - Useful for importing into audio editors
-- [ ] **Normalization**: `specplay file.spec --normalize --export normalized.wav`
- - Auto-scale to target peak/RMS
- - Preserve transients
-
-### Priority 4: Advanced Analysis
-- [ ] **Spectral envelope**: Extract formants, resonances
-- [ ] **Harmonic analysis**: Detect fundamental frequency, harmonics
-- [ ] **Onset detection**: Find transient attacks (kick/snare hits)
-- [ ] **Spectral flux**: Measure frequency content change over time
-
-### Priority 5: Interactive Mode
-- [ ] **Seek controls**: Play from specific time offset
-- [ ] **Loop mode**: Repeat playback indefinitely
-- [ ] **Volume control**: Adjust playback gain
-- [ ] **Real-time waveform**: Live visualization during playback
-
-## Integration with Build System
-
-Built automatically when `DEMO_BUILD_TOOLS=ON`:
-```bash
-cmake -S . -B build -DDEMO_BUILD_TOOLS=ON
-cmake --build build --target specplay
-```
-
-Or with all options:
-```bash
-cmake -S . -B build -DDEMO_ALL_OPTIONS=ON
-cmake --build build
-```
-
-## Code Architecture
-
-- **Minimal dependencies**: Only uses audio subsystem + miniaudio
-- **Self-contained**: No GPU, no platform layer (pure audio)
-- **Size**: ~250 lines (easy to extend)
-- **Format support**:
- - `.spec` via SpecHeader parser
- - `.wav` via miniaudio decoder (also handles .aif, .mp3)
-
-## Related Tools
-
-- **spectool**: Analyzes .wav → generates .spec (batch processing)
-- **specview**: ASCII visualization of .spec frequency content
-- **specplay**: This tool (playback + analysis)
-
-## Troubleshooting
-
-**"Failed to read SpecHeader"**
-- File is not a valid .spec file
-- Try with spectool to regenerate: `./build/spectool analyze input.wav output.spec`
-
-**"Peak exceeds 1.0! Will clip during playback."**
-- Spectrogram has too much energy
-- Likely needs regeneration after DCT changes
-- Or source .wav is already clipping
-
-**No audio output**
-- Check system audio device
-- Ensure 32kHz sample rate is supported
-- Try with a different .spec file (may be empty/silent)
-
-## Examples
-
-### Find all clipping samples
-```bash
-for f in assets/final/*.spec; do
- ./build/specplay "$f" | grep "WARNING" && echo "$f"
-done
-```
-
-### Compare wav source to spec output
-```bash
-./build/specplay assets/originals/kick.wav
-./build/specplay assets/final/KICK.spec
-# Compare Peak/RMS values
-```
-
-### Quick loudness check
-```bash
-./build/specplay assets/final/KICK_606.spec | grep "RMS"
-# RMS > 0.3: loud, RMS < 0.1: quiet
-```
-
----
-
-**Last Updated**: February 6, 2026
-**Author**: Claude (AI assistant)
-**Status**: Production-ready, actively used for debugging