1use super::DecoderOptions;
3use crate::decoder::file::FileDecoder;
4use crate::{AudioFormat, Error, ErrorCode, Result};
5use std::path::Path;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize, Ordering};
8use std::thread::{self, JoinHandle};
9use std::time::Duration;
10
11#[derive(Clone, Debug)]
13pub struct FileStreamOptions {
14 pub decoder: DecoderOptions,
15 pub buffer_frames: usize,
17 pub encoded_buffer_bytes: usize,
20 pub max_decoder_bytes: usize,
23 pub max_pcm_bytes: usize,
25 pub max_header_chunks: usize,
27 pub max_file_bytes: u64,
29}
30impl Default for FileStreamOptions {
31 fn default() -> Self {
32 Self {
33 decoder: DecoderOptions::default(),
34 buffer_frames: 16384,
35 encoded_buffer_bytes: 1024 * 1024,
36 max_decoder_bytes: 16 * 1024 * 1024,
37 max_pcm_bytes: 32 * 1024 * 1024,
38 max_header_chunks: 4096,
39 max_file_bytes: 8 * 1024 * 1024 * 1024,
40 }
41 }
42}
43impl FileStreamOptions {
44 fn validate(&self) -> Result<()> {
45 if !(1..=1_048_576).contains(&self.buffer_frames)
46 || !(4096..=16 * 1024 * 1024).contains(&self.encoded_buffer_bytes)
47 || !(1..=65536).contains(&self.decoder.chunk_frames)
48 || self.max_header_chunks == 0
49 || self.max_header_chunks > 65536
50 || self.max_decoder_bytes == 0
51 || self.max_decoder_bytes > 256 * 1024 * 1024
52 || self.max_pcm_bytes == 0
53 || self.max_pcm_bytes > 256 * 1024 * 1024
54 {
55 return Err(Error::invalid(
56 "Invalid file-stream buffer or header limits",
57 ));
58 }
59 Ok(())
60 }
61}
62
63#[derive(Clone, Copy, Debug)]
64struct Info {
65 channels: usize,
66 rate: u32,
67 frames: Option<u64>,
68 format: AudioFormat,
69}
70#[derive(Clone, Copy, Debug, Eq, PartialEq)]
72pub struct FileStreamStatus {
73 pub generation: u64,
75 pub buffered_frames: usize,
77 pub underruns: u64,
79 pub cancelled: bool,
80}
81struct Ring {
82 samples: Box<[AtomicU32]>,
83 generations: Box<[AtomicU64]>,
84 head: AtomicUsize,
85 tail: AtomicUsize,
86 generation: AtomicU64,
87 target: AtomicU64,
88 terminal: AtomicU64,
90 cancelled: AtomicBool,
91 worker_alive: AtomicBool,
92 underruns: AtomicU64,
93 info: Info,
94}
95impl Ring {
96 fn new(info: Info, capacity: usize) -> Self {
97 Self {
98 samples: (0..capacity * info.channels)
99 .map(|_| AtomicU32::new(0))
100 .collect(),
101 generations: (0..capacity).map(|_| AtomicU64::new(0)).collect(),
102 head: AtomicUsize::new(0),
103 tail: AtomicUsize::new(0),
104 generation: AtomicU64::new(0),
105 target: AtomicU64::new(0),
106 terminal: AtomicU64::new(0),
107 cancelled: AtomicBool::new(false),
108 worker_alive: AtomicBool::new(true),
109 underruns: AtomicU64::new(0),
110 info,
111 }
112 }
113 fn capacity(&self) -> usize {
114 self.generations.len()
115 }
116 fn next(&self, at: usize) -> usize {
117 (at + 1) % (2 * self.capacity())
118 }
119 fn len(&self) -> usize {
120 let head = self.head.load(Ordering::Acquire);
122 let tail = self.tail.load(Ordering::Acquire);
123 (tail + 2 * self.capacity() - head) % (2 * self.capacity())
124 }
125 fn push(&self, frame: &[f32], generation: u64) -> bool {
126 let tail = self.tail.load(Ordering::Relaxed);
127 let head = self.head.load(Ordering::Acquire);
128 if (tail + 2 * self.capacity() - head) % (2 * self.capacity()) >= self.capacity() {
129 return false;
130 }
131 let at = tail % self.capacity();
132 for (c, &sample) in frame.iter().enumerate() {
133 self.samples[at * self.info.channels + c].store(sample.to_bits(), Ordering::Relaxed);
134 }
135 self.generations[at].store(generation, Ordering::Relaxed);
136 self.tail.store(self.next(tail), Ordering::Release);
137 true
138 }
139 fn finish(&self, generation: u64, error: Option<ErrorCode>) {
140 self.terminal.store(
141 ((generation + 1) << 8) | error.map_or(1, |e| error_number(e) as u64 + 1),
142 Ordering::Release,
143 );
144 }
145}
146
147pub struct FileStream {
162 ring: Arc<Ring>,
163 position: u64,
164 generation: u64,
165}
166pub struct FileStreamHandle {
169 ring: Arc<Ring>,
170 worker: Option<JoinHandle<()>>,
171}
172impl FileStream {
173 pub fn open(path: impl AsRef<Path>) -> Result<(Self, FileStreamHandle)> {
174 Self::with_options(path, &FileStreamOptions::default())
175 }
176 pub fn with_options(
177 path: impl AsRef<Path>,
178 options: &FileStreamOptions,
179 ) -> Result<(Self, FileStreamHandle)> {
180 options.validate()?;
181 let path = path.as_ref().to_owned();
182 let options = options.clone();
183 let (tx, rx) = std::sync::mpsc::sync_channel(1);
184 let worker = thread::Builder::new()
185 .name("audiobox-file-reader".into())
186 .spawn(move || {
187 let result = FileDecoder::open(&path, &options);
188 let mut decoder = match result {
189 Ok(d) => d,
190 Err(e) => {
191 let _ = tx.send(Err(e));
192 return;
193 }
194 };
195 let info = Info {
196 channels: decoder.channels,
197 rate: decoder.rate,
198 frames: decoder.frames,
199 format: decoder.format,
200 };
201 let pcm_bytes = options
202 .buffer_frames
203 .checked_mul(info.channels * 4 + 8)
204 .and_then(|n| n.checked_add(options.decoder.chunk_frames * info.channels * 4));
205 if pcm_bytes.is_none_or(|n| n > options.max_pcm_bytes) {
206 let _ = tx.send(Err(Error::limit("PCM ring/scratch exceeds budget")));
207 return;
208 }
209 let ring = Arc::new(Ring::new(info, options.buffer_frames));
210 let _alive = Alive(ring.clone());
211 let mut scratch = vec![0.0; options.decoder.chunk_frames * info.channels];
212 let initial = fill_initial(&mut decoder, &ring, &mut scratch);
213 if let Err(e) = initial {
214 let _ = tx.send(Err(e));
215 return;
216 }
217 if tx.send(Ok(ring.clone())).is_err() {
218 return;
219 }
220 run(&mut decoder, &ring, &mut scratch);
221 })?;
222 match rx.recv() {
223 Ok(Ok(ring)) => Ok((
224 Self {
225 ring: ring.clone(),
226 position: 0,
227 generation: 0,
228 },
229 FileStreamHandle {
230 ring,
231 worker: Some(worker),
232 },
233 )),
234 result => {
235 let _ = worker.join();
236 Err(match result {
237 Ok(Err(e)) => e,
238 _ => Error::new(ErrorCode::Io, "Reader exited during opening"),
239 })
240 }
241 }
242 }
243 pub fn sample_rate(&self) -> u32 {
244 self.ring.info.rate
245 }
246 pub fn channels(&self) -> usize {
247 self.ring.info.channels
248 }
249 pub fn frames(&self) -> Option<u64> {
250 self.ring.info.frames
251 }
252 pub fn format(&self) -> AudioFormat {
253 self.ring.info.format
254 }
255 pub fn position(&self) -> u64 {
257 self.position
258 }
259 pub fn read(&mut self, out: &mut [f32]) -> Result<usize> {
264 let ch = self.channels();
265 let wanted = out.len() / ch;
266 if wanted == 0 {
267 return Ok(0);
268 }
269 let ring = &self.ring;
270 let mut written = 0;
271 for _ in 0..ring.capacity() {
273 if written == wanted {
274 break;
275 }
276 let head = ring.head.load(Ordering::Relaxed);
277 let tail = ring.tail.load(Ordering::Acquire);
278 if head == tail {
279 break;
280 }
281 let at = head % ring.capacity();
282 if ring.generations[at].load(Ordering::Relaxed) == self.generation {
283 for c in 0..ch {
284 out[written * ch + c] =
285 f32::from_bits(ring.samples[at * ch + c].load(Ordering::Relaxed));
286 }
287 written += 1;
288 }
289 ring.head.store(ring.next(head), Ordering::Release);
290 }
291 self.position += written as u64;
292 if written > 0 {
293 return Ok(written);
294 }
295 if ring.cancelled.load(Ordering::Acquire) {
296 return Err(empty_error(ErrorCode::Io));
297 }
298 let terminal = ring.terminal.load(Ordering::Acquire);
299 let empty = ring.head.load(Ordering::Relaxed) == ring.tail.load(Ordering::Acquire);
301 if empty && terminal >> 8 == self.generation + 1 {
302 if terminal & 255 == 1 {
303 return Ok(0);
304 }
305 if terminal & 255 > 1 {
306 return Err(empty_error(number_error((terminal & 255) as u8 - 1)));
307 }
308 }
309 if !ring.worker_alive.load(Ordering::Acquire) {
310 return Err(empty_error(ErrorCode::Io));
311 }
312 ring.underruns.fetch_add(1, Ordering::Relaxed);
313 Err(empty_error(ErrorCode::Underrun))
314 }
315 pub fn seek(&mut self, frame: u64) -> Result<()> {
318 if self.generation >= (u64::MAX >> 8) - 1 {
319 return Err(empty_error(ErrorCode::LimitExceeded));
320 }
321 let frame = self.frames().map_or(frame, |n| frame.min(n));
322 self.position = frame;
323 self.generation += 1;
324 self.ring.target.store(frame, Ordering::Relaxed);
325 self.ring
326 .generation
327 .store(self.generation, Ordering::Release);
328 Ok(())
329 }
330 pub fn rewind(&mut self) -> Result<()> {
331 self.seek(0)
332 }
333}
334impl Drop for FileStream {
335 fn drop(&mut self) {
336 self.ring.cancelled.store(true, Ordering::Release);
337 }
338}
339impl FileStreamHandle {
340 pub fn status(&self) -> FileStreamStatus {
341 FileStreamStatus {
342 generation: self.ring.generation.load(Ordering::Acquire),
343 buffered_frames: self.ring.len().min(self.ring.capacity()),
344 underruns: self.ring.underruns.load(Ordering::Relaxed),
345 cancelled: self.ring.cancelled.load(Ordering::Acquire),
346 }
347 }
348 pub fn cancel(&self) {
350 self.ring.cancelled.store(true, Ordering::Release);
351 }
352}
353impl Drop for FileStreamHandle {
354 fn drop(&mut self) {
355 self.cancel();
356 if let Some(worker) = self.worker.take() {
357 let _ = worker.join();
358 }
359 }
360}
361struct Alive(Arc<Ring>);
362impl Drop for Alive {
363 fn drop(&mut self) {
364 self.0.worker_alive.store(false, Ordering::Release);
365 }
366}
367fn empty_error(code: ErrorCode) -> Error {
368 Error {
369 code,
370 message: String::new(),
371 }
372}
373fn error_number(code: ErrorCode) -> u8 {
374 match code {
375 ErrorCode::UnsupportedFormat => 1,
376 ErrorCode::DecodeError => 2,
377 ErrorCode::EncodeError => 3,
378 ErrorCode::InvalidArgument => 4,
379 ErrorCode::LimitExceeded => 5,
380 ErrorCode::Io => 6,
381 ErrorCode::Syntax => 7,
382 ErrorCode::Underrun => 8,
383 }
384}
385fn number_error(n: u8) -> ErrorCode {
386 match n {
387 1 => ErrorCode::UnsupportedFormat,
388 2 => ErrorCode::DecodeError,
389 3 => ErrorCode::EncodeError,
390 4 => ErrorCode::InvalidArgument,
391 5 => ErrorCode::LimitExceeded,
392 7 => ErrorCode::Syntax,
393 8 => ErrorCode::Underrun,
394 _ => ErrorCode::Io,
395 }
396}
397fn fill_initial(decoder: &mut FileDecoder, ring: &Ring, scratch: &mut [f32]) -> Result<()> {
398 while ring.len() < ring.capacity() {
399 let want = (ring.capacity() - ring.len()).min(scratch.len() / decoder.channels);
400 let n = decoder.read(&mut scratch[..want * decoder.channels])?;
401 if n == 0 {
402 ring.finish(0, None);
403 break;
404 }
405 for frame in scratch[..n * decoder.channels].chunks_exact(decoder.channels) {
406 assert!(ring.push(frame, 0));
407 }
408 }
409 Ok(())
410}
411fn run(decoder: &mut FileDecoder, ring: &Ring, scratch: &mut [f32]) {
412 let (mut generation, mut offset, mut ready) = (0, 0, 0);
413 loop {
414 if ring.cancelled.load(Ordering::Acquire) {
415 return;
416 }
417 let requested = ring.generation.load(Ordering::Acquire);
418 if requested != generation {
419 generation = requested;
420 offset = 0;
421 ready = 0;
422 ring.terminal.store(0, Ordering::Release);
423 let target = ring.target.load(Ordering::Relaxed);
424 let result = decoder.seek(target, || {
425 ring.cancelled.load(Ordering::Acquire)
426 || ring.generation.load(Ordering::Acquire) != generation
427 });
428 if let Err(e) = result {
429 ring.finish(generation, Some(e.code));
430 }
431 continue;
432 }
433 if offset < ready {
434 let ch = decoder.channels;
435 if ring.push(&scratch[offset * ch..(offset + 1) * ch], generation) {
436 offset += 1;
437 continue;
438 }
439 } else if ring.terminal.load(Ordering::Acquire) == 0 {
440 match decoder.read(scratch) {
441 Ok(0) => ring.finish(generation, None),
442 Ok(n) => {
443 offset = 0;
444 ready = n;
445 continue;
446 }
447 Err(e) => ring.finish(generation, Some(e.code)),
448 }
449 }
450 thread::sleep(Duration::from_millis(1));
451 }
452}
453
454#[cfg(feature = "mixer")]
455impl crate::mixer::Stream for FileStream {
456 fn sample_rate(&self) -> u32 {
457 self.sample_rate()
458 }
459 fn channels(&self) -> usize {
460 self.channels()
461 }
462 fn frames(&self) -> Option<u64> {
463 self.frames()
464 }
465 fn read(&mut self, out: &mut [f32]) -> Result<usize> {
466 self.read(out)
467 }
468 fn seek(&mut self, frame: u64) -> Result<()> {
469 self.seek(frame)
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 fn source(capacity: usize) -> FileStream {
477 FileStream {
478 ring: Arc::new(Ring::new(
479 Info {
480 channels: 1,
481 rate: 8000,
482 frames: None,
483 format: AudioFormat::Wav,
484 },
485 capacity,
486 )),
487 position: 0,
488 generation: 0,
489 }
490 }
491 #[test]
492 fn underrun_does_not_touch_output_or_turn_into_eof() {
493 let mut stream = source(3);
494 let mut out = [7.0; 4];
495 let e = stream.read(&mut out).unwrap_err();
496 assert_eq!(e.code, ErrorCode::Underrun);
497 assert!(e.message.is_empty());
498 assert_eq!(out, [7.0; 4]);
499 assert_eq!(stream.position(), 0);
500 assert!(stream.ring.push(&[0.5], 0));
501 stream.ring.finish(0, None);
502 assert_eq!(stream.read(&mut out).unwrap(), 1);
503 assert_eq!(out, [0.5, 7.0, 7.0, 7.0]);
504 assert_eq!(stream.read(&mut out).unwrap(), 0);
505 }
506 #[test]
507 fn stale_audio_and_terminal_are_ignored_after_seek() {
508 let mut stream = source(3);
509 for _ in 0..3 {
510 assert!(stream.ring.push(&[-1.0], 0));
511 }
512 stream.ring.finish(0, Some(ErrorCode::DecodeError));
513 stream.seek(100).unwrap();
514 assert_eq!(
515 stream.read(&mut [0.0; 10]).unwrap_err().code,
516 ErrorCode::Underrun
517 );
518 assert!(stream.ring.push(&[0.75], 1));
519 stream.ring.finish(1, None);
520 let mut out = [0.0; 2];
521 assert_eq!(stream.read(&mut out).unwrap(), 1);
522 assert_eq!(out[0], 0.75);
523 assert_eq!(stream.position(), 101);
524 assert_eq!(stream.read(&mut out).unwrap(), 0);
525 }
526 #[test]
527 fn worker_disconnection_and_terminal_errors_are_stable() {
528 let mut stream = source(1);
529 stream.ring.finish(0, Some(ErrorCode::DecodeError));
530 for _ in 0..3 {
531 assert_eq!(
532 stream.read(&mut [0.0]).unwrap_err().code,
533 ErrorCode::DecodeError
534 );
535 }
536 stream.seek(0).unwrap();
537 stream.ring.worker_alive.store(false, Ordering::Release);
538 assert_eq!(stream.read(&mut [0.0]).unwrap_err().code, ErrorCode::Io);
539 }
540 #[test]
541 fn odd_ring_wraps_and_eof_never_overtakes_the_last_frame() {
542 let mut stream = source(7);
543 let ring = stream.ring.clone();
544 let worker = thread::spawn(move || {
545 for i in 0..50000 {
546 while !ring.push(&[i as f32], 0) {
547 thread::yield_now();
548 }
549 }
550 ring.finish(0, None);
551 });
552 let mut expected = 0;
553 let mut buffer = [0.0; 11];
554 loop {
555 match stream.read(&mut buffer) {
556 Ok(0) => break,
557 Ok(n) => {
558 for &v in &buffer[..n] {
559 assert_eq!(v, expected as f32);
560 expected += 1;
561 }
562 }
563 Err(e) if e.code == ErrorCode::Underrun => thread::yield_now(),
564 Err(e) => panic!("{e:?}"),
565 }
566 }
567 worker.join().unwrap();
568 assert_eq!(expected, 50000);
569 }
570}