servo_media_audio/
offline_sink.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, RefCell};
6use std::sync::mpsc::Sender;
7
8use servo_media_streams::MediaSocket;
9
10use crate::block::{Chunk, FRAMES_PER_BLOCK_USIZE};
11use crate::render_thread::{AudioRenderThreadMsg, SinkEosCallback};
12use crate::sink::{AudioSink, AudioSinkError};
13
14pub struct ProcessedAudio(Box<[f32]>);
15
16impl AsRef<[f32]> for ProcessedAudio {
17    fn as_ref(&self) -> &[f32] {
18        &self.0
19    }
20}
21
22pub struct OfflineAudioSink {
23    buffer: RefCell<Option<Vec<f32>>>,
24    channel_count: usize,
25    has_enough_data: Cell<bool>,
26    length: usize,
27    rendered_blocks: Cell<usize>,
28    eos_callback: RefCell<Option<SinkEosCallback>>,
29}
30
31impl OfflineAudioSink {
32    pub fn new(channel_count: usize, length: usize) -> Self {
33        Self {
34            buffer: RefCell::new(None),
35            channel_count,
36            has_enough_data: Cell::new(false),
37            length,
38            rendered_blocks: Cell::new(0),
39            eos_callback: RefCell::new(None),
40        }
41    }
42}
43
44impl AudioSink for OfflineAudioSink {
45    fn init(&self, _: f32, _: Sender<AudioRenderThreadMsg>) -> Result<(), AudioSinkError> {
46        Ok(())
47    }
48    fn init_stream(&self, _: u8, _: f32, _: Box<dyn MediaSocket>) -> Result<(), AudioSinkError> {
49        unreachable!("OfflineAudioSink should never be used for MediaStreamDestinationNode")
50    }
51    fn play(&self) -> Result<(), AudioSinkError> {
52        self.has_enough_data.set(false);
53        Ok(())
54    }
55
56    fn stop(&self) -> Result<(), AudioSinkError> {
57        self.has_enough_data.set(true);
58        Ok(())
59    }
60
61    fn has_enough_data(&self) -> bool {
62        self.has_enough_data.get() ||
63            (self.rendered_blocks.get() * FRAMES_PER_BLOCK_USIZE >= self.length)
64    }
65
66    fn push_data(&self, mut chunk: Chunk) -> Result<(), AudioSinkError> {
67        let offset = self.rendered_blocks.get() * FRAMES_PER_BLOCK_USIZE;
68        let (last, copy_len) = if self.length - offset <= FRAMES_PER_BLOCK_USIZE {
69            (true, self.length - offset)
70        } else {
71            (false, FRAMES_PER_BLOCK_USIZE)
72        };
73        let mut buffer = self.buffer.borrow_mut();
74        if buffer.is_none() {
75            *buffer = Some(vec![0.; self.channel_count * self.length]);
76        }
77        if chunk.is_empty() {
78            chunk.blocks.push(Default::default());
79        }
80        if chunk.blocks[0].is_empty() {
81            chunk.blocks[0].explicit_silence();
82        }
83        if let Some(ref mut buffer) = *buffer {
84            for channel_number in 0..self.channel_count {
85                let channel_offset = offset + (channel_number * self.length);
86                let channel_data = &mut buffer[channel_offset..channel_offset + copy_len];
87                channel_data
88                    .copy_from_slice(&chunk.blocks[0].data_chan(channel_number as u8)[0..copy_len]);
89            }
90        };
91        self.rendered_blocks.set(self.rendered_blocks.get() + 1);
92
93        if last {
94            if let Some(callback) = self.eos_callback.borrow_mut().take() {
95                let processed_audio = ProcessedAudio(buffer.take().unwrap().into_boxed_slice());
96                callback(Box::new(processed_audio));
97            }
98        }
99
100        Ok(())
101    }
102
103    fn set_eos_callback(
104        &self,
105        callback: Box<dyn Fn(Box<dyn AsRef<[f32]>>) + Send + Sync + 'static>,
106    ) {
107        *self.eos_callback.borrow_mut() = Some(callback);
108    }
109}