Main: Fix directives to hide compiler notices
[d2df-sdl.git] / src / engine / e_sound_sdl.inc
blobade82620b1dd0bec1c121a52b7e43ed28b787428
1 (* Copyright (C)  Doom 2D: Forever Developers
2  *
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.
7  *
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.
12  *
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/>.
15  *)
16 interface
18 uses
19   sdl2,
20   SDL2_mixer,
21   {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
22   e_log,
23   SysUtils;
25 type
26   TSoundRec = record
27     Data: Pointer;
28     Sound: PMix_Chunk;
29     Music: PMix_Music;
30     isMusic: Boolean;
31     nRefs: Integer;
32   end;
34   TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
35   private
36     FChanNum: Integer; // <0: no channel allocated
38   protected
39     FID: DWORD;
40     FMusic: Boolean;
41     FPosition: DWORD;
42     FPriority: Integer;
44     function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
45     function GetChan (): Integer;
47     property Channel: Integer read GetChan;
49   public
50     constructor Create();
51     destructor Destroy(); override;
52     procedure SetID(ID: DWORD);
53     procedure FreeSound();
54     function IsPlaying(): Boolean;
55     procedure Stop();
56     function IsPaused(): Boolean;
57     procedure Pause(Enable: Boolean);
58     function GetVolume(): Single;
59     procedure SetVolume(Volume: Single);
60     function GetPan(): Single;
61     procedure SetPan(Pan: Single);
62     function IsMuted(): Boolean;
63     procedure Mute(Enable: Boolean);
64     function GetPosition(): DWORD;
65     procedure SetPosition(aPos: DWORD);
66     procedure SetPriority(priority: Integer);
67   end;
69 const
70   NO_SOUND_ID = DWORD(-1);
72 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
74 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean): Boolean;
75 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
77 // returns channel number or -1
78 function e_PlaySound(ID: DWORD): Integer;
79 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
80 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
81 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
83 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
84 procedure e_MuteChannels(Enable: Boolean);
85 procedure e_StopChannels();
87 procedure e_DeleteSound(ID: DWORD);
88 procedure e_RemoveAllSounds();
89 procedure e_ReleaseSoundSystem();
90 procedure e_SoundUpdate();
92 var
93   e_SoundsArray: array of TSoundRec = nil;
95 implementation
97 uses
98   g_window, g_options;
100 const
101   N_CHANNELS = 512;
102   N_MUSCHAN = N_CHANNELS+42;
104 type
105   TChanInfo = record
106     id: DWORD; // sound id
107     muted: Boolean;
108     oldvol: Integer; // for muted
109     pan: Single;
110   end;
113   SoundMuted: Boolean = False;
114   SoundInitialized: Boolean = False;
115   ChanSIds: array[0..N_CHANNELS] of TChanInfo;
116   MusVolume: Integer = MIX_MAX_VOLUME;
119 procedure chanFinished (chan: Integer); cdecl;
120 begin
121   //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
122   if (chan >= 0) and (chan < N_CHANNELS) then
123   begin
124     if ChanSIds[chan].id <> NO_SOUND_ID then
125     begin
126       if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
127       begin
128         Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
129       end;
130       ChanSIds[chan].id := NO_SOUND_ID;
131     end;
132   end;
133 end;
136 procedure dumpMusicType (ms: PMix_Music);
137 begin
138   if ms = nil then
139   begin
140     e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
141   end
142   else
143   begin
144     case Mix_GetMusicType(ms^) of
145       TMix_MusicType.MUS_NONE:
146         e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
147       TMix_MusicType.MUS_CMD:
148         e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
149       TMix_MusicType.MUS_WAV:
150         e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
151       TMix_MusicType.MUS_MOD:
152         e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
153       TMix_MusicType.MUS_MID:
154         e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
155       TMix_MusicType.MUS_OGG:
156         e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
157       TMix_MusicType.MUS_MP3:
158         e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
159       TMix_MusicType.MUS_MP3_MAD:
160         e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
161       TMix_MusicType.MUS_FLAC:
162         e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
163       TMix_MusicType.MUS_MODPLUG:
164         e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
165       otherwise
166         e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
167     end;
168   end;
169 end;
171 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
173   res, i: Integer;
174   rfreq: Integer;
175   rformat: UInt16;
176   rchans: Integer;
177 begin
178   if SoundInitialized then begin Result := true; Exit end;
180   Result := False;
181   SoundInitialized := False;
183   if NoOutput then begin Result := true; Exit end;
185   // wow, this is actually MIDI player!
186   // we need module player
187   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);
188   e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
189   if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
190   if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
191   if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
192   if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
193   if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
194   if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
196   e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
197   res := Mix_OpenAudio(gsSDLSampleRate, AUDIO_S16LSB, 2, gsSDLBufferSize);
198   if res = -1 then
199   begin
200     e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
201     e_WriteLog(Mix_GetError(), TMsgType.Fatal);
202     Exit;
203   end;
205   if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
206   begin
207     e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
208   end;
210   for i := 0 to Mix_GetNumChunkDecoders()-1 do
211   begin
212     e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
213   end;
214   for i := 0 to Mix_GetNumMusicDecoders()-1 do
215   begin
216     e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
217   end;
219   Mix_AllocateChannels(N_CHANNELS);
220   Mix_ChannelFinished(chanFinished);
222   for i := 0 to N_CHANNELS-1 do
223   begin
224     ChanSIds[i].id := NO_SOUND_ID;
225     ChanSIds[i].muted := SoundMuted;
226     ChanSIds[i].oldvol := MIX_MAX_VOLUME;
227     ChanSIds[i].pan := 1.0;
228   end;
229   MusVolume := MIX_MAX_VOLUME;
231   SoundInitialized := True;
232   Result := True;
233 end;
235 function e_isMusic (id: DWORD): Boolean;
236 begin
237   Result := False;
238   if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
239   begin
240     Result := (e_SoundsArray[id].Music <> nil);
241   end;
242 end;
244 function e_isSound (id: DWORD): Boolean;
245 begin
246   Result := False;
247   if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
248   begin
249     Result := (e_SoundsArray[id].Sound <> nil);
250   end;
251 end;
253 function FindESound(): DWORD;
255   i: Integer;
256 begin
257   if e_SoundsArray <> nil then
258   begin
259     for i := 0 to High(e_SoundsArray) do
260       if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
261       begin
262         Result := i;
263         Exit;
264       end;
265   end;
266   if e_SoundsArray = nil then
267   begin
268     SetLength(e_SoundsArray, 16);
269     Result := 0;
270   end
271   else
272   begin
273     Result := High(e_SoundsArray) + 1;
274     SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
275   end;
276   for i := Result to High(e_SoundsArray) do
277   begin
278     e_SoundsArray[i].Sound := nil;
279     e_SoundsArray[i].Music := nil;
280     e_SoundsArray[i].Data := nil;
281     e_SoundsArray[i].isMusic := False;
282     e_SoundsArray[i].nRefs := 0;
283   end;
284 end;
286 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean): Boolean;
288   find_id: DWORD;
289 begin
290   ID := NO_SOUND_ID;
291   Result := False;
292   if not SoundInitialized then Exit;
294   if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
295   else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
297   {
298   if isMusic then
299   begin
300     e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
301     Exit;
302   end;
303   }
305   find_id := FindESound();
307   e_SoundsArray[find_id].Data := nil;
308   e_SoundsArray[find_id].isMusic := isMusic;
309   e_SoundsArray[find_id].nRefs := 0;
311   if isMusic then
312   begin
313     e_WriteLog(Format('  MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
314     e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
315     if e_SoundsArray[find_id].Music = nil then
316     begin
317       e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
318       e_WriteLog(Mix_GetError(), TMsgType.Warning);
319       Exit;
320     end;
321     dumpMusicType(e_SoundsArray[find_id].Music);
322   end
323   else
324   begin
325     e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
326     if e_SoundsArray[find_id].Sound = nil then Exit;
327   end;
329   ID := find_id;
331   Result := True;
332 end;
334 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
336   find_id: DWORD;
337   rw: PSDL_RWops;
338   //pc: PChar;
339   isid3: Boolean;
340 begin
341   ID := NO_SOUND_ID;
342   Result := False;
343   if not SoundInitialized then Exit;
344   isid3 := False;
346   {
347   if isMusic then
348   begin
349     e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
350     Exit;
351   end;
352   }
354   //FIXME: correctly skip ID3
355   {
356   pc := PChar(pData);
357   if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
358   begin
359     isid3 := True;
360     Inc(pc, $400);
361     pData := Pointer(pc);
362     Dec(Length, $400);
363     e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
364   end;
365   }
367   rw := SDL_RWFromConstMem(pData, Length);
368   if rw = nil then Exit;
370   find_id := FindESound();
372   e_SoundsArray[find_id].Data := pData;
373   if isid3 then e_SoundsArray[find_id].Data := nil;
374   e_SoundsArray[find_id].isMusic := isMusic;
375   e_SoundsArray[find_id].nRefs := 0;
377   if isMusic then
378   begin
379     e_WriteLog(Format('  MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
380     e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
381     if e_SoundsArray[find_id].Music = nil then
382     begin
383       e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
384       e_WriteLog(Mix_GetError(), TMsgType.Warning);
385     end
386     else
387     begin
388       dumpMusicType(e_SoundsArray[find_id].Music);
389     end;
390     //SDL_FreeRW(rw);
391     {
392     if e_SoundsArray[find_id].Music <> nil then
393     begin
394       Mix_FreeMusic(e_SoundsArray[find_id].Music);
395     end;
396     e_SoundsArray[find_id].Music := nil;
397     Exit;
398     }
399   end
400   else
401   begin
402     e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
403   end;
404   //SDL_FreeRW(rw); // somehow it segfaults...
405   if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
406   begin
407     e_SoundsArray[find_id].Data := nil;
408     Exit;
409   end;
411   ID := find_id;
413   Result := True;
414 end;
416 function e_PlaySound (ID: DWORD): Integer;
418   res: Integer = -1;
419 begin
420   Result := -1;
421   if not SoundInitialized then Exit;
423   if e_isSound(ID) then
424   begin
425     if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
426     Inc(e_SoundsArray[ID].nRefs);
427     res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
428     if res >= 0 then
429     begin
430       ChanSIds[res].id := ID;
431       ChanSIds[res].muted := SoundMuted;
432       if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
433       {
434       if e_SoundsArray[ID].isMusic then
435         res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
436       else
437         res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
438       }
439     end;
440   end
441   else
442   begin
443     if not e_isMusic(ID) then Exit;
444     Mix_HaltMusic();
445     res := Mix_PlayMusic(e_SoundsArray[ID].Music, -1);
446     if res >= 0 then res := N_MUSCHAN;
447     if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
448     Result := res;
449   end;
451   Result := res;
452 end;
454 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
456   l, r: UInt8;
457 begin
458   Result := True;
459   if chan = N_MUSCHAN then
460   begin
461     // no panning for music
462   end
463   else if chan >= 0 then
464   begin
465     if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
466     Pan := Pan+1.0; // 0..2
467     l := trunc(127.0*(2.0-Pan));
468     r := trunc(127.0*Pan);
469     Mix_SetPanning(chan, l, r);
470     ChanSIds[chan].pan := Pan;
471   end
472   else
473   begin
474     Result := False;
475   end;
476 end;
478 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
480   vol: Integer;
481 begin
482   Result := True;
483   if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
484   vol := trunc(Volume*MIX_MAX_VOLUME);
485   if chan = N_MUSCHAN then
486   begin
487     MusVolume := vol;
488     if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
489   end
490   else if chan >= 0 then
491   begin
492     ChanSIds[chan].oldvol := vol;
493     if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
494   end
495   else
496   begin
497     Result := False;
498   end;
499 end;
501 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
503   chan: Integer;
504 begin
505   Result := -1;
506   chan := e_PlaySound(ID);
507   e_chanSetPan(chan, Pan);
508   Result := chan;
509 end;
511 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
513   chan: Integer;
514 begin
515   Result := -1;
516   chan := e_PlaySound(ID);
517   e_chanSetVol(chan, Volume);
518   Result := chan;
519 end;
521 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
523   chan: Integer;
524 begin
525   Result := -1;
526   chan := e_PlaySound(ID);
527   e_chanSetPan(chan, Pan);
528   e_chanSetVol(chan, Volume);
529   Result := chan;
530 end;
532 procedure e_DeleteSound(ID: DWORD);
534   i: Integer;
535 begin
536   if ID > High(e_SoundsArray) then Exit;
537   if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
539   for i := 0 to N_CHANNELS-1 do
540   begin
541     if ChanSIds[i].id = ID then
542     begin
543       ChanSIds[i].id := NO_SOUND_ID;
544       Mix_HaltChannel(i);
545     end;
546   end;
548   if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
549   if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
550   if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
552   e_SoundsArray[ID].Sound := nil;
553   e_SoundsArray[ID].Music := nil;
554   e_SoundsArray[ID].Data := nil;
555   e_SoundsArray[ID].nRefs := 0;
556 end;
558 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
560   i: Integer;
561   vol: Single;
562   ovol: Integer;
563 begin
564   for i := 0 to N_CHANNELS-1 do
565   begin
566     ovol := ChanSIds[i].oldvol;
567     if setMode then
568     begin
569       vol := SoundMod;
570     end
571     else
572     begin
573       vol := (MIX_MAX_VOLUME+0.0)/ovol;
574       vol := vol*SoundMod;
575     end;
576     if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
577     ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
578     //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
579     if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
580   end;
581   ovol := Mix_VolumeMusic(-1);
582   if ovol >= 0 then
583   begin
584     if setMode then
585     begin
586       vol := SoundMod;
587     end
588     else
589     begin
590       vol := (MIX_MAX_VOLUME+0.0)/ovol;
591       vol := vol * SoundMod;
592     end;
593     if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
594     MusVolume := trunc(vol*MIX_MAX_VOLUME);
595     if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
596   end;
597 end;
599 procedure e_MuteChannels(Enable: Boolean);
601   i: Integer;
602 begin
603   //if Enable = SoundMuted then Exit;
604   SoundMuted := Enable;
605   for i := 0 to N_CHANNELS-1 do
606   begin
607     if ChanSIds[i].muted <> SoundMuted then
608     begin
609       ChanSIds[i].muted := SoundMuted;
610       //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
611       if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
612     end;
613   end;
614   //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
615   if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
616 end;
618 procedure e_StopChannels();
620   i: Integer;
621 begin
622   Mix_HaltChannel(-1);
623   Mix_HaltMusic();
624   for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
625   for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
626 end;
628 procedure e_RemoveAllSounds();
630   i: Integer;
631 begin
632   if SoundInitialized then e_StopChannels();
633   for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
634   SetLength(e_SoundsArray, 0);
635   e_SoundsArray := nil;
636 end;
638 procedure e_ReleaseSoundSystem();
639 begin
640   e_RemoveAllSounds();
641   if SoundInitialized then
642   begin
643     Mix_CloseAudio();
644     SoundInitialized := False;
645   end;
646 end;
648 procedure e_SoundUpdate();
649 begin
650   //FMOD_System_Update(F_System);
651 end;
654 { TBasicSound: }
656 constructor TBasicSound.Create();
657 begin
658   FID := NO_SOUND_ID;
659   FMusic := False;
660   FChanNum := -1;
661   FPosition := 0;
662   FPriority := 128;
663 end;
665 destructor TBasicSound.Destroy();
666 begin
667   FreeSound();
668   inherited;
669 end;
671 function TBasicSound.GetChan (): Integer;
672 begin
673   if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
674   begin
675     if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
676   end
677   else if e_isMusic(FID) then
678   begin
679     FChanNum := N_MUSCHAN;
680   end;
681   Result := FChanNum;
682 end;
684 procedure TBasicSound.FreeSound();
685 begin
686   if FID = NO_SOUND_ID then Exit;
687   Stop();
688   FID := NO_SOUND_ID;
689   FMusic := False;
690   FPosition := 0;
691   FChanNum := -1;
692 end;
694 // aPos: msecs
695 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
696 begin
697   Result := False;
698   if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
699   FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
700   Result := (FChanNum >= 0);
701   //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
702   //TODO: aPos
703 end;
705 procedure TBasicSound.SetID(ID: DWORD);
706 begin
707   FreeSound();
708   FID := ID;
709   if ID <> NO_SOUND_ID then
710   begin
711     FMusic := e_SoundsArray[ID].isMusic;
712   end;
713   FChanNum := -1;
714 end;
716 function TBasicSound.IsPlaying(): Boolean;
718   chan: Integer;
719 begin
720   Result := False;
721   if e_isSound(FID) then
722   begin
723     //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
724     chan := Channel;
725     if chan < 0 then
726     begin
727       //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
728       Exit;
729     end;
730     //Result := (Mix_Playing(chan) > 0)
731     //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
732     Result := True;
733   end
734   else if e_isMusic(FID) then
735   begin
736     Result := (Mix_PlayingMusic() > 0);
737   end;
738 end;
740 procedure TBasicSound.Stop();
742   chan: Integer;
743 begin
744   if e_isSound(FID) then
745   begin
746     chan := Channel;
747     if chan >= 0 then
748     begin
749       //GetPosition();
750       Mix_HaltChannel(chan);
751     end;
752   end
753   else if e_isMusic(FID) then
754   begin
755     Mix_HaltMusic();
756   end;
757   FChanNum := -1;
758 end;
760 function TBasicSound.IsPaused(): Boolean;
762   chan: Integer;
763 begin
764   Result := False;
765   if e_isSound(FID) then
766   begin
767     chan := Channel;
768     if chan < 0 then Exit;
769     Result := (Mix_Paused(chan) > 0);
770   end
771   else if e_isMusic(FID) then
772   begin
773     Result := (Mix_PausedMusic() > 0);
774   end;
775 end;
777 procedure TBasicSound.Pause(Enable: Boolean);
779   chan: Integer;
780   pl: Boolean;
781 begin
782   Enable := not Enable; // fuckin' double negation
783   if e_isSound(FID) then
784   begin
785     chan := Channel;
786     if chan < 0 then Exit;
787     pl := not (Mix_Paused(chan) > 0);
788     if pl <> Enable then
789     begin
790       if Enable then Mix_Resume(chan) else Mix_Pause(chan);
791     end;
792   end
793   else if e_isMusic(FID) then
794   begin
795     pl := not (Mix_PausedMusic() > 0);
796     if pl <> Enable then
797     begin
798       if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
799     end;
800   end;
801   {
802   if Enable then
803   begin
804     res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
805     if res <> FMOD_OK then
806     begin
807     end;
808   end;
809   }
810 end;
812 function TBasicSound.GetVolume(): Single;
814   chan: Integer;
815 begin
816   Result := 0.0;
817   if e_isSound(FID) then
818   begin
819     chan := Channel;
820     if chan < 0 then Exit;
821     Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
822   end
823   else if e_isMusic(FID) then
824   begin
825     Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
826   end;
827 end;
829 procedure TBasicSound.SetVolume(Volume: Single);
831   chan: Integer;
832 begin
833   if e_isSound(FID) then
834   begin
835     chan := Channel;
836     if chan < 0 then Exit;
837     //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
838     e_chanSetVol(chan, Volume);
839   end
840   else if e_isMusic(FID) then
841   begin
842     //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
843     e_chanSetVol(N_MUSCHAN, Volume);
844   end;
845 end;
847 function TBasicSound.GetPan(): Single;
849   chan: Integer;
850 begin
851   Result := 1.0;
852   if e_isSound(FID) then
853   begin
854     chan := Channel;
855     if chan < 0 then Exit;
856     Result := ChanSIds[chan].pan;
857   end;
858 end;
860 procedure TBasicSound.SetPan(Pan: Single);
862   chan: Integer;
863 begin
864   if e_isSound(FID) then
865   begin
866     chan := Channel;
867     if chan < 0 then Exit;
868     e_chanSetPan(chan, Pan);
869   end;
870 end;
872 function TBasicSound.IsMuted(): Boolean;
874   chan: Integer;
875 begin
876   Result := False;
877   if e_isSound(FID) then
878   begin
879     chan := Channel;
880     if chan < 0 then Exit;
881     Result := ChanSIds[chan].muted;
882   end
883   else if e_isMusic(FID) then
884   begin
885     Result := SoundMuted;
886   end;
887 end;
889 procedure TBasicSound.Mute(Enable: Boolean);
891   chan: Integer;
892 begin
893   if e_isSound(FID) then
894   begin
895     chan := Channel;
896     if chan < 0 then Exit;
897     if ChanSIds[chan].muted <> Enable then
898     begin
899       //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
900       ChanSIds[chan].muted := Enable;
901       if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
902     end;
903   end
904   else if e_isMusic(FID) then
905   begin
906     if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
907   end;
908 end;
910 //TODO
911 function TBasicSound.GetPosition(): DWORD;
912 begin
913   Result := 0;
915   if FChanNum < 0 then Exit;
916   res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
917   if res <> FMOD_OK then
918   begin
919     Exit;
920   end;
921   Result := FPosition;
923 end;
925 //TODO
926 procedure TBasicSound.SetPosition(aPos: DWORD);
927 begin
928   FPosition := aPos;
930   if FChanNum < 0 then Exit;
931   res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
932   if res <> FMOD_OK then
933   begin
934   end;
936 end;
938 //TODO
939 procedure TBasicSound.SetPriority(priority: Integer);
940 begin
942   if (FChanNum <> nil) and (FPriority <> priority) and
943      (priority >= 0) and (priority <= 256) then
944   begin
945     FPriority := priority;
946     res := FMOD_Channel_SetPriority(FChanNum, priority);
947     if res <> FMOD_OK then
948     begin
949     end;
950   end;
952 end;
954 end.