From 9dcf94ab01269311b4e5d39be23c95560904c626 Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 27 Jan 2026 23:09:27 +0100 Subject: feat: Implement spectool & specview; refactor coding style; update docs This commit introduces new tools for spectrogram manipulation and visualization, establishes a consistent coding style, and updates project documentation. Key changes include: - **Spectrogram Tools: - : A command-line utility for analyzing WAV/MP3 files into custom spectrogram format and playing back these spectrograms via the synth engine. - : A command-line tool for visualizing spectrogram files as ASCII art in the console. - **Coding Style Enforcement: - Added a configuration file enforcing LLVM-based style with 2-space indentation, no tabs, and an 80-column line limit. - Renamed all C++ source files from to for project consistency. - Applied automatic formatting using exit across the entire codebase. - **Documentation & Workflow: - Created to define a commit policy requiring tests to pass before committing. - Updated with instructions for building and using and , and referenced . - Updated and to reflect the new tools, audio architecture decisions (real-time additive synthesis, double-buffering for dynamic updates, WAV/MP3 support), coding style, and development workflow. - **Build System: - Modified to: - Include new targets for and under the option. - Update source file extensions to . - Add a new end-to-end test for to the suite. --- src/audio/synth.cpp | 167 ---------------------------------------------------- 1 file changed, 167 deletions(-) delete mode 100644 src/audio/synth.cpp (limited to 'src/audio/synth.cpp') diff --git a/src/audio/synth.cpp b/src/audio/synth.cpp deleted file mode 100644 index 3c20b0b..0000000 --- a/src/audio/synth.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "synth.h" -#include "audio/window.h" -#include // For memset -#include - -struct Voice { - bool active; - int spectrogram_id; - float volume; - float pan_left; - float pan_right; - - int current_spectral_frame; - int total_spectral_frames; - - float time_domain_buffer[DCT_SIZE]; - int buffer_pos; - - const volatile float* active_spectral_data; -}; - -static struct { - Spectrogram spectrograms[MAX_SPECTROGRAMS]; - const volatile float* active_spectrogram_data[MAX_SPECTROGRAMS]; - bool spectrogram_registered[MAX_SPECTROGRAMS]; -} g_synth_data; - -static Voice g_voices[MAX_VOICES]; - -void synth_init() { - memset(&g_synth_data, 0, sizeof(g_synth_data)); - memset(g_voices, 0, sizeof(g_voices)); -} - -void synth_shutdown() { - // Nothing to do here since we are not allocating memory -} - -int synth_register_spectrogram(const Spectrogram* spec) { - for (int i = 0; i < MAX_SPECTROGRAMS; ++i) { - if (!g_synth_data.spectrogram_registered[i]) { - g_synth_data.spectrograms[i] = *spec; - g_synth_data.active_spectrogram_data[i] = spec->spectral_data_a; - g_synth_data.spectrogram_registered[i] = true; - return i; - } - } - return -1; // No free slots -} - -void synth_unregister_spectrogram(int spectrogram_id) { - if (spectrogram_id >= 0 && spectrogram_id < MAX_SPECTROGRAMS) { - g_synth_data.spectrogram_registered[spectrogram_id] = false; - } -} - -float* synth_begin_update(int spectrogram_id) { - if (spectrogram_id < 0 || spectrogram_id >= MAX_SPECTROGRAMS || !g_synth_data.spectrogram_registered[spectrogram_id]) { - return nullptr; - } - - const volatile float* active_ptr = g_synth_data.active_spectrogram_data[spectrogram_id]; - - if (active_ptr == g_synth_data.spectrograms[spectrogram_id].spectral_data_a) { - return g_synth_data.spectrograms[spectrogram_id].spectral_data_b; - } else { - return g_synth_data.spectrograms[spectrogram_id].spectral_data_a; - } -} - -void synth_commit_update(int spectrogram_id) { - if (spectrogram_id < 0 || spectrogram_id >= MAX_SPECTROGRAMS || !g_synth_data.spectrogram_registered[spectrogram_id]) { - return; - } - - const volatile float* old_active_ptr = g_synth_data.active_spectrogram_data[spectrogram_id]; - const float* new_active_ptr = (old_active_ptr == g_synth_data.spectrograms[spectrogram_id].spectral_data_a) - ? g_synth_data.spectrograms[spectrogram_id].spectral_data_b - : g_synth_data.spectrograms[spectrogram_id].spectral_data_a; - - // Atomic swap using GCC/Clang builtins for thread safety - __atomic_store_n((const float**)&g_synth_data.active_spectrogram_data[spectrogram_id], new_active_ptr, __ATOMIC_RELEASE); -} - - -void synth_trigger_voice(int spectrogram_id, float volume, float pan) { - if (spectrogram_id < 0 || spectrogram_id >= MAX_SPECTROGRAMS || !g_synth_data.spectrogram_registered[spectrogram_id]) { - return; - } - - for (int i = 0; i < MAX_VOICES; ++i) { - if (!g_voices[i].active) { - Voice& v = g_voices[i]; - v.active = true; - v.spectrogram_id = spectrogram_id; - v.volume = volume; - - // Simple linear panning - v.pan_left = (pan > 0.0f) ? (1.0f - pan) : 1.0f; - v.pan_right = (pan < 0.0f) ? (1.0f + pan) : 1.0f; - - v.current_spectral_frame = 0; - v.total_spectral_frames = g_synth_data.spectrograms[spectrogram_id].num_frames; - v.buffer_pos = DCT_SIZE; // Force IDCT on first render - v.active_spectral_data = g_synth_data.active_spectrogram_data[spectrogram_id]; - - return; // Voice triggered - } - } -} - - -void synth_render(float* output_buffer, int num_frames) { - float window[WINDOW_SIZE]; - hamming_window_512(window); - - for (int i = 0; i < num_frames; ++i) { - float left_sample = 0.0f; - float right_sample = 0.0f; - - for (int v_idx = 0; v_idx < MAX_VOICES; ++v_idx) { - Voice& v = g_voices[v_idx]; - if (!v.active) continue; - - if (v.buffer_pos >= DCT_SIZE) { - if (v.current_spectral_frame >= v.total_spectral_frames) { - v.active = false; - continue; - } - - // Fetch the latest active spectrogram pointer for this voice - v.active_spectral_data = g_synth_data.active_spectrogram_data[v.spectrogram_id]; - - const float* spectral_frame = (const float*)v.active_spectral_data + (v.current_spectral_frame * DCT_SIZE); - - float windowed_frame[DCT_SIZE]; - for(int j=0; j