DEADSOFTWARE

sdl sound: now it seems to work (much better, lol)
[d2df-sdl.git] / src / engine / e_sound.pas
1 unit e_sound;
3 interface
5 uses
6 sdl2,
7 SDL2_mixer,
8 e_log,
9 SysUtils;
11 type
12 TSoundRec = record
13 Data: Pointer;
14 Sound: PMix_Chunk;
15 Music: PMix_Music;
16 isMusic: Boolean;
17 nRefs: Integer;
18 end;
20 TBasicSound = class (TObject)
21 private
22 FChanNum: Integer; // <0: no channel allocated
24 protected
25 FID: DWORD;
26 FMusic: Boolean;
27 FPosition: DWORD;
28 FPriority: Integer;
30 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
31 function GetChan (): Integer;
33 property Channel: Integer read GetChan;
35 public
36 constructor Create();
37 destructor Destroy(); override;
38 procedure SetID(ID: DWORD);
39 procedure FreeSound();
40 function IsPlaying(): Boolean;
41 procedure Stop();
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);
53 end;
55 const
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();
78 var
79 e_SoundsArray: array of TSoundRec = nil;
81 implementation
83 uses
84 g_window, g_options, BinEditor;
86 const
87 N_CHANNELS = 512;
88 N_MUSCHAN = N_CHANNELS+42;
90 type
91 TChanInfo = record
92 id: DWORD; // sound id
93 muted: Boolean;
94 oldvol: Integer; // for muted
95 pan: Single;
96 end;
98 var
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;
106 begin
107 if (chan >= 0) and (chan < N_CHANNELS) then
108 begin
109 if ChanSIds[chan].id <> NO_SOUND_ID then
110 begin
111 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
112 ChanSIds[chan].id := NO_SOUND_ID;
113 end;
114 end;
115 end;
118 function e_InitSoundSystem(): Boolean;
119 var
120 res, i: Integer;
121 begin
122 if SoundInitialized then begin Result := true; Exit end;
124 Result := False;
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
130 begin
131 e_WriteLog('Error initializing SDL module player:', MSG_FATALERROR);
132 e_WriteLog(Mix_GetError(), MSG_FATALERROR);
133 //Exit;
134 end;
137 res := Mix_OpenAudio(44100, AUDIO_S16LSB, 2, 512);
138 if res = -1 then
139 begin
140 e_WriteLog('Error initializing SDL mixer:', MSG_FATALERROR);
141 e_WriteLog(Mix_GetError(), MSG_FATALERROR);
142 Exit;
143 end;
145 Mix_AllocateChannels(N_CHANNELS);
146 Mix_ChannelFinished(chanFinished);
148 for i := 0 to N_CHANNELS-1 do
149 begin
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;
154 end;
155 MusVolume := MIX_MAX_VOLUME;
157 SoundInitialized := True;
158 Result := True;
159 end;
161 function e_isMusic (id: DWORD): Boolean;
162 begin
163 Result := False;
164 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
165 begin
166 Result := (e_SoundsArray[id].Music <> nil);
167 end;
168 end;
170 function e_isSound (id: DWORD): Boolean;
171 begin
172 Result := False;
173 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
174 begin
175 Result := (e_SoundsArray[id].Sound <> nil);
176 end;
177 end;
179 function FindESound(): DWORD;
180 var
181 i: Integer;
182 begin
183 if e_SoundsArray <> nil then
184 begin
185 for i := 0 to High(e_SoundsArray) do
186 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
187 begin
188 Result := i;
189 Exit;
190 end;
191 end;
192 if e_SoundsArray = nil then
193 begin
194 SetLength(e_SoundsArray, 16);
195 Result := 0;
196 end
197 else
198 begin
199 Result := High(e_SoundsArray) + 1;
200 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
201 end;
202 end;
204 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean): Boolean;
205 var
206 find_id: DWORD;
207 begin
208 Result := False;
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;
220 if isMusic then
221 begin
222 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
223 if e_SoundsArray[find_id].Music = nil then Exit;
224 end
225 else
226 begin
227 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
228 if e_SoundsArray[find_id].Sound = nil then Exit;
229 end;
231 ID := find_id;
233 Result := True;
234 end;
236 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
237 var
238 find_id: DWORD;
239 rw: PSDL_RWops;
240 begin
241 Result := False;
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;
253 if isMusic then
254 begin
255 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
256 end
257 else
258 begin
259 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
260 end;
261 SDL_FreeRW(rw);
262 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then Exit;
264 ID := find_id;
266 Result := True;
267 end;
269 function e_PlaySound (ID: DWORD): Integer;
270 var
271 res: Integer = -1;
272 begin
273 Result := -1;
274 if not SoundInitialized then Exit;
276 if e_isSound(ID) then
277 begin
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);
281 if res >= 0 then
282 begin
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)
289 else
290 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
292 end;
293 end
294 else
295 begin
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);
300 Result := res;
301 end;
303 Result := res;
304 end;
306 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
307 var
308 l, r: UInt8;
309 begin
310 Result := True;
311 if chan = N_MUSCHAN then
312 begin
313 // no panning for music
314 end
315 else if chan >= 0 then
316 begin
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;
323 end
324 else
325 begin
326 Result := False;
327 end;
328 end;
330 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
331 var
332 vol: Integer;
333 begin
334 Result := True;
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
338 begin
339 MusVolume := vol;
340 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
341 end
342 else if chan >= 0 then
343 begin
344 ChanSIds[chan].oldvol := vol;
345 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
346 end
347 else
348 begin
349 Result := False;
350 end;
351 end;
353 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
354 var
355 chan: Integer;
356 begin
357 Result := -1;
358 chan := e_PlaySound(ID);
359 e_chanSetPan(chan, Pan);
360 Result := chan;
361 end;
363 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
364 var
365 chan: Integer;
366 begin
367 Result := -1;
368 chan := e_PlaySound(ID);
369 e_chanSetVol(chan, Volume);
370 Result := chan;
371 end;
373 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
374 var
375 chan: Integer;
376 begin
377 Result := -1;
378 chan := e_PlaySound(ID);
379 e_chanSetPan(chan, Pan);
380 e_chanSetVol(chan, Volume);
381 Result := chan;
382 end;
384 procedure e_DeleteSound(ID: DWORD);
385 var
386 i: Integer;
387 begin
388 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
390 for i := 0 to N_CHANNELS-1 do
391 begin
392 if ChanSIds[i].id = ID then
393 begin
394 ChanSIds[i].id := NO_SOUND_ID;
395 Mix_HaltChannel(i);
396 end;
397 end;
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;
407 end;
409 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
410 var
411 i: Integer;
412 vol: Single;
413 ovol: Integer;
414 begin
415 for i := 0 to N_CHANNELS-1 do
416 begin
417 ovol := ChanSIds[i].oldvol;
418 if setMode then
419 begin
420 vol := SoundMod;
421 end
422 else
423 begin
424 vol := (MIX_MAX_VOLUME+0.0)/ovol;
425 vol := vol*SoundMod;
426 end;
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);
431 end;
432 ovol := Mix_VolumeMusic(-1);
433 if ovol >= 0 then
434 begin
435 if setMode then
436 begin
437 vol := SoundMod;
438 end
439 else
440 begin
441 vol := (MIX_MAX_VOLUME+0.0)/ovol;
442 vol := vol * SoundMod;
443 end;
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);
447 end;
448 end;
450 procedure e_MuteChannels(Enable: Boolean);
451 var
452 i: Integer;
453 begin
454 //if Enable = SoundMuted then Exit;
455 SoundMuted := Enable;
456 for i := 0 to N_CHANNELS-1 do
457 begin
458 if ChanSIds[i].muted <> SoundMuted then
459 begin
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);
463 end;
464 end;
465 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
466 end;
468 procedure e_StopChannels();
469 var
470 i: Integer;
471 begin
472 Mix_HaltChannel(-1);
473 Mix_HaltMusic();
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;
476 end;
478 procedure e_RemoveAllSounds();
479 var
480 i: Integer;
481 begin
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;
486 end;
488 procedure e_ReleaseSoundSystem();
489 begin
490 e_RemoveAllSounds();
491 if SoundInitialized then
492 begin
493 Mix_CloseAudio();
494 SoundInitialized := False;
495 end;
496 end;
498 procedure e_SoundUpdate();
499 begin
500 //FMOD_System_Update(F_System);
501 end;
504 { TBasicSound: }
506 constructor TBasicSound.Create();
507 begin
508 FID := NO_SOUND_ID;
509 FMusic := False;
510 FChanNum := -1;
511 FPosition := 0;
512 FPriority := 128;
513 end;
515 destructor TBasicSound.Destroy();
516 begin
517 FreeSound();
518 inherited;
519 end;
521 function TBasicSound.GetChan (): Integer;
522 begin
523 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
524 begin
525 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
526 end
527 else if e_isMusic(FID) then
528 begin
529 FChanNum := N_MUSCHAN;
530 end;
531 Result := FChanNum;
532 end;
534 procedure TBasicSound.FreeSound();
535 begin
536 if FID = NO_SOUND_ID then Exit;
537 Stop();
538 FID := NO_SOUND_ID;
539 FMusic := False;
540 FPosition := 0;
541 FChanNum := -1;
542 end;
544 // aPos: msecs
545 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
546 begin
547 Result := False;
548 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
549 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
550 Result := (FChanNum >= 0);
551 //TODO: aPos
552 end;
554 procedure TBasicSound.SetID(ID: DWORD);
555 begin
556 FreeSound();
557 FID := ID;
558 FMusic := e_SoundsArray[ID].isMusic;
559 FChanNum := -1;
560 end;
562 function TBasicSound.IsPlaying(): Boolean;
563 var
564 chan: Integer;
565 begin
566 Result := False;
567 if e_isSound(FID) then
568 begin
569 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), MSG_WARNING);
570 chan := Channel;
571 if chan < 0 then
572 begin
573 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), MSG_WARNING);
574 Exit;
575 end;
576 //Result := (Mix_Playing(chan) > 0)
577 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), MSG_WARNING);
578 Result := True;
579 end
580 else if e_isMusic(FID) then
581 begin
582 Result := (Mix_PlayingMusic() > 0);
583 end;
584 end;
586 procedure TBasicSound.Stop();
587 var
588 chan: Integer;
589 begin
590 if e_isSound(FID) then
591 begin
592 chan := Channel;
593 if chan >= 0 then
594 begin
595 //GetPosition();
596 Mix_HaltChannel(chan);
597 end;
598 end
599 else if e_isMusic(FID) then
600 begin
601 Mix_HaltMusic();
602 end;
603 FChanNum := -1;
604 end;
606 function TBasicSound.IsPaused(): Boolean;
607 var
608 chan: Integer;
609 begin
610 Result := False;
611 if e_isSound(FID) then
612 begin
613 chan := Channel;
614 if chan < 0 then Exit;
615 Result := (Mix_Paused(chan) > 0);
616 end
617 else if e_isMusic(FID) then
618 begin
619 Result := (Mix_PausedMusic() > 0);
620 end;
621 end;
623 procedure TBasicSound.Pause(Enable: Boolean);
624 var
625 chan: Integer;
626 pl: Boolean;
627 begin
628 if e_isSound(FID) then
629 begin
630 chan := Channel;
631 if chan < 0 then Exit;
632 pl := not (Mix_Paused(chan) > 0);
633 if pl <> Enable then
634 begin
635 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
636 end;
637 end
638 else if e_isMusic(FID) then
639 begin
640 pl := not (Mix_PausedMusic() > 0);
641 if pl <> Enable then
642 begin
643 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
644 end;
645 end;
647 if Enable then
648 begin
649 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
650 if res <> FMOD_OK then
651 begin
652 end;
653 end;
655 end;
657 function TBasicSound.GetVolume(): Single;
658 var
659 chan: Integer;
660 begin
661 Result := 0.0;
662 if e_isSound(FID) then
663 begin
664 chan := Channel;
665 if chan < 0 then Exit;
666 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
667 end
668 else if e_isMusic(FID) then
669 begin
670 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
671 end;
672 end;
674 procedure TBasicSound.SetVolume(Volume: Single);
675 var
676 chan: Integer;
677 begin
678 if e_isSound(FID) then
679 begin
680 chan := Channel;
681 if chan < 0 then Exit;
682 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), MSG_WARNING);
683 e_chanSetVol(chan, Volume);
684 end
685 else if e_isMusic(FID) then
686 begin
687 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), MSG_WARNING);
688 e_chanSetVol(N_MUSCHAN, Volume);
689 end;
690 end;
692 function TBasicSound.GetPan(): Single;
693 var
694 chan: Integer;
695 begin
696 Result := 1.0;
697 if e_isSound(FID) then
698 begin
699 chan := Channel;
700 if chan < 0 then Exit;
701 Result := ChanSIds[chan].pan;
702 end;
703 end;
705 procedure TBasicSound.SetPan(Pan: Single);
706 var
707 chan: Integer;
708 begin
709 if e_isSound(FID) then
710 begin
711 chan := Channel;
712 if chan < 0 then Exit;
713 e_chanSetPan(chan, Pan);
714 end;
715 end;
717 function TBasicSound.IsMuted(): Boolean;
718 var
719 chan: Integer;
720 begin
721 Result := False;
722 if e_isSound(FID) then
723 begin
724 chan := Channel;
725 if chan < 0 then Exit;
726 Result := ChanSIds[chan].muted;
727 end
728 else if e_isMusic(FID) then
729 begin
730 Result := SoundMuted;
731 end;
732 end;
734 procedure TBasicSound.Mute(Enable: Boolean);
735 var
736 chan: Integer;
737 begin
738 if e_isSound(FID) then
739 begin
740 chan := Channel;
741 if chan < 0 then Exit;
742 if ChanSIds[chan].muted <> Enable then
743 begin
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);
747 end;
748 end
749 else if e_isMusic(FID) then
750 begin
751 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
752 end;
753 end;
755 //TODO
756 function TBasicSound.GetPosition(): DWORD;
757 begin
758 Result := 0;
760 if FChanNum < 0 then Exit;
761 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
762 if res <> FMOD_OK then
763 begin
764 Exit;
765 end;
766 Result := FPosition;
768 end;
770 //TODO
771 procedure TBasicSound.SetPosition(aPos: DWORD);
772 begin
773 FPosition := aPos;
775 if FChanNum < 0 then Exit;
776 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
777 if res <> FMOD_OK then
778 begin
779 end;
781 end;
783 //TODO
784 procedure TBasicSound.SetPriority(priority: Integer);
785 begin
787 if (FChanNum <> nil) and (FPriority <> priority) and
788 (priority >= 0) and (priority <= 256) then
789 begin
790 FPriority := priority;
791 res := FMOD_Channel_SetPriority(FChanNum, priority);
792 if res <> FMOD_OK then
793 begin
794 end;
795 end;
797 end;
799 end.