Skip to main content

pfx_sound/
pcm.rs

1use std::collections::VecDeque;
2use std::sync::{Arc, Mutex, MutexGuard};
3
4use crate::mix::lock;
5
6pub const PCM_EDGE_SECONDS: f32 = 0.002;
7
8struct Inner {
9    samples: VecDeque<f32>,
10    closed: bool,
11}
12
13pub(crate) struct PcmRing {
14    inner: Mutex<Inner>,
15    capacity: usize,
16    channels: usize,
17}
18
19impl PcmRing {
20    fn new(channels: usize, capacity_frames: usize) -> Arc<Self> {
21        Arc::new(Self {
22            inner: Mutex::new(Inner {
23                samples: VecDeque::new(),
24                closed: false,
25            }),
26            capacity: capacity_frames.max(1),
27            channels: channels.max(1),
28        })
29    }
30
31    fn lock(&self) -> MutexGuard<'_, Inner> {
32        lock(&self.inner)
33    }
34}
35
36pub struct PcmSender {
37    ring: Arc<PcmRing>,
38    rate: u32,
39}
40
41impl PcmSender {
42    pub fn push(&self, samples: &[f32]) -> usize {
43        let channels = self.ring.channels;
44        let mut inner = self.ring.lock();
45        let queued = inner.samples.len() / channels;
46        let room = self.ring.capacity.saturating_sub(queued);
47        let frames = (samples.len() / channels).min(room);
48        inner.samples.extend(&samples[..frames * channels]);
49        frames
50    }
51
52    pub fn queued_frames(&self) -> usize {
53        self.ring.lock().samples.len() / self.ring.channels
54    }
55
56    pub fn capacity_frames(&self) -> usize {
57        self.ring.capacity
58    }
59
60    pub fn room_frames(&self) -> usize {
61        self.ring.capacity.saturating_sub(self.queued_frames())
62    }
63
64    pub fn channels(&self) -> u16 {
65        self.ring.channels as u16
66    }
67
68    pub fn rate(&self) -> u32 {
69        self.rate
70    }
71
72    pub fn is_connected(&self) -> bool {
73        Arc::strong_count(&self.ring) > 1
74    }
75
76    pub fn finish(&self) {
77        self.ring.lock().closed = true;
78    }
79}
80
81impl Drop for PcmSender {
82    fn drop(&mut self) {
83        self.finish();
84    }
85}
86
87pub(crate) fn pcm_pair(channels: u16, capacity_frames: usize, rate: u32) -> (PcmVoice, PcmSender) {
88    let ring = PcmRing::new(usize::from(channels), capacity_frames);
89    let edge = ((f64::from(PCM_EDGE_SECONDS) * f64::from(rate)).round() as u32).max(1);
90    let voice = PcmVoice {
91        ring: Arc::clone(&ring),
92        local: VecDeque::new(),
93        level: 0.0,
94        step: 1.0 / edge as f32,
95        last: (0.0, 0.0),
96    };
97    (voice, PcmSender { ring, rate })
98}
99
100pub(crate) struct PcmVoice {
101    ring: Arc<PcmRing>,
102    local: VecDeque<(f32, f32)>,
103    level: f32,
104    step: f32,
105    last: (f32, f32),
106}
107
108impl PcmVoice {
109    pub(crate) fn channels(&self) -> u16 {
110        self.ring.channels.min(2) as u16
111    }
112
113    pub(crate) fn fill(&mut self, frames: usize) {
114        if self.local.len() >= frames {
115            return;
116        }
117        let channels = self.ring.channels;
118        let mut inner = self.ring.lock();
119        let take = (inner.samples.len() / channels).min(frames - self.local.len());
120        for _ in 0..take {
121            let left = inner.samples.pop_front().unwrap_or(0.0);
122            let right = if channels > 1 {
123                inner.samples.pop_front().unwrap_or(0.0)
124            } else {
125                left
126            };
127            for _ in 2..channels {
128                inner.samples.pop_front();
129            }
130            self.local.push_back((left, right));
131        }
132    }
133
134    pub(crate) fn next(&mut self) -> (f32, f32) {
135        match self.local.pop_front() {
136            Some(frame) => {
137                self.last = frame;
138                self.level = (self.level + self.step).min(1.0);
139                (frame.0 * self.level, frame.1 * self.level)
140            }
141            None => {
142                self.level = (self.level - self.step).max(0.0);
143                (self.last.0 * self.level, self.last.1 * self.level)
144            }
145        }
146    }
147
148    pub(crate) fn finished(&self) -> bool {
149        if self.level > 0.0 || !self.local.is_empty() {
150            return false;
151        }
152        let inner = self.ring.lock();
153        inner.closed && inner.samples.is_empty()
154    }
155}