429 lines
13 KiB
ObjectPascal
429 lines
13 KiB
ObjectPascal
unit uos_libxmp;
|
|
{This unit is part of United Openlibraries of Sound (uos)}
|
|
|
|
{This is the Pascal Wrapper + Dynamic loading of libxmp library.
|
|
Load library with xmp_load() and release with xmp_unload().
|
|
License : modified LGPL.
|
|
by Fred vS | fiens@hotmail.com | 2024}
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$PACKRECORDS C}
|
|
|
|
interface
|
|
|
|
uses
|
|
dynlibs,
|
|
sysutils,
|
|
CTypes;
|
|
|
|
const
|
|
XMP_VERSION = '4.6.0';
|
|
XMP_VERCODE = $040600;
|
|
XMP_VER_MAJOR = 4;
|
|
XMP_VER_MINOR = 6;
|
|
XMP_VER_RELEASE = 0;
|
|
|
|
const
|
|
{$IFDEF windows}
|
|
XMP_LIB_NAME = 'libxmp.dll';
|
|
{$ENDIF}
|
|
|
|
{$IFDEF unix}
|
|
{$IFDEF darwin}
|
|
XMP_LIB_NAME = 'libxmp.dylib';
|
|
{$ELSE}
|
|
XMP_LIB_NAME = 'libxmp.so.4.6.0';
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
const
|
|
XMP_NAME_SIZE = 64;
|
|
|
|
// Note event constants
|
|
XMP_KEY_OFF = $81;
|
|
XMP_KEY_CUT = $82;
|
|
XMP_KEY_FADE = $83;
|
|
|
|
// Sample format flags
|
|
XMP_FORMAT_8BIT = 1 shl 0;
|
|
XMP_FORMAT_UNSIGNED = 1 shl 1;
|
|
XMP_FORMAT_MONO = 1 shl 2;
|
|
|
|
// Player parameters
|
|
XMP_PLAYER_AMP = 0;
|
|
XMP_PLAYER_MIX = 1;
|
|
XMP_PLAYER_INTERP = 2;
|
|
XMP_PLAYER_DSP = 3;
|
|
XMP_PLAYER_FLAGS = 4;
|
|
XMP_PLAYER_CFLAGS = 5;
|
|
XMP_PLAYER_SMPCTL = 6;
|
|
XMP_PLAYER_VOLUME = 7;
|
|
XMP_PLAYER_STATE = 8;
|
|
XMP_PLAYER_SMIX_VOLUME = 9;
|
|
XMP_PLAYER_DEFPAN = 10;
|
|
XMP_PLAYER_MODE = 11;
|
|
XMP_PLAYER_MIXER_TYPE = 12;
|
|
XMP_PLAYER_VOICES = 13;
|
|
|
|
// Interpolation types
|
|
XMP_INTERP_NEAREST = 0;
|
|
XMP_INTERP_LINEAR = 1;
|
|
XMP_INTERP_SPLINE = 2;
|
|
|
|
// DSP effect types
|
|
XMP_DSP_LOWPASS = 1 shl 0;
|
|
XMP_DSP_ALL = XMP_DSP_LOWPASS;
|
|
|
|
// Player state
|
|
XMP_STATE_UNLOADED = 0;
|
|
XMP_STATE_LOADED = 1;
|
|
XMP_STATE_PLAYING = 2;
|
|
|
|
// Player flags
|
|
XMP_FLAGS_VBLANK = 1 shl 0;
|
|
XMP_FLAGS_FX9BUG = 1 shl 1;
|
|
XMP_FLAGS_FIXLOOP = 1 shl 2;
|
|
XMP_FLAGS_A500 = 1 shl 3;
|
|
|
|
// Player modes
|
|
XMP_MODE_AUTO = 0;
|
|
XMP_MODE_MOD = 1;
|
|
XMP_MODE_NOISETRACKER = 2;
|
|
XMP_MODE_PROTRACKER = 3;
|
|
XMP_MODE_S3M = 4;
|
|
XMP_MODE_ST3 = 5;
|
|
XMP_MODE_ST3GUS = 6;
|
|
XMP_MODE_XM = 7;
|
|
XMP_MODE_FT2 = 8;
|
|
XMP_MODE_IT = 9;
|
|
XMP_MODE_ITSMP = 10;
|
|
|
|
// Mixer types
|
|
XMP_MIXER_STANDARD = 0;
|
|
XMP_MIXER_A500 = 1;
|
|
XMP_MIXER_A500F = 2;
|
|
|
|
// Sample flags
|
|
XMP_SMPCTL_SKIP = 1 shl 0;
|
|
|
|
// Limits
|
|
XMP_MAX_KEYS = 121;
|
|
XMP_MAX_ENV_POINTS = 32;
|
|
XMP_MAX_MOD_LENGTH = 256;
|
|
XMP_MAX_CHANNELS = 64;
|
|
XMP_MAX_SRATE = 49170;
|
|
XMP_MIN_SRATE = 4000;
|
|
XMP_MIN_BPM = 20;
|
|
XMP_MAX_FRAMESIZE = 5 * XMP_MAX_SRATE * 2 div XMP_MIN_BPM;
|
|
|
|
// Error codes
|
|
XMP_END = 1;
|
|
XMP_ERROR_INTERNAL = 2;
|
|
XMP_ERROR_FORMAT = 3;
|
|
XMP_ERROR_LOAD = 4;
|
|
XMP_ERROR_DEPACK = 5;
|
|
XMP_ERROR_SYSTEM = 6;
|
|
XMP_ERROR_INVALID = 7;
|
|
XMP_ERROR_STATE = 8;
|
|
|
|
type
|
|
xmp_context = PChar;
|
|
|
|
type
|
|
xmp_channel = record
|
|
pan: integer; // Pan de canal (0x80 centrum)
|
|
vol: integer; // Volume of canal
|
|
flg: integer; // Flags of canal
|
|
end;
|
|
|
|
xmp_pattern = record
|
|
rows: integer;
|
|
index: array[1..1] of integer;
|
|
end;
|
|
|
|
xmp_event = record
|
|
note: byte;
|
|
ins: byte;
|
|
vol: byte;
|
|
fxt: byte;
|
|
fxp: byte;
|
|
f2t: byte;
|
|
f2p: byte;
|
|
_flag: byte;
|
|
end;
|
|
|
|
xmp_track = record
|
|
rows: integer;
|
|
event: array[1..1] of xmp_event;
|
|
end;
|
|
|
|
xmp_envelope = record
|
|
flg: integer;
|
|
npt: integer;
|
|
scl: integer;
|
|
sus: integer;
|
|
sue: integer;
|
|
lps: integer;
|
|
lpe: integer;
|
|
Data: array[1..XMP_MAX_ENV_POINTS * 2] of smallint;
|
|
end;
|
|
|
|
xmp_subinstrument = record
|
|
vol: integer;
|
|
gvl: integer;
|
|
pan: integer;
|
|
xpo: integer;
|
|
fin: integer;
|
|
vwf: integer;
|
|
vde: integer;
|
|
vra: integer;
|
|
vsw: integer;
|
|
rvv: integer;
|
|
sid: integer;
|
|
nna: integer;
|
|
dct: integer;
|
|
dca: integer;
|
|
ifc: integer;
|
|
ifr: integer;
|
|
end;
|
|
|
|
xmp_instrument = record
|
|
Name: array[0..31] of char;
|
|
vol: integer;
|
|
nsm: integer;
|
|
rls: integer;
|
|
aei: xmp_envelope;
|
|
pei: xmp_envelope;
|
|
fei: xmp_envelope;
|
|
map: array[0..XMP_MAX_KEYS] of record
|
|
ins: byte;
|
|
xpo: shortint;
|
|
end;
|
|
sub: ^xmp_subinstrument;
|
|
extra: Pointer;
|
|
end;
|
|
|
|
xmp_sample = record
|
|
Name: array[0..31] of char;
|
|
len: integer;
|
|
lps: integer;
|
|
lpe: integer;
|
|
flg: integer;
|
|
Data: PByte;
|
|
end;
|
|
|
|
xmp_sequence = record
|
|
entry_point: integer;
|
|
duration: integer;
|
|
end;
|
|
|
|
xmp_module = record
|
|
Name: array[0..XMP_NAME_SIZE - 1] of char;
|
|
typ: array[0..XMP_NAME_SIZE - 1] of char;
|
|
pat: integer;
|
|
trk: integer;
|
|
chn: integer;
|
|
ins: integer;
|
|
smp: integer;
|
|
spd: integer;
|
|
bpm: integer;
|
|
len: integer;
|
|
rst: integer;
|
|
gvl: integer;
|
|
xxp: ^xmp_pattern;
|
|
xxt: ^xmp_track;
|
|
xxi: ^xmp_instrument;
|
|
xxs: ^xmp_sample;
|
|
xxc: array[0..XMP_MAX_CHANNELS - 1] of xmp_channel;
|
|
xxo: array[0..XMP_MAX_MOD_LENGTH] of byte;
|
|
end;
|
|
|
|
xmp_test_info = record
|
|
Name: array[0..XMP_NAME_SIZE - 1] of char;
|
|
type_: array[0..XMP_NAME_SIZE - 1] of char;
|
|
end;
|
|
|
|
xmp_module_info = record
|
|
md5: array[0..15] of byte;
|
|
vol_base: integer;
|
|
module: ^xmp_module;
|
|
comment: PChar;
|
|
num_sequences: integer;
|
|
seq_data: ^xmp_sequence;
|
|
end;
|
|
|
|
xmp_channel_info = record
|
|
period: longword;
|
|
position: longword;
|
|
pitchbend: smallint;
|
|
note: byte;
|
|
instrument: byte;
|
|
sample: byte;
|
|
volume: byte;
|
|
pan: byte;
|
|
reserved: byte;
|
|
event: xmp_event;
|
|
end;
|
|
|
|
xmp_frame_info = record
|
|
pos: integer;
|
|
pattern: integer;
|
|
row: integer;
|
|
num_rows: integer;
|
|
frame: integer;
|
|
speed: integer;
|
|
bpm: integer;
|
|
time: integer;
|
|
total_time: integer;
|
|
frame_time: integer;
|
|
buffer: Pointer;
|
|
buffer_size: integer;
|
|
total_size: integer;
|
|
volume: integer;
|
|
loop_count: integer;
|
|
virt_channels: integer;
|
|
virt_used: integer;
|
|
sequence: integer;
|
|
channel_info: array[0..XMP_MAX_CHANNELS - 1] of xmp_channel_info;
|
|
end;
|
|
|
|
xmp_callbacks = record
|
|
read_func: function(dest: Pointer; len, nmemb: longword; priv: Pointer): longword;
|
|
seek_func: function(priv: Pointer; offset: longint; whence: integer): integer;
|
|
tell_func: function(priv: Pointer): longint;
|
|
close_func: function(priv: Pointer): integer;
|
|
end;
|
|
|
|
{ Dynamic load : Vars that will hold our dynamically loaded functions...
|
|
|
|
*************************** functions ******************************* }
|
|
|
|
var
|
|
xmp_create_context: function: pointer; cdecl;
|
|
xmp_free_context: procedure(ctx: Pointer); cdecl;
|
|
xmp_load_module: function(ctx: Pointer; const filename: PChar): Integer; cdecl;
|
|
xmp_load_module_from_memory: function(ctx: xmp_context; const Data: Pointer; size: longint): integer; cdecl;
|
|
xmp_load_module_from_file: function(ctx: xmp_context; file_: Pointer; size: longint): integer; cdecl;
|
|
xmp_load_module_from_callbacks: function(ctx: xmp_context; file_: Pointer; callbacks: xmp_callbacks): integer; cdecl;
|
|
xmp_test_module: function(const filename: PChar; info: xmp_test_info): Integer; cdecl;
|
|
xmp_release_module: procedure(ctx: xmp_context); cdecl;
|
|
xmp_start_player: function(ctx: xmp_context; rate: Integer; flags: Integer): Integer; cdecl;
|
|
xmp_play_buffer: function(ctx: xmp_context; buffer: Pointer; size: Integer; loop: Integer): Integer; cdecl;
|
|
xmp_get_frame_info: procedure(ctx: xmp_context; var info: xmp_frame_info); cdecl;
|
|
xmp_end_player: procedure(ctx: xmp_context); cdecl;
|
|
xmp_get_module_info: procedure(ctx: xmp_context; var info: xmp_module_info); cdecl;
|
|
xmp_get_format_list: function(): PAnsiChar; cdecl;
|
|
xmp_stop_module: procedure(ctx: xmp_context); cdecl;
|
|
xmp_restart_module: procedure(ctx: xmp_context); cdecl;
|
|
xmp_channel_vol: function(ctx: xmp_context; channel: Integer; volume: Integer): Integer; cdecl;
|
|
xmp_set_player: function(ctx: xmp_context; param: Integer; value: Integer): Integer; cdecl;
|
|
|
|
// Not used yet...
|
|
//function xmp_test_module_from_memory(const data: Pointer; size: LongInt; info: xmp_test_info): Integer; cdecl; external 'xmp';
|
|
//function xmp_test_module_from_file(file_: Pointer; info: xmp_test_info): Integer; cdecl; external 'xmp';
|
|
//function xmp_test_module_from_callbacks(file_: Pointer; callbacks: xmp_callbacks; info: xmp_test_info): Integer; cdecl; external 'xmp';
|
|
//procedure xmp_scan_module(ctx: xmp_context); cdecl; external 'xmp';
|
|
//function xmp_play_frame(ctx: xmp_context): Integer; cdecl; external 'xmp';
|
|
//procedure xmp_inject_event(ctx: xmp_context; channel: Integer; var event: xmp_event); cdecl; external 'xmp';//function xmp_next_position(ctx: xmp_context): Integer; cdecl; external 'xmp';
|
|
//function xmp_prev_position(ctx: xmp_context): Integer; cdecl; external 'xmp';
|
|
//function xmp_set_position(ctx: xmp_context; pos: Integer): Integer; cdecl; external 'xmp';
|
|
//function xmp_set_row(ctx: xmp_context; row: Integer): Integer; cdecl; external 'xmp';
|
|
//function xmp_set_tempo_factor(ctx: xmp_context; factor: Double): Integer; cdecl; external 'xmp';//function xmp_seek_time(ctx: xmp_context; time: Integer): Integer; cdecl; external 'xmp';
|
|
//function xmp_channel_mute(ctx: xmp_context; channel: Integer; mute: Integer): Integer; cdecl; external 'xmp';//function xmp_get_player(ctx: xmp_context; param: Integer): Integer; cdecl; external 'xmp';
|
|
//function xmp_set_instrument_path(ctx: xmp_context; const path: PChar): Integer; cdecl; external 'xmp';
|
|
|
|
|
|
{Special function for dynamic loading of lib ...}
|
|
|
|
var
|
|
xmp_Handle: TLibHandle = dynlibs.NilHandle;
|
|
{$if defined(cpu32) and defined(windows)} // try load dependency if not in /windows/system32/
|
|
gc_Handle :TLibHandle=dynlibs.NilHandle;
|
|
{$endif}
|
|
var
|
|
ReferenceCounter: cardinal = 0; // Reference counter
|
|
|
|
function xmp_IsLoaded: Boolean; inline;
|
|
|
|
function xmp_Load(const libfilename: string): Boolean; // load the lib
|
|
|
|
procedure xmp_Unload(); // unload and frees the lib from memory : do not forget to call it before close application.
|
|
|
|
implementation
|
|
|
|
function xmp_IsLoaded: boolean;
|
|
begin
|
|
Result := (xmp_Handle <> dynlibs.NilHandle);
|
|
end;
|
|
|
|
Function xmp_Load(const libfilename:string) :boolean;
|
|
var
|
|
thelib, thelibgcc: string;
|
|
begin
|
|
Result := False;
|
|
if xmp_Handle<>0 then
|
|
begin
|
|
Inc(ReferenceCounter);
|
|
result:=true {is it already there ?}
|
|
end else
|
|
begin
|
|
{$if defined(cpu32) and defined(windows)}
|
|
if Length(libfilename) = 0 then thelibgcc := 'libgcc_s_dw2-1.dll' else
|
|
thelibgcc := IncludeTrailingBackslash(ExtractFilePath(libfilename)) + 'libgcc_s_dw2-1.dll';
|
|
gc_Handle:= DynLibs.SafeLoadLibrary(thelibgcc);
|
|
{$endif}
|
|
|
|
{go & load the library}
|
|
if Length(libfilename) = 0 then thelib := XMP_LIB_NAME else thelib := libfilename;
|
|
xmp_Handle:=DynLibs.LoadLibrary(thelib); // obtain the handle we want
|
|
if xmp_Handle <> DynLibs.NilHandle then
|
|
begin {now we tie the functions to the VARs from above}
|
|
|
|
Pointer(xmp_create_context):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_create_context'));
|
|
Pointer(xmp_free_context):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_free_context'));
|
|
Pointer(xmp_load_module):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_load_module'));
|
|
Pointer(xmp_load_module_from_memory):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_load_module_from_memory'));
|
|
Pointer(xmp_load_module_from_file):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_load_module_from_file'));
|
|
Pointer(xmp_load_module_from_callbacks):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_load_module_from_callbacks'));
|
|
Pointer(xmp_test_module):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_test_module'));
|
|
Pointer(xmp_release_module):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_release_module'));
|
|
Pointer(xmp_start_player):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_start_player'));
|
|
Pointer(xmp_play_buffer):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_play_buffer'));
|
|
Pointer(xmp_get_frame_info):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_get_frame_info'));
|
|
Pointer(xmp_end_player):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_end_player'));
|
|
Pointer(xmp_get_module_info):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_get_module_info'));
|
|
Pointer(xmp_get_format_list):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_get_format_list'));
|
|
Pointer(xmp_stop_module):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_stop_module'));
|
|
Pointer(xmp_restart_module):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_restart_module'));
|
|
Pointer(xmp_channel_vol):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_channel_vol'));
|
|
Pointer(xmp_set_player):=DynLibs.GetProcedureAddress(xmp_Handle,PChar('xmp_set_player'));
|
|
end;
|
|
Result := xmp_IsLoaded;
|
|
ReferenceCounter:=1;
|
|
end;
|
|
|
|
end;
|
|
|
|
Procedure xmp_Unload;
|
|
begin
|
|
if ReferenceCounter > 0 then
|
|
dec(ReferenceCounter);
|
|
if ReferenceCounter > 0 then
|
|
exit;
|
|
if xmp_IsLoaded then
|
|
begin
|
|
DynLibs.UnloadLibrary(xmp_Handle);
|
|
xmp_Handle:=DynLibs.NilHandle;
|
|
{$if defined(cpu32) and defined(windows)}
|
|
if gc_Handle <> DynLibs.NilHandle then begin
|
|
DynLibs.UnloadLibrary(gc_Handle);
|
|
gc_Handle:=DynLibs.NilHandle;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|
|
|