songbird/input/codecs/
raw.rs

1use std::io::{Seek, SeekFrom};
2use symphonia::core::{
3    audio::Channels,
4    codecs::{CodecParameters, CODEC_TYPE_PCM_F32LE},
5    errors::{self as symph_err, Result as SymphResult, SeekErrorKind},
6    formats::prelude::*,
7    io::{MediaSource, MediaSourceStream, ReadBytes, SeekBuffered},
8    meta::{Metadata as SymphMetadata, MetadataLog},
9    probe::{Descriptor, Instantiate, QueryDescriptor},
10};
11
12impl QueryDescriptor for RawReader {
13    fn query() -> &'static [Descriptor] {
14        &[symphonia_core::support_format!(
15            "raw",
16            "Raw arbitrary-length f32 audio container.",
17            &["rawf32"],
18            &[],
19            &[b"SbirdRaw"]
20        )]
21    }
22
23    fn score(_context: &[u8]) -> u8 {
24        255
25    }
26}
27
28/// Symphonia support for a simple container for raw f32-PCM data of unknown duration.
29///
30/// Contained files have a simple header:
31/// * the 8-byte signature `b"SbirdRaw"`,
32/// * the sample rate, as a little-endian `u32`,
33/// * the channel count, as a little-endian `u32`.
34///
35/// The remainder of the file is interleaved little-endian `f32` samples.
36pub struct RawReader {
37    source: MediaSourceStream,
38    track: Track,
39    meta: MetadataLog,
40    curr_ts: TimeStamp,
41    max_ts: Option<TimeStamp>,
42}
43
44impl FormatReader for RawReader {
45    fn try_new(mut source: MediaSourceStream, _options: &FormatOptions) -> SymphResult<Self> {
46        let mut magic = [0u8; 8];
47        ReadBytes::read_buf_exact(&mut source, &mut magic[..])?;
48
49        if &magic != b"SbirdRaw" {
50            source.seek_buffered_rel(-(magic.len() as isize));
51            return symph_err::decode_error("rawf32: illegal magic byte sequence.");
52        }
53
54        let sample_rate = source.read_u32()?;
55        let n_chans = source.read_u32()?;
56
57        let chans = match n_chans {
58            1 => Channels::FRONT_LEFT,
59            2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT,
60            _ =>
61                return symph_err::decode_error(
62                    "rawf32: channel layout is not stereo or mono for fmt_pcm",
63                ),
64        };
65
66        let mut codec_params = CodecParameters::new();
67
68        codec_params
69            .for_codec(CODEC_TYPE_PCM_F32LE)
70            .with_bits_per_coded_sample((std::mem::size_of::<f32>() as u32) * 8)
71            .with_bits_per_sample((std::mem::size_of::<f32>() as u32) * 8)
72            .with_sample_rate(sample_rate)
73            .with_time_base(TimeBase::new(1, sample_rate))
74            .with_sample_format(symphonia_core::sample::SampleFormat::F32)
75            .with_max_frames_per_packet(sample_rate as u64 / 50)
76            .with_channels(chans);
77
78        Ok(Self {
79            source,
80            track: Track {
81                id: 0,
82                language: None,
83                codec_params,
84            },
85            meta: MetadataLog::default(),
86            curr_ts: 0,
87            max_ts: None,
88        })
89    }
90
91    fn cues(&self) -> &[Cue] {
92        &[]
93    }
94
95    fn metadata(&mut self) -> SymphMetadata<'_> {
96        self.meta.metadata()
97    }
98
99    fn seek(&mut self, _mode: SeekMode, to: SeekTo) -> SymphResult<SeekedTo> {
100        let can_backseek = self.source.is_seekable();
101
102        let track = &self.track;
103        let rate = track.codec_params.sample_rate;
104        let ts = match to {
105            SeekTo::Time { time, .. } =>
106                if let Some(rate) = rate {
107                    TimeBase::new(1, rate).calc_timestamp(time)
108                } else {
109                    return symph_err::seek_error(SeekErrorKind::Unseekable);
110                },
111            SeekTo::TimeStamp { ts, .. } => ts,
112        };
113
114        if let Some(max_ts) = self.max_ts {
115            if ts > max_ts {
116                return symph_err::seek_error(SeekErrorKind::OutOfRange);
117            }
118        }
119
120        let backseek_needed = self.curr_ts > ts;
121
122        if backseek_needed && !can_backseek {
123            return symph_err::seek_error(SeekErrorKind::ForwardOnly);
124        }
125
126        let chan_count = track
127            .codec_params
128            .channels
129            .expect("Channel count is built into format.")
130            .count() as u64;
131
132        let seek_pos = 16 + (std::mem::size_of::<f32>() as u64) * (ts * chan_count);
133
134        self.source.seek(SeekFrom::Start(seek_pos))?;
135        self.curr_ts = ts;
136
137        Ok(SeekedTo {
138            track_id: track.id,
139            required_ts: ts,
140            actual_ts: ts,
141        })
142    }
143
144    fn tracks(&self) -> &[Track] {
145        std::slice::from_ref(&self.track)
146    }
147
148    fn default_track(&self) -> Option<&Track> {
149        Some(&self.track)
150    }
151
152    fn next_packet(&mut self) -> SymphResult<Packet> {
153        let track = &self.track;
154        let rate = track
155            .codec_params
156            .sample_rate
157            .expect("Sample rate is built into format.") as usize;
158
159        let chan_count = track
160            .codec_params
161            .channels
162            .expect("Channel count is built into format.")
163            .count();
164
165        let sample_unit = std::mem::size_of::<f32>() * chan_count;
166
167        // Aim for 20ms (50Hz).
168        let buf = self.source.read_boxed_slice((rate / 50) * sample_unit)?;
169
170        let sample_ct = (buf.len() / sample_unit) as u64;
171        let out = Packet::new_from_boxed_slice(0, self.curr_ts, sample_ct, buf);
172
173        self.curr_ts += sample_ct;
174
175        Ok(out)
176    }
177
178    fn into_inner(self: Box<Self>) -> MediaSourceStream {
179        self.source
180    }
181}