Browse Source

NdspError enum and better Channel

pull/83/head
Andrea Ciliberti 2 years ago
parent
commit
84df959603
  1. 8
      ctru-rs/examples/audio-filters.rs
  2. 3
      ctru-rs/src/error.rs
  3. 95
      ctru-rs/src/services/ndsp/mod.rs
  4. 4
      ctru-rs/src/services/ndsp/wave.rs

8
ctru-rs/examples/audio-filters.rs

@ -84,8 +84,8 @@ fn main() {
mix[1] = 1.0; mix[1] = 1.0;
channel_zero.set_mix(&mix); channel_zero.set_mix(&mix);
channel_zero.queue_wave(&mut wave_info1); channel_zero.queue_wave(&mut wave_info1).unwrap();
channel_zero.queue_wave(&mut wave_info2); channel_zero.queue_wave(&mut wave_info2).unwrap();
println!("\x1b[1;1HPress up/down to change tone frequency"); println!("\x1b[1;1HPress up/down to change tone frequency");
println!("\x1b[2;1HPress left/right to change filter"); println!("\x1b[2;1HPress left/right to change filter");
@ -105,7 +105,7 @@ fn main() {
if keys_down.intersects(KeyPad::KEY_DOWN) { if keys_down.intersects(KeyPad::KEY_DOWN) {
note = note.saturating_sub(1); note = note.saturating_sub(1);
} else if keys_down.intersects(KeyPad::KEY_UP) { } 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; let mut update_params = false;
@ -150,7 +150,7 @@ fn main() {
if let WaveStatus::Done = status { if let WaveStatus::Done = status {
fill_buffer(current.get_buffer_mut(), NOTEFREQ[note]); fill_buffer(current.get_buffer_mut(), NOTEFREQ[note]);
channel_zero.queue_wave(current); channel_zero.queue_wave(current).unwrap();
altern = !altern; altern = !altern;
} }

3
ctru-rs/src/error.rs

@ -51,7 +51,6 @@ pub enum Error {
Libc(String), Libc(String),
ServiceAlreadyActive, ServiceAlreadyActive,
OutputAlreadyRedirected, OutputAlreadyRedirected,
InvalidChannel(i32),
} }
impl Error { impl Error {
@ -98,7 +97,6 @@ impl fmt::Debug for Error {
Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(), Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(),
Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(), Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
Self::OutputAlreadyRedirected => f.debug_tuple("OutputAlreadyRedirected").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 => { Self::OutputAlreadyRedirected => {
write!(f, "output streams are already redirected to 3dslink") write!(f, "output streams are already redirected to 3dslink")
} }
Self::InvalidChannel(id) => write!(f, "Audio Channel with id {id} doesn't exist"),
} }
} }
} }

95
ctru-rs/src/services/ndsp/mod.rs

@ -4,6 +4,8 @@ use wave::{WaveInfo, WaveStatus};
use crate::error::ResultCode; use crate::error::ResultCode;
use crate::services::ServiceReference; use crate::services::ServiceReference;
use std::error;
use std::fmt;
use std::sync::Mutex; use std::sync::Mutex;
const NUMBER_OF_CHANNELS: u8 = 24; const NUMBER_OF_CHANNELS: u8 = 24;
@ -34,7 +36,13 @@ pub enum InterpolationType {
None = ctru_sys::NDSP_INTERP_NONE, 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<usize> = Mutex::new(0); static NDSP_ACTIVE: Mutex<usize> = Mutex::new(0);
@ -65,12 +73,12 @@ impl Ndsp {
/// # Errors /// # Errors
/// ///
/// An error will be returned if the channel id is not between 0 and 23. /// An error will be returned if the channel id is not between 0 and 23.
pub fn channel(&self, id: u8) -> crate::Result<Channel> { pub fn channel(&self, id: u8) -> std::result::Result<Channel, NdspError> {
if id >= NUMBER_OF_CHANNELS as u8 { if id >= NUMBER_OF_CHANNELS {
return Err(crate::Error::InvalidChannel(id.into())); Err(NdspError::InvalidChannel(id))
} else {
Ok(Channel(id))
} }
Ok(Channel(id.into()))
} }
/// Set the audio output mode. Defaults to `OutputMode::Stereo`. /// Set the audio output mode. Defaults to `OutputMode::Stereo`.
@ -85,66 +93,66 @@ impl Ndsp {
impl Channel { impl Channel {
/// Reset the channel /// Reset the channel
pub fn reset(&self) { pub fn reset(&self) {
unsafe { ctru_sys::ndspChnReset(self.0) }; unsafe { ctru_sys::ndspChnReset(self.0.into()) };
} }
/// Initialize the channel's parameters /// Initialize the channel's parameters
pub fn init_parameters(&self) { 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. /// Returns whether the channel is playing any audio.
pub fn is_playing(&self) -> bool { 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. /// Returns whether the channel's playback is currently paused.
pub fn is_paused(&self) -> bool { 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. /// Returns the channel's current sample's position.
pub fn get_sample_position(&self) -> u32 { 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. /// Returns the channel's current wave sequence's id.
pub fn get_wave_sequence_id(&self) -> u16 { 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. /// Pause or un-pause the channel's playback.
pub fn set_paused(&self, state: bool) { 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. /// Set the channel's output format.
/// Change this setting based on the used sample's format. /// Change this setting based on the used sample's format.
pub fn set_format(&self, format: AudioFormat) { 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. /// Set the channel's interpolation mode.
pub fn set_interpolation(&self, interp_type: InterpolationType) { 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. /// Set the channel's volume mix.
/// Docs about the buffer usage: https://libctru.devkitpro.org/channel_8h.html#a30eb26f1972cc3ec28370263796c0444 /// Docs about the buffer usage: https://libctru.devkitpro.org/channel_8h.html#a30eb26f1972cc3ec28370263796c0444
pub fn set_mix(&self, mix: &[f32; 12]) { 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. /// Set the channel's rate of sampling.
pub fn set_sample_rate(&self, rate: f32) { 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` // TODO: find a way to wrap `ndspChnSetAdpcmCoefs`
/// Clear the wave buffer queue and stop playback. /// Clear the wave buffer queue and stop playback.
pub fn clear_queue(&self) { 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. /// 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. /// `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. /// 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) { pub fn queue_wave(&self, wave: &mut WaveInfo) -> std::result::Result<(), NdspError> {
// TODO: Return an error for already queued/used WaveInfos.
match wave.get_status() { match wave.get_status() {
WaveStatus::Playing | WaveStatus::Queued => return, WaveStatus::Playing | WaveStatus::Queued => {
return Err(NdspError::WaveAlreadyQueued(self.0))
}
_ => (), _ => (),
} }
wave.set_channel(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 // FILTERS
@ -171,23 +182,31 @@ impl Channel {
// TODO: Add Mono filters (and maybe setup the filter functions in a better way) // TODO: Add Mono filters (and maybe setup the filter functions in a better way)
pub fn iir_biquad_set_enabled(&self, enable: bool) { 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) { 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) { 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) { 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) { 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( pub fn iir_biquad_set_params_peaking_equalizer(
@ -197,7 +216,12 @@ impl Channel {
gain: f32, gain: f32,
) { ) {
unsafe { 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 { impl Drop for Ndsp {
fn drop(&mut self) { fn drop(&mut self) {
for i in 0..NUMBER_OF_CHANNELS { for i in 0..NUMBER_OF_CHANNELS {

4
ctru-rs/src/services/ndsp/wave.rs

@ -78,8 +78,8 @@ impl WaveInfo {
self.audio_format self.audio_format
} }
pub(crate) fn set_channel(&mut self, id: i32) { pub(crate) fn set_channel(&mut self, id: u8) {
self.played_on_channel = Some(id as u8) self.played_on_channel = Some(id)
} }
} }

Loading…
Cancel
Save