DEADSOFTWARE

fmod -> sdl2 mixer, first part: we have music now; still no advanced stuff
[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 FMusic write FMusic;
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, 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) >= 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)) >= 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)) >= 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, Pan, Vol * (gSoundLevel/255.0)) >= 0);
252 Exit;
253 end;
255 e_WriteLog(Format(_lc[I_GAME_ERROR_SOUND], [SoundName]), MSG_WARNING);
256 end;
258 function g_Sound_CreateFile(var ID: DWORD; FileName: string; isMusic: Boolean = False): Boolean;
259 begin
260 Result := e_LoadSound(FileName, ID, isMusic);
261 end;
263 function g_Sound_CreateFileEx(SoundName: ShortString; FileName: string; isMusic: Boolean = False): Boolean;
264 var
265 find_id: DWORD;
266 begin
267 Result := False;
269 find_id := FindSound();
271 if not e_LoadSound(FileName, SoundArray[find_id].ID, isMusic) then
272 Exit;
274 SoundArray[find_id].Name := SoundName;
275 SoundArray[find_id].IsMusic := isMusic;
277 Result := True;
278 end;
280 function g_Sound_CreateWAD(var ID: DWORD; Resource: string; isMusic: Boolean = False): Boolean;
281 var
282 WAD: TWADEditor_1;
283 FileName,
284 SectionName,
285 ResourceName: string;
286 SoundData: Pointer;
287 ResLength: Integer;
288 ok: Boolean;
289 begin
290 Result := False;
291 ok := False;
293 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
294 g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName);
296 WAD := TWADEditor_1.Create();
297 WAD.ReadFile(FileName);
299 if WAD.GetResource(SectionName, ResourceName, SoundData, ResLength) then
300 begin
301 if e_LoadSoundMem(SoundData, ResLength, ID, isMusic) then
302 ok := True
303 else
304 FreeMem(SoundData);
305 end
306 else
307 e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
309 WAD.Free();
311 if not ok then
312 begin
313 if isMusic then
314 e_WriteLog(Format('Error loading music %s', [Resource]), MSG_WARNING)
315 else
316 e_WriteLog(Format('Error loading sound %s', [Resource]), MSG_WARNING);
317 Exit;
318 end;
320 Result := True;
321 end;
323 function g_Sound_CreateWADEx(SoundName: ShortString; Resource: string; isMusic: Boolean = False): Boolean;
324 var
325 WAD: TWADEditor_1;
326 FileName, SectionName, ResourceName: string;
327 SoundData: Pointer;
328 ResLength: Integer;
329 find_id: DWORD;
330 ok: Boolean;
331 begin
332 Result := False;
333 ok := False;
335 // e_WriteLog('Loading sound: ' + Resource, MSG_NOTIFY);
336 g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName);
338 find_id := FindSound();
340 WAD := TWADEditor_1.Create();
341 WAD.ReadFile(FileName);
343 if WAD.GetResource(SectionName, ResourceName, SoundData, ResLength) then
344 begin
345 if e_LoadSoundMem(SoundData, ResLength, SoundArray[find_id].ID, isMusic) then
346 begin
347 SoundArray[find_id].Name := SoundName;
348 SoundArray[find_id].IsMusic := isMusic;
349 ok := True;
350 end
351 else
352 FreeMem(SoundData);
353 end
354 else
355 e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
357 WAD.Free();
359 if not ok then
360 begin
361 if isMusic then
362 e_WriteLog(Format('Error loading music %s', [Resource]), MSG_WARNING)
363 else
364 e_WriteLog(Format('Error loading sound %s', [Resource]), MSG_WARNING);
365 Exit;
366 end;
368 Result := True;
369 end;
371 procedure g_Sound_Delete(SoundName: ShortString);
372 var
373 a: DWORD;
374 begin
375 if (SoundArray = nil) or (SoundName = '') then
376 Exit;
378 for a := 0 to High(SoundArray) do
379 if SoundArray[a].Name = SoundName then
380 begin
381 e_DeleteSound(SoundArray[a].ID);
382 SoundArray[a].Name := '';
383 SoundArray[a].ID := 0;
384 SoundArray[a].IsMusic := False;
385 end;
386 end;
388 function g_Sound_Exists(SoundName: string): Boolean;
389 var
390 a: DWORD;
391 begin
392 Result := False;
394 if SoundName = '' then
395 Exit;
397 if SoundArray <> nil then
398 for a := 0 to High(SoundArray) do
399 if SoundArray[a].Name = SoundName then
400 begin
401 Result := True;
402 Break;
403 end;
404 end;
406 function g_Sound_Get(var ID: DWORD; SoundName: ShortString): Boolean;
407 var
408 a: DWORD;
409 begin
410 Result := False;
412 if SoundName = '' then
413 Exit;
415 if SoundArray <> nil then
416 for a := 0 to High(SoundArray) do
417 if SoundArray[a].Name = SoundName then
418 begin
419 ID := SoundArray[a].ID;
420 Result := True;
421 Break;
422 end;
423 end;
425 procedure g_Sound_SetupAllVolumes(SoundVol, MusicVol: Byte);
426 var
427 Svol, Mvol: Single;
428 sm: Boolean;
429 begin
430 Mvol := 0; // shut up, compiler
431 if (gSoundLevel = SoundVol) and (gMusicLevel = MusicVol) then
432 Exit;
434 if gSoundLevel > 0 then
435 begin
436 Svol := SoundVol / gSoundLevel;
437 sm := False;
438 end
439 else
440 begin
441 Svol := SoundVol / 255.0;
442 sm := True;
443 end;
445 if gMusic <> nil then
446 if gMusicLevel > 0 then
447 Mvol := gMusic.GetVolume() * MusicVol / gMusicLevel
448 else
449 Mvol := MusicVol / 255.0;
451 e_ModifyChannelsVolumes(Svol, sm);
453 if gMusic <> nil then
454 gMusic.SetVolume(Mvol);
456 gSoundLevel := SoundVol;
457 gMusicLevel := MusicVol;
458 end;
460 { TPlayableSound: }
462 constructor TPlayableSound.Create();
463 begin
464 inherited;
465 FName := '';
466 end;
468 destructor TPlayableSound.Destroy();
469 begin
470 inherited;
471 end;
473 function TPlayableSound.Play(Force: Boolean = False): Boolean;
474 begin
475 if Force or not IsPlaying() then
476 begin
477 Stop();
478 Result := RawPlay(0.0, gSoundLevel/255.0, FPosition);
479 end
480 else
481 Result := False;
482 end;
484 function TPlayableSound.PlayAt(X, Y: Integer): Boolean;
485 var
486 Pan, Vol: Single;
487 begin
488 if PlaySoundAt(X, Y, Pan, Vol) then
489 begin
490 Stop();
491 Result := RawPlay(Pan, Vol * (gSoundLevel/255.0), FPosition);
492 end
493 else
494 Result := False;
495 end;
497 function TPlayableSound.PlayPanVolume(Pan, Volume: Single; Force: Boolean = False): Boolean;
498 begin
499 if Force or not IsPlaying() then
500 begin
501 Stop();
502 Result := RawPlay(Pan, Volume * (gSoundLevel/255.0), FPosition);
503 end
504 else
505 Result := False;
506 end;
508 function TPlayableSound.PlayVolumeAt(X, Y: Integer; Volume: Single): Boolean;
509 var
510 Pan, Vol: Single;
511 begin
512 if PlaySoundAt(X, Y, Pan, Vol, Volume) then
513 begin
514 Stop();
515 Result := RawPlay(Pan, Volume * Vol * (gSoundLevel/255.0), FPosition);
516 end
517 else
518 Result := False;
519 end;
521 function TPlayableSound.SetCoords(X, Y: Integer; Volume: Single): Boolean;
522 var
523 Pan, Vol: Single;
524 begin
525 if PlaySoundAt(X, Y, Pan, Vol, Volume) then
526 begin
527 SetVolume(Volume * Vol * (gSoundLevel/255.0));
528 SetPan(Pan);
529 Result := True;
530 end
531 else
532 begin
533 SetVolume(0.0);
534 SetPan(0.0);
535 Result := False;
536 end;
537 end;
539 function TPlayableSound.SetByName(SN: String): Boolean;
540 var
541 id: DWORD;
542 begin
543 if g_Sound_Get(id, SN) then
544 begin
545 SetID(id);
546 FName := SN;
547 Result := True;
548 end
549 else
550 Result := False;
551 end;
553 { TMusic: }
555 constructor TMusic.Create();
556 begin
557 inherited;
558 FName := '';
559 FSpecPause := False;
560 FNoMusic := True;
561 end;
563 destructor TMusic.Destroy();
564 begin
565 inherited;
566 end;
568 function TMusic.Play(Force: Boolean = False): Boolean;
569 begin
570 if FNoMusic then
571 begin
572 Result := True;
573 Exit;
574 end;
576 if Force or not IsPlaying() then
577 begin
578 Stop();
579 Result := RawPlay(0.0, gMusicLevel/255.0, FPosition);
580 if Result then
581 SetPriority(0);
582 if Result and FSpecPause then
583 Pause(True);
584 end
585 else
586 Result := False;
587 end;
589 function TMusic.SetByName(SN: String): Boolean;
590 var
591 id: DWORD;
592 begin
593 if SN = '' then
594 begin
595 FNoMusic := True;
596 Result := True;
597 Exit;
598 end;
600 if g_Sound_Get(id, SN) then
601 begin
602 SetID(id);
603 FName := SN;
604 FNoMusic := False;
605 FSpecPause := False;
606 Result := True;
607 end
608 else
609 Result := False;
610 end;
612 function TMusic.IsPaused(): Boolean;
613 begin
614 Result := inherited IsPaused();
615 Result := Result or FSpecPause;
616 end;
618 procedure TMusic.Pause(Enable: Boolean);
619 begin
620 // Îòêëþ÷àåì ïàóçó, òîëüêî åñëè íå áûëî ñïåö-ïàóçû:
621 if Enable or (not FSpecPause) then
622 inherited Pause(Enable);
623 end;
625 procedure TMusic.SetSpecPause(Enable: Boolean);
626 begin
627 FSpecPause := Enable;
628 Pause(Enable);
629 end;
631 end.