summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: d194bf7)
raw | patch | inline | side by side (parent: d194bf7)
author | fgsfds <pvt.fgsfds@gmail.com> | |
Sun, 1 Sep 2019 00:00:27 +0000 (03:00 +0300) | ||
committer | fgsfds <pvt.fgsfds@gmail.com> | |
Sun, 1 Sep 2019 00:00:27 +0000 (03:00 +0300) |
src/engine/e_soundfile_modplug.pas | [new file with mode: 0644] | patch | blob |
src/lib/modplug/modplug.pas | [new file with mode: 0644] | patch | blob |
diff --git a/src/engine/e_soundfile_modplug.pas b/src/engine/e_soundfile_modplug.pas
--- /dev/null
@@ -0,0 +1,191 @@
+(* Copyright (C) Doom 2D: Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$INCLUDE ../shared/a_modes.inc}
+unit e_soundfile_modplug;
+
+interface
+
+uses e_soundfile, modplug;
+
+type
+ // a module loader that uses libxmp-lite
+
+ 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 FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
+ function GetAll(var OutPtr: Pointer): LongWord; override;
+ procedure Free(); override;
+ private
+ FFile: PModPlugFile;
+ end;
+
+ TModPlugLoaderFactory = class (TSoundLoaderFactory)
+ function MatchHeader(Data: Pointer; Len: LongWord): Boolean; override;
+ function MatchExtension(FName: string): Boolean; override;
+ function GetLoader(): TSoundLoader; override;
+ end;
+
+implementation
+
+uses sysutils, utils, e_sound, e_log, classes;
+
+var
+ Settings: ModPlug_Settings = (
+ mFlags : MODPLUG_ENABLE_OVERSAMPLING or MODPLUG_ENABLE_NOISE_REDUCTION;
+ mChannels : 2;
+ mBits : 16;
+ mFrequency : 44100;
+ mResamplingMode : MODPLUG_RESAMPLE_LINEAR;
+ mStereoSeparation : 128;
+ mMaxMixChannels : 32;
+ mReverbDepth : 0;
+ mReverbDelay : 0;
+ mBassAmount : 0;
+ mBassRange : 0;
+ mSurroundDepth : 0;
+ mSurroundDelay : 0;
+ mLoopCount : -1;
+ );
+
+(* TModPlugLoaderFactory *)
+
+function TModPlugLoaderFactory.MatchHeader(Data: Pointer; Len: LongWord): Boolean;
+var
+ Mpf: PModPlugFile;
+begin
+ // HACK: there's no "test" function in modplug, so just try to load that shit
+ Result := False;
+
+ Mpf := ModPlug_Load(Data, Len);
+ if Mpf = nil then Exit;
+ ModPlug_Unload(Mpf);
+
+ Result := True;
+end;
+
+function TModPlugLoaderFactory.MatchExtension(FName: string): Boolean;
+var
+ Ext: string;
+begin
+ Ext := GetFilenameExt(FName);
+ Result := (Ext = '.it') or (Ext = '.xm') or (Ext = '.mod') or (Ext = '.s3m');
+end;
+
+function TModPlugLoaderFactory.GetLoader(): TSoundLoader;
+begin
+ ModPlug_SetSettings(@Settings); // update settings just in case
+ Result := TModPlugLoader.Create();
+end;
+
+(* TModPlugLoader *)
+
+function TModPlugLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+begin
+ Result := False;
+
+ FFile := ModPlug_Load(Data, Len);
+ if FFile = nil then
+ begin
+ e_LogWriteln('ModPlug: ERROR: ModPlug_Load failed');
+ Exit;
+ end;
+
+ FFormat.SampleRate := 44100;
+ FFormat.SampleBits := 16;
+ FFormat.Channels := 2;
+ FStreaming := True; // modules are always streaming
+
+ Result := True;
+end;
+
+function TModPlugLoader.Load(FName: string; SStreaming: Boolean): Boolean;
+var
+ S: TStream = nil;
+ Data: Pointer;
+ Len: LongInt;
+begin
+ Result := False;
+
+ try
+ S := openDiskFileRO(FName);
+ // ayy just read the entire file
+ Data := GetMem(S.Size);
+ if Data = nil then
+ raise Exception.Create('out of memory');
+ Len := S.Read(Data^, S.Size);
+ if Len < 0 then
+ raise Exception.Create('what the fuck');
+ Result := Load(Data, Len, SStreaming)
+ except
+ on E: Exception do
+ e_LogWritefln('ModPlug: ERROR: could not read file `%s`: %s', [FName, E.Message]);
+ end;
+
+ if Data <> nil then FreeMem(Data);
+ if S <> nil then S.Free();
+end;
+
+function TModPlugLoader.SetPosition(Pos: LongWord): Boolean;
+begin
+ Result := False;
+ if FFile = nil then Exit;
+ ModPlug_Seek(FFile, Pos);
+ Result := True;
+end;
+
+function TModPlugLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
+var
+ Cnt: LongInt;
+begin
+ Result := 0;
+ if FFile = nil then Exit;
+
+ 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;
+
+function TModPlugLoader.GetAll(var OutPtr: Pointer): LongWord;
+begin
+ Result := 0; // modules are always streaming, so this don't make sense
+end;
+
+procedure TModPlugLoader.Free();
+begin
+ if FFile <> nil then
+ begin
+ ModPlug_Unload(FFile);
+ FFile := nil;
+ end;
+end;
+
+initialization
+ e_AddSoundLoader(TModPlugLoaderFactory.Create());
+end.
diff --git a/src/lib/modplug/modplug.pas b/src/lib/modplug/modplug.pas
--- /dev/null
@@ -0,0 +1,134 @@
+{
+ Translation of the libmodplug headers for FreePascal
+ Copyright (C) 2006 by Ivo Steinmann
+}
+
+(*
+ * This source code is public domain.
+ *
+ * Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
+ *)
+
+unit modplug;
+
+{$MODE OBJFPC}
+{$PACKRECORDS C}
+
+interface
+
+uses
+ ctypes;
+
+{$IFDEF WINDOWS}
+ {$IFNDEF LIBMODPLUG_PINDOZE_STATIC}
+ {$DEFINE MP_DYNAMIC}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF MP_DYNAMIC}
+const
+{$IF DEFINED(WINDOWS)}
+ modpluglib = 'libmodplug.dll';
+{$ELSEIF DEFINED(UNIX)}
+ modpluglib = 'libmodplug.so';
+{$ELSE}
+ {$MESSAGE ERROR 'MP_DYNAMIC not supported'}
+{$IFEND}
+{$ELSE}
+ {$LINKLIB stdc++} // is this necessary?
+ {$LINKLIB modplug}
+{$ENDIF}
+
+
+type
+ PModPlugFile = Pointer;
+ ModPlugFile = record
+ end;
+
+(* Load a mod file. [data] should point to a block of memory containing the complete
+ * file, and [size] should be the size of that block.
+ * Return the loaded mod file on success, or NULL on failure. *)
+function ModPlug_Load(data: pointer; size: cint): PModPlugFile; cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+(* Unload a mod file. *)
+procedure ModPlug_Unload(_file: PModPlugFile); cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+(* Read sample data into the buffer. Returns the number of bytes read. If the end
+ * of the mod has been reached, zero is returned. *)
+function ModPlug_Read(_file: PModPlugFile; buffer: pointer; size: cint): cint; cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+(* Get the name of the mod. The returned buffer is stored within the ModPlugFile
+ * structure and will remain valid until you unload the file. *)
+function ModPlug_GetName(_file: PModPlugFile): pcchar; cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+(* Get the length of the mod, in milliseconds. Note that this result is not always
+ * accurate, especially in the case of mods with loops. *)
+function ModPlug_GetLength(_file: PModPlugFile): cint; cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+(* Seek to a particular position in the song. Note that seeking and MODs don't mix very
+ * well. Some mods will be missing instruments for a short time after a seek, as ModPlug
+ * does not scan the sequence backwards to find out which instruments were supposed to be
+ * playing at that time. (Doing so would be difficult and not very reliable.) Also,
+ * note that seeking is not very exact in some mods -- especially those for which
+ * ModPlug_GetLength() does not report the full length. *)
+procedure ModPlug_Seek(_file: PModPlugFile; millisecond: cint); cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+
+const
+// _ModPlug_Flags
+ MODPLUG_ENABLE_OVERSAMPLING = 1 shl 0; (* Enable oversampling (highly recommended) *)
+ MODPLUG_ENABLE_NOISE_REDUCTION = 1 shl 1; (* Enable noise reduction *)
+ MODPLUG_ENABLE_REVERB = 1 shl 2; (* Enable reverb *)
+ MODPLUG_ENABLE_MEGABASS = 1 shl 3; (* Enable megabass *)
+ MODPLUG_ENABLE_SURROUND = 1 shl 4; (* Enable surround sound. *)
+
+// _ModPlug_ResamplingMode
+ MODPLUG_RESAMPLE_NEAREST = 0; (* No interpolation (very fast, extremely bad sound quality) *)
+ MODPLUG_RESAMPLE_LINEAR = 1; (* Linear interpolation (fast, good quality) *)
+ MODPLUG_RESAMPLE_SPLINE = 2; (* Cubic spline interpolation (high quality) *)
+ MODPLUG_RESAMPLE_FIR = 3; (* 8-tap fir filter (extremely high quality) *)
+
+type
+ PModPlug_Settings = ^ModPlug_Settings;
+ ModPlug_Settings = record
+ mFlags : cint; (* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed *)
+ mChannels : cint; (* Number of channels - 1 for mono or 2 for stereo *)
+ mBits : cint; (* Bits per sample - 8, 16, or 32 *)
+ mFrequency : cint; (* Sampling rate - 11025, 22050, or 44100 *)
+ mResamplingMode : cint; (* One of MODPLUG_RESAMPLE_*, above *)
+ mStereoSeparation : cint; (* 1-256 *)
+ mMaxMixChannels : cint; (* Maximum number of mixing channels, 32-256 *)
+ mReverbDepth : cint; (* Reverb level 0(quiet)-100(loud) *)
+ mReverbDelay : cint; (* Reverb delay in ms, usually 40-200ms *)
+ mBassAmount : cint; (* XBass level 0(quiet)-100(loud) *)
+ mBassRange : cint; (* XBass cutoff in Hz 10-100 *)
+ mSurroundDepth : cint; (* Surround level 0(quiet)-100(heavy) *)
+ mSurroundDelay : cint; (* Surround delay in ms, usually 5-40ms *)
+ mLoopCount : cint; (* Number of times to loop. Zero prevents looping. -1 loops forever. *)
+ end;
+
+(* Get and set the mod decoder settings. All options, except for channels, bits-per-sample,
+ * sampling rate, and loop count, will take effect immediately. Those options which don't
+ * take effect immediately will take effect the next time you load a mod. *)
+procedure ModPlug_GetSettings(settings: PModPlug_Settings); cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+procedure ModPlug_SetSettings(const settings: PModPlug_Settings); cdecl; external {$IFDEF MP_DYNAMIC}modpluglib{$ENDIF};
+
+implementation
+
+// TODO: why the fuck does this exist here
+
+(*
+
+function cppNew(s: cint): pointer; cdecl; public; alias : '_Znaj'; alias : '_Znwj';
+begin
+ GetMem(Result, s);
+end;
+
+procedure cppDelete(p: pointer); cdecl; public; alias : '_ZdlPv'; alias : '_ZdaPv';
+begin
+ FreeMem(p);
+end;
+
+*)
+
+end.