DEADSOFTWARE

Game: Use proper syntax of sets for game options instead of raw bitwise operations
[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 e_TimidityDecoder: Boolean; (* sdl_mixer special *)
103 implementation
105 uses
106 g_window, g_options;
108 const
109 N_CHANNELS = 512;
110 N_MUSCHAN = N_CHANNELS+42;
112 type
113 TChanInfo = record
114 id: DWORD; // sound id
115 muted: Boolean;
116 oldvol: Integer; // for muted
117 pan: Single;
118 end;
120 var
121 SoundMuted: Boolean = False;
122 SoundInitialized: Boolean = False;
123 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
124 MusVolume: Integer = MIX_MAX_VOLUME;
127 procedure chanFinished (chan: Integer); cdecl;
128 begin
129 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
130 if (chan >= 0) and (chan < N_CHANNELS) then
131 begin
132 if ChanSIds[chan].id <> NO_SOUND_ID then
133 begin
134 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
135 begin
136 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
137 end;
138 ChanSIds[chan].id := NO_SOUND_ID;
139 end;
140 end;
141 end;
144 procedure dumpMusicType (ms: PMix_Music);
145 begin
146 if ms = nil then
147 begin
148 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
149 end
150 else
151 begin
152 case Mix_GetMusicType(ms^) of
153 TMix_MusicType.MUS_NONE:
154 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
155 TMix_MusicType.MUS_CMD:
156 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
157 TMix_MusicType.MUS_WAV:
158 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
159 TMix_MusicType.MUS_MOD:
160 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
161 TMix_MusicType.MUS_MID:
162 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
163 TMix_MusicType.MUS_OGG:
164 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
165 TMix_MusicType.MUS_MP3:
166 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
167 TMix_MusicType.MUS_MP3_MAD:
168 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
169 TMix_MusicType.MUS_FLAC:
170 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
171 TMix_MusicType.MUS_MODPLUG:
172 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
173 otherwise
174 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
175 end;
176 end;
177 end;
179 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
180 var
181 res, i: Integer;
182 rfreq: Integer;
183 rformat: UInt16;
184 rchans: Integer;
185 flags: Integer;
186 begin
187 if SoundInitialized then begin Result := true; Exit end;
189 Result := False;
190 SoundInitialized := False;
192 {$IFDEF HEADLESS}
193 // HACK: shit this into env and hope for the best
194 SetEnvVar('SDL_AUDIODRIVER', 'dummy');
195 {$ENDIF}
197 if NoOutput then begin Result := true; Exit end;
199 // wow, this is actually MIDI player!
200 // we need module player
201 flags := MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH;
202 {$IFDEF SDL2MIXER}
203 flags := flags or MIX_INIT_MODPLUG;
204 {$ENDIF}
205 res := Mix_Init(flags);
206 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
207 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
208 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
209 {$IFDEF SDL2MIXER}
210 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
211 {$ENDIF}
212 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
213 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
214 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
216 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
217 res := Mix_OpenAudio(gsSDLSampleRate, MIX_DEFAULT_FORMAT, 2, gsSDLBufferSize);
218 if res = -1 then
219 begin
220 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
221 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
222 Exit;
223 end;
225 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
226 begin
227 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
228 end;
230 for i := 0 to Mix_GetNumChunkDecoders()-1 do
231 begin
232 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
233 end;
235 e_TimidityDecoder := false;
236 for i := 0 to Mix_GetNumMusicDecoders()-1 do
237 begin
238 case AnsiString(Mix_GetMusicDecoder(i)) of
239 'TIMIDITY': e_TimidityDecoder := true;
240 end;
241 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
242 end;
244 Mix_AllocateChannels(N_CHANNELS);
245 Mix_ChannelFinished(chanFinished);
247 for i := 0 to N_CHANNELS-1 do
248 begin
249 ChanSIds[i].id := NO_SOUND_ID;
250 ChanSIds[i].muted := SoundMuted;
251 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
252 ChanSIds[i].pan := 1.0;
253 end;
254 MusVolume := MIX_MAX_VOLUME;
256 SoundInitialized := True;
257 Result := True;
258 end;
260 function e_isMusic (id: DWORD): Boolean;
261 begin
262 Result := False;
263 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
264 begin
265 Result := (e_SoundsArray[id].Music <> nil);
266 end;
267 end;
269 function e_isSound (id: DWORD): Boolean;
270 begin
271 Result := False;
272 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
273 begin
274 Result := (e_SoundsArray[id].Sound <> nil);
275 end;
276 end;
278 function FindESound(): DWORD;
279 var
280 i: Integer;
281 begin
282 if e_SoundsArray <> nil then
283 begin
284 for i := 0 to High(e_SoundsArray) do
285 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
286 begin
287 Result := i;
288 Exit;
289 end;
290 end;
291 if e_SoundsArray = nil then
292 begin
293 SetLength(e_SoundsArray, 16);
294 Result := 0;
295 end
296 else
297 begin
298 Result := High(e_SoundsArray) + 1;
299 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
300 end;
301 for i := Result to High(e_SoundsArray) do
302 begin
303 e_SoundsArray[i].Sound := nil;
304 e_SoundsArray[i].Music := nil;
305 e_SoundsArray[i].Data := nil;
306 e_SoundsArray[i].isMusic := False;
307 e_SoundsArray[i].nRefs := 0;
308 end;
309 end;
311 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
312 var
313 find_id: DWORD;
314 begin
315 ID := NO_SOUND_ID;
316 Result := False;
317 if not SoundInitialized then Exit;
319 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
320 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
323 if isMusic then
324 begin
325 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
326 Exit;
327 end;
330 find_id := FindESound();
332 e_SoundsArray[find_id].Data := nil;
333 e_SoundsArray[find_id].isMusic := isMusic;
334 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
335 e_SoundsArray[find_id].nRefs := 0;
337 if isMusic then
338 begin
339 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
340 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
341 if e_SoundsArray[find_id].Music = nil then
342 begin
343 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
344 e_WriteLog(Mix_GetError(), TMsgType.Warning);
345 Exit;
346 end;
347 dumpMusicType(e_SoundsArray[find_id].Music);
348 end
349 else
350 begin
351 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
352 if e_SoundsArray[find_id].Sound = nil then Exit;
353 end;
355 ID := find_id;
357 Result := True;
358 end;
360 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
361 var
362 find_id: DWORD;
363 rw: PSDL_RWops;
364 //pc: PChar;
365 isid3: Boolean;
366 begin
367 ID := NO_SOUND_ID;
368 Result := False;
369 if not SoundInitialized then Exit;
370 isid3 := False;
373 if isMusic then
374 begin
375 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
376 Exit;
377 end;
380 //FIXME: correctly skip ID3
382 pc := PChar(pData);
383 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
384 begin
385 isid3 := True;
386 Inc(pc, $400);
387 pData := Pointer(pc);
388 Dec(Length, $400);
389 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
390 end;
393 rw := SDL_RWFromConstMem(pData, Length);
394 if rw = nil then Exit;
396 find_id := FindESound();
398 e_SoundsArray[find_id].Data := pData;
399 if isid3 then e_SoundsArray[find_id].Data := nil;
400 e_SoundsArray[find_id].isMusic := isMusic;
401 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
402 e_SoundsArray[find_id].nRefs := 0;
404 if isMusic then
405 begin
406 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
407 {$IFDEF SDL1MIXER}
408 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw);
409 {$ELSE}
410 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
411 {$ENDIF}
412 if e_SoundsArray[find_id].Music = nil then
413 begin
414 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
415 e_WriteLog(Mix_GetError(), TMsgType.Warning);
416 end
417 else
418 begin
419 dumpMusicType(e_SoundsArray[find_id].Music);
420 end;
421 //SDL_FreeRW(rw);
423 if e_SoundsArray[find_id].Music <> nil then
424 begin
425 Mix_FreeMusic(e_SoundsArray[find_id].Music);
426 end;
427 e_SoundsArray[find_id].Music := nil;
428 Exit;
430 end
431 else
432 begin
433 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
434 end;
435 //SDL_FreeRW(rw); // somehow it segfaults...
436 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
437 begin
438 e_SoundsArray[find_id].Data := nil;
439 Exit;
440 end;
442 ID := find_id;
444 Result := True;
445 end;
447 function e_PlaySound (ID: DWORD): Integer;
448 var
449 res: Integer = -1;
450 loops: Integer = 0;
451 begin
452 Result := -1;
453 if not SoundInitialized then Exit;
455 if e_isSound(ID) then
456 begin
457 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
458 Inc(e_SoundsArray[ID].nRefs);
459 if e_SoundsArray[ID].Loops then loops := -1;
460 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
461 if res >= 0 then
462 begin
463 ChanSIds[res].id := ID;
464 ChanSIds[res].muted := SoundMuted;
465 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
467 if e_SoundsArray[ID].isMusic then
468 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
469 else
470 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
472 end;
473 end
474 else
475 begin
476 if not e_isMusic(ID) then Exit;
477 Mix_HaltMusic();
478 if e_SoundsArray[ID].Loops then loops := -1;
479 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
480 if res >= 0 then res := N_MUSCHAN;
481 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
482 Result := res;
483 end;
485 Result := res;
486 end;
488 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
489 var
490 l, r: UInt8;
491 begin
492 Result := True;
493 if chan = N_MUSCHAN then
494 begin
495 // no panning for music
496 end
497 else if chan >= 0 then
498 begin
499 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
500 Pan := Pan+1.0; // 0..2
501 l := trunc(127.0*(2.0-Pan));
502 r := trunc(127.0*Pan);
503 Mix_SetPanning(chan, l, r);
504 ChanSIds[chan].pan := Pan;
505 end
506 else
507 begin
508 Result := False;
509 end;
510 end;
512 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
513 var
514 vol: Integer;
515 begin
516 Result := True;
517 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
518 vol := trunc(Volume*MIX_MAX_VOLUME);
519 if chan = N_MUSCHAN then
520 begin
521 MusVolume := vol;
522 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
523 end
524 else if chan >= 0 then
525 begin
526 ChanSIds[chan].oldvol := vol;
527 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
528 end
529 else
530 begin
531 Result := False;
532 end;
533 end;
535 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
536 var
537 chan: Integer;
538 begin
539 Result := -1;
540 chan := e_PlaySound(ID);
541 e_chanSetPan(chan, Pan);
542 Result := chan;
543 end;
545 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
546 var
547 chan: Integer;
548 begin
549 Result := -1;
550 chan := e_PlaySound(ID);
551 e_chanSetVol(chan, Volume);
552 Result := chan;
553 end;
555 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
556 var
557 chan: Integer;
558 begin
559 Result := -1;
560 chan := e_PlaySound(ID);
561 e_chanSetPan(chan, Pan);
562 e_chanSetVol(chan, Volume);
563 Result := chan;
564 end;
566 procedure e_DeleteSound(ID: DWORD);
567 var
568 i: Integer;
569 begin
570 if ID > High(e_SoundsArray) then Exit;
571 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
573 for i := 0 to N_CHANNELS-1 do
574 begin
575 if ChanSIds[i].id = ID then
576 begin
577 ChanSIds[i].id := NO_SOUND_ID;
578 Mix_HaltChannel(i);
579 end;
580 end;
582 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
583 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
584 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
586 e_SoundsArray[ID].Sound := nil;
587 e_SoundsArray[ID].Music := nil;
588 e_SoundsArray[ID].Data := nil;
589 e_SoundsArray[ID].nRefs := 0;
590 end;
592 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
593 var
594 i: Integer;
595 vol: Single;
596 ovol: Integer;
597 begin
598 for i := 0 to N_CHANNELS-1 do
599 begin
600 ovol := ChanSIds[i].oldvol;
601 if setMode then
602 begin
603 vol := SoundMod;
604 end
605 else
606 begin
607 vol := (MIX_MAX_VOLUME+0.0)/ovol;
608 vol := vol*SoundMod;
609 end;
610 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
611 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
612 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
613 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
614 end;
615 ovol := Mix_VolumeMusic(-1);
616 if ovol >= 0 then
617 begin
618 if setMode then
619 begin
620 vol := SoundMod;
621 end
622 else
623 begin
624 vol := (MIX_MAX_VOLUME+0.0)/ovol;
625 vol := vol * SoundMod;
626 end;
627 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
628 MusVolume := trunc(vol*MIX_MAX_VOLUME);
629 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
630 end;
631 end;
633 procedure e_MuteChannels(Enable: Boolean);
634 var
635 i: Integer;
636 begin
637 //if Enable = SoundMuted then Exit;
638 SoundMuted := Enable;
639 for i := 0 to N_CHANNELS-1 do
640 begin
641 if ChanSIds[i].muted <> SoundMuted then
642 begin
643 ChanSIds[i].muted := SoundMuted;
644 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
645 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
646 end;
647 end;
648 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
649 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
650 end;
652 procedure e_StopChannels();
653 var
654 i: Integer;
655 begin
656 Mix_HaltChannel(-1);
657 Mix_HaltMusic();
658 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
659 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
660 end;
662 procedure e_RemoveAllSounds();
663 var
664 i: Integer;
665 begin
666 if SoundInitialized then e_StopChannels();
667 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
668 SetLength(e_SoundsArray, 0);
669 e_SoundsArray := nil;
670 end;
672 procedure e_ReleaseSoundSystem();
673 begin
674 e_RemoveAllSounds();
675 if SoundInitialized then
676 begin
677 Mix_CloseAudio();
678 SoundInitialized := False;
679 end;
680 end;
682 procedure e_SoundUpdate();
683 begin
684 //FMOD_System_Update(F_System);
685 end;
688 { TBasicSound: }
690 constructor TBasicSound.Create();
691 begin
692 FID := NO_SOUND_ID;
693 FMusic := False;
694 FChanNum := -1;
695 FPosition := 0;
696 FPriority := 128;
697 end;
699 destructor TBasicSound.Destroy();
700 begin
701 FreeSound();
702 inherited;
703 end;
705 function TBasicSound.GetChan (): Integer;
706 begin
707 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
708 begin
709 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
710 end
711 else if e_isMusic(FID) then
712 begin
713 FChanNum := N_MUSCHAN;
714 end;
715 Result := FChanNum;
716 end;
718 procedure TBasicSound.FreeSound();
719 begin
720 if FID = NO_SOUND_ID then Exit;
721 Stop();
722 FID := NO_SOUND_ID;
723 FMusic := False;
724 FPosition := 0;
725 FChanNum := -1;
726 end;
728 // aPos: msecs
729 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
730 begin
731 Result := False;
732 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
733 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
734 Result := (FChanNum >= 0);
735 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
736 //TODO: aPos
737 end;
739 procedure TBasicSound.SetID(ID: DWORD);
740 begin
741 FreeSound();
742 FID := ID;
743 if ID <> NO_SOUND_ID then
744 begin
745 FMusic := e_SoundsArray[ID].isMusic;
746 end;
747 FChanNum := -1;
748 end;
750 function TBasicSound.IsPlaying(): Boolean;
751 var
752 chan: Integer;
753 begin
754 Result := False;
755 if e_isSound(FID) then
756 begin
757 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
758 chan := Channel;
759 if chan < 0 then
760 begin
761 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
762 Exit;
763 end;
764 //Result := (Mix_Playing(chan) > 0)
765 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
766 Result := True;
767 end
768 else if e_isMusic(FID) then
769 begin
770 Result := (Mix_PlayingMusic() > 0);
771 end;
772 end;
774 procedure TBasicSound.Stop();
775 var
776 chan: Integer;
777 begin
778 if e_isSound(FID) then
779 begin
780 chan := Channel;
781 if chan >= 0 then
782 begin
783 //GetPosition();
784 Mix_HaltChannel(chan);
785 end;
786 end
787 else if e_isMusic(FID) then
788 begin
789 Mix_HaltMusic();
790 end;
791 FChanNum := -1;
792 end;
794 function TBasicSound.IsPaused(): Boolean;
795 var
796 chan: Integer;
797 begin
798 Result := False;
799 if e_isSound(FID) then
800 begin
801 chan := Channel;
802 if chan < 0 then Exit;
803 Result := (Mix_Paused(chan) > 0);
804 end
805 else if e_isMusic(FID) then
806 begin
807 Result := (Mix_PausedMusic() > 0);
808 end;
809 end;
811 procedure TBasicSound.Pause(Enable: Boolean);
812 var
813 chan: Integer;
814 pl: Boolean;
815 begin
816 Enable := not Enable; // fuckin' double negation
817 if e_isSound(FID) then
818 begin
819 chan := Channel;
820 if chan < 0 then Exit;
821 pl := not (Mix_Paused(chan) > 0);
822 if pl <> Enable then
823 begin
824 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
825 end;
826 end
827 else if e_isMusic(FID) then
828 begin
829 pl := not (Mix_PausedMusic() > 0);
830 if pl <> Enable then
831 begin
832 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
833 end;
834 end;
836 if Enable then
837 begin
838 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
839 if res <> FMOD_OK then
840 begin
841 end;
842 end;
844 end;
846 function TBasicSound.GetVolume(): Single;
847 var
848 chan: Integer;
849 begin
850 Result := 0.0;
851 if e_isSound(FID) then
852 begin
853 chan := Channel;
854 if chan < 0 then Exit;
855 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
856 end
857 else if e_isMusic(FID) then
858 begin
859 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
860 end;
861 end;
863 procedure TBasicSound.SetVolume(Volume: Single);
864 var
865 chan: Integer;
866 begin
867 if e_isSound(FID) then
868 begin
869 chan := Channel;
870 if chan < 0 then Exit;
871 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
872 e_chanSetVol(chan, Volume);
873 end
874 else if e_isMusic(FID) then
875 begin
876 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
877 e_chanSetVol(N_MUSCHAN, Volume);
878 end;
879 end;
881 function TBasicSound.GetPan(): Single;
882 var
883 chan: Integer;
884 begin
885 Result := 1.0;
886 if e_isSound(FID) then
887 begin
888 chan := Channel;
889 if chan < 0 then Exit;
890 Result := ChanSIds[chan].pan;
891 end;
892 end;
894 procedure TBasicSound.SetPan(Pan: Single);
895 var
896 chan: Integer;
897 begin
898 if e_isSound(FID) then
899 begin
900 chan := Channel;
901 if chan < 0 then Exit;
902 e_chanSetPan(chan, Pan);
903 end;
904 end;
906 function TBasicSound.IsMuted(): Boolean;
907 var
908 chan: Integer;
909 begin
910 Result := False;
911 if e_isSound(FID) then
912 begin
913 chan := Channel;
914 if chan < 0 then Exit;
915 Result := ChanSIds[chan].muted;
916 end
917 else if e_isMusic(FID) then
918 begin
919 Result := SoundMuted;
920 end;
921 end;
923 procedure TBasicSound.Mute(Enable: Boolean);
924 var
925 chan: Integer;
926 begin
927 if e_isSound(FID) then
928 begin
929 chan := Channel;
930 if chan < 0 then Exit;
931 if ChanSIds[chan].muted <> Enable then
932 begin
933 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
934 ChanSIds[chan].muted := Enable;
935 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
936 end;
937 end
938 else if e_isMusic(FID) then
939 begin
940 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
941 end;
942 end;
944 //TODO
945 function TBasicSound.GetPosition(): DWORD;
946 begin
947 Result := 0;
949 if FChanNum < 0 then Exit;
950 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
951 if res <> FMOD_OK then
952 begin
953 Exit;
954 end;
955 Result := FPosition;
957 end;
959 //TODO
960 procedure TBasicSound.SetPosition(aPos: DWORD);
961 begin
962 FPosition := aPos;
964 if FChanNum < 0 then Exit;
965 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
966 if res <> FMOD_OK then
967 begin
968 end;
970 end;
972 //TODO
973 procedure TBasicSound.SetPriority(priority: Integer);
974 begin
976 if (FChanNum <> nil) and (FPriority <> priority) and
977 (priority >= 0) and (priority <= 256) then
978 begin
979 FPriority := priority;
980 res := FMOD_Channel_SetPriority(FChanNum, priority);
981 if res <> FMOD_OK then
982 begin
983 end;
984 end;
986 end;
988 end.