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