use crate::{constants::*, driver::tasks::mixer::mix_logic, input::Parsed}; use byteorder::{LittleEndian, WriteBytesExt}; use rubato::{FftFixedOut, Resampler}; use std::{ io::{ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, Write}, mem, ops::Range, }; use symphonia_core::{ audio::{AudioBuffer, AudioBufferRef, Layout, Signal, SignalSpec}, conv::IntoSample, io::MediaSource, sample::Sample, }; const SAMPLE_LEN: usize = mem::size_of::(); /// Adapter for Symphonia sources into an interleaved f32 bytestream. /// /// This will output `f32`s in LE byte order, matching the channel count /// of the input. pub struct ToAudioBytes { chan_count: usize, chan_limit: usize, parsed: Parsed, /// Position with parsed's last decoded frame. inner_pos: Range, resample: Option, done: bool, interrupted_samples: Vec, interrupted_byte_pos: Range, } struct ResampleState { /// Used to hold outputs from resampling, *ready to be used*. resampled_data: Vec>, /// The actual resampler. resampler: FftFixedOut, /// Used to hold inputs to resampler across packet boundaries. scratch: AudioBuffer, /// The range of floats in `resampled_data` which have not yet /// been read. resample_pos: Range, } impl ToAudioBytes { pub fn new(parsed: Parsed, chan_limit: Option) -> Self { let track_info = parsed.decoder.codec_params(); let sample_rate = track_info.sample_rate.unwrap_or(SAMPLE_RATE_RAW as u32); let maybe_layout = track_info.channel_layout; let maybe_chans = track_info.channels; let chan_count = if let Some(chans) = maybe_chans { chans.count() } else if let Some(layout) = maybe_layout { match layout { Layout::Mono => 1, Layout::Stereo => 2, Layout::TwoPointOne => 3, Layout::FivePointOne => 6, } } else { 2 }; let chan_limit = chan_limit.unwrap_or(chan_count); let resample = (sample_rate != SAMPLE_RATE_RAW as u32).then(|| { let spec = if let Some(chans) = maybe_chans { SignalSpec::new(SAMPLE_RATE_RAW as u32, chans) } else if let Some(layout) = maybe_layout { SignalSpec::new_with_layout(SAMPLE_RATE_RAW as u32, layout) } else { SignalSpec::new_with_layout(SAMPLE_RATE_RAW as u32, Layout::Stereo) }; let scratch = AudioBuffer::::new(MONO_FRAME_SIZE as u64, spec); // TODO: integ. error handling here. let resampler = FftFixedOut::new( sample_rate as usize, SAMPLE_RATE_RAW, RESAMPLE_OUTPUT_FRAME_SIZE, 4, chan_count, ) .expect("Failed to create resampler."); let resampled_data = resampler.output_buffer_allocate(); ResampleState { resampled_data, resampler, scratch, resample_pos: 0..0, } }); Self { chan_count, chan_limit, parsed, inner_pos: 0..0, resample, done: false, interrupted_samples: Vec::with_capacity(chan_count), interrupted_byte_pos: 0..0, } } pub fn num_channels(&self) -> usize { self.chan_count.min(self.chan_limit) } fn is_done(&self) -> bool { self.done && self.inner_pos.is_empty() && self.resample.as_ref().map_or(true, |v| { v.scratch.frames() == 0 && v.resample_pos.is_empty() }) && self.interrupted_byte_pos.is_empty() } } impl Read for ToAudioBytes { fn read(&mut self, mut buf: &mut [u8]) -> IoResult { // NOTE: this is disturbingly similar to the mixer code, but different enough that we can't // just reuse it freely. let orig_sz = buf.len(); let num_chans = self.num_channels(); while !buf.is_empty() && !self.is_done() { // Work to clear interrupted channel floats. while !buf.is_empty() && !self.interrupted_byte_pos.is_empty() { let index_of_first_f32 = self.interrupted_byte_pos.start / SAMPLE_LEN; let f32_inner_pos = self.interrupted_byte_pos.start % SAMPLE_LEN; let f32_bytes_remaining = SAMPLE_LEN - f32_inner_pos; let to_write = f32_bytes_remaining.min(buf.len()); let bytes = self.interrupted_samples[index_of_first_f32].to_le_bytes(); let written = buf.write(&bytes[f32_inner_pos..][..to_write])?; self.interrupted_byte_pos.start += written; } // Clear out already produced resampled floats. if let Some(resample) = self.resample.as_mut() { if !buf.is_empty() && !resample.resample_pos.is_empty() { let bytes_advanced = write_resample_buffer( &resample.resampled_data, buf, &mut resample.resample_pos, &mut self.interrupted_samples, &mut self.interrupted_byte_pos, num_chans, ); buf = &mut buf[bytes_advanced..]; } if !resample.resample_pos.is_empty() { continue; } } // Now work with new packets. let source_packet = if !self.inner_pos.is_empty() { Some(self.parsed.decoder.last_decoded()) } else if let Ok(pkt) = self.parsed.format.next_packet() { if pkt.track_id() != self.parsed.track_id { continue; } self.parsed .decoder .decode(&pkt) .map(|pkt| { self.inner_pos = 0..pkt.frames(); pkt }) .ok() } else { // EOF. None }; if source_packet.is_none() { self.done = true; if let Some(resample) = self.resample.as_mut() { if resample.scratch.frames() != 0 { let data = &mut resample.resampled_data; let resampler = &mut resample.resampler; let in_len = resample.scratch.frames(); let to_render = resampler.input_frames_next().saturating_sub(in_len); if to_render != 0 { resample.scratch.render_reserved(Some(to_render)); for plane in resample.scratch.planes_mut().planes() { for val in &mut plane[in_len..] { *val = 0.0f32; } } } // Luckily, we make use of the WHOLE input buffer here. resampler .process_into_buffer(resample.scratch.planes().planes(), data, None) .unwrap(); // Calculate true end position using sample rate math let ratio = (data[0].len() as f32) / (resample.scratch.frames() as f32); let out_samples = (ratio * (in_len as f32)).round() as usize; resample.scratch.clear(); resample.resample_pos = 0..out_samples; } } // Now go back and make use of the buffer. // We have to do this here because we can't make any guarantees about // the read site having enough space to hold all samples etc. continue; } let source_packet = source_packet.unwrap(); if let Some(resample) = self.resample.as_mut() { // Do a resample using the newest packet. let pkt_frames = source_packet.frames(); if pkt_frames == 0 { continue; } let needed_in_frames = resample.resampler.input_frames_next(); let available_frames = self.inner_pos.len(); let force_copy = resample.scratch.frames() != 0 || needed_in_frames > available_frames; if (!force_copy) && matches!(source_packet, AudioBufferRef::F32(_)) { // This is the only case where we can pull off a straight resample... // I.e., skip scratch. // NOTE: if let needed as if-let && {bool} is nightly only. if let AudioBufferRef::F32(s_pkt) = source_packet { let refs: Vec<&[f32]> = s_pkt .planes() .planes() .iter() .map(|s| &s[self.inner_pos.start..][..needed_in_frames]) .collect(); self.inner_pos.start += needed_in_frames; resample .resampler .process_into_buffer(&refs, &mut resample.resampled_data, None) .unwrap(); } else { unreachable!() } } else { // We either lack enough samples, or have the wrong data format, forcing // a conversion/copy into scratch. let old_scratch_len = resample.scratch.frames(); let missing_frames = needed_in_frames - old_scratch_len; let frames_to_take = available_frames.min(missing_frames); resample.scratch.render_reserved(Some(frames_to_take)); mix_logic::copy_into_resampler( &source_packet, &mut resample.scratch, self.inner_pos.start, old_scratch_len, frames_to_take, ); self.inner_pos.start += frames_to_take; if resample.scratch.frames() == needed_in_frames { resample .resampler .process_into_buffer( resample.scratch.planes().planes(), &mut resample.resampled_data, None, ) .unwrap(); resample.scratch.clear(); } else { continue; } } resample.resample_pos = 0..resample.resampled_data[0].len(); } else { // Newest packet may be used straight away: just convert format // to ensure it's f32. let bytes_advanced = write_out( &source_packet, buf, &mut self.inner_pos, &mut self.interrupted_samples, &mut self.interrupted_byte_pos, num_chans, ); buf = &mut buf[bytes_advanced..]; } } Ok(orig_sz - buf.len()) } } impl Seek for ToAudioBytes { fn seek(&mut self, _pos: std::io::SeekFrom) -> IoResult { Err(IoErrorKind::Unsupported.into()) } } impl MediaSource for ToAudioBytes { fn is_seekable(&self) -> bool { false } fn byte_len(&self) -> Option { None } } #[inline] fn write_out( source: &AudioBufferRef, target: &mut [u8], source_pos: &mut Range, spillover: &mut Vec, spill_range: &mut Range, num_chans: usize, ) -> usize { match source { AudioBufferRef::U8(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::U16(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::U24(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::U32(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::S8(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::S16(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::S24(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::S32(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::F32(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), AudioBufferRef::F64(v) => write_symph_buffer(v, target, source_pos, spillover, spill_range, num_chans), } } #[inline] fn write_symph_buffer( source: &AudioBuffer, buf: &mut [u8], source_pos: &mut Range, spillover: &mut Vec, spill_range: &mut Range, num_chans: usize, ) -> usize where S: Sample + IntoSample, { let float_space = buf.len() / SAMPLE_LEN; let interleaved_space = float_space / num_chans; let non_contiguous_end = (float_space % num_chans) != 0; let remaining = source_pos.len(); let to_write = remaining.min(interleaved_space); let need_spill = non_contiguous_end && to_write < remaining; let samples_used = to_write + usize::from(need_spill); let last_sample = source_pos.start + to_write; if need_spill { spillover.clear(); *spill_range = 0..num_chans * SAMPLE_LEN; } for (i, plane) in source.planes().planes()[..num_chans].iter().enumerate() { for (j, sample) in plane[source_pos.start..][..to_write].iter().enumerate() { // write this into the correct slot of buf. let addr = ((j * num_chans) + i) * SAMPLE_LEN; (&mut buf[addr..][..SAMPLE_LEN]) .write_f32::((*sample).into_sample()) .expect("Address known to exist by length checks."); } if need_spill { spillover.push(plane[last_sample].into_sample()); } } source_pos.start += samples_used; to_write * num_chans * SAMPLE_LEN } #[inline] fn write_resample_buffer( source: &[Vec], buf: &mut [u8], source_pos: &mut Range, spillover: &mut Vec, spill_range: &mut Range, num_chans: usize, ) -> usize { let float_space = buf.len() / SAMPLE_LEN; let interleaved_space = float_space / num_chans; let non_contiguous_end = (float_space % num_chans) != 0; let remaining = source_pos.len(); let to_write = remaining.min(interleaved_space); let need_spill = non_contiguous_end && to_write < remaining; let samples_used = to_write + usize::from(need_spill); let last_sample = source_pos.start + to_write; if need_spill { spillover.clear(); *spill_range = 0..num_chans * SAMPLE_LEN; } for (i, plane) in source[..num_chans].iter().enumerate() { for (j, sample) in plane[source_pos.start..][..to_write].iter().enumerate() { // write this into the correct slot of buf. let addr = ((j * num_chans) + i) * SAMPLE_LEN; (&mut buf[addr..][..SAMPLE_LEN]) .write_f32::(*sample) .expect("Address well-formed according to bounds checks."); } if need_spill { spillover.push(plane[last_sample]); } } source_pos.start += samples_used; to_write * num_chans * SAMPLE_LEN }