1
+ //! Audio Filters example.
2
+ //!
3
+ //! This example showcases basic audio functionality using [`Ndsp`].
4
+
1
5
#![ feature( allocator_api) ]
2
6
3
7
use std:: f32:: consts:: PI ;
4
8
5
9
use ctru:: linear:: LinearAllocator ;
6
10
use ctru:: prelude:: * ;
7
11
use ctru:: services:: ndsp:: {
8
- wave:: { Wave , WaveStatus } ,
12
+ wave:: { Status , Wave } ,
9
13
AudioFormat , AudioMix , InterpolationType , Ndsp , OutputMode ,
10
14
} ;
11
15
16
+ // Configuration for the NDSP process and channels.
12
17
const SAMPLE_RATE : usize = 22050 ;
13
18
const SAMPLES_PER_BUF : usize = SAMPLE_RATE / 10 ; // 2205
14
19
const BYTES_PER_SAMPLE : usize = AudioFormat :: PCM16Stereo . size ( ) ;
15
20
const AUDIO_WAVE_LENGTH : usize = SAMPLES_PER_BUF * BYTES_PER_SAMPLE ;
16
21
17
- // Note Frequencies
22
+ // Note frequencies.
18
23
const NOTEFREQ : [ f32 ; 7 ] = [ 220. , 440. , 880. , 1760. , 3520. , 7040. , 14080. ] ;
19
24
20
- // The audio format is Stereo PCM16
21
- // As such, a sample is made up of 2 "Mono" samples (2 * i16 = u32), one for each channel (left and right)
22
25
fn fill_buffer ( audio_data : & mut [ u8 ] , frequency : f32 ) {
26
+ // The audio format is Stereo PCM16.
27
+ // As such, a sample is made up of 2 "Mono" samples (2 * i16), one for each channel (left and right).
23
28
let formatted_data = bytemuck:: cast_slice_mut :: < _ , [ i16 ; 2 ] > ( audio_data) ;
24
29
25
30
for ( i, chunk) in formatted_data. iter_mut ( ) . enumerate ( ) {
@@ -44,8 +49,7 @@ fn main() {
44
49
45
50
let mut note: usize = 4 ;
46
51
47
- // Filters
48
-
52
+ // Filter names to display.
49
53
let filter_names = [
50
54
"None" ,
51
55
"Low-Pass" ,
@@ -60,19 +64,26 @@ fn main() {
60
64
// We set up two wave buffers and alternate between the two,
61
65
// effectively streaming an infinitely long sine wave.
62
66
67
+ // We create a buffer on the LINEAR memory that will hold our audio data.
68
+ // It's necessary for the buffer to live on the LINEAR memory sector since it needs to be accessed by the DSP processor.
63
69
let mut audio_data1 = Box :: new_in ( [ 0u8 ; AUDIO_WAVE_LENGTH ] , LinearAllocator ) ;
70
+
71
+ // Fill the buffer with the first set of data. This simply writes a sine wave into the buffer.
64
72
fill_buffer ( audio_data1. as_mut_slice ( ) , NOTEFREQ [ 4 ] ) ;
65
73
74
+ // Clone the original buffer to obtain an equal buffer on the LINEAR memory used for double buffering.
66
75
let audio_data2 = audio_data1. clone ( ) ;
67
76
77
+ // Setup two wave info objects with the correct configuration and ownership of the audio data.
68
78
let mut wave_info1 = Wave :: new ( audio_data1, AudioFormat :: PCM16Stereo , false ) ;
69
79
let mut wave_info2 = Wave :: new ( audio_data2, AudioFormat :: PCM16Stereo , false ) ;
70
80
71
- let mut ndsp = Ndsp :: new ( ) . expect ( "Couldn't obtain NDSP controller" ) ;
81
+ // Setup the NDSP service and its configuration.
72
82
73
- // This line isn 't needed since the default NDSP configuration already sets the output mode to `Stereo`
83
+ let mut ndsp = Ndsp :: new ( ) . expect ( "Couldn 't obtain NDSP controller" ) ;
74
84
ndsp. set_output_mode ( OutputMode :: Stereo ) ;
75
85
86
+ // Channel configuration. We use channel zero but any channel would do just fine.
76
87
let mut channel_zero = ndsp. channel ( 0 ) . unwrap ( ) ;
77
88
channel_zero. set_interpolation ( InterpolationType :: Linear ) ;
78
89
channel_zero. set_sample_rate ( SAMPLE_RATE as f32 ) ;
@@ -82,6 +93,7 @@ fn main() {
82
93
let mix = AudioMix :: default ( ) ;
83
94
channel_zero. set_mix ( & mix) ;
84
95
96
+ // First set of queueing for the two buffers. The second one will only play after the first one has ended.
85
97
channel_zero. queue_wave ( & mut wave_info1) . unwrap ( ) ;
86
98
channel_zero. queue_wave ( & mut wave_info2) . unwrap ( ) ;
87
99
@@ -93,6 +105,8 @@ fn main() {
93
105
filter_names[ filter as usize ]
94
106
) ;
95
107
108
+ println ! ( "\x1b [29;16HPress Start to exit" ) ;
109
+
96
110
let mut altern = true ; // true is wave_info1, false is wave_info2
97
111
98
112
while apt. main_loop ( ) {
@@ -101,14 +115,16 @@ fn main() {
101
115
102
116
if keys_down. contains ( KeyPad :: START ) {
103
117
break ;
104
- } // break in order to return to hbmenu
118
+ }
105
119
120
+ // Note frequency controller using the buttons.
106
121
if keys_down. intersects ( KeyPad :: DOWN ) {
107
122
note = note. saturating_sub ( 1 ) ;
108
123
} else if keys_down. intersects ( KeyPad :: UP ) {
109
124
note = std:: cmp:: min ( note + 1 , NOTEFREQ . len ( ) - 1 ) ;
110
125
}
111
126
127
+ // Filter controller using the buttons.
112
128
let mut update_params = false ;
113
129
if keys_down. intersects ( KeyPad :: LEFT ) {
114
130
filter -= 1 ;
@@ -139,22 +155,23 @@ fn main() {
139
155
}
140
156
}
141
157
158
+ // Double buffer alternation depending on the one used.
142
159
let current: & mut Wave = if altern {
143
160
& mut wave_info1
144
161
} else {
145
162
& mut wave_info2
146
163
} ;
147
164
165
+ // If the current buffer has finished playing, we can refill it with new data and re-queue it.
148
166
let status = current. status ( ) ;
149
- if let WaveStatus :: Done = status {
167
+ if let Status :: Done = status {
150
168
fill_buffer ( current. get_buffer_mut ( ) . unwrap ( ) , NOTEFREQ [ note] ) ;
151
169
152
170
channel_zero. queue_wave ( current) . unwrap ( ) ;
153
171
154
172
altern = !altern;
155
173
}
156
174
157
- //Wait for VBlank
158
175
gfx. wait_for_vblank ( ) ;
159
176
}
160
177
}
0 commit comments