1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
// 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 ¶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<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; }
}
}
|