Files
package-zearch-temp/UOS/examples/uos_libxmp.pas
Indrajith K L 6a1d80d3b8 Implements Module Player working in Form
* Implements Embedding .xm music in Final Application
2025-06-02 15:15:16 +05:30

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.