Browse and listen to thousands of radio stations across the globe right from your terminal ๐ ๐ป ๐ตโจ
radio
rust
tokio
web-radio
command-line-tool
tui
1use std::time::Duration;
2use std::{io::Read, sync::mpsc::Sender};
3
4use minimp3::{Decoder, Frame};
5use rodio::Source;
6
7pub struct Mp3Decoder<R>
8where
9 R: Read,
10{
11 decoder: Decoder<R>,
12 current_frame: Frame,
13 current_frame_offset: usize,
14 tx: Option<Sender<Frame>>,
15}
16
17impl<R> Mp3Decoder<R>
18where
19 R: Read,
20{
21 pub fn new(mut data: R, tx: Option<Sender<Frame>>) -> Result<Self, R> {
22 if !is_mp3(data.by_ref()) {
23 return Err(data);
24 }
25 let mut decoder = Decoder::new(data);
26 let current_frame = decoder.next_frame().unwrap();
27
28 Ok(Mp3Decoder {
29 decoder,
30 current_frame,
31 current_frame_offset: 0,
32 tx,
33 })
34 }
35}
36
37impl<R> Source for Mp3Decoder<R>
38where
39 R: Read,
40{
41 #[inline]
42 fn current_frame_len(&self) -> Option<usize> {
43 Some(self.current_frame.data.len())
44 }
45
46 #[inline]
47 fn channels(&self) -> u16 {
48 self.current_frame.channels as _
49 }
50
51 #[inline]
52 fn sample_rate(&self) -> u32 {
53 self.current_frame.sample_rate as _
54 }
55
56 #[inline]
57 fn total_duration(&self) -> Option<Duration> {
58 None
59 }
60}
61
62impl<R> Iterator for Mp3Decoder<R>
63where
64 R: Read,
65{
66 type Item = i16;
67
68 #[inline]
69 fn next(&mut self) -> Option<i16> {
70 if self.current_frame_offset == self.current_frame.data.len() {
71 match self.decoder.next_frame() {
72 Ok(frame) => {
73 if let Some(tx) = &self.tx {
74 if tx.send(frame.clone()).is_err() {
75 return None;
76 }
77 }
78 self.current_frame = frame
79 }
80 _ => return None,
81 }
82 self.current_frame_offset = 0;
83 }
84
85 let v = self.current_frame.data[self.current_frame_offset];
86 self.current_frame_offset += 1;
87
88 Some(v)
89 }
90}
91
92/// Returns true if the stream contains mp3 data, then resets it to where it was.
93fn is_mp3<R>(mut data: R) -> bool
94where
95 R: Read,
96{
97 let mut decoder = Decoder::new(data.by_ref());
98 let ok = decoder.next_frame().is_ok();
99 ok
100}