summaryrefslogtreecommitdiff
path: root/src/audio/spectral_brush.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/spectral_brush.cc')
-rw-r--r--src/audio/spectral_brush.cc145
1 files changed, 66 insertions, 79 deletions
diff --git a/src/audio/spectral_brush.cc b/src/audio/spectral_brush.cc
index c6eb64d..000b258 100644
--- a/src/audio/spectral_brush.cc
+++ b/src/audio/spectral_brush.cc
@@ -1,6 +1,7 @@
// This file is part of the 64k demo project.
// It implements the "Spectral Brush" primitive for procedural audio generation.
-// Implementation of Bezier curves, vertical profiles, and spectrogram rendering.
+// Implementation of Bezier curves, vertical profiles, and spectrogram
+// rendering.
#include "spectral_brush.h"
@@ -11,9 +12,8 @@ static const float SAMPLE_RATE = 32000.0f;
// Evaluate linear Bezier interpolation between control points
float evaluate_bezier_linear(const float* control_frames,
- const float* control_values,
- int n_points,
- float frame) {
+ const float* control_values, int n_points,
+ float frame) {
if (n_points == 0) {
return 0.0f;
}
@@ -33,8 +33,8 @@ float evaluate_bezier_linear(const float* control_frames,
for (int i = 0; i < n_points - 1; ++i) {
if (frame >= control_frames[i] && frame <= control_frames[i + 1]) {
// Linear interpolation: value = v0 + (v1 - v0) * t
- const float t =
- (frame - control_frames[i]) / (control_frames[i + 1] - control_frames[i]);
+ const float t = (frame - control_frames[i]) /
+ (control_frames[i + 1] - control_frames[i]);
return control_values[i] * (1.0f - t) + control_values[i + 1] * t;
}
}
@@ -49,72 +49,67 @@ uint32_t spectral_brush_rand(uint32_t seed) {
// X_{n+1} = (a * X_n + c) mod m
const uint32_t a = 1664525;
const uint32_t c = 1013904223;
- return a * seed + c; // Implicit mod 2^32
+ return a * seed + c; // Implicit mod 2^32
}
// Evaluate vertical profile at distance from curve center
-float evaluate_profile(ProfileType type, float distance, float param1, float param2) {
+float evaluate_profile(ProfileType type, float distance, float param1,
+ float param2) {
switch (type) {
- case PROFILE_GAUSSIAN: {
- // Gaussian: exp(-(dist^2 / sigma^2))
- // param1 = sigma (width in bins)
- const float sigma = param1;
- if (sigma <= 0.0f) {
- return 0.0f;
- }
- return expf(-(distance * distance) / (sigma * sigma));
+ case PROFILE_GAUSSIAN: {
+ // Gaussian: exp(-(dist^2 / sigma^2))
+ // param1 = sigma (width in bins)
+ const float sigma = param1;
+ if (sigma <= 0.0f) {
+ return 0.0f;
}
+ return expf(-(distance * distance) / (sigma * sigma));
+ }
- case PROFILE_DECAYING_SINUSOID: {
- // Decaying sinusoid: exp(-decay * dist) * cos(omega * dist)
- // param1 = decay rate
- // param2 = oscillation frequency (omega)
- const float decay = param1;
- const float omega = param2;
- const float envelope = expf(-decay * distance);
- const float oscillation = cosf(omega * distance);
- return envelope * oscillation;
- }
+ case PROFILE_DECAYING_SINUSOID: {
+ // Decaying sinusoid: exp(-decay * dist) * cos(omega * dist)
+ // param1 = decay rate
+ // param2 = oscillation frequency (omega)
+ const float decay = param1;
+ const float omega = param2;
+ const float envelope = expf(-decay * distance);
+ const float oscillation = cosf(omega * distance);
+ return envelope * oscillation;
+ }
- case PROFILE_NOISE: {
- // Random noise: deterministic RNG based on distance
- // param1 = amplitude scale
- // param2 = seed
- const float amplitude = param1;
- const uint32_t seed = (uint32_t)(param2) + (uint32_t)(distance * 1000.0f);
- const uint32_t rand_val = spectral_brush_rand(seed);
- // Map to [0, 1]
- const float normalized = (float)(rand_val % 10000) / 10000.0f;
- return amplitude * normalized;
- }
+ case PROFILE_NOISE: {
+ // Random noise: deterministic RNG based on distance
+ // param1 = amplitude scale
+ // param2 = seed
+ const float amplitude = param1;
+ const uint32_t seed = (uint32_t)(param2) + (uint32_t)(distance * 1000.0f);
+ const uint32_t rand_val = spectral_brush_rand(seed);
+ // Map to [0, 1]
+ const float normalized = (float)(rand_val % 10000) / 10000.0f;
+ return amplitude * normalized;
+ }
}
return 0.0f;
}
// Internal implementation: Render Bezier curve with profile
-static void draw_bezier_curve_impl(float* spectrogram,
- int dct_size,
- int num_frames,
- const float* control_frames,
- const float* control_freqs_hz,
- const float* control_amps,
- int n_control_points,
- ProfileType profile_type,
- float profile_param1,
- float profile_param2,
- bool additive) {
+static void draw_bezier_curve_impl(
+ float* spectrogram, int dct_size, int num_frames,
+ const float* control_frames, const float* control_freqs_hz,
+ const float* control_amps, int n_control_points, ProfileType profile_type,
+ float profile_param1, float profile_param2, bool additive) {
if (n_control_points < 1) {
- return; // Nothing to draw
+ return; // Nothing to draw
}
// For each frame in the spectrogram
for (int f = 0; f < num_frames; ++f) {
// 1. Evaluate Bezier curve at this frame
- const float freq_hz =
- evaluate_bezier_linear(control_frames, control_freqs_hz, n_control_points, (float)f);
- const float amplitude =
- evaluate_bezier_linear(control_frames, control_amps, n_control_points, (float)f);
+ const float freq_hz = evaluate_bezier_linear(
+ control_frames, control_freqs_hz, n_control_points, (float)f);
+ const float amplitude = evaluate_bezier_linear(control_frames, control_amps,
+ n_control_points, (float)f);
// 2. Convert frequency (Hz) to frequency bin index
// Nyquist frequency = SAMPLE_RATE / 2
@@ -125,7 +120,8 @@ static void draw_bezier_curve_impl(float* spectrogram,
// 3. Apply vertical profile around freq_bin_0
for (int b = 0; b < dct_size; ++b) {
const float dist = fabsf(b - freq_bin_0);
- const float profile_val = evaluate_profile(profile_type, dist, profile_param1, profile_param2);
+ const float profile_val =
+ evaluate_profile(profile_type, dist, profile_param1, profile_param2);
const float contribution = amplitude * profile_val;
const int idx = f * dct_size + b;
@@ -139,33 +135,24 @@ static void draw_bezier_curve_impl(float* spectrogram,
}
// Draw spectral brush (overwrites spectrogram content)
-void draw_bezier_curve(float* spectrogram,
- int dct_size,
- int num_frames,
+void draw_bezier_curve(float* spectrogram, int dct_size, int num_frames,
const float* control_frames,
- const float* control_freqs_hz,
- const float* control_amps,
- int n_control_points,
- ProfileType profile_type,
- float profile_param1,
- float profile_param2) {
- draw_bezier_curve_impl(spectrogram, dct_size, num_frames, control_frames, control_freqs_hz,
- control_amps, n_control_points, profile_type, profile_param1,
- profile_param2, false);
+ const float* control_freqs_hz, const float* control_amps,
+ int n_control_points, ProfileType profile_type,
+ float profile_param1, float profile_param2) {
+ draw_bezier_curve_impl(spectrogram, dct_size, num_frames, control_frames,
+ control_freqs_hz, control_amps, n_control_points,
+ profile_type, profile_param1, profile_param2, false);
}
// Draw spectral brush (adds to existing spectrogram content)
-void draw_bezier_curve_add(float* spectrogram,
- int dct_size,
- int num_frames,
- const float* control_frames,
- const float* control_freqs_hz,
- const float* control_amps,
- int n_control_points,
- ProfileType profile_type,
- float profile_param1,
- float profile_param2) {
- draw_bezier_curve_impl(spectrogram, dct_size, num_frames, control_frames, control_freqs_hz,
- control_amps, n_control_points, profile_type, profile_param1,
- profile_param2, true);
+void draw_bezier_curve_add(float* spectrogram, int dct_size, int num_frames,
+ const float* control_frames,
+ const float* control_freqs_hz,
+ const float* control_amps, int n_control_points,
+ ProfileType profile_type, float profile_param1,
+ float profile_param2) {
+ draw_bezier_curve_impl(spectrogram, dct_size, num_frames, control_frames,
+ control_freqs_hz, control_amps, n_control_points,
+ profile_type, profile_param1, profile_param2, true);
}