1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use std::io::Cursor;

use symphonia::core::io::{MediaSource, MediaSourceStream};

use crate::sound::{
	static_sound::StaticSoundSettings, symphonia::load_frames_from_buffer_ref, FromFileError,
};

use super::StaticSoundData;

impl StaticSoundData {
	/// Loads an audio file into a [`StaticSoundData`].
	#[cfg(not(target_arch = "wasm32"))]
	#[cfg_attr(docsrs, doc(cfg(all(feature = "symphonia", not(wasm32)))))]
	pub fn from_file(
		path: impl AsRef<std::path::Path>,
		settings: StaticSoundSettings,
	) -> Result<Self, FromFileError> {
		Self::from_media_source(std::fs::File::open(path)?, settings)
	}

	/// Loads a cursor wrapping audio file data into a [`StaticSoundData`].
	#[cfg_attr(docsrs, doc(cfg(feature = "symphonia")))]
	pub fn from_cursor<T: AsRef<[u8]> + Send + Sync + 'static>(
		cursor: Cursor<T>,
		settings: StaticSoundSettings,
	) -> Result<StaticSoundData, FromFileError> {
		Self::from_media_source(cursor, settings)
	}

	/// Loads an audio file from a type that implements Symphonia's [`MediaSource`]
	/// trait.
	#[cfg_attr(docsrs, doc(cfg(feature = "symphonia")))]
	pub fn from_media_source(
		media_source: impl MediaSource + 'static,
		settings: StaticSoundSettings,
	) -> Result<Self, FromFileError> {
		Self::from_boxed_media_source(Box::new(media_source), settings)
	}

	fn from_boxed_media_source(
		media_source: Box<dyn MediaSource>,
		settings: StaticSoundSettings,
	) -> Result<Self, FromFileError> {
		let codecs = symphonia::default::get_codecs();
		let probe = symphonia::default::get_probe();
		let mss = MediaSourceStream::new(media_source, Default::default());
		let mut format_reader = probe
			.format(
				&Default::default(),
				mss,
				&Default::default(),
				&Default::default(),
			)?
			.format;
		let codec_params = &format_reader
			.default_track()
			.ok_or(FromFileError::NoDefaultTrack)?
			.codec_params;
		let sample_rate = codec_params
			.sample_rate
			.ok_or(FromFileError::UnknownSampleRate)?;
		let mut decoder = codecs.make(codec_params, &Default::default())?;
		let mut frames = vec![];
		loop {
			match format_reader.next_packet() {
				Ok(packet) => {
					let buffer = decoder.decode(&packet)?;
					frames.append(&mut load_frames_from_buffer_ref(&buffer)?);
				}
				Err(error) => match error {
					symphonia::core::errors::Error::IoError(error) => {
						if error.kind() == std::io::ErrorKind::UnexpectedEof {
							break;
						}
						return Err(symphonia::core::errors::Error::IoError(error).into());
					}
					error => return Err(error.into()),
				},
			}
		}
		Ok(Self {
			sample_rate,
			frames: frames.into(),
			settings,
		})
	}
}