summaryrefslogtreecommitdiff
path: root/src/audio/gen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/gen.cc')
-rw-r--r--src/audio/gen.cc42
1 files changed, 42 insertions, 0 deletions
diff --git a/src/audio/gen.cc b/src/audio/gen.cc
index 74b468c..0757b4d 100644
--- a/src/audio/gen.cc
+++ b/src/audio/gen.cc
@@ -87,6 +87,48 @@ std::vector<float> generate_note_spectrogram(const NoteParams& params,
}
}
+ // Normalize to consistent RMS level (matching spectool --normalize behavior)
+ // 1. Synthesize PCM to measure actual output levels
+ std::vector<float> pcm_data(num_frames * DCT_SIZE);
+ for (int f = 0; f < num_frames; ++f) {
+ const float* spectral_frame = spec_data.data() + (f * DCT_SIZE);
+ float* time_frame = pcm_data.data() + (f * DCT_SIZE);
+ idct_512(spectral_frame, time_frame);
+ }
+
+ // 2. Calculate RMS and peak
+ float rms_sum = 0.0f;
+ float peak = 0.0f;
+ for (size_t i = 0; i < pcm_data.size(); ++i) {
+ const float abs_val = fabsf(pcm_data[i]);
+ if (abs_val > peak) {
+ peak = abs_val;
+ }
+ rms_sum += pcm_data[i] * pcm_data[i];
+ }
+ const float rms = sqrtf(rms_sum / pcm_data.size());
+
+ // 3. Normalize to target RMS (0.15, matching spectool default)
+ const float target_rms = 0.15f;
+ const float max_safe_peak = 1.0f; // Conservative: ensure output peak ≤ 1.0
+
+ if (rms > 1e-6f) {
+ // Calculate scale factor to reach target RMS
+ float norm_scale = target_rms / rms;
+
+ // Check if this would cause clipping
+ const float predicted_peak = peak * norm_scale;
+ if (predicted_peak > max_safe_peak) {
+ // Reduce scale to prevent clipping
+ norm_scale = max_safe_peak / peak;
+ }
+
+ // Apply normalization scale to spectrogram
+ for (size_t i = 0; i < spec_data.size(); ++i) {
+ spec_data[i] *= norm_scale;
+ }
+ }
+
return spec_data;
}