diff options
| author | skal <pascal.massimino@gmail.com> | 2026-01-27 23:09:27 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-01-27 23:10:49 +0100 |
| commit | 9dcf94ab01269311b4e5d39be23c95560904c626 (patch) | |
| tree | ac271af3d51d7ee1bce6827f81e97f1f463336db /tools/spectool.cpp | |
| parent | 364d9e60e3c27cb131a598fe5f83deb74493319f (diff) | |
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.
Diffstat (limited to 'tools/spectool.cpp')
| -rw-r--r-- | tools/spectool.cpp | 159 |
1 files changed, 0 insertions, 159 deletions
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 <stdio.h> -#include <string.h> -#include "audio/dct.h" -#include "audio/window.h" -#include "audio/synth.h" -#include "platform.h" -#include "audio/audio.h" - -#include "miniaudio.h" - -#include <vector> - -// 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<float> 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<float> 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 <command> <input> [output]\n"); - printf("Commands:\n"); - printf(" analyze <input.wav|.mp3> <output.spec> Analyze an audio file and save as a spectrogram.\n"); - printf(" play <input.spec> 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; -} |
