31 * Looped Sound - Subtype of sound: plays in a loop using a |
31 * Looped Sound - Subtype of sound: plays in a loop using a |
32 * "channel", of which the id is returned on start. |
32 * "channel", of which the id is returned on start. |
33 * The channel id can be used to stop a specific sound loop. |
33 * The channel id can be used to stop a specific sound loop. |
34 *) |
34 *) |
35 interface |
35 interface |
36 uses SDLh, uConsts, uTypes, sysutils; |
36 uses SDLh, uConsts, uTypes, SysUtils; |
37 |
|
38 var MusicFN: shortstring; // music file name |
|
39 previousVolume: LongInt; // cached volume value |
|
40 |
37 |
41 procedure initModule; |
38 procedure initModule; |
42 procedure freeModule; |
39 procedure freeModule; |
43 |
40 |
44 procedure InitSound; // Initiates sound-system if isSoundEnabled. |
41 procedure InitSound; // Initiates sound-system if isSoundEnabled. |
45 procedure ReleaseSound(complete: boolean); // Releases sound-system and used resources. |
42 procedure ReleaseSound(complete: boolean); // Releases sound-system and used resources. |
46 procedure SoundLoad; // Preloads some sounds for performance reasons. |
43 procedure ResetSound; // Reset sound state to the previous state. |
47 |
44 procedure SetSound(enabled: boolean); // Enable/disable sound-system and backup status. |
48 |
45 |
49 // MUSIC |
46 // MUSIC |
50 |
47 |
51 // Obvious music commands for music track specified in MusicFN. |
48 // Obvious music commands for music track |
52 procedure PlayMusic; |
49 procedure SetMusic(enabled: boolean); // Enable/disable music. |
53 procedure PauseMusic; |
50 procedure SetMusicName(musicname: shortstring); // Enable/disable music and set name of musicfile to play. |
54 procedure ResumeMusic; |
51 procedure PlayMusic; // Play music from the start. |
55 procedure ChangeMusic; // Replaces music track with current MusicFN and plays it. |
52 procedure PauseMusic; // Pause music. |
56 procedure StopMusic; // Stops and releases the current track |
53 procedure ResumeMusic; // Resume music from pause point. |
|
54 procedure ChangeMusic(musicname: shortstring); // Replaces music track with musicname and plays it. |
|
55 procedure StopMusic; // Stops and releases the current track. |
57 |
56 |
58 |
57 |
59 // SOUNDS |
58 // SOUNDS |
60 |
59 |
61 // Plays the sound snd [from a given voicepack], |
60 // Plays the sound snd [from a given voicepack], |
62 // if keepPlaying is given and true, |
61 // if keepPlaying is given and true, |
63 // then the sound's playback won't be interrupted if asked to play again. |
62 // then the sound's playback won't be interrupted if asked to play again. |
64 procedure PlaySound(snd: TSound); |
63 procedure PlaySound(snd: TSound); |
65 procedure PlaySound(snd: TSound; keepPlaying: boolean); |
64 procedure PlaySound(snd: TSound; keepPlaying: boolean); |
66 procedure PlaySound(snd: TSound; voicepack: PVoicepack); |
65 procedure PlaySoundV(snd: TSound; voicepack: PVoicepack); |
67 procedure PlaySound(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean); |
66 procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean); |
68 |
67 |
69 // Plays sound snd [of voicepack] in a loop, but starts with fadems milliseconds of fade-in. |
68 // Plays sound snd [of voicepack] in a loop, but starts with fadems milliseconds of fade-in. |
70 // Returns sound channel of the looped sound. |
69 // Returns sound channel of the looped sound. |
71 function LoopSound(snd: TSound): LongInt; |
70 function LoopSound(snd: TSound): LongInt; |
72 function LoopSound(snd: TSound; fadems: LongInt): LongInt; |
71 function LoopSound(snd: TSound; fadems: LongInt): LongInt; |
73 function LoopSound(snd: TSound; voicepack: PVoicepack): LongInt; // WTF? |
72 function LoopSoundV(snd: TSound; voicepack: PVoicepack): LongInt; // WTF? |
74 function LoopSound(snd: TSound; voicepack: PVoicepack; fadems: LongInt): LongInt; |
73 function LoopSoundV(snd: TSound; voicepack: PVoicepack; fadems: LongInt): LongInt; |
75 |
74 |
76 // Stops the normal/looped sound of the given type/in the given channel |
75 // Stops the normal/looped sound of the given type/in the given channel |
77 // [with a fade-out effect for fadems milliseconds]. |
76 // [with a fade-out effect for fadems milliseconds]. |
78 procedure StopSound(snd: TSound); |
77 procedure StopSound(snd: TSound); |
79 procedure StopSound(chn: LongInt); |
78 procedure StopSoundChan(chn: LongInt); |
80 procedure StopSound(chn, fadems: LongInt); |
79 procedure StopSoundChan(chn, fadems: LongInt); |
81 |
80 |
82 procedure AddVoice(snd: TSound; voicepack: PVoicepack); |
81 procedure AddVoice(snd: TSound; voicepack: PVoicepack); |
83 procedure PlayNextVoice; |
82 procedure PlayNextVoice; |
84 |
83 |
85 |
84 |
86 // MISC |
85 // MISC |
87 |
86 |
|
87 // Set the initial volume |
|
88 procedure SetVolume(volume: LongInt); |
|
89 |
88 // Modifies the sound volume of the game by voldelta and returns the new volume level. |
90 // Modifies the sound volume of the game by voldelta and returns the new volume level. |
89 function ChangeVolume(voldelta: LongInt): LongInt; |
91 function ChangeVolume(voldelta: LongInt): LongInt; |
90 |
92 |
91 // Returns a pointer to the voicepack with the given name. |
93 // Returns a pointer to the voicepack with the given name. |
92 function AskForVoicepack(name: shortstring): Pointer; |
94 function AskForVoicepack(name: shortstring): Pointer; |
93 |
95 |
94 // Drastically lower the volume when we lose focus (and restore the previous value) |
96 // Drastically lower the volume when we lose focus (and restore the previous value). |
95 procedure DampenAudio; |
97 procedure DampenAudio; |
96 procedure UndampenAudio; |
98 procedure UndampenAudio; |
97 |
99 |
98 implementation |
100 implementation |
99 uses uVariables, uConsole, uUtils, uCommands, uDebug; |
101 uses uVariables, uConsole, uUtils, uCommands, uDebug; |
149 voicepacks[i].name:= name; |
158 voicepacks[i].name:= name; |
150 AskForVoicepack:= @voicepacks[i] |
159 AskForVoicepack:= @voicepacks[i] |
151 end; |
160 end; |
152 |
161 |
153 procedure InitSound; |
162 procedure InitSound; |
154 var i: TSound; |
163 const channels: LongInt = {$IFDEF MOBILE}1{$ELSE}2{$ENDIF}; |
155 channels: LongInt; |
|
156 begin |
164 begin |
157 if not isSoundEnabled then |
165 if not isSoundEnabled then |
158 exit; |
166 exit; |
159 WriteToConsole('Init sound...'); |
167 WriteToConsole('Init sound...'); |
160 isSoundEnabled:= SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0; |
168 isSoundEnabled:= SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0; |
161 |
169 |
162 {$IFDEF MOBILE} |
|
163 channels:= 1; |
|
164 {$ELSE} |
|
165 channels:= 2; |
|
166 {$ENDIF} |
|
167 |
|
168 if isSoundEnabled then |
170 if isSoundEnabled then |
169 isSoundEnabled:= Mix_OpenAudio(44100, $8010, channels, 1024) = 0; |
171 isSoundEnabled:= Mix_OpenAudio(44100, $8010, channels, 1024) = 0; |
170 |
|
171 WriteToConsole('Init SDL_mixer... '); |
|
172 SDLTry(Mix_Init(MIX_INIT_OGG) <> 0, true); |
|
173 WriteLnToConsole(msgOK); |
|
174 |
172 |
175 if isSoundEnabled then |
173 if isSoundEnabled then |
176 WriteLnToConsole(msgOK) |
174 WriteLnToConsole(msgOK) |
177 else |
175 else |
178 WriteLnToConsole(msgFailed); |
176 WriteLnToConsole(msgFailed); |
179 |
177 |
|
178 WriteToConsole('Init SDL_mixer... '); |
|
179 SDLTry(Mix_Init(MIX_INIT_OGG) <> 0, true); |
|
180 WriteLnToConsole(msgOK); |
|
181 |
180 Mix_AllocateChannels(Succ(chanTPU)); |
182 Mix_AllocateChannels(Succ(chanTPU)); |
181 if isMusicEnabled then |
183 ChangeVolume(cInitVolume); |
182 Mix_VolumeMusic(50); |
184 end; |
183 for i:= Low(TSound) to High(TSound) do |
185 |
184 lastChan[i]:= -1; |
186 procedure ResetSound; |
185 |
187 begin |
186 Volume:= 0; |
188 isSoundEnabled:= isSEBackup; |
187 ChangeVolume(cInitVolume) |
189 end; |
|
190 |
|
191 procedure SetSound(enabled: boolean); |
|
192 begin |
|
193 isSEBackup:= isSoundEnabled; |
|
194 isSoundEnabled:= enabled; |
188 end; |
195 end; |
189 |
196 |
190 // when complete is false, this procedure just releases some of the chucks on inactive channels |
197 // when complete is false, this procedure just releases some of the chucks on inactive channels |
191 // this way music is not stopped, nor are chucks currently being plauyed |
198 // in this way music is not stopped, nor are chucks currently being played |
192 procedure ReleaseSound(complete: boolean); |
199 procedure ReleaseSound(complete: boolean); |
193 var i: TSound; |
200 var i: TSound; |
194 t: Longword; |
201 t: Longword; |
195 begin |
202 begin |
196 // release and nil all sounds |
203 // release and nil all sounds |
222 |
229 |
223 Mix_CloseAudio(); |
230 Mix_CloseAudio(); |
224 end; |
231 end; |
225 end; |
232 end; |
226 |
233 |
227 procedure SoundLoad; |
|
228 var i: TSound; |
|
229 t: Longword; |
|
230 begin |
|
231 if not isSoundEnabled then |
|
232 exit; |
|
233 |
|
234 defVoicepack:= AskForVoicepack('Default'); |
|
235 |
|
236 // initialize all voices to nil so that they can be loaded when needed |
|
237 for t:= 0 to cMaxTeams do |
|
238 if voicepacks[t].name <> '' then |
|
239 for i:= Low(TSound) to High(TSound) do |
|
240 voicepacks[t].chunks[i]:= nil; |
|
241 |
|
242 for i:= Low(TSound) to High(TSound) do |
|
243 begin |
|
244 defVoicepack^.chunks[i]:= nil; |
|
245 (* this is not necessary when SDL_mixer is compiled with USE_OGG_TREMOR |
|
246 // preload all the big sound files (>32k) that would otherwise lockup the game |
|
247 if (i in [sndBeeWater, sndBee, sndCake, sndHellishImpact1, sndHellish, sndHomerun, |
|
248 sndMolotov, sndMortar, sndRideOfTheValkyries, sndYoohoo]) |
|
249 and (Soundz[i].Path <> ptVoices) and (Soundz[i].FileName <> '') then |
|
250 begin |
|
251 s:= UserPathz[Soundz[i].Path] + '/' + Soundz[i].FileName; |
|
252 if not FileExists(s) then s:= Pathz[Soundz[i].Path] + '/' + Soundz[i].FileName; |
|
253 WriteToConsole(msgLoading + s + ' '); |
|
254 defVoicepack^.chunks[i]:= Mix_LoadWAV_RW(SDL_RWFromFile(Str2PChar(s), 'rb'), 1); |
|
255 SDLTry(defVoicepack^.chunks[i] <> nil, true); |
|
256 WriteLnToConsole(msgOK); |
|
257 end;*) |
|
258 end; |
|
259 |
|
260 end; |
|
261 |
|
262 procedure PlaySound(snd: TSound); |
234 procedure PlaySound(snd: TSound); |
263 begin |
235 begin |
264 PlaySound(snd, nil, false); |
236 PlaySoundV(snd, nil, false); |
265 end; |
237 end; |
266 |
238 |
267 procedure PlaySound(snd: TSound; keepPlaying: boolean); |
239 procedure PlaySound(snd: TSound; keepPlaying: boolean); |
268 begin |
240 begin |
269 PlaySound(snd, nil, keepPlaying); |
241 PlaySoundV(snd, nil, keepPlaying); |
270 end; |
242 end; |
271 |
243 |
272 procedure PlaySound(snd: TSound; voicepack: PVoicepack); |
244 procedure PlaySoundV(snd: TSound; voicepack: PVoicepack); |
273 begin |
245 begin |
274 PlaySound(snd, voicepack, false); |
246 PlaySoundV(snd, voicepack, false); |
275 end; |
247 end; |
276 |
248 |
277 procedure PlaySound(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean); |
249 procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean); |
278 var s:shortstring; |
250 var s:shortstring; |
279 begin |
251 begin |
280 if (not isSoundEnabled) or fastUntilLag then |
252 if (not isSoundEnabled) or fastUntilLag then |
281 exit; |
253 exit; |
282 |
254 |
351 if (VoiceList[i].snd <> sndNone) then |
323 if (VoiceList[i].snd <> sndNone) then |
352 begin |
324 begin |
353 LastVoice.snd:= VoiceList[i].snd; |
325 LastVoice.snd:= VoiceList[i].snd; |
354 LastVoice.voicepack:= VoiceList[i].voicepack; |
326 LastVoice.voicepack:= VoiceList[i].voicepack; |
355 VoiceList[i].snd:= sndNone; |
327 VoiceList[i].snd:= sndNone; |
356 PlaySound(LastVoice.snd, LastVoice.voicepack) |
328 PlaySoundV(LastVoice.snd, LastVoice.voicepack) |
357 end |
329 end |
358 else LastVoice.snd:= sndNone; |
330 else LastVoice.snd:= sndNone; |
359 end; |
331 end; |
360 |
332 |
361 function LoopSound(snd: TSound): LongInt; |
333 function LoopSound(snd: TSound): LongInt; |
362 begin |
334 begin |
363 LoopSound:= LoopSound(snd, nil) |
335 LoopSound:= LoopSoundV(snd, nil) |
364 end; |
336 end; |
365 |
337 |
366 function LoopSound(snd: TSound; fadems: LongInt): LongInt; |
338 function LoopSound(snd: TSound; fadems: LongInt): LongInt; |
367 begin |
339 begin |
368 LoopSound:= LoopSound(snd, nil, fadems) |
340 LoopSound:= LoopSoundV(snd, nil, fadems) |
369 end; |
341 end; |
370 |
342 |
371 function LoopSound(snd: TSound; voicepack: PVoicepack): LongInt; |
343 function LoopSoundV(snd: TSound; voicepack: PVoicepack): LongInt; |
372 begin |
344 begin |
373 voicepack:= voicepack; // avoid compiler hint |
345 voicepack:= voicepack; // avoid compiler hint |
374 LoopSound:= LoopSound(snd, nil, 0) |
346 LoopSoundV:= LoopSoundV(snd, nil, 0) |
375 end; |
347 end; |
376 |
348 |
377 function LoopSound(snd: TSound; voicepack: PVoicepack; fadems: LongInt): LongInt; |
349 function LoopSoundV(snd: TSound; voicepack: PVoicepack; fadems: LongInt): LongInt; |
378 var s: shortstring; |
350 var s: shortstring; |
379 begin |
351 begin |
380 if (not isSoundEnabled) or fastUntilLag then |
352 if (not isSoundEnabled) or fastUntilLag then |
381 begin |
353 begin |
382 LoopSound:= -1; |
354 LoopSoundV:= -1; |
383 exit |
355 exit |
384 end; |
356 end; |
385 |
357 |
386 if (voicepack <> nil) then |
358 if (voicepack <> nil) then |
387 begin |
359 begin |
464 WriteLnToConsole(msgOK); |
436 WriteLnToConsole(msgOK); |
465 |
437 |
466 SDLTry(Mix_FadeInMusic(Mus, -1, 3000) <> -1, false) |
438 SDLTry(Mix_FadeInMusic(Mus, -1, 3000) <> -1, false) |
467 end; |
439 end; |
468 |
440 |
|
441 procedure SetVolume(volume: LongInt); |
|
442 begin |
|
443 cInitVolume:= volume; |
|
444 end; |
|
445 |
469 function ChangeVolume(voldelta: LongInt): LongInt; |
446 function ChangeVolume(voldelta: LongInt): LongInt; |
470 begin |
447 begin |
471 ChangeVolume:= 0; |
448 ChangeVolume:= 0; |
472 if not isSoundEnabled then |
449 if not isSoundEnabled then |
473 exit; |
450 exit; |
474 |
451 |
475 inc(Volume, voldelta); |
452 inc(Volume, voldelta); |
476 if Volume < 0 then |
453 if Volume < 0 then |
477 Volume:= 0; |
454 Volume:= 0; |
|
455 // apply Volume to all channels |
478 Mix_Volume(-1, Volume); |
456 Mix_Volume(-1, Volume); |
|
457 // get assigned Volume |
479 Volume:= Mix_Volume(-1, -1); |
458 Volume:= Mix_Volume(-1, -1); |
480 if isMusicEnabled then |
459 if isMusicEnabled then |
481 Mix_VolumeMusic(Volume * 4 div 8); |
460 Mix_VolumeMusic(Volume * 4 div 8); |
482 ChangeVolume:= Volume * 100 div MIX_MAX_VOLUME |
461 ChangeVolume:= Volume * 100 div MIX_MAX_VOLUME |
483 end; |
462 end; |
541 Delete(s, byte(s[0]), 1); |
533 Delete(s, byte(s[0]), 1); |
542 CurrentTeam^.voicepack:= AskForVoicepack(s) |
534 CurrentTeam^.voicepack:= AskForVoicepack(s) |
543 end; |
535 end; |
544 |
536 |
545 procedure initModule; |
537 procedure initModule; |
|
538 var t: LongInt; |
|
539 i: TSound; |
546 begin |
540 begin |
547 RegisterVariable('voicepack', @chVoicepack, false); |
541 RegisterVariable('voicepack', @chVoicepack, false); |
|
542 |
548 MusicFN:=''; |
543 MusicFN:=''; |
|
544 isMusicEnabled:= true; |
|
545 isSoundEnabled:= true; |
|
546 isSEBackup:= isSoundEnabled; |
|
547 cInitVolume:= 100; |
|
548 Volume:= 0; |
|
549 defVoicepack:= AskForVoicepack('Default'); |
|
550 |
|
551 for i:= Low(TSound) to High(TSound) do |
|
552 lastChan[i]:= -1; |
|
553 |
|
554 // initialize all voices to nil so that they can be loaded lazily |
|
555 for t:= 0 to cMaxTeams do |
|
556 if voicepacks[t].name <> '' then |
|
557 for i:= Low(TSound) to High(TSound) do |
|
558 voicepacks[t].chunks[i]:= nil; |
|
559 |
|
560 (* on MOBILE SDL_mixer has to be compiled against Tremor (USE_OGG_TREMOR) |
|
561 or sound files bigger than 32k will lockup the game*) |
|
562 for i:= Low(TSound) to High(TSound) do |
|
563 defVoicepack^.chunks[i]:= nil; |
|
564 |
549 end; |
565 end; |
550 |
566 |
551 procedure freeModule; |
567 procedure freeModule; |
552 begin |
568 begin |
553 if isSoundEnabled then |
569 if isSoundEnabled then |