Skip to main content

siglus_scene_vm/runtime/commands/
audio.rs

1use anyhow::Result;
2
3use crate::runtime::commands::util;
4use crate::runtime::{Command, CommandContext, Value};
5
6fn clamp_u8(x: i64) -> u8 {
7    if x <= 0 {
8        return 0;
9    }
10    if x >= 255 {
11        return 255;
12    }
13    x as u8
14}
15
16fn pct_to_raw(pct: i64) -> u8 {
17    let p = if pct < 0 {
18        0
19    } else if pct > 100 {
20        100
21    } else {
22        pct
23    };
24    // Map 0..100 -> 0..255
25    ((p as u32 * 255) / 100) as u8
26}
27
28/// Best-effort named audio commands.
29///
30/// This keeps script-visible audio state moving without blocking unrelated runtime paths.
31/// validate decoding and VM control flow.
32pub fn handle(ctx: &mut CommandContext, cmd: &Command) -> Result<bool> {
33    let name = cmd.name.to_ascii_uppercase();
34    let args = util::strip_vm_meta(&cmd.args);
35
36    // ---- BGM ----
37    if matches!(
38        name.as_str(),
39        "BGM" | "BGM_PLAY" | "BGMSTART" | "PLAYBGM" | "BGMON"
40    ) {
41        let file = args.iter().find_map(|v| match v {
42            Value::Str(s) => Some(s.as_str()),
43            _ => None,
44        });
45        if let Some(f) = file {
46            // Record errors and continue.
47            let (bgm, audio) = (&mut ctx.bgm, &mut ctx.audio);
48            let _ = bgm.play_name(audio, f);
49        }
50        return Ok(true);
51    }
52    if matches!(name.as_str(), "BGM_STOP" | "BGMSTOP" | "STOPBGM" | "BGMOFF") {
53        let _ = ctx.bgm.stop();
54        return Ok(true);
55    }
56    if matches!(name.as_str(), "BGM_VOL" | "BGM_VOLUME" | "BGMVOLUME") {
57        // Accept either 0..255 or 0..100
58        let vol = args.iter().find_map(|v| match v {
59            Value::Int(x) => Some(*x),
60            _ => None,
61        });
62        if let Some(v) = vol {
63            let raw = if v <= 100 { pct_to_raw(v) } else { clamp_u8(v) };
64            let (bgm, audio) = (&mut ctx.bgm, &mut ctx.audio);
65            let _ = bgm.set_volume_raw(audio, raw);
66        }
67        return Ok(true);
68    }
69    if matches!(name.as_str(), "BGM_PAUSE" | "BGMPAUSE") {
70        let _ = ctx.bgm.pause();
71        return Ok(true);
72    }
73    if matches!(name.as_str(), "BGM_RESUME" | "BGMRESUME") {
74        let _ = ctx.bgm.resume();
75        return Ok(true);
76    }
77
78    // ---- SE ----
79    if matches!(name.as_str(), "SE" | "SE_PLAY" | "SEPLAY") {
80        let file = args.iter().find_map(|v| match v {
81            Value::Str(s) => Some(s.as_str()),
82            _ => None,
83        });
84        if let Some(f) = file {
85            let (se, audio) = (&mut ctx.se, &mut ctx.audio);
86            let _ = se.play_file_name(audio, f);
87        }
88        return Ok(true);
89    }
90    if matches!(name.as_str(), "SE_STOP" | "SESTOP") {
91        let _ = ctx.se.stop(None);
92        return Ok(true);
93    }
94    if matches!(name.as_str(), "SE_VOL" | "SE_VOLUME" | "SEVOLUME") {
95        let vol = args.iter().find_map(|v| match v {
96            Value::Int(x) => Some(*x),
97            _ => None,
98        });
99        if let Some(v) = vol {
100            let raw = if v <= 100 { pct_to_raw(v) } else { clamp_u8(v) };
101            let (se, audio) = (&mut ctx.se, &mut ctx.audio);
102            let _ = se.set_volume_raw(audio, raw);
103        }
104        return Ok(true);
105    }
106
107    // ---- PCM ----
108    if matches!(name.as_str(), "PCM" | "PCM_PLAY" | "PCMPLAY") {
109        let file = args.iter().find_map(|v| match v {
110            Value::Str(s) => Some(s.as_str()),
111            _ => None,
112        });
113        if let Some(f) = file {
114            let (pcm, audio) = (&mut ctx.pcm, &mut ctx.audio);
115            let _ = pcm.play_file_name(audio, f);
116        }
117        return Ok(true);
118    }
119    if matches!(name.as_str(), "PCM_STOP" | "PCMSTOP") {
120        let _ = ctx.pcm.stop(None);
121        return Ok(true);
122    }
123
124    Ok(false)
125}