summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/gen.cc138
-rw-r--r--src/audio/gen.h38
2 files changed, 176 insertions, 0 deletions
diff --git a/src/audio/gen.cc b/src/audio/gen.cc
new file mode 100644
index 0000000..3666116
--- /dev/null
+++ b/src/audio/gen.cc
@@ -0,0 +1,138 @@
+// This file is part of the 64k demo project.
+// It implements the procedural audio generation logic.
+// Uses IDCT/FDCT to synthesize notes in the frequency domain.
+
+#include "audio/gen.h"
+#include "audio/dct.h"
+#include "audio/window.h"
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+std::vector<float> generate_note_spectrogram(const NoteParams &params,
+ int *out_num_frames) {
+ int num_frames = (int)(params.duration_sec * 32000.0f / DCT_SIZE);
+ if (num_frames < 1)
+ num_frames = 1;
+ *out_num_frames = num_frames;
+
+ std::vector<float> spec_data(num_frames * DCT_SIZE, 0.0f);
+ float window[WINDOW_SIZE];
+ hamming_window_512(window);
+
+ float phase = 0.0f;
+ float time_step = 1.0f / 32000.0f;
+
+ for (int f = 0; f < num_frames; ++f) {
+ float pcm_chunk[DCT_SIZE] = {0};
+ float frame_time = f * DCT_SIZE * time_step;
+
+ // Generate PCM for this frame
+ for (int i = 0; i < DCT_SIZE; ++i) {
+ float t = frame_time + i * time_step;
+
+ // Envelope (Simple Attack)
+ float env = 1.0f;
+ if (t < params.attack_sec) {
+ env = t / params.attack_sec;
+ }
+
+ // Vibrato
+ float vib =
+ sinf(t * params.vibrato_rate * 2.0f * 3.14159f) * params.vibrato_depth;
+
+ // Randomness
+ float pitch_rnd =
+ ((float)rand() / RAND_MAX - 0.5f) * params.pitch_randomness;
+ float amp_rnd = ((float)rand() / RAND_MAX - 0.5f) * params.amp_randomness;
+
+ float sample = 0.0f;
+ for (int h = 1; h <= params.num_harmonics; ++h) {
+ float h_amp = powf(params.harmonic_decay, h - 1);
+ float freq = (params.base_freq + vib + pitch_rnd) * h;
+ sample += sinf(phase * h) * h_amp;
+ }
+
+ // Update phase for fundamental (approximate, since freq changes)
+ phase +=
+ (params.base_freq + vib + pitch_rnd) * 2.0f * 3.14159f * time_step;
+
+ pcm_chunk[i] = sample * params.amplitude * env * (1.0f + amp_rnd);
+ }
+
+ // 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);
+
+ // Copy to buffer
+ for (int i = 0; i < DCT_SIZE; ++i) {
+ spec_data[f * DCT_SIZE + i] = dct_chunk[i];
+ }
+ }
+
+ return spec_data;
+}
+
+void paste_spectrogram(std::vector<float> &dest_data, int *dest_num_frames,
+ const std::vector<float> &src_data, int src_num_frames,
+ int frame_offset) {
+ if (src_num_frames <= 0)
+ return;
+
+ int needed_frames = frame_offset + src_num_frames;
+ if (needed_frames > *dest_num_frames) {
+ dest_data.resize(needed_frames * DCT_SIZE, 0.0f);
+ *dest_num_frames = needed_frames;
+ }
+
+ for (int f = 0; f < src_num_frames; ++f) {
+ int dst_frame_idx = frame_offset + f;
+ if (dst_frame_idx < 0)
+ continue;
+
+ for (int i = 0; i < DCT_SIZE; ++i) {
+ dest_data[dst_frame_idx * DCT_SIZE + i] += src_data[f * DCT_SIZE + i];
+ }
+ }
+}
+
+void apply_spectral_noise(std::vector<float> &data, int num_frames,
+ float amount) {
+ for (int f = 0; f < num_frames; ++f) {
+ for (int i = 0; i < DCT_SIZE; ++i) {
+ float rnd = ((float)rand() / RAND_MAX - 0.5f) * 2.0f * amount;
+ data[f * DCT_SIZE + i] *= (1.0f + rnd);
+ }
+ }
+}
+
+void apply_spectral_lowpass(std::vector<float> &data, int num_frames,
+ float cutoff_ratio) {
+ int cutoff_bin = (int)(cutoff_ratio * DCT_SIZE);
+ if (cutoff_bin < 0)
+ cutoff_bin = 0;
+ if (cutoff_bin >= DCT_SIZE)
+ return;
+
+ for (int f = 0; f < num_frames; ++f) {
+ for (int i = cutoff_bin; i < DCT_SIZE; ++i) {
+ data[f * DCT_SIZE + i] = 0.0f;
+ }
+ }
+}
+
+void apply_spectral_comb(std::vector<float> &data, int num_frames,
+ float period_bins, float depth) {
+ for (int i = 0; i < DCT_SIZE; ++i) {
+ float mod =
+ 1.0f - depth * (0.5f + 0.5f * cosf((float)i / period_bins * 6.28318f));
+ for (int f = 0; f < num_frames; ++f) {
+ data[f * DCT_SIZE + i] *= mod;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/audio/gen.h b/src/audio/gen.h
new file mode 100644
index 0000000..f490115
--- /dev/null
+++ b/src/audio/gen.h
@@ -0,0 +1,38 @@
+// This file is part of the 64k demo project.
+// It defines the procedural audio generation interface.
+// Shared between the offline spectool and the runtime demo.
+
+#pragma once
+#include <vector>
+
+struct NoteParams {
+ float base_freq;
+ float duration_sec;
+ float amplitude;
+ float attack_sec;
+ float decay_sec; // Unused for now
+ float vibrato_rate;
+ float vibrato_depth;
+ int num_harmonics;
+ float harmonic_decay;
+ float pitch_randomness;
+ float amp_randomness;
+};
+
+// Generates a single note into a new spectrogram buffer
+std::vector<float> generate_note_spectrogram(const NoteParams &params,
+ int *out_num_frames);
+
+// Pastes a source spectrogram into a destination spectrogram at a given frame
+// offset Expands destination if necessary
+void paste_spectrogram(std::vector<float> &dest_data, int *dest_num_frames,
+ const std::vector<float> &src_data, int src_num_frames,
+ int frame_offset);
+
+// Post-processing effects
+void apply_spectral_noise(std::vector<float> &data, int num_frames,
+ float amount);
+void apply_spectral_lowpass(std::vector<float> &data, int num_frames,
+ float cutoff_ratio);
+void apply_spectral_comb(std::vector<float> &data, int num_frames,
+ float period_bins, float depth);