Skip to main content

servo_media_audio/delay_node/
delay_writer.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 log::error;
6use num_traits::Zero;
7
8use crate::audio_node::{AudioNodeEngine, AudioNodeType, BlockInfo, ChannelInfo};
9use crate::block::{Chunk, FRAMES_PER_BLOCK_USIZE};
10use crate::delay_node::{CachedUpmixedBlock, DelayBuffer};
11
12/// <https://webaudio.github.io/web-audio-api/#delaywriter>
13/// > ...an object that has the same interface as an AudioNode,
14/// > and that writes the input audio into the internal buffer of the DelayNode.
15/// > It has the same input connections as the DelayNode it was created from.
16#[derive(AudioNodeCommon)]
17pub(crate) struct DelayWriter {
18    channel_info: ChannelInfo,
19    // Ring buffer where we push to the front.
20    // Easier mental model since entries in the back are the oldest.
21    delay_line: DelayBuffer,
22    // Block that has been upmixed based on the channel count of the output.
23    upmixed_block: CachedUpmixedBlock,
24    // Maximum delay time in seconds. Passed from the DelayNode
25    max_delay_time: f64,
26}
27
28impl DelayWriter {
29    pub(super) fn new(
30        buffer: DelayBuffer,
31        upmixed_block: CachedUpmixedBlock,
32        channel_info: ChannelInfo,
33        max_delay_time: f64,
34    ) -> Self {
35        Self {
36            channel_info,
37            delay_line: buffer,
38            upmixed_block,
39            max_delay_time,
40        }
41    }
42
43    /// Updates the capacity of the delay line if it is currently zero.
44    fn update_delay_line_capacity(&self, capacity: usize) {
45        let mut delay_line = self.delay_line.write();
46        if delay_line.capacity().is_zero() {
47            delay_line.reserve(capacity);
48        }
49    }
50
51    /// Writes the input block to the delay line
52    fn write(&self, mut input: Chunk) {
53        {
54            // First remove the oldest values if we are at capacity (max_delay_time)
55            let Some(block) = input.blocks.pop() else {
56                error!(
57                    "Attempted to write a chunk with no data to the delay node internal buffer. This should not have occurred due to size check prior to write"
58                );
59                return;
60            };
61            let mut delay_line = self.delay_line.write();
62            let last_index = delay_line.capacity() - 1;
63            delay_line.truncate(last_index);
64            // Push the input block to the front of the delay line.
65            delay_line.push_front(block);
66        }
67        // Shifts the index of the existing upmixed block
68        if let Some(upmixed_block) = self.upmixed_block.write().as_mut() {
69            upmixed_block.increment_index();
70        }
71    }
72}
73
74impl AudioNodeEngine for DelayWriter {
75    fn node_type(&self) -> AudioNodeType {
76        AudioNodeType::DelayWriter
77    }
78
79    fn process(&mut self, inputs: Chunk, info: &BlockInfo) -> Chunk {
80        debug_assert!(inputs.len() == 1);
81
82        let max_delay_frame = self.max_delay_time * info.sample_rate as f64;
83        let max_delay_block = (max_delay_frame / FRAMES_PER_BLOCK_USIZE as f64).ceil() as usize;
84
85        // Update the delay line capacity if necessary, and write the input blocks to the internal buffer
86        self.update_delay_line_capacity(max_delay_block + 1);
87
88        // Write to the internal buffer
89        self.write(inputs);
90        Chunk::default()
91    }
92
93    fn output_count(&self) -> u32 {
94        0
95    }
96}