Files
songbird/src/input/adapters/cached/util.rs
Gnome! 1b98c30746 Fix clippy warnings (#214)
* Fix clippy warnings

* Fix implicitly elided lifetimes
2024-01-03 16:45:23 +00:00

459 lines
16 KiB
Rust

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::<f32>();
/// 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<usize>,
resample: Option<ResampleState>,
done: bool,
interrupted_samples: Vec<f32>,
interrupted_byte_pos: Range<usize>,
}
struct ResampleState {
/// Used to hold outputs from resampling, *ready to be used*.
resampled_data: Vec<Vec<f32>>,
/// The actual resampler.
resampler: FftFixedOut<f32>,
/// Used to hold inputs to resampler across packet boundaries.
scratch: AudioBuffer<f32>,
/// The range of floats in `resampled_data` which have not yet
/// been read.
resample_pos: Range<usize>,
}
impl ToAudioBytes {
pub fn new(parsed: Parsed, chan_limit: Option<usize>) -> 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::<f32>::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(true);
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<usize> {
// 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<u64> {
Err(IoErrorKind::Unsupported.into())
}
}
impl MediaSource for ToAudioBytes {
fn is_seekable(&self) -> bool {
false
}
fn byte_len(&self) -> Option<u64> {
None
}
}
#[inline]
fn write_out(
source: &AudioBufferRef<'_>,
target: &mut [u8],
source_pos: &mut Range<usize>,
spillover: &mut Vec<f32>,
spill_range: &mut Range<usize>,
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<S>(
source: &AudioBuffer<S>,
buf: &mut [u8],
source_pos: &mut Range<usize>,
spillover: &mut Vec<f32>,
spill_range: &mut Range<usize>,
num_chans: usize,
) -> usize
where
S: Sample + IntoSample<f32>,
{
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::<LittleEndian>((*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<f32>],
buf: &mut [u8],
source_pos: &mut Range<usize>,
spillover: &mut Vec<f32>,
spill_range: &mut Range<usize>,
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::<LittleEndian>(*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
}