···231231 let vqt_fn = lua.create_function(move |_, bin: i32| {
232232 let val = if let Some(arc) = get_global_vqt() {
233233 let guard = arc.read();
234234- // Return normalized instantaneous; align with TIC: vqt returns normalized
234234+ // normalized instantaneous (may exceed 1.0)
235235 if bin >= 0 && (bin as usize) < guard.bins_count() {
236236- guard.vqt_norm[bin as usize] as f64
236236+ (guard.vqt_raw[bin as usize] / guard.vqt_peak) as f64
237237 } else {
238238 0.0
239239 }
···293293 let val = if let Some(arc) = get_global_vqt() {
294294 let guard = arc.read();
295295 if bin >= 0 && (bin as usize) < guard.bins_count() {
296296- guard.vqt_w_norm[bin as usize] as f64
296296+ (guard.vqt_w_raw[bin as usize] / guard.vqt_w_peak) as f64
297297 } else {
298298 0.0
299299 }
+25
tic80_rust/tests/vqt_tests.rs
···4040}
41414242#[test]
4343+fn vqt_instantaneous_vs_smoothed_differs() {
4444+ // Fresh state so smoothed = (1-a)*raw; normalized instantaneous should exceed smoothed normalized
4545+ let cap = 8192;
4646+ let sr = 44_100;
4747+ let mut vqt = VQTState::new(sr, cap);
4848+ let bin = 24usize;
4949+ let f0 = vqt_center_freq(bin);
5050+ let samples = gen_sine(f0, sr, 9000);
5151+ for s in samples {
5252+ vqt.ingest(s);
5353+ }
5454+ vqt.update();
5555+ let inst = vqt.vqt_raw[bin] / vqt.vqt_peak;
5656+ let sm = vqt.vqt_norm[bin];
5757+ assert!(
5858+ inst > 1.05,
5959+ "instantaneous normalized should exceed 1.0 on first update"
6060+ );
6161+ assert!(
6262+ sm <= 1.0 + 1e-6,
6363+ "smoothed normalized should be clamped to <= 1.0"
6464+ );
6565+}
6666+6767+#[test]
4368fn lua_vqt_reads_bin() {
4469 let cap = 8192;
4570 let sr = 44_100;