this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cleanup

alice 0d897b68 a4618d45

+58 -14
+13 -3
tic80_rust/src/audio/capture.rs
··· 77 77 break; 78 78 } 79 79 } 80 - // Fallback to default config 80 + // Fallback to default config, with a notice 81 81 let stream_cfg = if let Some(ch) = chosen { 82 82 ch 83 83 } else { 84 - device 84 + let def = device 85 85 .default_input_config() 86 - .map_err(|e| anyhow::anyhow!("No default input config: {e}"))? 86 + .map_err(|e| anyhow::anyhow!("No default input config: {e}"))?; 87 + println!( 88 + "Audio: requested 44100 Hz not supported; using device default: {} Hz, {:?} format, {} ch", 89 + def.sample_rate().0, 90 + def.sample_format(), 91 + def.channels() 92 + ); 93 + def 87 94 }; 88 95 89 96 let sample_format = stream_cfg.sample_format(); ··· 102 109 103 110 let stream = match sample_format { 104 111 SampleFormat::F32 => build_stream::<f32>(&device, &cfg_fixed, channels, prod.clone())?, 112 + SampleFormat::F64 => build_stream::<f64>(&device, &cfg_fixed, channels, prod.clone())?, 105 113 SampleFormat::I16 => build_stream::<i16>(&device, &cfg_fixed, channels, prod.clone())?, 114 + SampleFormat::I32 => build_stream::<i32>(&device, &cfg_fixed, channels, prod.clone())?, 115 + SampleFormat::U8 => build_stream::<u8>(&device, &cfg_fixed, channels, prod.clone())?, 106 116 SampleFormat::U16 => build_stream::<u16>(&device, &cfg_fixed, channels, prod.clone())?, 107 117 other => return Err(anyhow::anyhow!("Unsupported sample format: {other:?}")), 108 118 };
+13 -11
tic80_rust/src/main.rs
··· 70 70 let mut audio_disable = false; 71 71 let mut audio_device: Option<String> = None; 72 72 let mut audio_vu = false; 73 + let mut debug_fft = false; 73 74 while let Some(arg) = args_iter.next() { 74 75 match arg.as_str() { 75 76 "--list-audio" => list_audio = true, 76 77 "--audio-disable" => audio_disable = true, 77 78 "--audio-vu" => audio_vu = true, 79 + "--debug-fft" => debug_fft = true, 78 80 "--audio-device" => { 79 81 if let Some(val) = args_iter.next() { 80 82 audio_device = Some(val); ··· 124 126 peak_acc: f32, 125 127 fft: Arc<RwLock<FFTState>>, 126 128 debug_fft: bool, 129 + last_fft_dbg: Instant, 127 130 } 128 131 let mut audio_state: Option<AudioState> = None; 129 - // Optional debug flag for FFT bins 130 - let debug_fft = std::env::args().any(|a| a == "--debug-fft"); 131 - 132 132 if !audio_disable { 133 133 let cap_cfg = audio_cap::AudioCaptureConfig { 134 134 device_substr: audio_device.clone(), ··· 156 156 peak_acc: 0.0, 157 157 fft: fft_arc, 158 158 debug_fft, 159 + last_fft_dbg: Instant::now(), 159 160 }); 160 161 } 161 162 Err(e) => { ··· 193 194 } 194 195 // Simple VU meter from audio ring 195 196 if let Some(a) = audio_state.as_mut() { 196 - // Drain available samples, feed analyzer, track peak 197 - while let Ok(s) = a.cons.pop() { 198 - a.peak_acc = a.peak_acc.max(s.abs()); 199 - if let Some(mut w) = a.fft.try_write() { 197 + // Drain available samples, feed analyzer with a single write lock, track peak 198 + { 199 + let mut w = a.fft.write(); 200 + while let Ok(s) = a.cons.pop() { 201 + a.peak_acc = a.peak_acc.max(s.abs()); 200 202 w.ingest(s); 201 203 } 202 - } 203 - if let Some(mut w) = a.fft.try_write() { 204 204 w.update(); 205 205 } 206 - if a.debug_fft { 206 + if a.debug_fft && a.last_fft_dbg.elapsed() >= Duration::from_millis(500) { 207 207 // Print a small subset of normalized bins 208 208 let bins = { 209 209 let r = a.fft.read(); 210 210 r.bins().min(16) 211 211 }; 212 212 let mut line = String::from("FFT[0..16]: "); 213 - if let Some(r) = a.fft.try_read() { 213 + { 214 + let r = a.fft.read(); 214 215 for i in 0..bins { 215 216 line.push_str(&format!("{:.2} ", r.fft_sm[i])); 216 217 } 217 218 } 218 219 println!("{}", line); 220 + a.last_fft_dbg = Instant::now(); 219 221 } 220 222 if a.vu_enabled && a.last_print.elapsed() >= Duration::from_millis(1000) { 221 223 let peak = a.peak_acc.max(1e-9);
+32
tic80_rust/tests/fft_tests.rs
··· 74 74 75 75 assert_eq!(fb.borrow_mut().pix(0, 0, None), Some(7)); 76 76 } 77 + 78 + #[test] 79 + fn fft_query_range_clamps_and_sums() { 80 + // Build an FFT state and fill normalized data with 1.0 for easy sums 81 + let mut fft = FFTState::new(8192); 82 + let size = fft.bins(); // 1024 83 + for k in 0..size { 84 + fft.fft_data[k] = 1.0; 85 + fft.fft_sm[k] = 1.0; 86 + fft.fft_raw[k] = 1.0; 87 + fft.fft_raw_sm[k] = 1.0; 88 + } 89 + 90 + // both below zero -> 0 91 + assert_eq!(query_fft(&fft, -10, -1, false, false), 0.0); 92 + // both above size -> 0 93 + assert_eq!( 94 + query_fft(&fft, size as i32, (size as i32) + 10, false, false), 95 + 0.0 96 + ); 97 + // start < 0 clamps to 0; end 5 => 6 bins 98 + assert_eq!(query_fft(&fft, -5, 5, false, false), 6.0); 99 + // start >= size clamps to 0; end 10 => 11 bins 100 + assert_eq!(query_fft(&fft, size as i32, 10, false, false), 11.0); 101 + // end >= size clamps to size-1; start 1020 => (1020..1023) length 4 102 + assert_eq!( 103 + query_fft(&fft, (size as i32) - 4, (size as i32) + 99, false, false), 104 + 4.0 105 + ); 106 + // start > end becomes start=end; single bin 107 + assert_eq!(query_fft(&fft, 10, 2, false, false), 1.0); 108 + }