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. --- tools/spectool.cpp | 159 ----------------------------------------------------- 1 file changed, 159 deletions(-) delete mode 100644 tools/spectool.cpp (limited to 'tools/spectool.cpp') diff --git a/tools/spectool.cpp b/tools/spectool.cpp deleted file mode 100644 index defb118..0000000 --- a/tools/spectool.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include -#include "audio/dct.h" -#include "audio/window.h" -#include "audio/synth.h" -#include "platform.h" -#include "audio/audio.h" - -#include "miniaudio.h" - -#include - -// Simple .spec file format: -// char[4] magic = "SPEC" -// int32_t version = 1 -// int32_t dct_size -// int32_t num_frames -// float[num_frames * dct_size] data -struct SpecHeader { - char magic[4]; - int32_t version; - int32_t dct_size; - int32_t num_frames; -}; - -int analyze_audio(const char* in_path, const char* out_path) { - printf("Analyzing %s -> %s\n", in_path, out_path); - - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 1, 32000); - ma_decoder decoder; - if (ma_decoder_init_file(in_path, &config, &decoder) != MA_SUCCESS) { - printf("Error: Failed to open or decode audio file: %s\n", in_path); - return 1; - } - - std::vector spec_data; - float pcm_chunk[DCT_SIZE]; - float window[WINDOW_SIZE]; - hamming_window_512(window); - - ma_uint64 frames_read; - while (ma_decoder_read_pcm_frames(&decoder, pcm_chunk, DCT_SIZE, &frames_read) == MA_SUCCESS && frames_read > 0) { - if (frames_read < DCT_SIZE) { - // Zero-pad the last chunk if it's smaller - memset(pcm_chunk + frames_read, 0, (DCT_SIZE - frames_read) * sizeof(float)); - } - - // Apply window - for (int i = 0; i < DCT_SIZE; ++i) { - pcm_chunk[i] *= window[i]; - } - - // Apply FDCT - float dct_chunk[DCT_SIZE]; - fdct_512(pcm_chunk, dct_chunk); - - // Add to spectrogram data - spec_data.insert(spec_data.end(), dct_chunk, dct_chunk + DCT_SIZE); - } - - ma_decoder_uninit(&decoder); - - // Write to .spec file - FILE* f_out = fopen(out_path, "wb"); - if (!f_out) { - printf("Error: Failed to open output file: %s\n", out_path); - return 1; - } - - SpecHeader header; - memcpy(header.magic, "SPEC", 4); - header.version = 1; - header.dct_size = DCT_SIZE; - header.num_frames = spec_data.size() / DCT_SIZE; - - fwrite(&header, sizeof(SpecHeader), 1, f_out); - fwrite(spec_data.data(), sizeof(float), spec_data.size(), f_out); - fclose(f_out); - - printf("Analysis complete. Wrote %d spectral frames.\n", header.num_frames); - return 0; -} - -int play_spec(const char* in_path) { - printf("Playing %s\n", in_path); - - FILE* f_in = fopen(in_path, "rb"); - if (!f_in) { - printf("Error: Failed to open input file: %s\n", in_path); - return 1; - } - - SpecHeader header; - if (fread(&header, sizeof(SpecHeader), 1, f_in) != 1 || strncmp(header.magic, "SPEC", 4) != 0) { - printf("Error: Invalid spectrogram file format.\n"); - fclose(f_in); - return 1; - } - - std::vector spec_data(header.num_frames * header.dct_size); - fread(spec_data.data(), sizeof(float), spec_data.size(), f_in); - fclose(f_in); - - platform_init(); - audio_init(); - - Spectrogram spec; - spec.spectral_data_a = spec_data.data(); - spec.spectral_data_b = spec_data.data(); // Point both to the same buffer for playback - spec.num_frames = header.num_frames; - - int spec_id = synth_register_spectrogram(&spec); - synth_trigger_voice(spec_id, 0.7f, 0.0f); - - printf("Playing... Press Ctrl+C to exit.\n"); - while (synth_get_active_voice_count() > 0 && !platform_should_close()) { - platform_poll(); - } - - - audio_shutdown(); - platform_shutdown(); - - return 0; -} - -void print_usage() { - printf("Usage: spectool [output]\n"); - printf("Commands:\n"); - printf(" analyze Analyze an audio file and save as a spectrogram.\n"); - printf(" play Play a spectrogram file.\n"); -} - -int main(int argc, char** argv) { - if (argc < 3) { - print_usage(); - return 1; - } - - const char* command = argv[1]; - const char* input_path = argv[2]; - - if (strcmp(command, "analyze") == 0) { - if (argc < 4) { - printf("Error: 'analyze' command requires an output file.\n"); - print_usage(); - return 1; - } - return analyze_audio(input_path, argv[3]); - } else if (strcmp(command, "play") == 0) { - return play_spec(input_path); - } else { - printf("Error: Unknown command '%s'\n", command); - print_usage(); - return 1; - } - - return 0; -} -- cgit v1.2.3