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/>.
18 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
28 envvars, e_log, SysUtils;
40 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
42 FChanNum: Integer; // <0: no channel allocated
50 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
51 function GetChan (): Integer;
53 property Channel: Integer read GetChan;
57 destructor Destroy(); override;
58 procedure SetID(ID: DWORD);
59 procedure FreeSound();
60 function IsPlaying(): Boolean;
62 function IsPaused(): Boolean;
63 procedure Pause(Enable: Boolean);
64 function GetVolume(): Single;
65 procedure SetVolume(Volume: Single);
66 function GetPan(): Single;
67 procedure SetPan(Pan: Single);
68 function IsMuted(): Boolean;
69 procedure Mute(Enable: Boolean);
70 function GetPosition(): DWORD;
71 procedure SetPosition(aPos: DWORD);
72 procedure SetPriority(priority: Integer);
76 NO_SOUND_ID = DWORD(-1);
78 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
80 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
81 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
83 // returns channel number or -1
84 function e_PlaySound(ID: DWORD): Integer;
85 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
86 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
87 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
89 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
90 procedure e_MuteChannels(Enable: Boolean);
91 procedure e_StopChannels();
93 procedure e_DeleteSound(ID: DWORD);
94 procedure e_RemoveAllSounds();
95 procedure e_ReleaseSoundSystem();
96 procedure e_SoundUpdate();
99 e_SoundsArray: array of TSoundRec = nil;
108 N_MUSCHAN = N_CHANNELS+42;
112 id: DWORD; // sound id
114 oldvol: Integer; // for muted
119 SoundMuted: Boolean = False;
120 SoundInitialized: Boolean = False;
121 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
122 MusVolume: Integer = MIX_MAX_VOLUME;
125 procedure chanFinished (chan: Integer); cdecl;
127 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
128 if (chan >= 0) and (chan < N_CHANNELS) then
130 if ChanSIds[chan].id <> NO_SOUND_ID then
132 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
134 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
136 ChanSIds[chan].id := NO_SOUND_ID;
142 procedure dumpMusicType (ms: PMix_Music);
146 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
150 case Mix_GetMusicType(ms^) of
151 TMix_MusicType.MUS_NONE:
152 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
153 TMix_MusicType.MUS_CMD:
154 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
155 TMix_MusicType.MUS_WAV:
156 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
157 TMix_MusicType.MUS_MOD:
158 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
159 TMix_MusicType.MUS_MID:
160 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
161 TMix_MusicType.MUS_OGG:
162 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
163 TMix_MusicType.MUS_MP3:
164 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
165 TMix_MusicType.MUS_MP3_MAD:
166 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
167 TMix_MusicType.MUS_FLAC:
168 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
169 TMix_MusicType.MUS_MODPLUG:
170 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
172 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
177 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
185 if SoundInitialized then begin Result := true; Exit end;
188 SoundInitialized := False;
191 // HACK: shit this into env and hope for the best
192 SetEnvVar('SDL_AUDIODRIVER', 'dummy');
195 if NoOutput then begin Result := true; Exit end;
197 // wow, this is actually MIDI player!
198 // we need module player
199 flags := MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH;
201 flags := flags or MIX_INIT_MODPLUG;
203 res := Mix_Init(flags);
204 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
205 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
206 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
208 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
210 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
211 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
212 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
214 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
215 res := Mix_OpenAudio(gsSDLSampleRate, MIX_DEFAULT_FORMAT, 2, gsSDLBufferSize);
218 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
219 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
223 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
225 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
228 for i := 0 to Mix_GetNumChunkDecoders()-1 do
230 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
232 for i := 0 to Mix_GetNumMusicDecoders()-1 do
234 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
237 Mix_AllocateChannels(N_CHANNELS);
238 Mix_ChannelFinished(chanFinished);
240 for i := 0 to N_CHANNELS-1 do
242 ChanSIds[i].id := NO_SOUND_ID;
243 ChanSIds[i].muted := SoundMuted;
244 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
245 ChanSIds[i].pan := 1.0;
247 MusVolume := MIX_MAX_VOLUME;
249 SoundInitialized := True;
253 function e_isMusic (id: DWORD): Boolean;
256 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
258 Result := (e_SoundsArray[id].Music <> nil);
262 function e_isSound (id: DWORD): Boolean;
265 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
267 Result := (e_SoundsArray[id].Sound <> nil);
271 function FindESound(): DWORD;
275 if e_SoundsArray <> nil then
277 for i := 0 to High(e_SoundsArray) do
278 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
284 if e_SoundsArray = nil then
286 SetLength(e_SoundsArray, 16);
291 Result := High(e_SoundsArray) + 1;
292 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
294 for i := Result to High(e_SoundsArray) do
296 e_SoundsArray[i].Sound := nil;
297 e_SoundsArray[i].Music := nil;
298 e_SoundsArray[i].Data := nil;
299 e_SoundsArray[i].isMusic := False;
300 e_SoundsArray[i].nRefs := 0;
304 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
310 if not SoundInitialized then Exit;
312 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
313 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
318 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
323 find_id := FindESound();
325 e_SoundsArray[find_id].Data := nil;
326 e_SoundsArray[find_id].isMusic := isMusic;
327 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
328 e_SoundsArray[find_id].nRefs := 0;
332 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
333 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
334 if e_SoundsArray[find_id].Music = nil then
336 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
337 e_WriteLog(Mix_GetError(), TMsgType.Warning);
340 dumpMusicType(e_SoundsArray[find_id].Music);
344 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
345 if e_SoundsArray[find_id].Sound = nil then Exit;
353 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
362 if not SoundInitialized then Exit;
368 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
373 //FIXME: correctly skip ID3
376 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
380 pData := Pointer(pc);
382 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
386 rw := SDL_RWFromConstMem(pData, Length);
387 if rw = nil then Exit;
389 find_id := FindESound();
391 e_SoundsArray[find_id].Data := pData;
392 if isid3 then e_SoundsArray[find_id].Data := nil;
393 e_SoundsArray[find_id].isMusic := isMusic;
394 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
395 e_SoundsArray[find_id].nRefs := 0;
399 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
401 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw);
403 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
405 if e_SoundsArray[find_id].Music = nil then
407 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
408 e_WriteLog(Mix_GetError(), TMsgType.Warning);
412 dumpMusicType(e_SoundsArray[find_id].Music);
416 if e_SoundsArray[find_id].Music <> nil then
418 Mix_FreeMusic(e_SoundsArray[find_id].Music);
420 e_SoundsArray[find_id].Music := nil;
426 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
428 //SDL_FreeRW(rw); // somehow it segfaults...
429 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
431 e_SoundsArray[find_id].Data := nil;
440 function e_PlaySound (ID: DWORD): Integer;
446 if not SoundInitialized then Exit;
448 if e_isSound(ID) then
450 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
451 Inc(e_SoundsArray[ID].nRefs);
452 if e_SoundsArray[ID].Loops then loops := -1;
453 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
456 ChanSIds[res].id := ID;
457 ChanSIds[res].muted := SoundMuted;
458 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
460 if e_SoundsArray[ID].isMusic then
461 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
463 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
469 if not e_isMusic(ID) then Exit;
471 if e_SoundsArray[ID].Loops then loops := -1;
472 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
473 if res >= 0 then res := N_MUSCHAN;
474 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
481 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
486 if chan = N_MUSCHAN then
488 // no panning for music
490 else if chan >= 0 then
492 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
493 Pan := Pan+1.0; // 0..2
494 l := trunc(127.0*(2.0-Pan));
495 r := trunc(127.0*Pan);
496 Mix_SetPanning(chan, l, r);
497 ChanSIds[chan].pan := Pan;
505 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
510 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
511 vol := trunc(Volume*MIX_MAX_VOLUME);
512 if chan = N_MUSCHAN then
515 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
517 else if chan >= 0 then
519 ChanSIds[chan].oldvol := vol;
520 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
528 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
533 chan := e_PlaySound(ID);
534 e_chanSetPan(chan, Pan);
538 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
543 chan := e_PlaySound(ID);
544 e_chanSetVol(chan, Volume);
548 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
553 chan := e_PlaySound(ID);
554 e_chanSetPan(chan, Pan);
555 e_chanSetVol(chan, Volume);
559 procedure e_DeleteSound(ID: DWORD);
563 if ID > High(e_SoundsArray) then Exit;
564 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
566 for i := 0 to N_CHANNELS-1 do
568 if ChanSIds[i].id = ID then
570 ChanSIds[i].id := NO_SOUND_ID;
575 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
576 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
577 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
579 e_SoundsArray[ID].Sound := nil;
580 e_SoundsArray[ID].Music := nil;
581 e_SoundsArray[ID].Data := nil;
582 e_SoundsArray[ID].nRefs := 0;
585 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
591 for i := 0 to N_CHANNELS-1 do
593 ovol := ChanSIds[i].oldvol;
600 vol := (MIX_MAX_VOLUME+0.0)/ovol;
603 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
604 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
605 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
606 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
608 ovol := Mix_VolumeMusic(-1);
617 vol := (MIX_MAX_VOLUME+0.0)/ovol;
618 vol := vol * SoundMod;
620 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
621 MusVolume := trunc(vol*MIX_MAX_VOLUME);
622 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
626 procedure e_MuteChannels(Enable: Boolean);
630 //if Enable = SoundMuted then Exit;
631 SoundMuted := Enable;
632 for i := 0 to N_CHANNELS-1 do
634 if ChanSIds[i].muted <> SoundMuted then
636 ChanSIds[i].muted := SoundMuted;
637 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
638 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
641 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
642 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
645 procedure e_StopChannels();
651 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
652 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
655 procedure e_RemoveAllSounds();
659 if SoundInitialized then e_StopChannels();
660 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
661 SetLength(e_SoundsArray, 0);
662 e_SoundsArray := nil;
665 procedure e_ReleaseSoundSystem();
668 if SoundInitialized then
671 SoundInitialized := False;
675 procedure e_SoundUpdate();
677 //FMOD_System_Update(F_System);
683 constructor TBasicSound.Create();
692 destructor TBasicSound.Destroy();
698 function TBasicSound.GetChan (): Integer;
700 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
702 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
704 else if e_isMusic(FID) then
706 FChanNum := N_MUSCHAN;
711 procedure TBasicSound.FreeSound();
713 if FID = NO_SOUND_ID then Exit;
722 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
725 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
726 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
727 Result := (FChanNum >= 0);
728 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
732 procedure TBasicSound.SetID(ID: DWORD);
736 if ID <> NO_SOUND_ID then
738 FMusic := e_SoundsArray[ID].isMusic;
743 function TBasicSound.IsPlaying(): Boolean;
748 if e_isSound(FID) then
750 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
754 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
757 //Result := (Mix_Playing(chan) > 0)
758 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
761 else if e_isMusic(FID) then
763 Result := (Mix_PlayingMusic() > 0);
767 procedure TBasicSound.Stop();
771 if e_isSound(FID) then
777 Mix_HaltChannel(chan);
780 else if e_isMusic(FID) then
787 function TBasicSound.IsPaused(): Boolean;
792 if e_isSound(FID) then
795 if chan < 0 then Exit;
796 Result := (Mix_Paused(chan) > 0);
798 else if e_isMusic(FID) then
800 Result := (Mix_PausedMusic() > 0);
804 procedure TBasicSound.Pause(Enable: Boolean);
809 Enable := not Enable; // fuckin' double negation
810 if e_isSound(FID) then
813 if chan < 0 then Exit;
814 pl := not (Mix_Paused(chan) > 0);
817 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
820 else if e_isMusic(FID) then
822 pl := not (Mix_PausedMusic() > 0);
825 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
831 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
832 if res <> FMOD_OK then
839 function TBasicSound.GetVolume(): Single;
844 if e_isSound(FID) then
847 if chan < 0 then Exit;
848 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
850 else if e_isMusic(FID) then
852 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
856 procedure TBasicSound.SetVolume(Volume: Single);
860 if e_isSound(FID) then
863 if chan < 0 then Exit;
864 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
865 e_chanSetVol(chan, Volume);
867 else if e_isMusic(FID) then
869 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
870 e_chanSetVol(N_MUSCHAN, Volume);
874 function TBasicSound.GetPan(): Single;
879 if e_isSound(FID) then
882 if chan < 0 then Exit;
883 Result := ChanSIds[chan].pan;
887 procedure TBasicSound.SetPan(Pan: Single);
891 if e_isSound(FID) then
894 if chan < 0 then Exit;
895 e_chanSetPan(chan, Pan);
899 function TBasicSound.IsMuted(): Boolean;
904 if e_isSound(FID) then
907 if chan < 0 then Exit;
908 Result := ChanSIds[chan].muted;
910 else if e_isMusic(FID) then
912 Result := SoundMuted;
916 procedure TBasicSound.Mute(Enable: Boolean);
920 if e_isSound(FID) then
923 if chan < 0 then Exit;
924 if ChanSIds[chan].muted <> Enable then
926 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
927 ChanSIds[chan].muted := Enable;
928 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
931 else if e_isMusic(FID) then
933 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
938 function TBasicSound.GetPosition(): DWORD;
942 if FChanNum < 0 then Exit;
943 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
944 if res <> FMOD_OK then
953 procedure TBasicSound.SetPosition(aPos: DWORD);
957 if FChanNum < 0 then Exit;
958 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
959 if res <> FMOD_OK then
966 procedure TBasicSound.SetPriority(priority: Integer);
969 if (FChanNum <> nil) and (FPriority <> priority) and
970 (priority >= 0) and (priority <= 256) then
972 FPriority := priority;
973 res := FMOD_Channel_SetPriority(FChanNum, priority);
974 if res <> FMOD_OK then