web_audio_api/
media_element.rs

1use std::error::Error;
2use std::path::PathBuf;
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5
6use creek::{ReadDiskStream, SeekMode, SymphoniaDecoder};
7use crossbeam_channel::{Receiver, Sender};
8
9use crate::{AtomicF64, AudioBuffer, RENDER_QUANTUM_SIZE};
10
11/// Real time safe audio stream
12pub(crate) struct RTSStream {
13    stream: ReadDiskStream<SymphoniaDecoder>,
14    number_of_channels: usize,
15    current_time: Arc<AtomicF64>,
16    receiver: Receiver<MediaElementAction>,
17    loop_: Arc<AtomicBool>,
18    paused: Arc<AtomicBool>,
19    playback_rate: Arc<AtomicF64>,
20}
21
22impl std::fmt::Debug for RTSStream {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.debug_struct("RTSStream")
25            .field("number_of_channels", &self.number_of_channels)
26            .finish_non_exhaustive()
27    }
28}
29
30/// Controller actions for a media element
31pub(crate) enum MediaElementAction {
32    /// Seek to the given timestamp
33    Seek(f64),
34    /// Enable/disable looping
35    SetLoop(bool),
36    /// Start or restart the stream
37    Play,
38    /// Pause the stream
39    Pause,
40    /// Update the playback rate
41    SetPlaybackRate(f64),
42}
43
44/// Shim of the `<audio>` element which allows you to efficiently play and seek audio from disk
45///
46/// The documentation for [`MediaElementAudioSourceNode`](crate::node::MediaElementAudioSourceNode)
47/// contains usage instructions.
48pub struct MediaElement {
49    stream: Option<RTSStream>,
50    current_time: Arc<AtomicF64>,
51    sender: Sender<MediaElementAction>,
52    loop_: Arc<AtomicBool>,
53    paused: Arc<AtomicBool>,
54    playback_rate: Arc<AtomicF64>,
55}
56
57impl std::fmt::Debug for MediaElement {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        f.debug_struct("MediaElement")
60            .field("stream", &self.stream)
61            .field("current_time", &self.current_time())
62            .field("loop", &self.loop_())
63            .field("paused", &self.paused())
64            .field("playback_rate", &self.playback_rate())
65            .finish_non_exhaustive()
66    }
67}
68
69impl MediaElement {
70    /// Create a new instance for a given file path
71    pub fn new<P: Into<PathBuf>>(file: P) -> Result<Self, Box<dyn Error>> {
72        // Open a read stream.
73        let mut read_disk_stream = ReadDiskStream::<SymphoniaDecoder>::new(
74            file,               // Path to file.
75            0,                  // The frame in the file to start reading from.
76            Default::default(), // Use default read stream options.
77        )?;
78        let number_of_channels = read_disk_stream.info().num_channels as usize;
79
80        // Cache the start of the file into cache with index `0`.
81        let _ = read_disk_stream.cache(0, 0);
82
83        // Tell the stream to seek to the beginning of file. This will also alert the stream to the existence
84        // of the cache with index `0`.
85        read_disk_stream.seek(0, SeekMode::default())?;
86
87        // Wait until the buffer is filled before sending it to the process thread.
88        read_disk_stream.block_until_ready()?;
89
90        // Setup control/render thread message bus
91        // Use a bounded channel for real-time safety. A maximum of 32 control messages (start,
92        // seek, ..) will be handled per render quantum. The control thread will block when the
93        // capacity is reached.
94        let (sender, receiver) = crossbeam_channel::bounded(32);
95        // Setup currentTime shared value
96        let current_time = Arc::new(AtomicF64::new(0.));
97
98        let loop_ = Arc::new(AtomicBool::new(false));
99        let paused = Arc::new(AtomicBool::new(true));
100        let playback_rate = Arc::new(AtomicF64::new(1.));
101
102        let rts_stream = RTSStream {
103            stream: read_disk_stream,
104            number_of_channels,
105            current_time: Arc::clone(&current_time),
106            receiver,
107            loop_: Arc::clone(&loop_),
108            paused: Arc::clone(&paused),
109            playback_rate: Arc::clone(&playback_rate),
110        };
111
112        Ok(Self {
113            stream: Some(rts_stream),
114            current_time,
115            sender,
116            loop_,
117            paused,
118            playback_rate,
119        })
120    }
121
122    pub(crate) fn take_stream(&mut self) -> Option<RTSStream> {
123        self.stream.take()
124    }
125
126    pub fn current_time(&self) -> f64 {
127        self.current_time.load(Ordering::SeqCst)
128    }
129
130    pub fn set_current_time(&self, value: f64) {
131        let _ = self.sender.send(MediaElementAction::Seek(value));
132    }
133
134    pub fn loop_(&self) -> bool {
135        self.loop_.load(Ordering::SeqCst)
136    }
137
138    pub fn set_loop(&self, value: bool) {
139        let _ = self.sender.send(MediaElementAction::SetLoop(value));
140    }
141
142    pub fn play(&self) {
143        let _ = self.sender.send(MediaElementAction::Play);
144    }
145
146    pub fn pause(&self) {
147        let _ = self.sender.send(MediaElementAction::Pause);
148    }
149
150    pub fn paused(&self) -> bool {
151        self.paused.load(Ordering::SeqCst)
152    }
153
154    pub fn playback_rate(&self) -> f64 {
155        self.playback_rate.load(Ordering::SeqCst)
156    }
157
158    pub fn set_playback_rate(&self, value: f64) {
159        let _ = self.sender.send(MediaElementAction::SetPlaybackRate(value));
160    }
161}
162
163impl Iterator for RTSStream {
164    type Item = Result<AudioBuffer, Box<dyn Error + Send + Sync>>;
165
166    fn next(&mut self) -> Option<Self::Item> {
167        let sample_rate = self.stream.info().sample_rate.unwrap() as f32;
168
169        if let Ok(msg) = self.receiver.try_recv() {
170            use MediaElementAction::*;
171            match msg {
172                Seek(value) => {
173                    self.current_time.store(value, Ordering::SeqCst);
174                    let frame = (value * sample_rate as f64) as usize;
175                    self.stream.seek(frame, SeekMode::default()).unwrap();
176                }
177                SetLoop(value) => {
178                    self.loop_.store(value, Ordering::SeqCst);
179                }
180                Play => self.paused.store(false, Ordering::SeqCst),
181                Pause => self.paused.store(true, Ordering::SeqCst),
182                SetPlaybackRate(value) => self.playback_rate.store(value, Ordering::SeqCst),
183            };
184        }
185
186        if self.paused.load(Ordering::SeqCst) {
187            let silence = AudioBuffer::from(
188                vec![vec![0.; RENDER_QUANTUM_SIZE]; self.number_of_channels],
189                sample_rate,
190            );
191            return Some(Ok(silence));
192        }
193
194        let playback_rate = self.playback_rate.load(Ordering::SeqCst).abs();
195        let _reverse = playback_rate < 0.; // TODO
196        let samples = (RENDER_QUANTUM_SIZE as f64 * playback_rate) as usize;
197
198        let next = match self.stream.read(samples) {
199            Ok(data) => {
200                let channels: Vec<_> = (0..data.num_channels())
201                    .map(|i| data.read_channel(i).to_vec())
202                    .collect();
203                let buf = AudioBuffer::from(channels, sample_rate * playback_rate as f32);
204
205                if self.loop_.load(Ordering::SeqCst) && data.reached_end_of_file() {
206                    self.stream.seek(0, SeekMode::default()).unwrap();
207                    self.current_time.store(0., Ordering::SeqCst);
208                } else {
209                    let current_time = self.current_time.load(Ordering::SeqCst);
210                    self.current_time.store(
211                        current_time + (RENDER_QUANTUM_SIZE as f64 / sample_rate as f64),
212                        Ordering::SeqCst,
213                    );
214                }
215
216                Ok(buf)
217            }
218            Err(e) => Err(Box::new(e) as _),
219        };
220
221        Some(next)
222    }
223}