summaryrefslogtreecommitdiff
path: root/src/audio/gen.cc
blob: ddc4fa6f08b21b3b8ddcc1008cf20a4d4b310748 (plain)
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 &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; }
  }
}