summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/spectool.cc165
-rw-r--r--tools/spectool.cpp159
-rw-r--r--tools/specview.cc94
3 files changed, 259 insertions, 159 deletions
diff --git a/tools/spectool.cc b/tools/spectool.cc
new file mode 100644
index 0000000..49c6384
--- /dev/null
+++ b/tools/spectool.cc
@@ -0,0 +1,165 @@
+#include "audio/audio.h"
+#include "audio/dct.h"
+#include "audio/synth.h"
+#include "audio/window.h"
+#include "platform.h"
+#include <stdio.h>
+#include <string.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;
+}
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;
-}
diff --git a/tools/specview.cc b/tools/specview.cc
new file mode 100644
index 0000000..77b362c
--- /dev/null
+++ b/tools/specview.cc
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <string.h>
+#include <vector>
+#include <algorithm> // For std::max_element
+
+// Redefine SpecHeader from spectool.cc
+struct SpecHeader {
+ char magic[4];
+ int32_t version;
+ int32_t dct_size;
+ int32_t num_frames;
+};
+
+void print_usage() {
+ printf("Usage: specview <input.spec>\n");
+ printf("Displays an ASCII representation of a spectrogram file.\n");
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ print_usage();
+ return 1;
+ }
+
+ const char* input_path = argv[1];
+
+ FILE* f_in = fopen(input_path, "rb");
+ if (!f_in) {
+ printf("Error: Failed to open input file: %s\n", input_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;
+ }
+
+ if (header.version != 1) {
+ printf("Error: Unsupported spectrogram version %d.\n", header.version);
+ fclose(f_in);
+ return 1;
+ }
+
+ std::vector<float> spec_data(header.num_frames * header.dct_size);
+ if (fread(spec_data.data(), sizeof(float), spec_data.size(), f_in) != spec_data.size()) {
+ printf("Error: Failed to read all spectrogram data.\n");
+ fclose(f_in);
+ return 1;
+ }
+ fclose(f_in);
+
+ printf("Spectrogram: %s\n", input_path);
+ printf(" DCT Size: %d\n", header.dct_size);
+ printf(" Num Frames: %d\n", header.num_frames);
+
+ // Find max magnitude for normalization
+ float max_mag = 0.0f;
+ for (float val : spec_data) {
+ max_mag = std::max(max_mag, fabsf(val));
+ }
+ if (max_mag == 0.0f) max_mag = 1.0f; // Avoid division by zero
+
+ // ASCII visualization
+ const char* gradient = " .:-=+*#%@";
+ int gradient_len = strlen(gradient);
+
+ printf("\nASCII Visualization:\n");
+ for (int frame = 0; frame < header.num_frames; ++frame) {
+ printf("%4d: ", frame);
+ const float* current_frame_data = spec_data.data() + frame * header.dct_size;
+
+ // Average bins into fewer columns for better fit on console
+ const int display_cols = 80; // Max console width
+ const int bins_per_col = header.dct_size / display_cols; // Divide into 80 bins
+
+ for (int col = 0; col < display_cols; ++col) {
+ float sum_mag = 0.0f;
+ for (int bin_idx = 0; bin_idx < bins_per_col; ++bin_idx) {
+ int current_bin = col * bins_per_col + bin_idx;
+ if (current_bin < header.dct_size) {
+ sum_mag += fabsf(current_frame_data[current_bin]);
+ }
+ }
+ float avg_mag = sum_mag / bins_per_col;
+ int char_idx = (int)((avg_mag / max_mag) * (gradient_len - 1));
+ printf("%c", gradient[char_idx]);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}