|
|
@ -5,7 +5,7 @@ use std::f32::consts::PI; |
|
|
|
use ctru::linear::LinearAllocator; |
|
|
|
use ctru::linear::LinearAllocator; |
|
|
|
use ctru::prelude::*; |
|
|
|
use ctru::prelude::*; |
|
|
|
use ctru::services::ndsp::{ |
|
|
|
use ctru::services::ndsp::{ |
|
|
|
wave::{WaveBuffer, WaveInfo}, |
|
|
|
wave::{WaveBuffer, WaveInfo, WaveStatus}, |
|
|
|
AudioFormat, InterpolationType, Ndsp, OutputMode, |
|
|
|
AudioFormat, InterpolationType, Ndsp, OutputMode, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -19,15 +19,20 @@ const NOTEFREQ: [u32; 7] = [220, 440, 880, 1760, 3520, 7040, 14080]; |
|
|
|
|
|
|
|
|
|
|
|
// audioBuffer is stereo PCM16
|
|
|
|
// audioBuffer is stereo PCM16
|
|
|
|
fn fill_buffer(audioData: &mut [u8], frequency: u32) { |
|
|
|
fn fill_buffer(audioData: &mut [u8], frequency: u32) { |
|
|
|
let formatted_data: Vec<i16> = audioData.chunks_exact(2).map(|s| i16::from_le_bytes(s.try_into().unwrap())).collect(); |
|
|
|
let formatted_data = audioData.chunks_exact_mut(2); |
|
|
|
|
|
|
|
|
|
|
|
for i in 0..audioData.len() { |
|
|
|
let mut i = 0; |
|
|
|
// This is a simple sine wave, with a frequency of `frequency` Hz, and an amplitude 30% of maximum.
|
|
|
|
for chunk in formatted_data { |
|
|
|
let sample: i16 = (0.3 * i16::MAX as f32 * (frequency as f32 * (2f32 * PI) * (i / SAMPLE_RATE as usize) as f32).sin()) as i16; |
|
|
|
// This is a simple sine wave, with a frequency of `frequency` Hz, and an amplitude 30% of maximum.
|
|
|
|
|
|
|
|
let sample: i16 = (0.3 * i16::MAX as f32 * (frequency as f32 * (2f32 * PI) * (i as f32 / SAMPLE_RATE as f32)).sin()) as i16; |
|
|
|
|
|
|
|
|
|
|
|
// Stereo samples are interleaved: left and right channels.
|
|
|
|
// This operation is safe, since we are writing to a slice of exactly 16 bits
|
|
|
|
formatted_data[i] = (sample << 16) | (sample & 0xffff); |
|
|
|
let chunk_ptr: &mut i16 = unsafe { std::mem::transmute(chunk.as_mut_ptr()) }; |
|
|
|
} |
|
|
|
// Stereo samples are interleaved: left and right channels.
|
|
|
|
|
|
|
|
*chunk_ptr = (sample << 16) | (sample & 0xffff); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i += 1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn main() { |
|
|
|
fn main() { |
|
|
@ -39,19 +44,16 @@ fn main() { |
|
|
|
|
|
|
|
|
|
|
|
println!("libctru filtered streamed audio\n"); |
|
|
|
println!("libctru filtered streamed audio\n"); |
|
|
|
|
|
|
|
|
|
|
|
let audioBuffer = Box::new_in( |
|
|
|
let mut audioBuffer = Box::new_in( |
|
|
|
[0u8; AUDIO_WAVE_LENGTH], |
|
|
|
[0u8; AUDIO_WAVE_LENGTH], |
|
|
|
LinearAllocator, |
|
|
|
LinearAllocator, |
|
|
|
); |
|
|
|
); |
|
|
|
fill_buffer(&mut audioBuffer, NOTEFREQ[4]); |
|
|
|
fill_buffer(&mut audioBuffer[..], NOTEFREQ[4]); |
|
|
|
|
|
|
|
|
|
|
|
let audioBuffer1 = |
|
|
|
|
|
|
|
WaveBuffer::new(audioBuffer, AudioFormat::PCM16Stereo).expect("Couldn't sync DSP cache"); |
|
|
|
|
|
|
|
let audioBuffer2 = audioBuffer1.clone(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let fillBlock = false; |
|
|
|
let mut audioBuffer1 = WaveBuffer::new(audioBuffer, AudioFormat::PCM16Stereo).expect("Couldn't sync DSP cache"); |
|
|
|
|
|
|
|
let mut audioBuffer2 = audioBuffer1.clone(); |
|
|
|
|
|
|
|
|
|
|
|
let ndsp = Ndsp::init().expect("Couldn't obtain NDSP controller"); |
|
|
|
let mut ndsp = Ndsp::init().expect("Couldn't obtain NDSP controller"); |
|
|
|
|
|
|
|
|
|
|
|
// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo`
|
|
|
|
// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo`
|
|
|
|
ndsp.set_output_mode(OutputMode::Stereo); |
|
|
|
ndsp.set_output_mode(OutputMode::Stereo); |
|
|
@ -63,12 +65,12 @@ fn main() { |
|
|
|
|
|
|
|
|
|
|
|
// Output at 100% on the first pair of left and right channels.
|
|
|
|
// Output at 100% on the first pair of left and right channels.
|
|
|
|
|
|
|
|
|
|
|
|
let mix = [0f32; 12]; |
|
|
|
let mut mix: [f32; 12] = [0f32; 12]; |
|
|
|
mix[0] = 1.0; |
|
|
|
mix[0] = 1.0; |
|
|
|
mix[1] = 1.0; |
|
|
|
mix[1] = 1.0; |
|
|
|
channel_zero.set_mix(&mix); |
|
|
|
channel_zero.set_mix(&mix); |
|
|
|
|
|
|
|
|
|
|
|
let note: usize = 4; |
|
|
|
let mut note: usize = 4; |
|
|
|
|
|
|
|
|
|
|
|
// Filters
|
|
|
|
// Filters
|
|
|
|
|
|
|
|
|
|
|
@ -81,24 +83,23 @@ fn main() { |
|
|
|
"Peaking", |
|
|
|
"Peaking", |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
let filter = 0; |
|
|
|
let mut filter = 0; |
|
|
|
|
|
|
|
|
|
|
|
// We set up two wave buffers and alternate between the two,
|
|
|
|
// We set up two wave buffers and alternate between the two,
|
|
|
|
// effectively streaming an infinitely long sine wave.
|
|
|
|
// effectively streaming an infinitely long sine wave.
|
|
|
|
|
|
|
|
|
|
|
|
let mut buf1 = WaveInfo::new(&mut audioBuffer1, false); |
|
|
|
let mut bufs: [WaveInfo; 2] = [ WaveInfo::new(&mut audioBuffer1, false), WaveInfo::new(&mut audioBuffer2, false)]; |
|
|
|
let mut buf2 = WaveInfo::new(&mut audioBuffer2, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe { |
|
|
|
channel_zero.queue_wave(&mut bufs[0]); |
|
|
|
channel_zero.queue_wave(&mut buf1); |
|
|
|
channel_zero.queue_wave(&mut bufs[1]); |
|
|
|
channel_zero.queue_wave(&mut buf2); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
println!("Press up/down to change tone frequency\n"); |
|
|
|
println!("Press up/down to change tone frequency\n"); |
|
|
|
println!("Press left/right to change filter\n"); |
|
|
|
println!("Press left/right to change filter\n"); |
|
|
|
println!("\x1b[6;1Hnote = {} Hz ", NOTEFREQ[note]); |
|
|
|
println!("\x1b[6;1Hnote = {} Hz ", NOTEFREQ[note]); |
|
|
|
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
|
|
|
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut fillBlock = false; |
|
|
|
|
|
|
|
|
|
|
|
while apt.main_loop() { |
|
|
|
while apt.main_loop() { |
|
|
|
hid.scan_input(); |
|
|
|
hid.scan_input(); |
|
|
|
let keys_down = hid.keys_down(); |
|
|
|
let keys_down = hid.keys_down(); |
|
|
@ -124,7 +125,7 @@ fn main() { |
|
|
|
// Check for upper limit
|
|
|
|
// Check for upper limit
|
|
|
|
note = std::cmp::max(note, NOTEFREQ.len() - 1); |
|
|
|
note = std::cmp::max(note, NOTEFREQ.len() - 1); |
|
|
|
|
|
|
|
|
|
|
|
let update_params = false; |
|
|
|
let mut update_params = false; |
|
|
|
if keys_down.contains(KeyPad::KEY_LEFT) { |
|
|
|
if keys_down.contains(KeyPad::KEY_LEFT) { |
|
|
|
filter = filter.saturating_sub(1); |
|
|
|
filter = filter.saturating_sub(1); |
|
|
|
if filter < 0 { |
|
|
|
if filter < 0 { |
|
|
@ -151,15 +152,12 @@ fn main() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if waveBuf[fillBlock].status == NDSP_WBUF_DONE { |
|
|
|
let status = bufs[fillBlock as usize].get_status(); |
|
|
|
if fillBlock { |
|
|
|
if let WaveStatus::Done = status { |
|
|
|
fill_buffer(buf1.get_mut_wavebuffer().get_mut_data(), NOTEFREQ[note]); |
|
|
|
fillBlock = !fillBlock; |
|
|
|
channel_zero.queue_wave(&mut buf1); |
|
|
|
|
|
|
|
} else { |
|
|
|
fill_buffer(bufs[fillBlock as usize].get_mut_wavebuffer().get_mut_data(), NOTEFREQ[note]); |
|
|
|
fill_buffer(buf2.get_mut_wavebuffer().get_mut_data(), NOTEFREQ[note]); |
|
|
|
channel_zero.queue_wave(&mut bufs[fillBlock as usize]); |
|
|
|
channel_zero.queue_wave(&mut buf2); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fillBlock = !fillBlock; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Flush and swap framebuffers
|
|
|
|
// Flush and swap framebuffers
|
|
|
|