use crate::proc::{Procer, ProcerData, Update}; use crate::notes::{Note, NoteValue, ProcerNote}; use crate::outputers::{Outputer, Outputers, SimpleOutputer}; use crate::rfft::RFftProcer; use realfft::FftNum; use rustfft::num_traits::float::Float; use alsa::{Direction, ValueOr}; use alsa::pcm::{PCM, HwParams, Format, Access, State}; use derive_more::Display; use std::fmt::Debug; use std::io::Write; //use std::collections::VecDeque; pub enum Procers { Rfft(RFftProcer), // XXX: if i add a fir filter that doesn't need a buffer i think // i can just spec it as Fir12Tet(Fir12TetProcer) or similar // here } #[derive(Display)] #[display(fmt="StaticBuffer(rate={}, psize={}, channels={}, opsize={}, adev=(name={}), outdev=(name={:?}), cperiods={}, operiods={})", rate, period_size, channels, out_period_size, adev_name, outdev_name, capture_periods, output_periods)] pub struct StaticBuffer<'nl, I: Default + Debug + Clone + FftNum + Float + NoteValue, const US: usize, const BS: usize> { pub rate: u32, pub period_size: usize, pub channels: u32, pub out_period_size: usize, pub procers: Vec<(Procers, ProcerData<'nl, I>)>, pub adev_name: String, pub adev: PCM, pub outdev_name: Option, pub outdev: Option, pub outputer: Outputers, pub capture_periods: u32, pub output_periods: u32, } impl<'nl, I: Default + Debug + Clone + FftNum + Float + From + NoteValue, const US: usize, const BS: usize> StaticBuffer<'nl, I, US, BS> { pub fn new(rate: u32, channels: u32, procers: Vec<(Procers, ProcerData<'nl, I>)>, adev_name: String, outdev_name: Option, outputer: Outputers) -> Self { let mut adev = PCM::new(&adev_name, Direction::Capture, true).unwrap(); let mut capture_periods = 0; { let hwp = HwParams::any(&adev).unwrap(); hwp.set_channels(channels).unwrap(); //hwp.set_rate_resample(false); hwp.set_rate(rate, ValueOr::Nearest).unwrap(); hwp.set_format(Format::s16()).unwrap(); hwp.set_access(Access::MMapInterleaved).unwrap(); hwp.set_period_size(US as i64, ValueOr::Nearest).unwrap(); hwp.set_periods(8, ValueOr::Nearest).unwrap(); // TODO: directly using the ALSA ring buffer and copying directly from it instead of // going through the Update/VecDeque might be interesting in the future, i think it'd // require changing the const sizes to runtime determined sizes though, so future // changes to try~ adev.hw_params(&hwp).unwrap(); capture_periods = hwp.get_periods().unwrap(); println!("capture periods: {}", capture_periods); } let (outdev, out_period_size, output_periods) = match &outdev_name { None => (None, 0, 0), Some(od_name) => { let mut outdev = PCM::new(&od_name, Direction::Playback, true).unwrap(); let mut output_periods = 0; let mut out_period_size = 0; { let hwp = HwParams::any(&outdev).unwrap(); hwp.set_channels(channels).unwrap(); hwp.set_rate(rate, ValueOr::Nearest).unwrap(); hwp.set_format(Format::s16()).unwrap(); hwp.set_access(Access::RWInterleaved).unwrap(); //hwp.set_period_size(US as i64, ValueOr::Nearest).unwrap(); //hwp.set_periods(8, ValueOr::Nearest).unwrap(); outdev.hw_params(&hwp).unwrap(); output_periods = hwp.get_periods().unwrap(); out_period_size = hwp.get_period_size().unwrap() as usize; println!("output periods: {}", output_periods); } (Some(outdev), out_period_size, output_periods) } }; return Self { rate, procers, adev_name, adev, outdev_name, outdev, period_size: US, out_period_size: out_period_size, channels: channels, outputer, capture_periods, output_periods, } } pub fn capture_loop(&mut self) { let empty: Vec = vec![0i16; US * self.channels as usize]; //let mut line_str: String = format!("\r{:80}", ""); let in_io = self.adev.io_i16().unwrap(); let out_io_opt = { match &self.outdev { None => (None), Some(outdev) => { //self.adev.link(&outdev).unwrap(); let out_io = outdev.io_i16().unwrap(); // queue up an empty period in the outdev so we can sync between in and out devices // with a 4 period latency for _i in 0..std::cmp::min(4, self.output_periods/2) { out_io.writei(&empty).unwrap(); } outdev.start();//.unwrap(); println!("output available: {}", outdev.avail().unwrap()); Some(out_io) } } }; self.adev.start();//.unwrap(); //let vt: I = I::from(112); let conv_scale: I = >::from(i16::MAX); println!("input available: {}", self.adev.avail_update().unwrap()); let mut latentframes = 0; let mut unstarted = false; loop { /*println!("output available: {}", self.outdev.avail().unwrap()); println!("input available: {}", self.adev.avail_update().unwrap());*/ self.adev.wait(None).unwrap(); //println!("a"); // should be called right before mmap_begin according to: // https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html#ga6d4acf42de554d4d1177fb035d484ea4 let avail_frames = self.adev.avail_update().unwrap(); in_io.mmap(US, |in_slice| { let size = in_slice.len(); //println!("slice length: {}", size); if size == 0 { println!("warning: passed a 0-sized buffer from mmap\n"); return 0; } let stride = self.channels as usize; if size != (US * stride) { println!("warning: passed a {} sized buffer from mmap\n\n", size); return 0; } assert_eq!(size, US * stride); let (written, oa) = match &out_io_opt { Some(out_io) => { let wri = out_io.writei(&in_slice).unwrap(); assert_eq!(wri, (size/stride)); let oaa = self.outdev.as_ref().unwrap().avail().unwrap(); (wri, oaa) } None => (size/stride, 0i64), }; let ia = self.adev.avail_update().unwrap(); let total_outbuf: i64 = oa - ia; /*print!("\rtotal: {}, out: {}, in: {}", total_outbuf, oa, ia); std::io::stdout().flush().unwrap();*/ //return written; //let constrained_slice = in_slice[0..US*stride]; let mut update: Update = [I::default(); US]; // copy over the left channel data (even indexes) // TODO/XXX: see if this uses SIMD or needs optimizing :o // using iter and map/function that does strides would prob also work well :o // gotta scale this from i16::MAX to float, alsa // seems to divide by i16::MAX, not sure if there's a better way to do this for i in 0..US { update[i] = >::from(in_slice[i*stride]) / conv_scale; }; // hmm, i only need to use 1 procer here for displaying, not sure what to do with // the rest :o //for (procer, pdata) in self.procers { } let (ref mut procer, ref mut pdata) = self.procers.first_mut().unwrap(); let mut processed = false; { let mut pnotes = pdata.pnotes.0.as_mut_slice(); //let processed = true; processed = procer.process_data(&update, pnotes); } if processed { self.outputer.handle_pnotes(&mut pdata.pnotes); //let mut position = 1; // TODO: maybe copy, inplace probably works for now though /*pnotes.sort_unstable_by(|pn1, pn2| (-pn1.2).partial_cmp(&-pn2.2).unwrap()); print!("\r{:-80}", pdata.pnotes); std::io::stdout().flush();*/ } //let mut position = 0; //let mut remaining = in_slice.len(); //let mut remain_frames = remaining/(self.channels as usize); //let mut handled = false; //self.outdev.wait(None).unwrap(); //println!("wrote {} frames", written); /*while handled == false { self.outdev.wait(None).unwrap(); out_io.mmap(remain_frames, |out_slice| { let outlen = out_slice.len(); println!("out slice len: {}", out_slice.len()); if outlen == 0 { return 0; } out_slice.clone_from_slice(&in_slice); handled = true; return out_slice.len()/(self.channels as usize); }).unwrap(); }*/ /*if latentframes > 0 { latentframes -= 1; } else if unstarted { unstarted = false; println!("starting outdev"); self.outdev.start().unwrap(); }*/ return in_slice.len()/(self.channels as usize); }).unwrap(); } } //fn new( } /*impl StaticBuffer<'nl, B> { }*/ //impl<'nl, B> StaticBuffer<'nl, A> { //pub fn fill_initial(&self, //pub fn new(rate: usize, period_size: usize, out_period_size: usize, procers: Vec<(Procers, ProcerData<'nl>)>, //} impl Procer for Procers { fn get_size(&self) -> usize { match self { Procers::Rfft(rfp) => rfp.get_size(), } } fn get_frequency(&self) -> usize { match self { Procers::Rfft(rfp) => rfp.get_frequency(), } } fn make_pnotes<'nl>(&self, notes: &'nl [Note]) -> Vec> { match self { Procers::Rfft(rfp) => rfp.make_pnotes(notes), } } fn process_data(&mut self, input: &Update, notes: &mut [ProcerNote]) -> bool { match self { Procers::Rfft(rfp) => rfp.process_data(input, notes), } } }