DEADSOFTWARE

fixed music (all sounds, actually) muting after exiting in-game menu (stupid double...
[d2df-sdl.git] / src / engine / e_sound.pas
index 05bcb10611fe9ac5977d030d894a4e83e96a62ac..857101a017105c165b4ecdb5bd8db29e9533cd20 100644 (file)
@@ -3,31 +3,34 @@ unit e_sound;
 interface
 
 uses
-  fmod,
-  fmodtypes,
-  fmoderrors,
+  sdl2,
+  SDL2_mixer,
   e_log,
   SysUtils;
 
 type
   TSoundRec = record
     Data: Pointer;
-    Sound: FMOD_SOUND;
-    Loop: Boolean;
+    Sound: PMix_Chunk;
+    Music: PMix_Music;
+    isMusic: Boolean;
     nRefs: Integer;
   end;
 
   TBasicSound = class (TObject)
   private
-    FChannel: FMOD_CHANNEL;
+    FChanNum: Integer; // <0: no channel allocated
 
   protected
     FID: DWORD;
-    FLoop: Boolean;
+    FMusic: Boolean;
     FPosition: DWORD;
     FPriority: Integer;
 
     function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
+    function GetChan (): Integer;
+
+    property Channel: Integer read GetChan;
 
   public
     constructor Create();
@@ -52,15 +55,16 @@ type
 const
   NO_SOUND_ID = DWORD(-1);
 
-function e_InitSoundSystem(Freq: Integer; forceNoSound: Boolean): Boolean;
+function e_InitSoundSystem(): Boolean;
 
-function e_LoadSound(FileName: string; var ID: DWORD; bLoop: Boolean): Boolean;
-function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; bLoop: Boolean): Boolean;
+function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean): Boolean;
+function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
 
-function e_PlaySound(ID: DWORD): Boolean;
-function e_PlaySoundPan(ID: DWORD; Pan: Single): Boolean;
-function e_PlaySoundVolume(ID: DWORD; Volume: Single): Boolean;
-function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Boolean;
+// returns channel number or -1
+function e_PlaySound(ID: DWORD): Integer;
+function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
+function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
+function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
 
 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
 procedure e_MuteChannels(Enable: Boolean);
@@ -81,615 +85,546 @@ uses
 
 const
   N_CHANNELS = 512;
+  N_MUSCHAN = N_CHANNELS+42;
+
+type
+  TChanInfo = record
+    id: DWORD; // sound id
+    muted: Boolean;
+    oldvol: Integer; // for muted
+    pan: Single;
+  end;
 
 var
-  F_System: FMOD_SYSTEM = nil;
   SoundMuted: Boolean = False;
+  SoundInitialized: Boolean = False;
+  ChanSIds: array[0..N_CHANNELS] of TChanInfo;
+  MusVolume: Integer = MIX_MAX_VOLUME;
 
 
-function Channel_Callback(channel: FMOD_CHANNEL; callbacktype: FMOD_CHANNEL_CALLBACKTYPE;
-                          commanddata1: Pointer; commanddata2: Pointer): FMOD_RESULT; {$IFDEF WIN32} stdcall; {$ELSE} cdecl; {$ENDIF}
-var
-  res: FMOD_RESULT;
-  sound: FMOD_SOUND;
-  ud: Pointer;
-  id: DWORD;
-
+procedure chanFinished (chan: Integer); cdecl;
 begin
-  res := FMOD_OK;
-
-  if callbacktype = FMOD_CHANNEL_CALLBACKTYPE_END then
+  //e_WriteLog(Format('chanFinished: %d', [chan]), MSG_NOTIFY);
+  if (chan >= 0) and (chan < N_CHANNELS) then
   begin
-    res := FMOD_Channel_GetCurrentSound(channel, sound);
-    if res = FMOD_OK then
+    if ChanSIds[chan].id <> NO_SOUND_ID then
     begin
-      res := FMOD_Sound_GetUserData(sound, ud);
-      if res = FMOD_OK then
+      if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
       begin
-        id := DWORD(ud^);
-        if id < DWORD(Length(e_SoundsArray)) then
-          if e_SoundsArray[id].nRefs > 0 then
-            Dec(e_SoundsArray[id].nRefs);
+        Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
       end;
+      ChanSIds[chan].id := NO_SOUND_ID;
     end;
   end;
-
-  Result := res;
 end;
 
-function TryInitWithOutput(Output: FMOD_OUTPUTTYPE; OutputName: String): FMOD_RESULT;
+
+procedure dumpMusicType (ms: PMix_Music);
 begin
-  e_WriteLog('Trying with ' + OutputName + '...', MSG_WARNING);
-  Result := FMOD_System_SetOutput(F_System, Output);
-  if Result <> FMOD_OK then
+  if ms = nil then
   begin
-    e_WriteLog('Error setting FMOD output to ' + OutputName + '!', MSG_WARNING);
-    e_WriteLog(FMOD_ErrorString(Result), MSG_WARNING);
-    Exit;
-  end;
-  Result := FMOD_System_Init(F_System, N_CHANNELS, FMOD_INIT_NORMAL, nil);
-  if Result <> FMOD_OK then
+    e_WriteLog('MUSIC FORMAT: NONE', MSG_NOTIFY);
+  end
+  else
   begin
-    e_WriteLog('Error initializing FMOD system!', MSG_WARNING);
-    e_WriteLog(FMOD_ErrorString(Result), MSG_WARNING);
-    Exit;
+    case Mix_GetMusicType(ms^) of
+      TMix_MusicType.MUS_NONE:
+        e_WriteLog('MUSIC FORMAT: NONE', MSG_NOTIFY);
+      TMix_MusicType.MUS_CMD:
+        e_WriteLog('MUSIC FORMAT: CMD', MSG_NOTIFY);
+      TMix_MusicType.MUS_WAV:
+        e_WriteLog('MUSIC FORMAT: WAV', MSG_NOTIFY);
+      TMix_MusicType.MUS_MOD:
+        e_WriteLog('MUSIC FORMAT: MOD', MSG_NOTIFY);
+      TMix_MusicType.MUS_MID:
+        e_WriteLog('MUSIC FORMAT: MID', MSG_NOTIFY);
+      TMix_MusicType.MUS_OGG:
+        e_WriteLog('MUSIC FORMAT: OGG', MSG_NOTIFY);
+      TMix_MusicType.MUS_MP3:
+        e_WriteLog('MUSIC FORMAT: MP3', MSG_NOTIFY);
+      TMix_MusicType.MUS_MP3_MAD:
+        e_WriteLog('MUSIC FORMAT: MP3_MAD', MSG_NOTIFY);
+      TMix_MusicType.MUS_FLAC:
+        e_WriteLog('MUSIC FORMAT: FLAC', MSG_NOTIFY);
+      TMix_MusicType.MUS_MODPLUG:
+        e_WriteLog('MUSIC FORMAT: MODPLUG', MSG_NOTIFY);
+      otherwise
+        e_WriteLog('MUSIC FORMAT: UNKNOWN', MSG_NOTIFY);
+    end;
   end;
 end;
 
-function e_InitSoundSystem(Freq: Integer; forceNoSound: Boolean): Boolean;
+function e_InitSoundSystem(): Boolean;
 var
-  res: FMOD_RESULT;
-  ver: Cardinal;
-  output: FMOD_OUTPUTTYPE;
-  drv: Integer;
-
+  res, i: Integer;
 begin
+  if SoundInitialized then begin Result := true; Exit end;
+
   Result := False;
+  SoundInitialized := False;
 
-  res := FMOD_System_Create(F_System);
-  if res <> FMOD_OK then
-  begin
-    e_WriteLog('Error creating FMOD system:', MSG_FATALERROR);
-    e_WriteLog(FMOD_ErrorString(res), MSG_FATALERROR);
-    Exit;
-  end;
+  // wow, this is actually MIDI player!
+  // we need module player
+  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);
+  e_WriteLog(Format('SDL: res=0x%x', [res]), MSG_NOTIFY);
+  if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', MSG_NOTIFY);
+  if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', MSG_NOTIFY);
+  if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', MSG_NOTIFY);
+  if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', MSG_NOTIFY);
+  if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', MSG_NOTIFY);
+  if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', MSG_NOTIFY);
 
-  res := FMOD_System_GetVersion(F_System, ver);
-  if res <> FMOD_OK then
+  res := Mix_OpenAudio(48000, AUDIO_S16LSB, 2, 2048);
+  if res = -1 then res := Mix_OpenAudio(44100, AUDIO_S16LSB, 2, 2048);
+  if res = -1 then
   begin
-    e_WriteLog('Error getting FMOD version:', MSG_FATALERROR);
-    e_WriteLog(FMOD_ErrorString(res), MSG_FATALERROR);
+    e_WriteLog('Error initializing SDL mixer:', MSG_FATALERROR);
+    e_WriteLog(Mix_GetError(), MSG_FATALERROR);
     Exit;
   end;
 
-  if ver < FMOD_VERSION then
+  Mix_AllocateChannels(N_CHANNELS);
+  Mix_ChannelFinished(chanFinished);
+
+  for i := 0 to N_CHANNELS-1 do
   begin
-    e_WriteLog('FMOD library version is too old! Need '+IntToStr(FMOD_VERSION), MSG_FATALERROR);
-    Exit;
+    ChanSIds[i].id := NO_SOUND_ID;
+    ChanSIds[i].muted := SoundMuted;
+    ChanSIds[i].oldvol := MIX_MAX_VOLUME;
+    ChanSIds[i].pan := 1.0;
   end;
+  MusVolume := MIX_MAX_VOLUME;
 
-  res := FMOD_System_SetSoftwareFormat(F_System, Freq,
-           FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR);
-  if res <> FMOD_OK then
+  SoundInitialized := True;
+  Result := True;
+end;
+
+function e_isMusic (id: DWORD): Boolean;
+begin
+  Result := False;
+  if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
   begin
-    e_WriteLog('Error setting FMOD software format!', MSG_FATALERROR);
-    e_WriteLog(FMOD_ErrorString(res), MSG_FATALERROR);
-    Exit;
+    Result := (e_SoundsArray[id].Music <> nil);
   end;
+end;
 
-  res := FMOD_System_Init(F_System, N_CHANNELS, FMOD_INIT_NORMAL, nil);
-  if res <> FMOD_OK then
+function e_isSound (id: DWORD): Boolean;
+begin
+  Result := False;
+  if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
   begin
-    e_WriteLog('Error initializing FMOD system!', MSG_WARNING);
-    e_WriteLog(FMOD_ErrorString(res), MSG_WARNING);
-
-    {$IFDEF LINUX}
-    res := TryInitWithOutput(FMOD_OUTPUTTYPE_ALSA, 'OUTPUTTYPE_ALSA');
-    if res <> FMOD_OK then
-      res := TryInitWithOutput(FMOD_OUTPUTTYPE_OSS, 'OUTPUTTYPE_OSS');
-    {$ENDIF}
-    if not forceNoSound then Exit;
-    if res <> FMOD_OK then
-      res := TryInitWithOutput(FMOD_OUTPUTTYPE_NOSOUND, 'OUTPUTTYPE_NOSOUND');
-    if res <> FMOD_OK then
-    begin
-      e_WriteLog('FMOD: Giving up, can''t init any output.', MSG_FATALERROR);
-      Exit;
-    end;
+    Result := (e_SoundsArray[id].Sound <> nil);
   end;
-
-  res := FMOD_System_GetOutput(F_System, output);
-  if res <> FMOD_OK then
-    e_WriteLog('Error getting FMOD output!', MSG_WARNING)
-  else
-    case output of
-      FMOD_OUTPUTTYPE_NOSOUND: e_WriteLog('FMOD Output Method: NOSOUND', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_NOSOUND_NRT: e_WriteLog('FMOD Output Method: NOSOUND_NRT', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_DSOUND: e_WriteLog('FMOD Output Method: DSOUND', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_WINMM: e_WriteLog('FMOD Output Method: WINMM', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_OPENAL: e_WriteLog('FMOD Output Method: OPENAL', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_WASAPI: e_WriteLog('FMOD Output Method: WASAPI', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_ASIO: e_WriteLog('FMOD Output Method: ASIO', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_OSS:  e_WriteLog('FMOD Output Method: OSS', MSG_NOTIFY);
-      FMOD_OUTPUTTYPE_ALSA: e_Writelog('FMOD Output Method: ALSA', MSG_NOTIFY);
-      else e_WriteLog('FMOD Output Method: Unknown', MSG_NOTIFY);
-    end;
-
-  res := FMOD_System_GetDriver(F_System, drv);
-  if res <> FMOD_OK then
-    e_WriteLog('Error getting FMOD driver!', MSG_WARNING)
-  else
-    e_WriteLog('FMOD driver id: '+IntToStr(drv), MSG_NOTIFY);
-
-  Result := True;
 end;
 
 function FindESound(): DWORD;
 var
   i: Integer;
-
 begin
   if e_SoundsArray <> nil then
+  begin
     for i := 0 to High(e_SoundsArray) do
-      if e_SoundsArray[i].Sound = nil then
+      if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
       begin
         Result := i;
         Exit;
       end;
-
+  end;
   if e_SoundsArray = nil then
-    begin
-      SetLength(e_SoundsArray, 16);
-      Result := 0;
-    end
+  begin
+    SetLength(e_SoundsArray, 16);
+    Result := 0;
+  end
   else
-    begin
-      Result := High(e_SoundsArray) + 1;
-      SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
-    end;
+  begin
+    Result := High(e_SoundsArray) + 1;
+    SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
+  end;
+  for i := Result to High(e_SoundsArray) do
+  begin
+    e_SoundsArray[i].Sound := nil;
+    e_SoundsArray[i].Music := nil;
+    e_SoundsArray[i].Data := nil;
+    e_SoundsArray[i].isMusic := False;
+    e_SoundsArray[i].nRefs := 0;
+  end;
 end;
 
-function e_LoadSound(FileName: String; var ID: DWORD; bLoop: Boolean): Boolean;
+function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean): Boolean;
 var
   find_id: DWORD;
-  res: FMOD_RESULT;
-  bt: Cardinal;
-  ud: Pointer;
-
 begin
+  ID := NO_SOUND_ID;
   Result := False;
+  if not SoundInitialized then Exit;
 
-  e_WriteLog('Loading sound '+FileName+'...', MSG_NOTIFY);
+  if isMusic then e_WriteLog('Loading music '+FileName+'...', MSG_NOTIFY)
+  else e_WriteLog('Loading sound '+FileName+'...', MSG_NOTIFY);
 
-  find_id := FindESound();
-
-  if bLoop then
-    bt := FMOD_LOOP_NORMAL
-  else
-    bt := FMOD_LOOP_OFF;
-
-  if not bLoop then
-    res := FMOD_System_CreateSound(F_System, PAnsiChar(FileName),
-             bt + FMOD_2D + FMOD_HARDWARE,
-             nil, e_SoundsArray[find_id].Sound)
-  else
-    res := FMOD_System_CreateStream(F_System, PAnsiChar(FileName),
-             bt + FMOD_2D + FMOD_HARDWARE,
-             nil, e_SoundsArray[find_id].Sound);
-  if res <> FMOD_OK then
+  {
+  if isMusic then
   begin
-    e_SoundsArray[find_id].Sound := nil;
+    e_WriteLog('IGNORING MUSIC FROM FILE', MSG_WARNING);
     Exit;
   end;
+  }
 
-  GetMem(ud, SizeOf(DWORD));
-  DWORD(ud^) := find_id;
-  res := FMOD_Sound_SetUserData(e_SoundsArray[find_id].Sound, ud);
-  if res <> FMOD_OK then
-  begin
-    e_SoundsArray[find_id].Sound := nil;
-    Exit;
-  end;
+  find_id := FindESound();
 
   e_SoundsArray[find_id].Data := nil;
-  e_SoundsArray[find_id].Loop := bLoop;
+  e_SoundsArray[find_id].isMusic := isMusic;
   e_SoundsArray[find_id].nRefs := 0;
 
-  ID := find_id;
-
-  Result := True;
-end;
-
-function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; bLoop: Boolean): Boolean;
-var
-  find_id: DWORD;
-  res: FMOD_RESULT;
-  sz: Integer;
-  bt: Cardinal;
-  soundExInfo: FMOD_CREATESOUNDEXINFO;
-  ud: Pointer;
-
-begin
-  Result := False;
-
-  find_id := FindESound();
-
-  sz := SizeOf(FMOD_CREATESOUNDEXINFO);
-  FillMemory(@soundExInfo, sz, 0);
-  soundExInfo.cbsize := sz;
-  soundExInfo.length := Length;
-
-  if bLoop then
-    bt := FMOD_LOOP_NORMAL
-  else
-    bt := FMOD_LOOP_OFF;
-
-  if not bLoop then
-    res := FMOD_System_CreateSound(F_System, pData,
-             bt + FMOD_2D + FMOD_HARDWARE + FMOD_OPENMEMORY,
-             @soundExInfo, e_SoundsArray[find_id].Sound)
-  else
-    res := FMOD_System_CreateStream(F_System, pData,
-             bt + FMOD_2D + FMOD_HARDWARE + FMOD_OPENMEMORY,
-             @soundExInfo, e_SoundsArray[find_id].Sound);
-  if res <> FMOD_OK then
+  if isMusic then
   begin
-    e_SoundsArray[find_id].Sound := nil;
-    Exit;
-  end;
-
-  GetMem(ud, SizeOf(DWORD));
-  DWORD(ud^) := find_id;
-  res := FMOD_Sound_SetUserData(e_SoundsArray[find_id].Sound, ud);
-  if res <> FMOD_OK then
+    e_WriteLog(Format('  MUSIC SLOT: %u', [find_id]), MSG_NOTIFY);
+    e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
+    if e_SoundsArray[find_id].Music = nil then
+    begin
+      e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), MSG_WARNING);
+      e_WriteLog(Mix_GetError(), MSG_WARNING);
+      Exit;
+    end;
+    dumpMusicType(e_SoundsArray[find_id].Music);
+  end
+  else
   begin
-    e_SoundsArray[find_id].Sound := nil;
-    Exit;
+    e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
+    if e_SoundsArray[find_id].Sound = nil then Exit;
   end;
 
-  e_SoundsArray[find_id].Data := pData;
-  e_SoundsArray[find_id].Loop := bLoop;
-  e_SoundsArray[find_id].nRefs := 0;
-
   ID := find_id;
 
   Result := True;
 end;
 
-function e_PlaySound(ID: DWORD): Boolean;
+function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
 var
-  res: FMOD_RESULT;
-  Chan: FMOD_CHANNEL;
-
+  find_id: DWORD;
+  rw: PSDL_RWops;
+  //pc: PChar;
+  isid3: Boolean;
 begin
-  if e_SoundsArray[ID].nRefs >= gMaxSimSounds then
-  begin
-    Result := True;
-    Exit;
-  end;
-
+  ID := NO_SOUND_ID;
   Result := False;
+  if not SoundInitialized then Exit;
+  isid3 := False;
 
-  res := FMOD_System_PlaySound(F_System, FMOD_CHANNEL_FREE,
-           e_SoundsArray[ID].Sound, False, Chan);
-  if res <> FMOD_OK then
+  {
+  if isMusic then
   begin
+    e_WriteLog('IGNORING MUSIC FROM MEMORY', MSG_WARNING);
     Exit;
   end;
+  }
 
-  res := FMOD_Channel_SetCallback(Chan, Channel_Callback);
-  if res <> FMOD_OK then
-  begin
-  end;
-
-  if SoundMuted then
+  //FIXME: correctly skip ID3
+  {
+  pc := PChar(pData);
+  if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
   begin
-    res := FMOD_Channel_SetMute(Chan, True);
-    if res <> FMOD_OK then
-    begin
-    end;
+    isid3 := True;
+    Inc(pc, $400);
+    pData := Pointer(pc);
+    Dec(Length, $400);
+    e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', MSG_WARNING);
   end;
+  }
 
-  Inc(e_SoundsArray[ID].nRefs);
-  Result := True;
-end;
+  rw := SDL_RWFromConstMem(pData, Length);
+  if rw = nil then Exit;
 
-function e_PlaySoundPan(ID: DWORD; Pan: Single): Boolean;
-var
-  res: FMOD_RESULT;
-  Chan: FMOD_CHANNEL;
-
-begin
-  if e_SoundsArray[ID].nRefs >= gMaxSimSounds then
-  begin
-    Result := True;
-    Exit;
-  end;
+  find_id := FindESound();
 
-  Result := False;
+  e_SoundsArray[find_id].Data := pData;
+  if isid3 then e_SoundsArray[find_id].Data := nil;
+  e_SoundsArray[find_id].isMusic := isMusic;
+  e_SoundsArray[find_id].nRefs := 0;
 
-  res := FMOD_System_PlaySound(F_System, FMOD_CHANNEL_FREE,
-           e_SoundsArray[ID].Sound, False, Chan);
-  if res <> FMOD_OK then
+  if isMusic then
   begin
+    e_WriteLog(Format('  MUSIC SLOT: %u', [find_id]), MSG_NOTIFY);
+    e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
+    if e_SoundsArray[find_id].Music = nil then
+    begin
+      e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), MSG_WARNING);
+      e_WriteLog(Mix_GetError(), MSG_WARNING);
+    end
+    else
+    begin
+      dumpMusicType(e_SoundsArray[find_id].Music);
+    end;
+    //SDL_FreeRW(rw);
+    {
+    if e_SoundsArray[find_id].Music <> nil then
+    begin
+      Mix_FreeMusic(e_SoundsArray[find_id].Music);
+    end;
+    e_SoundsArray[find_id].Music := nil;
     Exit;
-  end;
-
-  res := FMOD_Channel_SetPan(Chan, Pan);
-  if res <> FMOD_OK then
+    }
+  end
+  else
   begin
+    e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
   end;
-
-  res := FMOD_Channel_SetCallback(Chan, Channel_Callback);
-  if res <> FMOD_OK then
+  //SDL_FreeRW(rw); // somehow it segfaults...
+  if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
   begin
+    e_SoundsArray[find_id].Data := nil;
+    Exit;
   end;
 
-  if SoundMuted then
-  begin
-    res := FMOD_Channel_SetMute(Chan, True);
-    if res <> FMOD_OK then
-    begin
-    end;
-  end;
+  ID := find_id;
 
-  Inc(e_SoundsArray[ID].nRefs);
   Result := True;
 end;
 
-function e_PlaySoundVolume(ID: DWORD; Volume: Single): Boolean;
+function e_PlaySound (ID: DWORD): Integer;
 var
-  res: FMOD_RESULT;
-  Chan: FMOD_CHANNEL;
-
+  res: Integer = -1;
 begin
-  if e_SoundsArray[ID].nRefs >= gMaxSimSounds then
-  begin
-    Result := True;
-    Exit;
-  end;
-
-  Result := False;
-
-  res := FMOD_System_PlaySound(F_System, FMOD_CHANNEL_FREE,
-           e_SoundsArray[ID].Sound, False, Chan);
-  if res <> FMOD_OK then
-  begin
-    Exit;
-  end;
-
-  res := FMOD_Channel_SetVolume(Chan, Volume);
-  if res <> FMOD_OK then
-  begin
-  end;
+  Result := -1;
+  if not SoundInitialized then Exit;
 
-  res := FMOD_Channel_SetCallback(Chan, Channel_Callback);
-  if res <> FMOD_OK then
+  if e_isSound(ID) then
   begin
-  end;
-
-  if SoundMuted then
-  begin
-    res := FMOD_Channel_SetMute(Chan, True);
-    if res <> FMOD_OK then
+    if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
+    Inc(e_SoundsArray[ID].nRefs);
+    res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
+    if res >= 0 then
     begin
+      ChanSIds[res].id := ID;
+      ChanSIds[res].muted := SoundMuted;
+      if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
+      {
+      if e_SoundsArray[ID].isMusic then
+        res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
+      else
+        res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
+      }
     end;
+  end
+  else
+  begin
+    if not e_isMusic(ID) then Exit;
+    Mix_HaltMusic();
+    res := Mix_PlayMusic(e_SoundsArray[ID].Music, -1);
+    if res >= 0 then res := N_MUSCHAN;
+    if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
+    Result := res;
   end;
 
-  Inc(e_SoundsArray[ID].nRefs);
-  Result := True;
+  Result := res;
 end;
 
-function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Boolean;
+function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
 var
-  res: FMOD_RESULT;
-  Chan: FMOD_CHANNEL;
-
+  l, r: UInt8;
 begin
-  if e_SoundsArray[ID].nRefs >= gMaxSimSounds then
-  begin
-    Result := True;
-    Exit;
-  end;
-
-  Result := False;
-
-  res := FMOD_System_PlaySound(F_System, FMOD_CHANNEL_FREE,
-           e_SoundsArray[ID].Sound, False, Chan);
-  if res <> FMOD_OK then
-  begin
-    Exit;
-  end;
-
-  res := FMOD_Channel_SetPan(Chan, Pan);
-  if res <> FMOD_OK then
+  Result := True;
+  if chan = N_MUSCHAN then
+  begin
+    // no panning for music
+  end
+  else if chan >= 0 then
+  begin
+    if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
+    Pan := Pan+1.0; // 0..2
+    l := trunc(127.0*(2.0-Pan));
+    r := trunc(127.0*Pan);
+    Mix_SetPanning(chan, l, r);
+    ChanSIds[chan].pan := Pan;
+  end
+  else
   begin
+    Result := False;
   end;
+end;
 
-  res := FMOD_Channel_SetVolume(Chan, Volume);
-  if res <> FMOD_OK then
+function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
+var
+  vol: Integer;
+begin
+  Result := True;
+  if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
+  vol := trunc(Volume*MIX_MAX_VOLUME);
+  if chan = N_MUSCHAN then
+  begin
+    MusVolume := vol;
+    if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
+  end
+  else if chan >= 0 then
+  begin
+    ChanSIds[chan].oldvol := vol;
+    if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
+  end
+  else
   begin
+    Result := False;
   end;
+end;
 
-  res := FMOD_Channel_SetCallback(Chan, Channel_Callback);
-  if res <> FMOD_OK then
-  begin
-  end;
+function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
+var
+  chan: Integer;
+begin
+  Result := -1;
+  chan := e_PlaySound(ID);
+  e_chanSetPan(chan, Pan);
+  Result := chan;
+end;
 
-  if SoundMuted then
-  begin
-    res := FMOD_Channel_SetMute(Chan, True);
-    if res <> FMOD_OK then
-    begin
-    end;
-  end;
+function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
+var
+  chan: Integer;
+begin
+  Result := -1;
+  chan := e_PlaySound(ID);
+  e_chanSetVol(chan, Volume);
+  Result := chan;
+end;
 
-  Inc(e_SoundsArray[ID].nRefs);
-  Result := True;
+function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
+var
+  chan: Integer;
+begin
+  Result := -1;
+  chan := e_PlaySound(ID);
+  e_chanSetPan(chan, Pan);
+  e_chanSetVol(chan, Volume);
+  Result := chan;
 end;
 
 procedure e_DeleteSound(ID: DWORD);
 var
-  res: FMOD_RESULT;
-  ud: Pointer;
-
+  i: Integer;
 begin
-  if e_SoundsArray[ID].Sound = nil then
-    Exit;
-
-  if e_SoundsArray[ID].Data <> nil then
-    FreeMem(e_SoundsArray[ID].Data);
+  if ID > High(e_SoundsArray) then Exit;
+  if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
 
-  res := FMOD_Sound_GetUserData(e_SoundsArray[ID].Sound, ud);
-  if res = FMOD_OK then
+  for i := 0 to N_CHANNELS-1 do
   begin
-    FreeMem(ud);
+    if ChanSIds[i].id = ID then
+    begin
+      ChanSIds[i].id := NO_SOUND_ID;
+      Mix_HaltChannel(i);
+    end;
   end;
 
-  res := FMOD_Sound_Release(e_SoundsArray[ID].Sound);
-  if res <> FMOD_OK then
-  begin
-    e_WriteLog('Error releasing sound:', MSG_WARNING);
-    e_WriteLog(FMOD_ErrorString(res), MSG_WARNING);
-  end;
+  if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
+  if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
+  if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
 
   e_SoundsArray[ID].Sound := nil;
+  e_SoundsArray[ID].Music := nil;
   e_SoundsArray[ID].Data := nil;
+  e_SoundsArray[ID].nRefs := 0;
 end;
 
 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
 var
-  res: FMOD_RESULT;
   i: Integer;
-  Chan: FMOD_CHANNEL;
   vol: Single;
-
+  ovol: Integer;
 begin
   for i := 0 to N_CHANNELS-1 do
   begin
-    Chan := nil;
-    res := FMOD_System_GetChannel(F_System, i, Chan);
-
-    if (res = FMOD_OK) and (Chan <> nil) then
+    ovol := ChanSIds[i].oldvol;
+    if setMode then
     begin
-      res := FMOD_Channel_GetVolume(Chan, vol);
-
-      if res = FMOD_OK then
-      begin
-        if setMode then
-          vol := SoundMod
-        else
-          vol := vol * SoundMod;
-
-        res := FMOD_Channel_SetVolume(Chan, vol);
-
-        if res <> FMOD_OK then
-        begin
-        end;
-      end;
+      vol := SoundMod;
+    end
+    else
+    begin
+      vol := (MIX_MAX_VOLUME+0.0)/ovol;
+      vol := vol*SoundMod;
     end;
+    if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
+    ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
+    //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), MSG_WARNING);
+    if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
+  end;
+  ovol := Mix_VolumeMusic(-1);
+  if ovol >= 0 then
+  begin
+    if setMode then
+    begin
+      vol := SoundMod;
+    end
+    else
+    begin
+      vol := (MIX_MAX_VOLUME+0.0)/ovol;
+      vol := vol * SoundMod;
+    end;
+    if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
+    MusVolume := trunc(vol*MIX_MAX_VOLUME);
+    if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
   end;
 end;
 
 procedure e_MuteChannels(Enable: Boolean);
 var
-  res: FMOD_RESULT;
   i: Integer;
-  Chan: FMOD_CHANNEL;
-
 begin
-  if Enable = SoundMuted then
-    Exit;
-
+  //if Enable = SoundMuted then Exit;
   SoundMuted := Enable;
-
   for i := 0 to N_CHANNELS-1 do
   begin
-    Chan := nil;
-    res := FMOD_System_GetChannel(F_System, i, Chan);
-
-    if (res = FMOD_OK) and (Chan <> nil) then
+    if ChanSIds[i].muted <> SoundMuted then
     begin
-      res := FMOD_Channel_SetMute(Chan, Enable);
-
-      if res <> FMOD_OK then
-      begin
-      end;
+      ChanSIds[i].muted := SoundMuted;
+      //e_WriteLog(Format('gmuting sound for channel %d', [i]), MSG_WARNING);
+      if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
     end;
   end;
+  //if SoundMuted then e_WriteLog('muting music', MSG_NOTIFY) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), MSG_NOTIFY);
+  if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
 end;
 
 procedure e_StopChannels();
 var
-  res: FMOD_RESULT;
   i: Integer;
-  Chan: FMOD_CHANNEL;
-
 begin
-  for i := 0 to N_CHANNELS-1 do
-  begin
-    Chan := nil;
-    res := FMOD_System_GetChannel(F_System, i, Chan);
-
-    if (res = FMOD_OK) and (Chan <> nil) then
-    begin
-      res := FMOD_Channel_Stop(Chan);
-
-      if res <> FMOD_OK then
-      begin
-      end;
-    end;
-  end;
+  Mix_HaltChannel(-1);
+  Mix_HaltMusic();
+  for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
+  for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
 end;
 
 procedure e_RemoveAllSounds();
 var
   i: Integer;
-
 begin
-  for i := 0 to High(e_SoundsArray) do
-    if e_SoundsArray[i].Sound <> nil then
-      e_DeleteSound(i);
-
+  if SoundInitialized then e_StopChannels();
+  for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
   SetLength(e_SoundsArray, 0);
   e_SoundsArray := nil;
 end;
 
 procedure e_ReleaseSoundSystem();
-var
-  res: FMOD_RESULT;
-
 begin
   e_RemoveAllSounds();
-
-  res := FMOD_System_Close(F_System);
-  if res <> FMOD_OK then
-  begin
-    e_WriteLog('Error closing FMOD system!', MSG_FATALERROR);
-    e_WriteLog(FMOD_ErrorString(res), MSG_FATALERROR);
-    Exit;
-  end;
-
-  res := FMOD_System_Release(F_System);
-  if res <> FMOD_OK then
+  if SoundInitialized then
   begin
-    e_WriteLog('Error releasing FMOD system!', MSG_FATALERROR);
-    e_WriteLog(FMOD_ErrorString(res), MSG_FATALERROR);
+    Mix_CloseAudio();
+    SoundInitialized := False;
   end;
 end;
 
 procedure e_SoundUpdate();
 begin
-  FMOD_System_Update(F_System);
+  //FMOD_System_Update(F_System);
 end;
 
+
 { TBasicSound: }
 
 constructor TBasicSound.Create();
 begin
   FID := NO_SOUND_ID;
-  FLoop := False;
-  FChannel := nil;
+  FMusic := False;
+  FChanNum := -1;
   FPosition := 0;
   FPriority := 128;
 end;
@@ -700,314 +635,287 @@ begin
   inherited;
 end;
 
-procedure TBasicSound.FreeSound();
+function TBasicSound.GetChan (): Integer;
 begin
-  if FID = NO_SOUND_ID then
-    Exit;
+  if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
+  begin
+    if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
+  end
+  else if e_isMusic(FID) then
+  begin
+    FChanNum := N_MUSCHAN;
+  end;
+  Result := FChanNum;
+end;
 
+procedure TBasicSound.FreeSound();
+begin
+  if FID = NO_SOUND_ID then Exit;
   Stop();
   FID := NO_SOUND_ID;
-  FLoop := False;
+  FMusic := False;
   FPosition := 0;
+  FChanNum := -1;
 end;
 
+// aPos: msecs
 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
-var
-  res: FMOD_RESULT;
-
 begin
-  if e_SoundsArray[FID].nRefs >= gMaxSimSounds then
-  begin
-    Result := True;
-    Exit;
-  end;
-
   Result := False;
-
-  if FID = NO_SOUND_ID then
-    Exit;
-
-  res := FMOD_System_PlaySound(F_System, FMOD_CHANNEL_FREE,
-           e_SoundsArray[FID].Sound, False, FChannel);
-  if res <> FMOD_OK then
-  begin
-    FChannel := nil;
-    Exit;
-  end;
-
-  res := FMOD_Channel_SetPosition(FChannel, aPos, FMOD_TIMEUNIT_MS);
-  if res <> FMOD_OK then
-    begin
-      FPosition := 0;
-    end
-  else
-    FPosition := aPos;
-
-  res := FMOD_Channel_SetPan(FChannel, Pan);
-  if res <> FMOD_OK then
-  begin
-  end;
-
-  res := FMOD_Channel_SetVolume(FChannel, Volume);
-  if res <> FMOD_OK then
-  begin
-  end;
-
-  res := FMOD_Channel_SetCallback(FChannel, Channel_Callback);
-  if res <> FMOD_OK then
-  begin
-  end;
-
-  if SoundMuted then
-  begin
-    res := FMOD_Channel_SetMute(FChannel, True);
-    if res <> FMOD_OK then
-    begin
-    end;
-  end;
-
-  Inc(e_SoundsArray[FID].nRefs);
-  Result := True;
+  if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
+  FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
+  Result := (FChanNum >= 0);
+  //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), MSG_NOTIFY);
+  //TODO: aPos
 end;
 
 procedure TBasicSound.SetID(ID: DWORD);
 begin
   FreeSound();
   FID := ID;
-  FLoop := e_SoundsArray[ID].Loop;
+  if ID <> NO_SOUND_ID then
+  begin
+    FMusic := e_SoundsArray[ID].isMusic;
+  end;
+  FChanNum := -1;
 end;
 
 function TBasicSound.IsPlaying(): Boolean;
 var
-  res: FMOD_RESULT;
-  b: LongBool;
-
+  chan: Integer;
 begin
   Result := False;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_IsPlaying(FChannel, b);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
-    Exit;
+    //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), MSG_WARNING);
+    chan := Channel;
+    if chan < 0 then
+    begin
+      //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), MSG_WARNING);
+      Exit;
+    end;
+    //Result := (Mix_Playing(chan) > 0)
+    //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), MSG_WARNING);
+    Result := True;
+  end
+  else if e_isMusic(FID) then
+  begin
+    Result := (Mix_PlayingMusic() > 0);
   end;
-
-  Result := b;
 end;
 
 procedure TBasicSound.Stop();
 var
-  res: FMOD_RESULT;
-
+  chan: Integer;
 begin
-  if FChannel = nil then
-    Exit;
-
-  GetPosition();
-
-  res := FMOD_Channel_Stop(FChannel);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
+    chan := Channel;
+    if chan >= 0 then
+    begin
+      //GetPosition();
+      Mix_HaltChannel(chan);
+    end;
+  end
+  else if e_isMusic(FID) then
+  begin
+    Mix_HaltMusic();
   end;
-
-  FChannel := nil;
+  FChanNum := -1;
 end;
 
 function TBasicSound.IsPaused(): Boolean;
 var
-  res: FMOD_RESULT;
-  b: LongBool;
-
+  chan: Integer;
 begin
   Result := False;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_GetPaused(FChannel, b);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
-    Exit;
+    chan := Channel;
+    if chan < 0 then Exit;
+    Result := (Mix_Paused(chan) > 0);
+  end
+  else if e_isMusic(FID) then
+  begin
+    Result := (Mix_PausedMusic() > 0);
   end;
-
-  Result := b;
 end;
 
 procedure TBasicSound.Pause(Enable: Boolean);
 var
-  res: FMOD_RESULT;
-
+  chan: Integer;
+  pl: Boolean;
 begin
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_SetPaused(FChannel, Enable);
-  if res <> FMOD_OK then
+  Enable := not Enable; // fuckin' double negation
+  if e_isSound(FID) then
   begin
+    chan := Channel;
+    if chan < 0 then Exit;
+    pl := not (Mix_Paused(chan) > 0);
+    if pl <> Enable then
+    begin
+      if Enable then Mix_Resume(chan) else Mix_Pause(chan);
+    end;
+  end
+  else if e_isMusic(FID) then
+  begin
+    pl := not (Mix_PausedMusic() > 0);
+    if pl <> Enable then
+    begin
+      if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
+    end;
   end;
-
+  {
   if Enable then
   begin
-    res := FMOD_Channel_GetPosition(FChannel, FPosition, FMOD_TIMEUNIT_MS);
+    res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
     if res <> FMOD_OK then
     begin
     end;
   end;
+  }
 end;
 
 function TBasicSound.GetVolume(): Single;
 var
-  res: FMOD_RESULT;
-  vol: Single;
-
+  chan: Integer;
 begin
   Result := 0.0;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_GetVolume(FChannel, vol);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
-    Exit;
+    chan := Channel;
+    if chan < 0 then Exit;
+    Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
+  end
+  else if e_isMusic(FID) then
+  begin
+    Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
   end;
-
-  Result := vol;
 end;
 
 procedure TBasicSound.SetVolume(Volume: Single);
 var
-  res: FMOD_RESULT;
-
+  chan: Integer;
 begin
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_SetVolume(FChannel, Volume);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
+  begin
+    chan := Channel;
+    if chan < 0 then Exit;
+    //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), MSG_WARNING);
+    e_chanSetVol(chan, Volume);
+  end
+  else if e_isMusic(FID) then
   begin
+    //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), MSG_WARNING);
+    e_chanSetVol(N_MUSCHAN, Volume);
   end;
 end;
 
 function TBasicSound.GetPan(): Single;
 var
-  res: FMOD_RESULT;
-  pan: Single;
-
+  chan: Integer;
 begin
-  Result := 0.0;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_GetPan(FChannel, pan);
-  if res <> FMOD_OK then
+  Result := 1.0;
+  if e_isSound(FID) then
   begin
-    Exit;
+    chan := Channel;
+    if chan < 0 then Exit;
+    Result := ChanSIds[chan].pan;
   end;
-
-  Result := pan;
 end;
 
 procedure TBasicSound.SetPan(Pan: Single);
 var
-  res: FMOD_RESULT;
-
+  chan: Integer;
 begin
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_SetPan(FChannel, Pan);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
+    chan := Channel;
+    if chan < 0 then Exit;
+    e_chanSetPan(chan, Pan);
   end;
 end;
 
 function TBasicSound.IsMuted(): Boolean;
 var
-  res: FMOD_RESULT;
-  b: LongBool;
-
+  chan: Integer;
 begin
   Result := False;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_GetMute(FChannel, b);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
   begin
-    Exit;
+    chan := Channel;
+    if chan < 0 then Exit;
+    Result := ChanSIds[chan].muted;
+  end
+  else if e_isMusic(FID) then
+  begin
+    Result := SoundMuted;
   end;
-
-  Result := b;
 end;
 
 procedure TBasicSound.Mute(Enable: Boolean);
 var
-  res: FMOD_RESULT;
-
+  chan: Integer;
 begin
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_SetMute(FChannel, Enable);
-  if res <> FMOD_OK then
+  if e_isSound(FID) then
+  begin
+    chan := Channel;
+    if chan < 0 then Exit;
+    if ChanSIds[chan].muted <> Enable then
+    begin
+      //e_WriteLog(Format('muting sound for channel %d', [cnan]), MSG_WARNING);
+      ChanSIds[chan].muted := Enable;
+      if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
+    end;
+  end
+  else if e_isMusic(FID) then
   begin
+    if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
   end;
 end;
 
+//TODO
 function TBasicSound.GetPosition(): DWORD;
-var
-  res: FMOD_RESULT;
-
 begin
   Result := 0;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_GetPosition(FChannel, FPosition, FMOD_TIMEUNIT_MS);
+{
+  if FChanNum < 0 then Exit;
+  res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
   if res <> FMOD_OK then
   begin
     Exit;
   end;
-
   Result := FPosition;
+}
 end;
 
+//TODO
 procedure TBasicSound.SetPosition(aPos: DWORD);
-var
-  res: FMOD_RESULT;
-
 begin
   FPosition := aPos;
-
-  if FChannel = nil then
-    Exit;
-
-  res := FMOD_Channel_SetPosition(FChannel, FPosition, FMOD_TIMEUNIT_MS);
+{
+  if FChanNum < 0 then Exit;
+  res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
   if res <> FMOD_OK then
   begin
   end;
+}
 end;
 
+//TODO
 procedure TBasicSound.SetPriority(priority: Integer);
-var
-  res: FMOD_RESULT;
-
 begin
-  if (FChannel <> nil) and (FPriority <> priority) and
+{
+  if (FChanNum <> nil) and (FPriority <> priority) and
      (priority >= 0) and (priority <= 256) then
   begin
     FPriority := priority;
-    res := FMOD_Channel_SetPriority(FChannel, priority);
+    res := FMOD_Channel_SetPriority(FChanNum, priority);
     if res <> FMOD_OK then
     begin
     end;
   end;
+}
 end;
 
 end.