Implements Module Player working in Form
* Implements Embedding .xm music in Final Application
This commit is contained in:
428
UOS/examples/uos_libxmp.pas
Normal file
428
UOS/examples/uos_libxmp.pas
Normal file
@@ -0,0 +1,428 @@
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user