Skip to main content

atmos/kernel/
audio.rs

1// audio.rs - AtmOS PWM Audio Driver & MML Playback Engine
2#![allow(dead_code)]
3
4extern crate alloc;
5use crate::kernel::audio_stream_fmt::{pwm_range, PcmQueue, PWM_STREAM_CLOCK_HZ};
6use crate::os_lib::mml::Note;
7use alloc::vec::Vec;
8use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
9use spin::Mutex;
10
11const GPIO_BASE: usize = 0x3F20_0000;
12const GPFSEL4: *mut u32 = (GPIO_BASE + 0x10) as *mut u32;
13
14const CM_BASE: usize = 0x3F10_1000;
15const CM_PWMCTL: *mut u32 = (CM_BASE + 0x10A0) as *mut u32;
16const CM_PWMDIV: *mut u32 = (CM_BASE + 0x10A4) as *mut u32;
17
18const PWM_BASE: usize = 0x3F20_C000;
19const PWM_CTL: *mut u32 = PWM_BASE as *mut u32;
20const PWM_STA: *mut u32 = (PWM_BASE + 0x04) as *mut u32;
21const PWM_DMAC: *mut u32 = (PWM_BASE + 0x08) as *mut u32;
22const PWM_RNG1: *mut u32 = (PWM_BASE + 0x10) as *mut u32;
23const PWM_DAT1: *mut u32 = (PWM_BASE + 0x14) as *mut u32;
24const PWM_FIF1: *mut u32 = (PWM_BASE + 0x18) as *mut u32;
25const PWM_RNG2: *mut u32 = (PWM_BASE + 0x20) as *mut u32;
26const PWM_DAT2: *mut u32 = (PWM_BASE + 0x24) as *mut u32;
27
28// I2S (HDMI Audio Output Destination) Registers
29const I2S_BASE: usize = 0x3F20_3000;
30const I2S_CS: *mut u32 = I2S_BASE as *mut u32;
31const I2S_FIFO: *mut u32 = (I2S_BASE + 0x04) as *mut u32;
32const I2S_MODE: *mut u32 = (I2S_BASE + 0x08) as *mut u32;
33const I2S_RXC: *mut u32 = (I2S_BASE + 0x0C) as *mut u32;
34const I2S_TXC: *mut u32 = (I2S_BASE + 0x10) as *mut u32;
35const I2S_DREQ: *mut u32 = (I2S_BASE + 0x14) as *mut u32;
36
37// DMA 3 (for PWM) and DMA 4 (for I2S) Registers
38const DMA3_BASE: usize = 0x3F00_7300;
39const DMA3_CS: *mut u32 = DMA3_BASE as *mut u32;
40const DMA3_CONBLK_AD: *mut u32 = (DMA3_BASE + 0x04) as *mut u32;
41
42const DMA4_BASE: usize = 0x3F00_7400;
43const DMA4_CS: *mut u32 = DMA4_BASE as *mut u32;
44const DMA4_CONBLK_AD: *mut u32 = (DMA4_BASE + 0x04) as *mut u32;
45
46// PWM CLOCK: 19.2MHz / 16 = 1.2MHz (1,200,000 Hz)
47const PWM_CLOCK_FREQ: u32 = 1_200_000;
48
49// 非同期演奏用グローバルステート
50static PLAY_QUEUE: Mutex<Vec<Note>> = Mutex::new(Vec::new());
51static PLAY_INDEX: Mutex<usize> = Mutex::new(0);
52static IS_PLAYING: Mutex<bool> = Mutex::new(false);
53static THREAD_STARTED: Mutex<bool> = Mutex::new(false);
54
55// PCMバッファと再生用静的領域
56static mut PCM_BUFFER_PWM: [u8; 65536] = [0; 65536];
57static mut PCM_BUFFER_I2S: [i16; 65536] = [0; 65536];
58static mut DMA_CB_PWM: crate::kernel::dma::DmaControlBlock = crate::kernel::dma::DmaControlBlock {
59    ti: 0,
60    source_ad: 0,
61    dest_ad: 0,
62    txfr_len: 0,
63    stride: 0,
64    nextconbk: 0,
65    _reserved: [0; 2],
66};
67static mut DMA_CB_I2S: crate::kernel::dma::DmaControlBlock = crate::kernel::dma::DmaControlBlock {
68    ti: 0,
69    source_ad: 0,
70    dest_ad: 0,
71    txfr_len: 0,
72    stride: 0,
73    nextconbk: 0,
74    _reserved: [0; 2],
75};
76
77pub struct Audio;
78
79impl Audio {
80    /// PWMおよびGPIO40/45の初期化処理、I2S(HDMI)の初期化、DMAの設定を行います
81    pub fn init() {
82        unsafe {
83            // 1. GPIO40 (PWM0) / GPIO45 (PWM1) のオルタネート機能を ALT0 (100) に設定
84            let mut val = core::ptr::read_volatile(GPFSEL4);
85            val &= !0b111; // GPIO 40
86            val |= 0b100; // ALT0 (PWM0)
87            val &= !(0b111 << 15); // GPIO 45
88            val |= 0b100 << 15; // ALT0 (PWM1)
89            core::ptr::write_volatile(GPFSEL4, val);
90
91            // 2. PWM クロックの初期化
92            // 停止させて設定をクリーンにする
93            core::ptr::write_volatile(CM_PWMCTL, 0x5A00_0000);
94            for _ in 0..1000 {
95                core::arch::asm!("nop");
96            }
97
98            // 分分周比 16 (19.2MHz / 16 = 1.2MHz)
99            core::ptr::write_volatile(CM_PWMDIV, 0x5A00_0000 | (16 << 12));
100            for _ in 0..1000 {
101                core::arch::asm!("nop");
102            }
103
104            // クロックを起動 (ENAB=0x10, SRC=0x01: OSC 19.2MHz)
105            core::ptr::write_volatile(CM_PWMCTL, 0x5A00_0010 | 0x01);
106            for _ in 0..1000 {
107                core::arch::asm!("nop");
108            }
109
110            // 3. PWM コントローラの初期化
111            // 一度全体をクリア
112            core::ptr::write_volatile(PWM_CTL, 0);
113            for _ in 0..1000 {
114                core::arch::asm!("nop");
115            }
116
117            // 周期の初期値を 1024 に
118            core::ptr::write_volatile(PWM_RNG1, 1024);
119            core::ptr::write_volatile(PWM_RNG2, 1024);
120
121            // 出力データを 0 (Mute) に
122            core::ptr::write_volatile(PWM_DAT1, 0);
123            core::ptr::write_volatile(PWM_DAT2, 0);
124
125            // Mark-Space Mode で両方のチャンネルを有効化
126            core::ptr::write_volatile(PWM_CTL, (1 << 0) | (1 << 1) | (1 << 8) | (1 << 9));
127
128            // 4. I2S (HDMI Audio) の初期化
129            // I2Sクロックおよびレジスタの初期設定
130            core::ptr::write_volatile(I2S_CS, 0); // Disable I2S first
131            core::ptr::write_volatile(I2S_MODE, 0);
132            // 16-bit stereo transmit control
133            core::ptr::write_volatile(I2S_TXC, (1 << 30) | (1 << 29) | (8 << 16) | (1 << 15)); // CH1EN | CH1WEX | CH1POS
134            core::ptr::write_volatile(I2S_DREQ, (8 << 24) | (8 << 8)); // TX DREQ, RX DREQ threshold
135            core::ptr::write_volatile(I2S_CS, 1 << 2); // TX Enable
136
137            // 5. DMA 3 / 4 のイネーブル
138            // DMAコントロール全体のENABLEレジスタ (0x3F007FF0)
139            let dma_en_ptr = 0x3F007FF0 as *mut u32;
140            let current_en = core::ptr::read_volatile(dma_en_ptr);
141            core::ptr::write_volatile(dma_en_ptr, current_en | (1 << 3) | (1 << 4));
142            // Enable DMA 3 and 4
143        }
144
145        crate::info!(
146            "[AUD] Hardware PWM (Analog) and I2S (HDMI) audio engines initialized successfully."
147        );
148    }
149
150    /// 常駐再生スレッドを必要なら起動する(プロセス全体で一度だけ spawn される)。
151    /// 以前は効果音ごとにスレッドを spawn→終了していたため、ESC 連打などで
152    /// 頻繁なスレッド生成・破棄が起き、スタック確保/解放でヒープ破損を招いていた。
153    /// Audio::init() は scheduler::init() より前に呼ばれるため、ここで遅延起動する。
154    fn ensure_playback_thread() {
155        let mut started = THREAD_STARTED.lock();
156        if !*started {
157            *started = true;
158            // 【2026-08-25 バグ修正】`spawn` はヒープ不足で **0 を返す**。
159            // `started` を先に立てているので、失敗したまま戻さないと
160            // 「再生スレッドは居ないのに起動済み」になり、
161            // **以後まったく音が鳴らなくなる**(再試行の機会が二度と来ない)。
162            let pid = crate::kernel::scheduler::spawn(
163                Self::playback_thread_entry,
164                crate::kernel::scheduler::Priority::High,
165                "mml_player",
166            );
167            if pid == 0 {
168                crate::warn!("[AUDIO] 再生スレッドを起動できません(ヒープ不足)");
169                *started = false;
170            }
171        }
172    }
173
174    /// 指定された周波数 (Hz) で自律発振を開始します (0 の場合は Mute)
175    pub fn set_tone(freq: u32) {
176        // 連続再生中は PWM を FIFO+DMA で使っているので触らない。
177        if STREAM_ACTIVE.load(Ordering::Acquire) {
178            return;
179        }
180        if freq == 0 {
181            Self::mute();
182            return;
183        }
184
185        let rng = PWM_CLOCK_FREQ / freq;
186        let dat = rng / 2; // デューティ比 50%
187
188        unsafe {
189            core::ptr::write_volatile(PWM_RNG1, rng);
190            core::ptr::write_volatile(PWM_RNG2, rng);
191            core::ptr::write_volatile(PWM_DAT1, dat);
192            core::ptr::write_volatile(PWM_DAT2, dat);
193        }
194    }
195
196    /// 音声を消音します
197    pub fn mute() {
198        if STREAM_ACTIVE.load(Ordering::Acquire) {
199            return;
200        }
201        unsafe {
202            core::ptr::write_volatile(PWM_DAT1, 0);
203            core::ptr::write_volatile(PWM_DAT2, 0);
204        }
205    }
206
207    /// MML 文字列をバックグラウンド(ノンブロッキング)で演奏開始します
208    pub fn play_mml(mml_str: &str) {
209        let notes = crate::os_lib::mml::parse_mml(mml_str);
210        if notes.is_empty() {
211            return;
212        }
213
214        Self::ensure_playback_thread();
215        {
216            let mut queue = PLAY_QUEUE.lock();
217            let mut idx = PLAY_INDEX.lock();
218            let mut playing = IS_PLAYING.lock();
219
220            // 常駐再生スレッドにキューを渡すだけ(スレッドは spawn しない)
221            *queue = notes;
222            *idx = 0;
223            *playing = true;
224        }
225    }
226
227    /// 現在MMLを演奏中かどうかを判定します
228    pub fn is_playing() -> bool {
229        *IS_PLAYING.lock()
230    }
231
232    /// MML演奏を強制停止します
233    pub fn stop_mml() {
234        let mut queue = PLAY_QUEUE.lock();
235        let mut idx = PLAY_INDEX.lock();
236        queue.clear();
237        *idx = 0;
238        Self::mute();
239    }
240
241    /// 単音をバックグラウンド(ノンブロッキング)で指定ミリ秒間演奏します
242    pub fn play_tone_async(freq: u32, duration_ms: u32) {
243        let notes = alloc::vec![Note { freq, duration_ms }];
244
245        Self::ensure_playback_thread();
246        {
247            let mut queue = PLAY_QUEUE.lock();
248            let mut idx = PLAY_INDEX.lock();
249            let mut playing = IS_PLAYING.lock();
250
251            // 常駐再生スレッドにキューを渡すだけ(スレッドは spawn しない)
252            *queue = notes;
253            *idx = 0;
254            *playing = true;
255        }
256    }
257
258    /// 再生スレッドの実行エントリポイント
259    fn playback_thread_entry() {
260        loop {
261            let note_opt = {
262                let mut queue = PLAY_QUEUE.lock();
263                let mut idx = PLAY_INDEX.lock();
264                if *idx < queue.len() {
265                    let note = queue[*idx];
266                    *idx += 1;
267                    Some(note)
268                } else {
269                    // 全ての音符の再生が終了
270                    queue.clear();
271                    *idx = 0;
272                    let mut playing = IS_PLAYING.lock();
273                    *playing = false;
274                    None
275                }
276            };
277
278            if let Some(note) = note_opt {
279                if note.freq == 0 {
280                    Self::mute();
281                } else {
282                    Self::set_tone(note.freq);
283                }
284
285                // tick 単位 (10ms) に変換
286                let ticks = (note.duration_ms / 10) as usize;
287                if ticks > 0 {
288                    crate::kernel::scheduler::sleep(ticks);
289                } else {
290                    crate::kernel::scheduler::sleep(1);
291                }
292
293                // 同音の連続を綺麗に分離するため、一瞬消音する (10ms)
294                Self::mute();
295                crate::kernel::scheduler::sleep(1);
296            } else {
297                // キューが空の間はアイドル待機(スレッドは終了せず常駐し続ける)
298                crate::kernel::scheduler::sleep(2);
299            }
300        }
301    }
302
303    /// デコードされた 16-bit PCM ステレオ音声データを、HDMI (I2S) と PWM (イヤホンジャック) に供給してDMA再生します
304    pub fn play_pcm_stream(pcm_data: &[i16]) {
305        if pcm_data.is_empty() {
306            return;
307        }
308
309        unsafe {
310            // 静的バッファサイズに合わせてコピー(サイズ上限 65536)
311            let len = pcm_data.len().min(65536);
312
313            // 1. HDMI (I2S) 用バッファの構築
314            core::ptr::copy_nonoverlapping(pcm_data.as_ptr(), PCM_BUFFER_I2S.as_mut_ptr(), len);
315
316            // 2. PWM (アナログ) 用バッファの構築 (16-bit ステレオ -> 8-bit モノラル/ステレオ変換)
317            for i in 0..len {
318                // -32768..32767 を 0..255 にマッピング
319                let sample_8 = (((pcm_data[i] as i32 + 32768) >> 8) & 0xFF) as u8;
320                // PWMの出力範囲 (RNG=1024) に合わせてスケーリング (0..255 を 0..1023 へ)
321                PCM_BUFFER_PWM[i] = ((sample_8 as u32 * 4).min(1023)) as u8;
322            }
323
324            // 3. DMA コントロールブロックの設定 (キャッシュクリーン)
325            let pwm_buf_ptr = PCM_BUFFER_PWM.as_ptr() as usize;
326            let i2s_buf_ptr = PCM_BUFFER_I2S.as_ptr() as usize;
327
328            crate::kernel::mmu::clean_dcache_range(pwm_buf_ptr, len);
329            crate::kernel::mmu::clean_dcache_range(i2s_buf_ptr, len * 2);
330
331            // 4. DMA 3 (PWM) 転送ブロックの構築
332            DMA_CB_PWM = crate::kernel::dma::DmaControlBlock {
333                ti: (5 << 16) | (1 << 8) | (1 << 4) | (1 << 3), // PERMAP=5 (PWM), SRC_INC, DEST_DREQ, WAIT_RESP
334                source_ad: crate::kernel::dma::to_bus_address(pwm_buf_ptr as u32),
335                dest_ad: crate::kernel::dma::to_bus_address(PWM_FIF1 as u32),
336                txfr_len: len as u32,
337                stride: 0,
338                nextconbk: 0,
339                _reserved: [0; 2],
340            };
341            crate::kernel::mmu::clean_dcache_range(
342                &DMA_CB_PWM as *const _ as usize,
343                core::mem::size_of::<crate::kernel::dma::DmaControlBlock>(),
344            );
345
346            // 5. DMA 4 (I2S / HDMI) 転送ブロックの構築
347            DMA_CB_I2S = crate::kernel::dma::DmaControlBlock {
348                ti: (6 << 16) | (1 << 8) | (1 << 4) | (1 << 3), // PERMAP=6 (I2S), SRC_INC, DEST_DREQ, WAIT_RESP
349                source_ad: crate::kernel::dma::to_bus_address(i2s_buf_ptr as u32),
350                dest_ad: crate::kernel::dma::to_bus_address(I2S_FIFO as u32),
351                txfr_len: (len * 2) as u32,
352                stride: 0,
353                nextconbk: 0,
354                _reserved: [0; 2],
355            };
356            crate::kernel::mmu::clean_dcache_range(
357                &DMA_CB_I2S as *const _ as usize,
358                core::mem::size_of::<crate::kernel::dma::DmaControlBlock>(),
359            );
360
361            if !crate::kernel::dma::is_qemu_env() {
362                // 実機の場合: DMAレジスタを設定して転送開始
363                // DMA 3 (PWM)
364                core::ptr::write_volatile(
365                    DMA3_CONBLK_AD,
366                    crate::kernel::dma::to_bus_address(&DMA_CB_PWM as *const _ as *const _ as u32),
367                );
368                core::ptr::write_volatile(DMA3_CS, (1 << 0) | (8 << 16)); // ACTIVE | PRIORITY=8
369
370                // DMA 4 (I2S / HDMI)
371                core::ptr::write_volatile(
372                    DMA4_CONBLK_AD,
373                    crate::kernel::dma::to_bus_address(&DMA_CB_I2S as *const _ as *const _ as u32),
374                );
375                core::ptr::write_volatile(DMA4_CS, (1 << 0) | (8 << 16)); // ACTIVE | PRIORITY=8
376            }
377        }
378    }
379}
380
381#[derive(Clone, Copy, Debug)]
382pub enum SeType {
383    Apply,  // 設定適用時のきらびやかなチャイム
384    Save,   // 保存時の安心なピピッという完了音
385    Delete, // 削除時の少し低めのドシュッという音
386    Close,  // ウィンドウを閉じる時のフェード音
387}
388
389/// システム効果音を再生します。サウンド設定がOFFの場合は完全に静音化されます。
390pub fn play_system_se(se: SeType) {
391    // グローバルサウンド設定の確認
392    if !crate::kernel::config::get_config().enable_sound {
393        return;
394    }
395
396    let mml = match se {
397        SeType::Apply => "T180 L16 O5 C E G",
398        SeType::Save => "T240 L16 O5 G O6 C",
399        SeType::Delete => "T150 L12 O4 C O3 G",
400        SeType::Close => "T240 L16 O5 F D",
401    };
402
403    Audio::play_mml(mml);
404}
405
406// ============================================================================
407// 連続再生(ストリーミング): PWM の FIFO へ DMA のリング(2 面)で流し続ける
408// ============================================================================
409//
410// 【2026-10-19】MSE(YouTube 等)の AAC を途切れずに鳴らすために新設。従来の
411// `play_pcm_stream` は 65536 標本の一括転送で、しかも PWM の周期(約 1.2kHz)・FIFO/DMA の
412// 有効化・DMA の TI(DEST_INC と DEST_DREQ の取り違え)・周辺機器のバスアドレス
413// (0x3F→0x7E ではなく 0xC0 を OR していた)が誤っており、実際には鳴らなかった。
414//
415// 構成:
416// - PWM のクロック = PLLD 500MHz / 2 = 250MHz、周期 RNG = 250MHz / 標本化周波数
417//   (44.1kHz で 5669、約 12.5 ビット相当)。PWM 方式(MSEN=0)、両チャンネルとも FIFO(USEF)。
418// - DMA チャンネル 5(ファームウェアが他で使わない範囲)。制御ブロック 2 つを互いに
419//   `nextconbk` で指し合い、2 面のバッファを交互に PWM の FIFO へ送り続ける(DREQ 同期)。
420// - 補給スレッドが、DMA が今どちらの面を読んでいるか(CONBLK_AD)を見て、読み終えた面を
421//   待ち行列(`PcmQueue`)から埋め直す。待ち行列が空なら無音(周期の半分)。
422// - QEMU(PWM・DMA を模擬しない)ではレジスタに触れず、補給スレッドが実時間で待ち行列を
423//   消費するだけにする(生産側の流れを QEMU で確かめるため)。
424//
425// 実機での確認が前提(QEMU では音は出ない)。純粋な計算は `audio_stream_fmt.rs`(ホスト試験あり)。
426
427const STREAM_DMA_CH: usize = 5;
428const STREAM_DMA_BASE: usize = 0x3F00_7000 + STREAM_DMA_CH * 0x100;
429const STREAM_DMA_CS: *mut u32 = STREAM_DMA_BASE as *mut u32;
430const STREAM_DMA_CONBLK_AD: *mut u32 = (STREAM_DMA_BASE + 0x04) as *mut u32;
431const DMA_ENABLE_REG: *mut u32 = 0x3F00_7FF0 as *mut u32;
432/// PWM の FIFO の**バスアドレス**(DMA から見た周辺機器の番地。ARM の 0x3F20C018)。
433const PWM_FIF1_BUS: u32 = 0x7E20_C018;
434/// BCM2835 Clock Manager レジスタ書き込み保護マジック(データシート上の PASSWD=0x5A)
435const CM_MAGIC: u32 = 0x5A00_0000;
436/// 1 面あたりのフレーム数(44.1kHz で約 46ms)。
437const STREAM_HALF_FRAMES: usize = 2048;
438const STREAM_HALF_WORDS: usize = STREAM_HALF_FRAMES * 2;
439
440#[repr(C, align(32))]
441struct StreamBuf([u32; STREAM_HALF_WORDS * 2]);
442static mut STREAM_BUF: StreamBuf = StreamBuf([0; STREAM_HALF_WORDS * 2]);
443static mut STREAM_CBS: [crate::kernel::dma::DmaControlBlock; 2] = [crate::kernel::dma::DmaControlBlock {
444    ti: 0,
445    source_ad: 0,
446    dest_ad: 0,
447    txfr_len: 0,
448    stride: 0,
449    nextconbk: 0,
450    _reserved: [0; 2],
451}; 2];
452
453static STREAM_QUEUE: Mutex<Option<PcmQueue>> = Mutex::new(None);
454static STREAM_ACTIVE: AtomicBool = AtomicBool::new(false);
455static STREAM_PAUSED: AtomicBool = AtomicBool::new(false);
456static STREAM_RANGE: AtomicU32 = AtomicU32::new(0);
457static FEEDER_RUNNING: AtomicBool = AtomicBool::new(false);
458
459/// RAM の番地を DMA から見たバスアドレスへ(キャッシュを通らない 0xC0000000 の別名)。
460fn ram_bus(addr: usize) -> u32 {
461    (addr as u32) | 0xC000_0000
462}
463
464pub struct AudioStream;
465
466impl AudioStream {
467    /// 連続再生を始める(既に同じ形式で動いていれば何もしない)。
468    pub fn start(sample_rate: u32, channels: u32) {
469        if sample_rate == 0 {
470            return;
471        }
472        {
473            let mut q = STREAM_QUEUE.lock();
474            if let Some(cur) = q.as_ref() {
475                if STREAM_ACTIVE.load(Ordering::Acquire) && cur.sample_rate == sample_rate && cur.channels == channels.clamp(1, 2) {
476                    return;
477                }
478            }
479            *q = Some(PcmQueue::new(sample_rate, channels));
480        }
481        let range = pwm_range(PWM_STREAM_CLOCK_HZ, sample_rate);
482        STREAM_RANGE.store(range, Ordering::Release);
483        STREAM_PAUSED.store(false, Ordering::Release);
484        // 鳴っている効果音(MML)を止めてから PWM を連続再生用に切り替える。
485        Audio::stop_mml();
486        STREAM_ACTIVE.store(true, Ordering::Release);
487        if !crate::kernel::dma::is_qemu_env() {
488            unsafe { Self::hw_start(range) };
489        }
490        if !FEEDER_RUNNING.swap(true, Ordering::AcqRel) {
491            let pid = crate::kernel::scheduler::spawn(
492                Self::feeder_thread,
493                crate::kernel::scheduler::Priority::High,
494                "audio_feeder",
495            );
496            if pid == 0 {
497                crate::warn!("[AUDIO] 補給スレッドを起動できません(ヒープ不足)。連続再生を止めます");
498                FEEDER_RUNNING.store(false, Ordering::Release);
499                Self::stop();
500                return;
501            }
502        }
503        crate::warn!("[AUDIO] 連続再生開始 {}Hz {}ch range={}", sample_rate, channels, range);
504    }
505
506    /// 復号済みの PCM(交互配置)を足す。
507    pub fn push(pcm: &[i16]) {
508        if let Some(q) = STREAM_QUEUE.lock().as_mut() {
509            q.push(pcm);
510        }
511    }
512
513    /// 溜まっている時間 [ms](連続再生していなければ 0)。
514    pub fn buffered_ms() -> u64 {
515        STREAM_QUEUE.lock().as_ref().map(|q| q.buffered_ms()).unwrap_or(0)
516    }
517
518    /// 一時停止(無音を流し、待ち行列は保つ)・再開。
519    pub fn set_paused(paused: bool) {
520        STREAM_PAUSED.store(paused, Ordering::Release);
521    }
522
523    /// 待ち行列を捨てる(シーク時)。
524    pub fn flush() {
525        if let Some(q) = STREAM_QUEUE.lock().as_mut() {
526            q.clear();
527        }
528    }
529
530    pub fn is_active() -> bool {
531        STREAM_ACTIVE.load(Ordering::Acquire)
532    }
533
534    /// 連続再生を止め、PWM を効果音(単音)用の設定へ戻す。
535    pub fn stop() {
536        if !STREAM_ACTIVE.swap(false, Ordering::AcqRel) {
537            return;
538        }
539        if !crate::kernel::dma::is_qemu_env() {
540            unsafe {
541                core::ptr::write_volatile(STREAM_DMA_CS, 1 << 31); // RESET(転送を止める)
542                core::ptr::write_volatile(PWM_DMAC, 0);
543                core::ptr::write_volatile(PWM_CTL, 0);
544                Self::delay();
545                Self::restore_tone_mode();
546            }
547        }
548        *STREAM_QUEUE.lock() = None;
549        crate::warn!("[AUDIO] 連続再生停止");
550    }
551
552    fn delay() {
553        for _ in 0..2000 {
554            core::hint::spin_loop();
555        }
556    }
557
558    /// PWM のクロック(PLLD/2)・周期・FIFO・DREQ を設定し、DMA のリングを始める。
559    unsafe fn hw_start(range: u32) {
560        unsafe {
561            // 動いている転送と PWM を止める。
562            core::ptr::write_volatile(STREAM_DMA_CS, 1 << 31);
563            core::ptr::write_volatile(PWM_DMAC, 0);
564            core::ptr::write_volatile(PWM_CTL, 0);
565            Self::delay();
566
567            // クロック: 止めて BUSY が落ちるのを待ち、PLLD(SRC=6)/2 で起動。
568            let ctl = core::ptr::read_volatile(CM_PWMCTL);
569            core::ptr::write_volatile(CM_PWMCTL, CM_MAGIC | (ctl & !0x10));
570            let mut guard = 0;
571            while core::ptr::read_volatile(CM_PWMCTL) & 0x80 != 0 && guard < 100_000 {
572                guard += 1;
573                core::hint::spin_loop();
574            }
575            core::ptr::write_volatile(CM_PWMDIV, CM_MAGIC | (2 << 12));
576            core::ptr::write_volatile(CM_PWMCTL, CM_MAGIC | 6);
577            core::ptr::write_volatile(CM_PWMCTL, CM_MAGIC | 6 | 0x10);
578            Self::delay();
579
580            core::ptr::write_volatile(PWM_RNG1, range);
581            core::ptr::write_volatile(PWM_RNG2, range);
582            core::ptr::write_volatile(PWM_CTL, 1 << 6); // CLRF1(FIFO を空に)
583            Self::delay();
584            core::ptr::write_volatile(PWM_STA, 0x1FC); // 誤り表示(BERR・GAPO・RERR・WERR)を消す
585            // DMA 要求: ENAB | PANIC=7 | DREQ=7
586            core::ptr::write_volatile(PWM_DMAC, (1 << 31) | (7 << 8) | 7);
587            // PWEN1 | USEF1 | PWEN2 | USEF2(PWM 方式。FIFO の語は ch1, ch2 の順に交互に使われる)
588            core::ptr::write_volatile(PWM_CTL, (1 << 0) | (1 << 5) | (1 << 8) | (1 << 13));
589
590            // 2 面のバッファを埋める(待ち行列が空なら無音)。
591            Self::fill_half(0);
592            Self::fill_half(1);
593
594            // 制御ブロック 2 つを互いに指し合わせる。
595            let cbs = core::ptr::addr_of_mut!(STREAM_CBS) as *mut crate::kernel::dma::DmaControlBlock;
596            let buf = core::ptr::addr_of_mut!(STREAM_BUF.0) as *mut u32;
597            for i in 0..2usize {
598                let next = cbs.add(1 - i) as usize;
599                *cbs.add(i) = crate::kernel::dma::DmaControlBlock {
600                    // PERMAP=5(PWM)| SRC_INC | DEST_DREQ | WAIT_RESP
601                    ti: (5 << 16) | (1 << 8) | (1 << 6) | (1 << 3),
602                    source_ad: ram_bus(buf.add(i * STREAM_HALF_WORDS) as usize),
603                    dest_ad: PWM_FIF1_BUS,
604                    txfr_len: (STREAM_HALF_WORDS * 4) as u32,
605                    stride: 0,
606                    nextconbk: ram_bus(next),
607                    _reserved: [0; 2],
608                };
609            }
610            crate::kernel::mmu::clean_dcache_range(
611                cbs as usize,
612                2 * core::mem::size_of::<crate::kernel::dma::DmaControlBlock>(),
613            );
614
615            let en = core::ptr::read_volatile(DMA_ENABLE_REG);
616            core::ptr::write_volatile(DMA_ENABLE_REG, en | (1 << STREAM_DMA_CH));
617            core::ptr::write_volatile(STREAM_DMA_CONBLK_AD, ram_bus(cbs as usize));
618            // ACTIVE | PRIORITY=8 | PANIC_PRIORITY=8
619            core::ptr::write_volatile(STREAM_DMA_CS, (1 << 0) | (8 << 16) | (8 << 20));
620        }
621    }
622
623    /// 面 `half`(0/1)を待ち行列から埋め、DMA から見えるようにキャッシュを書き出す。
624    unsafe fn fill_half(half: usize) {
625        let range = STREAM_RANGE.load(Ordering::Acquire);
626        let buf = unsafe { core::ptr::addr_of_mut!(STREAM_BUF.0) as *mut u32 };
627        let words = unsafe { core::slice::from_raw_parts_mut(buf.add(half * STREAM_HALF_WORDS), STREAM_HALF_WORDS) };
628        if STREAM_PAUSED.load(Ordering::Acquire) {
629            words.fill(range / 2);
630        } else {
631            match STREAM_QUEUE.lock().as_mut() {
632                Some(q) => {
633                    q.fill_words(words, range);
634                }
635                None => words.fill(range / 2),
636            }
637        }
638        crate::kernel::mmu::clean_dcache_range(words.as_ptr() as usize, STREAM_HALF_WORDS * 4);
639    }
640
641    /// 補給スレッド: DMA が読み終えた面を埋め直す(QEMU では実時間で消費するだけ)。
642    fn feeder_thread() {
643        let qemu = crate::kernel::dma::is_qemu_env();
644        let cb1_bus = unsafe { ram_bus((core::ptr::addr_of!(STREAM_CBS) as *const crate::kernel::dma::DmaControlBlock).add(1) as usize) };
645        let mut last_half = 0usize;
646        let mut last_ms = crate::kernel::timer::get_system_time_ms();
647        while STREAM_ACTIVE.load(Ordering::Acquire) {
648            if qemu {
649                let now = crate::kernel::timer::get_system_time_ms();
650                let dt = now.wrapping_sub(last_ms);
651                last_ms = now;
652                if !STREAM_PAUSED.load(Ordering::Acquire) {
653                    if let Some(q) = STREAM_QUEUE.lock().as_mut() {
654                        let frames = (q.sample_rate as u64 * dt / 1000) as usize;
655                        q.drain_frames(frames);
656                    }
657                }
658            } else {
659                let cur = unsafe { core::ptr::read_volatile(STREAM_DMA_CONBLK_AD) };
660                let half = if cur == cb1_bus { 1 } else { 0 };
661                if half != last_half {
662                    // DMA が次の面へ移った → 読み終えた面を埋め直す。
663                    unsafe { Self::fill_half(last_half) };
664                    last_half = half;
665                }
666            }
667            crate::kernel::scheduler::sleep(1);
668        }
669        FEEDER_RUNNING.store(false, Ordering::Release);
670        crate::kernel::scheduler::exit();
671    }
672
673    /// PWM を `init()` と同じ効果音(単音)用の設定へ戻す(OSC 19.2MHz / 16、M/S 方式)。
674    unsafe fn restore_tone_mode() {
675        unsafe {
676            core::ptr::write_volatile(CM_PWMCTL, CM_MAGIC);
677            Self::delay();
678            core::ptr::write_volatile(CM_PWMDIV, CM_MAGIC | (16 << 12));
679            Self::delay();
680            core::ptr::write_volatile(CM_PWMCTL, CM_MAGIC | 0x10 | 0x01);
681            Self::delay();
682            core::ptr::write_volatile(PWM_RNG1, 1024);
683            core::ptr::write_volatile(PWM_RNG2, 1024);
684            core::ptr::write_volatile(PWM_DAT1, 0);
685            core::ptr::write_volatile(PWM_DAT2, 0);
686            core::ptr::write_volatile(PWM_CTL, (1 << 0) | (1 << 1) | (1 << 8) | (1 << 9));
687        }
688    }
689}