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 res
:= Mix_Init(MIX_INIT_MOD
or MIX_INIT_MP3
or MIX_INIT_OGG
or MIX_INIT_FLAC
);
130 if (res
and MIX_INIT_FLAC
) <> 0 then e_WriteLog('SDL: FLAC playback is active', MSG_NOTIFY
);
131 if (res
and MIX_INIT_MOD
) <> 0 then e_WriteLog('SDL: MOD/MIDI playback is active', MSG_NOTIFY
);
132 if (res
and MIX_INIT_MP3
) <> 0 then e_WriteLog('SDL: MP3 playback is active', MSG_NOTIFY
);
133 if (res
and MIX_INIT_OGG
) <> 0 then e_WriteLog('SDL: OGG playback is active', MSG_NOTIFY
);
135 res
:= Mix_OpenAudio(48000, AUDIO_S16LSB
, 2, 2048);
136 if res
= -1 then res
:= Mix_OpenAudio(44100, AUDIO_S16LSB
, 2, 2048);
139 e_WriteLog('Error initializing SDL mixer:', MSG_FATALERROR
);
140 e_WriteLog(Mix_GetError(), MSG_FATALERROR
);
144 Mix_AllocateChannels(N_CHANNELS
);
145 Mix_ChannelFinished(chanFinished
);
147 for i
:= 0 to N_CHANNELS
-1 do
149 ChanSIds
[i
].id
:= NO_SOUND_ID
;
150 ChanSIds
[i
].muted
:= SoundMuted
;
151 ChanSIds
[i
].oldvol
:= MIX_MAX_VOLUME
;
152 ChanSIds
[i
].pan
:= 1.0;
154 MusVolume
:= MIX_MAX_VOLUME
;
156 SoundInitialized
:= True;
160 function e_isMusic (id
: DWORD
): Boolean;
163 if (e_SoundsArray
<> nil) and (id
<= High(e_SoundsArray
)) then
165 Result
:= (e_SoundsArray
[id
].Music
<> nil);
169 function e_isSound (id
: DWORD
): Boolean;
172 if (e_SoundsArray
<> nil) and (id
<= High(e_SoundsArray
)) then
174 Result
:= (e_SoundsArray
[id
].Sound
<> nil);
178 function FindESound(): DWORD
;
182 if e_SoundsArray
<> nil then
184 for i
:= 0 to High(e_SoundsArray
) do
185 if (e_SoundsArray
[i
].Sound
= nil) and (e_SoundsArray
[i
].Music
= nil) then
191 if e_SoundsArray
= nil then
193 SetLength(e_SoundsArray
, 16);
198 Result
:= High(e_SoundsArray
) + 1;
199 SetLength(e_SoundsArray
, Length(e_SoundsArray
) + 16);
203 function e_LoadSound(FileName
: String; var ID
: DWORD
; isMusic
: Boolean): Boolean;
208 if not SoundInitialized
then Exit
;
210 if isMusic
then e_WriteLog('Loading music '+FileName
+'...', MSG_NOTIFY
)
211 else e_WriteLog('Loading sound '+FileName
+'...', MSG_NOTIFY
);
213 find_id
:= FindESound();
215 e_SoundsArray
[find_id
].Data
:= nil;
216 e_SoundsArray
[find_id
].isMusic
:= isMusic
;
217 e_SoundsArray
[find_id
].nRefs
:= 0;
221 e_SoundsArray
[find_id
].Music
:= Mix_LoadMUS(PAnsiChar(FileName
));
222 if e_SoundsArray
[find_id
].Music
= nil then Exit
;
226 e_SoundsArray
[find_id
].Sound
:= Mix_LoadWAV(PAnsiChar(FileName
));
227 if e_SoundsArray
[find_id
].Sound
= nil then Exit
;
235 function e_LoadSoundMem(pData
: Pointer; Length
: Integer; var ID
: DWORD
; isMusic
: Boolean): Boolean;
241 if not SoundInitialized
then Exit
;
243 rw
:= SDL_RWFromConstMem(pData
, Length
);
244 if rw
= nil then Exit
;
246 find_id
:= FindESound();
248 e_SoundsArray
[find_id
].Data
:= pData
;
249 e_SoundsArray
[find_id
].isMusic
:= isMusic
;
250 e_SoundsArray
[find_id
].nRefs
:= 0;
254 e_SoundsArray
[find_id
].Music
:= Mix_LoadMUS_RW(rw
, 0);
258 e_SoundsArray
[find_id
].Sound
:= Mix_LoadWAV_RW(rw
, 0);
261 if (e_SoundsArray
[find_id
].Sound
= nil) and (e_SoundsArray
[find_id
].Music
= nil) then Exit
;
268 function e_PlaySound (ID
: DWORD
): Integer;
273 if not SoundInitialized
then Exit
;
275 if e_isSound(ID
) then
277 if e_SoundsArray
[ID
].nRefs
>= gMaxSimSounds
then Exit
;
278 Inc(e_SoundsArray
[ID
].nRefs
);
279 res
:= Mix_PlayChannel(-1, e_SoundsArray
[ID
].Sound
, 0);
282 ChanSIds
[res
].id
:= ID
;
283 ChanSIds
[res
].muted
:= SoundMuted
;
284 if SoundMuted
then Mix_Volume(res
, 0) else Mix_Volume(res
, ChanSIds
[res
].oldvol
);
286 if e_SoundsArray[ID].isMusic then
287 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
289 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
295 if not e_isMusic(ID
) then Exit
;
296 res
:= Mix_PlayMusic(e_SoundsArray
[ID
].Music
, -1);
297 if res
>= 0 then res
:= N_MUSCHAN
;
298 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
305 function e_chanSetPan (chan
: Integer; Pan
: Single): Boolean;
310 if chan
= N_MUSCHAN
then
312 // no panning for music
314 else if chan
>= 0 then
316 if Pan
< -1.0 then Pan
:= -1.0 else if Pan
> 1.0 then Pan
:= 1.0;
317 Pan
:= Pan
+1.0; // 0..2
318 l
:= trunc(127.0*(2.0-Pan
));
319 r
:= trunc(127.0*Pan
);
320 Mix_SetPanning(chan
, l
, r
);
321 ChanSIds
[chan
].pan
:= Pan
;
329 function e_chanSetVol (chan
: Integer; Volume
: Single): Boolean;
334 if Volume
< 0 then Volume
:= 0 else if Volume
> 1 then Volume
:= 1;
335 vol
:= trunc(Volume
*MIX_MAX_VOLUME
);
336 if chan
= N_MUSCHAN
then
339 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol
);
341 else if chan
>= 0 then
343 ChanSIds
[chan
].oldvol
:= vol
;
344 if ChanSIds
[chan
].muted
then Mix_Volume(chan
, 0) else Mix_Volume(chan
, vol
);
352 function e_PlaySoundPan(ID
: DWORD
; Pan
: Single): Integer;
357 chan
:= e_PlaySound(ID
);
358 e_chanSetPan(chan
, Pan
);
362 function e_PlaySoundVolume(ID
: DWORD
; Volume
: Single): Integer;
367 chan
:= e_PlaySound(ID
);
368 e_chanSetVol(chan
, Volume
);
372 function e_PlaySoundPanVolume(ID
: DWORD
; Pan
, Volume
: Single): Integer;
377 chan
:= e_PlaySound(ID
);
378 e_chanSetPan(chan
, Pan
);
379 e_chanSetVol(chan
, Volume
);
383 procedure e_DeleteSound(ID
: DWORD
);
387 if (e_SoundsArray
[ID
].Sound
= nil) and (e_SoundsArray
[ID
].Music
= nil) then Exit
;
389 for i
:= 0 to N_CHANNELS
-1 do
391 if ChanSIds
[i
].id
= ID
then
393 ChanSIds
[i
].id
:= NO_SOUND_ID
;
398 if e_SoundsArray
[ID
].Data
<> nil then FreeMem(e_SoundsArray
[ID
].Data
);
399 if e_SoundsArray
[ID
].Sound
<> nil then Mix_FreeChunk(e_SoundsArray
[ID
].Sound
);
400 if e_SoundsArray
[ID
].Music
<> nil then Mix_FreeMusic(e_SoundsArray
[ID
].Music
);
402 e_SoundsArray
[ID
].Sound
:= nil;
403 e_SoundsArray
[ID
].Music
:= nil;
404 e_SoundsArray
[ID
].Data
:= nil;
405 e_SoundsArray
[ID
].nRefs
:= 0;
408 procedure e_ModifyChannelsVolumes(SoundMod
: Single; setMode
: Boolean);
414 for i
:= 0 to N_CHANNELS
-1 do
416 ovol
:= ChanSIds
[i
].oldvol
;
423 vol
:= (MIX_MAX_VOLUME
+0.0)/ovol
;
426 if vol
< 0 then vol
:= 0 else if vol
> 1 then vol
:= 1;
427 ChanSIds
[i
].oldvol
:= trunc(vol
*MIX_MAX_VOLUME
);
428 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), MSG_WARNING);
429 if ChanSIds
[i
].muted
then Mix_Volume(i
, 0) else Mix_Volume(i
, ChanSIds
[i
].oldvol
);
431 ovol
:= Mix_VolumeMusic(-1);
440 vol
:= (MIX_MAX_VOLUME
+0.0)/ovol
;
441 vol
:= vol
* SoundMod
;
443 if vol
< 0 then vol
:= 0 else if vol
> 1 then vol
:= 1;
444 MusVolume
:= trunc(vol
*MIX_MAX_VOLUME
);
445 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
449 procedure e_MuteChannels(Enable
: Boolean);
453 //if Enable = SoundMuted then Exit;
454 SoundMuted
:= Enable
;
455 for i
:= 0 to N_CHANNELS
-1 do
457 if ChanSIds
[i
].muted
<> SoundMuted
then
459 ChanSIds
[i
].muted
:= SoundMuted
;
460 //e_WriteLog(Format('gmuting sound for channel %d', [i]), MSG_WARNING);
461 if ChanSIds
[i
].muted
then Mix_Volume(i
, 0) else Mix_Volume(i
, ChanSIds
[i
].oldvol
);
464 if SoundMuted
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
467 procedure e_StopChannels();
473 for i
:= 0 to High(e_SoundsArray
) do e_SoundsArray
[i
].nRefs
:= 0;
474 for i
:= 0 to N_CHANNELS
-1 do ChanSIds
[i
].id
:= NO_SOUND_ID
;
477 procedure e_RemoveAllSounds();
481 if SoundInitialized
then e_StopChannels();
482 for i
:= 0 to High(e_SoundsArray
) do e_DeleteSound(i
);
483 SetLength(e_SoundsArray
, 0);
484 e_SoundsArray
:= nil;
487 procedure e_ReleaseSoundSystem();
490 if SoundInitialized
then
493 SoundInitialized
:= False;
497 procedure e_SoundUpdate();
499 //FMOD_System_Update(F_System);
505 constructor TBasicSound
.Create();
514 destructor TBasicSound
.Destroy();
520 function TBasicSound
.GetChan (): Integer;
522 if (FID
<> NO_SOUND_ID
) and (FChanNum
>= 0) and (FChanNum
< N_CHANNELS
) then
524 if ChanSIds
[FChanNum
].id
<> FID
then FChanNum
:= -1;
526 else if e_isMusic(FID
) then
528 FChanNum
:= N_MUSCHAN
;
533 procedure TBasicSound
.FreeSound();
535 if FID
= NO_SOUND_ID
then Exit
;
544 function TBasicSound
.RawPlay(Pan
: Single; Volume
: Single; aPos
: DWORD
): Boolean;
547 if (FID
= NO_SOUND_ID
) or not SoundInitialized
then Exit
;
548 FChanNum
:= e_PlaySoundPanVolume(FID
, Pan
, Volume
);
549 Result
:= (FChanNum
>= 0);
553 procedure TBasicSound
.SetID(ID
: DWORD
);
557 FMusic
:= e_SoundsArray
[ID
].isMusic
;
561 function TBasicSound
.IsPlaying(): Boolean;
566 if e_isSound(FID
) then
568 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), MSG_WARNING);
572 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), MSG_WARNING);
575 //Result := (Mix_Playing(chan) > 0)
576 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), MSG_WARNING);
579 else if e_isMusic(FID
) then
581 Result
:= (Mix_PlayingMusic() > 0);
585 procedure TBasicSound
.Stop();
589 if e_isSound(FID
) then
595 Mix_HaltChannel(chan
);
598 else if e_isMusic(FID
) then
605 function TBasicSound
.IsPaused(): Boolean;
610 if e_isSound(FID
) then
613 if chan
< 0 then Exit
;
614 Result
:= (Mix_Paused(chan
) > 0);
616 else if e_isMusic(FID
) then
618 Result
:= (Mix_PausedMusic() > 0);
622 procedure TBasicSound
.Pause(Enable
: Boolean);
627 if e_isSound(FID
) then
630 if chan
< 0 then Exit
;
631 pl
:= not (Mix_Paused(chan
) > 0);
634 if Enable
then Mix_Resume(chan
) else Mix_Pause(chan
);
637 else if e_isMusic(FID
) then
639 pl
:= not (Mix_PausedMusic() > 0);
642 if Enable
then Mix_ResumeMusic() else Mix_PauseMusic();
648 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
649 if res <> FMOD_OK then
656 function TBasicSound
.GetVolume(): Single;
661 if e_isSound(FID
) then
664 if chan
< 0 then Exit
;
665 Result
:= (ChanSIds
[chan
].oldvol
+0.0)/(MIX_MAX_VOLUME
+0.0);
667 else if e_isMusic(FID
) then
669 Result
:= (MusVolume
+0.0)/(MIX_MAX_VOLUME
+0.0);
673 procedure TBasicSound
.SetVolume(Volume
: Single);
677 if e_isSound(FID
) then
680 if chan
< 0 then Exit
;
681 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), MSG_WARNING);
682 e_chanSetVol(chan
, Volume
);
684 else if e_isMusic(FID
) then
686 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), MSG_WARNING);
687 e_chanSetVol(N_MUSCHAN
, Volume
);
691 function TBasicSound
.GetPan(): Single;
696 if e_isSound(FID
) then
699 if chan
< 0 then Exit
;
700 Result
:= ChanSIds
[chan
].pan
;
704 procedure TBasicSound
.SetPan(Pan
: Single);
708 if e_isSound(FID
) then
711 if chan
< 0 then Exit
;
712 e_chanSetPan(chan
, Pan
);
716 function TBasicSound
.IsMuted(): Boolean;
721 if e_isSound(FID
) then
724 if chan
< 0 then Exit
;
725 Result
:= ChanSIds
[chan
].muted
;
727 else if e_isMusic(FID
) then
729 Result
:= SoundMuted
;
733 procedure TBasicSound
.Mute(Enable
: Boolean);
737 if e_isSound(FID
) then
740 if chan
< 0 then Exit
;
741 if ChanSIds
[chan
].muted
<> Enable
then
743 //e_WriteLog(Format('muting sound for channel %d', [cnan]), MSG_WARNING);
744 ChanSIds
[chan
].muted
:= Enable
;
745 if ChanSIds
[chan
].muted
then Mix_Volume(chan
, 0) else Mix_Volume(chan
, ChanSIds
[chan
].oldvol
);
748 else if e_isMusic(FID
) then
750 if Enable
then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume
);
755 function TBasicSound
.GetPosition(): DWORD
;
759 if FChanNum < 0 then Exit;
760 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
761 if res <> FMOD_OK then
770 procedure TBasicSound
.SetPosition(aPos
: DWORD
);
774 if FChanNum < 0 then Exit;
775 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
776 if res <> FMOD_OK then
783 procedure TBasicSound
.SetPriority(priority
: Integer);
786 if (FChanNum <> nil) and (FPriority <> priority) and
787 (priority >= 0) and (priority <= 256) then
789 FPriority := priority;
790 res := FMOD_Channel_SetPriority(FChanNum, priority);
791 if res <> FMOD_OK then