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