diff --git a/ctru-rs/examples/audio-filters.rs b/ctru-rs/examples/audio-filters.rs index a576150..e78fd56 100644 --- a/ctru-rs/examples/audio-filters.rs +++ b/ctru-rs/examples/audio-filters.rs @@ -84,8 +84,8 @@ fn main() { mix[1] = 1.0; channel_zero.set_mix(&mix); - channel_zero.queue_wave(&mut wave_info1); - channel_zero.queue_wave(&mut wave_info2); + channel_zero.queue_wave(&mut wave_info1).unwrap(); + channel_zero.queue_wave(&mut wave_info2).unwrap(); println!("\x1b[1;1HPress up/down to change tone frequency"); println!("\x1b[2;1HPress left/right to change filter"); @@ -105,7 +105,7 @@ fn main() { if keys_down.intersects(KeyPad::KEY_DOWN) { note = note.saturating_sub(1); } else if keys_down.intersects(KeyPad::KEY_UP) { - note = std::cmp::min(note + 1, NOTEFREQ.len() - 1);; + note = std::cmp::min(note + 1, NOTEFREQ.len() - 1); } let mut update_params = false; @@ -150,7 +150,7 @@ fn main() { if let WaveStatus::Done = status { fill_buffer(current.get_buffer_mut(), NOTEFREQ[note]); - channel_zero.queue_wave(current); + channel_zero.queue_wave(current).unwrap(); altern = !altern; } diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index ccaadaa..18fd175 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -51,7 +51,6 @@ pub enum Error { Libc(String), ServiceAlreadyActive, OutputAlreadyRedirected, - InvalidChannel(i32), } impl Error { @@ -98,7 +97,6 @@ impl fmt::Debug for Error { Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(), Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(), Self::OutputAlreadyRedirected => f.debug_tuple("OutputAlreadyRedirected").finish(), - Self::InvalidChannel(id) => f.debug_tuple("InvalidChannel").field(id).finish(), } } } @@ -115,7 +113,6 @@ impl fmt::Display for Error { Self::OutputAlreadyRedirected => { write!(f, "output streams are already redirected to 3dslink") } - Self::InvalidChannel(id) => write!(f, "Audio Channel with id {id} doesn't exist"), } } } diff --git a/ctru-rs/src/services/ndsp/mod.rs b/ctru-rs/src/services/ndsp/mod.rs index e1b10e1..8761978 100644 --- a/ctru-rs/src/services/ndsp/mod.rs +++ b/ctru-rs/src/services/ndsp/mod.rs @@ -4,6 +4,8 @@ use wave::{WaveInfo, WaveStatus}; use crate::error::ResultCode; use crate::services::ServiceReference; +use std::error; +use std::fmt; use std::sync::Mutex; const NUMBER_OF_CHANNELS: u8 = 24; @@ -34,7 +36,13 @@ pub enum InterpolationType { None = ctru_sys::NDSP_INTERP_NONE, } -pub struct Channel(i32); +#[derive(Copy, Clone, Debug)] +pub enum NdspError { + InvalidChannel(u8), // channel id + WaveAlreadyQueued(u8), // channel id +} + +pub struct Channel(u8); static NDSP_ACTIVE: Mutex = Mutex::new(0); @@ -65,12 +73,12 @@ impl Ndsp { /// # Errors /// /// An error will be returned if the channel id is not between 0 and 23. - pub fn channel(&self, id: u8) -> crate::Result { - if id >= NUMBER_OF_CHANNELS as u8 { - return Err(crate::Error::InvalidChannel(id.into())); + pub fn channel(&self, id: u8) -> std::result::Result { + if id >= NUMBER_OF_CHANNELS { + Err(NdspError::InvalidChannel(id)) + } else { + Ok(Channel(id)) } - - Ok(Channel(id.into())) } /// Set the audio output mode. Defaults to `OutputMode::Stereo`. @@ -85,66 +93,66 @@ impl Ndsp { impl Channel { /// Reset the channel pub fn reset(&self) { - unsafe { ctru_sys::ndspChnReset(self.0) }; + unsafe { ctru_sys::ndspChnReset(self.0.into()) }; } /// Initialize the channel's parameters pub fn init_parameters(&self) { - unsafe { ctru_sys::ndspChnInitParams(self.0) }; + unsafe { ctru_sys::ndspChnInitParams(self.0.into()) }; } /// Returns whether the channel is playing any audio. pub fn is_playing(&self) -> bool { - unsafe { ctru_sys::ndspChnIsPlaying(self.0) } + unsafe { ctru_sys::ndspChnIsPlaying(self.0.into()) } } /// Returns whether the channel's playback is currently paused. pub fn is_paused(&self) -> bool { - unsafe { ctru_sys::ndspChnIsPaused(self.0) } + unsafe { ctru_sys::ndspChnIsPaused(self.0.into()) } } /// Returns the channel's current sample's position. pub fn get_sample_position(&self) -> u32 { - unsafe { ctru_sys::ndspChnGetSamplePos(self.0) } + unsafe { ctru_sys::ndspChnGetSamplePos(self.0.into()) } } /// Returns the channel's current wave sequence's id. pub fn get_wave_sequence_id(&self) -> u16 { - unsafe { ctru_sys::ndspChnGetWaveBufSeq(self.0) } + unsafe { ctru_sys::ndspChnGetWaveBufSeq(self.0.into()) } } /// Pause or un-pause the channel's playback. pub fn set_paused(&self, state: bool) { - unsafe { ctru_sys::ndspChnSetPaused(self.0, state) }; + unsafe { ctru_sys::ndspChnSetPaused(self.0.into(), state) }; } /// Set the channel's output format. /// Change this setting based on the used sample's format. pub fn set_format(&self, format: AudioFormat) { - unsafe { ctru_sys::ndspChnSetFormat(self.0, format as u16) }; + unsafe { ctru_sys::ndspChnSetFormat(self.0.into(), format as u16) }; } /// Set the channel's interpolation mode. pub fn set_interpolation(&self, interp_type: InterpolationType) { - unsafe { ctru_sys::ndspChnSetInterp(self.0, interp_type as u32) }; + unsafe { ctru_sys::ndspChnSetInterp(self.0.into(), interp_type as u32) }; } /// Set the channel's volume mix. /// Docs about the buffer usage: https://libctru.devkitpro.org/channel_8h.html#a30eb26f1972cc3ec28370263796c0444 pub fn set_mix(&self, mix: &[f32; 12]) { - unsafe { ctru_sys::ndspChnSetMix(self.0, mix.as_ptr().cast_mut()) } + unsafe { ctru_sys::ndspChnSetMix(self.0.into(), mix.as_ptr().cast_mut()) } } /// Set the channel's rate of sampling. pub fn set_sample_rate(&self, rate: f32) { - unsafe { ctru_sys::ndspChnSetRate(self.0, rate) }; + unsafe { ctru_sys::ndspChnSetRate(self.0.into(), rate) }; } // TODO: find a way to wrap `ndspChnSetAdpcmCoefs` /// Clear the wave buffer queue and stop playback. pub fn clear_queue(&self) { - unsafe { ctru_sys::ndspChnWaveBufClear(self.0) }; + unsafe { ctru_sys::ndspChnWaveBufClear(self.0.into()) }; } /// Add a wave buffer to the channel's queue. @@ -154,16 +162,19 @@ impl Channel { /// /// `libctru` expects the user to manually keep the info data (in this case [WaveInfo]) alive during playback. /// To ensure safety, checks within [WaveInfo] will clear the whole channel queue if any queued [WaveInfo] is dropped prematurely. - pub fn queue_wave(&self, wave: &mut WaveInfo) { - // TODO: Return an error for already queued/used WaveInfos. + pub fn queue_wave(&self, wave: &mut WaveInfo) -> std::result::Result<(), NdspError> { match wave.get_status() { - WaveStatus::Playing | WaveStatus::Queued => return, + WaveStatus::Playing | WaveStatus::Queued => { + return Err(NdspError::WaveAlreadyQueued(self.0)) + } _ => (), } wave.set_channel(self.0); - unsafe { ctru_sys::ndspChnWaveBufAdd(self.0, &mut wave.raw_data) }; + unsafe { ctru_sys::ndspChnWaveBufAdd(self.0.into(), &mut wave.raw_data) }; + + Ok(()) } // FILTERS @@ -171,23 +182,31 @@ impl Channel { // TODO: Add Mono filters (and maybe setup the filter functions in a better way) pub fn iir_biquad_set_enabled(&self, enable: bool) { - unsafe { ctru_sys::ndspChnIirBiquadSetEnable(self.0, enable) }; + unsafe { ctru_sys::ndspChnIirBiquadSetEnable(self.0.into(), enable) }; } pub fn iir_biquad_set_params_high_pass_filter(&self, cut_off_freq: f32, quality: f32) { - unsafe { ctru_sys::ndspChnIirBiquadSetParamsHighPassFilter(self.0, cut_off_freq, quality) }; + unsafe { + ctru_sys::ndspChnIirBiquadSetParamsHighPassFilter(self.0.into(), cut_off_freq, quality) + }; } pub fn iir_biquad_set_params_low_pass_filter(&self, cut_off_freq: f32, quality: f32) { - unsafe { ctru_sys::ndspChnIirBiquadSetParamsLowPassFilter(self.0, cut_off_freq, quality) }; + unsafe { + ctru_sys::ndspChnIirBiquadSetParamsLowPassFilter(self.0.into(), cut_off_freq, quality) + }; } pub fn iir_biquad_set_params_notch_filter(&self, notch_freq: f32, quality: f32) { - unsafe { ctru_sys::ndspChnIirBiquadSetParamsNotchFilter(self.0, notch_freq, quality) }; + unsafe { + ctru_sys::ndspChnIirBiquadSetParamsNotchFilter(self.0.into(), notch_freq, quality) + }; } pub fn iir_biquad_set_params_band_pass_filter(&self, mid_freq: f32, quality: f32) { - unsafe { ctru_sys::ndspChnIirBiquadSetParamsBandPassFilter(self.0, mid_freq, quality) }; + unsafe { + ctru_sys::ndspChnIirBiquadSetParamsBandPassFilter(self.0.into(), mid_freq, quality) + }; } pub fn iir_biquad_set_params_peaking_equalizer( @@ -197,7 +216,12 @@ impl Channel { gain: f32, ) { unsafe { - ctru_sys::ndspChnIirBiquadSetParamsPeakingEqualizer(self.0, central_freq, quality, gain) + ctru_sys::ndspChnIirBiquadSetParamsPeakingEqualizer( + self.0.into(), + central_freq, + quality, + gain, + ) }; } } @@ -216,6 +240,21 @@ impl AudioFormat { } } +impl fmt::Display for NdspError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InvalidChannel(id) => write!(f, "Audio Channel with id {id} doesn't exist. Valid channels have an id between 0 and 23."), + Self::WaveAlreadyQueued(id) => write!(f, "The selected WaveInfo is already playing on channel {id}.") + } + } +} + +impl error::Error for NdspError { + fn description(&self) -> &str { + "Error caused within the NDSP service wrapper" + } +} + impl Drop for Ndsp { fn drop(&mut self) { for i in 0..NUMBER_OF_CHANNELS { diff --git a/ctru-rs/src/services/ndsp/wave.rs b/ctru-rs/src/services/ndsp/wave.rs index e565df4..349372f 100644 --- a/ctru-rs/src/services/ndsp/wave.rs +++ b/ctru-rs/src/services/ndsp/wave.rs @@ -78,8 +78,8 @@ impl WaveInfo { self.audio_format } - pub(crate) fn set_channel(&mut self, id: i32) { - self.played_on_channel = Some(id as u8) + pub(crate) fn set_channel(&mut self, id: u8) { + self.played_on_channel = Some(id) } }