use crate::{ffi, BitFlags, Channel, MusicId};
#[must_use = "music does nothing until you call .play()"]
#[derive(Debug, Clone, Copy)]
pub struct Music {
pattern: MusicId,
fade_in_duration: u32,
channels: BitFlags<Channel>,
}
impl Music {
pub(crate) fn new(pattern: MusicId) -> Self {
Self {
pattern,
fade_in_duration: 0,
channels: BitFlags::empty(),
}
}
pub fn fade_in(mut self, duration: u32) -> Self {
self.fade_in_duration = duration;
self
}
pub fn reserve_channels(mut self, channels: impl Into<BitFlags<Channel>>) -> Self {
self.channels = channels.into();
self
}
pub fn play(self) -> Result<PlayingMusic, MusicBusy> {
let token = unsafe {
ffi::music(
self.pattern.0 as i32,
self.fade_in_duration as i32,
self.channels.bits() as i32,
0,
)
};
if token != 0 {
Ok(PlayingMusic {
token,
fade_out_duration: 0,
})
} else {
Err(MusicBusy(self))
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct MusicBusy(pub Music);
impl core::fmt::Display for MusicBusy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("a song is already playing")
}
}
impl core::error::Error for MusicBusy {}
#[derive(Debug)]
#[must_use = "dropping the handle stops the music; keep it to control playback"]
pub struct PlayingMusic {
token: i32,
fade_out_duration: u32,
}
impl PlayingMusic {
pub fn fade_out(mut self, duration: u32) -> Self {
self.fade_out_duration = duration;
self
}
pub fn stop(self) {
}
}
impl Drop for PlayingMusic {
fn drop(&mut self) {
unsafe { ffi::music(-1, self.fade_out_duration as i32, 0, self.token) };
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BitFlags;
#[test]
fn channel_bits_and_combine() {
assert_eq!(Channel::Channel0 as u8, 0b0001);
assert_eq!(Channel::Channel3 as u8, 0b1000);
let set = Channel::Channel0 | Channel::Channel2;
assert_eq!(set.bits(), 0b0101);
let one: BitFlags<Channel> = Channel::Channel1.into();
assert_eq!(one.bits(), 0b0010);
}
#[test]
fn play_returns_a_handle_on_the_native_stub() {
let m = Music::new(crate::MusicId::new(0).unwrap())
.fade_in(500)
.reserve_channels(Channel::Channel0 | Channel::Channel1);
let handle: Option<PlayingMusic> = m.play().ok();
assert!(handle.is_some(), "stub start succeeds");
handle.unwrap().fade_out(500).stop();
}
#[test]
fn music_busy_is_an_error() {
fn assert_error<E>()
where
E: core::error::Error,
{
}
assert_error::<MusicBusy>();
use core::fmt::Write as _;
let mut buf = crate::fmt::FmtBuf::<64>::new();
write!(buf, "{}", MusicBusy(Music::new(MusicId::new(0).unwrap()))).unwrap();
assert_eq!(buf.as_str(), "a song is already playing");
}
}