20 TBasicSound
= class (TObject
)
22 FChanNum
: Integer; // <0: no channel allocated
30 function RawPlay(Pan
: Single; Volume
: Single; aPos
: DWORD
): Boolean;
31 function GetChan (): Integer;
33 property Channel
: Integer read GetChan
;
37 destructor Destroy(); override;
38 procedure SetID(ID
: DWORD
);
39 procedure FreeSound();
40 function IsPlaying(): Boolean;
42 function IsPaused(): Boolean;
43 procedure Pause(Enable
: Boolean);
44 function GetVolume(): Single;
45 procedure SetVolume(Volume
: Single);
46 function GetPan(): Single;
47 procedure SetPan(Pan
: Single);
48 function IsMuted(): Boolean;
49 procedure Mute(Enable
: Boolean);
50 function GetPosition(): DWORD
;
51 procedure SetPosition(aPos
: DWORD
);
52 procedure SetPriority(priority
: Integer);
56 NO_SOUND_ID
= DWORD(-1);
58 function e_InitSoundSystem(): Boolean;
60 function e_LoadSound(FileName
: string; var ID
: DWORD
; isMusic
: Boolean): Boolean;
61 function e_LoadSoundMem(pData
: Pointer; Length
: Integer; var ID
: DWORD
; isMusic
: Boolean): Boolean;
63 // returns channel number or -1
64 function e_PlaySound(ID
: DWORD
): Integer;
65 function e_PlaySoundPan(ID
: DWORD
; Pan
: Single): Integer;
66 function e_PlaySoundVolume(ID
: DWORD
; Volume
: Single): Integer;
67 function e_PlaySoundPanVolume(ID
: DWORD
; Pan
, Volume
: Single): Integer;
69 procedure e_ModifyChannelsVolumes(SoundMod
: Single; setMode
: Boolean);
70 procedure e_MuteChannels(Enable
: Boolean);
71 procedure e_StopChannels();
73 procedure e_DeleteSound(ID
: DWORD
);
74 procedure e_RemoveAllSounds();
75 procedure e_ReleaseSoundSystem();
76 procedure e_SoundUpdate();
79 e_SoundsArray
: array of TSoundRec
= nil;
84 g_window
, g_options
, BinEditor
;
88 N_MUSCHAN
= N_CHANNELS
+42;
92 id
: DWORD
; // sound id
94 oldvol
: Integer; // for muted
99 SoundMuted
: Boolean = False;
100 SoundInitialized
: Boolean = False;
101 ChanSIds
: array[0..N_CHANNELS
] of TChanInfo
;
102 MusVolume
: Integer = MIX_MAX_VOLUME
;
105 procedure chanFinished (chan
: Integer); cdecl;
107 if (chan
>= 0) and (chan
< N_CHANNELS
) then
109 if ChanSIds
[chan
].id
<> NO_SOUND_ID
then
111 Dec(e_SoundsArray
[ChanSIds
[chan
].id
].nRefs
);
112 ChanSIds
[chan
].id
:= NO_SOUND_ID
;
118 function e_InitSoundSystem(): Boolean;
122 if SoundInitialized
then begin Result
:= true; Exit
end;
125 SoundInitialized
:= False;
127 { // wow, this is actually MIDI player!
128 // we need module player
129 if (Mix_Init(MIX_INIT_MOD) and MIX_INIT_MOD) <> MIX_INIT_MOD then
131 e_WriteLog('Error initializing SDL module player:', MSG_FATALERROR);
132 e_WriteLog(Mix_GetError(), MSG_FATALERROR);
137 res
:= Mix_OpenAudio(44100, AUDIO_S16LSB
, 2, 2048);
140 e_WriteLog('Error initializing SDL mixer:', MSG_FATALERROR
);
141 e_WriteLog(Mix_GetError(), MSG_FATALERROR
);
145 Mix_AllocateChannels(N_CHANNELS
);
146 Mix_ChannelFinished(chanFinished
);
148 for i
:= 0 to N_CHANNELS
-1 do
150 ChanSIds
[i
].id
:= NO_SOUND_ID
;
151 ChanSIds
[i
].muted
:= SoundMuted
;
152 ChanSIds
[i
].oldvol
:= MIX_MAX_VOLUME
;
153 ChanSIds
[i
].pan
:= 1.0;
155 MusVolume
:= MIX_MAX_VOLUME
;
157 SoundInitialized
:= True;
161 function e_isMusic (id
: DWORD
): Boolean;
164 if (e_SoundsArray
<> nil) and (id
<= High(e_SoundsArray
)) then
166 Result
:= (e_SoundsArray
[id
].Music
<> nil);
170 function e_isSound (id
: DWORD
): Boolean;
173 if (e_SoundsArray
<> nil) and (id
<= High(e_SoundsArray
)) then
175 Result
:= (e_SoundsArray
[id
].Sound
<> nil);
179 function FindESound(): DWORD
;
183 if e_SoundsArray
<> nil then
185 for i
:= 0 to High(e_SoundsArray
) do
186 if (e_SoundsArray
[i
].Sound
= nil) and (e_SoundsArray
[i
].Music
= nil) then
192 if e_SoundsArray
= nil then
194 SetLength(e_SoundsArray
, 16);
199 Result
:= High(e_SoundsArray
) + 1;
200 SetLength(e_SoundsArray
, Length(e_SoundsArray
) + 16);
204 function e_LoadSound(FileName
: String; var ID
: DWORD
; isMusic
: Boolean): Boolean;
209 if not SoundInitialized
then Exit
;
211 if isMusic
then e_WriteLog('Loading music '+FileName
+'...', MSG_NOTIFY
)
212 else e_WriteLog('Loading sound '+FileName
+'...', MSG_NOTIFY
);
214 find_id
:= FindESound();
216 e_SoundsArray
[find_id
].Data
:= nil;
217 e_SoundsArray
[find_id
].isMusic
:= isMusic
;
218 e_SoundsArray
[find_id
].nRefs
:= 0;
222 e_SoundsArray
[find_id
].Music
:= Mix_LoadMUS(PAnsiChar(FileName
));
223 if e_SoundsArray
[find_id
].Music
= nil then Exit
;
227 e_SoundsArray
[find_id
].Sound
:= Mix_LoadWAV(PAnsiChar(FileName
));
228 if e_SoundsArray
[find_id
].Sound
= nil then Exit
;
236 function e_LoadSoundMem(pData
: Pointer; Length
: Integer; var ID
: DWORD
; isMusic
: Boolean): Boolean;
242 if not SoundInitialized
then Exit
;
244 rw
:= SDL_RWFromConstMem(pData
, Length
);
245 if rw
= nil then Exit
;
247 find_id
:= FindESound();
249 e_SoundsArray
[find_id
].Data
:= pData
;
250 e_SoundsArray
[find_id
].isMusic
:= isMusic
;
251 e_SoundsArray
[find_id
].nRefs
:= 0;
255 e_SoundsArray
[find_id
].Music
:= Mix_LoadMUS_RW(rw
, 0);
259 e_SoundsArray
[find_id
].Sound
:= Mix_LoadWAV_RW(rw
, 0);
262 if (e_SoundsArray
[find_id
].Sound
= nil) and (e_SoundsArray
[find_id
].Music
= nil) then Exit
;
269 function e_PlaySound (ID
: DWORD
): Integer;
274 if not SoundInitialized
then Exit
;
276 if e_isSound(ID
) then
278 if e_SoundsArray
[ID
].nRefs
>= gMaxSimSounds
then Exit
;
279 Inc(e_SoundsArray
[ID
].nRefs
);
280 res
:= Mix_PlayChannel(-1, e_SoundsArray
[ID
].Sound
, 0);
283 ChanSIds
[res
].id
:= ID
;
284 ChanSIds
[res
].muted
:= SoundMuted
;
285 if SoundMuted
then Mix_Volume(res
, 0) else Mix_Volume(res
, ChanSIds
[res
].oldvol
);
287 if e_SoundsArray[ID].isMusic then
288 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
290 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
296 if not e_isMusic(ID
) then Exit
;
297 res
:= Mix_PlayMusic(e_SoundsArray
[ID
].Music
, -1);
298 if res
>= 0 then res
:= N_MUSCHAN
;
299 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
306 function e_chanSetPan (chan
: Integer; Pan
: Single): Boolean;
311 if chan
= N_MUSCHAN
then
313 // no panning for music
315 else if chan
>= 0 then
317 if Pan
< -1.0 then Pan
:= -1.0 else if Pan
> 1.0 then Pan
:= 1.0;
318 Pan
:= Pan
+1.0; // 0..2
319 l
:= trunc(127.0*(2.0-Pan
));
320 r
:= trunc(127.0*Pan
);
321 Mix_SetPanning(chan
, l
, r
);
322 ChanSIds
[chan
].pan
:= Pan
;
330 function e_chanSetVol (chan
: Integer; Volume
: Single): Boolean;
335 if Volume
< 0 then Volume
:= 0 else if Volume
> 1 then Volume
:= 1;
336 vol
:= trunc(Volume
*MIX_MAX_VOLUME
);
337 if chan
= N_MUSCHAN
then
340 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol
);
342 else if chan
>= 0 then
344 ChanSIds
[chan
].oldvol
:= vol
;
345 if ChanSIds
[chan
].muted
then Mix_Volume(chan
, 0) else Mix_Volume(chan
, vol
);
353 function e_PlaySoundPan(ID
: DWORD
; Pan
: Single): Integer;
358 chan
:= e_PlaySound(ID
);
359 e_chanSetPan(chan
, Pan
);
363 function e_PlaySoundVolume(ID
: DWORD
; Volume
: Single): Integer;
368 chan
:= e_PlaySound(ID
);
369 e_chanSetVol(chan
, Volume
);
373 function e_PlaySoundPanVolume(ID
: DWORD
; Pan
, Volume
: Single): Integer;
378 chan
:= e_PlaySound(ID
);
379 e_chanSetPan(chan
, Pan
);
380 e_chanSetVol(chan
, Volume
);
384 procedure e_DeleteSound(ID
: DWORD
);
388 if (e_SoundsArray
[ID
].Sound
= nil) and (e_SoundsArray
[ID
].Music
= nil) then Exit
;
390 for i
:= 0 to N_CHANNELS
-1 do
392 if ChanSIds
[i
].id
= ID
then
394 ChanSIds
[i
].id
:= NO_SOUND_ID
;
399 if e_SoundsArray
[ID
].Data
<> nil then FreeMem(e_SoundsArray
[ID
].Data
);
400 if e_SoundsArray
[ID
].Sound
<> nil then Mix_FreeChunk(e_SoundsArray
[ID
].Sound
);
401 if e_SoundsArray
[ID
].Music
<> nil then Mix_FreeMusic(e_SoundsArray
[ID
].Music
);
403 e_SoundsArray
[ID
].Sound
:= nil;
404 e_SoundsArray
[ID
].Music
:= nil;
405 e_SoundsArray
[ID
].Data
:= nil;
406 e_SoundsArray
[ID
].nRefs
:= 0;
409 procedure e_ModifyChannelsVolumes(SoundMod
: Single; setMode
: Boolean);
415 for i
:= 0 to N_CHANNELS
-1 do
417 ovol
:= ChanSIds
[i
].oldvol
;
424 vol
:= (MIX_MAX_VOLUME
+0.0)/ovol
;
427 if vol
< 0 then vol
:= 0 else if vol
> 1 then vol
:= 1;
428 ChanSIds
[i
].oldvol
:= trunc(vol
*MIX_MAX_VOLUME
);
429 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), MSG_WARNING);
430 if ChanSIds
[i
].muted
then Mix_Volume(i
, 0) else Mix_Volume(i
, ChanSIds
[i
].oldvol
);
432 ovol
:= Mix_VolumeMusic(-1);
441 vol
:= (MIX_MAX_VOLUME
+0.0)/ovol
;
442 vol
:= vol
* SoundMod
;
444 if vol
< 0 then vol
:= 0 else if vol
> 1 then vol
:= 1;
445 MusVolume
:= trunc(vol
*MIX_MAX_VOLUME
);
446 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
450 procedure e_MuteChannels(Enable
: Boolean);
454 //if Enable = SoundMuted then Exit;
455 SoundMuted
:= Enable
;
456 for i
:= 0 to N_CHANNELS
-1 do
458 if ChanSIds
[i
].muted
<> SoundMuted
then
460 ChanSIds
[i
].muted
:= SoundMuted
;
461 //e_WriteLog(Format('gmuting sound for channel %d', [i]), MSG_WARNING);
462 if ChanSIds
[i
].muted
then Mix_Volume(i
, 0) else Mix_Volume(i
, ChanSIds
[i
].oldvol
);
465 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
468 procedure e_StopChannels();
474 for i
:= 0 to High(e_SoundsArray
) do e_SoundsArray
[i
].nRefs
:= 0;
475 for i
:= 0 to N_CHANNELS
-1 do ChanSIds
[i
].id
:= NO_SOUND_ID
;
478 procedure e_RemoveAllSounds();
482 if SoundInitialized
then e_StopChannels();
483 for i
:= 0 to High(e_SoundsArray
) do e_DeleteSound(i
);
484 SetLength(e_SoundsArray
, 0);
485 e_SoundsArray
:= nil;
488 procedure e_ReleaseSoundSystem();
491 if SoundInitialized
then
494 SoundInitialized
:= False;
498 procedure e_SoundUpdate();
500 //FMOD_System_Update(F_System);
506 constructor TBasicSound
.Create();
515 destructor TBasicSound
.Destroy();
521 function TBasicSound
.GetChan (): Integer;
523 if (FID
<> NO_SOUND_ID
) and (FChanNum
>= 0) and (FChanNum
< N_CHANNELS
) then
525 if ChanSIds
[FChanNum
].id
<> FID
then FChanNum
:= -1;
527 else if e_isMusic(FID
) then
529 FChanNum
:= N_MUSCHAN
;
534 procedure TBasicSound
.FreeSound();
536 if FID
= NO_SOUND_ID
then Exit
;
545 function TBasicSound
.RawPlay(Pan
: Single; Volume
: Single; aPos
: DWORD
): Boolean;
548 if (FID
= NO_SOUND_ID
) or not SoundInitialized
then Exit
;
549 FChanNum
:= e_PlaySoundPanVolume(FID
, Pan
, Volume
);
550 Result
:= (FChanNum
>= 0);
554 procedure TBasicSound
.SetID(ID
: DWORD
);
558 FMusic
:= e_SoundsArray
[ID
].isMusic
;
562 function TBasicSound
.IsPlaying(): Boolean;
567 if e_isSound(FID
) then
569 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), MSG_WARNING);
573 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), MSG_WARNING);
576 //Result := (Mix_Playing(chan) > 0)
577 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), MSG_WARNING);
580 else if e_isMusic(FID
) then
582 Result
:= (Mix_PlayingMusic() > 0);
586 procedure TBasicSound
.Stop();
590 if e_isSound(FID
) then
596 Mix_HaltChannel(chan
);
599 else if e_isMusic(FID
) then
606 function TBasicSound
.IsPaused(): Boolean;
611 if e_isSound(FID
) then
614 if chan
< 0 then Exit
;
615 Result
:= (Mix_Paused(chan
) > 0);
617 else if e_isMusic(FID
) then
619 Result
:= (Mix_PausedMusic() > 0);
623 procedure TBasicSound
.Pause(Enable
: Boolean);
628 if e_isSound(FID
) then
631 if chan
< 0 then Exit
;
632 pl
:= not (Mix_Paused(chan
) > 0);
635 if Enable
then Mix_Resume(chan
) else Mix_Pause(chan
);
638 else if e_isMusic(FID
) then
640 pl
:= not (Mix_PausedMusic() > 0);
643 if Enable
then Mix_ResumeMusic() else Mix_PauseMusic();
649 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
650 if res <> FMOD_OK then
657 function TBasicSound
.GetVolume(): Single;
662 if e_isSound(FID
) then
665 if chan
< 0 then Exit
;
666 Result
:= (ChanSIds
[chan
].oldvol
+0.0)/(MIX_MAX_VOLUME
+0.0);
668 else if e_isMusic(FID
) then
670 Result
:= (MusVolume
+0.0)/(MIX_MAX_VOLUME
+0.0);
674 procedure TBasicSound
.SetVolume(Volume
: Single);
678 if e_isSound(FID
) then
681 if chan
< 0 then Exit
;
682 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), MSG_WARNING);
683 e_chanSetVol(chan
, Volume
);
685 else if e_isMusic(FID
) then
687 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), MSG_WARNING);
688 e_chanSetVol(N_MUSCHAN
, Volume
);
692 function TBasicSound
.GetPan(): Single;
697 if e_isSound(FID
) then
700 if chan
< 0 then Exit
;
701 Result
:= ChanSIds
[chan
].pan
;
705 procedure TBasicSound
.SetPan(Pan
: Single);
709 if e_isSound(FID
) then
712 if chan
< 0 then Exit
;
713 e_chanSetPan(chan
, Pan
);
717 function TBasicSound
.IsMuted(): Boolean;
722 if e_isSound(FID
) then
725 if chan
< 0 then Exit
;
726 Result
:= ChanSIds
[chan
].muted
;
728 else if e_isMusic(FID
) then
730 Result
:= SoundMuted
;
734 procedure TBasicSound
.Mute(Enable
: Boolean);
738 if e_isSound(FID
) then
741 if chan
< 0 then Exit
;
742 if ChanSIds
[chan
].muted
<> Enable
then
744 //e_WriteLog(Format('muting sound for channel %d', [cnan]), MSG_WARNING);
745 ChanSIds
[chan
].muted
:= Enable
;
746 if ChanSIds
[chan
].muted
then Mix_Volume(chan
, 0) else Mix_Volume(chan
, ChanSIds
[chan
].oldvol
);
749 else if e_isMusic(FID
) then
751 if Enable
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
756 function TBasicSound
.GetPosition(): DWORD
;
760 if FChanNum < 0 then Exit;
761 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
762 if res <> FMOD_OK then
771 procedure TBasicSound
.SetPosition(aPos
: DWORD
);
775 if FChanNum < 0 then Exit;
776 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
777 if res <> FMOD_OK then
784 procedure TBasicSound
.SetPriority(priority
: Integer);
787 if (FChanNum <> nil) and (FPriority <> priority) and
788 (priority >= 0) and (priority <= 256) then
790 FPriority := priority;
791 res := FMOD_Channel_SetPriority(FChanNum, priority);
792 if res <> FMOD_OK then