1 (* Copyright (C) Doom 2D: Forever Developers
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.
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.
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/>.
15 {$INCLUDE ../shared/a_modes.inc}
28 TPlayableSound
= class(TBasicSound
)
34 destructor Destroy(); override;
35 function Play(Force
: Boolean = False): Boolean;
36 function PlayAt(X
, Y
: Integer): Boolean;
37 function PlayPanVolume(Pan
, Volume
: Single; Force
: Boolean = False): Boolean;
38 function PlayVolumeAt(X
, Y
: Integer; Volume
: Single): Boolean;
39 function PlayVolumeAtRect (X
, Y
, W
, H
: Integer; Volume
: Single): Boolean;
40 function SetByName(SN
: String): Boolean;
41 function SetCoords(X
, Y
: Integer; Volume
: Single): Boolean;
42 function SetCoordsRect (X
, Y
, W
, H
: Integer; Volume
: Single): Boolean;
44 property Loop
: Boolean read FMusic write FMusic
;
45 property Name
: String read FName
;
48 TMusic
= class(TBasicSound
)
51 FSpecPause
: Boolean; // Ñïåö-ïàóçà. "Ñèëüíåå" îáû÷íîé
54 procedure SetSpecPause(Enable
: Boolean);
58 destructor Destroy(); override;
59 function Play(Force
: Boolean = False): Boolean;
60 function SetByName(SN
: String): Boolean;
61 function IsPaused(): Boolean;
62 procedure Pause(Enable
: Boolean);
64 property Name
: String read FName
;
65 property SpecPause
: Boolean read FSpecPause write SetSpecPause
;
66 property NoMusic
: Boolean read FNoMusic
;
69 function g_Sound_PlayEx(SoundName
: ShortString
): Boolean;
70 function g_Sound_PlayExPanVolume(SoundName
: ShortString
; Pan
: Single; Volume
: Single): Boolean;
71 function g_Sound_PlayAt(ID
: DWORD
; X
, Y
: Integer): Boolean;
72 function g_Sound_PlayExAt(SoundName
: ShortString
; X
, Y
: Integer): Boolean;
74 function g_Sound_CreateWAD(var ID
: TSoundID
; Resource
: String; isMusic
: Boolean = False): Boolean;
75 function g_Sound_CreateWADEx(SoundName
: ShortString
; Resource
: string; isMusic
: Boolean = False;
76 ForceNoLoop
: Boolean = False): Boolean;
77 function g_Sound_CreateFile(var ID
: TSoundID
; FileName
: String; isMusic
: Boolean = False): Boolean;
78 function g_Sound_CreateFileEx(SoundName
: ShortString
; FileName
: string; isMusic
: Boolean = False;
79 ForceNoLoop
: Boolean = False): Boolean;
81 procedure g_Sound_Delete(SoundName
: ShortString
);
82 function g_Sound_Exists(SoundName
: string): Boolean;
83 function g_Sound_Get(var ID
: DWORD
; SoundName
: ShortString
): Boolean;
85 procedure g_Sound_SetupAllVolumes(SoundVol
, MusicVol
: Byte);
90 e_log
, SysUtils
, g_console
, g_options
, wadreader
,
91 g_game
, g_basic
, g_items
, g_map
, Math
,
102 SoundArray
: Array of TGameSound
;
103 //SoundsMuted: Boolean = False;
106 function FindSound(): DWORD
;
110 if SoundArray
<> nil then
111 for i
:= 0 to High(SoundArray
) do
112 if SoundArray
[i
].Name
= '' then
118 if SoundArray
= nil then
120 SetLength(SoundArray
, 8);
125 Result
:= High(SoundArray
) + 1;
126 SetLength(SoundArray
, Length(SoundArray
) + 8);
130 function g_Sound_PlayEx(SoundName
: ShortString
): Boolean;
135 if SoundArray
= nil then
138 for a
:= 0 to High(SoundArray
) do
139 if SoundArray
[a
].Name
= SoundName
then
141 Result
:= (e_PlaySoundVolume(SoundArray
[a
].ID
, gSoundLevel
/255.0) >= 0);
145 e_WriteLog(Format(_lc
[I_GAME_ERROR_SOUND
], [SoundName
]), TMsgType
.Warning
);
148 function g_Sound_PlayExPanVolume(SoundName
: ShortString
; Pan
: Single; Volume
: Single): Boolean;
153 if SoundArray
= nil then
156 for a
:= 0 to High(SoundArray
) do
157 if SoundArray
[a
].Name
= SoundName
then
159 Result
:= (e_PlaySoundPanVolume(SoundArray
[a
].ID
, Pan
, Volume
* (gSoundLevel
/255.0)) >= 0);
163 e_WriteLog(Format(_lc
[I_GAME_ERROR_SOUND
], [SoundName
]), TMsgType
.Warning
);
166 function PlaySoundAtRect (X
, Y
, W
, H
: Integer; out Pan
, Volume
: Single; InVolume
: Single = 1.0): Boolean;
172 procedure CalcDest (const p
: THearPoint
; out pan
: Single; out len
: Integer);
173 var XX
, YY
, lx
, rx
: Integer;
175 pan
:= 0.0; len
:= gMaxDist
;
178 XX
:= Max(X
, Min(X
+ W
, p
.Coords
.X
));
179 YY
:= Max(Y
, Min(Y
+ H
, p
.Coords
.Y
));
180 len
:= Round(Hypot(XX
- p
.Coords
.X
, YY
- p
.Coords
.Y
));
181 if sMaxDist
< SOUND_MINDIST
then
183 lx
:= X
- SOUND_MINDIST
;
184 rx
:= X
+ W
+ SOUND_MINDIST
;
185 if p
.Coords
.X
< lx
then
186 pan
:= (lx
- p
.Coords
.X
) / sMaxDist
187 else if p
.Coords
.X
> rx
then
188 pan
:= (rx
- p
.Coords
.X
) / sMaxDist
194 ASSERT((W
>= 0) and (H
>= 0));
195 ASSERT((InVolume
>= 0.0) and (InVolume
<= 1.0));
196 sMaxDist
:= SOUND_MAXDIST
* InVolume
;
197 X
:= Max(0, Min(X
, gMapInfo
.Width
));
198 Y
:= Max(0, Min(Y
, gMapInfo
.Height
));
199 CalcDest(gHearPoint1
, pan1
, len1
);
200 CalcDest(gHearPoint2
, pan2
, len2
);
206 if len1
>= sMaxDist
then
215 Volume
:= 1.0 - len1
/ sMaxDist
;
220 function PlaySoundAt(X
, Y
: Integer; out Pan
: Single; out Volume
: Single; InVolume
: Single = 1.0): Boolean;
222 Result
:= PlaySoundAtRect(X
, Y
, 0, 0, Pan
, Volume
, InVolume
)
225 function g_Sound_PlayAt(ID
: DWORD
; X
, Y
: Integer): Boolean;
229 if PlaySoundAt(X
, Y
, Pan
, Vol
) then
230 Result
:= (e_PlaySoundPanVolume(ID
, Pan
, Vol
* (gSoundLevel
/255.0)) >= 0)
235 function g_Sound_PlayExAt(SoundName
: ShortString
; X
, Y
: Integer): Boolean;
242 if SoundArray
= nil then
245 for a
:= 0 to High(SoundArray
) do
246 if SoundArray
[a
].Name
= SoundName
then
248 if PlaySoundAt(X
, Y
, Pan
, Vol
) then
249 Result
:= (e_PlaySoundPanVolume(SoundArray
[a
].ID
, Pan
, Vol
* (gSoundLevel
/255.0)) >= 0);
253 e_WriteLog(Format(_lc
[I_GAME_ERROR_SOUND
], [SoundName
]), TMsgType
.Warning
);
256 function g_Sound_CreateFile(var ID
: TSoundID
; FileName
: String; isMusic
: Boolean): Boolean;
258 Result
:= e_LoadSound(FileName
, ID
, isMusic
);
261 function g_Sound_CreateFileEx(SoundName
: ShortString
; FileName
: String; isMusic
: Boolean;
262 ForceNoLoop
: Boolean): Boolean;
268 find_id
:= FindSound();
270 if not e_LoadSound(FileName
, SoundArray
[find_id
].ID
, isMusic
, ForceNoLoop
) then
273 SoundArray
[find_id
].Name
:= SoundName
;
274 SoundArray
[find_id
].IsMusic
:= isMusic
;
279 function g_Sound_CreateWAD(var ID
: TSoundID
; Resource
: String; isMusic
: Boolean): Boolean;
290 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
291 FileName
:= g_ExtractWadName(Resource
);
293 WAD
:= TWADFile
.Create();
294 WAD
.ReadFile(FileName
);
296 if WAD
.GetResource(g_ExtractFilePathName(Resource
), SoundData
, ResLength
) then
298 if e_LoadSoundMem(SoundData
, ResLength
, ID
, isMusic
) then
305 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
313 e_WriteLog(Format('Error loading music %s', [Resource
]), TMsgType
.Warning
)
315 e_WriteLog(Format('Error loading sound %s', [Resource
]), TMsgType
.Warning
);
322 function g_Sound_CreateWADEx(SoundName
: ShortString
; Resource
: String; isMusic
: Boolean;
323 ForceNoLoop
: Boolean): Boolean;
335 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
336 FileName
:= g_ExtractWadName(Resource
);
338 find_id
:= FindSound();
340 WAD
:= TWADFile
.Create();
341 WAD
.ReadFile(FileName
);
343 if WAD
.GetResource(g_ExtractFilePathName(Resource
), SoundData
, ResLength
) then
345 if e_LoadSoundMem(SoundData
, ResLength
, SoundArray
[find_id
].ID
, isMusic
, ForceNoLoop
) then
347 SoundArray
[find_id
].Name
:= SoundName
;
348 SoundArray
[find_id
].IsMusic
:= isMusic
;
356 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
364 e_WriteLog(Format('Error loading music %s', [Resource
]), TMsgType
.Warning
)
366 e_WriteLog(Format('Error loading sound %s', [Resource
]), TMsgType
.Warning
);
373 procedure g_Sound_Delete(SoundName
: ShortString
);
377 if (SoundArray
= nil) or (SoundName
= '') then
380 for a
:= 0 to High(SoundArray
) do
381 if SoundArray
[a
].Name
= SoundName
then
383 e_DeleteSound(SoundArray
[a
].ID
);
384 SoundArray
[a
].Name
:= '';
385 SoundArray
[a
].ID
:= 0;
386 SoundArray
[a
].IsMusic
:= False;
390 function g_Sound_Exists(SoundName
: string): Boolean;
396 if SoundName
= '' then
399 if SoundArray
<> nil then
400 for a
:= 0 to High(SoundArray
) do
401 if SoundArray
[a
].Name
= SoundName
then
408 function g_Sound_Get(var ID
: DWORD
; SoundName
: ShortString
): Boolean;
414 if SoundName
= '' then
417 if SoundArray
<> nil then
418 for a
:= 0 to High(SoundArray
) do
419 if SoundArray
[a
].Name
= SoundName
then
421 ID
:= SoundArray
[a
].ID
;
427 procedure g_Sound_SetupAllVolumes(SoundVol
, MusicVol
: Byte);
432 Mvol
:= 0; // shut up, compiler
433 if (gSoundLevel
= SoundVol
) and (gMusicLevel
= MusicVol
) then
436 if gSoundLevel
> 0 then
438 Svol
:= SoundVol
/ gSoundLevel
;
443 Svol
:= SoundVol
/ 255.0;
447 if gMusic
<> nil then
448 if gMusicLevel
> 0 then
449 Mvol
:= gMusic
.GetVolume() * MusicVol
/ gMusicLevel
451 Mvol
:= MusicVol
/ 255.0;
453 e_ModifyChannelsVolumes(Svol
, sm
);
455 if gMusic
<> nil then
456 gMusic
.SetVolume(Mvol
);
458 gSoundLevel
:= SoundVol
;
459 gMusicLevel
:= MusicVol
;
464 constructor TPlayableSound
.Create();
470 destructor TPlayableSound
.Destroy();
475 function TPlayableSound
.Play(Force
: Boolean = False): Boolean;
477 if Force
or not IsPlaying() then
480 Result
:= RawPlay(0.0, gSoundLevel
/255.0, FPosition
);
486 function TPlayableSound
.PlayAt(X
, Y
: Integer): Boolean;
490 if PlaySoundAt(X
, Y
, Pan
, Vol
) then
493 Result
:= RawPlay(Pan
, Vol
* (gSoundLevel
/255.0), FPosition
);
499 function TPlayableSound
.PlayPanVolume(Pan
, Volume
: Single; Force
: Boolean = False): Boolean;
501 if Force
or not IsPlaying() then
504 Result
:= RawPlay(Pan
, Volume
* (gSoundLevel
/255.0), FPosition
);
510 function TPlayableSound
.PlayVolumeAtRect (X
, Y
, W
, H
: Integer; Volume
: Single): Boolean;
511 var Pan
, Vol
: Single;
514 if PlaySoundAtRect(X
, Y
, W
, H
, Pan
, Vol
, Volume
) then
517 Result
:= RawPlay(Pan
, Volume
* Vol
* (gSoundLevel
/ 255.0), FPosition
)
521 function TPlayableSound
.PlayVolumeAt (X
, Y
: Integer; Volume
: Single): Boolean;
523 Result
:= Self
.PlayVolumeAtRect(X
, Y
, 0, 0, Volume
)
526 function TPlayableSound
.SetCoordsRect (X
, Y
, W
, H
: Integer; Volume
: Single): Boolean;
527 var Pan
, Vol
: Single;
529 if PlaySoundAtRect(X
, Y
, W
, H
, Pan
, Vol
, Volume
) then
531 SetVolume(Volume
* Vol
* (gSoundLevel
/ 255.0));
543 function TPlayableSound
.SetCoords(X
, Y
: Integer; Volume
: Single): Boolean;
545 Result
:= Self
.SetCoordsRect(X
, Y
, 0, 0, Volume
)
548 function TPlayableSound
.SetByName(SN
: String): Boolean;
552 if g_Sound_Get(id
, SN
) then
564 constructor TMusic
.Create();
572 destructor TMusic
.Destroy();
577 function TMusic
.Play(Force
: Boolean = False): Boolean;
585 if Force
or not IsPlaying() then
588 Result
:= RawPlay(0.0, gMusicLevel
/255.0, FPosition
);
591 if Result
and FSpecPause
then
598 function TMusic
.SetByName(SN
: String): Boolean;
609 if g_Sound_Get(id
, SN
) then
621 function TMusic
.IsPaused(): Boolean;
623 Result
:= inherited IsPaused();
624 Result
:= Result
or FSpecPause
;
627 procedure TMusic
.Pause(Enable
: Boolean);
629 // Îòêëþ÷àåì ïàóçó, òîëüêî åñëè íå áûëî ñïåö-ïàóçû:
630 if Enable
or (not FSpecPause
) then
631 inherited Pause(Enable
);
634 procedure TMusic
.SetSpecPause(Enable
: Boolean);
636 FSpecPause
:= Enable
;
642 conRegVar('s_midi_soundfont', @e_SoundFont
, 'soundfont to use for midi playback', 'midi soundfont');
643 conRegVar('s_mod_lerp', @e_MusicLerp
, 'interpolate module playback', 'module interpolation');