WIP push-to-talk Letta chat frontend
at main 81 lines 2.7 kB view raw
1use crate::devices::types::AudioDeviceError; 2use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; 3use cpal::{default_host, Device, SampleFormat, Stream}; 4use dasp::interpolate::sinc::Sinc; 5use dasp::ring_buffer; 6use dasp::signal::{self, Signal}; 7use dasp::Frame; 8use std::sync::{mpsc::channel, Arc}; 9use tauri::async_runtime::Mutex; 10 11pub struct InputDeviceManager { 12 device: Device, 13 stream: Arc<Mutex<Option<Stream>>>, 14} 15 16fn resample( 17 signal: impl Signal<Frame = i16>, 18 source_hz: f64, 19 target_hz: f64, 20) -> impl Signal<Frame = i16> { 21 let ring_buffer = ring_buffer::Fixed::from([i16::EQUILIBRIUM; 10]); 22 let sinc = Sinc::new(ring_buffer); 23 signal.from_hz_to_hz(sinc, source_hz, target_hz) 24} 25 26impl InputDeviceManager { 27 pub fn new() -> Result<Self, AudioDeviceError> { 28 let host = default_host(); 29 let device = host 30 .default_input_device() 31 .ok_or(AudioDeviceError::NoDevicesFound)?; 32 let manager = InputDeviceManager { 33 device, 34 stream: Arc::new(Mutex::new(None)), 35 }; 36 37 Ok(manager) 38 } 39 40 pub async fn start_listening(&self) -> impl Signal<Frame = i16> { 41 let config = self 42 .device 43 .supported_input_configs() 44 .expect("failed to get supported input configs") 45 .find(|c| c.sample_format() == SampleFormat::I16) 46 .expect("failed to find PCM s16 config") 47 .with_max_sample_rate(); 48 let channels = config.channels() as usize; 49 let (tx, rx) = channel::<Vec<i16>>(); 50 let stream = self 51 .device 52 .build_input_stream( 53 &config.config(), 54 move |data: &[i16], _: &cpal::InputCallbackInfo| { 55 let mono = data 56 .chunks(channels) 57 .map(|frame| (frame.iter().sum::<i16>() / channels as i16).to_le()); 58 59 let _ = tx.send(mono.collect()); 60 }, 61 |err| eprintln!("encountered error streaming input: {}", err), 62 None, 63 ) 64 .expect("failed to create input stream"); 65 stream.play().expect("failed to start input stream"); 66 67 let samples = rx.into_iter().flat_map(|x| x.into_iter()); 68 let signal = signal::from_iter(samples); 69 let signal = resample(signal, config.sample_rate().0 as f64, 16000.0); 70 71 let mut s = self.stream.lock().await; 72 *s = Some(stream); 73 74 signal 75 } 76 77 pub async fn stop_listening(&self) { 78 let mut s = self.stream.lock().await; 79 *s = None; 80 } 81}