From 9193a04898bdd4020400c8acd8b1bcaccbe33dbf Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 10 Mar 2020 23:29:50 +0300 Subject: [PATCH] simplify TSoundLoader interface --- src/engine/e_sound_al.inc | 132 +++++++++++++++-------------- src/engine/e_soundfile.pas | 11 +-- src/engine/e_soundfile_fluid.pas | 40 +++++---- src/engine/e_soundfile_modplug.pas | 60 +++++++------ src/engine/e_soundfile_mp3.pas | 64 ++++++++------ src/engine/e_soundfile_opus.pas | 46 +++++++--- src/engine/e_soundfile_vorbis.pas | 115 ++++++++----------------- src/engine/e_soundfile_wav.pas | 62 +++++++++----- src/engine/e_soundfile_xmp.pas | 51 ++++++----- src/lib/fluidsynth/fluidsynth.pas | 2 + 10 files changed, 311 insertions(+), 272 deletions(-) diff --git a/src/engine/e_sound_al.inc b/src/engine/e_sound_al.inc index 53ee47e..838e158 100644 --- a/src/engine/e_sound_al.inc +++ b/src/engine/e_sound_al.inc @@ -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 diff --git a/src/engine/e_soundfile.pas b/src/engine/e_soundfile.pas index 4ba5cd6..c508593 100644 --- a/src/engine/e_soundfile.pas +++ b/src/engine/e_soundfile.pas @@ -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 diff --git a/src/engine/e_soundfile_fluid.pas b/src/engine/e_soundfile_fluid.pas index 5235f2c..fba2726 100644 --- a/src/engine/e_soundfile_fluid.pas +++ b/src/engine/e_soundfile_fluid.pas @@ -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 diff --git a/src/engine/e_soundfile_modplug.pas b/src/engine/e_soundfile_modplug.pas index 0a2c280..600512b 100644 --- a/src/engine/e_soundfile_modplug.pas +++ b/src/engine/e_soundfile_modplug.pas @@ -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; diff --git a/src/engine/e_soundfile_mp3.pas b/src/engine/e_soundfile_mp3.pas index 65b47e6..c49f376 100644 --- a/src/engine/e_soundfile_mp3.pas +++ b/src/engine/e_soundfile_mp3.pas @@ -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 diff --git a/src/engine/e_soundfile_opus.pas b/src/engine/e_soundfile_opus.pas index ab79951..e0b41e3 100644 --- a/src/engine/e_soundfile_opus.pas +++ b/src/engine/e_soundfile_opus.pas @@ -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 diff --git a/src/engine/e_soundfile_vorbis.pas b/src/engine/e_soundfile_vorbis.pas index d003fb6..d2d364b 100644 --- a/src/engine/e_soundfile_vorbis.pas +++ b/src/engine/e_soundfile_vorbis.pas @@ -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 diff --git a/src/engine/e_soundfile_wav.pas b/src/engine/e_soundfile_wav.pas index fe9a8a2..84ea42d 100644 --- a/src/engine/e_soundfile_wav.pas +++ b/src/engine/e_soundfile_wav.pas @@ -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 diff --git a/src/engine/e_soundfile_xmp.pas b/src/engine/e_soundfile_xmp.pas index b4c406c..3d2ef08 100644 --- a/src/engine/e_soundfile_xmp.pas +++ b/src/engine/e_soundfile_xmp.pas @@ -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 diff --git a/src/lib/fluidsynth/fluidsynth.pas b/src/lib/fluidsynth/fluidsynth.pas index 6a8c34f..de04206 100644 --- a/src/lib/fluidsynth/fluidsynth.pas +++ b/src/lib/fluidsynth/fluidsynth.pas @@ -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}; -- 2.29.2