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, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
35 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
37 FChanNum: Integer; // <0: no channel allocated
45 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
46 function GetChan (): Integer;
48 property Channel: Integer read GetChan;
52 destructor Destroy(); override;
53 procedure SetID(ID: DWORD);
54 procedure FreeSound();
55 function IsPlaying(): Boolean;
57 function IsPaused(): Boolean;
58 procedure Pause(Enable: Boolean);
59 function GetVolume(): Single;
60 procedure SetVolume(Volume: Single);
61 function GetPan(): Single;
62 procedure SetPan(Pan: Single);
63 function IsMuted(): Boolean;
64 procedure Mute(Enable: Boolean);
65 function GetPosition(): DWORD;
66 procedure SetPosition(aPos: DWORD);
67 procedure SetPriority(priority: Integer);
71 NO_SOUND_ID = DWORD(-1);
73 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
75 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
76 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
78 // returns channel number or -1
79 function e_PlaySound(ID: DWORD): Integer;
80 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
81 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
82 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
84 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
85 procedure e_MuteChannels(Enable: Boolean);
86 procedure e_StopChannels();
88 procedure e_DeleteSound(ID: DWORD);
89 procedure e_RemoveAllSounds();
90 procedure e_ReleaseSoundSystem();
91 procedure e_SoundUpdate();
94 e_SoundsArray: array of TSoundRec = nil;
103 N_MUSCHAN = N_CHANNELS+42;
107 id: DWORD; // sound id
109 oldvol: Integer; // for muted
114 SoundMuted: Boolean = False;
115 SoundInitialized: Boolean = False;
116 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
117 MusVolume: Integer = MIX_MAX_VOLUME;
120 procedure chanFinished (chan: Integer); cdecl;
122 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
123 if (chan >= 0) and (chan < N_CHANNELS) then
125 if ChanSIds[chan].id <> NO_SOUND_ID then
127 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
129 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
131 ChanSIds[chan].id := NO_SOUND_ID;
137 procedure dumpMusicType (ms: PMix_Music);
141 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
145 case Mix_GetMusicType(ms^) of
146 TMix_MusicType.MUS_NONE:
147 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
148 TMix_MusicType.MUS_CMD:
149 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
150 TMix_MusicType.MUS_WAV:
151 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
152 TMix_MusicType.MUS_MOD:
153 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
154 TMix_MusicType.MUS_MID:
155 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
156 TMix_MusicType.MUS_OGG:
157 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
158 TMix_MusicType.MUS_MP3:
159 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
160 TMix_MusicType.MUS_MP3_MAD:
161 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
162 TMix_MusicType.MUS_FLAC:
163 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
164 TMix_MusicType.MUS_MODPLUG:
165 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
167 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
172 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
179 if SoundInitialized then begin Result := true; Exit end;
182 SoundInitialized := False;
184 if NoOutput then begin Result := true; Exit end;
186 // wow, this is actually MIDI player!
187 // we need module player
188 res := Mix_Init(MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MODPLUG or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH);
189 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
190 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
191 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
192 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
193 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
194 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
195 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
197 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
198 res := Mix_OpenAudio(gsSDLSampleRate, AUDIO_S16LSB, 2, gsSDLBufferSize);
201 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
202 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
206 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
208 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
211 for i := 0 to Mix_GetNumChunkDecoders()-1 do
213 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
215 for i := 0 to Mix_GetNumMusicDecoders()-1 do
217 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
220 Mix_AllocateChannels(N_CHANNELS);
221 Mix_ChannelFinished(chanFinished);
223 for i := 0 to N_CHANNELS-1 do
225 ChanSIds[i].id := NO_SOUND_ID;
226 ChanSIds[i].muted := SoundMuted;
227 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
228 ChanSIds[i].pan := 1.0;
230 MusVolume := MIX_MAX_VOLUME;
232 SoundInitialized := True;
236 function e_isMusic (id: DWORD): Boolean;
239 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
241 Result := (e_SoundsArray[id].Music <> nil);
245 function e_isSound (id: DWORD): Boolean;
248 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
250 Result := (e_SoundsArray[id].Sound <> nil);
254 function FindESound(): DWORD;
258 if e_SoundsArray <> nil then
260 for i := 0 to High(e_SoundsArray) do
261 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
267 if e_SoundsArray = nil then
269 SetLength(e_SoundsArray, 16);
274 Result := High(e_SoundsArray) + 1;
275 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
277 for i := Result to High(e_SoundsArray) do
279 e_SoundsArray[i].Sound := nil;
280 e_SoundsArray[i].Music := nil;
281 e_SoundsArray[i].Data := nil;
282 e_SoundsArray[i].isMusic := False;
283 e_SoundsArray[i].nRefs := 0;
287 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
293 if not SoundInitialized then Exit;
295 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
296 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
301 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
306 find_id := FindESound();
308 e_SoundsArray[find_id].Data := nil;
309 e_SoundsArray[find_id].isMusic := isMusic;
310 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
311 e_SoundsArray[find_id].nRefs := 0;
315 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
316 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
317 if e_SoundsArray[find_id].Music = nil then
319 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
320 e_WriteLog(Mix_GetError(), TMsgType.Warning);
323 dumpMusicType(e_SoundsArray[find_id].Music);
327 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
328 if e_SoundsArray[find_id].Sound = nil then Exit;
336 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
345 if not SoundInitialized then Exit;
351 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
356 //FIXME: correctly skip ID3
359 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
363 pData := Pointer(pc);
365 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
369 rw := SDL_RWFromConstMem(pData, Length);
370 if rw = nil then Exit;
372 find_id := FindESound();
374 e_SoundsArray[find_id].Data := pData;
375 if isid3 then e_SoundsArray[find_id].Data := nil;
376 e_SoundsArray[find_id].isMusic := isMusic;
377 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
378 e_SoundsArray[find_id].nRefs := 0;
382 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
383 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
384 if e_SoundsArray[find_id].Music = nil then
386 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
387 e_WriteLog(Mix_GetError(), TMsgType.Warning);
391 dumpMusicType(e_SoundsArray[find_id].Music);
395 if e_SoundsArray[find_id].Music <> nil then
397 Mix_FreeMusic(e_SoundsArray[find_id].Music);
399 e_SoundsArray[find_id].Music := nil;
405 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
407 //SDL_FreeRW(rw); // somehow it segfaults...
408 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
410 e_SoundsArray[find_id].Data := nil;
419 function e_PlaySound (ID: DWORD): Integer;
425 if not SoundInitialized then Exit;
427 if e_isSound(ID) then
429 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
430 Inc(e_SoundsArray[ID].nRefs);
431 if e_SoundsArray[ID].Loops then loops := -1;
432 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
435 ChanSIds[res].id := ID;
436 ChanSIds[res].muted := SoundMuted;
437 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
439 if e_SoundsArray[ID].isMusic then
440 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
442 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
448 if not e_isMusic(ID) then Exit;
450 if e_SoundsArray[ID].Loops then loops := -1;
451 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
452 if res >= 0 then res := N_MUSCHAN;
453 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
460 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
465 if chan = N_MUSCHAN then
467 // no panning for music
469 else if chan >= 0 then
471 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
472 Pan := Pan+1.0; // 0..2
473 l := trunc(127.0*(2.0-Pan));
474 r := trunc(127.0*Pan);
475 Mix_SetPanning(chan, l, r);
476 ChanSIds[chan].pan := Pan;
484 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
489 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
490 vol := trunc(Volume*MIX_MAX_VOLUME);
491 if chan = N_MUSCHAN then
494 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
496 else if chan >= 0 then
498 ChanSIds[chan].oldvol := vol;
499 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
507 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
512 chan := e_PlaySound(ID);
513 e_chanSetPan(chan, Pan);
517 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
522 chan := e_PlaySound(ID);
523 e_chanSetVol(chan, Volume);
527 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
532 chan := e_PlaySound(ID);
533 e_chanSetPan(chan, Pan);
534 e_chanSetVol(chan, Volume);
538 procedure e_DeleteSound(ID: DWORD);
542 if ID > High(e_SoundsArray) then Exit;
543 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
545 for i := 0 to N_CHANNELS-1 do
547 if ChanSIds[i].id = ID then
549 ChanSIds[i].id := NO_SOUND_ID;
554 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
555 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
556 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
558 e_SoundsArray[ID].Sound := nil;
559 e_SoundsArray[ID].Music := nil;
560 e_SoundsArray[ID].Data := nil;
561 e_SoundsArray[ID].nRefs := 0;
564 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
570 for i := 0 to N_CHANNELS-1 do
572 ovol := ChanSIds[i].oldvol;
579 vol := (MIX_MAX_VOLUME+0.0)/ovol;
582 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
583 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
584 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
585 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
587 ovol := Mix_VolumeMusic(-1);
596 vol := (MIX_MAX_VOLUME+0.0)/ovol;
597 vol := vol * SoundMod;
599 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
600 MusVolume := trunc(vol*MIX_MAX_VOLUME);
601 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
605 procedure e_MuteChannels(Enable: Boolean);
609 //if Enable = SoundMuted then Exit;
610 SoundMuted := Enable;
611 for i := 0 to N_CHANNELS-1 do
613 if ChanSIds[i].muted <> SoundMuted then
615 ChanSIds[i].muted := SoundMuted;
616 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
617 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
620 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
621 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
624 procedure e_StopChannels();
630 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
631 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
634 procedure e_RemoveAllSounds();
638 if SoundInitialized then e_StopChannels();
639 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
640 SetLength(e_SoundsArray, 0);
641 e_SoundsArray := nil;
644 procedure e_ReleaseSoundSystem();
647 if SoundInitialized then
650 SoundInitialized := False;
654 procedure e_SoundUpdate();
656 //FMOD_System_Update(F_System);
662 constructor TBasicSound.Create();
671 destructor TBasicSound.Destroy();
677 function TBasicSound.GetChan (): Integer;
679 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
681 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
683 else if e_isMusic(FID) then
685 FChanNum := N_MUSCHAN;
690 procedure TBasicSound.FreeSound();
692 if FID = NO_SOUND_ID then Exit;
701 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
704 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
705 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
706 Result := (FChanNum >= 0);
707 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
711 procedure TBasicSound.SetID(ID: DWORD);
715 if ID <> NO_SOUND_ID then
717 FMusic := e_SoundsArray[ID].isMusic;
722 function TBasicSound.IsPlaying(): Boolean;
727 if e_isSound(FID) then
729 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
733 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
736 //Result := (Mix_Playing(chan) > 0)
737 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
740 else if e_isMusic(FID) then
742 Result := (Mix_PlayingMusic() > 0);
746 procedure TBasicSound.Stop();
750 if e_isSound(FID) then
756 Mix_HaltChannel(chan);
759 else if e_isMusic(FID) then
766 function TBasicSound.IsPaused(): Boolean;
771 if e_isSound(FID) then
774 if chan < 0 then Exit;
775 Result := (Mix_Paused(chan) > 0);
777 else if e_isMusic(FID) then
779 Result := (Mix_PausedMusic() > 0);
783 procedure TBasicSound.Pause(Enable: Boolean);
788 Enable := not Enable; // fuckin' double negation
789 if e_isSound(FID) then
792 if chan < 0 then Exit;
793 pl := not (Mix_Paused(chan) > 0);
796 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
799 else if e_isMusic(FID) then
801 pl := not (Mix_PausedMusic() > 0);
804 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
810 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
811 if res <> FMOD_OK then
818 function TBasicSound.GetVolume(): Single;
823 if e_isSound(FID) then
826 if chan < 0 then Exit;
827 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
829 else if e_isMusic(FID) then
831 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
835 procedure TBasicSound.SetVolume(Volume: Single);
839 if e_isSound(FID) then
842 if chan < 0 then Exit;
843 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
844 e_chanSetVol(chan, Volume);
846 else if e_isMusic(FID) then
848 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
849 e_chanSetVol(N_MUSCHAN, Volume);
853 function TBasicSound.GetPan(): Single;
858 if e_isSound(FID) then
861 if chan < 0 then Exit;
862 Result := ChanSIds[chan].pan;
866 procedure TBasicSound.SetPan(Pan: Single);
870 if e_isSound(FID) then
873 if chan < 0 then Exit;
874 e_chanSetPan(chan, Pan);
878 function TBasicSound.IsMuted(): Boolean;
883 if e_isSound(FID) then
886 if chan < 0 then Exit;
887 Result := ChanSIds[chan].muted;
889 else if e_isMusic(FID) then
891 Result := SoundMuted;
895 procedure TBasicSound.Mute(Enable: Boolean);
899 if e_isSound(FID) then
902 if chan < 0 then Exit;
903 if ChanSIds[chan].muted <> Enable then
905 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
906 ChanSIds[chan].muted := Enable;
907 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
910 else if e_isMusic(FID) then
912 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
917 function TBasicSound.GetPosition(): DWORD;
921 if FChanNum < 0 then Exit;
922 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
923 if res <> FMOD_OK then
932 procedure TBasicSound.SetPosition(aPos: DWORD);
936 if FChanNum < 0 then Exit;
937 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
938 if res <> FMOD_OK then
945 procedure TBasicSound.SetPriority(priority: Integer);
948 if (FChanNum <> nil) and (FPriority <> priority) and
949 (priority >= 0) and (priority <= 256) then
951 FPriority := priority;
952 res := FMOD_Channel_SetPriority(FChanNum, priority);
953 if res <> FMOD_OK then