Mercurial > SDL_sound_CoreAudio
annotate mixer/tutorial.txt @ 534:a116d8f628a0
Merged r541:543 from branches/stable-1.0: Speex include directory fix.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 17 Apr 2008 17:56:09 +0000 |
parents | 3a3807dcf57f |
children |
rev | line source |
---|---|
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 | |
523
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
541 Hardcore #6: Procedural samples. |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
542 !!! WRITEME: Hook up a RAW Sound_Sample to a RWOPS that generates sound on |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
543 !!! WRITEME: read, so you can mix in a procedural sample instead of just |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
544 !!! WRITEME: pre/post mixing. |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
545 |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
546 |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
547 |
3a3807dcf57f
Minor tutorial.txt update.
Ryan C. Gordon <icculus@icculus.org>
parents:
478
diff
changeset
|
548 Hardcore #7: Your suggestion here! |
478 | 549 |
550 The goal is to try and make audio work fairly painless for the game developer, | |
551 which means that if there is a good way to generalize functionality into the | |
552 mixer layer, we probably should. Comments are welcome! | |
553 | |
554 It's worth noting that this tutorial covers common usage patterns and the Big | |
555 Important Things, so a lot of support API isn't covered here. For example, | |
556 important things like being able to query sample attributes weren't important | |
557 enough to mention here, but that doesn't mean you can't do it). | |
558 | |
559 // end of mixer.txt ... | |
560 |