478
|
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
|