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 }