+ }
+
+ if !args.file_audio_only {
+ for dst in samples.iter_mut() {
+ *dst = 0.0;
+ }
+ for (frequency, dat) in ¬es_map {
+ let mul = frequency / (rate as f32) * 2.0 * std::f32::consts::PI;
+ for (i, dst) in samples.iter_mut().enumerate() {
+ let intensity = dat[i * fft_ratio / FFT_BUF_SIZE];
+ let pos = (i as f32) * mul;
+ *dst += pos.sin() * intensity / 16.0;
+ }
+ }
+ }
+
+ let state = Arc::new(SharedState::default());
+ let state2 = Arc::clone(&state);
+ let stream = device.build_output_stream(
+ &supported_config.into(),
+ move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
+ let state = &state2;
+ for (dst, src) in data.iter_mut().zip(
+ samples
+ .get(state.timestamp.load(Ordering::Relaxed)..)
+ .unwrap_or(&[])
+ .iter()
+ .chain(repeat(&0.0)),
+ ) {
+ *dst = <f32 as IntoSample<i16>>::into_sample(*src);
+ }
+ state.timestamp.fetch_add(data.len(), Ordering::Relaxed);
+ },
+ move |_err| {},
+ None,
+ )?;
+ stream.play()?;
+ loop {
+ sleep(Duration::from_secs(1000));
+ }
+}
+
+#[derive(Default)]
+struct SharedState {
+ timestamp: AtomicUsize,
+}
+
+struct FFT {
+ window: Vec<f32>,
+ input: Vec<f32>,
+ fft: Arc<dyn RealToComplex<f32>>,
+ output: Vec<Complex<f32>>,
+ scratch: Vec<Complex<f32>>,
+}
+
+impl FFT {
+ fn all_windows<'t>(
+ &'_ self,
+ slice: &'t [f32],
+ sample_spacing: usize,
+ ) -> impl Iterator<Item = impl Iterator<Item = f32> + 't> + 't {
+ let len = self.len();
+ (0..(slice.len()))
+ .filter(move |i| i % sample_spacing == 0)
+ .map(move |window| {
+ (window..(window + len)).map(move |i: usize| {
+ i.checked_sub(len)
+ .and_then(|i| slice.get(i).copied())
+ .unwrap_or(0.0)
+ })
+ })
+ }
+ fn new(len: usize) -> Self {
+ let fft = realfft::RealFftPlanner::new().plan_fft_forward(len);
+ Self {
+ window: (0..len)
+ .map(|i| {
+ let pos = (i as f32) / (len - 1) as f32;
+ 0.5 * (1.0 - f32::cos(std::f32::consts::PI * 2.0 * pos))
+ })
+ .collect(),
+ input: fft.make_input_vec(),
+ output: fft.make_output_vec(),
+ scratch: fft.make_scratch_vec(),
+ fft,
+ }
+ }
+ fn process<'t>(
+ &'t mut self,
+ samples: impl IntoIterator<Item = f32>,
+ hz: u32,
+ ) -> eyre::Result<impl Fn(f32) -> f32 + 't> {
+ self.input.clear();
+ self.input
+ .extend(samples.into_iter().zip(&self.window).map(|(a, b)| a * *b));
+ self.fft
+ .process_with_scratch(&mut self.input, &mut self.output, &mut self.scratch)?;
+ let mul = 1.0 / (self.len() as f32).sqrt();
+ let self2 = &*self;
+ let discrete_query = move |i: usize| (self2.output[i] * mul).norm();
+ let hz_to_idx = move |query_hz: f32| (self2.len() as f32 * query_hz / (hz as f32)) as usize;
+ Ok(move |query_hz| discrete_query(hz_to_idx(query_hz)))
+ }
+ fn len(&self) -> usize {
+ self.window.len()
+ }