Skip to main content

atmos/kernel/
audio_stream_fmt.rs

1//! PWM 音声の連続再生(ストリーミング)の純粋な計算部分。
2//!
3//! 【2026-10-19】MSE(YouTube 等)の AAC を途切れずに鳴らすため、PWM の FIFO へ DMA で
4//! 流し続ける。ここはハードウェアに触れない部分(PWM の周期、標本 → デューティ値、
5//! 復号済み PCM の待ち行列から FIFO の語列を作る)だけを持ち、ホストで試験できる。
6//! レジスタ操作と DMA のリングは `audio.rs` の `AudioStream`。
7
8use alloc::collections::VecDeque;
9
10/// PWM のクロック [Hz](PLLD 500MHz を 2 分周)。
11pub const PWM_STREAM_CLOCK_HZ: u32 = 250_000_000;
12
13/// 標本化周波数に対する PWM の周期(RNG)。1 標本 = 1 周期。
14pub fn pwm_range(clock_hz: u32, sample_rate: u32) -> u32 {
15    if sample_rate == 0 {
16        return 0;
17    }
18    (clock_hz + sample_rate / 2) / sample_rate
19}
20
21/// 16 ビット符号付き標本を PWM のデューティ値(0..range)へ写す。0 は range/2(無音)。
22pub fn sample_to_pwm(s: i16, range: u32) -> u32 {
23    let u = (s as i32 + 32768) as u64; // 0..65535
24    ((u * range as u64) >> 16) as u32
25}
26
27/// 復号済み PCM の待ち行列(ステレオ交互、i16)。
28pub struct PcmQueue {
29    samples: VecDeque<i16>,
30    /// 入力のチャンネル数(1 ならモノラル。出力時に左右へ複製する)。
31    pub channels: u32,
32    /// 標本化周波数 [Hz]。
33    pub sample_rate: u32,
34}
35
36impl PcmQueue {
37    pub fn new(sample_rate: u32, channels: u32) -> Self {
38        PcmQueue { samples: VecDeque::new(), channels: channels.clamp(1, 2), sample_rate }
39    }
40
41    /// 交互配置の PCM(`channels` 本)を末尾へ足す。
42    pub fn push(&mut self, pcm: &[i16]) {
43        self.samples.extend(pcm.iter().copied());
44    }
45
46    /// 溜まっている時間 [ms]。
47    pub fn buffered_ms(&self) -> u64 {
48        let frames = self.samples.len() as u64 / self.channels as u64;
49        if self.sample_rate == 0 {
50            0
51        } else {
52            frames * 1000 / self.sample_rate as u64
53        }
54    }
55
56    pub fn clear(&mut self) {
57        self.samples.clear();
58    }
59
60    /// 溜まっているフレーム数。
61    pub fn frames(&self) -> usize {
62        self.samples.len() / self.channels as usize
63    }
64
65    /// `out`(左右交互の PWM 語。長さは偶数)を待ち行列から埋める。足りない分は無音。
66    /// 埋めた(実データの)フレーム数を返す。
67    pub fn fill_words(&mut self, out: &mut [u32], range: u32) -> usize {
68        let silence = range / 2;
69        let mut filled = 0;
70        for pair in out.chunks_mut(2) {
71            let (l, r) = if self.channels >= 2 {
72                if self.samples.len() >= 2 {
73                    let l = self.samples.pop_front().unwrap_or(0);
74                    let r = self.samples.pop_front().unwrap_or(0);
75                    filled += 1;
76                    (sample_to_pwm(l, range), sample_to_pwm(r, range))
77                } else {
78                    (silence, silence)
79                }
80            } else if let Some(m) = self.samples.pop_front() {
81                filled += 1;
82                let v = sample_to_pwm(m, range);
83                (v, v)
84            } else {
85                (silence, silence)
86            };
87            pair[0] = l;
88            if pair.len() > 1 {
89                pair[1] = r;
90            }
91        }
92        filled
93    }
94
95    /// `frames` フレーム分を捨てる(実機の DMA が無い環境で、実時間の消費を真似る)。
96    pub fn drain_frames(&mut self, frames: usize) {
97        let n = (frames * self.channels as usize).min(self.samples.len());
98        self.samples.drain(..n);
99    }
100}