Skip to main content

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