DEADSOFTWARE

simplify TSoundLoader interface
authorfgsfds <pvt.fgsfds@gmail.com>
Tue, 10 Mar 2020 20:29:50 +0000 (23:29 +0300)
committerfgsfds <pvt.fgsfds@gmail.com>
Tue, 10 Mar 2020 20:29:50 +0000 (23:29 +0300)
src/engine/e_sound_al.inc
src/engine/e_soundfile.pas
src/engine/e_soundfile_fluid.pas
src/engine/e_soundfile_modplug.pas
src/engine/e_soundfile_mp3.pas
src/engine/e_soundfile_opus.pas
src/engine/e_soundfile_vorbis.pas
src/engine/e_soundfile_wav.pas
src/engine/e_soundfile_xmp.pas
src/lib/fluidsynth/fluidsynth.pas

index 53ee47e63a9e351ffefbf73209d094305eddfdd3..838e15863608563cbfad4d6f76eab805a1344ad2 100644 (file)
@@ -265,12 +265,70 @@ begin
   alGetSourcei(S, AL_SOURCE_STATE, Result);
 end;
 
+function LoadEntireSound(var Snd: TSoundRec; Loader: TSoundLoader): Boolean;
+var
+  Frame: Pointer;
+  Data: Pointer;
+  Rx: LongWord;
+  DataLen, OldLen: LongWord;
+const
+  CHUNK_SIZE = 65536 * 2 * 2;
+begin
+  Result := False;
+
+  Frame := GetMem(CHUNK_SIZE);
+  if Frame = nil then exit;
+
+  Data := nil;
+  DataLen := 0;
+
+  repeat
+    Rx := Loader.FillBuffer(Frame, CHUNK_SIZE);
+    if Rx = 0 then break;
+
+    OldLen := DataLen;
+    DataLen := DataLen + Rx;
+    Data := ReAllocMem(Data, DataLen);
+    if Data = nil then begin FreeMem(Frame); exit; end;
+
+    Move(Frame^, (Data + OldLen)^, Rx);
+  until Loader.Finished();
+
+  FreeMem(Frame);
+
+  alGenBuffers(1, Addr(Snd.alBuffer));
+  if CheckALError() then
+  begin
+    e_LogWritefln('AL: Could not create AL buffer: %s', [GetALError()]);
+    FreeMem(Data);
+    exit;
+  end;
+
+  alBufferData(
+    Snd.alBuffer,
+    GetALSoundFormat(Loader.Format),
+    Data,
+    DataLen,
+    Loader.Format.SampleRate
+  );
+
+  FreeMem(Data);
+
+  if CheckALError() then
+  begin
+    e_LogWriteln('AL: Could not fill AL buffer: ' + GetALError());
+    alDeleteBuffers(1, Addr(Snd.alBuffer));
+    Snd.alBuffer := 0;
+    exit;
+  end;
+
+  Result := True;
+end;
+
 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
 var
   find_id: DWORD;
   Loader: TSoundLoader;
-  OutData: Pointer;
-  OutLen: LongWord;
 begin
   ID := NO_SOUND_ID;
   Result := False;
@@ -289,9 +347,7 @@ begin
     exit;
   end;
 
-  Loader.Looping := e_SoundsArray[find_id].Loops;
-
-  if not Loader.Load(FileName, e_SoundsArray[find_id].isMusic) then
+  if not Loader.Load(FileName, e_SoundsArray[find_id].Loops) then
   begin
     e_LogWritefln('Could not load sound `%s`', [FileName]);
     exit;
@@ -299,36 +355,13 @@ begin
 
   alGetError(); // reset error state, god damn it
 
-  if not Loader.Streaming then
+  if not isMusic then
   begin
-    alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
-    if CheckALError() then
-    begin
-      e_LogWritefln('Could not create AL buffer for `%s`: %s', [FileName, GetALError()]);
-      Loader.Free();
-      exit;
-    end;
-
-    OutLen := Loader.GetAll(OutData);
-    alBufferData(
-      e_SoundsArray[find_id].alBuffer,
-      GetALSoundFormat(Loader.Format),
-      OutData,
-      OutLen,
-      Loader.Format.SampleRate
-    );
-
+    if not LoadEntireSound(e_SoundsArray[find_id], Loader) then
+      e_LogWritefln('AL: Could not buffer sound effect `%s`', [FileName]);
     // don't need this anymore
     Loader.Free();
     Loader := nil;
-
-    if CheckALError() then
-    begin
-      e_LogWriteln('AL: what the fuck: ' + GetALError());
-      alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
-      e_SoundsArray[find_id].alBuffer := 0;
-      exit;
-    end;
   end
   else
   begin
@@ -344,8 +377,6 @@ function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic:
 var
   find_id: DWORD;
   Loader: TSoundLoader;
-  OutData: Pointer;
-  OutLen: LongWord;
 begin
   ID := NO_SOUND_ID;
   Result := False;
@@ -364,9 +395,7 @@ begin
     exit;
   end;
 
-  Loader.Looping := e_SoundsArray[find_id].Loops;
-
-  if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].isMusic) then
+  if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].Loops) then
   begin
     e_LogWritefln('Could not load sound `%p`', [pData]);
     exit;
@@ -374,36 +403,13 @@ begin
 
   alGetError(); // reset error state, god damn it
 
-  if not Loader.Streaming then
+  if not isMusic then
   begin
-    alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
-    if CheckALError() then
-    begin
-      e_LogWritefln('Could not create AL buffer for `%p`: %s', [pData, GetALError()]);
-      Loader.Free();
-      exit;
-    end;
-
-    OutLen := Loader.GetAll(OutData);
-    alBufferData(
-      e_SoundsArray[find_id].alBuffer,
-      GetALSoundFormat(Loader.Format),
-      OutData,
-      OutLen,
-      Loader.Format.SampleRate
-    );
-
+    if not LoadEntireSound(e_SoundsArray[find_id], Loader) then
+      e_LogWritefln('AL: Could not buffer sound effect `%p`', [pData]);
     // don't need this anymore
     Loader.Free();
     Loader := nil;
-
-    if CheckALError() then
-    begin
-      e_LogWriteln('AL: what the fuck: ' + GetALError());
-      alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
-      e_SoundsArray[find_id].alBuffer := 0;
-      exit;
-    end;
   end
   else
   begin
@@ -463,7 +469,7 @@ begin
   begin
     // this is a stream
     // reset position
-    e_SoundsArray[ID].Loader.SetPosition(0);
+    e_SoundsArray[ID].Loader.Restart();
     if CurStream <> ID then // changing streams
     begin
       alSourceStop(Src); // this should mark all buffers as processed
index 4ba5cd6ac0b40eeaaa6cd2948655a03df2373b89..c508593235bc070b10a2d348efcc7942131e4885 100644 (file)
@@ -33,22 +33,19 @@ type
   protected
     FFormat: TSoundFormat;
     FStreaming: Boolean;
-    FLooping: Boolean;
 
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; virtual; abstract; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; virtual; abstract; overload;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; virtual; abstract; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; virtual; abstract; overload;
 
-    function SetPosition(Pos: LongWord): Boolean; virtual; abstract;
+    function Finished(): Boolean; virtual; abstract;
+    function Restart(): Boolean; virtual; abstract;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; virtual; abstract;
 
-    function GetAll(var OutPtr: Pointer): LongWord; virtual; abstract;
-
     procedure Free(); virtual; abstract;
 
     property Format: TSoundFormat read FFormat;
     property Streaming: Boolean read FStreaming;
-    property Looping: Boolean read FLooping write FLooping;
   end;
 
   TSoundLoaderFactory = class
index 5235f2c0097a7040f297b350e984b45bdda46fd7..fba2726d408d211e3fd32b453cf70dce4ab62513 100644 (file)
@@ -24,11 +24,11 @@ type
 
   TFluidLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
 
   private
@@ -114,7 +114,7 @@ end;
 
 (* TFluidLoader *)
 
-function TFluidLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TFluidLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 var
   Ret: cint;
 begin
@@ -146,8 +146,9 @@ begin
     end;
   end;
 
-  if FLooping then
+  if Loop then
     fluid_player_set_loop(FPlayer, -1);
+
   FFormat.SampleRate := 44100;
   FFormat.SampleBits := 16;
   FFormat.Channels := 2;
@@ -156,7 +157,7 @@ begin
   Result := True;
 end;
 
-function TFluidLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TFluidLoader.Load(FName: string; Loop: Boolean): Boolean;
 var
   Ret: cint;
 begin
@@ -188,8 +189,9 @@ begin
     end;
   end;
 
-  if FLooping then
+  if Loop then
     fluid_player_set_loop(FPlayer, -1);
+
   FFormat.SampleRate := 44100;
   FFormat.SampleBits := 16;
   FFormat.Channels := 2;
@@ -198,9 +200,22 @@ begin
   Result := True;
 end;
 
-function TFluidLoader.SetPosition(Pos: LongWord): Boolean;
+function TFluidLoader.Finished(): Boolean;
 begin
-  Result := False; // unsupported?
+  Result := fluid_player_get_status(FPlayer) = FLUID_PLAYER_DONE;
+end;
+
+function TFluidLoader.Restart(): Boolean;
+begin
+  Result := False;
+  // fluid_player_seek() is only supported in full 2.x.x, and I ain't compiling that shit
+  // if (FSynth <> nil) and (FPlayer <> nil) then
+  // begin
+  //   fluid_synth_system_reset(FSynth);
+  //   fluid_player_seek(FPlayer, 0);
+  //   fluid_player_play(FPlayer);
+  //   Result := True;
+  // end;
 end;
 
 function TFluidLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
@@ -213,11 +228,6 @@ begin
   if Ret = FLUID_OK then Result := Len;
 end;
 
-function TFluidLoader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0; // midis are always streaming, so this don't make sense
-end;
-
 procedure TFluidLoader.Free();
 begin
   if FPlayer <> nil then 
index 0a2c280c656d0a111e50cae9e1b231b2b503bd9a..600512b06e9cb05d805cb8d36e527f9b1b570fe4 100644 (file)
@@ -24,14 +24,16 @@ type
 
   TModPlugLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
   private
     FFile: PModPlugFile;
+    FFinished: Boolean;
+    FLooping: Boolean;
   end;
 
   TModPlugLoaderFactory = class (TSoundLoaderFactory)
@@ -100,7 +102,7 @@ end;
 
 (* TModPlugLoader *)
 
-function TModPlugLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TModPlugLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 begin
   Result := False;
 
@@ -115,11 +117,13 @@ begin
   FFormat.SampleBits := 16;
   FFormat.Channels := 2;
   FStreaming := True; // modules are always streaming
+  FFinished := False;
+  FLooping := Loop;
 
   Result := True;
 end;
 
-function TModPlugLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TModPlugLoader.Load(FName: string; Loop: Boolean): Boolean;
 var
   S: TStream = nil;
   Data: Pointer;
@@ -136,7 +140,7 @@ begin
     Len := S.Read(Data^, S.Size);
     if Len < 0 then
       raise Exception.Create('what the fuck');
-    Result := Load(Data, Len, SStreaming)
+    Result := Load(Data, Len, Loop);
   except
     on E: Exception do
       e_LogWritefln('ModPlug: ERROR: could not read file `%s`: %s', [FName, E.Message]);
@@ -146,11 +150,17 @@ begin
   if S <> nil then S.Free();
 end;
 
-function TModPlugLoader.SetPosition(Pos: LongWord): Boolean;
+function TModPlugLoader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TModPlugLoader.Restart(): Boolean;
 begin
   Result := False;
   if FFile = nil then Exit;
-  ModPlug_Seek(FFile, Pos);
+  ModPlug_Seek(FFile, 0);
+  FFinished := False;
   Result := True;
 end;
 
@@ -164,22 +174,22 @@ begin
   Cnt := ModPlug_Read(FFile, Buf, Len);
   if Cnt < 0 then Exit;
 
-  if FLooping and (Cnt < Len) then
-  begin
-    // assume it just ended and restart, because modplug only loops if the
-    // module tells it to
-    ModPlug_Seek(FFile, 0);
-    // this used to be Result := Cnt + Read(FFile, Buf + Cnt, Len - Cnt)
-    // but the difference appears to be negligible
-    Result := ModPlug_Read(FFile, Buf, Len);
-  end
-  else
-    Result := Len;
-end;
+  Result := Cnt;
 
-function TModPlugLoader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0; // modules are always streaming, so this don't make sense
+  if Cnt < Len then
+  begin
+    if FLooping then
+    begin
+      // assume it just ended and restart, because modplug only loops if the
+      // module tells it to
+      ModPlug_Seek(FFile, 0);
+      // this used to be Result := Cnt + Read(FFile, Buf + Cnt, Len - Cnt)
+      // but the difference appears to be negligible
+      Result := ModPlug_Read(FFile, Buf, Len);
+    end
+    else
+      FFinished := True;
+  end;
 end;
 
 procedure TModPlugLoader.Free();
@@ -188,6 +198,8 @@ begin
   begin
     ModPlug_Unload(FFile);
     FFile := nil;
+    FFinished := False;
+    FLooping := False;
   end;
 end;
 
index 65b47e60e68e6176a2c8a97ef00904219184bdb3..c49f376b574c01abe207f062ee934890884333c4 100644 (file)
@@ -24,21 +24,22 @@ type
 
   TMP3Loader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
 
   private
     FMPG: pmpg123_handle;
     FData: TStream;
     FBuf: Pointer;
-    FAllSamples: Pointer;
     FOpen: Boolean;
+    FFinished: Boolean;
+    FLooping: Boolean;
 
-    function LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
+    function LoadStream(Stream: TStream): Boolean;
   end;
 
   TMP3LoaderFactory = class (TSoundLoaderFactory)
@@ -138,7 +139,7 @@ end;
 
 (* TMP3Loader *)
 
-function TMP3Loader.LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
+function TMP3Loader.LoadStream(Stream: TStream): Boolean;
 var
   SRate: clong;
   SEnc, SChans: LongInt;
@@ -182,12 +183,13 @@ begin
   FFormat.SampleRate := SRate;
   FFormat.SampleBits := 16;
   FFormat.Channels := SChans;
-  FStreaming := SStreaming;
+  FStreaming := True;
+  FFinished := False;
 
   Result := True;
 end;
 
-function TMP3Loader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TMP3Loader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 var
   S: TStream;
 begin
@@ -200,7 +202,8 @@ begin
   Move(Data^, FBuf^, Len);
 
   S := TSFSMemoryStreamRO.Create(FBuf, Len{, True});
-  Result := LoadStream(S, SStreaming);
+  Result := LoadStream(S);
+  FLooping := Loop;
 
   if not Result and (S <> nil) then
   begin
@@ -210,7 +213,7 @@ begin
   end;
 end;
 
-function TMP3Loader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TMP3Loader.Load(FName: string; Loop: Boolean): Boolean;
 var
   S: TStream = nil;
 begin
@@ -218,7 +221,8 @@ begin
 
   try
     S := openDiskFileRO(FName);
-    Result := LoadStream(S, SStreaming);
+    Result := LoadStream(S);
+    FLooping := Loop;
   except
     on E: Exception do
       e_LogWritefln('MPG123: ERROR: could not read file `%s`: %s', [FName, E.Message]);
@@ -228,11 +232,17 @@ begin
     S.Destroy();
 end;
 
-function TMP3Loader.SetPosition(Pos: LongWord): Boolean;
+function TMP3Loader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TMP3Loader.Restart(): Boolean;
 begin
   Result := False;
   if FMPG = nil then Exit;
-  Result := mpg123_seek(FMPG, Pos, 0) = MPG123_OK;
+  FFinished := False;
+  Result := mpg123_seek(FMPG, 0, 0) = MPG123_OK;
 end;
 
 function TMP3Loader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
@@ -243,19 +253,19 @@ begin
   Result := 0;
   Got := 0;
   if FMPG = nil then Exit;
+
   Ret := mpg123_read(FMPG, Buf, Len, @Got);
-  if FLooping and ((Ret = MPG123_DONE) or (Got = 0)) then
-    Ret := mpg123_seek(FMPG, 0, 0); // loop
-  if Ret = MPG123_OK then
-    Result := Got;
-end;
 
-function TMP3Loader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0;
-  if FMPG = nil then Exit;
-  if FStreaming then Exit;
-  // TODO
+  if (Ret = MPG123_DONE) or (Got = 0) then
+  begin
+    if FLooping then
+      Ret := mpg123_seek(FMPG, 0, 0) // loop
+    else
+      FFinished := True;
+  end;
+
+  if (Ret = MPG123_OK) or FFinished then
+    Result := Got;
 end;
 
 procedure TMP3Loader.Free();
@@ -264,12 +274,12 @@ begin
   if FMPG <> nil then mpg123_delete(FMPG);
   if FData <> nil then FData.Destroy();
   if FBuf <> nil then FreeMem(FBuf);
-  if FAllSamples <> nil then FreeMem(FAllSamples);
   FOpen := False;
+  FFinished := False;
+  FLooping := False;
   FMPG := nil;
   FData := nil;
   FBuf := nil;
-  FAllSamples := nil;
 end;
 
 initialization
index ab799510d62777515048b071d79121e671cd84e7..e0b41e392fd078be32895fd36cc3d709bf891dce 100644 (file)
@@ -24,15 +24,17 @@ type
 
   TOpusLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
   private
     FOpus: POggOpusFile;
     FBuf: Pointer;
+    FFinished: Boolean;
+    FLooping: Boolean;
   end;
 
   TOpusLoaderFactory = class (TSoundLoaderFactory)
@@ -78,7 +80,7 @@ end;
 
 (* TOpusLoader *)
 
-function TOpusLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TOpusLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 begin
   Result := False;
 
@@ -102,11 +104,13 @@ begin
   FFormat.SampleBits := 16;
   FFormat.SampleRate := 48000; // is this even correct?
   FStreaming := True; // opus is always streaming
+  FFinished := False;
+  FLooping := Loop;
 
   Result := True;
 end;
 
-function TOpusLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TOpusLoader.Load(FName: string; Loop: Boolean): Boolean;
 begin
   Result := False;
 
@@ -121,15 +125,23 @@ begin
   FFormat.SampleBits := 16;
   FFormat.SampleRate := 48000; // is this even correct?
   FStreaming := True; // opus is always streaming
+  FFinished := False;
+  FLooping := Loop;
 
   Result := True;
 end;
 
-function TOpusLoader.SetPosition(Pos: LongWord): Boolean;
+function TOpusLoader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TOpusLoader.Restart(): Boolean;
 begin
   Result := False;
   if (FOpus = nil) or (op_seekable(FOpus) = 0) then Exit;
-  Result := op_pcm_seek(FOpus, Pos) = 0;
+  Result := op_pcm_seek(FOpus, 0) = 0;
+  FFinished := False;
 end;
 
 function TOpusLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
@@ -147,18 +159,22 @@ begin
     Ret := op_read_stereo(FOpus, Buf + Rx, (Len - Rx) div 2);
     if Ret = OP_HOLE then continue;
     if Ret < 0 then break;
-    if FLooping and (Ret = 0) then op_pcm_seek(FOpus, 0); // loop
+    if Ret = 0 then
+    begin
+      if FLooping then
+        op_pcm_seek(FOpus, 0)
+      else
+      begin
+        FFinished := True;
+        break;
+      end;
+    end;
     Rx := Rx + Ret * 4;
   end;
 
   Result := Rx;
 end;
 
-function TOpusLoader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0; // always streaming
-end;
-
 procedure TOpusLoader.Free();
 begin
   if FOpus <> nil then
@@ -168,6 +184,8 @@ begin
   FOpus := nil;
   FBuf := nil;
   FStreaming := False;
+  FFinished := False;
+  FLooping := False;
 end;
 
 initialization
index d003fb697b728a16065af8e245af5bbf9c6d6571..d2d364bf53e6854ff2d78856c03abe885afc11b7 100644 (file)
@@ -24,22 +24,22 @@ type
 
   TVorbisLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
 
   private
     FOgg: OggVorbis_File;
     FData: TStream;
     FBuf: Pointer;
-    FTotal: LongWord;
     FOpen: Boolean;
+    FFinished: Boolean;
+    FLooping: Boolean;
 
-    function LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
-    function LoadEntireStream(): Pointer;
+    function LoadStream(Stream: TStream): Boolean;
   end;
 
   TVorbisLoaderFactory = class (TSoundLoaderFactory)
@@ -142,35 +142,10 @@ end;
 
 (* TVorbisLoader *)
 
-function TVorbisLoader.LoadEntireStream(): Pointer;
-var
-  Samples: ogg_int64_t;
-  Ret: clong;
-begin
-  Result := nil;
-
-  Samples := ov_pcm_total(FOgg, -1);
-  if Samples < 0 then Exit;
-
-  FTotal := Samples * 2 * FFormat.Channels;
-  Result := GetMem(FTotal);
-  if Result = nil then Exit;
-
-  Ret := ov_read_ext(FOgg, Result, FTotal, False, 2, True);
-  if Ret < 0 then
-  begin
-    FreeMem(Result);
-    Result := nil;
-  end
-  else
-    FTotal := Ret;
-end;
-
-function TVorbisLoader.LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
+function TVorbisLoader.LoadStream(Stream: TStream): Boolean;
 var
   Ret: clong;
   Info: pvorbis_info;
-  FullBuf: Pointer;
 begin
   Result := False;
 
@@ -192,37 +167,15 @@ begin
   FFormat.SampleRate := Info^.rate;
   FFormat.Channels := Info^.channels;
   FFormat.SampleBits := 16;
+  FOpen := True;
+  FData := Stream;
 
-  if not SStreaming then
-  begin
-    FullBuf := LoadEntireStream();
-
-    if FullBuf = nil then
-    begin
-      e_LogWriteln('OGG: Load(Data) failed: couldn''t allocate for non-streaming chunk');
-      ov_clear(FOgg);
-      FTotal := 0;
-      Exit;
-    end;
-
-    ov_clear(FOgg);
-    Stream.Free();
-
-    FreeMem(FBuf);
-    FBuf := FullBuf;
-  end
-  else
-  begin
-    FTotal := 0;
-    FOpen := True;
-    FData := Stream;
-  end;
-
-  FStreaming := SStreaming;
+  FStreaming := True;
+  FFinished := False;
   Result := True;
 end;
 
-function TVorbisLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TVorbisLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 var
   S: TStream;
 begin
@@ -235,7 +188,8 @@ begin
   Move(Data^, FBuf^, Len);
 
   S := TSFSMemoryStreamRO.Create(FBuf, Len{, True});
-  Result := LoadStream(S, SStreaming);
+  Result := LoadStream(S);
+  FLooping := Loop;
 
   if not Result and (S <> nil) then
   begin
@@ -245,7 +199,7 @@ begin
   end;
 end;
 
-function TVorbisLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TVorbisLoader.Load(FName: string; Loop: Boolean): Boolean;
 var
   S: TStream = nil;
 begin
@@ -253,7 +207,8 @@ begin
 
   try
     S := openDiskFileRO(FName);
-    Result := LoadStream(S, SStreaming);
+    Result := LoadStream(S);
+    FLooping := Loop;
   except
     on E: Exception do
       e_LogWritefln('OGG: ERROR: could not read file `%s`: %s', [FName, E.Message]);
@@ -263,11 +218,17 @@ begin
     S.Free();
 end;
 
-function TVorbisLoader.SetPosition(Pos: LongWord): Boolean;
+function TVorbisLoader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TVorbisLoader.Restart(): Boolean;
 begin
   Result := False;
   if not FOpen or (ov_seekable(FOgg) = 0) then Exit;
-  Result := ov_pcm_seek(FOgg, Pos) = 0;
+  FFinished := False;
+  Result := ov_pcm_seek(FOgg, 0) = 0;
 end;
 
 function TVorbisLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
@@ -275,35 +236,29 @@ var
   Ret: clong;
 begin
   Result := 0;
-  if not FOpen or not FStreaming then Exit;
+  if not FOpen then Exit;
   Ret := ov_read_ext(FOgg, Buf, Len, False, 2, True);
   if Ret < 0 then Exit;
-  if FLooping and (Ret = 0) then
-    ov_pcm_seek(FOgg, 0);
+  if Ret = 0 then
+  begin
+    if FLooping then
+      ov_pcm_seek(FOgg, 0)
+    else
+      FFinished := True;
+  end;
   Result := Ret; 
 end;
 
-function TVorbisLoader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0;
-  if FStreaming or (FTotal = 0) then Exit;
-  Result := FTotal;
-  OutPtr := FBuf;
-end;
-
 procedure TVorbisLoader.Free();
 begin
   if FOpen then
     ov_clear(FOgg);
   if FData <> nil then
     FData.Free();
-  if FBuf <> nil then
-    FreeMem(FBuf);
   FData := nil;
-  FBuf := nil;
   FOpen := False;
-  FTotal := 0;
   FStreaming := False;
+  FFinished := False;
 end;
 
 initialization
index fe9a8a26f9c10ded420e82f183cee4c6167f3c77..84ea42df4c5c12068309086e75f61ff2941c2593 100644 (file)
@@ -24,15 +24,17 @@ type
 
   TWAVLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
   private
     FData: Pointer;
     FDataLen: LongWord;
+    FDataPos: LongWord;
+    FLooping: Boolean;
   end;
 
   TWAVLoaderFactory = class (TSoundLoaderFactory)
@@ -131,7 +133,7 @@ begin
           FFormat.SampleBits := Spec.format and $FF;
         {$ENDIF}
         FFormat.Channels := Spec.channels;
-        FStreaming := False; // never stream wavs
+        FDataPos := 0;
         FDataLen := Len;
         FData := Buf;
       end
@@ -143,7 +145,7 @@ begin
   end
 end;
 
-function TWAVLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TWAVLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
   var
     RW: PSDL_RWops;
 begin
@@ -152,9 +154,11 @@ begin
   if Result = False then
     e_LogWriteln('Could not load WAV: ' + SDL_GetError());
   SDL_RWclose(RW);
+  FStreaming := False;
+  FLooping := Loop;
 end;
 
-function TWAVLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TWAVLoader.Load(FName: string; Loop: Boolean): Boolean;
   var
     RW: PSDL_RWops;
 begin
@@ -171,36 +175,50 @@ begin
     Result := False
   end;
   SDL_RWclose(RW);
+  FStreaming := False;
+  FLooping := Loop;
 end;
 
-function TWAVLoader.SetPosition(Pos: LongWord): Boolean;
+function TWAVLoader.Finished(): Boolean;
 begin
-  Result := False; // makes no sense when not streaming
+  Result := FDataPos >= FDataLen;
 end;
 
-function TWAVLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
+function TWAVLoader.Restart(): Boolean;
 begin
-  if FDataLen < Len then
-    Len := FDataLen;
-  if FData <> nil then
-  begin
-    Move(FData^, Buf^, Len);
-    Result := Len;
-  end
-  else
-    Result := 0;
+  Result := True;
+  FDataPos := 0;
 end;
 
-function TWAVLoader.GetAll(var OutPtr: Pointer): LongWord;
+function TWAVLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
+var
+  OutPos: LongWord;
+  Tx: LongWord;
 begin
-  OutPtr := FData;
-  Result := FDataLen;
+  OutPos := 0;
+  Result := 0;
+
+  while (FDataPos < FDataLen) and (OutPos < Len) do
+  begin
+    Tx := nmin(FDataLen - FDataPos, Len - OutPos);
+    Move((FData + FDataPos)^, (Buf + OutPos)^, Tx);
+
+    FDataPos := FDataPos + Tx;
+    OutPos := OutPos + Tx;
+    Result := Result + Tx;
+
+    if (FDataPos >= FDataLen) and FLooping then
+      FDataPos := 0;
+  end;
 end;
 
 procedure TWAVLoader.Free();
 begin
   if FData <> nil then
     SDL_FreeWAV(FData); // SDL allocates inside the DLL, so we need this
+  FDataPos := 0;
+  FData := nil;
+  FDataLen := 0;
 end;
 
 initialization
index b4c406ce139d3ade999c6697c5b0aa4c55ceba27..3d2ef087b957a08a1fcd69e22c17ef48b5569ea3 100644 (file)
@@ -24,15 +24,17 @@ type
 
   TXMPLoader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
   private
     FXMP: xmp_context;
     FLoaded: Boolean;
+    FLooping: Boolean;
+    FFinished: Boolean;
   end;
 
   TXMPLoaderFactory = class (TSoundLoaderFactory)
@@ -44,7 +46,7 @@ type
 
 implementation
 
-uses sysutils, utils, e_sound, e_log;
+uses sysutils, utils, math, e_sound, e_log;
 
 (* TXMPLoaderFactory *)
 
@@ -85,7 +87,7 @@ end;
 
 (* TXMPLoader *)
 
-function TXMPLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TXMPLoader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 var
   Err: LongInt;
   Interp: LongInt;
@@ -114,6 +116,8 @@ begin
 
     FStreaming := True; // modules are always streaming
     FLoaded := True;
+    FLooping := Loop;
+    FFinished := False;
     Result := True;
   except
     on E: Exception do
@@ -126,7 +130,7 @@ begin
   end;
 end;
 
-function TXMPLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TXMPLoader.Load(FName: string; Loop: Boolean): Boolean;
 var
   Err: LongInt;
   Interp: LongInt;
@@ -154,7 +158,9 @@ begin
     FFormat.Channels := 2;
 
     FStreaming := True; // modules are always streaming
+    FLooping := Loop;
     FLoaded := True;
+    FFinished := False;
     Result := True;
   except
     on E: Exception do
@@ -167,30 +173,33 @@ begin
   end;
 end;
 
-function TXMPLoader.SetPosition(Pos: LongWord): Boolean;
+function TXMPLoader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TXMPLoader.Restart(): Boolean;
 begin
   Result := False;
   if FXMP = nil then Exit;
-  Result := xmp_set_position(FXMP, Pos) = 0;
+  Result := True;
+  FFinished := False;
+  xmp_restart_module(FXMP);
 end;
 
 function TXMPLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
 var
-  LoopN: LongInt;
+  Ret: LongInt;
 begin
   Result := 0;
   if FXMP = nil then Exit;
-  if FLooping then
-    LoopN := 0
-  else
-    LoopN := 1;
-  if xmp_play_buffer(FXMP, Buf, Len, LoopN) = 0 then
-    Result := Len;
-end;
 
-function TXMPLoader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0; // modules are always streaming, so this don't make sense
+  Ret := xmp_play_buffer(FXMP, Buf, Len, IfThen(FLooping, 0, 1));
+
+  if Ret = 0 then
+    Result := Len
+  else if (Ret = -XMP_END) and not FLooping then
+    FFinished := True;
 end;
 
 procedure TXMPLoader.Free();
@@ -206,6 +215,8 @@ begin
     FXMP := nil;
   end;
   FLoaded := False;
+  FLooping := False;
+  FFinished := False;
 end;
 
 initialization
index 6a8c34f3fff4ee44b2a005c00c3862fd9d1d95e3..de042062d862fd800ab108374457738931a52d9d 100644 (file)
@@ -28,6 +28,7 @@ uses
 const
   FLUID_OK = 0;
   FLUID_FAILED = -1;
+  FLUID_PLAYER_DONE = 2;
 
 type
   pfluid_settings_t = pointer;
@@ -53,6 +54,7 @@ function new_fluid_synth(settings: pfluid_settings_t): pfluid_synth_t; cdecl; ex
 function delete_fluid_synth(synth: pfluid_synth_t): cint; cdecl; external {$IFDEF FS_DYNAMIC}fluidlib{$ENDIF};
 function fluid_synth_get_settings(synth: pfluid_synth_t): pfluid_settings_t; cdecl; external {$IFDEF FS_DYNAMIC}fluidlib{$ENDIF};
 function fluid_synth_sfload(synth: pfluid_synth_t; fname: pchar; reset: cint): cint; cdecl; external {$IFDEF FS_DYNAMIC}fluidlib{$ENDIF};
+function fluid_synth_system_reset(synth: pfluid_synth_t): cint; cdecl; external {$IFDEF FS_DYNAMIC}fluidlib{$ENDIF};
 
 function fluid_synth_write_s16(synth: pfluid_synth_t; len: cint; lout: pointer; loff, linc: cint; rout: pointer; roff, rinc: cint): cint; cdecl; external {$IFDEF FS_DYNAMIC}fluidlib{$ENDIF};