use std::time::Duration;
use super::{ObjId, graphics::Argb};
use crate::bms::command::time::ObjTime;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExWavPan(i64);
impl AsRef<i64> for ExWavPan {
fn as_ref(&self) -> &i64 {
&self.0
}
}
impl ExWavPan {
#[must_use]
pub fn new(value: i64) -> Option<Self> {
(-10000..=10000).contains(&value).then_some(Self(value))
}
#[must_use]
pub const fn value(self) -> i64 {
self.0
}
#[must_use]
pub const fn default() -> Self {
Self(0)
}
}
impl TryFrom<i64> for ExWavPan {
type Error = i64;
fn try_from(value: i64) -> std::result::Result<Self, Self::Error> {
Self::new(value).ok_or_else(|| value.clamp(-10000, 10000))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExWavVolume(i64);
impl AsRef<i64> for ExWavVolume {
fn as_ref(&self) -> &i64 {
&self.0
}
}
impl ExWavVolume {
#[must_use]
pub fn new(value: i64) -> Option<Self> {
(-10000..=0).contains(&value).then_some(Self(value))
}
#[must_use]
pub const fn value(self) -> i64 {
self.0
}
#[must_use]
pub const fn default() -> Self {
Self(0)
}
}
impl TryFrom<i64> for ExWavVolume {
type Error = i64;
fn try_from(value: i64) -> std::result::Result<Self, Self::Error> {
Self::new(value).ok_or_else(|| value.clamp(-10000, 0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExWavFrequency(u64);
impl AsRef<u64> for ExWavFrequency {
fn as_ref(&self) -> &u64 {
&self.0
}
}
impl From<ExWavFrequency> for u64 {
fn from(value: ExWavFrequency) -> Self {
value.0
}
}
impl ExWavFrequency {
const MIN_FREQUENCY: u64 = 100;
const MAX_FREQUENCY: u64 = 100_000;
#[must_use]
pub const fn value(self) -> u64 {
self.0
}
#[must_use]
pub fn new(value: u64) -> Option<Self> {
(Self::MIN_FREQUENCY..=Self::MAX_FREQUENCY)
.contains(&value)
.then_some(Self(value))
}
}
impl TryFrom<u64> for ExWavFrequency {
type Error = u64;
fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
Self::new(value).ok_or_else(|| value.clamp(Self::MIN_FREQUENCY, Self::MAX_FREQUENCY))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StpEvent {
pub time: ObjTime,
pub duration: Duration,
}
#[allow(clippy::doc_markdown)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WavCmdEvent {
pub param: WavCmdParam,
pub wav_index: ObjId,
pub value: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SwBgaEvent {
pub frame_rate: u32,
pub total_time: u32,
pub line: u8,
pub loop_mode: bool,
pub argb: Argb,
pub pattern: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExtChrEvent {
pub sprite_num: i32,
pub bmp_num: i32,
pub start_x: i32,
pub start_y: i32,
pub end_x: i32,
pub end_y: i32,
pub offset_x: Option<i32>,
pub offset_y: Option<i32>,
pub abs_x: Option<i32>,
pub abs_y: Option<i32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum WavCmdParam {
Pitch,
Volume,
Time,
}
impl WavCmdParam {
#[must_use]
pub const fn to_str(self) -> &'static str {
match self {
Self::Pitch => "00",
Self::Volume => "01",
Self::Time => "02",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exwav_pan_try_from() {
assert!(ExWavPan::try_from(0).is_ok());
assert!(ExWavPan::try_from(10000).is_ok());
assert!(ExWavPan::try_from(-10000).is_ok());
assert!(ExWavPan::try_from(5000).is_ok());
assert!(ExWavPan::try_from(-5000).is_ok());
assert!(ExWavPan::try_from(10001).is_err());
assert!(ExWavPan::try_from(-10001).is_err());
assert!(ExWavPan::try_from(i64::MAX).is_err());
assert!(ExWavPan::try_from(i64::MIN).is_err());
}
#[test]
fn test_exwav_volume_try_from() {
assert!(ExWavVolume::try_from(0).is_ok());
assert!(ExWavVolume::try_from(-10000).is_ok());
assert!(ExWavVolume::try_from(-5000).is_ok());
assert!(ExWavVolume::try_from(1).is_err());
assert!(ExWavVolume::try_from(-10001).is_err());
assert!(ExWavVolume::try_from(i64::MAX).is_err());
assert!(ExWavVolume::try_from(i64::MIN).is_err());
}
#[test]
fn test_exwav_frequency_try_from() {
assert!(ExWavFrequency::try_from(100).is_ok());
assert!(ExWavFrequency::try_from(100000).is_ok());
assert!(ExWavFrequency::try_from(50000).is_ok());
assert!(ExWavFrequency::try_from(99).is_err());
assert!(ExWavFrequency::try_from(100001).is_err());
assert!(ExWavFrequency::try_from(0).is_err());
assert!(ExWavFrequency::try_from(u64::MAX).is_err());
}
#[test]
fn test_exwav_values() {
let pan = ExWavPan::try_from(5000).unwrap();
assert_eq!(pan.value(), 5000);
let volume = ExWavVolume::try_from(-5000).unwrap();
assert_eq!(volume.value(), -5000);
let frequency = ExWavFrequency::try_from(48000).unwrap();
assert_eq!(frequency.value(), 48000);
}
#[test]
fn test_exwav_defaults() {
assert_eq!(ExWavPan::default().value(), 0);
assert_eq!(ExWavVolume::default().value(), 0);
}
}