|
|
@ -4,6 +4,7 @@ use wave::{WaveInfo, WaveStatus}; |
|
|
|
use crate::error::ResultCode; |
|
|
|
use crate::error::ResultCode; |
|
|
|
use crate::services::ServiceReference; |
|
|
|
use crate::services::ServiceReference; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::cell::{RefCell, RefMut}; |
|
|
|
use std::error; |
|
|
|
use std::error; |
|
|
|
use std::fmt; |
|
|
|
use std::fmt; |
|
|
|
use std::sync::Mutex; |
|
|
|
use std::sync::Mutex; |
|
|
@ -38,15 +39,20 @@ pub enum InterpolationType { |
|
|
|
#[derive(Copy, Clone, Debug)] |
|
|
|
#[derive(Copy, Clone, Debug)] |
|
|
|
pub enum NdspError { |
|
|
|
pub enum NdspError { |
|
|
|
InvalidChannel(u8), // channel id
|
|
|
|
InvalidChannel(u8), // channel id
|
|
|
|
|
|
|
|
ChannelAlreadyInUse(u8), // channel id
|
|
|
|
WaveAlreadyQueued(u8), // channel id
|
|
|
|
WaveAlreadyQueued(u8), // channel id
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct Channel(u8); |
|
|
|
pub struct Channel<'ndsp> { |
|
|
|
|
|
|
|
id: u8, |
|
|
|
|
|
|
|
_rf: RefMut<'ndsp, ()>, // we don't need to hold any data
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static NDSP_ACTIVE: Mutex<usize> = Mutex::new(0); |
|
|
|
static NDSP_ACTIVE: Mutex<usize> = Mutex::new(0); |
|
|
|
|
|
|
|
|
|
|
|
pub struct Ndsp { |
|
|
|
pub struct Ndsp { |
|
|
|
_service_handler: ServiceReference, |
|
|
|
_service_handler: ServiceReference, |
|
|
|
|
|
|
|
channel_flags: [RefCell<()>; 24], |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Ndsp { |
|
|
|
impl Ndsp { |
|
|
@ -64,7 +70,10 @@ impl Ndsp { |
|
|
|
}, |
|
|
|
}, |
|
|
|
)?; |
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
Ok(Self { _service_handler }) |
|
|
|
Ok(Self { |
|
|
|
|
|
|
|
_service_handler, |
|
|
|
|
|
|
|
channel_flags: Default::default(), |
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the representation of the specified channel.
|
|
|
|
/// Return the representation of the specified channel.
|
|
|
@ -73,10 +82,17 @@ impl Ndsp { |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// 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) -> std::result::Result<Channel, NdspError> { |
|
|
|
pub fn channel(&self, id: u8) -> std::result::Result<Channel, NdspError> { |
|
|
|
if id >= NUMBER_OF_CHANNELS { |
|
|
|
let in_bounds = self.channel_flags.get(id as usize); |
|
|
|
Err(NdspError::InvalidChannel(id)) |
|
|
|
|
|
|
|
} else { |
|
|
|
match in_bounds { |
|
|
|
Ok(Channel(id)) |
|
|
|
Some(ref_cell) => { |
|
|
|
|
|
|
|
let flag = ref_cell.try_borrow_mut(); |
|
|
|
|
|
|
|
match flag { |
|
|
|
|
|
|
|
Ok(_rf) => Ok(Channel { id, _rf }), |
|
|
|
|
|
|
|
Err(_) => Err(NdspError::ChannelAlreadyInUse(id)), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
None => Err(NdspError::InvalidChannel(id)), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -86,70 +102,67 @@ impl Ndsp { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// All channel operations are thread-safe thanks to `libctru`'s use of thread locks.
|
|
|
|
impl Channel<'_> { |
|
|
|
// As such, there is no need to hold channels to ensure correct mutability.
|
|
|
|
|
|
|
|
// As such, this struct is more of a dummy than an actually functional block.
|
|
|
|
|
|
|
|
impl Channel { |
|
|
|
|
|
|
|
/// Reset the channel
|
|
|
|
/// Reset the channel
|
|
|
|
pub fn reset(&self) { |
|
|
|
pub fn reset(&self) { |
|
|
|
unsafe { ctru_sys::ndspChnReset(self.0.into()) }; |
|
|
|
unsafe { ctru_sys::ndspChnReset(self.id.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.into()) }; |
|
|
|
unsafe { ctru_sys::ndspChnInitParams(self.id.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.into()) } |
|
|
|
unsafe { ctru_sys::ndspChnIsPlaying(self.id.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.into()) } |
|
|
|
unsafe { ctru_sys::ndspChnIsPaused(self.id.into()) } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Returns the channel's id
|
|
|
|
// Returns the channel's id
|
|
|
|
pub fn get_id(&self) -> u8 { |
|
|
|
pub fn get_id(&self) -> u8 { |
|
|
|
self.0 |
|
|
|
self.id |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 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.into()) } |
|
|
|
unsafe { ctru_sys::ndspChnGetSamplePos(self.id.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.into()) } |
|
|
|
unsafe { ctru_sys::ndspChnGetWaveBufSeq(self.id.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.into(), state) }; |
|
|
|
unsafe { ctru_sys::ndspChnSetPaused(self.id.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.into(), format as u16) }; |
|
|
|
unsafe { ctru_sys::ndspChnSetFormat(self.id.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.into(), interp_type as u32) }; |
|
|
|
unsafe { ctru_sys::ndspChnSetInterp(self.id.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.into(), mix.as_ptr().cast_mut()) } |
|
|
|
unsafe { ctru_sys::ndspChnSetMix(self.id.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.into(), rate) }; |
|
|
|
unsafe { ctru_sys::ndspChnSetRate(self.id.into(), rate) }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// `ndspChnSetAdpcmCoefs` isn't wrapped on purpose.
|
|
|
|
// `ndspChnSetAdpcmCoefs` isn't wrapped on purpose.
|
|
|
@ -158,7 +171,7 @@ impl Channel { |
|
|
|
|
|
|
|
|
|
|
|
/// 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.into()) }; |
|
|
|
unsafe { ctru_sys::ndspChnWaveBufClear(self.id.into()) }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Add a wave buffer to the channel's queue.
|
|
|
|
/// Add a wave buffer to the channel's queue.
|
|
|
@ -171,14 +184,14 @@ impl Channel { |
|
|
|
pub fn queue_wave(&self, wave: &mut WaveInfo) -> std::result::Result<(), NdspError> { |
|
|
|
pub fn queue_wave(&self, wave: &mut WaveInfo) -> std::result::Result<(), NdspError> { |
|
|
|
match wave.get_status() { |
|
|
|
match wave.get_status() { |
|
|
|
WaveStatus::Playing | WaveStatus::Queued => { |
|
|
|
WaveStatus::Playing | WaveStatus::Queued => { |
|
|
|
return Err(NdspError::WaveAlreadyQueued(self.0)) |
|
|
|
return Err(NdspError::WaveAlreadyQueued(self.id)) |
|
|
|
} |
|
|
|
} |
|
|
|
_ => (), |
|
|
|
_ => (), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
wave.set_channel(self.0); |
|
|
|
wave.set_channel(self.id); |
|
|
|
|
|
|
|
|
|
|
|
unsafe { ctru_sys::ndspChnWaveBufAdd(self.0.into(), &mut wave.raw_data) }; |
|
|
|
unsafe { ctru_sys::ndspChnWaveBufAdd(self.id.into(), &mut wave.raw_data) }; |
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
@ -186,42 +199,42 @@ impl Channel { |
|
|
|
// FILTERS
|
|
|
|
// FILTERS
|
|
|
|
|
|
|
|
|
|
|
|
pub fn iir_mono_set_enabled(&self, enable: bool) { |
|
|
|
pub fn iir_mono_set_enabled(&self, enable: bool) { |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetEnable(self.0.into(), enable) }; |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetEnable(self.id.into(), enable) }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn iir_mono_set_params_high_pass_filter(&self, cut_off_freq: f32) { |
|
|
|
pub fn iir_mono_set_params_high_pass_filter(&self, cut_off_freq: f32) { |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetParamsHighPassFilter(self.0.into(), cut_off_freq) }; |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetParamsHighPassFilter(self.id.into(), cut_off_freq) }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn iir_mono_set_params_low_pass_filter(&self, cut_off_freq: f32) { |
|
|
|
pub fn iir_mono_set_params_low_pass_filter(&self, cut_off_freq: f32) { |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetParamsLowPassFilter(self.0.into(), cut_off_freq) }; |
|
|
|
unsafe { ctru_sys::ndspChnIirMonoSetParamsLowPassFilter(self.id.into(), cut_off_freq) }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn iir_biquad_set_enabled(&self, enable: bool) { |
|
|
|
pub fn iir_biquad_set_enabled(&self, enable: bool) { |
|
|
|
unsafe { ctru_sys::ndspChnIirBiquadSetEnable(self.0.into(), enable) }; |
|
|
|
unsafe { ctru_sys::ndspChnIirBiquadSetEnable(self.id.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 { |
|
|
|
unsafe { |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsHighPassFilter(self.0.into(), cut_off_freq, quality) |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsHighPassFilter(self.id.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 { |
|
|
|
unsafe { |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsLowPassFilter(self.0.into(), cut_off_freq, quality) |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsLowPassFilter(self.id.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 { |
|
|
|
unsafe { |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsNotchFilter(self.0.into(), notch_freq, quality) |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsNotchFilter(self.id.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 { |
|
|
|
unsafe { |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsBandPassFilter(self.0.into(), mid_freq, quality) |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsBandPassFilter(self.id.into(), mid_freq, quality) |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -233,7 +246,7 @@ impl Channel { |
|
|
|
) { |
|
|
|
) { |
|
|
|
unsafe { |
|
|
|
unsafe { |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsPeakingEqualizer( |
|
|
|
ctru_sys::ndspChnIirBiquadSetParamsPeakingEqualizer( |
|
|
|
self.0.into(), |
|
|
|
self.id.into(), |
|
|
|
central_freq, |
|
|
|
central_freq, |
|
|
|
quality, |
|
|
|
quality, |
|
|
|
gain, |
|
|
|
gain, |
|
|
@ -260,6 +273,7 @@ impl fmt::Display for NdspError { |
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
match self { |
|
|
|
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::InvalidChannel(id) => write!(f, "Audio Channel with id {id} doesn't exist. Valid channels have an id between 0 and 23."), |
|
|
|
|
|
|
|
Self::ChannelAlreadyInUse(id) => write!(f, "Audio Channel with id {id} is already being used. Drop the other instance if you want to use it here."), |
|
|
|
Self::WaveAlreadyQueued(id) => write!(f, "The selected WaveInfo is already playing on channel {id}.") |
|
|
|
Self::WaveAlreadyQueued(id) => write!(f, "The selected WaveInfo is already playing on channel {id}.") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -271,6 +285,12 @@ impl error::Error for NdspError { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<'ndsp> Drop for Channel<'ndsp> { |
|
|
|
|
|
|
|
fn drop(&mut self) { |
|
|
|
|
|
|
|
self.reset(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|