DEADSOFTWARE

initial commit:
[d2df-sdl.git] / src / game / g_sound.pas
1 unit g_sound;
3 interface
5 uses
6 e_sound;
8 const
9 SOUND_MINDIST = 400;
10 SOUND_MAXDIST = 1000;
12 type
13 TPlayableSound = class(TBasicSound)
14 private
15 FName: String;
17 public
18 constructor Create();
19 destructor Destroy(); override;
20 function Play(Force: Boolean = False): Boolean;
21 function PlayAt(X, Y: Integer): Boolean;
22 function PlayPanVolume(Pan, Volume: Single; Force: Boolean = False): Boolean;
23 function PlayVolumeAt(X, Y: Integer; Volume: Single): Boolean;
24 function SetByName(SN: String): Boolean;
25 function SetCoords(X, Y: Integer; Volume: Single): Boolean;
27 property Loop: Boolean read FLoop write FLoop;
28 property Name: String read FName;
29 end;
31 TMusic = class(TBasicSound)
32 private
33 FName: String;
34 FSpecPause: Boolean; // Ñïåö-ïàóçà. "Ñèëüíåå" îáû÷íîé
35 FNoMusic: Boolean;
37 procedure SetSpecPause(Enable: Boolean);
39 public
40 constructor Create();
41 destructor Destroy(); override;
42 function Play(Force: Boolean = False): Boolean;
43 function SetByName(SN: String): Boolean;
44 function IsPaused(): Boolean;
45 procedure Pause(Enable: Boolean);
47 property Name: String read FName;
48 property SpecPause: Boolean read FSpecPause write SetSpecPause;
49 property NoMusic: Boolean read FNoMusic;
50 end;
52 function g_Sound_PlayEx(SoundName: ShortString): Boolean;
53 function g_Sound_PlayExPanVolume(SoundName: ShortString; Pan: Single; Volume: Single): Boolean;
54 function g_Sound_PlayAt(ID: DWORD; X, Y: Integer): Boolean;
55 function g_Sound_PlayExAt(SoundName: ShortString; X, Y: Integer): Boolean;
57 function g_Sound_CreateWAD(var ID: DWORD; Resource: string; isMusic: Boolean = False): Boolean;
58 function g_Sound_CreateWADEx(SoundName: ShortString; Resource: string; isMusic: Boolean = False): Boolean;
59 function g_Sound_CreateFile(var ID: DWORD; FileName: string; isMusic: Boolean = False): Boolean;
60 function g_Sound_CreateFileEx(SoundName: ShortString; FileName: string; isMusic: Boolean = False): Boolean;
62 procedure g_Sound_Delete(SoundName: ShortString);
63 function g_Sound_Exists(SoundName: string): Boolean;
64 function g_Sound_Get(var ID: DWORD; SoundName: ShortString): Boolean;
66 procedure g_Sound_SetupAllVolumes(SoundVol, MusicVol: Byte);
68 implementation
70 uses
71 e_log, SysUtils, g_console, g_options, WADEDITOR,
72 g_game, g_basic, g_items, g_map, fmod, fmodtypes, Math,
73 g_language;
75 type
76 TGameSound = record
77 Name: ShortString;
78 ID: DWORD;
79 IsMusic: Boolean;
80 end;
82 var
83 SoundArray: Array of TGameSound;
84 //SoundsMuted: Boolean = False;
87 function FindSound(): DWORD;
88 var
89 i: integer;
90 begin
91 if SoundArray <> nil then
92 for i := 0 to High(SoundArray) do
93 if SoundArray[i].Name = '' then
94 begin
95 Result := i;
96 Exit;
97 end;
99 if SoundArray = nil then
100 begin
101 SetLength(SoundArray, 8);
102 Result := 0;
103 end
104 else
105 begin
106 Result := High(SoundArray) + 1;
107 SetLength(SoundArray, Length(SoundArray) + 8);
108 end;
109 end;
111 function g_Sound_PlayEx(SoundName: ShortString): Boolean;
112 var
113 a: DWORD;
114 begin
115 Result := False;
116 if SoundArray = nil then
117 Exit;
119 for a := 0 to High(SoundArray) do
120 if SoundArray[a].Name = SoundName then
121 begin
122 Result := e_PlaySoundVolume(SoundArray[a].ID, gSoundLevel/255.0);
123 Exit;
124 end;
126 e_WriteLog(Format(_lc[I_GAME_ERROR_SOUND], [SoundName]), MSG_WARNING);
127 end;
129 function g_Sound_PlayExPanVolume(SoundName: ShortString; Pan: Single; Volume: Single): Boolean;
130 var
131 a: DWORD;
132 begin
133 Result := False;
134 if SoundArray = nil then
135 Exit;
137 for a := 0 to High(SoundArray) do
138 if SoundArray[a].Name = SoundName then
139 begin
140 Result := e_PlaySoundPanVolume(SoundArray[a].ID, Pan, Volume * (gSoundLevel/255.0));
141 Exit;
142 end;
144 e_WriteLog(Format(_lc[I_GAME_ERROR_SOUND], [SoundName]), MSG_WARNING);
145 end;
147 function PlaySoundAt(X, Y: Integer; var Pan: Single; var Volume: Single; InVolume: Single = 1.0): Boolean;
148 var
149 l1, l2, lx, rx: Integer;
150 d1, d2, sMaxDist: Single;
151 c: Boolean;
152 begin
153 l1 := gMaxDist;
154 l2 := gMaxDist;
155 sMaxDist := SOUND_MAXDIST * InVolume;
157 d1 := 0.0;
159 c := SOUND_MINDIST >= sMaxDist;
161 if X > gMapInfo.Width then
162 X := gMapInfo.Width
163 else
164 if X < 0 then
165 X := 0;
167 if Y > gMapInfo.Height then
168 Y := gMapInfo.Height
169 else
170 if Y < 0 then
171 Y := 0;
173 if gHearPoint1.Active then
174 begin
175 l1 := Round(Hypot(X - gHearPoint1.Coords.X, Y - gHearPoint1.Coords.Y));
177 lx := gHearPoint1.Coords.X - SOUND_MINDIST;
178 rx := gHearPoint1.Coords.X + SOUND_MINDIST;
179 if c then
180 d1 := 0.0
181 else if (X >= lx) and (X <= rx) then
182 d1 := 0.0
183 else if X < lx then
184 d1 := (X-lx)/sMaxDist
185 else
186 d1 := (X-rx)/sMaxDist;
187 end;
189 d2 := d1;
191 if gHearPoint2.Active then
192 begin
193 l2 := Round(Hypot(X - gHearPoint2.Coords.X, Y - gHearPoint2.Coords.Y));
195 lx := gHearPoint2.Coords.X - SOUND_MINDIST;
196 rx := gHearPoint2.Coords.X + SOUND_MINDIST;
197 if c then
198 d2 := 0.0
199 else if (X >= lx) and (X <= rx) then
200 d2 := 0.0
201 else if X < lx then
202 d2 := (X-lx)/sMaxDist
203 else
204 d2 := (X-rx)/sMaxDist;
205 end;
207 if l2 < l1 then
208 begin
209 l1 := l2;
210 d1 := d2;
211 end;
213 if l1 >= sMaxDist then
214 begin
215 Pan := 0.0;
216 Volume := 0.0;
217 Result := False;
218 end
219 else
220 begin
221 Pan := d1;
222 Volume := 1.0 - l1/sMaxDist;
223 Result := True;
224 end;
225 end;
227 function g_Sound_PlayAt(ID: DWORD; X, Y: Integer): Boolean;
228 var
229 Pan, Vol: Single;
230 begin
231 if PlaySoundAt(X, Y, Pan, Vol) then
232 Result := e_PlaySoundPanVolume(ID, Pan, Vol * (gSoundLevel/255.0))
233 else
234 Result := False;
235 end;
237 function g_Sound_PlayExAt(SoundName: ShortString; X, Y: Integer): Boolean;
238 var
239 a: DWORD;
240 Pan, Vol: Single;
241 begin
242 Result := False;
244 if SoundArray = nil then
245 Exit;
247 for a := 0 to High(SoundArray) do
248 if SoundArray[a].Name = SoundName then
249 begin
250 if PlaySoundAt(X, Y, Pan, Vol) then
251 Result := e_PlaySoundPanVolume(SoundArray[a].ID,
252 Pan, Vol * (gSoundLevel/255.0));
253 Exit;
254 end;
256 e_WriteLog(Format(_lc[I_GAME_ERROR_SOUND], [SoundName]), MSG_WARNING);
257 end;
259 function g_Sound_CreateFile(var ID: DWORD; FileName: string; isMusic: Boolean = False): Boolean;
260 begin
261 Result := e_LoadSound(FileName, ID, isMusic);
262 end;
264 function g_Sound_CreateFileEx(SoundName: ShortString; FileName: string; isMusic: Boolean = False): Boolean;
265 var
266 find_id: DWORD;
267 begin
268 Result := False;
270 find_id := FindSound();
272 if not e_LoadSound(FileName, SoundArray[find_id].ID, isMusic) then
273 Exit;
275 SoundArray[find_id].Name := SoundName;
276 SoundArray[find_id].IsMusic := isMusic;
278 Result := True;
279 end;
281 function g_Sound_CreateWAD(var ID: DWORD; Resource: string; isMusic: Boolean = False): Boolean;
282 var
283 WAD: TWADEditor_1;
284 FileName,
285 SectionName,
286 ResourceName: string;
287 SoundData: Pointer;
288 ResLength: Integer;
289 ok: Boolean;
290 begin
291 Result := False;
292 ok := False;
294 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
295 g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName);
297 WAD := TWADEditor_1.Create();
298 WAD.ReadFile(FileName);
300 if WAD.GetResource(SectionName, ResourceName, SoundData, ResLength) then
301 begin
302 if e_LoadSoundMem(SoundData, ResLength, ID, isMusic) then
303 ok := True
304 else
305 FreeMem(SoundData);
306 end
307 else
308 e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
310 WAD.Free();
312 if not ok then
313 begin
314 if isMusic then
315 e_WriteLog(Format('Error loading music %s', [Resource]), MSG_WARNING)
316 else
317 e_WriteLog(Format('Error loading sound %s', [Resource]), MSG_WARNING);
318 Exit;
319 end;
321 Result := True;
322 end;
324 function g_Sound_CreateWADEx(SoundName: ShortString; Resource: string; isMusic: Boolean = False): Boolean;
325 var
326 WAD: TWADEditor_1;
327 FileName, SectionName, ResourceName: string;
328 SoundData: Pointer;
329 ResLength: Integer;
330 find_id: DWORD;
331 ok: Boolean;
332 begin
333 Result := False;
334 ok := False;
336 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
337 g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName);
339 find_id := FindSound();
341 WAD := TWADEditor_1.Create();
342 WAD.ReadFile(FileName);
344 if WAD.GetResource(SectionName, ResourceName, SoundData, ResLength) then
345 begin
346 if e_LoadSoundMem(SoundData, ResLength, SoundArray[find_id].ID, isMusic) then
347 begin
348 SoundArray[find_id].Name := SoundName;
349 SoundArray[find_id].IsMusic := isMusic;
350 ok := True;
351 end
352 else
353 FreeMem(SoundData);
354 end
355 else
356 e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
358 WAD.Free();
360 if not ok then
361 begin
362 if isMusic then
363 e_WriteLog(Format('Error loading music %s', [Resource]), MSG_WARNING)
364 else
365 e_WriteLog(Format('Error loading sound %s', [Resource]), MSG_WARNING);
366 Exit;
367 end;
369 Result := True;
370 end;
372 procedure g_Sound_Delete(SoundName: ShortString);
373 var
374 a: DWORD;
375 begin
376 if (SoundArray = nil) or (SoundName = '') then
377 Exit;
379 for a := 0 to High(SoundArray) do
380 if SoundArray[a].Name = SoundName then
381 begin
382 e_DeleteSound(SoundArray[a].ID);
383 SoundArray[a].Name := '';
384 SoundArray[a].ID := 0;
385 SoundArray[a].IsMusic := False;
386 end;
387 end;
389 function g_Sound_Exists(SoundName: string): Boolean;
390 var
391 a: DWORD;
392 begin
393 Result := False;
395 if SoundName = '' then
396 Exit;
398 if SoundArray <> nil then
399 for a := 0 to High(SoundArray) do
400 if SoundArray[a].Name = SoundName then
401 begin
402 Result := True;
403 Break;
404 end;
405 end;
407 function g_Sound_Get(var ID: DWORD; SoundName: ShortString): Boolean;
408 var
409 a: DWORD;
410 begin
411 Result := False;
413 if SoundName = '' then
414 Exit;
416 if SoundArray <> nil then
417 for a := 0 to High(SoundArray) do
418 if SoundArray[a].Name = SoundName then
419 begin
420 ID := SoundArray[a].ID;
421 Result := True;
422 Break;
423 end;
424 end;
426 procedure g_Sound_SetupAllVolumes(SoundVol, MusicVol: Byte);
427 var
428 Svol, Mvol: Single;
429 sm: Boolean;
430 begin
431 Mvol := 0; // shut up, compiler
432 if (gSoundLevel = SoundVol) and (gMusicLevel = MusicVol) then
433 Exit;
435 if gSoundLevel > 0 then
436 begin
437 Svol := SoundVol / gSoundLevel;
438 sm := False;
439 end
440 else
441 begin
442 Svol := SoundVol / 255.0;
443 sm := True;
444 end;
446 if gMusic <> nil then
447 if gMusicLevel > 0 then
448 Mvol := gMusic.GetVolume() * MusicVol / gMusicLevel
449 else
450 Mvol := MusicVol / 255.0;
452 e_ModifyChannelsVolumes(Svol, sm);
454 if gMusic <> nil then
455 gMusic.SetVolume(Mvol);
457 gSoundLevel := SoundVol;
458 gMusicLevel := MusicVol;
459 end;
461 { TPlayableSound: }
463 constructor TPlayableSound.Create();
464 begin
465 inherited;
466 FName := '';
467 end;
469 destructor TPlayableSound.Destroy();
470 begin
471 inherited;
472 end;
474 function TPlayableSound.Play(Force: Boolean = False): Boolean;
475 begin
476 if Force or not IsPlaying() then
477 begin
478 Stop();
479 Result := RawPlay(0.0, gSoundLevel/255.0, FPosition);
480 end
481 else
482 Result := False;
483 end;
485 function TPlayableSound.PlayAt(X, Y: Integer): Boolean;
486 var
487 Pan, Vol: Single;
488 begin
489 if PlaySoundAt(X, Y, Pan, Vol) then
490 begin
491 Stop();
492 Result := RawPlay(Pan, Vol * (gSoundLevel/255.0), FPosition);
493 end
494 else
495 Result := False;
496 end;
498 function TPlayableSound.PlayPanVolume(Pan, Volume: Single; Force: Boolean = False): Boolean;
499 begin
500 if Force or not IsPlaying() then
501 begin
502 Stop();
503 Result := RawPlay(Pan, Volume * (gSoundLevel/255.0), FPosition);
504 end
505 else
506 Result := False;
507 end;
509 function TPlayableSound.PlayVolumeAt(X, Y: Integer; Volume: Single): Boolean;
510 var
511 Pan, Vol: Single;
512 begin
513 if PlaySoundAt(X, Y, Pan, Vol, Volume) then
514 begin
515 Stop();
516 Result := RawPlay(Pan, Volume * Vol * (gSoundLevel/255.0), FPosition);
517 end
518 else
519 Result := False;
520 end;
522 function TPlayableSound.SetCoords(X, Y: Integer; Volume: Single): Boolean;
523 var
524 Pan, Vol: Single;
525 begin
526 if PlaySoundAt(X, Y, Pan, Vol, Volume) then
527 begin
528 SetVolume(Volume * Vol * (gSoundLevel/255.0));
529 SetPan(Pan);
530 Result := True;
531 end
532 else
533 begin
534 SetVolume(0.0);
535 SetPan(0.0);
536 Result := False;
537 end;
538 end;
540 function TPlayableSound.SetByName(SN: String): Boolean;
541 var
542 id: DWORD;
543 begin
544 if g_Sound_Get(id, SN) then
545 begin
546 SetID(id);
547 FName := SN;
548 Result := True;
549 end
550 else
551 Result := False;
552 end;
554 { TMusic: }
556 constructor TMusic.Create();
557 begin
558 inherited;
559 FName := '';
560 FSpecPause := False;
561 FNoMusic := True;
562 end;
564 destructor TMusic.Destroy();
565 begin
566 inherited;
567 end;
569 function TMusic.Play(Force: Boolean = False): Boolean;
570 begin
571 if FNoMusic then
572 begin
573 Result := True;
574 Exit;
575 end;
577 if Force or not IsPlaying() then
578 begin
579 Stop();
580 Result := RawPlay(0.0, gMusicLevel/255.0, FPosition);
581 if Result then
582 SetPriority(0);
583 if Result and FSpecPause then
584 Pause(True);
585 end
586 else
587 Result := False;
588 end;
590 function TMusic.SetByName(SN: String): Boolean;
591 var
592 id: DWORD;
593 begin
594 if SN = '' then
595 begin
596 FNoMusic := True;
597 Result := True;
598 Exit;
599 end;
601 if g_Sound_Get(id, SN) then
602 begin
603 SetID(id);
604 FName := SN;
605 FNoMusic := False;
606 FSpecPause := False;
607 Result := True;
608 end
609 else
610 Result := False;
611 end;
613 function TMusic.IsPaused(): Boolean;
614 begin
615 Result := inherited IsPaused();
616 Result := Result or FSpecPause;
617 end;
619 procedure TMusic.Pause(Enable: Boolean);
620 begin
621 // Îòêëþ÷àåì ïàóçó, òîëüêî åñëè íå áûëî ñïåö-ïàóçû:
622 if Enable or (not FSpecPause) then
623 inherited Pause(Enable);
624 end;
626 procedure TMusic.SetSpecPause(Enable: Boolean);
627 begin
628 FSpecPause := Enable;
629 Pause(Enable);
630 end;
632 end.