playback_rs/
lib.rs

1#![warn(missing_docs)]
2#![doc(issue_tracker_base_url = "https://gitlab.101100.ca/veda/playback-rs/-/issues")]
3#![doc = include_str!("../docs.md")]
4#![feature(c_variadic)]
5
6use std::collections::VecDeque;
7use std::num::Wrapping;
8use std::sync::atomic::{AtomicU64, Ordering};
9use std::sync::mpsc::{self, TryRecvError};
10use std::sync::{Arc, Mutex, RwLock};
11use std::thread;
12use std::time::Duration;
13
14use color_eyre::eyre::{ensure, Report, Result};
15use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
16use cpal::{
17	Device, FrameCount, FromSample, HostId, OutputCallbackInfo, Sample, SampleFormat, SizedSample,
18	Stream, StreamConfig, StreamError, SupportedBufferSize, SupportedStreamConfigRange,
19};
20use log::{debug, error, info, warn};
21use rubato::{
22	Resampler, SincFixedOut, SincInterpolationParameters, SincInterpolationType, WindowFunction,
23};
24use symphonia::core::audio::SampleBuffer;
25use symphonia::core::codecs::DecoderOptions;
26use symphonia::core::errors::Error as SymphoniaError;
27use symphonia::core::formats::FormatOptions;
28use symphonia::core::io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions};
29use symphonia::core::meta::MetadataOptions;
30use symphonia::default;
31
32pub use symphonia::core::probe::Hint;
33
34#[derive(Debug, Clone, Copy, PartialEq)]
35struct SampleRequest {
36	frame: Option<(Duration, Wrapping<u8>)>,
37	speed: f64,
38}
39
40#[derive(Debug, Clone, PartialEq)]
41struct SampleResult {
42	samples: Vec<f32>,
43	end_pos: Duration,
44	skip_count: Wrapping<u8>,
45	done: bool,
46}
47
48#[derive(Debug)]
49struct DecodingSong {
50	song_length: Duration,
51	channel_count: usize,
52
53	requests_channel: mpsc::SyncSender<SampleRequest>,
54	samples_channel: Mutex<mpsc::Receiver<SampleResult>>,
55	frames_per_resample: usize,
56
57	buffer: VecDeque<f32>,
58	pending_requests: usize,
59	done: bool,
60	had_output: bool,
61	expected_pos: Duration,
62	skip_count: Wrapping<u8>,
63}
64
65const MAXIMUM_SPEED_ADJUSTMENT_FACTOR: f64 = 2.0;
66const MINIMUM_PLAYBACK_SPEED: f64 = 1.0 / MAXIMUM_SPEED_ADJUSTMENT_FACTOR;
67const MAXIMUM_PLAYBACK_SPEED: f64 = 1.0 * MAXIMUM_SPEED_ADJUSTMENT_FACTOR;
68
69impl DecodingSong {
70	fn new(
71		song: &Song,
72		initial_pos: Duration,
73		player_sample_rate: usize,
74		player_channel_count: usize,
75		expected_buffer_size: usize,
76		initial_playback_speed: f64,
77	) -> Result<DecodingSong> {
78		let frames = song.samples.clone();
79		let song_channel_count = song.channel_count;
80		if player_channel_count != song_channel_count {
81			warn!("Playing song with {song_channel_count} channels while the player has {player_channel_count} channels");
82		}
83		let total_frames = frames[0].len();
84		let frames_per_resample = expected_buffer_size / player_channel_count;
85		let volume_adjustment = song.volume_adjustment;
86
87		let (rtx, rrx) = mpsc::sync_channel::<SampleRequest>(10);
88		let (stx, srx) = mpsc::channel();
89		let song_sample_rate = song.sample_rate as u64;
90		let song_length = Self::frame_to_duration(total_frames, song_sample_rate);
91		let resample_ratio = player_sample_rate as f64 / song.sample_rate as f64;
92		let (etx, erx) = mpsc::channel();
93		thread::spawn(move || {
94			let sinc_len = 128;
95			let f_cutoff = 0.925_914_65;
96			let params = SincInterpolationParameters {
97				sinc_len,
98				f_cutoff,
99				interpolation: SincInterpolationType::Linear,
100				oversampling_factor: 2048,
101				window: WindowFunction::Blackman2,
102			};
103			let mut resampler = match SincFixedOut::<f32>::new(
104				resample_ratio,
105				MAXIMUM_SPEED_ADJUSTMENT_FACTOR,
106				params,
107				frames_per_resample, // SincFixedOut theoretically always gives us this much each time we process
108				player_channel_count,
109			) {
110				Ok(resampler) => {
111					etx.send(Ok(())).unwrap();
112					resampler
113				}
114				Err(e) => {
115					etx.send(Err(e)).unwrap();
116					return;
117				}
118			};
119			let mut input_buffer = resampler.input_buffer_allocate(true);
120			let mut output_buffer = resampler.output_buffer_allocate(true);
121
122			let mut current_frame = 0;
123			let mut skip_count = Wrapping(0);
124			let mut last_request_speed = 1.0;
125			loop {
126				let request = match rrx.recv() {
127					Ok(request) => request,
128					Err(_) => {
129						debug!("Ending resampling thread.");
130						break;
131					}
132				};
133
134				// adjust position based on seek
135				if let Some((new_pos, new_skip_count)) = request.frame {
136					let new_frame = (song_sample_rate * new_pos.as_secs()
137						+ song_sample_rate * new_pos.subsec_nanos() as u64 / 1_000_000_000)
138						as usize;
139					current_frame = new_frame.min(total_frames);
140					skip_count = new_skip_count;
141				}
142
143				// adjust the speed if it has changed
144				if request.speed != last_request_speed {
145					resampler
146						.set_resample_ratio_relative(1.0 / request.speed, false)
147						.unwrap();
148					last_request_speed = request.speed;
149				}
150
151				// determine which samples to pass in to the converter
152				let frames_wanted_by_resampler = resampler.input_frames_next();
153				let last_frame = (current_frame + frames_wanted_by_resampler).min(total_frames);
154				let frames_we_have = last_frame - current_frame;
155				for i in 0..player_channel_count {
156					input_buffer[i].clear();
157					for j in 0..frames_wanted_by_resampler {
158						if current_frame + j < total_frames {
159							input_buffer[i].push(frames[i % song_channel_count][current_frame + j]);
160						} else {
161							input_buffer[i].push(0.0);
162						}
163					}
164				}
165				current_frame = last_frame;
166				let end_pos = Self::frame_to_duration(current_frame, song_sample_rate);
167
168				// resample the frames and convert into interleaved samples
169				let processed_samples =
170					match resampler.process_into_buffer(&input_buffer, &mut output_buffer, None) {
171						Ok((_, frame_count)) => {
172							let mut samples = vec![0.0; player_channel_count * frame_count];
173							for chan in 0..player_channel_count {
174								if chan < 2 || chan < output_buffer.len() {
175									for sample in 0..frame_count {
176										samples[sample * player_channel_count + chan] =
177											output_buffer[chan % output_buffer.len()][sample]
178												* volume_adjustment
179									}
180								};
181							}
182							samples
183						}
184						Err(e) => {
185							error!("Error converting sample rate: {e}");
186							vec![0.0; expected_buffer_size]
187						}
188					};
189
190				// send the data out over the channel
191				// Dropping the other end of the channel will cause this to error, which will stop decoding.
192				if stx
193					.send(SampleResult {
194						samples: processed_samples,
195						skip_count,
196						end_pos,
197						done: frames_we_have < frames_wanted_by_resampler,
198					})
199					.is_err()
200				{
201					debug!("Ending resampling thread.");
202					break;
203				}
204			}
205		});
206		erx.recv()??;
207		let skip_count = Wrapping(0);
208		rtx.send(SampleRequest {
209			speed: initial_playback_speed,
210			frame: Some((initial_pos, skip_count)),
211		})?;
212		Ok(DecodingSong {
213			song_length,
214			channel_count: player_channel_count,
215			requests_channel: rtx,
216			samples_channel: Mutex::new(srx),
217			frames_per_resample,
218			buffer: VecDeque::new(),
219			pending_requests: 1,
220			done: false,
221			had_output: false,
222			expected_pos: initial_pos,
223			skip_count,
224		})
225	}
226	fn read_samples(
227		&mut self,
228		pos: Duration,
229		count: usize,
230		playback_speed: f64,
231	) -> (Vec<f32>, Duration, bool) {
232		// if they want another position, we're seeking, so reset the buffer
233		if pos != self.expected_pos {
234			self.had_output = false;
235			self.done = false;
236			self.buffer.clear();
237			self.skip_count += 1;
238			self.requests_channel
239				.send(SampleRequest {
240					speed: playback_speed,
241					frame: Some((pos, self.skip_count)),
242				})
243				.unwrap(); // This shouldn't be able to fail unless the thread stops which shouldn't be able to happen.
244			self.pending_requests = 1;
245		}
246
247		while count
248			> self.buffer.len()
249				+ self.pending_requests * self.frames_per_resample * self.channel_count
250		{
251			if self
252				.requests_channel
253				.send(SampleRequest {
254					speed: playback_speed,
255					frame: None,
256				})
257				.is_err()
258			{
259				break;
260			}
261
262			self.pending_requests += 1;
263		}
264		let channel = self.samples_channel.lock().unwrap();
265		if !self.done {
266			// Fetch samples until there are none left to fetch and we have enough.
267			let mut sent_warning = !self.had_output;
268			loop {
269				let got = channel.try_recv();
270				match got {
271					Ok(SampleResult {
272						samples,
273						skip_count,
274						end_pos,
275						done,
276					}) => {
277						if self.skip_count == skip_count {
278							self.pending_requests -= 1;
279							self.buffer.append(&mut VecDeque::from(samples));
280							self.expected_pos = end_pos;
281							if done {
282								self.done = true;
283								break;
284							}
285							if self.buffer.len() >= count {
286								break;
287							}
288						}
289					}
290					Err(TryRecvError::Disconnected) => {
291						self.done = true;
292						break;
293					}
294					Err(TryRecvError::Empty) => {
295						if self.buffer.len() >= count {
296							break;
297						} else if !sent_warning {
298							warn!("Waiting on resampler, this could cause audio choppyness. If you are a developer and this happens repeatedly in release mode please file an issue on playback-rs.");
299							sent_warning = true;
300						}
301					}
302				}
303			}
304		}
305		let mut vec = Vec::new();
306		let mut done = false;
307		for _i in 0..count {
308			if let Some(sample) = self.buffer.pop_front() {
309				vec.push(sample);
310			} else {
311				done = true;
312				break;
313			}
314		}
315
316		(vec, self.expected_pos, done)
317	}
318	fn frame_to_duration(frame: usize, song_sample_rate: u64) -> Duration {
319		let sub_second_samples = frame as u64 % song_sample_rate;
320		Duration::new(
321			frame as u64 / song_sample_rate,
322			(1_000_000_000 * sub_second_samples / song_sample_rate) as u32,
323		)
324	}
325}
326
327type PlaybackState = (DecodingSong, Duration);
328
329#[derive(Clone)]
330struct PlayerState {
331	playback: Arc<RwLock<Option<PlaybackState>>>,
332	next_samples: Arc<RwLock<Option<PlaybackState>>>,
333	playing: Arc<RwLock<bool>>,
334	channel_count: usize,
335	sample_rate: usize,
336	buffer_size: u32,
337	playback_speed: Arc<RwLock<f64>>,
338}
339
340impl PlayerState {
341	fn new(channel_count: u32, sample_rate: u32, buffer_size: FrameCount) -> Result<PlayerState> {
342		Ok(PlayerState {
343			playback: Arc::new(RwLock::new(None)),
344			next_samples: Arc::new(RwLock::new(None)),
345			playing: Arc::new(RwLock::new(true)),
346			channel_count: channel_count as usize,
347			sample_rate: sample_rate as usize,
348			buffer_size,
349			playback_speed: Arc::new(RwLock::new(1.0)),
350		})
351	}
352	fn write_samples<T>(&self, data: &mut [T], _info: &OutputCallbackInfo)
353	where
354		T: Sample + FromSample<f32>,
355	{
356		for sample in data.iter_mut() {
357			*sample = Sample::EQUILIBRIUM;
358		}
359		if *self.playing.read().unwrap() {
360			let playback_speed = *self.playback_speed.read().unwrap();
361			let mut playback = self.playback.write().unwrap();
362			if playback.is_none() {
363				if let Some((new_samples, new_pos)) = self.next_samples.write().unwrap().take() {
364					*playback = Some((new_samples, new_pos));
365				}
366			}
367			let mut done = false;
368			if let Some((decoding_song, sample_pos)) = playback.as_mut() {
369				let mut neg_offset = 0;
370				let data_len = data.len();
371				let (mut samples, mut new_pos, mut is_final) =
372					decoding_song.read_samples(*sample_pos, data_len, playback_speed);
373				for (i, sample) in data.iter_mut().enumerate() {
374					if i >= samples.len() {
375						if let Some((next_samples, next_pos)) =
376							self.next_samples.write().unwrap().take()
377						{
378							*decoding_song = next_samples;
379							neg_offset = i;
380							*sample_pos = next_pos;
381							(samples, new_pos, is_final) = decoding_song.read_samples(
382								*sample_pos,
383								data_len - neg_offset,
384								playback_speed,
385							);
386						} else {
387							break;
388						}
389					}
390					*sample = T::from_sample(samples[i - neg_offset]);
391				}
392				*sample_pos = new_pos;
393				done = is_final;
394			}
395			if done {
396				*playback = None;
397			}
398		}
399	}
400	fn decode_song(&self, song: &Song, initial_pos: Duration) -> Result<DecodingSong> {
401		DecodingSong::new(
402			song,
403			initial_pos,
404			self.sample_rate,
405			self.channel_count,
406			self.buffer_size as usize,
407			*self.playback_speed.read().unwrap(),
408		)
409	}
410	fn set_playback_speed(&self, speed: f64) {
411		*self.playback_speed.write().unwrap() =
412			speed.clamp(MINIMUM_PLAYBACK_SPEED, MAXIMUM_PLAYBACK_SPEED);
413	}
414	fn stop(&self) {
415		*self.next_samples.write().unwrap() = None;
416		*self.playback.write().unwrap() = None;
417	}
418	fn skip(&self) {
419		*self.playback.write().unwrap() = None;
420	}
421	fn play_song(&self, song: &Song, time: Option<Duration>) -> Result<()> {
422		let initial_pos = time.unwrap_or_default();
423		let samples = self.decode_song(song, initial_pos)?;
424		*self.next_samples.write().unwrap() = Some((samples, initial_pos));
425		Ok(())
426	}
427	fn set_playing(&self, playing: bool) {
428		*self.playing.write().unwrap() = playing;
429	}
430	fn get_position(&self) -> Option<(Duration, Duration)> {
431		self.playback
432			.read()
433			.unwrap()
434			.as_ref()
435			.map(|(samples, pos)| (*pos, samples.song_length))
436	}
437	fn seek(&self, time: Duration) -> bool {
438		let (mut playback, mut next_song) = (
439			self.playback.write().unwrap(),
440			self.next_samples.write().unwrap(),
441		);
442		if let Some((_, pos)) = playback.as_mut() {
443			*pos = time;
444			true
445		} else if let Some((_, pos)) = next_song.as_mut() {
446			*pos = time;
447			true
448		} else {
449			false
450		}
451	}
452	fn force_remove_next_song(&self) {
453		let (mut playback, mut next_song) = (
454			self.playback.write().unwrap(),
455			self.next_samples.write().unwrap(),
456		);
457		if next_song.is_some() {
458			*next_song = None;
459		} else {
460			*playback = None;
461		}
462	}
463}
464
465/// Manages playback of [Song]s through [cpal] and sample conversion through [rubato].
466pub struct Player {
467	_stream: Box<dyn StreamTrait>,
468	error_receiver: mpsc::Receiver<Report>,
469	error_count: Arc<AtomicU64>,
470	player_state: PlayerState,
471}
472
473impl Player {
474	/// Creates a new [Player] to play [Song]s. If specified, the player will attempt to use one of
475	/// the specified sampling rates. If not specified or the list is empty, the preferred rates
476	/// are 48000 and 44100.
477	///
478	/// If none of the preferred sampling rates are available, the closest available rate to the
479	/// first preferred rate will be selected.
480	///
481	/// On Linux, this prefers `pipewire`, `jack`, and `pulseaudio` devices over `alsa`.
482	pub fn new(preferred_sampling_rates: Option<Vec<u32>>) -> Result<Player> {
483		let device = {
484			let mut selected_host = cpal::default_host();
485			for host in cpal::available_hosts() {
486				if host.name().to_lowercase().contains("jack") {
487					selected_host = cpal::host_from_id(host)?;
488				}
489			}
490			info!("Selected Host: {:?}", selected_host.id());
491			#[cfg(any(
492				target_os = "linux",
493				target_os = "dragonfly",
494				target_os = "freebsd",
495				target_os = "netbsd"
496			))]
497			{
498				if selected_host.id() == HostId::Alsa {
499					block_alsa_output();
500				}
501			}
502			let mut selected_device = selected_host
503				.default_output_device()
504				.ok_or_else(|| Report::msg("No output device found."))?;
505			for device in selected_host.output_devices()? {
506				if let Ok(name) = device.description().map(|s| s.name().to_lowercase()) {
507					if name.contains("pipewire") || name.contains("pulse") || name.contains("jack")
508					{
509						selected_device = device;
510					}
511				}
512			}
513			info!(
514				"Selected device: {}, {}",
515				selected_device
516					.id()
517					.map(|di| format!("host: '{}', unique ID: '{}'", di.0, di.1))
518					.unwrap_or_else(|_| "no ID".to_string()),
519				selected_device
520					.description()
521					.map(|dd| format!("description: '{}'", dd.name()))
522					.unwrap_or_else(|_| "no description".to_string()),
523			);
524			selected_device
525		};
526		let mut supported_configs = device.supported_output_configs()?.collect::<Vec<_>>();
527		let preferred_sampling_rates = preferred_sampling_rates
528			.filter(|given_rates| !given_rates.is_empty())
529			.unwrap_or(vec![48000, 44100]);
530		let preferred_sampling_rate = preferred_sampling_rates[0];
531		let rank_supported_config = |config: &SupportedStreamConfigRange| {
532			let chans = config.channels() as u32;
533			let channel_rank = match chans {
534				0 => 0,
535				1 => 1,
536				2 => 4,
537				4 => 3,
538				_ => 2,
539			};
540			let min_sample_rank = if config.min_sample_rate() <= preferred_sampling_rate {
541				3
542			} else {
543				0
544			};
545			let max_sample_rank = if config.max_sample_rate() >= preferred_sampling_rate {
546				3
547			} else {
548				0
549			};
550			let sample_format_rank = if config.sample_format() == SampleFormat::F32 {
551				4
552			} else {
553				0
554			};
555			channel_rank + min_sample_rank + max_sample_rank + sample_format_rank
556		};
557		supported_configs.sort_by_key(|c_2| std::cmp::Reverse(rank_supported_config(c_2)));
558
559		let supported_config = supported_configs
560			.into_iter()
561			.next()
562			.ok_or_else(|| Report::msg("No supported output config."))?;
563
564		let sample_rate_range =
565			supported_config.min_sample_rate()..supported_config.max_sample_rate();
566		let supported_config = if let Some(selected_rate) = preferred_sampling_rates
567			.into_iter()
568			.find(|rate| sample_rate_range.contains(rate))
569		{
570			supported_config.with_sample_rate(selected_rate)
571		} else if sample_rate_range.end <= preferred_sampling_rate {
572			supported_config.with_sample_rate(sample_rate_range.end)
573		} else {
574			supported_config.with_sample_rate(sample_rate_range.start)
575		};
576		let sample_format = supported_config.sample_format();
577		let sample_rate = supported_config.sample_rate();
578		let channel_count = supported_config.channels();
579		let buffer_size = match supported_config.buffer_size() {
580			SupportedBufferSize::Range { min, .. } => (*min).max(1024) * 2,
581			SupportedBufferSize::Unknown => 1024 * 2,
582		};
583		let config = supported_config.into();
584		let player_state = PlayerState::new(channel_count as u32, sample_rate, buffer_size)?;
585		info!(
586			"SR, CC, SF: {}, {}, {:?}",
587			sample_rate, channel_count, sample_format
588		);
589
590		let error_count = Arc::new(AtomicU64::new(0));
591		let (error_sender, error_receiver) = mpsc::channel();
592		fn build_stream<T>(
593			device: &Device,
594			config: &StreamConfig,
595			player_state: PlayerState,
596			error_sender: mpsc::Sender<Report>,
597			error_count: Arc<AtomicU64>,
598		) -> Result<Stream>
599		where
600			T: SizedSample + FromSample<f32>,
601		{
602			let err_fn = move |err: StreamError| {
603				// log and save the first 5 errors
604				if error_count.fetch_add(1, Ordering::Relaxed) < 5 {
605					error!("A playback error has occurred! {}", err);
606					let _ = error_sender.send(err.clone().into());
607				}
608			};
609			let stream = device.build_output_stream(
610				config,
611				move |data, info| player_state.write_samples::<T>(data, info),
612				err_fn,
613				None,
614			)?;
615			// Not all platforms (*cough cough* windows *cough*) automatically run the stream upon creation, so do that here.
616			stream.play()?;
617			Ok(stream)
618		}
619		let stream = {
620			let player_state = player_state.clone();
621			match sample_format {
622				SampleFormat::I8 => build_stream::<i8>(
623					&device,
624					&config,
625					player_state,
626					error_sender,
627					error_count.clone(),
628				)?,
629				SampleFormat::I16 => build_stream::<i16>(
630					&device,
631					&config,
632					player_state,
633					error_sender,
634					error_count.clone(),
635				)?,
636				SampleFormat::I32 => build_stream::<i32>(
637					&device,
638					&config,
639					player_state,
640					error_sender,
641					error_count.clone(),
642				)?,
643				SampleFormat::I64 => build_stream::<i64>(
644					&device,
645					&config,
646					player_state,
647					error_sender,
648					error_count.clone(),
649				)?,
650				SampleFormat::U8 => build_stream::<u8>(
651					&device,
652					&config,
653					player_state,
654					error_sender,
655					error_count.clone(),
656				)?,
657				SampleFormat::U16 => build_stream::<u16>(
658					&device,
659					&config,
660					player_state,
661					error_sender,
662					error_count.clone(),
663				)?,
664				SampleFormat::U32 => build_stream::<u32>(
665					&device,
666					&config,
667					player_state,
668					error_sender,
669					error_count.clone(),
670				)?,
671				SampleFormat::U64 => build_stream::<u64>(
672					&device,
673					&config,
674					player_state,
675					error_sender,
676					error_count.clone(),
677				)?,
678				SampleFormat::F32 => build_stream::<f32>(
679					&device,
680					&config,
681					player_state,
682					error_sender,
683					error_count.clone(),
684				)?,
685				SampleFormat::F64 => build_stream::<f64>(
686					&device,
687					&config,
688					player_state,
689					error_sender,
690					error_count.clone(),
691				)?,
692				sample_format => Err(Report::msg(format!(
693					"Unsupported sample format '{sample_format}'"
694				)))?,
695			}
696		};
697		Ok(Player {
698			_stream: Box::new(stream),
699			error_receiver,
700			error_count,
701			player_state,
702		})
703	}
704	/// Set the playback speed (This will also affect song pitch)
705	pub fn set_playback_speed(&self, speed: f64) {
706		self.player_state.set_playback_speed(speed);
707	}
708	/// Set the song that will play after the current song is over (or immediately if no song is currently playing), optionally start playing in the middle of the song.
709	pub fn play_song_next(&self, song: &Song, start_time: Option<Duration>) -> Result<()> {
710		self.player_state.play_song(song, start_time)
711	}
712	/// Start playing a song immediately, while discarding any song that might have been queued to play next. Optionally start playing in the middle of the song.
713	pub fn play_song_now(&self, song: &Song, start_time: Option<Duration>) -> Result<()> {
714		self.player_state.stop();
715		self.player_state.play_song(song, start_time)?;
716		Ok(())
717	}
718	/// Used to replace the next song, or the current song if there is no next song. Optionally start playing in the middle of the song.
719	///
720	/// This will remove the current song if no next song exists to avoid a race condition in case the current song ends after you have determined that the next song must be replaced but before you call this function.
721	/// See also [`force_remove_next_song`](Player::force_remove_next_song)
722	pub fn force_replace_next_song(&self, song: &Song, start_time: Option<Duration>) -> Result<()> {
723		self.player_state.force_remove_next_song();
724		self.player_state.play_song(song, start_time)?;
725		Ok(())
726	}
727	/// Used to remove the next song, or the current song if there is no next song.
728	///
729	/// This will remove the current song if no next song exists to avoid a race condition in case the current song ends after you have determined that the next song must be replaced but before you call this function.
730	/// See also [`force_replace_next_song`](Player::force_replace_next_song)
731	pub fn force_remove_next_song(&self) -> Result<()> {
732		self.player_state.force_remove_next_song();
733		Ok(())
734	}
735	/// Stop playing any songs and remove a next song if it has been queued.
736	///
737	/// Note that this does not pause playback (use [`set_playing`](Player::set_playing)), meaning new songs will play upon adding them.
738	pub fn stop(&self) {
739		self.player_state.stop();
740	}
741	/// Skip the currently playing song (i.e. stop playing it immediately.
742	///
743	/// This will immediately start playing the next song if it exists.
744	pub fn skip(&self) {
745		self.player_state.skip();
746	}
747	/// Return the current playback position, if there is currently a song playing (see [`has_current_song`](Player::has_current_song))
748	///
749	/// See also [`seek`](Player::seek)
750	pub fn get_playback_position(&self) -> Option<(Duration, Duration)> {
751		self.player_state.get_position()
752	}
753	/// Set the current playback position if there is a song playing or a song queued to be played next.
754	///
755	/// Returns whether the seek was successful (whether there was a song to seek).
756	/// Note that seeking past the end of the song will be successful and will cause playback to begin at the _beginning_ of the next song.
757	///
758	/// See also [`get_playback_position`](Player::get_playback_position)
759	pub fn seek(&self, time: Duration) -> bool {
760		self.player_state.seek(time)
761	}
762	/// Sets whether playback is enabled or not, without touching the song queue.
763	///
764	/// See also [`is_playing`](Player::is_playing)
765	pub fn set_playing(&self, playing: bool) {
766		self.player_state.set_playing(playing);
767	}
768	/// Returns whether playback is currently paused.
769	///
770	/// See also [`set_playing`](Player::set_playing)
771	pub fn is_playing(&self) -> bool {
772		*self.player_state.playing.read().unwrap()
773	}
774	/// Returns whether there is a song queued to play next after the current song has finished
775	///
776	/// If you want to check whether there is currently a song playing, use [`has_current_song`][Player::has_current_song] and [`is_playing`][Player::is_playing].
777	/// This should always be queried before calling [`play_song_next`](Player::play_song_next) if you do not intend on replacing the song currently in the queue.
778	pub fn has_next_song(&self) -> bool {
779		self.player_state
780			.next_samples
781			.read()
782			.expect("Next song mutex poisoned.")
783			.is_some()
784	}
785	/// Returns whether there is a song currently playing (or about to start playing next audio frame)
786	///
787	/// Note that this **does not** indicate whether the current song is actively being played or paused, for that functionality you can use [is_playing](Self::is_playing).
788	pub fn has_current_song(&self) -> bool {
789		self.player_state
790			.playback
791			.read()
792			.expect("Current song mutex poisoned.")
793			.is_some()
794			|| self
795				.player_state
796				.next_samples
797				.read()
798				.expect("Next song mutex poisoned.")
799				.is_some()
800	}
801	/// Returns a list of playback errors that have been collected and the total number of errors detected.
802	///
803	/// The library will only store 5 errors at a time and all others will be thrown away, so this function also returns a count of the errors up to this point.
804	pub fn get_errors(&self) -> (Vec<Report>, u64) {
805		let mut errors = Vec::new();
806		while let Ok(err) = self.error_receiver.try_recv() {
807			errors.push(err);
808		}
809		let errors_count = self.error_count.load(Ordering::Relaxed);
810		(errors, errors_count)
811	}
812	/// Resets the playback error count and returns the old value.
813	///
814	/// This also has the effect of clearing out any errors that have been saved so that the library will only ever collect up to 5 errors.
815	pub fn reset_errors(&self) -> u64 {
816		// clear out the received errors to avoid saving more than 5
817		while self.error_receiver.try_recv().is_ok() {}
818		self.error_count.swap(0, Ordering::Relaxed)
819	}
820}
821
822/// Represents a single song that has been decoded into memory, can be played in a <Player> struct.
823///
824/// The data in the song is stored in an <Arc> so cloning a song is a lightweight operation.
825#[derive(Debug, Clone)]
826pub struct Song {
827	samples: Arc<Vec<Vec<f32>>>,
828	sample_rate: u32,
829	channel_count: usize,
830	volume_adjustment: f32,
831}
832
833impl Song {
834	/// Creates a new song using a reader of some kind and a type hint (the Symphonia hint type has been reexported at the crate root for convenience), as well as an optional volume adjustment (used for e.g. replay gain).
835	pub fn new(
836		reader: Box<dyn MediaSource>,
837		hint: &Hint,
838		volume_adjustment: Option<f32>,
839	) -> Result<Song> {
840		let media_source_stream =
841			MediaSourceStream::new(reader, MediaSourceStreamOptions::default());
842		let mut probe_result = default::get_probe().format(
843			hint,
844			media_source_stream,
845			&FormatOptions {
846				enable_gapless: true,
847				..FormatOptions::default()
848			},
849			&MetadataOptions::default(),
850		)?;
851		let mut decoder = default::get_codecs().make(
852			&probe_result
853				.format
854				.default_track()
855				.ok_or_else(|| Report::msg("No default track in media file."))?
856				.codec_params,
857			&DecoderOptions::default(),
858		)?;
859		let mut song: Option<(Vec<Vec<f32>>, u32, usize)> = None;
860		let mut bad_packet = false;
861		loop {
862			match probe_result.format.next_packet() {
863				Ok(packet) => {
864					let decoded = match decoder.decode(&packet) {
865						Ok(decoded) => decoded,
866						Err(symphonia::core::errors::Error::DecodeError(err)) => {
867							// The example playback code doesn't treat decode errors as fatal errors,
868							// so just log this at most once per file.
869							if !bad_packet {
870								bad_packet = true;
871								warn!("Bad packet: {err:?}");
872							}
873							continue;
874						}
875						Err(err) => {
876							return Err(Report::new(err));
877						}
878					};
879					let spec = *decoded.spec();
880					let song_samples =
881						if let Some((samples, sample_rate, channel_count)) = &mut song {
882							ensure!(
883								spec.rate == *sample_rate,
884								"Sample rate of decoded does not match previous sample rate."
885							);
886							ensure!(
887								spec.channels.count() == *channel_count,
888								"Channel count of decoded does not match previous channel count."
889							);
890							samples
891						} else {
892							song = Some((
893								vec![Vec::new(); spec.channels.count()],
894								spec.rate,
895								spec.channels.count(),
896							));
897							&mut song.as_mut().unwrap().0
898						};
899					if decoded.frames() > 0 {
900						let mut samples = SampleBuffer::new(decoded.frames() as u64, spec);
901						samples.copy_interleaved_ref(decoded);
902						for frame in samples.samples().chunks(spec.channels.count()) {
903							for (chan, sample) in frame.iter().enumerate() {
904								song_samples[chan].push(*sample)
905							}
906						}
907					} else {
908						warn!("Empty packet encountered while loading song!");
909					}
910				}
911				Err(SymphoniaError::IoError(_)) => break,
912				Err(e) => return Err(e.into()),
913			}
914		}
915		song.map(|(samples, sample_rate, channel_count)| Song {
916			samples: Arc::new(samples),
917			sample_rate,
918			channel_count,
919			volume_adjustment: volume_adjustment.unwrap_or(1.0),
920		})
921		.ok_or_else(|| Report::msg("No song data decoded."))
922	}
923	/// Creates a [Song] by reading data from a file and using the file's extension as a format type hint. Takes an optional volume adjustment (used for e.g. replay gain)
924	pub fn from_file<P: AsRef<std::path::Path>>(
925		path: P,
926		volume_adjustment: Option<f32>,
927	) -> Result<Song> {
928		let mut hint = Hint::new();
929		if let Some(extension) = path.as_ref().extension().and_then(|s| s.to_str()) {
930			hint.with_extension(extension);
931		}
932		Self::new(
933			Box::new(std::fs::File::open(path)?),
934			&hint,
935			volume_adjustment,
936		)
937	}
938}
939
940#[cfg(any(
941	target_os = "linux",
942	target_os = "dragonfly",
943	target_os = "freebsd",
944	target_os = "netbsd"
945))]
946fn block_alsa_output() {
947	use std::os::raw::{c_char, c_int};
948
949	use alsa_sys::snd_lib_error_set_handler;
950	use log::trace;
951
952	unsafe extern "C" fn error_handler(
953		file: *const c_char,
954		line: c_int,
955		function: *const c_char,
956		err: c_int,
957		format: *const c_char,
958		mut format_args: ...
959	) {
960		use std::ffi::CStr;
961		let file = String::from_utf8_lossy(CStr::from_ptr(file).to_bytes());
962		let function = String::from_utf8_lossy(CStr::from_ptr(function).to_bytes());
963		let format = String::from_utf8_lossy(CStr::from_ptr(format).to_bytes());
964		// FIXME: This should really be better, but it works for alsa so
965		let mut last_m = 0;
966		let formatted: String = format
967			.match_indices("%s")
968			.flat_map(|(m, s)| {
969				let res = [
970					format[last_m..m].to_string(),
971					String::from_utf8_lossy(
972						CStr::from_ptr(format_args.arg::<*const c_char>()).to_bytes(),
973					)
974					.to_string(),
975				];
976				last_m = m + s.len();
977				res
978			})
979			.collect();
980		trace!("ALSA Error: {err}: {file} ({line}): {function}: {formatted}");
981	}
982
983	unsafe {
984		snd_lib_error_set_handler(Some(error_handler));
985	}
986}