bms_rs/bms/command/
channel.rs

1//! Definitions of channel command argument data.
2//!
3//! For more details, please see [`Channel`] enum and its related types.
4//! For documents of modes, please see [BMS command memo#KEYMAP Table](https://hitkey.bms.ms/cmds.htm#KEYMAP-TABLE)
5//!
6//! For converting key/channel between different modes, please see [`ModeKeyChannel`] enum and [`convert_key_channel_between`] function.
7
8use crate::command::{base62_to_byte, char_to_base62};
9use std::str::FromStr;
10use thiserror::Error;
11
12use self::mapper::KeyLayoutMapper;
13
14pub mod converter;
15pub mod mapper;
16
17/// The channel, or lane, where the note will be on.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[non_exhaustive]
21pub enum Channel {
22    /// The BGA channel.
23    BgaBase,
24    /// The BGA channel but overlay to [`Channel::BgaBase`] channel.
25    BgaLayer,
26    /// The POOR BGA channel.
27    BgaPoor,
28    /// For the note which will be auto-played.
29    Bgm,
30    /// For the bpm change by an [`u8`] integer.
31    BpmChangeU8,
32    /// For the bpm change object.
33    BpmChange,
34    /// For the note which the user can interact.
35    Note {
36        /// The channel ID from the BMS file.
37        channel_id: NoteChannelId,
38    },
39    /// For the section length change object.
40    SectionLen,
41    /// For the stop object.
42    Stop,
43    /// For the scroll speed change object.
44    Scroll,
45    /// For the note spacing change object.
46    Speed,
47    /// For the video seek object. `#SEEKxx n`
48    Seek,
49    /// For the BGA LAYER2 object. `#BMPxx` (LAYER2 is layered over LAYER)
50    BgaLayer2,
51    /// For the opacity of BGA BASE. transparent « [01-FF] » opaque
52    BgaBaseOpacity,
53    /// For the opacity of BGA LAYER. transparent « [01-FF] » opaque
54    BgaLayerOpacity,
55    /// For the opacity of BGA LAYER2. transparent « [01-FF] » opaque
56    BgaLayer2Opacity,
57    /// For the opacity of BGA POOR. transparent « [01-FF] » opaque
58    BgaPoorOpacity,
59    /// For the BGM volume. min 1 « [01-FF] » max 255 (= original sound)
60    BgmVolume,
61    /// For the KEY volume. min 1 « [01-FF] » max 255 (= original sound)
62    KeyVolume,
63    /// For the TEXT object. `#TEXTxx "string"`
64    Text,
65    /// For the JUDGE object. `#EXRANKxx n` (100 corresponds to RANK:NORMAL. integer or decimal fraction)
66    Judge,
67    /// For the BGA BASE aRGB. `#ARGBxx a,r,g,b` (each [0-255])
68    BgaBaseArgb,
69    /// For the BGA LAYER aRGB. `#ARGBxx`
70    BgaLayerArgb,
71    /// For the BGA LAYER2 aRGB. `#ARGBxx`
72    BgaLayer2Argb,
73    /// For the BGA POOR aRGB. `#ARGBxx`
74    BgaPoorArgb,
75    /// For the BGA KEYBOUND. `#SWBGAxx`
76    BgaKeybound,
77    /// For the OPTION. `#CHANGEOPTIONxx` (multiline)
78    OptionChange,
79}
80
81impl std::fmt::Display for Channel {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        write!(f, "Channel: ")?;
84        match self {
85            Self::BgaBase => write!(f, "BGA"),
86            Self::BgaLayer => write!(f, "BGA_LAYER"),
87            Self::BgaPoor => write!(f, "BGA_POOR"),
88            Self::Bgm => write!(f, "BGM"),
89            Self::BpmChangeU8 => write!(f, "BPM_CHANGE_U8"),
90            Self::BpmChange => write!(f, "BPM_CHANGE"),
91            Self::Note { .. } => write!(f, "NOTE"),
92            Self::SectionLen => write!(f, "SECTION_LEN"),
93            Self::Stop => write!(f, "STOP"),
94            Self::Scroll => write!(f, "SCROLL"),
95            Self::Speed => write!(f, "SPEED"),
96
97            Self::Seek => write!(f, "SEEK"),
98            Self::BgaLayer2 => write!(f, "BGA_LAYER2"),
99
100            Self::BgaBaseOpacity => write!(f, "BGA_BASE_OPACITY"),
101
102            Self::BgaLayerOpacity => write!(f, "BGA_LAYER_OPACITY"),
103
104            Self::BgaLayer2Opacity => write!(f, "BGA_LAYER2_OPACITY"),
105
106            Self::BgaPoorOpacity => write!(f, "BGA_POOR_OPACITY"),
107            Self::BgmVolume => write!(f, "BGM_VOLUME"),
108            Self::KeyVolume => write!(f, "KEY_VOLUME"),
109            Self::Text => write!(f, "TEXT"),
110            Self::Judge => write!(f, "JUDGE"),
111
112            Self::BgaBaseArgb => write!(f, "BGA_BASE_ARGB"),
113
114            Self::BgaLayerArgb => write!(f, "BGA_LAYER_ARGB"),
115
116            Self::BgaLayer2Argb => write!(f, "BGA_LAYER2_ARGB"),
117
118            Self::BgaPoorArgb => write!(f, "BGA_POOR_ARGB"),
119
120            Self::BgaKeybound => write!(f, "BGA_KEYBOUND"),
121
122            Self::OptionChange => write!(f, "CHANGE_OPTION"),
123        }
124    }
125}
126
127/// A kind of the note.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130pub enum NoteKind {
131    /// A normal note can be seen by the user.
132    Visible,
133    /// A invisible note cannot be played by the user.
134    Invisible,
135    /// A long-press note (LN), requires the user to hold pressing the key.
136    Long,
137    /// A landmine note that treated as POOR judgement when
138    Landmine,
139}
140
141impl NoteKind {
142    /// Returns whether the note is a displayable.
143    #[must_use]
144    pub const fn is_displayable(self) -> bool {
145        !matches!(self, Self::Invisible)
146    }
147
148    /// Returns whether the note is a playable.
149    #[must_use]
150    pub const fn is_playable(self) -> bool {
151        matches!(self, Self::Visible | Self::Long)
152    }
153
154    /// Returns whether the note is a long-press note.
155    #[must_use]
156    pub const fn is_long(self) -> bool {
157        matches!(self, Self::Long)
158    }
159}
160
161/// A side of the player.
162#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub enum PlayerSide {
165    /// The player 1 side.
166    #[default]
167    Player1,
168    /// The player 2 side.
169    Player2,
170}
171
172/// Error type for parsing [`ChannelId`] from string.
173#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
174pub enum ChannelIdParseError {
175    /// The channel id must be exactly 2 ascii characters, got `{0}`.
176    #[error("channel id must be exactly 2 ascii characters, got `{0}`")]
177    ExpectedTwoAsciiChars(String),
178    /// The channel id must be an alpha numeric to parse as base 62, got {0}.
179    #[error("channel id must be an alpha numeric to parse as base 62, got {0}")]
180    InvalidAsBase62(String),
181}
182
183/// A channel ID of notes playing sound.
184#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct NoteChannelId([u8; 2]);
187
188impl std::fmt::Debug for NoteChannelId {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        f.debug_tuple("ChannelId")
191            .field(&format!("{}{}", self.0[0] as char, self.0[1] as char))
192            .finish()
193    }
194}
195
196impl std::fmt::Display for NoteChannelId {
197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        write!(f, "{}{}", self.0[0] as char, self.0[1] as char)
199    }
200}
201
202impl TryFrom<[char; 2]> for NoteChannelId {
203    type Error = [char; 2];
204    fn try_from(value: [char; 2]) -> core::result::Result<Self, Self::Error> {
205        Ok(Self([
206            char_to_base62(value[0]).ok_or(value)?,
207            char_to_base62(value[1]).ok_or(value)?,
208        ]))
209    }
210}
211
212impl TryFrom<[u8; 2]> for NoteChannelId {
213    type Error = [u8; 2];
214    fn try_from(value: [u8; 2]) -> core::result::Result<Self, Self::Error> {
215        <Self as TryFrom<[char; 2]>>::try_from([value[0] as char, value[1] as char])
216            .map_err(|_| value)
217    }
218}
219
220impl FromStr for NoteChannelId {
221    type Err = ChannelIdParseError;
222    fn from_str(s: &str) -> Result<Self, Self::Err> {
223        if s.len() != 2 {
224            return Err(ChannelIdParseError::ExpectedTwoAsciiChars(s.to_string()));
225        }
226        let mut chars = s.bytes();
227        let [Some(ch1), Some(ch2), None] = [chars.next(), chars.next(), chars.next()] else {
228            return Err(ChannelIdParseError::ExpectedTwoAsciiChars(s.to_string()));
229        };
230        Self::try_from([ch1, ch2]).map_err(|_| ChannelIdParseError::InvalidAsBase62(s.to_string()))
231    }
232}
233
234impl From<NoteChannelId> for u16 {
235    fn from(value: NoteChannelId) -> Self {
236        base62_to_byte(value.0[0]) as u16 * 62 + base62_to_byte(value.0[1]) as u16
237    }
238}
239
240impl From<NoteChannelId> for u32 {
241    fn from(value: NoteChannelId) -> Self {
242        Into::<u16>::into(value) as u32
243    }
244}
245
246impl From<NoteChannelId> for u64 {
247    fn from(value: NoteChannelId) -> Self {
248        Into::<u16>::into(value) as u64
249    }
250}
251
252impl NoteChannelId {
253    /// Converts the channel id into an `u16` value.
254    #[must_use]
255    pub fn as_u16(self) -> u16 {
256        self.into()
257    }
258
259    /// Gets a bgm channel ID.
260    #[must_use]
261    pub const fn bgm() -> Self {
262        Self([b'0', b'1'])
263    }
264
265    /// Converts the channel into a key mapping.
266    #[must_use]
267    pub fn try_into_map<T: KeyLayoutMapper>(self) -> Option<T> {
268        T::from_channel_id(self)
269    }
270}
271
272impl TryFrom<NoteChannelId> for Channel {
273    type Error = NoteChannelId;
274
275    fn try_from(channel_id: NoteChannelId) -> Result<Self, Self::Error> {
276        match channel_id.0 {
277            [b'0', b'1'] => Ok(Channel::Bgm),
278            [b'0', b'2'] => Ok(Channel::SectionLen),
279            [b'0', b'3'] => Ok(Channel::BpmChangeU8),
280            [b'0', b'4'] => Ok(Channel::BgaBase),
281
282            [b'0', b'5'] => Ok(Channel::Seek),
283            [b'0', b'6'] => Ok(Channel::BgaPoor),
284            [b'0', b'7'] => Ok(Channel::BgaLayer),
285            [b'0', b'8'] => Ok(Channel::BpmChange),
286            [b'0', b'9'] => Ok(Channel::Stop),
287            [b'0', b'A'] => Ok(Channel::BgaLayer2),
288
289            [b'0', b'B'] => Ok(Channel::BgaBaseOpacity),
290
291            [b'0', b'C'] => Ok(Channel::BgaLayerOpacity),
292
293            [b'0', b'D'] => Ok(Channel::BgaLayer2Opacity),
294
295            [b'0', b'E'] => Ok(Channel::BgaPoorOpacity),
296            [b'9', b'7'] => Ok(Channel::BgmVolume),
297            [b'9', b'8'] => Ok(Channel::KeyVolume),
298            [b'9', b'9'] => Ok(Channel::Text),
299            [b'A', b'0'] => Ok(Channel::Judge),
300
301            [b'A', b'1'] => Ok(Channel::BgaBaseArgb),
302
303            [b'A', b'2'] => Ok(Channel::BgaLayerArgb),
304
305            [b'A', b'3'] => Ok(Channel::BgaLayer2Argb),
306
307            [b'A', b'4'] => Ok(Channel::BgaPoorArgb),
308
309            [b'A', b'5'] => Ok(Channel::BgaKeybound),
310
311            [b'A', b'6'] => Ok(Channel::OptionChange),
312            [b'S', b'C'] => Ok(Channel::Scroll),
313            [b'S', b'P'] => Ok(Channel::Speed),
314            _ => Err(channel_id),
315        }
316    }
317}
318
319impl From<Channel> for NoteChannelId {
320    fn from(channel: Channel) -> Self {
321        match channel {
322            Channel::Bgm => Self([b'0', b'1']),
323            Channel::SectionLen => Self([b'0', b'2']),
324            Channel::BpmChangeU8 => Self([b'0', b'3']),
325            Channel::BgaBase => Self([b'0', b'4']),
326
327            Channel::Seek => Self([b'0', b'5']),
328            Channel::BgaPoor => Self([b'0', b'6']),
329            Channel::BgaLayer => Self([b'0', b'7']),
330            Channel::BpmChange => Self([b'0', b'8']),
331            Channel::Stop => Self([b'0', b'9']),
332            Channel::BgaLayer2 => Self([b'0', b'A']),
333
334            Channel::BgaBaseOpacity => Self([b'0', b'B']),
335
336            Channel::BgaLayerOpacity => Self([b'0', b'C']),
337
338            Channel::BgaLayer2Opacity => Self([b'0', b'D']),
339
340            Channel::BgaPoorOpacity => Self([b'0', b'E']),
341            Channel::BgmVolume => Self([b'9', b'7']),
342            Channel::KeyVolume => Self([b'9', b'8']),
343            Channel::Text => Self([b'9', b'9']),
344            Channel::Judge => Self([b'A', b'0']),
345
346            Channel::BgaBaseArgb => Self([b'A', b'1']),
347
348            Channel::BgaLayerArgb => Self([b'A', b'2']),
349
350            Channel::BgaLayer2Argb => Self([b'A', b'3']),
351
352            Channel::BgaPoorArgb => Self([b'A', b'4']),
353
354            Channel::BgaKeybound => Self([b'A', b'5']),
355
356            Channel::OptionChange => Self([b'A', b'6']),
357            Channel::Scroll => Self([b'S', b'C']),
358            Channel::Speed => Self([b'S', b'P']),
359            Channel::Note { channel_id } => channel_id,
360        }
361    }
362}
363
364/// A key of the controller or keyboard.
365///
366/// - Beat 5K/7K/10K/14K:
367/// ```text
368/// |---------|----------------------|
369/// |         |   [K2]  [K4]  [K6]   |
370/// |(Scratch)|[K1]  [K3]  [K5]  [K7]|
371/// |---------|----------------------|
372/// ```
373///
374/// - PMS:
375/// ```text
376/// |----------------------------|
377/// |   [K2]  [K4]  [K6]  [K8]   |
378/// |[K1]  [K3]  [K5]  [K7]  [K9]|
379/// |----------------------------|
380/// ```
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[non_exhaustive]
384pub enum Key {
385    /// The keys for the controller.
386    Key(u8),
387    /// The scratch disk.
388    Scratch(u8),
389    /// The foot pedal.
390    FootPedal,
391    /// The zone that the user can scratch disk freely.
392    /// `17` in BMS-type Player1.
393    FreeZone,
394}
395
396impl Key {
397    /// Returns whether the key expected a piano keyboard.
398    #[must_use]
399    pub const fn is_keyxx(&self) -> bool {
400        matches!(self, Self::Key(_))
401    }
402
403    /// Returns the key number if it's a Key variant.
404    #[must_use]
405    pub const fn key_number(&self) -> Option<u8> {
406        match self {
407            Self::Key(n) => Some(*n),
408            _ => None,
409        }
410    }
411
412    /// Returns the scratch number if it's a Scratch variant.
413    #[must_use]
414    pub const fn scratch_number(&self) -> Option<u8> {
415        match self {
416            Self::Scratch(n) => Some(*n),
417            _ => None,
418        }
419    }
420
421    /// Creates a Key variant with the given number.
422    #[must_use]
423    pub const fn new_key(n: u8) -> Self {
424        Self::Key(n)
425    }
426
427    /// Creates a Scratch variant with the given number.
428    #[must_use]
429    pub const fn new_scratch(n: u8) -> Self {
430        Self::Scratch(n)
431    }
432}
433
434/// Reads a channel from a string.
435///
436/// For general part, please call this function when using other functions.
437fn read_channel_general(channel: &str) -> Option<Channel> {
438    let channel_id = channel.parse::<NoteChannelId>().ok()?;
439    Channel::try_from(channel_id).ok()
440}
441
442/// Reads a channel from a string. (Generic channel reader)
443#[must_use]
444pub fn read_channel(channel: &str) -> Option<Channel> {
445    if let Some(channel) = read_channel_general(channel) {
446        return Some(channel);
447    }
448    let channel_id = channel.parse::<NoteChannelId>().ok()?;
449    Some(Channel::Note { channel_id })
450}