DEADSOFTWARE

fix building with USE_SYSSTUB + USE_SDLMIXER
[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 SDL2, SDL2_mixer, envvars,
20 e_log, SysUtils;
22 type
23 TSoundRec = record
24 Data: Pointer;
25 Sound: PMix_Chunk;
26 Music: PMix_Music;
27 isMusic: Boolean;
28 Loops: Boolean;
29 nRefs: Integer;
30 end;
32 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
33 private
34 FChanNum: Integer; // <0: no channel allocated
36 protected
37 FID: DWORD;
38 FMusic: Boolean;
39 FPosition: DWORD;
40 FPriority: Integer;
42 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
43 function GetChan (): Integer;
45 property Channel: Integer read GetChan;
47 public
48 constructor Create();
49 destructor Destroy(); override;
50 procedure SetID(ID: DWORD);
51 procedure FreeSound();
52 function IsPlaying(): Boolean;
53 procedure Stop();
54 function IsPaused(): Boolean;
55 procedure Pause(Enable: Boolean);
56 function GetVolume(): Single;
57 procedure SetVolume(Volume: Single);
58 function GetPan(): Single;
59 procedure SetPan(Pan: Single);
60 function IsMuted(): Boolean;
61 procedure Mute(Enable: Boolean);
62 function GetPosition(): DWORD;
63 procedure SetPosition(aPos: DWORD);
64 procedure SetPriority(priority: Integer);
65 end;
67 const
68 NO_SOUND_ID = DWORD(-1);
70 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
72 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
73 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
75 // returns channel number or -1
76 function e_PlaySound(ID: DWORD): Integer;
77 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
78 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
79 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
81 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
82 procedure e_MuteChannels(Enable: Boolean);
83 procedure e_StopChannels();
85 procedure e_DeleteSound(ID: DWORD);
86 procedure e_RemoveAllSounds();
87 procedure e_ReleaseSoundSystem();
88 procedure e_SoundUpdate();
90 var
91 e_SoundsArray: array of TSoundRec = nil;
93 implementation
95 uses
96 g_window, g_options;
98 const
99 N_CHANNELS = 512;
100 N_MUSCHAN = N_CHANNELS+42;
102 type
103 TChanInfo = record
104 id: DWORD; // sound id
105 muted: Boolean;
106 oldvol: Integer; // for muted
107 pan: Single;
108 end;
110 var
111 SoundMuted: Boolean = False;
112 SoundInitialized: Boolean = False;
113 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
114 MusVolume: Integer = MIX_MAX_VOLUME;
117 procedure chanFinished (chan: Integer); cdecl;
118 begin
119 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
120 if (chan >= 0) and (chan < N_CHANNELS) then
121 begin
122 if ChanSIds[chan].id <> NO_SOUND_ID then
123 begin
124 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
125 begin
126 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
127 end;
128 ChanSIds[chan].id := NO_SOUND_ID;
129 end;
130 end;
131 end;
134 procedure dumpMusicType (ms: PMix_Music);
135 begin
136 if ms = nil then
137 begin
138 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
139 end
140 else
141 begin
142 case Mix_GetMusicType(ms^) of
143 TMix_MusicType.MUS_NONE:
144 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
145 TMix_MusicType.MUS_CMD:
146 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
147 TMix_MusicType.MUS_WAV:
148 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
149 TMix_MusicType.MUS_MOD:
150 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
151 TMix_MusicType.MUS_MID:
152 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
153 TMix_MusicType.MUS_OGG:
154 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
155 TMix_MusicType.MUS_MP3:
156 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
157 TMix_MusicType.MUS_MP3_MAD:
158 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
159 TMix_MusicType.MUS_FLAC:
160 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
161 TMix_MusicType.MUS_MODPLUG:
162 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
163 otherwise
164 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
165 end;
166 end;
167 end;
169 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
170 var
171 res, i: Integer;
172 rfreq: Integer;
173 rformat: UInt16;
174 rchans: Integer;
175 begin
176 if SoundInitialized then begin Result := true; Exit end;
178 Result := False;
179 SoundInitialized := False;
181 {$IFDEF HEADLESS}
182 // HACK: shit this into env and hope for the best
183 SetEnvVar('SDL_AUDIODRIVER', 'dummy');
184 {$ENDIF}
186 if NoOutput then begin Result := true; Exit end;
188 // wow, this is actually MIDI player!
189 // we need module player
190 res := Mix_Init(MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MODPLUG or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH);
191 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
192 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
193 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
194 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
195 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
196 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
197 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
199 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
200 res := Mix_OpenAudio(gsSDLSampleRate, AUDIO_S16LSB, 2, gsSDLBufferSize);
201 if res = -1 then
202 begin
203 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
204 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
205 Exit;
206 end;
208 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
209 begin
210 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
211 end;
213 for i := 0 to Mix_GetNumChunkDecoders()-1 do
214 begin
215 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
216 end;
217 for i := 0 to Mix_GetNumMusicDecoders()-1 do
218 begin
219 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
220 end;
222 Mix_AllocateChannels(N_CHANNELS);
223 Mix_ChannelFinished(chanFinished);
225 for i := 0 to N_CHANNELS-1 do
226 begin
227 ChanSIds[i].id := NO_SOUND_ID;
228 ChanSIds[i].muted := SoundMuted;
229 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
230 ChanSIds[i].pan := 1.0;
231 end;
232 MusVolume := MIX_MAX_VOLUME;
234 SoundInitialized := True;
235 Result := True;
236 end;
238 function e_isMusic (id: DWORD): Boolean;
239 begin
240 Result := False;
241 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
242 begin
243 Result := (e_SoundsArray[id].Music <> nil);
244 end;
245 end;
247 function e_isSound (id: DWORD): Boolean;
248 begin
249 Result := False;
250 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
251 begin
252 Result := (e_SoundsArray[id].Sound <> nil);
253 end;
254 end;
256 function FindESound(): DWORD;
257 var
258 i: Integer;
259 begin
260 if e_SoundsArray <> nil then
261 begin
262 for i := 0 to High(e_SoundsArray) do
263 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
264 begin
265 Result := i;
266 Exit;
267 end;
268 end;
269 if e_SoundsArray = nil then
270 begin
271 SetLength(e_SoundsArray, 16);
272 Result := 0;
273 end
274 else
275 begin
276 Result := High(e_SoundsArray) + 1;
277 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
278 end;
279 for i := Result to High(e_SoundsArray) do
280 begin
281 e_SoundsArray[i].Sound := nil;
282 e_SoundsArray[i].Music := nil;
283 e_SoundsArray[i].Data := nil;
284 e_SoundsArray[i].isMusic := False;
285 e_SoundsArray[i].nRefs := 0;
286 end;
287 end;
289 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
290 var
291 find_id: DWORD;
292 begin
293 ID := NO_SOUND_ID;
294 Result := False;
295 if not SoundInitialized then Exit;
297 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
298 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
301 if isMusic then
302 begin
303 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
304 Exit;
305 end;
308 find_id := FindESound();
310 e_SoundsArray[find_id].Data := nil;
311 e_SoundsArray[find_id].isMusic := isMusic;
312 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
313 e_SoundsArray[find_id].nRefs := 0;
315 if isMusic then
316 begin
317 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
318 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
319 if e_SoundsArray[find_id].Music = nil then
320 begin
321 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
322 e_WriteLog(Mix_GetError(), TMsgType.Warning);
323 Exit;
324 end;
325 dumpMusicType(e_SoundsArray[find_id].Music);
326 end
327 else
328 begin
329 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
330 if e_SoundsArray[find_id].Sound = nil then Exit;
331 end;
333 ID := find_id;
335 Result := True;
336 end;
338 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
339 var
340 find_id: DWORD;
341 rw: PSDL_RWops;
342 //pc: PChar;
343 isid3: Boolean;
344 begin
345 ID := NO_SOUND_ID;
346 Result := False;
347 if not SoundInitialized then Exit;
348 isid3 := False;
351 if isMusic then
352 begin
353 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
354 Exit;
355 end;
358 //FIXME: correctly skip ID3
360 pc := PChar(pData);
361 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
362 begin
363 isid3 := True;
364 Inc(pc, $400);
365 pData := Pointer(pc);
366 Dec(Length, $400);
367 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
368 end;
371 rw := SDL_RWFromConstMem(pData, Length);
372 if rw = nil then Exit;
374 find_id := FindESound();
376 e_SoundsArray[find_id].Data := pData;
377 if isid3 then e_SoundsArray[find_id].Data := nil;
378 e_SoundsArray[find_id].isMusic := isMusic;
379 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
380 e_SoundsArray[find_id].nRefs := 0;
382 if isMusic then
383 begin
384 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
385 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
386 if e_SoundsArray[find_id].Music = nil then
387 begin
388 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
389 e_WriteLog(Mix_GetError(), TMsgType.Warning);
390 end
391 else
392 begin
393 dumpMusicType(e_SoundsArray[find_id].Music);
394 end;
395 //SDL_FreeRW(rw);
397 if e_SoundsArray[find_id].Music <> nil then
398 begin
399 Mix_FreeMusic(e_SoundsArray[find_id].Music);
400 end;
401 e_SoundsArray[find_id].Music := nil;
402 Exit;
404 end
405 else
406 begin
407 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
408 end;
409 //SDL_FreeRW(rw); // somehow it segfaults...
410 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
411 begin
412 e_SoundsArray[find_id].Data := nil;
413 Exit;
414 end;
416 ID := find_id;
418 Result := True;
419 end;
421 function e_PlaySound (ID: DWORD): Integer;
422 var
423 res: Integer = -1;
424 loops: Integer = 0;
425 begin
426 Result := -1;
427 if not SoundInitialized then Exit;
429 if e_isSound(ID) then
430 begin
431 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
432 Inc(e_SoundsArray[ID].nRefs);
433 if e_SoundsArray[ID].Loops then loops := -1;
434 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
435 if res >= 0 then
436 begin
437 ChanSIds[res].id := ID;
438 ChanSIds[res].muted := SoundMuted;
439 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
441 if e_SoundsArray[ID].isMusic then
442 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
443 else
444 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
446 end;
447 end
448 else
449 begin
450 if not e_isMusic(ID) then Exit;
451 Mix_HaltMusic();
452 if e_SoundsArray[ID].Loops then loops := -1;
453 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
454 if res >= 0 then res := N_MUSCHAN;
455 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
456 Result := res;
457 end;
459 Result := res;
460 end;
462 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
463 var
464 l, r: UInt8;
465 begin
466 Result := True;
467 if chan = N_MUSCHAN then
468 begin
469 // no panning for music
470 end
471 else if chan >= 0 then
472 begin
473 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
474 Pan := Pan+1.0; // 0..2
475 l := trunc(127.0*(2.0-Pan));
476 r := trunc(127.0*Pan);
477 Mix_SetPanning(chan, l, r);
478 ChanSIds[chan].pan := Pan;
479 end
480 else
481 begin
482 Result := False;
483 end;
484 end;
486 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
487 var
488 vol: Integer;
489 begin
490 Result := True;
491 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
492 vol := trunc(Volume*MIX_MAX_VOLUME);
493 if chan = N_MUSCHAN then
494 begin
495 MusVolume := vol;
496 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
497 end
498 else if chan >= 0 then
499 begin
500 ChanSIds[chan].oldvol := vol;
501 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
502 end
503 else
504 begin
505 Result := False;
506 end;
507 end;
509 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
510 var
511 chan: Integer;
512 begin
513 Result := -1;
514 chan := e_PlaySound(ID);
515 e_chanSetPan(chan, Pan);
516 Result := chan;
517 end;
519 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
520 var
521 chan: Integer;
522 begin
523 Result := -1;
524 chan := e_PlaySound(ID);
525 e_chanSetVol(chan, Volume);
526 Result := chan;
527 end;
529 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
530 var
531 chan: Integer;
532 begin
533 Result := -1;
534 chan := e_PlaySound(ID);
535 e_chanSetPan(chan, Pan);
536 e_chanSetVol(chan, Volume);
537 Result := chan;
538 end;
540 procedure e_DeleteSound(ID: DWORD);
541 var
542 i: Integer;
543 begin
544 if ID > High(e_SoundsArray) then Exit;
545 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
547 for i := 0 to N_CHANNELS-1 do
548 begin
549 if ChanSIds[i].id = ID then
550 begin
551 ChanSIds[i].id := NO_SOUND_ID;
552 Mix_HaltChannel(i);
553 end;
554 end;
556 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
557 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
558 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
560 e_SoundsArray[ID].Sound := nil;
561 e_SoundsArray[ID].Music := nil;
562 e_SoundsArray[ID].Data := nil;
563 e_SoundsArray[ID].nRefs := 0;
564 end;
566 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
567 var
568 i: Integer;
569 vol: Single;
570 ovol: Integer;
571 begin
572 for i := 0 to N_CHANNELS-1 do
573 begin
574 ovol := ChanSIds[i].oldvol;
575 if setMode then
576 begin
577 vol := SoundMod;
578 end
579 else
580 begin
581 vol := (MIX_MAX_VOLUME+0.0)/ovol;
582 vol := vol*SoundMod;
583 end;
584 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
585 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
586 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
587 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
588 end;
589 ovol := Mix_VolumeMusic(-1);
590 if ovol >= 0 then
591 begin
592 if setMode then
593 begin
594 vol := SoundMod;
595 end
596 else
597 begin
598 vol := (MIX_MAX_VOLUME+0.0)/ovol;
599 vol := vol * SoundMod;
600 end;
601 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
602 MusVolume := trunc(vol*MIX_MAX_VOLUME);
603 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
604 end;
605 end;
607 procedure e_MuteChannels(Enable: Boolean);
608 var
609 i: Integer;
610 begin
611 //if Enable = SoundMuted then Exit;
612 SoundMuted := Enable;
613 for i := 0 to N_CHANNELS-1 do
614 begin
615 if ChanSIds[i].muted <> SoundMuted then
616 begin
617 ChanSIds[i].muted := SoundMuted;
618 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
619 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
620 end;
621 end;
622 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
623 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
624 end;
626 procedure e_StopChannels();
627 var
628 i: Integer;
629 begin
630 Mix_HaltChannel(-1);
631 Mix_HaltMusic();
632 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
633 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
634 end;
636 procedure e_RemoveAllSounds();
637 var
638 i: Integer;
639 begin
640 if SoundInitialized then e_StopChannels();
641 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
642 SetLength(e_SoundsArray, 0);
643 e_SoundsArray := nil;
644 end;
646 procedure e_ReleaseSoundSystem();
647 begin
648 e_RemoveAllSounds();
649 if SoundInitialized then
650 begin
651 Mix_CloseAudio();
652 SoundInitialized := False;
653 end;
654 end;
656 procedure e_SoundUpdate();
657 begin
658 //FMOD_System_Update(F_System);
659 end;
662 { TBasicSound: }
664 constructor TBasicSound.Create();
665 begin
666 FID := NO_SOUND_ID;
667 FMusic := False;
668 FChanNum := -1;
669 FPosition := 0;
670 FPriority := 128;
671 end;
673 destructor TBasicSound.Destroy();
674 begin
675 FreeSound();
676 inherited;
677 end;
679 function TBasicSound.GetChan (): Integer;
680 begin
681 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
682 begin
683 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
684 end
685 else if e_isMusic(FID) then
686 begin
687 FChanNum := N_MUSCHAN;
688 end;
689 Result := FChanNum;
690 end;
692 procedure TBasicSound.FreeSound();
693 begin
694 if FID = NO_SOUND_ID then Exit;
695 Stop();
696 FID := NO_SOUND_ID;
697 FMusic := False;
698 FPosition := 0;
699 FChanNum := -1;
700 end;
702 // aPos: msecs
703 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
704 begin
705 Result := False;
706 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
707 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
708 Result := (FChanNum >= 0);
709 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
710 //TODO: aPos
711 end;
713 procedure TBasicSound.SetID(ID: DWORD);
714 begin
715 FreeSound();
716 FID := ID;
717 if ID <> NO_SOUND_ID then
718 begin
719 FMusic := e_SoundsArray[ID].isMusic;
720 end;
721 FChanNum := -1;
722 end;
724 function TBasicSound.IsPlaying(): Boolean;
725 var
726 chan: Integer;
727 begin
728 Result := False;
729 if e_isSound(FID) then
730 begin
731 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
732 chan := Channel;
733 if chan < 0 then
734 begin
735 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
736 Exit;
737 end;
738 //Result := (Mix_Playing(chan) > 0)
739 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
740 Result := True;
741 end
742 else if e_isMusic(FID) then
743 begin
744 Result := (Mix_PlayingMusic() > 0);
745 end;
746 end;
748 procedure TBasicSound.Stop();
749 var
750 chan: Integer;
751 begin
752 if e_isSound(FID) then
753 begin
754 chan := Channel;
755 if chan >= 0 then
756 begin
757 //GetPosition();
758 Mix_HaltChannel(chan);
759 end;
760 end
761 else if e_isMusic(FID) then
762 begin
763 Mix_HaltMusic();
764 end;
765 FChanNum := -1;
766 end;
768 function TBasicSound.IsPaused(): Boolean;
769 var
770 chan: Integer;
771 begin
772 Result := False;
773 if e_isSound(FID) then
774 begin
775 chan := Channel;
776 if chan < 0 then Exit;
777 Result := (Mix_Paused(chan) > 0);
778 end
779 else if e_isMusic(FID) then
780 begin
781 Result := (Mix_PausedMusic() > 0);
782 end;
783 end;
785 procedure TBasicSound.Pause(Enable: Boolean);
786 var
787 chan: Integer;
788 pl: Boolean;
789 begin
790 Enable := not Enable; // fuckin' double negation
791 if e_isSound(FID) then
792 begin
793 chan := Channel;
794 if chan < 0 then Exit;
795 pl := not (Mix_Paused(chan) > 0);
796 if pl <> Enable then
797 begin
798 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
799 end;
800 end
801 else if e_isMusic(FID) then
802 begin
803 pl := not (Mix_PausedMusic() > 0);
804 if pl <> Enable then
805 begin
806 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
807 end;
808 end;
810 if Enable then
811 begin
812 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
813 if res <> FMOD_OK then
814 begin
815 end;
816 end;
818 end;
820 function TBasicSound.GetVolume(): Single;
821 var
822 chan: Integer;
823 begin
824 Result := 0.0;
825 if e_isSound(FID) then
826 begin
827 chan := Channel;
828 if chan < 0 then Exit;
829 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
830 end
831 else if e_isMusic(FID) then
832 begin
833 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
834 end;
835 end;
837 procedure TBasicSound.SetVolume(Volume: Single);
838 var
839 chan: Integer;
840 begin
841 if e_isSound(FID) then
842 begin
843 chan := Channel;
844 if chan < 0 then Exit;
845 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
846 e_chanSetVol(chan, Volume);
847 end
848 else if e_isMusic(FID) then
849 begin
850 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
851 e_chanSetVol(N_MUSCHAN, Volume);
852 end;
853 end;
855 function TBasicSound.GetPan(): Single;
856 var
857 chan: Integer;
858 begin
859 Result := 1.0;
860 if e_isSound(FID) then
861 begin
862 chan := Channel;
863 if chan < 0 then Exit;
864 Result := ChanSIds[chan].pan;
865 end;
866 end;
868 procedure TBasicSound.SetPan(Pan: Single);
869 var
870 chan: Integer;
871 begin
872 if e_isSound(FID) then
873 begin
874 chan := Channel;
875 if chan < 0 then Exit;
876 e_chanSetPan(chan, Pan);
877 end;
878 end;
880 function TBasicSound.IsMuted(): Boolean;
881 var
882 chan: Integer;
883 begin
884 Result := False;
885 if e_isSound(FID) then
886 begin
887 chan := Channel;
888 if chan < 0 then Exit;
889 Result := ChanSIds[chan].muted;
890 end
891 else if e_isMusic(FID) then
892 begin
893 Result := SoundMuted;
894 end;
895 end;
897 procedure TBasicSound.Mute(Enable: Boolean);
898 var
899 chan: Integer;
900 begin
901 if e_isSound(FID) then
902 begin
903 chan := Channel;
904 if chan < 0 then Exit;
905 if ChanSIds[chan].muted <> Enable then
906 begin
907 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
908 ChanSIds[chan].muted := Enable;
909 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
910 end;
911 end
912 else if e_isMusic(FID) then
913 begin
914 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
915 end;
916 end;
918 //TODO
919 function TBasicSound.GetPosition(): DWORD;
920 begin
921 Result := 0;
923 if FChanNum < 0 then Exit;
924 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
925 if res <> FMOD_OK then
926 begin
927 Exit;
928 end;
929 Result := FPosition;
931 end;
933 //TODO
934 procedure TBasicSound.SetPosition(aPos: DWORD);
935 begin
936 FPosition := aPos;
938 if FChanNum < 0 then Exit;
939 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
940 if res <> FMOD_OK then
941 begin
942 end;
944 end;
946 //TODO
947 procedure TBasicSound.SetPriority(priority: Integer);
948 begin
950 if (FChanNum <> nil) and (FPriority <> priority) and
951 (priority >= 0) and (priority <= 256) then
952 begin
953 FPriority := priority;
954 res := FMOD_Channel_SetPriority(FChanNum, priority);
955 if res <> FMOD_OK then
956 begin
957 end;
958 end;
960 end;
962 end.