Andrea Ciliberti
2 years ago
2 changed files with 175 additions and 169 deletions
@ -1,180 +1,170 @@ |
|||||||
#![feature(allocator_api)] |
#![feature(allocator_api)] |
||||||
|
|
||||||
use ctru::prelude::*; |
|
||||||
use ctru::services::ndsp::{Ndsp, OutputMode, InterpolationType}; |
|
||||||
use ctru::linear::LinearAllocator; |
use ctru::linear::LinearAllocator; |
||||||
|
use ctru::prelude::*; |
||||||
|
use ctru::services::ndsp::{ |
||||||
|
AudioFormat, InterpolationType, Ndsp, OutputMode, WaveBuffer, WaveInfo, |
||||||
|
}; |
||||||
|
|
||||||
const SAMPLERATE: u32 = 22050; |
const SAMPLERATE: u32 = 22050; |
||||||
const SAMPLESPERBUF: u32 = SAMPLERATE / 30; // 735
|
const SAMPLESPERBUF: u32 = SAMPLERATE / 30; // 735
|
||||||
const BYTESPERSAMPLE: u32 = 4; |
const BYTESPERSAMPLE: u32 = 4; |
||||||
|
|
||||||
fn array_size(array: &[u8]) -> usize { |
fn array_size(array: &[u8]) -> usize { |
||||||
array.len() |
array.len() |
||||||
} // (sizeof(array)/sizeof(array[0]))
|
} // (sizeof(array)/sizeof(array[0]))
|
||||||
|
|
||||||
// audioBuffer is stereo PCM16
|
// audioBuffer is stereo PCM16
|
||||||
void fill_buffer(void* audioBuffer, size_t offset, size_t size, int frequency) { |
fn fill_buffer(audioData: &mut Box<[u8], LinearAlloc>, frequency: i32) { |
||||||
u32* dest = (u32*) audioBuffer; |
for i in 0..size { |
||||||
|
// This is a simple sine wave, with a frequency of `frequency` Hz, and an amplitude 30% of maximum.
|
||||||
for (int i = 0; i < size; i++) { |
let sample: i16 = 0.3 * 0x7FFF * sin(frequency * (2 * std::f32::PI) * i / SAMPLERATE); |
||||||
// This is a simple sine wave, with a frequency of `frequency` Hz, and an amplitude 30% of maximum.
|
|
||||||
s16 sample = 0.3 * 0x7FFF * sin(frequency * (2 * M_PI) * (offset + i) / SAMPLERATE); |
// Stereo samples are interleaved: left and right channels.
|
||||||
|
audioData[i] = (sample << 16) | (sample & 0xffff); |
||||||
// Stereo samples are interleaved: left and right channels.
|
} |
||||||
dest[i] = (sample << 16) | (sample & 0xffff); |
|
||||||
} |
|
||||||
|
|
||||||
DSP_FlushDataCache(audioBuffer, size); |
|
||||||
} |
} |
||||||
|
|
||||||
fn main() { |
fn main() { |
||||||
ctru::init(); |
ctru::init(); |
||||||
let gfx = Gfx::init().expect("Couldn't obtain GFX controller"); |
let gfx = Gfx::init().expect("Couldn't obtain GFX controller"); |
||||||
let hid = Hid::init().expect("Couldn't obtain HID controller"); |
let hid = Hid::init().expect("Couldn't obtain HID controller"); |
||||||
let apt = Apt::init().expect("Couldn't obtain APT controller"); |
let apt = Apt::init().expect("Couldn't obtain APT controller"); |
||||||
let _console = Console::init(gfx.top_screen.borrow_mut()); |
let _console = Console::init(gfx.top_screen.borrow_mut()); |
||||||
|
|
||||||
println!("libctru filtered streamed audio\n"); |
println!("libctru filtered streamed audio\n"); |
||||||
|
|
||||||
let audioBuffer = Box::new_in([0u32; (SAMPLESPERBUF * BYTESPERSAMPLE * 2)], LinearAllocator); |
let audioBuffer = Box::new_in( |
||||||
|
[0u32; (SAMPLESPERBUF * BYTESPERSAMPLE * 2)], |
||||||
let fillBlock = false; |
LinearAllocator, |
||||||
|
); |
||||||
let ndsp = Ndsp::init().expect("Couldn't obtain NDSP controller"); |
fill_buffer(audioBuffer, notefreq[note]); |
||||||
|
|
||||||
// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo`
|
let audioBuffer1 = |
||||||
ndsp.set_output_mode(OutputMode::Stereo); |
WaveBuffer::new(audioBuffer, AudioFormat::PCM16Stereo).expect("Couldn't sync DSP cache"); |
||||||
|
let audioBuffer2 = audioBuffer1.clone(); |
||||||
let channel_zero = ndsp.channel(0); |
|
||||||
channel_zero.set_interpolation(InterpolationType::Linear); |
let fillBlock = false; |
||||||
channel_zero.set_sample_rate(SAMPLERATE); |
|
||||||
channel_zero.set_format(NDSP_FORMAT_STEREO_PCM16); |
let ndsp = Ndsp::init().expect("Couldn't obtain NDSP controller"); |
||||||
|
|
||||||
// Output at 100% on the first pair of left and right channels.
|
// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo`
|
||||||
|
ndsp.set_output_mode(OutputMode::Stereo); |
||||||
let mix = [0f32; 12]; |
|
||||||
mix[0] = 1.0; |
let channel_zero = ndsp.channel(0); |
||||||
mix[1] = 1.0; |
channel_zero.set_interpolation(InterpolationType::Linear); |
||||||
channel_zero.set_mix(mix); |
channel_zero.set_sample_rate(SAMPLERATE); |
||||||
|
channel_zero.set_format(NDSP_FORMAT_STEREO_PCM16); |
||||||
// Note Frequencies
|
|
||||||
|
// Output at 100% on the first pair of left and right channels.
|
||||||
let notefreq = [ |
|
||||||
220, |
let mix = [0f32; 12]; |
||||||
440, 880, 1760, 3520, 7040, |
mix[0] = 1.0; |
||||||
14080, |
mix[1] = 1.0; |
||||||
7040, 3520, 1760, 880, 440 |
channel_zero.set_mix(mix); |
||||||
]; |
|
||||||
|
// Note Frequencies
|
||||||
let note: i32 = 4; |
|
||||||
|
let notefreq = [ |
||||||
// Filters
|
220, 440, 880, 1760, 3520, 7040, 14080, 7040, 3520, 1760, 880, 440, |
||||||
|
]; |
||||||
let filter_names = [ |
|
||||||
"None", |
let note: i32 = 4; |
||||||
"Low-Pass", |
|
||||||
"High-Pass", |
// Filters
|
||||||
"Band-Pass", |
|
||||||
"Notch", |
let filter_names = [ |
||||||
"Peaking" |
"None", |
||||||
]; |
"Low-Pass", |
||||||
|
"High-Pass", |
||||||
let filter = 0; |
"Band-Pass", |
||||||
|
"Notch", |
||||||
// We set up two wave buffers and alternate between the two,
|
"Peaking", |
||||||
// effectively streaming an infinitely long sine wave.
|
]; |
||||||
|
|
||||||
ndspWaveBuf waveBuf[2]; |
let filter = 0; |
||||||
memset(waveBuf,0,sizeof(waveBuf)); |
|
||||||
waveBuf[0].data_vaddr = &audioBuffer[0]; |
// We set up two wave buffers and alternate between the two,
|
||||||
waveBuf[0].nsamples = SAMPLESPERBUF; |
// effectively streaming an infinitely long sine wave.
|
||||||
waveBuf[1].data_vaddr = &audioBuffer[SAMPLESPERBUF]; |
|
||||||
waveBuf[1].nsamples = SAMPLESPERBUF; |
let mut buf1 = WaveInfo::new(&mut audioBuffer1, false); |
||||||
|
let mut buf2 = WaveInfo::new(&mut audioBuffer2, false); |
||||||
let stream_offset = 0; |
|
||||||
|
unsafe { |
||||||
fill_buffer(audioBuffer,stream_offset, SAMPLESPERBUF * 2, notefreq[note]); |
channel_zero.add_wave_buffer(buf1); |
||||||
|
channel_zero.add_wave_buffer(buf2); |
||||||
stream_offset += SAMPLESPERBUF; |
}; |
||||||
|
|
||||||
channel_zero.add_wave_buffer(&waveBuf[0]); |
println!("Press up/down to change tone frequency\n"); |
||||||
channel_zero.add_wave_buffer(&waveBuf[1]); |
println!("Press left/right to change filter\n"); |
||||||
|
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
||||||
println!("Press up/down to change tone frequency\n"); |
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
||||||
println!("Press left/right to change filter\n"); |
|
||||||
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
while apt.main_loop() { |
||||||
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
hid.scan_input(); |
||||||
|
let keys_down = hid.keys_down(); |
||||||
while(aptMainLoop()) { |
|
||||||
|
if keys_down.contains(KeyPad::KEY_START) { |
||||||
gfxSwapBuffers(); |
break; |
||||||
gfxFlushBuffers(); |
} // break in order to return to hbmenu
|
||||||
gspWaitForVBlank(); |
|
||||||
|
if keys_down.contains(KeyPad::KEY_DOWN) { |
||||||
hidScanInput(); |
note -= 1; |
||||||
u32 kDown = hidKeysDown(); |
if (note < 0) { |
||||||
|
note = notefreq.len() - 1; |
||||||
if (kDown & KEY_START) |
} |
||||||
break; // break in order to return to hbmenu
|
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
||||||
|
} else if keys_down.contains(KeyPad::KEY_UP) { |
||||||
if (kDown & KEY_DOWN) { |
note += 1; |
||||||
note--; |
if (note >= notefreq.len()) { |
||||||
if (note < 0) { |
note = 0; |
||||||
note = ARRAY_SIZE(notefreq) - 1; |
} |
||||||
} |
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
||||||
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
} |
||||||
} else if (kDown & KEY_UP) { |
|
||||||
note++; |
let update_params = false; |
||||||
if (note >= ARRAY_SIZE(notefreq)) { |
if keys_down.contains(KeyPad::KEY_LEFT) { |
||||||
note = 0; |
filter -= 1; |
||||||
} |
if (filter < 0) { |
||||||
println!("\x1b[6;1Hnote = {} Hz ", notefreq[note]); |
filter = filter_names.len() - 1; |
||||||
} |
} |
||||||
|
update_params = true; |
||||||
bool update_params = false; |
} else if keys_down.contains(KeyPad::KEY_LEFT) { |
||||||
if (kDown & KEY_LEFT) { |
filter += 1; |
||||||
filter--; |
if (filter >= filter_names.len()) { |
||||||
if (filter < 0) { |
filter = 0; |
||||||
filter = ARRAY_SIZE(filter_names) - 1; |
} |
||||||
} |
update_params = true; |
||||||
update_params = true; |
} |
||||||
} else if (kDown & KEY_RIGHT) { |
|
||||||
filter++; |
if update_params { |
||||||
if (filter >= ARRAY_SIZE(filter_names)) { |
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
||||||
filter = 0; |
match filter { |
||||||
} |
1 => ndspChnIirBiquadSetParamsLowPassFilter(0, 1760., 0.707), |
||||||
update_params = true; |
2 => ndspChnIirBiquadSetParamsHighPassFilter(0, 1760., 0.707), |
||||||
} |
3 => ndspChnIirBiquadSetParamsBandPassFilter(0, 1760., 0.707), |
||||||
|
4 => ndspChnIirBiquadSetParamsNotchFilter(0, 1760., 0.707), |
||||||
if (update_params) { |
5 => ndspChnIirBiquadSetParamsPeakingEqualizer(0, 1760., 0.707, 3.0), |
||||||
println!("\x1b[7;1Hfilter = {} ", filter_names[filter]); |
_ => ndspChnIirBiquadSetEnable(0, false), |
||||||
switch (filter) { |
} |
||||||
default: |
} |
||||||
ndspChnIirBiquadSetEnable(0, false); |
|
||||||
break; |
if waveBuf[fillBlock].status == NDSP_WBUF_DONE { |
||||||
case 1: |
if fillBlock { |
||||||
ndspChnIirBiquadSetParamsLowPassFilter(0, 1760.f, 0.707f); |
fill_buffer(buf1.data_pcm16, notefreq[note]); |
||||||
break; |
channel_zero.add_wave_buffer(buf1); |
||||||
case 2: |
} else { |
||||||
ndspChnIirBiquadSetParamsHighPassFilter(0, 1760.f, 0.707f); |
fill_buffer(waveBuf[fillBlock].data_pcm16, notefreq[note]); |
||||||
break; |
channel_zero.add_wave_buffer(buf2); |
||||||
case 3: |
} |
||||||
ndspChnIirBiquadSetParamsBandPassFilter(0, 1760.f, 0.707f); |
fillBlock = !fillBlock; |
||||||
break; |
} |
||||||
case 4: |
|
||||||
ndspChnIirBiquadSetParamsNotchFilter(0, 1760.f, 0.707f); |
// Flush and swap framebuffers
|
||||||
break; |
gfx.flush_buffers(); |
||||||
case 5: |
gfx.swap_buffers(); |
||||||
ndspChnIirBiquadSetParamsPeakingEqualizer(0, 1760.f, 0.707f, 3.0f); |
|
||||||
break; |
//Wait for VBlank
|
||||||
} |
gfx.wait_for_vblank(); |
||||||
} |
} |
||||||
|
|
||||||
if (waveBuf[fillBlock].status == NDSP_WBUF_DONE) { |
|
||||||
fill_buffer(waveBuf[fillBlock].data_pcm16, stream_offset, waveBuf[fillBlock].nsamples, notefreq[note]); |
|
||||||
ndspChnWaveBufAdd(0, &waveBuf[fillBlock]); |
|
||||||
stream_offset += waveBuf[fillBlock].nsamples; |
|
||||||
fillBlock = !fillBlock; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
} |
||||||
|
Loading…
Reference in new issue