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, 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 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 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 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 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 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 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(); 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 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
470pub 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 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 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 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 pub fn set_playback_speed(&self, speed: f64) {
712 self.player_state.set_playback_speed(speed);
713 }
714 pub fn play_song_next(&self, song: &Song, start_time: Option<Duration>) -> Result<()> {
716 self.player_state.play_song(song, start_time)
717 }
718 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 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 pub fn force_remove_next_song(&self) -> Result<()> {
738 self.player_state.force_remove_next_song();
739 Ok(())
740 }
741 pub fn stop(&self) {
745 self.player_state.stop();
746 }
747 pub fn skip(&self) {
751 self.player_state.skip();
752 }
753 pub fn get_playback_position(&self) -> Option<(Duration, Duration)> {
757 self.player_state.get_position()
758 }
759 pub fn seek(&self, time: Duration) -> bool {
766 self.player_state.seek(time)
767 }
768 pub fn set_playing(&self, playing: bool) {
772 self.player_state.set_playing(playing);
773 }
774 pub fn is_playing(&self) -> bool {
778 *self.player_state.playing.read().unwrap()
779 }
780 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 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 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 pub fn reset_errors(&self) -> u64 {
822 while self.error_receiver.lock().unwrap().try_recv().is_ok() {}
824 self.error_count.swap(0, Ordering::Relaxed)
825 }
826}
827
828#[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 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 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 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 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 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}