DEADSOFTWARE

d8a1312669a7f7364b1807ae054fa9a9c7ef801f
[d2df-sdl.git] / src / engine / e_sound_sdl.inc
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 interface
17 uses
18 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
19 {$IFDEF USE_SDL}
20 SDL, SDL_mixer,
21 {$DEFINE SDL1MIXER}
22 {$UNDEF SDL2MIXER}
23 {$ELSE}
24 SDL2, SDL2_mixer,
25 {$UNDEF SDL1MIXER}
26 {$DEFINE SDL2MIXER}
27 {$ENDIF}
28 envvars, e_log, SysUtils;
30 type
31 TSoundRec = record
32 Data: Pointer;
33 Sound: PMix_Chunk;
34 Music: PMix_Music;
35 isMusic: Boolean;
36 Loops: Boolean;
37 nRefs: Integer;
38 end;
40 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
41 private
42 FChanNum: Integer; // <0: no channel allocated
44 protected
45 FID: DWORD;
46 FMusic: Boolean;
47 FPosition: DWORD;
48 FPriority: Integer;
50 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
51 function GetChan (): Integer;
53 property Channel: Integer read GetChan;
55 public
56 constructor Create();
57 destructor Destroy(); override;
58 procedure SetID(ID: DWORD);
59 procedure FreeSound();
60 function IsPlaying(): Boolean;
61 procedure Stop();
62 function IsPaused(): Boolean;
63 procedure Pause(Enable: Boolean);
64 function GetVolume(): Single;
65 procedure SetVolume(Volume: Single);
66 function GetPan(): Single;
67 procedure SetPan(Pan: Single);
68 function IsMuted(): Boolean;
69 procedure Mute(Enable: Boolean);
70 function GetPosition(): DWORD;
71 procedure SetPosition(aPos: DWORD);
72 procedure SetPriority(priority: Integer);
73 end;
75 const
76 NO_SOUND_ID = DWORD(-1);
78 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
80 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
81 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
83 // returns channel number or -1
84 function e_PlaySound(ID: DWORD): Integer;
85 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
86 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
87 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
89 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
90 procedure e_MuteChannels(Enable: Boolean);
91 procedure e_StopChannels();
93 procedure e_DeleteSound(ID: DWORD);
94 procedure e_RemoveAllSounds();
95 procedure e_ReleaseSoundSystem();
96 procedure e_SoundUpdate();
98 var
99 e_SoundsArray: array of TSoundRec = nil;
101 implementation
103 uses
104 g_window, g_options;
106 const
107 N_CHANNELS = 512;
108 N_MUSCHAN = N_CHANNELS+42;
110 type
111 TChanInfo = record
112 id: DWORD; // sound id
113 muted: Boolean;
114 oldvol: Integer; // for muted
115 pan: Single;
116 end;
118 var
119 SoundMuted: Boolean = False;
120 SoundInitialized: Boolean = False;
121 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
122 MusVolume: Integer = MIX_MAX_VOLUME;
125 procedure chanFinished (chan: Integer); cdecl;
126 begin
127 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
128 if (chan >= 0) and (chan < N_CHANNELS) then
129 begin
130 if ChanSIds[chan].id <> NO_SOUND_ID then
131 begin
132 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
133 begin
134 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
135 end;
136 ChanSIds[chan].id := NO_SOUND_ID;
137 end;
138 end;
139 end;
142 procedure dumpMusicType (ms: PMix_Music);
143 begin
144 if ms = nil then
145 begin
146 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
147 end
148 else
149 begin
150 case Mix_GetMusicType(ms^) of
151 TMix_MusicType.MUS_NONE:
152 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
153 TMix_MusicType.MUS_CMD:
154 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
155 TMix_MusicType.MUS_WAV:
156 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
157 TMix_MusicType.MUS_MOD:
158 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
159 TMix_MusicType.MUS_MID:
160 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
161 TMix_MusicType.MUS_OGG:
162 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
163 TMix_MusicType.MUS_MP3:
164 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
165 TMix_MusicType.MUS_MP3_MAD:
166 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
167 TMix_MusicType.MUS_FLAC:
168 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
169 TMix_MusicType.MUS_MODPLUG:
170 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
171 otherwise
172 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
173 end;
174 end;
175 end;
177 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
178 var
179 res, i: Integer;
180 rfreq: Integer;
181 rformat: UInt16;
182 rchans: Integer;
183 flags: Integer;
184 begin
185 if SoundInitialized then begin Result := true; Exit end;
187 Result := False;
188 SoundInitialized := False;
190 {$IFDEF HEADLESS}
191 // HACK: shit this into env and hope for the best
192 SetEnvVar('SDL_AUDIODRIVER', 'dummy');
193 {$ENDIF}
195 if NoOutput then begin Result := true; Exit end;
197 // wow, this is actually MIDI player!
198 // we need module player
199 flags := MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH;
200 {$IFDEF SDL2MIXER}
201 flags := flags or MIX_INIT_MODPLUG;
202 {$ENDIF}
203 res := Mix_Init(flags);
204 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
205 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
206 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
207 {$IFDEF SDL2MIXER}
208 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
209 {$ENDIF}
210 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
211 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
212 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
214 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
215 res := Mix_OpenAudio(gsSDLSampleRate, MIX_DEFAULT_FORMAT, 2, gsSDLBufferSize);
216 if res = -1 then
217 begin
218 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
219 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
220 Exit;
221 end;
223 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
224 begin
225 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
226 end;
228 for i := 0 to Mix_GetNumChunkDecoders()-1 do
229 begin
230 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
231 end;
232 for i := 0 to Mix_GetNumMusicDecoders()-1 do
233 begin
234 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
235 end;
237 Mix_AllocateChannels(N_CHANNELS);
238 Mix_ChannelFinished(chanFinished);
240 for i := 0 to N_CHANNELS-1 do
241 begin
242 ChanSIds[i].id := NO_SOUND_ID;
243 ChanSIds[i].muted := SoundMuted;
244 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
245 ChanSIds[i].pan := 1.0;
246 end;
247 MusVolume := MIX_MAX_VOLUME;
249 SoundInitialized := True;
250 Result := True;
251 end;
253 function e_isMusic (id: DWORD): Boolean;
254 begin
255 Result := False;
256 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
257 begin
258 Result := (e_SoundsArray[id].Music <> nil);
259 end;
260 end;
262 function e_isSound (id: DWORD): Boolean;
263 begin
264 Result := False;
265 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
266 begin
267 Result := (e_SoundsArray[id].Sound <> nil);
268 end;
269 end;
271 function FindESound(): DWORD;
272 var
273 i: Integer;
274 begin
275 if e_SoundsArray <> nil then
276 begin
277 for i := 0 to High(e_SoundsArray) do
278 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
279 begin
280 Result := i;
281 Exit;
282 end;
283 end;
284 if e_SoundsArray = nil then
285 begin
286 SetLength(e_SoundsArray, 16);
287 Result := 0;
288 end
289 else
290 begin
291 Result := High(e_SoundsArray) + 1;
292 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
293 end;
294 for i := Result to High(e_SoundsArray) do
295 begin
296 e_SoundsArray[i].Sound := nil;
297 e_SoundsArray[i].Music := nil;
298 e_SoundsArray[i].Data := nil;
299 e_SoundsArray[i].isMusic := False;
300 e_SoundsArray[i].nRefs := 0;
301 end;
302 end;
304 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
305 var
306 find_id: DWORD;
307 begin
308 ID := NO_SOUND_ID;
309 Result := False;
310 if not SoundInitialized then Exit;
312 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
313 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
316 if isMusic then
317 begin
318 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
319 Exit;
320 end;
323 find_id := FindESound();
325 e_SoundsArray[find_id].Data := nil;
326 e_SoundsArray[find_id].isMusic := isMusic;
327 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
328 e_SoundsArray[find_id].nRefs := 0;
330 if isMusic then
331 begin
332 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
333 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
334 if e_SoundsArray[find_id].Music = nil then
335 begin
336 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
337 e_WriteLog(Mix_GetError(), TMsgType.Warning);
338 Exit;
339 end;
340 dumpMusicType(e_SoundsArray[find_id].Music);
341 end
342 else
343 begin
344 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
345 if e_SoundsArray[find_id].Sound = nil then Exit;
346 end;
348 ID := find_id;
350 Result := True;
351 end;
353 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
354 var
355 find_id: DWORD;
356 rw: PSDL_RWops;
357 //pc: PChar;
358 isid3: Boolean;
359 begin
360 ID := NO_SOUND_ID;
361 Result := False;
362 if not SoundInitialized then Exit;
363 isid3 := False;
366 if isMusic then
367 begin
368 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
369 Exit;
370 end;
373 //FIXME: correctly skip ID3
375 pc := PChar(pData);
376 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
377 begin
378 isid3 := True;
379 Inc(pc, $400);
380 pData := Pointer(pc);
381 Dec(Length, $400);
382 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
383 end;
386 rw := SDL_RWFromConstMem(pData, Length);
387 if rw = nil then Exit;
389 find_id := FindESound();
391 e_SoundsArray[find_id].Data := pData;
392 if isid3 then e_SoundsArray[find_id].Data := nil;
393 e_SoundsArray[find_id].isMusic := isMusic;
394 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
395 e_SoundsArray[find_id].nRefs := 0;
397 if isMusic then
398 begin
399 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
400 {$IFDEF SDL1MIXER}
401 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw);
402 {$ELSE}
403 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
404 {$ENDIF}
405 if e_SoundsArray[find_id].Music = nil then
406 begin
407 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
408 e_WriteLog(Mix_GetError(), TMsgType.Warning);
409 end
410 else
411 begin
412 dumpMusicType(e_SoundsArray[find_id].Music);
413 end;
414 //SDL_FreeRW(rw);
416 if e_SoundsArray[find_id].Music <> nil then
417 begin
418 Mix_FreeMusic(e_SoundsArray[find_id].Music);
419 end;
420 e_SoundsArray[find_id].Music := nil;
421 Exit;
423 end
424 else
425 begin
426 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
427 end;
428 //SDL_FreeRW(rw); // somehow it segfaults...
429 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
430 begin
431 e_SoundsArray[find_id].Data := nil;
432 Exit;
433 end;
435 ID := find_id;
437 Result := True;
438 end;
440 function e_PlaySound (ID: DWORD): Integer;
441 var
442 res: Integer = -1;
443 loops: Integer = 0;
444 begin
445 Result := -1;
446 if not SoundInitialized then Exit;
448 if e_isSound(ID) then
449 begin
450 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
451 Inc(e_SoundsArray[ID].nRefs);
452 if e_SoundsArray[ID].Loops then loops := -1;
453 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
454 if res >= 0 then
455 begin
456 ChanSIds[res].id := ID;
457 ChanSIds[res].muted := SoundMuted;
458 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
460 if e_SoundsArray[ID].isMusic then
461 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
462 else
463 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
465 end;
466 end
467 else
468 begin
469 if not e_isMusic(ID) then Exit;
470 Mix_HaltMusic();
471 if e_SoundsArray[ID].Loops then loops := -1;
472 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
473 if res >= 0 then res := N_MUSCHAN;
474 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
475 Result := res;
476 end;
478 Result := res;
479 end;
481 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
482 var
483 l, r: UInt8;
484 begin
485 Result := True;
486 if chan = N_MUSCHAN then
487 begin
488 // no panning for music
489 end
490 else if chan >= 0 then
491 begin
492 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
493 Pan := Pan+1.0; // 0..2
494 l := trunc(127.0*(2.0-Pan));
495 r := trunc(127.0*Pan);
496 Mix_SetPanning(chan, l, r);
497 ChanSIds[chan].pan := Pan;
498 end
499 else
500 begin
501 Result := False;
502 end;
503 end;
505 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
506 var
507 vol: Integer;
508 begin
509 Result := True;
510 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
511 vol := trunc(Volume*MIX_MAX_VOLUME);
512 if chan = N_MUSCHAN then
513 begin
514 MusVolume := vol;
515 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
516 end
517 else if chan >= 0 then
518 begin
519 ChanSIds[chan].oldvol := vol;
520 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
521 end
522 else
523 begin
524 Result := False;
525 end;
526 end;
528 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
529 var
530 chan: Integer;
531 begin
532 Result := -1;
533 chan := e_PlaySound(ID);
534 e_chanSetPan(chan, Pan);
535 Result := chan;
536 end;
538 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
539 var
540 chan: Integer;
541 begin
542 Result := -1;
543 chan := e_PlaySound(ID);
544 e_chanSetVol(chan, Volume);
545 Result := chan;
546 end;
548 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
549 var
550 chan: Integer;
551 begin
552 Result := -1;
553 chan := e_PlaySound(ID);
554 e_chanSetPan(chan, Pan);
555 e_chanSetVol(chan, Volume);
556 Result := chan;
557 end;
559 procedure e_DeleteSound(ID: DWORD);
560 var
561 i: Integer;
562 begin
563 if ID > High(e_SoundsArray) then Exit;
564 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
566 for i := 0 to N_CHANNELS-1 do
567 begin
568 if ChanSIds[i].id = ID then
569 begin
570 ChanSIds[i].id := NO_SOUND_ID;
571 Mix_HaltChannel(i);
572 end;
573 end;
575 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
576 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
577 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
579 e_SoundsArray[ID].Sound := nil;
580 e_SoundsArray[ID].Music := nil;
581 e_SoundsArray[ID].Data := nil;
582 e_SoundsArray[ID].nRefs := 0;
583 end;
585 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
586 var
587 i: Integer;
588 vol: Single;
589 ovol: Integer;
590 begin
591 for i := 0 to N_CHANNELS-1 do
592 begin
593 ovol := ChanSIds[i].oldvol;
594 if setMode then
595 begin
596 vol := SoundMod;
597 end
598 else
599 begin
600 vol := (MIX_MAX_VOLUME+0.0)/ovol;
601 vol := vol*SoundMod;
602 end;
603 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
604 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
605 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
606 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
607 end;
608 ovol := Mix_VolumeMusic(-1);
609 if ovol >= 0 then
610 begin
611 if setMode then
612 begin
613 vol := SoundMod;
614 end
615 else
616 begin
617 vol := (MIX_MAX_VOLUME+0.0)/ovol;
618 vol := vol * SoundMod;
619 end;
620 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
621 MusVolume := trunc(vol*MIX_MAX_VOLUME);
622 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
623 end;
624 end;
626 procedure e_MuteChannels(Enable: Boolean);
627 var
628 i: Integer;
629 begin
630 //if Enable = SoundMuted then Exit;
631 SoundMuted := Enable;
632 for i := 0 to N_CHANNELS-1 do
633 begin
634 if ChanSIds[i].muted <> SoundMuted then
635 begin
636 ChanSIds[i].muted := SoundMuted;
637 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
638 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
639 end;
640 end;
641 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
642 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
643 end;
645 procedure e_StopChannels();
646 var
647 i: Integer;
648 begin
649 Mix_HaltChannel(-1);
650 Mix_HaltMusic();
651 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
652 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
653 end;
655 procedure e_RemoveAllSounds();
656 var
657 i: Integer;
658 begin
659 if SoundInitialized then e_StopChannels();
660 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
661 SetLength(e_SoundsArray, 0);
662 e_SoundsArray := nil;
663 end;
665 procedure e_ReleaseSoundSystem();
666 begin
667 e_RemoveAllSounds();
668 if SoundInitialized then
669 begin
670 Mix_CloseAudio();
671 SoundInitialized := False;
672 end;
673 end;
675 procedure e_SoundUpdate();
676 begin
677 //FMOD_System_Update(F_System);
678 end;
681 { TBasicSound: }
683 constructor TBasicSound.Create();
684 begin
685 FID := NO_SOUND_ID;
686 FMusic := False;
687 FChanNum := -1;
688 FPosition := 0;
689 FPriority := 128;
690 end;
692 destructor TBasicSound.Destroy();
693 begin
694 FreeSound();
695 inherited;
696 end;
698 function TBasicSound.GetChan (): Integer;
699 begin
700 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
701 begin
702 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
703 end
704 else if e_isMusic(FID) then
705 begin
706 FChanNum := N_MUSCHAN;
707 end;
708 Result := FChanNum;
709 end;
711 procedure TBasicSound.FreeSound();
712 begin
713 if FID = NO_SOUND_ID then Exit;
714 Stop();
715 FID := NO_SOUND_ID;
716 FMusic := False;
717 FPosition := 0;
718 FChanNum := -1;
719 end;
721 // aPos: msecs
722 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
723 begin
724 Result := False;
725 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
726 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
727 Result := (FChanNum >= 0);
728 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
729 //TODO: aPos
730 end;
732 procedure TBasicSound.SetID(ID: DWORD);
733 begin
734 FreeSound();
735 FID := ID;
736 if ID <> NO_SOUND_ID then
737 begin
738 FMusic := e_SoundsArray[ID].isMusic;
739 end;
740 FChanNum := -1;
741 end;
743 function TBasicSound.IsPlaying(): Boolean;
744 var
745 chan: Integer;
746 begin
747 Result := False;
748 if e_isSound(FID) then
749 begin
750 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
751 chan := Channel;
752 if chan < 0 then
753 begin
754 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
755 Exit;
756 end;
757 //Result := (Mix_Playing(chan) > 0)
758 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
759 Result := True;
760 end
761 else if e_isMusic(FID) then
762 begin
763 Result := (Mix_PlayingMusic() > 0);
764 end;
765 end;
767 procedure TBasicSound.Stop();
768 var
769 chan: Integer;
770 begin
771 if e_isSound(FID) then
772 begin
773 chan := Channel;
774 if chan >= 0 then
775 begin
776 //GetPosition();
777 Mix_HaltChannel(chan);
778 end;
779 end
780 else if e_isMusic(FID) then
781 begin
782 Mix_HaltMusic();
783 end;
784 FChanNum := -1;
785 end;
787 function TBasicSound.IsPaused(): Boolean;
788 var
789 chan: Integer;
790 begin
791 Result := False;
792 if e_isSound(FID) then
793 begin
794 chan := Channel;
795 if chan < 0 then Exit;
796 Result := (Mix_Paused(chan) > 0);
797 end
798 else if e_isMusic(FID) then
799 begin
800 Result := (Mix_PausedMusic() > 0);
801 end;
802 end;
804 procedure TBasicSound.Pause(Enable: Boolean);
805 var
806 chan: Integer;
807 pl: Boolean;
808 begin
809 Enable := not Enable; // fuckin' double negation
810 if e_isSound(FID) then
811 begin
812 chan := Channel;
813 if chan < 0 then Exit;
814 pl := not (Mix_Paused(chan) > 0);
815 if pl <> Enable then
816 begin
817 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
818 end;
819 end
820 else if e_isMusic(FID) then
821 begin
822 pl := not (Mix_PausedMusic() > 0);
823 if pl <> Enable then
824 begin
825 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
826 end;
827 end;
829 if Enable then
830 begin
831 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
832 if res <> FMOD_OK then
833 begin
834 end;
835 end;
837 end;
839 function TBasicSound.GetVolume(): Single;
840 var
841 chan: Integer;
842 begin
843 Result := 0.0;
844 if e_isSound(FID) then
845 begin
846 chan := Channel;
847 if chan < 0 then Exit;
848 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
849 end
850 else if e_isMusic(FID) then
851 begin
852 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
853 end;
854 end;
856 procedure TBasicSound.SetVolume(Volume: Single);
857 var
858 chan: Integer;
859 begin
860 if e_isSound(FID) then
861 begin
862 chan := Channel;
863 if chan < 0 then Exit;
864 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
865 e_chanSetVol(chan, Volume);
866 end
867 else if e_isMusic(FID) then
868 begin
869 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
870 e_chanSetVol(N_MUSCHAN, Volume);
871 end;
872 end;
874 function TBasicSound.GetPan(): Single;
875 var
876 chan: Integer;
877 begin
878 Result := 1.0;
879 if e_isSound(FID) then
880 begin
881 chan := Channel;
882 if chan < 0 then Exit;
883 Result := ChanSIds[chan].pan;
884 end;
885 end;
887 procedure TBasicSound.SetPan(Pan: Single);
888 var
889 chan: Integer;
890 begin
891 if e_isSound(FID) then
892 begin
893 chan := Channel;
894 if chan < 0 then Exit;
895 e_chanSetPan(chan, Pan);
896 end;
897 end;
899 function TBasicSound.IsMuted(): Boolean;
900 var
901 chan: Integer;
902 begin
903 Result := False;
904 if e_isSound(FID) then
905 begin
906 chan := Channel;
907 if chan < 0 then Exit;
908 Result := ChanSIds[chan].muted;
909 end
910 else if e_isMusic(FID) then
911 begin
912 Result := SoundMuted;
913 end;
914 end;
916 procedure TBasicSound.Mute(Enable: Boolean);
917 var
918 chan: Integer;
919 begin
920 if e_isSound(FID) then
921 begin
922 chan := Channel;
923 if chan < 0 then Exit;
924 if ChanSIds[chan].muted <> Enable then
925 begin
926 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
927 ChanSIds[chan].muted := Enable;
928 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
929 end;
930 end
931 else if e_isMusic(FID) then
932 begin
933 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
934 end;
935 end;
937 //TODO
938 function TBasicSound.GetPosition(): DWORD;
939 begin
940 Result := 0;
942 if FChanNum < 0 then Exit;
943 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
944 if res <> FMOD_OK then
945 begin
946 Exit;
947 end;
948 Result := FPosition;
950 end;
952 //TODO
953 procedure TBasicSound.SetPosition(aPos: DWORD);
954 begin
955 FPosition := aPos;
957 if FChanNum < 0 then Exit;
958 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
959 if res <> FMOD_OK then
960 begin
961 end;
963 end;
965 //TODO
966 procedure TBasicSound.SetPriority(priority: Integer);
967 begin
969 if (FChanNum <> nil) and (FPriority <> priority) and
970 (priority >= 0) and (priority <= 256) then
971 begin
972 FPriority := priority;
973 res := FMOD_Channel_SetPriority(FChanNum, priority);
974 if res <> FMOD_OK then
975 begin
976 end;
977 end;
979 end;
981 end.