DEADSOFTWARE

4d9eaba5d983da5fc644b7255be1923dfcce928f
[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,
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 if NoOutput then begin Result := true; Exit end;
183 // wow, this is actually MIDI player!
184 // we need module player
185 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);
186 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
187 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
188 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
189 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
190 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
191 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
192 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
194 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
195 res := Mix_OpenAudio(gsSDLSampleRate, AUDIO_S16LSB, 2, gsSDLBufferSize);
196 if res = -1 then
197 begin
198 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
199 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
200 Exit;
201 end;
203 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
204 begin
205 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
206 end;
208 for i := 0 to Mix_GetNumChunkDecoders()-1 do
209 begin
210 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
211 end;
212 for i := 0 to Mix_GetNumMusicDecoders()-1 do
213 begin
214 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
215 end;
217 Mix_AllocateChannels(N_CHANNELS);
218 Mix_ChannelFinished(chanFinished);
220 for i := 0 to N_CHANNELS-1 do
221 begin
222 ChanSIds[i].id := NO_SOUND_ID;
223 ChanSIds[i].muted := SoundMuted;
224 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
225 ChanSIds[i].pan := 1.0;
226 end;
227 MusVolume := MIX_MAX_VOLUME;
229 SoundInitialized := True;
230 Result := True;
231 end;
233 function e_isMusic (id: DWORD): Boolean;
234 begin
235 Result := False;
236 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
237 begin
238 Result := (e_SoundsArray[id].Music <> nil);
239 end;
240 end;
242 function e_isSound (id: DWORD): Boolean;
243 begin
244 Result := False;
245 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
246 begin
247 Result := (e_SoundsArray[id].Sound <> nil);
248 end;
249 end;
251 function FindESound(): DWORD;
252 var
253 i: Integer;
254 begin
255 if e_SoundsArray <> nil then
256 begin
257 for i := 0 to High(e_SoundsArray) do
258 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
259 begin
260 Result := i;
261 Exit;
262 end;
263 end;
264 if e_SoundsArray = nil then
265 begin
266 SetLength(e_SoundsArray, 16);
267 Result := 0;
268 end
269 else
270 begin
271 Result := High(e_SoundsArray) + 1;
272 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
273 end;
274 for i := Result to High(e_SoundsArray) do
275 begin
276 e_SoundsArray[i].Sound := nil;
277 e_SoundsArray[i].Music := nil;
278 e_SoundsArray[i].Data := nil;
279 e_SoundsArray[i].isMusic := False;
280 e_SoundsArray[i].nRefs := 0;
281 end;
282 end;
284 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
285 var
286 find_id: DWORD;
287 begin
288 ID := NO_SOUND_ID;
289 Result := False;
290 if not SoundInitialized then Exit;
292 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
293 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
296 if isMusic then
297 begin
298 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
299 Exit;
300 end;
303 find_id := FindESound();
305 e_SoundsArray[find_id].Data := nil;
306 e_SoundsArray[find_id].isMusic := isMusic;
307 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
308 e_SoundsArray[find_id].nRefs := 0;
310 if isMusic then
311 begin
312 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
313 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
314 if e_SoundsArray[find_id].Music = nil then
315 begin
316 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
317 e_WriteLog(Mix_GetError(), TMsgType.Warning);
318 Exit;
319 end;
320 dumpMusicType(e_SoundsArray[find_id].Music);
321 end
322 else
323 begin
324 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
325 if e_SoundsArray[find_id].Sound = nil then Exit;
326 end;
328 ID := find_id;
330 Result := True;
331 end;
333 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
334 var
335 find_id: DWORD;
336 rw: PSDL_RWops;
337 //pc: PChar;
338 isid3: Boolean;
339 begin
340 ID := NO_SOUND_ID;
341 Result := False;
342 if not SoundInitialized then Exit;
343 isid3 := False;
346 if isMusic then
347 begin
348 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
349 Exit;
350 end;
353 //FIXME: correctly skip ID3
355 pc := PChar(pData);
356 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
357 begin
358 isid3 := True;
359 Inc(pc, $400);
360 pData := Pointer(pc);
361 Dec(Length, $400);
362 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
363 end;
366 rw := SDL_RWFromConstMem(pData, Length);
367 if rw = nil then Exit;
369 find_id := FindESound();
371 e_SoundsArray[find_id].Data := pData;
372 if isid3 then e_SoundsArray[find_id].Data := nil;
373 e_SoundsArray[find_id].isMusic := isMusic;
374 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
375 e_SoundsArray[find_id].nRefs := 0;
377 if isMusic then
378 begin
379 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
380 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
381 if e_SoundsArray[find_id].Music = nil then
382 begin
383 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
384 e_WriteLog(Mix_GetError(), TMsgType.Warning);
385 end
386 else
387 begin
388 dumpMusicType(e_SoundsArray[find_id].Music);
389 end;
390 //SDL_FreeRW(rw);
392 if e_SoundsArray[find_id].Music <> nil then
393 begin
394 Mix_FreeMusic(e_SoundsArray[find_id].Music);
395 end;
396 e_SoundsArray[find_id].Music := nil;
397 Exit;
399 end
400 else
401 begin
402 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
403 end;
404 //SDL_FreeRW(rw); // somehow it segfaults...
405 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
406 begin
407 e_SoundsArray[find_id].Data := nil;
408 Exit;
409 end;
411 ID := find_id;
413 Result := True;
414 end;
416 function e_PlaySound (ID: DWORD): Integer;
417 var
418 res: Integer = -1;
419 loops: Integer = 0;
420 begin
421 Result := -1;
422 if not SoundInitialized then Exit;
424 if e_isSound(ID) then
425 begin
426 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
427 Inc(e_SoundsArray[ID].nRefs);
428 if e_SoundsArray[ID].Loops then loops := -1;
429 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
430 if res >= 0 then
431 begin
432 ChanSIds[res].id := ID;
433 ChanSIds[res].muted := SoundMuted;
434 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
436 if e_SoundsArray[ID].isMusic then
437 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
438 else
439 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
441 end;
442 end
443 else
444 begin
445 if not e_isMusic(ID) then Exit;
446 Mix_HaltMusic();
447 if e_SoundsArray[ID].Loops then loops := -1;
448 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
449 if res >= 0 then res := N_MUSCHAN;
450 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
451 Result := res;
452 end;
454 Result := res;
455 end;
457 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
458 var
459 l, r: UInt8;
460 begin
461 Result := True;
462 if chan = N_MUSCHAN then
463 begin
464 // no panning for music
465 end
466 else if chan >= 0 then
467 begin
468 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
469 Pan := Pan+1.0; // 0..2
470 l := trunc(127.0*(2.0-Pan));
471 r := trunc(127.0*Pan);
472 Mix_SetPanning(chan, l, r);
473 ChanSIds[chan].pan := Pan;
474 end
475 else
476 begin
477 Result := False;
478 end;
479 end;
481 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
482 var
483 vol: Integer;
484 begin
485 Result := True;
486 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
487 vol := trunc(Volume*MIX_MAX_VOLUME);
488 if chan = N_MUSCHAN then
489 begin
490 MusVolume := vol;
491 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
492 end
493 else if chan >= 0 then
494 begin
495 ChanSIds[chan].oldvol := vol;
496 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
497 end
498 else
499 begin
500 Result := False;
501 end;
502 end;
504 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
505 var
506 chan: Integer;
507 begin
508 Result := -1;
509 chan := e_PlaySound(ID);
510 e_chanSetPan(chan, Pan);
511 Result := chan;
512 end;
514 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
515 var
516 chan: Integer;
517 begin
518 Result := -1;
519 chan := e_PlaySound(ID);
520 e_chanSetVol(chan, Volume);
521 Result := chan;
522 end;
524 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
525 var
526 chan: Integer;
527 begin
528 Result := -1;
529 chan := e_PlaySound(ID);
530 e_chanSetPan(chan, Pan);
531 e_chanSetVol(chan, Volume);
532 Result := chan;
533 end;
535 procedure e_DeleteSound(ID: DWORD);
536 var
537 i: Integer;
538 begin
539 if ID > High(e_SoundsArray) then Exit;
540 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
542 for i := 0 to N_CHANNELS-1 do
543 begin
544 if ChanSIds[i].id = ID then
545 begin
546 ChanSIds[i].id := NO_SOUND_ID;
547 Mix_HaltChannel(i);
548 end;
549 end;
551 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
552 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
553 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
555 e_SoundsArray[ID].Sound := nil;
556 e_SoundsArray[ID].Music := nil;
557 e_SoundsArray[ID].Data := nil;
558 e_SoundsArray[ID].nRefs := 0;
559 end;
561 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
562 var
563 i: Integer;
564 vol: Single;
565 ovol: Integer;
566 begin
567 for i := 0 to N_CHANNELS-1 do
568 begin
569 ovol := ChanSIds[i].oldvol;
570 if setMode then
571 begin
572 vol := SoundMod;
573 end
574 else
575 begin
576 vol := (MIX_MAX_VOLUME+0.0)/ovol;
577 vol := vol*SoundMod;
578 end;
579 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
580 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
581 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
582 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
583 end;
584 ovol := Mix_VolumeMusic(-1);
585 if ovol >= 0 then
586 begin
587 if setMode then
588 begin
589 vol := SoundMod;
590 end
591 else
592 begin
593 vol := (MIX_MAX_VOLUME+0.0)/ovol;
594 vol := vol * SoundMod;
595 end;
596 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
597 MusVolume := trunc(vol*MIX_MAX_VOLUME);
598 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
599 end;
600 end;
602 procedure e_MuteChannels(Enable: Boolean);
603 var
604 i: Integer;
605 begin
606 //if Enable = SoundMuted then Exit;
607 SoundMuted := Enable;
608 for i := 0 to N_CHANNELS-1 do
609 begin
610 if ChanSIds[i].muted <> SoundMuted then
611 begin
612 ChanSIds[i].muted := SoundMuted;
613 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
614 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
615 end;
616 end;
617 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
618 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
619 end;
621 procedure e_StopChannels();
622 var
623 i: Integer;
624 begin
625 Mix_HaltChannel(-1);
626 Mix_HaltMusic();
627 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
628 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
629 end;
631 procedure e_RemoveAllSounds();
632 var
633 i: Integer;
634 begin
635 if SoundInitialized then e_StopChannels();
636 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
637 SetLength(e_SoundsArray, 0);
638 e_SoundsArray := nil;
639 end;
641 procedure e_ReleaseSoundSystem();
642 begin
643 e_RemoveAllSounds();
644 if SoundInitialized then
645 begin
646 Mix_CloseAudio();
647 SoundInitialized := False;
648 end;
649 end;
651 procedure e_SoundUpdate();
652 begin
653 //FMOD_System_Update(F_System);
654 end;
657 { TBasicSound: }
659 constructor TBasicSound.Create();
660 begin
661 FID := NO_SOUND_ID;
662 FMusic := False;
663 FChanNum := -1;
664 FPosition := 0;
665 FPriority := 128;
666 end;
668 destructor TBasicSound.Destroy();
669 begin
670 FreeSound();
671 inherited;
672 end;
674 function TBasicSound.GetChan (): Integer;
675 begin
676 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
677 begin
678 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
679 end
680 else if e_isMusic(FID) then
681 begin
682 FChanNum := N_MUSCHAN;
683 end;
684 Result := FChanNum;
685 end;
687 procedure TBasicSound.FreeSound();
688 begin
689 if FID = NO_SOUND_ID then Exit;
690 Stop();
691 FID := NO_SOUND_ID;
692 FMusic := False;
693 FPosition := 0;
694 FChanNum := -1;
695 end;
697 // aPos: msecs
698 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
699 begin
700 Result := False;
701 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
702 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
703 Result := (FChanNum >= 0);
704 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
705 //TODO: aPos
706 end;
708 procedure TBasicSound.SetID(ID: DWORD);
709 begin
710 FreeSound();
711 FID := ID;
712 if ID <> NO_SOUND_ID then
713 begin
714 FMusic := e_SoundsArray[ID].isMusic;
715 end;
716 FChanNum := -1;
717 end;
719 function TBasicSound.IsPlaying(): Boolean;
720 var
721 chan: Integer;
722 begin
723 Result := False;
724 if e_isSound(FID) then
725 begin
726 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
727 chan := Channel;
728 if chan < 0 then
729 begin
730 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
731 Exit;
732 end;
733 //Result := (Mix_Playing(chan) > 0)
734 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
735 Result := True;
736 end
737 else if e_isMusic(FID) then
738 begin
739 Result := (Mix_PlayingMusic() > 0);
740 end;
741 end;
743 procedure TBasicSound.Stop();
744 var
745 chan: Integer;
746 begin
747 if e_isSound(FID) then
748 begin
749 chan := Channel;
750 if chan >= 0 then
751 begin
752 //GetPosition();
753 Mix_HaltChannel(chan);
754 end;
755 end
756 else if e_isMusic(FID) then
757 begin
758 Mix_HaltMusic();
759 end;
760 FChanNum := -1;
761 end;
763 function TBasicSound.IsPaused(): Boolean;
764 var
765 chan: Integer;
766 begin
767 Result := False;
768 if e_isSound(FID) then
769 begin
770 chan := Channel;
771 if chan < 0 then Exit;
772 Result := (Mix_Paused(chan) > 0);
773 end
774 else if e_isMusic(FID) then
775 begin
776 Result := (Mix_PausedMusic() > 0);
777 end;
778 end;
780 procedure TBasicSound.Pause(Enable: Boolean);
781 var
782 chan: Integer;
783 pl: Boolean;
784 begin
785 Enable := not Enable; // fuckin' double negation
786 if e_isSound(FID) then
787 begin
788 chan := Channel;
789 if chan < 0 then Exit;
790 pl := not (Mix_Paused(chan) > 0);
791 if pl <> Enable then
792 begin
793 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
794 end;
795 end
796 else if e_isMusic(FID) then
797 begin
798 pl := not (Mix_PausedMusic() > 0);
799 if pl <> Enable then
800 begin
801 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
802 end;
803 end;
805 if Enable then
806 begin
807 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
808 if res <> FMOD_OK then
809 begin
810 end;
811 end;
813 end;
815 function TBasicSound.GetVolume(): Single;
816 var
817 chan: Integer;
818 begin
819 Result := 0.0;
820 if e_isSound(FID) then
821 begin
822 chan := Channel;
823 if chan < 0 then Exit;
824 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
825 end
826 else if e_isMusic(FID) then
827 begin
828 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
829 end;
830 end;
832 procedure TBasicSound.SetVolume(Volume: Single);
833 var
834 chan: Integer;
835 begin
836 if e_isSound(FID) then
837 begin
838 chan := Channel;
839 if chan < 0 then Exit;
840 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
841 e_chanSetVol(chan, Volume);
842 end
843 else if e_isMusic(FID) then
844 begin
845 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
846 e_chanSetVol(N_MUSCHAN, Volume);
847 end;
848 end;
850 function TBasicSound.GetPan(): Single;
851 var
852 chan: Integer;
853 begin
854 Result := 1.0;
855 if e_isSound(FID) then
856 begin
857 chan := Channel;
858 if chan < 0 then Exit;
859 Result := ChanSIds[chan].pan;
860 end;
861 end;
863 procedure TBasicSound.SetPan(Pan: 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_chanSetPan(chan, Pan);
872 end;
873 end;
875 function TBasicSound.IsMuted(): Boolean;
876 var
877 chan: Integer;
878 begin
879 Result := False;
880 if e_isSound(FID) then
881 begin
882 chan := Channel;
883 if chan < 0 then Exit;
884 Result := ChanSIds[chan].muted;
885 end
886 else if e_isMusic(FID) then
887 begin
888 Result := SoundMuted;
889 end;
890 end;
892 procedure TBasicSound.Mute(Enable: Boolean);
893 var
894 chan: Integer;
895 begin
896 if e_isSound(FID) then
897 begin
898 chan := Channel;
899 if chan < 0 then Exit;
900 if ChanSIds[chan].muted <> Enable then
901 begin
902 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
903 ChanSIds[chan].muted := Enable;
904 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
905 end;
906 end
907 else if e_isMusic(FID) then
908 begin
909 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
910 end;
911 end;
913 //TODO
914 function TBasicSound.GetPosition(): DWORD;
915 begin
916 Result := 0;
918 if FChanNum < 0 then Exit;
919 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
920 if res <> FMOD_OK then
921 begin
922 Exit;
923 end;
924 Result := FPosition;
926 end;
928 //TODO
929 procedure TBasicSound.SetPosition(aPos: DWORD);
930 begin
931 FPosition := aPos;
933 if FChanNum < 0 then Exit;
934 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
935 if res <> FMOD_OK then
936 begin
937 end;
939 end;
941 //TODO
942 procedure TBasicSound.SetPriority(priority: Integer);
943 begin
945 if (FChanNum <> nil) and (FPriority <> priority) and
946 (priority >= 0) and (priority <= 256) then
947 begin
948 FPriority := priority;
949 res := FMOD_Channel_SetPriority(FChanNum, priority);
950 if res <> FMOD_OK then
951 begin
952 end;
953 end;
955 end;
957 end.