Mercurial > sdl-ios-xcode
comparison XCodeiPhoneOS/Demos/src/mixer.c @ 2384:6a946f3155d8 gsoc2008_iphone
Drum kit demo (SDL_mixer)
author | Holmes Futrell <hfutrell@umail.ucsb.edu> |
---|---|
date | Fri, 18 Jul 2008 20:51:59 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2383:1cfe7fd15dad | 2384:6a946f3155d8 |
---|---|
1 /* | |
2 * mixer.c | |
3 * written by Holmes Futrell | |
4 * use however you want | |
5 */ | |
6 | |
7 #import "SDL.h" | |
8 #import "common.h" | |
9 | |
10 #define NUM_CHANNELS 8 /* max number of sounds we can play at once */ | |
11 #define NUM_DRUMS 4 /* number of drums in our set */ | |
12 #define MILLESECONDS_PER_FRAME 16 /* about 60 frames per second */ | |
13 | |
14 static struct { | |
15 SDL_Rect rect; /* where the button is drawn */ | |
16 SDL_Color upColor; /* color when button is not active */ | |
17 SDL_Color downColor; /* color when button is active */ | |
18 int isPressed; /* is the button being pressed ? */ | |
19 int touchIndex; /* what mouse (touch) index pressed the button ? */ | |
20 } buttons[NUM_DRUMS]; | |
21 | |
22 struct sound { | |
23 Uint8 *buffer; /* audio buffer for sound file */ | |
24 Uint32 length; /* length of the buffer (in bytes) */ | |
25 }; | |
26 | |
27 /* this array holds the audio for the drum noises */ | |
28 static struct sound drums[NUM_DRUMS]; | |
29 | |
30 /* function declarations */ | |
31 void handleMouseButtonDown(SDL_Event *event); | |
32 void handleMouseButtonUp(SDL_Event *event); | |
33 int playSound(struct sound *); | |
34 void render(void); | |
35 void initializeButtons(); | |
36 void audioCallback(void *userdata, Uint8 *stream, int len); | |
37 void loadSound(const char *file, struct sound *s); | |
38 | |
39 struct { | |
40 /* channel array holds information about currently playing sounds */ | |
41 struct { | |
42 Uint8 *position; /* what is the current position in the buffer of this sound ? */ | |
43 Uint32 remaining; /* how many bytes remaining before we're done playing the sound ? */ | |
44 Uint32 timestamp; /* when did this sound start playing ? */ | |
45 } channels[NUM_CHANNELS]; | |
46 SDL_AudioSpec outputSpec; /* what audio format are we using for output? */ | |
47 int numSoundsPlaying; /* how many sounds are currently playing */ | |
48 } mixer; | |
49 | |
50 /* sets up the buttons (color, position, state) */ | |
51 void initializeButtons() { | |
52 | |
53 int i; | |
54 int spacing = 10; /* gap between drum buttons */ | |
55 SDL_Rect buttonRect; /* keeps track of where to position drum */ | |
56 SDL_Color upColor = { 86, 86, 140, 255 }; /* color of drum when not pressed */ | |
57 SDL_Color downColor = { 191, 191, 221, 255 }; /* color of drum when pressed */ | |
58 | |
59 buttonRect.x = spacing; | |
60 buttonRect.y = spacing; | |
61 buttonRect.w = SCREEN_WIDTH - 2 * spacing; | |
62 buttonRect.h = ( SCREEN_HEIGHT - (NUM_DRUMS + 1) * spacing ) / NUM_DRUMS; | |
63 | |
64 /* setup each button */ | |
65 for (i=0; i<NUM_DRUMS; i++) { | |
66 | |
67 buttons[i].rect = buttonRect; | |
68 buttons[i].isPressed = 0; | |
69 buttons[i].upColor = upColor; | |
70 buttons[i].downColor = downColor; | |
71 | |
72 buttonRect.y += spacing + buttonRect.h; /* setup y coordinate for next drum */ | |
73 | |
74 } | |
75 } | |
76 /* | |
77 loads a wav file (stored in 'file'), converts it to the mixer's output format, | |
78 and stores the resulting buffer and length in the sound structure | |
79 */ | |
80 void loadSound(const char *file, struct sound *s) { | |
81 SDL_AudioSpec spec; /* the audio format of the .wav file */ | |
82 SDL_AudioCVT cvt; /* used to convert .wav to output format when formats differ */ | |
83 int result; | |
84 if (SDL_LoadWAV(file, &spec, &s->buffer, &s->length) == NULL) { | |
85 fatalError("could not load .wav"); | |
86 } | |
87 /* build the audio converter */ | |
88 result = SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, | |
89 mixer.outputSpec.format, mixer.outputSpec.channels, mixer.outputSpec.freq); | |
90 if (result == -1) { | |
91 fatalError("could not build audio CVT"); | |
92 } | |
93 else if (result != 0) { | |
94 /* | |
95 this happens when the .wav format differs from the output format. | |
96 we convert the .wav buffer here | |
97 */ | |
98 cvt.buf = (Uint8 *)SDL_malloc(s->length * cvt.len_mult); /* allocate conversion buffer */ | |
99 cvt.len = s->length; /* set conversion buffer length */ | |
100 SDL_memcpy(cvt.buf, s->buffer, s->length); /* copy sound to conversion buffer */ | |
101 if (SDL_ConvertAudio(&cvt) == -1) { /* convert the sound */ | |
102 fatalError("could not convert .wav"); | |
103 } | |
104 SDL_free(s->buffer); /* free the original (unconverted) buffer */ | |
105 s->buffer = cvt.buf; /* point sound buffer to converted buffer */ | |
106 s->length = cvt.len_cvt; /* set sound buffer's new length */ | |
107 } | |
108 } | |
109 | |
110 /* called from main event loop */ | |
111 void handleMouseButtonDown(SDL_Event *event) { | |
112 | |
113 int x, y, mouseIndex, i, drumIndex; | |
114 | |
115 mouseIndex = event->button.which; | |
116 drumIndex = -1; | |
117 | |
118 SDL_SelectMouse(mouseIndex); | |
119 SDL_GetMouseState(&x, &y); | |
120 /* check if we hit any of the drum buttons */ | |
121 for (i=0; i<NUM_DRUMS; i++) { | |
122 if (x >= buttons[i].rect.x && x < buttons[i].rect.x + buttons[i].rect.w \ | |
123 && y >= buttons[i].rect.y && y < buttons[i].rect.y + buttons[i].rect.h) { | |
124 drumIndex = i; | |
125 break; | |
126 } | |
127 } | |
128 if (drumIndex != -1) { | |
129 /* if we hit a button */ | |
130 buttons[drumIndex].touchIndex = mouseIndex; | |
131 buttons[drumIndex].isPressed = 1; | |
132 playSound(&drums[drumIndex]); | |
133 } | |
134 | |
135 } | |
136 | |
137 /* called from main event loop */ | |
138 void handleMouseButtonUp(SDL_Event *event) { | |
139 int i; | |
140 int mouseIndex = event->button.which; | |
141 /* check if this should cause any of the buttons to become unpressed */ | |
142 for (i=0; i<NUM_DRUMS; i++) { | |
143 if (buttons[i].touchIndex == mouseIndex) { | |
144 buttons[i].isPressed = 0; | |
145 } | |
146 } | |
147 } | |
148 | |
149 /* draws buttons to screen */ | |
150 void render(void) { | |
151 int i; | |
152 SDL_RenderFill(50, 50, 50, 255, NULL); /* draw background (gray) */ | |
153 /* draw the drum buttons */ | |
154 for (i=0; i<NUM_DRUMS; i++) { | |
155 SDL_Color color = buttons[i].isPressed ? buttons[i].downColor : buttons[i].upColor; | |
156 SDL_RenderFill(color.r, color.g, color.b, color.unused, &buttons[i].rect); | |
157 } | |
158 /* update the screen */ | |
159 SDL_RenderPresent(); | |
160 } | |
161 | |
162 /* | |
163 finds a sound channel in the mixer for a sound | |
164 and sets it up to start playing | |
165 */ | |
166 int playSound(struct sound *s) { | |
167 /* | |
168 find an empty channel to play on. | |
169 if no channel is available, use oldest channel | |
170 */ | |
171 int i; | |
172 int selected_channel = -1; | |
173 int oldest_channel = 0; | |
174 | |
175 if (mixer.numSoundsPlaying == 0) { | |
176 /* we're playing a sound now, so start audio callback back up */ | |
177 SDL_PauseAudio(0); | |
178 } | |
179 | |
180 /* find a sound channel to play the sound on */ | |
181 for (i=0; i<NUM_CHANNELS; i++) { | |
182 if (mixer.channels[i].position == NULL) { | |
183 /* if no sound on this channel, select it */ | |
184 selected_channel = i; | |
185 break; | |
186 } | |
187 /* if this channel's sound is older than the oldest so far, set it to oldest */ | |
188 if (mixer.channels[i].timestamp < mixer.channels[oldest_channel].timestamp) | |
189 oldest_channel = i; | |
190 } | |
191 | |
192 /* no empty channels, take the oldest one */ | |
193 if (selected_channel == -1) | |
194 selected_channel = oldest_channel; | |
195 else | |
196 mixer.numSoundsPlaying++; | |
197 | |
198 /* point channel data to wav data */ | |
199 mixer.channels[selected_channel].position = s->buffer; | |
200 mixer.channels[selected_channel].remaining = s->length; | |
201 mixer.channels[selected_channel].timestamp = SDL_GetTicks(); | |
202 | |
203 return selected_channel; | |
204 } | |
205 | |
206 /* | |
207 Called from SDL's audio system. Supplies sound input with data by mixing together all | |
208 currently playing sound effects. | |
209 */ | |
210 void audioCallback(void *userdata, Uint8 *stream, int len) { | |
211 int i; | |
212 int copy_amt; | |
213 SDL_memset(stream, mixer.outputSpec.silence, len); /* initialize buffer to silence */ | |
214 /* for each channel, mix in whatever is playing on that channel */ | |
215 for (i=0; i<NUM_CHANNELS; i++) { | |
216 if (mixer.channels[i].position == NULL) { | |
217 /* if no sound is playing on this channel */ | |
218 continue; /* nothing to do for this channel */ | |
219 } | |
220 | |
221 /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */ | |
222 copy_amt = mixer.channels[i].remaining < len ? mixer.channels[i].remaining : len; | |
223 | |
224 /* mix this sound effect with the output */ | |
225 SDL_MixAudioFormat(stream, mixer.channels[i].position, mixer.outputSpec.format, copy_amt, 150); | |
226 | |
227 /* update buffer position in sound effect and the number of bytes left */ | |
228 mixer.channels[i].position += copy_amt; | |
229 mixer.channels[i].remaining -= copy_amt; | |
230 | |
231 /* did we finish playing the sound effect ? */ | |
232 if (mixer.channels[i].remaining == 0) { | |
233 mixer.channels[i].position = NULL; /* indicates no sound playing on channel anymore */ | |
234 mixer.numSoundsPlaying--; | |
235 if (mixer.numSoundsPlaying == 0) { | |
236 /* if no sounds left playing, pause audio callback */ | |
237 SDL_PauseAudio(1); | |
238 } | |
239 } | |
240 } | |
241 } | |
242 | |
243 int main(int argc, char *argv[]) { | |
244 | |
245 int done; /* has user tried to quit ? */ | |
246 SDL_WindowID windowID; /* our main window */ | |
247 SDL_Event event; | |
248 Uint32 startFrame; /* holds when frame started processing */ | |
249 Uint32 endFrame; /* holds when frame ended processing */ | |
250 Uint32 delay; /* calculated delay, how long should we wait before next frame? */ | |
251 | |
252 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) { | |
253 fatalError("could not initialize SDL"); | |
254 } | |
255 windowID = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS); | |
256 SDL_CreateRenderer(windowID, 0, 0); | |
257 | |
258 /* initialize the mixer */ | |
259 SDL_memset(&mixer, 0, sizeof(mixer)); | |
260 /* setup output format */ | |
261 mixer.outputSpec.freq = 44100; | |
262 mixer.outputSpec.format = AUDIO_S16LSB; | |
263 mixer.outputSpec.channels = 2; | |
264 mixer.outputSpec.samples = 256; | |
265 mixer.outputSpec.callback = audioCallback; | |
266 mixer.outputSpec.userdata = NULL; | |
267 | |
268 /* open audio for output */ | |
269 if (SDL_OpenAudio(&mixer.outputSpec, NULL) != 0) { | |
270 fatalError("Opening audio failed"); | |
271 } | |
272 | |
273 /* load our drum noises */ | |
274 loadSound("ds_kick_big_amb.wav", &drums[3]); | |
275 loadSound("ds_brush_snare.wav", &drums[2]); | |
276 loadSound("ds_loose_skin_mute.wav", &drums[1]); | |
277 loadSound("ds_china.wav", &drums[0]); | |
278 | |
279 /* setup positions, colors, and state of buttons */ | |
280 initializeButtons(); | |
281 | |
282 /* enter main loop */ | |
283 done = 0; | |
284 while(!done) { | |
285 startFrame = SDL_GetTicks(); | |
286 while (SDL_PollEvent(&event)) { | |
287 switch(event.type) { | |
288 case SDL_MOUSEBUTTONDOWN: | |
289 handleMouseButtonDown(&event); | |
290 break; | |
291 case SDL_MOUSEBUTTONUP: | |
292 handleMouseButtonUp(&event); | |
293 break; | |
294 case SDL_QUIT: | |
295 done = 1; | |
296 break; | |
297 } | |
298 } | |
299 render(); /* draw buttons */ | |
300 endFrame = SDL_GetTicks(); | |
301 | |
302 /* figure out how much time we have left, and then sleep */ | |
303 delay = MILLESECONDS_PER_FRAME - (endFrame - startFrame); | |
304 if (delay < 0) { | |
305 delay = 0; | |
306 } else if (delay > MILLESECONDS_PER_FRAME) { | |
307 delay = MILLESECONDS_PER_FRAME; | |
308 } | |
309 SDL_Delay(delay); | |
310 } | |
311 | |
312 /* cleanup code, let's free up those sound buffers */ | |
313 int i; | |
314 for (i=0; i<NUM_DRUMS; i++) { | |
315 SDL_free(drums[i].buffer); | |
316 } | |
317 /* let SDL do its exit code */ | |
318 SDL_Quit(); | |
319 | |
320 return 0; | |
321 } |