old school music tracker audio backend
at main 195 lines 7.0 kB view raw
1use std::ops::{AddAssign, IndexMut}; 2 3use crate::audio_processing::playback::{PlaybackState, PlaybackStatus}; 4use crate::audio_processing::sample::Interpolation; 5use crate::audio_processing::sample::SamplePlayer; 6use crate::audio_processing::Frame; 7use crate::manager::{OutputConfig, ToWorkerMsg}; 8use crate::project::song::Song; 9use crate::sample::Sample; 10use dasp::sample::ToSample; 11use simple_left_right::Reader; 12 13pub(crate) struct LiveAudio { 14 song: Reader<Song>, 15 playback_state: Option<PlaybackState>, 16 live_note: Option<SamplePlayer>, 17 manager: rtrb::Consumer<ToWorkerMsg>, 18 state_sender: triple_buffer::Input<Option<PlaybackStatus>>, 19 config: OutputConfig, 20 21 buffer: Box<[Frame]>, 22} 23 24// should probabyl be made configurable at some point 25const INTERPOLATION: u8 = Interpolation::Linear as u8; 26 27impl LiveAudio { 28 /// Not realtime safe. 29 pub fn new( 30 song: Reader<Song>, 31 manager: rtrb::Consumer<ToWorkerMsg>, 32 state_sender: triple_buffer::Input<Option<PlaybackStatus>>, 33 config: OutputConfig, 34 ) -> Self { 35 Self { 36 song, 37 playback_state: None, 38 live_note: None, 39 manager, 40 state_sender, 41 config, 42 buffer: vec![Frame::default(); usize::try_from(config.buffer_size).unwrap() * 2].into(), 43 } 44 } 45 46 #[rtsan_standalone::nonblocking] 47 fn send_state(&mut self) { 48 self.state_sender 49 .write(self.playback_state.as_ref().map(|s| s.get_status())); 50 } 51 52 #[rtsan_standalone::nonblocking] 53 /// returns true if work was done 54 fn fill_internal_buffer(&mut self, len: usize) -> bool { 55 // the output buffer should be smaller than the internal buffer 56 let buffer = &mut self.buffer[..len]; 57 58 let song = self.song.lock(); 59 60 // process manager events 61 while let Ok(event) = self.manager.pop() { 62 match event { 63 ToWorkerMsg::StopPlayback => self.playback_state = None, 64 ToWorkerMsg::Playback(settings) => { 65 self.playback_state = 66 PlaybackState::new(&song, self.config.sample_rate, settings); 67 } 68 ToWorkerMsg::PlayEvent(note) => { 69 if let Some(sample) = &song.samples[usize::from(note.sample_instr)] { 70 let sample_player = SamplePlayer::new( 71 Sample::clone(&sample.1), 72 sample.0, 73 // this at some point was divided by two, if i ever figure out why, maybe put it back 74 self.config.sample_rate, 75 note.note, 76 ); 77 self.live_note = Some(sample_player); 78 } 79 } 80 ToWorkerMsg::StopLiveNote => self.live_note = None, 81 } 82 } 83 if self.live_note.is_none() && self.playback_state.is_none() { 84 // no processing todo 85 return false; 86 } 87 88 // clear buffer from past run 89 // only happens if there is work todo 90 buffer.fill(Frame::default()); 91 92 // process live_note 93 if let Some(live_note) = &mut self.live_note { 94 let note_iter = live_note.iter::<{ INTERPOLATION }>(); 95 buffer 96 .iter_mut() 97 .zip(note_iter) 98 .for_each(|(buf, note)| buf.add_assign(note)); 99 100 if live_note.check_position().is_break() { 101 self.live_note = None; 102 } 103 } 104 105 // process song playback 106 if let Some(playback) = &mut self.playback_state { 107 let playback_iter = playback.iter::<{ INTERPOLATION }>(&song); 108 buffer 109 .iter_mut() 110 .zip(playback_iter) 111 .for_each(|(buf, frame)| buf.add_assign(frame)); 112 113 if playback.is_done() { 114 self.playback_state = None; 115 } 116 } 117 118 true 119 } 120 121 /// converts the internal buffer to any possible output format and channel count 122 /// sums stereo to mono and fills channels 3 and up with silence 123 #[rtsan_standalone::nonblocking] 124 #[inline] 125 fn fill_from_internal<Sample: dasp::sample::Sample + dasp::sample::FromSample<f32>>( 126 &mut self, 127 data: &mut [Sample], 128 ) { 129 // convert the internal buffer and move it to the out_buffer 130 if self.config.channel_count.get() == 1 { 131 data.iter_mut() 132 .zip(self.buffer.iter()) 133 .for_each(|(out, buf)| *out = buf.sum_to_mono().to_sample_()); 134 } else { 135 data.chunks_exact_mut(usize::from(self.config.channel_count.get())) 136 .map(|frame| frame.split_first_chunk_mut::<2>().unwrap().0) 137 .zip(self.buffer.iter()) 138 .for_each(|(out, buf)| *out = buf.to_sample()); 139 } 140 } 141 142 // unsure wether i want to use this or untyped_callback 143 // also relevant when cpal gets made into a generic that maybe this gets useful 144 pub fn get_typed_callback<Sample: dasp::sample::Sample + dasp::sample::FromSample<f32>>( 145 mut self, 146 ) -> impl FnMut(&mut [Sample]) { 147 move |data| { 148 let channel_count = usize::from(self.config.channel_count.get()); 149 assert!(data.len().is_multiple_of(channel_count)); 150 let out_frames = data.len() / channel_count; 151 assert!(self.buffer.len() > out_frames); 152 // assert_eq!( 153 // data.len(), 154 // usize::try_from(self.config.buffer_size).unwrap() 155 // * usize::from(self.config.channel_count.get()) 156 // ); 157 158 if self.fill_internal_buffer(out_frames) { 159 self.fill_from_internal(data); 160 } 161 self.send_state(); 162 } 163 // move |data, info| { 164 // assert_eq!( 165 // data.len(), 166 // usize::try_from(self.config.buffer_size).unwrap() 167 // * usize::from(self.config.channel_count.get()) 168 // ); 169 // self.send_state(Some(info)); 170 // } 171 } 172 173 // pub fn get_callback(mut self) -> impl FnMut(&mut [Frame], S::BufferInformation) { 174 // move |data, info| { 175 // assert_eq!(data.len(), self.config.buffer_size as usize * self.config.channel_count.get() as usize) 176 177 // if self.fill_internal_buffer() { 178 // self.fill_from_internal(data); 179 // } 180 // } 181 // } 182} 183 184// only used for testing 185// if not testing is unused 186#[allow(dead_code)] 187fn sine(output: &mut [[f32; 2]], sample_rate: f32) { 188 let mut sample_clock = 0f32; 189 for frame in output { 190 sample_clock = (sample_clock + 1.) % sample_rate; 191 let value = (sample_clock * 440. * 2. * std::f32::consts::PI / sample_rate).sin(); 192 *frame.index_mut(0) = value; 193 *frame.index_mut(1) = value; 194 } 195}