diff options
Diffstat (limited to 'src/audio/spectral_brush.cc')
| -rw-r--r-- | src/audio/spectral_brush.cc | 145 |
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); } |
