Mercurial > sdl-ios-xcode
comparison Xcode-iPhoneOS/Demos/src/mixer.c @ 3277:20326ba2bda2
This name inconsistency has been bugging me for a while...
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 19 Sep 2009 07:32:36 +0000 |
parents | |
children | 64ce267332c6 |
comparison
equal
deleted
inserted
replaced
3276:720d176be107 | 3277:20326ba2bda2 |
---|---|
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_SetRenderDrawColor(50, 50, 50, 255); | |
171 SDL_RenderFill(NULL); /* draw background (gray) */ | |
172 /* draw the drum buttons */ | |
173 for (i = 0; i < NUM_DRUMS; i++) { | |
174 SDL_Color color = | |
175 buttons[i].isPressed ? buttons[i].downColor : buttons[i].upColor; | |
176 SDL_SetRenderDrawColor(color.r, color.g, color.b, color.unused); | |
177 SDL_RenderFill(&buttons[i].rect); | |
178 } | |
179 /* update the screen */ | |
180 SDL_RenderPresent(); | |
181 } | |
182 | |
183 /* | |
184 finds a sound channel in the mixer for a sound | |
185 and sets it up to start playing | |
186 */ | |
187 int | |
188 playSound(struct sound *s) | |
189 { | |
190 /* | |
191 find an empty channel to play on. | |
192 if no channel is available, use oldest channel | |
193 */ | |
194 int i; | |
195 int selected_channel = -1; | |
196 int oldest_channel = 0; | |
197 | |
198 if (mixer.numSoundsPlaying == 0) { | |
199 /* we're playing a sound now, so start audio callback back up */ | |
200 SDL_PauseAudio(0); | |
201 } | |
202 | |
203 /* find a sound channel to play the sound on */ | |
204 for (i = 0; i < NUM_CHANNELS; i++) { | |
205 if (mixer.channels[i].position == NULL) { | |
206 /* if no sound on this channel, select it */ | |
207 selected_channel = i; | |
208 break; | |
209 } | |
210 /* if this channel's sound is older than the oldest so far, set it to oldest */ | |
211 if (mixer.channels[i].timestamp < | |
212 mixer.channels[oldest_channel].timestamp) | |
213 oldest_channel = i; | |
214 } | |
215 | |
216 /* no empty channels, take the oldest one */ | |
217 if (selected_channel == -1) | |
218 selected_channel = oldest_channel; | |
219 else | |
220 mixer.numSoundsPlaying++; | |
221 | |
222 /* point channel data to wav data */ | |
223 mixer.channels[selected_channel].position = s->buffer; | |
224 mixer.channels[selected_channel].remaining = s->length; | |
225 mixer.channels[selected_channel].timestamp = SDL_GetTicks(); | |
226 | |
227 return selected_channel; | |
228 } | |
229 | |
230 /* | |
231 Called from SDL's audio system. Supplies sound input with data by mixing together all | |
232 currently playing sound effects. | |
233 */ | |
234 void | |
235 audioCallback(void *userdata, Uint8 * stream, int len) | |
236 { | |
237 int i; | |
238 int copy_amt; | |
239 SDL_memset(stream, mixer.outputSpec.silence, len); /* initialize buffer to silence */ | |
240 /* for each channel, mix in whatever is playing on that channel */ | |
241 for (i = 0; i < NUM_CHANNELS; i++) { | |
242 if (mixer.channels[i].position == NULL) { | |
243 /* if no sound is playing on this channel */ | |
244 continue; /* nothing to do for this channel */ | |
245 } | |
246 | |
247 /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */ | |
248 copy_amt = | |
249 mixer.channels[i].remaining < | |
250 len ? mixer.channels[i].remaining : len; | |
251 | |
252 /* mix this sound effect with the output */ | |
253 SDL_MixAudioFormat(stream, mixer.channels[i].position, | |
254 mixer.outputSpec.format, copy_amt, 150); | |
255 | |
256 /* update buffer position in sound effect and the number of bytes left */ | |
257 mixer.channels[i].position += copy_amt; | |
258 mixer.channels[i].remaining -= copy_amt; | |
259 | |
260 /* did we finish playing the sound effect ? */ | |
261 if (mixer.channels[i].remaining == 0) { | |
262 mixer.channels[i].position = NULL; /* indicates no sound playing on channel anymore */ | |
263 mixer.numSoundsPlaying--; | |
264 if (mixer.numSoundsPlaying == 0) { | |
265 /* if no sounds left playing, pause audio callback */ | |
266 SDL_PauseAudio(1); | |
267 } | |
268 } | |
269 } | |
270 } | |
271 | |
272 int | |
273 main(int argc, char *argv[]) | |
274 { | |
275 | |
276 int done; /* has user tried to quit ? */ | |
277 SDL_WindowID windowID; /* our main window */ | |
278 SDL_Event event; | |
279 Uint32 startFrame; /* holds when frame started processing */ | |
280 Uint32 endFrame; /* holds when frame ended processing */ | |
281 Uint32 delay; /* calculated delay, how long should we wait before next frame? */ | |
282 | |
283 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { | |
284 fatalError("could not initialize SDL"); | |
285 } | |
286 windowID = | |
287 SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, | |
288 SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS); | |
289 SDL_CreateRenderer(windowID, 0, 0); | |
290 | |
291 /* initialize the mixer */ | |
292 SDL_memset(&mixer, 0, sizeof(mixer)); | |
293 /* setup output format */ | |
294 mixer.outputSpec.freq = 44100; | |
295 mixer.outputSpec.format = AUDIO_S16LSB; | |
296 mixer.outputSpec.channels = 2; | |
297 mixer.outputSpec.samples = 256; | |
298 mixer.outputSpec.callback = audioCallback; | |
299 mixer.outputSpec.userdata = NULL; | |
300 | |
301 /* open audio for output */ | |
302 if (SDL_OpenAudio(&mixer.outputSpec, NULL) != 0) { | |
303 fatalError("Opening audio failed"); | |
304 } | |
305 | |
306 /* load our drum noises */ | |
307 loadSound("ds_kick_big_amb.wav", &drums[3]); | |
308 loadSound("ds_brush_snare.wav", &drums[2]); | |
309 loadSound("ds_loose_skin_mute.wav", &drums[1]); | |
310 loadSound("ds_china.wav", &drums[0]); | |
311 | |
312 /* setup positions, colors, and state of buttons */ | |
313 initializeButtons(); | |
314 | |
315 /* enter main loop */ | |
316 done = 0; | |
317 while (!done) { | |
318 startFrame = SDL_GetTicks(); | |
319 while (SDL_PollEvent(&event)) { | |
320 switch (event.type) { | |
321 case SDL_MOUSEBUTTONDOWN: | |
322 handleMouseButtonDown(&event); | |
323 break; | |
324 case SDL_MOUSEBUTTONUP: | |
325 handleMouseButtonUp(&event); | |
326 break; | |
327 case SDL_QUIT: | |
328 done = 1; | |
329 break; | |
330 } | |
331 } | |
332 render(); /* draw buttons */ | |
333 endFrame = SDL_GetTicks(); | |
334 | |
335 /* figure out how much time we have left, and then sleep */ | |
336 delay = MILLESECONDS_PER_FRAME - (endFrame - startFrame); | |
337 if (delay < 0) { | |
338 delay = 0; | |
339 } else if (delay > MILLESECONDS_PER_FRAME) { | |
340 delay = MILLESECONDS_PER_FRAME; | |
341 } | |
342 SDL_Delay(delay); | |
343 } | |
344 | |
345 /* cleanup code, let's free up those sound buffers */ | |
346 int i; | |
347 for (i = 0; i < NUM_DRUMS; i++) { | |
348 SDL_free(drums[i].buffer); | |
349 } | |
350 /* let SDL do its exit code */ | |
351 SDL_Quit(); | |
352 | |
353 return 0; | |
354 } |