comparison mixer/tutorial.txt @ 478:15a540505a02

Initial add.
author Ryan C. Gordon <icculus@icculus.org>
date Thu, 29 Jul 2004 09:26:14 +0000
parents
children 3a3807dcf57f
comparison
equal deleted inserted replaced
477:3e705c9180e5 478:15a540505a02
1
2 SDL_sound version 2.0: Your Mixer, And Welcome To It.
3
4
5 SDL_sound v2's major addition is a software mixer. There are some other new
6 features, but this is the Big New Thing.
7
8
9 Some notable features of this mixer:
10 - No clamping at mix time. Most SDL-based mixing is done via SDL_MixAudio(),
11 which mixes two samples together, clamping to 16-bits, and then mixes the
12 next sample into this already-clamped buffer. This can lead to audio
13 distortion. SDL_sound mixes all samples into a 32-bit floating point buffer
14 before passing it to the audio device, which means no unnecessary clamping.
15 It also means it can be more optimized for MacOS X's CoreAudio and other
16 next-gen audio subsystems, which require you to feed it float32 data.
17
18 - Optimized mixing: MMX, SSE, 3DNow!, and Altivec are used internally where
19 appropriate. (er...they WILL be, at least!)
20
21 - Multiple "music" files. SDL_mixer is notorious for making a distinction
22 between "sound" and "music" files (that is, things that can be trivially
23 decoded to a waveform without bloating the memory footprint vs. things that
24 can't), and only allows mixing of one music file at a time. SDL_sound
25 doesn't bother with this distinction, which means you are free to mix any
26 combination of audio formats at the same time.
27
28 - No "channels". If you want to mix 1,000 audio files and have the hardware
29 to cover it, you can. You don't have to manage playback channels. There
30 isn't a seperate "music" channel, since "music" isn't treated differently
31 from any other audio.
32
33 - Lots of formats. SDL_sound already decodes a huge number of audio formats.
34 As the mixer is layered on top of this, all of the format support comes
35 for free.
36
37 - Can handle non-power-of-two resampling. If your samples are at 8000Hz
38 and the audio hardware is at 11000Hz, this doesn't cause output problems.
39
40 - Control over buffering and mixing. SDL_sound already lets you control how
41 much audio is prebuffered and predecoded; this carries over to the mixer,
42 so you can customize how your resources are being used on the fly.
43
44 - Flexible enough for those that need to micromanage mixing, but dirt simple
45 to for those that just want to make some noise.
46
47 - It can be compiled out if you just want the 1.0 API for decoding formats.
48 (initializing the mixer subsystem in this case will fail, but for binary
49 compatibility, the entry points will still exist).
50
51 A brief tutorial follows.
52
53
54 Example #1: Play a sound and get the heck out of there.
55
56 #include "SDL_sound.h"
57
58 int main(int argc, char **argv)
59 {
60 Sound_MixInit(NULL); // start the mixer; don't care what format.
61 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
62 Sound_MixPlay(hello);
63 while (Sound_MixPlaying(hello))
64 SDL_Delay(100); // wait around; mixing is in a seperate thread!
65 Sound_FreeSample(hello);
66 Sound_MixDeinit();
67 return(0);
68 }
69
70 Every tutorial needs a "Hello World" example.
71 That will play hello.wav, wait for it to finish, and terminate the program.
72 But that's not really mixing! To qualify, you'd need to play two sounds at
73 once. So let's do that:
74
75
76 Example #2: Mixing two sounds.
77
78 #include "SDL_sound.h"
79
80 int main(int argc, char **argv)
81 {
82 Sound_MixInit(NULL); // start the mixer; don't care what format.
83 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
84 Sound_Sample *music = Sound_NewSampleFromFile("icculus.ogg", NULL, 10240);
85 Sound_MixPlay(music);
86 while (Sound_MixPlaying(music))
87 {
88 if (!Sound_MixPlaying(hello))
89 {
90 Sound_Rewind(hello);
91 Sound_MixPlay(hello);
92 }
93 SDL_Delay(100); // wait around.
94 }
95 Sound_FreeSample(music);
96 Sound_FreeSample(hello); // will stop if it happens to still be playing.
97 Sound_MixDeinit();
98 return(0);
99 }
100
101 Same deal, but we play some music ("Icculus in San Jose" from the talented
102 Emmett Plant, in this case). We also load our "hello" sound from the previous
103 example. While the music is playing, we check if "hello" is playing, and if
104 not, we set it up to play again. Note that the two sounds are playing at the
105 same time, mixed together. Cool, huh?
106
107 You might notice that we called Sound_Rewind() on the hello sample. This isn't
108 part of the mixer itself, and is a function from SDL_sound v1, before there
109 was a mixer at all. This illustrates that you can use the usual SDL_sound
110 methods to manipulate a sample in the mixer, including seeking and predecoding.
111 These are safe operations even while the sample is playing.
112
113 That's about all you need to know to effectively use the mixer. Everything
114 after that is extra credit.
115
116
117 Extra credit #1: Mixer Attributes.
118
119 An API's got to know its limitations. SDL_sound isn't meant to be a robust 3D
120 spatialization library. For that, one should look to the excellent OpenAL API
121 at http://www.openal.org/. Still, for many reasons, OpenAL might not be a good
122 fit: it doesn't support many audio formats (and all formats except uncompressed
123 integer PCM are optional extensions to the API), it is less likely to
124 support your platform and audio output target than SDL, and it is more
125 complicated to feed it streamed audio. While not as robust as AL's feature
126 set, SDL_sound v2 provides a handful of useful attributes you can set on a
127 sample to alter its playback.
128
129
130 Basic Attribute #1: Looping.
131
132 Checking a sample's playing state in a loop just so you know when to restart
133 it has two problems: first, it's a pain in the butt, and second, there may
134 be a gap in the audio between when the sound starts and when you're able to
135 restart it. To remedy this, SDL_sound lets you flag a sample as "looping" so
136 you don't have to micromanage it. It will continue to rewind and play until
137 you explicitly stop it. Let's take our last example and do this right:
138
139
140 Example #3: Mixing two sounds with better looping.
141
142 #include "SDL_sound.h"
143
144 int main(int argc, char **argv)
145 {
146 Sound_MixInit(NULL); // start the mixer; don't care what format.
147 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
148 Sound_Sample *music = Sound_NewSampleFromFile("icculus.ogg", NULL, 10240);
149 Sound_SetAttribute(hello, SOUND_ATTR_LOOPING, 1); // turn on looping.
150 Sound_MixPlay(music);
151 Sound_MixPlay(hello);
152 while (Sound_MixPlaying(music))
153 SDL_Delay(100); // wait around.
154 Sound_FreeSample(music);
155 Sound_FreeSample(hello); // will stop now.
156 Sound_MixDeinit();
157 return(0);
158 }
159
160 ...it's that easy.
161
162
163 Basic attribute #2: Fire and forget
164
165 You'll notice in previous examples that we are taking the pains to explicitly
166 free the resources associated with a sample via the Sound_FreeSample() call.
167 In a small program like this, it's easy to be tidy and sweep up after one
168 or two hardcoded sounds, but when you are managing a lot of different sounds,
169 or a lot of copies of the same sound, this can become tedious. Case in point:
170 laser beams.
171
172 Let's say you've got a space fighter game, with a bunch of ships flying
173 around and shooting at each other. Every time they fire a laser, do you really
174 want to take the effort to decide when it is done and clean it up? You want
175 to, quite literally in this case, "fire and forget" the sound...that is, you
176 want the mixer to playback the audio and then clean it up without further
177 action or intervention from you.
178
179 So let's take our previous example and adjust it to clean up after us.
180
181
182 Example #4: Fire and forget playback.
183
184 #include "SDL_sound.h"
185
186 int main(int argc, char **argv)
187 {
188 Sound_MixInit(NULL); // start the mixer; don't care what format.
189 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
190 Sound_Sample *music = Sound_NewSampleFromFile("icculus.ogg", NULL, 10240);
191 Sound_SetAttribute(hello, SOUND_ATTR_FIREANDFORGET, 1);
192 Sound_SetAttribute(music, SOUND_ATTR_FIREANDFORGET, 1);
193 Sound_MixPlay(music); // play once, then call Sound_FreeSample() for you.
194 Sound_MixPlay(hello); // play once, then call Sound_FreeSample() for you.
195 while (Sound_MixPlayingCount() > 0)
196 SDL_Delay(100); // wait around.
197
198 // Don't need Sound_FreeSample() here anymore!
199
200 Sound_MixDeinit();
201 return(0);
202 }
203
204 So everything was deallocated automatically, and your mother didn't even have
205 to come along and tell you to clean up this pig sty! She is very proud of you
206 right now, I assure you.
207
208 You'll note that we call Sound_MixPlayingCount() to see if the music finished.
209 You have to do this because the "music" sample is invalid once it gets pushed
210 through Sound_FreeSample(), which will happen as soon as the mixer is done
211 with it. To avoid touching deallocated memory, we just ask the mixer if
212 anything is still playing.
213
214 Also, common sense dictates that looping sounds never get to the "forget"
215 part of "fire and forget", since they don't stop playing. You can either
216 manually halt them or turn off the looping, though, and then they'll clean
217 themselves up.
218
219
220 Basic attribute #3: Per-channel Gain.
221
222 If you can tweak the volume of the left or right channel on a sample, you can
223 accomplish (or at least fake) a surprising number of simple sound effects.
224 Therefore the mixer allows you to do just this, and then builds a few features
225 on top of this magic.
226
227 This is accomplished by tweaking the "gain" of a given channel. "Gain" is just
228 a fancy way of saying "volume". You specify it as a floating point number,
229 usually in the range of 0.0f to 2.0f. If you set the gain to 0.0f, it results
230 in silence, and 1.0f results in no change at all. 0.5f halves the volume and
231 2.0f doubles it. As you might have guessed, the sample gets multiplied by
232 this value.
233
234 SDL_sound's mixer lets you tweak each channel in a sample individually. Right
235 now we're limited to mono (one channel) and stereo (two channel) sounds, but
236 this will probably be enhanced at some point. It's worth noting that this
237 refers not to the sample itself but to the speakers where they play. This
238 means you can set the left and right channels of a sample, even though the
239 sample itself only has one. Since a 2-speaker setup will promote a mono sound
240 to stereo (same waveform is fed to each speaker), you can tweak it to play at
241 different volumes in the left and right.
242
243
244 So to rehash our tired hello world example again...
245
246 Example #5: Per-channel gain.
247
248 #include "SDL_sound.h"
249
250 int main(int argc, char **argv)
251 {
252 Sound_MixInit(NULL); // start the mixer; don't care what format.
253 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
254
255 // Every channel's gain defaults to 1.0f, or no adjustment.
256
257 Sound_SetAttribute(hello, SOUND_ATTRGAIN0, 0.0f); // left chan==silence.
258 Sound_MixPlay(hello); // plays just right channel.
259 while (Sound_MixPlaying(hello))
260 SDL_Delay(100); // wait around.
261
262 Sound_SetAttribute(hello, SOUND_ATTRGAIN0, 1.0f); // left chan==normal
263 Sound_SetAttribute(hello, SOUND_ATTRGAIN1, 0.0f); // right chan==silence
264 Sound_MixPlay(hello); // plays just left channel.
265 while (Sound_MixPlaying(hello))
266 SDL_Delay(100); // wait around.
267
268 Sound_FreeSample(hello);
269 Sound_MixDeinit();
270 return(0);
271 }
272
273
274 This played the hello sound twice, once in each speaker. Simple.
275
276 Well, almost simple. If you only have mono output (one speaker), then this
277 will play silence the first time (channel 0 set to silence), then the sound
278 at normal volume the second time (channel 0, the only speaker, set to normal).
279 In a 1-speaker setup, screwing with the second channel is ignored.
280
281 If this is going to be a pain for you to track yourself, you can use
282 Sound_MixInit() to set up a stereo environment and let it dither everything
283 down to one speaker behind the scenes if need be. Generally, this isn't a
284 huge concern, though.
285
286
287 Extra Credit #2: Fading
288
289 Sometimes you want to fade out (or fade in) a sound over time...this is
290 handy when ending a game level. It's a nicer effect to silence everything
291 over some small amount of time than to abruptly kill all the noise. This is
292 more pleasant for the end-user.
293
294 You could accomplish this by tweaking each channel of all your samples' gain
295 over time, but this is another one of those things that are annoying to
296 micromanage. The mixer has to constantly pay attention to these samples anyhow,
297 why should you do it, too?
298
299 SDL_sound gives you a means to instruct the mixer to take care of this.
300
301
302 Example #6: Fading a sound.
303
304 #include "SDL_sound.h"
305
306 int main(int argc, char **argv)
307 {
308 Sound_MixInit(NULL); // start the mixer; don't care what format.
309 Sound_Sample *music = Sound_NewSampleFromFile("icculus.ogg", NULL, 10240);
310 Sound_MixPlay(music); // Start music playing.
311 Sound_SetAttribute(music, SOUND_ATTR_FADEOUT, 10000);
312 SDL_Delay(10000);
313 Sound_SetAttribute(music, SOUND_ATTR_FADEIN, 10000);
314 SDL_Delay(10000);
315 Sound_FreeSample(music);
316 Sound_MixDeinit();
317 return(0);
318 }
319
320
321 So this starts our favorite song playing, and tells it to fade to silence
322 smoothly over 10000 milliseconds (that is, 10 seconds). Since we know how
323 long we want this to take, we lazily call SDL_Delay() to wait that long; the
324 mixer works in another thread, so we have the luxury of doing nothing here.
325 Then we fade it back in over another 10 seconds before exiting.
326
327 It's worth noting a few things here:
328 First, the FADEOUT attribute uses the same mechanism as SetGain() under the
329 hood when mixing, but the two attributes exist seperately: if you double the
330 gain (2.0f), the sound will drop in volume twice as much each time the fading
331 updates, but it's still going to go from twice-as-loud to silence in the
332 same amount of time (10000 milliseconds in this case).
333
334 When a sample is totally silent, either because it faded out or you set its
335 gain to 0.0f, it is still playing! If you were to turn the volume back up
336 30 seconds after the fade completes, you'd hear the sound as it would be at
337 that 30 second moment as if you hadn't silenced it at all. This has a few
338 important ramifications:
339 1) It's still taking CPU time. Maybe not as much, since we can choose not
340 to mix when the gain is 0.0f, but in order to keep the sound "playing"
341 we might need to decode more of it, which means CPU time and memory
342 usage and such. Best to halt a silent sound if you aren't going to need
343 it.
344 2) Sound_MixPlayingCount() might be > 0 even though you don't hear noise.
345 3) A sound might not be where you left it. Keep better track of your things!
346
347 You might also notice that we called Sound_FreeSample() on a playing sample.
348 This is legal. If a sample is playing when you free it, the mixer knows to
349 halt it first.
350
351
352 Extra Credit #3: Halting
353
354 SDL_sound's mixer is a little different than most, in that there aren't
355 seperate playing states. "Halting" a mixing sample means you took it out of
356 the mixing list. "Playing" it means you put it back in, and it picks up the
357 mixing with the sample as it found it.
358
359 If you want something anologous to that "stop" button in WinAmp, you would
360 halt the sample and then call Sound_Rewind() on it. Next time you start it
361 playing, it'll be playing from the start of the sample. If you didn't call
362 Sound_Rewind() first, it'll be playing from where you halted it. That's more
363 like clicking WinAmp's "pause" button.
364
365 However, there are times when you want everything to stop at once. Just
366 looping over every sample and halting them isn't any good, since some might
367 play just a tiny bit longer...it's a lovely bug called a "race condition".
368
369 And, as I'm sure you've heard me say before, why do a lot of work to manage
370 stuff that the mixer has to manage itself anyhow? You should learn to
371 delegate more, you control freak.
372
373
374 Example #7: Halting.
375
376 #include "SDL_sound.h"
377
378 int main(int argc, char **argv)
379 {
380 Sound_MixInit(NULL); // start the mixer; don't care what format.
381 Sound_Sample *hello = Sound_NewSampleFromFile("hello.wav", NULL, 10240);
382 Sound_Sample *chatter = Sound_NewSampleFromFile("chatter.wav", NULL, 10240);
383 Sound_Sample *music = Sound_NewSampleFromFile("icculus.ogg", NULL, 10240);
384 Sound_MixPlay(music);
385
386 Sound_MixPlay(hello);
387 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
388 Sound_MixPlay(hello);
389 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
390 Sound_MixPlay(hello);
391 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
392
393 Sound_MixHalt(music); // halt the music.
394
395 Sound_MixPlay(hello);
396 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
397 Sound_MixPlay(hello);
398 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
399 Sound_MixPlay(hello);
400 while (Sound_MixPlaying(hello)) SDL_Delay(100); // wait around.
401
402 Sound_MixPlay(music); // start the music where it left off.
403 Sound_MixPlay(chatter); // start the chatter.
404
405 SDL_Delay(3000); // let them play.
406 Sound_MixHalt(NULL); // halt _everything_ that's playing.
407 SDL_Delay(3000); // waste some time.
408 Sound_MixPlay(music); // start the music where it left off.
409 Sound_MixPlay(chatter); // start the chatter where it left off.
410 SDL_Delay(3000); // waste some more time.
411
412 Sound_FreeSample(music); // clean up and quit.
413 Sound_FreeSample(chatter);
414 Sound_FreeSample(hello);
415 Sound_MixDeinit();
416 return(0);
417 }
418
419
420 Ok, you following? That plays the music, plays "hello" three times while the
421 music is playing, halts the music and plays hello three times without anything
422 else, restarts the music where it left off mixed with "chatter" for three
423 seconds, stops everything (music and chatter in this case), waits three more
424 seconds of silence, restarts the music and chatter where it left off and
425 lets them play for three more seconds. Then it shuts it all down and goes
426 home.
427
428 Fun, huh?
429
430 There are some notable exceptions to this rule. When a sound gets to the end
431 of its mixing, it either halts or (if set looping) rewinds and starts playing
432 again without missing a beat. For these, you don't have to manually halt
433 (or manually restart, as it were).
434
435
436 You now have everything you need to make a game with robust audio. But for
437 some of you, that's not enough. You're never satisfied. You need the section
438 of this tutorial written for...
439
440
441 THE HARDCORE.
442
443 (These sections are brief and lack full examples. If you're hardcore, you
444 probably don't read wussy tutorials anyhow.)
445
446
447 Hardcore #1: low-overhead copying of a sample.
448
449 Let's say you've got a sound file that represents a laser blast. Everytime a
450 shot is fired in your game, you don't want to have the overhead of reloading
451 it from disk, decoding and mixing it on the fly!
452
453 Here are some tips for your efficiency:
454
455 - Opt for an uncompressed format, such as a standard .WAV file or even
456 raw PCM. For small, frequently used sounds, the bigger memory footprint
457 is usually an acceptable tradeoff to constant redecoding. In some cases,
458 we've found that compressing to, say, .ogg format actually makes the
459 file bigger if it's a very short sound.
460
461 - Put your sound into a memory block and point multiple memory RWOPS at
462 it: one per playing sound. There are functions in SDL_sound 2
463 for allocating these from a pool, which reduces allocation overhead
464 and memory fragmentation, and eliminates multiple trips to the disk
465 when you "read" the sound.
466
467 - Sound_Sample structures are allocated in a pool, too, so throwaway
468 sounds (specifically, ones using pooled RWOPS) don't thrash system
469 resources. Good for those fire-and-forget effects.
470
471 - It's trivial to roll a reference-counting RWOPS that lets you use the
472 same memory block for several playing sounds, and when the last one
473 closes it (all related Sound_Samples go through Sound_FreeSample()), it
474 deletes the original memory block. Handy if you only want to loosely
475 manage those buffers.
476
477 - Cull samples if you're playing too many. The app can decide which sounds
478 are important and assign them a priority, and let only the first X
479 highest priority sounds actually mix.
480
481 - Alternately, if you can swallow the memory: take a highly-compressed
482 file and put it into a Sound_Sample, call Sound_DecodeAll. Now, use
483 the sample's "decoded" field as raw PCM for other Sound_Samples using
484 above tricks. When you are done, clean up the other samples first, then
485 call Sound_FreeSample() on this one. This is extremely useful if you
486 want to reduce CPU usage for one sound that is otherwise compressed.
487 Memory usage doesn't grow exponentially with each simulataneous mixing
488 of this sound, because everyone is feeding from the same memory block,
489 so each new sample instance adds some bytes for the structures (which
490 might have been allocated in the pool already anyhow).
491
492
493 Hardcore #2: Predecoding and buffer sizes.
494
495 Take advantage of the 1.0 API for predecoding and altering the decode buffer
496 size. This gives you control over the memory/CPU tradeoff at mix time, as the
497 mixer will call Sound_Decode() when it needs more data from a playing sample.
498 How much decoding is done at that point depends on how much buffering is
499 available. If you predecode the whole thing with Sound_DecodeAll(), then the
500 mixer can focus on mixing and not spend time decoding.
501
502
503
504 Hardcore #3: Global gain, global fade.
505
506 Most attributes that apply to one sample can be applied to all by passing a
507 NULL for the first argument to Sound_SetAttribute(). Gain and fade are
508 examples of this. If you want everything to fade out at once, this is the
509 best, race-condition free way to do it.
510
511 Note that global attributes don't override (or overwrite) per-sample
512 attributes. If you set a sample's gain to 2.0 and the global gain to 0.5, the
513 sound plays at normal (1.0) gain...the sample's gain is still 2.0 when you
514 change the global gain thereafter.
515
516
517 Hardcore #4: Postmix callbacks.
518
519 You can register a callback function per-sample that is called when the mixer
520 has finished its work and is about to send the data to the audio hardware.
521 These generally run in seperate threads on most platforms, and as such must be
522 protected from your main code with mutexes.
523
524 These are useful if you are are writing a media player and want to show some
525 sort of visual feedback based on the playing audio data, or if you want to
526 tweak the sound with your own special effects.
527
528
529 Hardcore #5: Sample finished callback.
530
531 You can register a callback function per-sample that is called when the mixer
532 has completely finished mixing a non-looping sample. This is largely a nod to
533 SDL_mixer, where this was the most convenient way to clean up fire-and-forget
534 sounds, but most people will want to let SDL_sound handle those. This has
535 other good uses: it lets you know when sound events are complete if you are
536 adding cinematics/cut-scenes to your program, or perhaps when it's safe for
537 characters to speak again (it's strange when one actor is speaking two
538 overlapping lines of dialogue, for example).
539
540
541 Hardcore #6: Your suggestion here!
542
543 The goal is to try and make audio work fairly painless for the game developer,
544 which means that if there is a good way to generalize functionality into the
545 mixer layer, we probably should. Comments are welcome!
546
547 It's worth noting that this tutorial covers common usage patterns and the Big
548 Important Things, so a lot of support API isn't covered here. For example,
549 important things like being able to query sample attributes weren't important
550 enough to mention here, but that doesn't mean you can't do it).
551
552 // end of mixer.txt ...
553