summaryrefslogtreecommitdiff
path: root/src/tests/test_spectool.cc
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-01-27 23:09:27 +0100
committerskal <pascal.massimino@gmail.com>2026-01-27 23:10:49 +0100
commit9dcf94ab01269311b4e5d39be23c95560904c626 (patch)
treeac271af3d51d7ee1bce6827f81e97f1f463336db /src/tests/test_spectool.cc
parent364d9e60e3c27cb131a598fe5f83deb74493319f (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 'src/tests/test_spectool.cc')
-rw-r--r--src/tests/test_spectool.cc95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/tests/test_spectool.cc b/src/tests/test_spectool.cc
new file mode 100644
index 0000000..7581f0d
--- /dev/null
+++ b/src/tests/test_spectool.cc
@@ -0,0 +1,95 @@
+#include "audio/dct.h" // For DCT_SIZE
+#include "miniaudio.h"
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h> // For system()
+#include <vector>
+
+// Re-defining the header here to avoid dependency on the tool's source
+struct SpecHeader {
+ char magic[4];
+ int32_t version;
+ int32_t dct_size;
+ int32_t num_frames;
+};
+
+#define TEST_SAMPLE_RATE 32000
+#define TEST_DURATION_SECONDS 1
+#define TEST_FREQ 440.0f
+#define PI 3.14159265f
+
+int main() {
+ printf("Running spectool end-to-end test...\n");
+
+ const char *wav_path = "test_signal.wav";
+ const char *spec_path = "test_signal.spec";
+ const char *spectool_path =
+ "./spectool"; // Assumes ctest is run from `build` dir
+
+ // 1. Generate and save a WAV file
+ ma_encoder_config enc_config = ma_encoder_config_init(
+ ma_encoding_format_wav, ma_format_f32, 1, TEST_SAMPLE_RATE);
+ ma_encoder encoder;
+ if (ma_encoder_init_file(wav_path, &enc_config, &encoder) != MA_SUCCESS) {
+ printf("TEST FAILED: Could not initialize WAV encoder.\n");
+ return 1;
+ }
+
+ std::vector<float> pcm_data(TEST_SAMPLE_RATE * TEST_DURATION_SECONDS);
+ for (size_t i = 0; i < pcm_data.size(); ++i) {
+ pcm_data[i] = 0.5f * sinf(2.0f * PI * TEST_FREQ * i / TEST_SAMPLE_RATE);
+ }
+ ma_uint64 frames_written;
+ ma_encoder_write_pcm_frames(&encoder, pcm_data.data(), pcm_data.size(),
+ &frames_written);
+ ma_encoder_uninit(&encoder);
+ printf(" Generated %s\n", wav_path);
+
+ // 2. Run spectool analyze
+ char command[512];
+ snprintf(command, sizeof(command), "%s analyze %s %s", spectool_path,
+ wav_path, spec_path);
+ printf(" Executing: %s\n", command);
+ int return_code = system(command);
+ assert(return_code == 0);
+ printf(" spectool executed successfully.\n");
+
+ // 3. Verify the output .spec file
+ FILE *f_spec = fopen(spec_path, "rb");
+ assert(f_spec != NULL);
+
+ SpecHeader header;
+ fread(&header, sizeof(SpecHeader), 1, f_spec);
+ assert(strncmp(header.magic, "SPEC", 4) == 0);
+ assert(header.version == 1);
+ assert(header.dct_size == DCT_SIZE);
+
+ int expected_frames = (TEST_SAMPLE_RATE * TEST_DURATION_SECONDS) / DCT_SIZE;
+ if ((TEST_SAMPLE_RATE * TEST_DURATION_SECONDS) % DCT_SIZE != 0) {
+ expected_frames++;
+ }
+ assert(header.num_frames == expected_frames);
+
+ printf(" .spec header verified.\n");
+
+ // Just check that we have some non-zero data
+ std::vector<float> spec_data(header.num_frames * header.dct_size);
+ fread(spec_data.data(), sizeof(float), spec_data.size(), f_spec);
+ fclose(f_spec);
+
+ double total_energy = 0.0;
+ for (float val : spec_data) {
+ total_energy += fabs(val);
+ }
+ assert(total_energy > 0.0);
+ printf(" .spec data seems valid.\n");
+
+ // 4. Cleanup
+ remove(wav_path);
+ remove(spec_path);
+ printf(" Cleaned up temporary files.\n");
+
+ printf("...spectool test PASSED!\n");
+ return 0;
+}