// 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 #include #include std::vector generate_note_spectrogram(const NoteParams ¶ms, 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 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 &dest_data, int *dest_num_frames, const std::vector &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 &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 &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 &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; } } }