Recent

Author Topic: Open MPT Library  (Read 1453 times)

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Open MPT Library
« on: January 30, 2025, 06:38:29 pm »
Hi,

I would share another module player library with you, OpenMpt Library; The library was compiled with visual studio 2022 for
X64 platform, it contains over 100 functions not all implémented;

Supported formats:   
    Composer 667 (.667)
    Composer 669 / UNIS 669 (.669)
    ASYLUM Music Format / Advanced Music Format (.amf / .dmf)
    Extreme’s Tracker / Velvet Studio (.ams)
    Composer 670 / CDFM (.c67)
    Digi Booster Pro (.dbm)
    Digi Booster (.digi)
    X-Tracker (.dmf)
    DSIK (.dsm)
    Dynamic Studio (.dsm)
    Digital Symphony (.dsym)
    Digital Tracker / Digital Home Studio (.dtm)
    Farandole Composer (.far)
    Davey W. Taylor’s FM Tracker (.fmt)
    General Digital Music (.gdm)
    Graoumf Tracker 1 (.gtk / .gt2)
    Ice Tracker / SoundTracker 2.6 (.ice / .st26)
    Imago Orpheus (.imf)
    Impulse Tracker Project (.itp) – legacy OpenMPT format with instruments stored in external files rather than directly in the module
    Jazz Jackrabbit 2 Music (.j2b)
    SoundTracker and compatible (.m15 / .stk)
    DigiTrakker (.mdl)
    OctaMED (.med)
    MO3 (.mo3)
    MadTracker 2 (.mt2)
    MultiTracker (.mtm)
    Psycho Pinball / Micro Machines 2 music format (.mus)
    Oktalyzer (.okt)
    OggMod-compressed XM files (.oxm)
    Epic Megagames MASI (.psm)
    Disorder Tracker 2 (.plm)
    ProTracker 3.6 IFF (.pt36)
    PolyTracker (.ptm)
    SoundFX / MultiMedia Sound (.sfx / .sfx2 / .mms)
    Scream Tracker 2 (.stm)
    Scream Tracker Music Interface Kit (.stx)
    Soundtracker Pro II (.stp)
    Symphonie / Symphonie Pro (.symmod)
    UltraTracker (.ult)
    Unreal Music (.umx) only from Unreal (Tournament 1), Deus Ex and Jazz Jackrabbit 3D
    Mod's Grave (.wow)
    Astroidea XMF (.xmf)

Just tested Old Obarski Soundracker and Oktalyzer;

The main unit like always ;
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  9.   ExtCtrls, Spin, mmsystem, windows, openmpt;
  10.  
  11. const
  12.  
  13.   Channels = 1;
  14.   BitsPerSample = 16;
  15.   SampleRate = 44100; // number of samples per second
  16.   BufSize = 8192 ; //   multiple of 2
  17.   BufferCount = 2;
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     FloatSpinEdit1: TFloatSpinEdit;
  25.     Label1: TLabel;
  26.     Label2: TLabel;
  27.     Memo1: TMemo;
  28.     Timer1: TTimer;
  29.     TrackBar1: TTrackBar;
  30.     procedure FloatSpinEdit1Change(Sender: TObject);
  31.     procedure FormCreate(Sender: TObject);
  32.     procedure FormShow(Sender: TObject);
  33.     procedure Timer1Timer(Sender: TObject);
  34.     procedure TrackBar1Change(Sender: TObject);
  35.  
  36.   private
  37.  
  38.     buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
  39.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  40.      currentBuffer: Integer;
  41.  
  42.   public
  43.  
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.   waveOut: HWAVEOUT;
  49.   waveHeader: TWaveHdr;
  50.   ok_flag : boolean = false;
  51.  
  52.   o_mod : Pointer;
  53.   o_mod_paramindex  : Integer;
  54.   o_mod_info : String;
  55.   o_mod_repeat : Integer;
  56.   o_mod_duration, o_mod_position,o_mod_set_pos  : Single;
  57.  
  58.   ctl : POpenMPTModuleInitialCtl;
  59.   lgfct  : Pointer;
  60.   lgusr : Pointer;
  61.   sc  : Pointer;
  62.  
  63. implementation
  64.  
  65. {$R *.lfm}
  66.  
  67. { TForm1 }
  68.  
  69. procedure FillBuff(bufferIndex: Integer);
  70. var
  71.   GenSmp, NumSmp: Integer;
  72. begin
  73.   if ok_flag then
  74.   begin
  75.  
  76.     bufferIndex := Form1.currentBuffer;
  77.     NumSmp := BufSize div (Channels * (BitsPerSample div 16));
  78.    // GenSmp := openmpt_module_read_mono(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0]);
  79.    GenSmp := openmpt_module_read_stereo(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0],@Form1.buffers[bufferIndex][0]);
  80.  //    GenSmp := openmpt_module_read_quad(o_mod,44100,Numsmp,@Form1.buffers[bufferIndex][0],
  81.  //    @Form1.buffers[bufferIndex][0],@Form1.buffers[bufferIndex][0],@Form1.buffers[bufferIndex][0]);
  82.  
  83.   end;
  84. end;
  85.  
  86. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  87. begin
  88.   if uMsg = WOM_DONE then
  89.   begin
  90.     FillBuff(Form1.currentBuffer);
  91.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  92.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  93.   end;
  94.   Result := 0;
  95. end;
  96.  
  97. procedure InitAudio;
  98. var
  99.   wFormat: TWaveFormatEx;
  100.   i: Integer;
  101. begin
  102.  
  103.  // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
  104.  
  105.   with wFormat do
  106.   begin
  107.     wFormatTag := WAVE_FORMAT_PCM;
  108.     nChannels := Channels;
  109.     nSamplesPerSec := SampleRate;
  110.     wBitsPerSample := BitsPerSample;
  111.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  112.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  113.     cbSize := 0;
  114.   end;
  115.  
  116.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  117.     raise Exception.Create('Erreur ouverture periph audio');
  118.  
  119.   // buffers
  120.   for i := 0 to BufferCount - 1 do
  121.   begin
  122.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  123.      with Form1.waveHeaders[i] do
  124.     begin
  125.       lpData := @Form1.buffers[i][0];
  126.       dwBufferLength := BufSize * SizeOf(SmallInt);
  127.       dwFlags := 0;
  128.     end;
  129.     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  130.   end;
  131.   Form1.currentBuffer := 0;
  132.    for i := 0 to BufferCount - 1 do
  133.       begin
  134.         FillBuff(i);
  135.         waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  136.       end;
  137.  
  138. end;
  139.  
  140. procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  141. var
  142.   MemoryStream: TMemoryStream;
  143. begin
  144.   MemoryStream := TMemoryStream.Create;
  145.   try
  146.     MemoryStream.LoadFromFile(FileName);
  147.     SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  148.     MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  149.   finally
  150.     MemoryStream.Free;
  151.   end;
  152. end;
  153.  
  154.  
  155. procedure TForm1.FormCreate(Sender: TObject);
  156. var
  157. Buf: array of Byte;
  158.   FileName: string;
  159.   GenSmp, NumSmp: Integer;
  160.  
  161. begin
  162.        FileName := 'demosong 1.okta';
  163.        try
  164.        LoadBinaryFileToBuffer(FileName, Buf);
  165.  
  166.   except
  167.     on E: Exception do
  168.       ShowMessage('Erreur Fichier : ' );
  169.   end;
  170.  // load module to memory from buffer !
  171.  o_mod := openmpt_module_create_from_memory2(@Buf[0],Length(Buf),nil,lgusr,nil,nil,nil,nil,ctl);
  172.  o_mod_repeat := openmpt_module_set_repeat_count(o_mod,-1); //-1 infinite ; 0 play once ; n>0 play once and repeat n times after
  173.  o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,50);
  174.    // Openmpt mod info one Shot !
  175.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('tracker'));
  176.  Memo1.Lines.Add(o_mod_info);
  177.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('type'));   //type_long
  178.  Memo1.Lines.Add(o_mod_info);
  179.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('artist'));
  180.  Memo1.Lines.Add(o_mod_info);
  181.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('title'));
  182.  Memo1.Lines.Add(o_mod_info);
  183.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('date'));
  184.  Memo1.Lines.Add(o_mod_info);
  185.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('message')); //message_raw
  186.  Memo1.Lines.Add(o_mod_info);
  187.  
  188.  o_mod_duration := openmpt_module_get_duration_seconds(o_mod);
  189.  
  190.  Memo1.Lines.Add('Duration : ' +  FloatToStrF( o_mod_duration, ffFixed, 8, 2 ));
  191.  
  192. end;
  193.  
  194.  
  195.  
  196. procedure TForm1.FormShow(Sender: TObject);
  197. begin
  198.    InitAudio;
  199.    ok_flag := true;
  200.  
  201.  
  202. end;
  203.  
  204. procedure TForm1.Timer1Timer(Sender: TObject);
  205. begin
  206.  
  207.   if ok_flag then
  208.   begin
  209.   o_mod_position := openmpt_module_get_position_seconds(o_mod);
  210.   label1.Caption := 'Position : ' + FloatToStrF(o_mod_position, ffFixed, 8, 2) ;
  211.   end;
  212.  
  213. end;
  214.  
  215. procedure TForm1.TrackBar1Change(Sender: TObject);
  216. begin
  217.   if ok_flag then
  218.     o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,TrackBar1.Position); // 2 = stereo sep
  219.  
  220.     //RENDER_MASTERGAIN_MILLIBEL = 1,
  221.     //RENDER_STEREOSEPARATION_PERCENT = 2, 0-100
  222.     //RENDER_INTERPOLATIONFILTER_LENGTH = 3,
  223.     //RENDER_VOLUMERAMPING_STRENGTH = 4
  224.  
  225. end;
  226.  
  227.  
  228. procedure TForm1.FloatSpinEdit1Change(Sender: TObject);
  229. begin
  230.   if ok_flag then
  231.      o_mod_set_pos := openmpt_module_set_position_seconds(o_mod,FloatSpinEdit1.value);
  232. end;
  233.  
  234. end.
  235.  

Openmpt lib unit:
Code: Pascal  [Select][+][-]
  1. unit OpenMPT;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. const
  11.   LIBOPENMPT_DLL = 'openmpt.dll';
  12.  
  13. type
  14.  
  15.   TOpenMptLogFunc = procedure(message: PChar; user: Pointer); cdecl;
  16.   TOpenMPTException = class(Exception);
  17.   TOpenMPTModule = Pointer;
  18.  
  19.   TUInt64 = UInt64;
  20.   TUInt32 = UInt32;
  21.   TSizeUInt = SizeUInt;
  22.  
  23.   TOpenMPTRenderParam = (
  24.     RENDER_MASTERGAIN_MILLIBEL = 1,
  25.     RENDER_STEREOSEPARATION_PERCENT = 2,
  26.     RENDER_INTERPOLATIONFILTER_LENGTH = 3,
  27.     RENDER_VOLUMERAMPING_STRENGTH = 4
  28.   );
  29.  
  30.   TOpenMPTProbeFlags = (
  31.     PROBE_FILE_HEADER_FLAGS_MODULES2 = $1,
  32.     PROBE_FILE_HEADER_FLAGS_CONTAINERS2 = $2,
  33.     PROBE_FILE_HEADER_FLAGS_DEFAULT2 = $3,
  34.     PROBE_FILE_HEADER_FLAGS_NONE2 = $0
  35.   );
  36.  
  37.   TOpenMPTCommandIndex = (
  38.     COMMAND_NOTE = 0,
  39.     COMMAND_INSTRUMENT = 1,
  40.     COMMAND_VOLUMEEFFECT = 2,
  41.     COMMAND_EFFECT = 3,
  42.     COMMAND_VOLUME = 4,
  43.     COMMAND_PARAMETER = 5
  44.   );
  45.  
  46.   TOpenMPTProbeResult = (
  47.     PROBE_FILE_HEADER_RESULT_SUCCESS = 1,
  48.     PROBE_FILE_HEADER_RESULT_FAILURE = 0,
  49.     PROBE_FILE_HEADER_RESULT_WANTMOREDATA = -1
  50.   );
  51.  
  52.   TOpenMPTModuleInitialCtl = record
  53.     ctl: PAnsiChar;
  54.     value: PAnsiChar;
  55.   end;
  56.   POpenMPTModuleInitialCtl = ^TOpenMPTModuleInitialCtl;
  57.  
  58.   POpenMptModule = Pointer;
  59.  
  60.   POpenMptData = ^TOpenMptData;
  61.   TOpenMptData = record
  62.     mod_: POpenMptModule;
  63.     message_api: Pointer;
  64.     ext: AnsiString;
  65.     channels: Integer;
  66.     sample_rate: Integer;
  67.     length: Single;
  68.     song_data: Pointer;
  69.   end;
  70.  
  71.   //TOpenMptStreamReadFunc = function(stream: Pointer; dst: Pointer; bytes: SizeUInt): SizeUInt; cdecl;
  72.   //TOpenMptStreamSeekFunc = function(stream: Pointer; offset: Int64; whence: Integer): Integer; cdecl;
  73.   //TOpenMptStreamTellFunc = function(stream: Pointer): Int64; cdecl;
  74.   //
  75.   //TOpenMptStreamCallbacks = record
  76.   //  read: TOpenMptStreamReadFunc;
  77.   //  seek: TOpenMptStreamSeekFunc;
  78.   //  tell: TOpenMptStreamTellFunc;
  79.   //end;
  80.   //
  81.   TOpenMptErrorFunc = function(error: Integer; user: Pointer): Integer; cdecl;
  82.  
  83. // Fonctions openmpt
  84. function openmpt_get_library_version: TUInt32; cdecl; external LIBOPENMPT_DLL;
  85. function openmpt_get_core_version: TUInt32; cdecl; external LIBOPENMPT_DLL;
  86.  
  87. function openmpt_string_get(const key: PAnsiChar): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  88. function openmpt_get_supported_extensions_count: Integer; cdecl; external LIBOPENMPT_DLL;
  89. function openmpt_get_supported_extension(index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  90.  
  91. function openmpt_probe_file_header(flags: TUInt64; data: Pointer; size: TSizeUInt; filesize: TUInt64): Integer; cdecl; external LIBOPENMPT_DLL;
  92.  
  93. function openmpt_module_create(data: Pointer; size: TSizeUInt; logfunc: TOpenMptLogFunc; user: Pointer; ctls: POpenMPTModuleInitialCtl): TOpenMPTModule; cdecl; external LIBOPENMPT_DLL;
  94. procedure openmpt_module_destroy(module: TOpenMPTModule); cdecl; external LIBOPENMPT_DLL;
  95.  
  96. function openmpt_module_read_interleaved_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_stereo: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  97. function openmpt_module_read_interleaved_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_quad: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  98. function openmpt_module_read_interleaved_float_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_stereo: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  99. function openmpt_module_read_interleaved_float_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_quad: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  100.  
  101. function openmpt_module_set_position_seconds(module: TOpenMPTModule; seconds: Double): Double; cdecl; external LIBOPENMPT_DLL;
  102. function openmpt_module_get_position_seconds(module: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  103.  
  104. function openmpt_module_get_num_subsongs(module: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  105. function openmpt_module_select_subsong(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  106. function openmpt_module_get_selected_subsong(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  107. function openmpt_module_get_restart_order(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  108. function openmpt_module_get_restart_row(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  109. function openmpt_module_set_repeat_count(mod_: TOpenMPTModule; repeat_count: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  110. function openmpt_module_get_repeat_count(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  111. function openmpt_module_get_duration_seconds(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  112. function openmpt_module_get_time_at_position(mod_: TOpenMPTModule; order, row: Integer): Double; cdecl; external LIBOPENMPT_DLL;
  113.  
  114. function openmpt_module_set_position_order_row(mod_: TOpenMPTModule; order, row: Integer): Double; cdecl; external LIBOPENMPT_DLL;
  115.  
  116. function openmpt_module_get_render_param(mod_: TOpenMPTModule; param: Integer; value: PInteger): Integer; cdecl; external LIBOPENMPT_DLL;
  117. function openmpt_module_set_render_param(mod_: TOpenMPTModule; param: Integer; value: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  118.  
  119. function openmpt_module_read_mono(mod_: Pointer; samplerate: Integer; count: SizeUInt; mono: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  120. function openmpt_module_read_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  121. function openmpt_module_read_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right, rear_left, rear_right: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  122. function openmpt_module_read_float_mono(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; mono: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  123. function openmpt_module_read_float_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  124. function openmpt_module_read_float_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right, rear_left, rear_right: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  125.  
  126. function openmpt_module_get_metadata_keys(mod_: TOpenMPTModule): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  127. function openmpt_module_get_metadata(mod_: TOpenMPTModule; const key: PAnsiChar): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  128.  
  129. function openmpt_module_get_current_estimated_bpm(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  130. function openmpt_module_get_current_speed(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  131. function openmpt_module_get_current_tempo2(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  132.  
  133. function openmpt_module_get_current_order(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  134. function openmpt_module_get_current_pattern(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  135. function openmpt_module_get_current_row(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  136. function openmpt_module_get_current_playing_channels(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  137.  
  138. function openmpt_module_get_num_channels(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  139. function openmpt_module_get_num_orders(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  140. function openmpt_module_get_num_patterns(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  141. function openmpt_module_get_num_instruments(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  142. function openmpt_module_get_num_samples(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  143.  
  144. function openmpt_module_get_subsong_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  145. function openmpt_module_get_channel_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  146. function openmpt_module_get_order_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  147. function openmpt_module_get_pattern_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  148. function openmpt_module_get_instrument_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  149. function openmpt_module_get_sample_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  150.  
  151. function openmpt_module_read_one_tick(mod_: pointer; samplerate: Integer): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  152.  
  153. function openmpt_module_create2(
  154.   st_callbacks: Pointer;
  155.   st: Pointer;
  156.   logf: Pointer;
  157.   logu: Pointer;
  158.   errfunc: TOpenMptErrorFunc;
  159.   erruser: Pointer;
  160.   error: PInteger;
  161.   error_message: PPChar;
  162.   ctls: Pointer
  163. ): Pointer; cdecl; external LIBOPENMPT_DLL;
  164.  
  165. function openmpt_module_create_from_memory2(
  166.   buff: Pointer;
  167.   buffsize: NativeUInt;
  168.   logfct: TOpenMPTLogFunc;
  169.   logusr: Pointer;
  170.   errfct: TOpenMPTErrorFunc;
  171.   errusr: Pointer;
  172.   err: PInteger;
  173.   err_msg: PPAnsiChar;
  174.   ctls: POpenMPTModuleInitialCtl
  175. ): Pointer; cdecl; external LIBOPENMPT_DLL;
  176.  
  177.  
  178. implementation
  179.  
  180. end.
  181.  
  182.  
« Last Edit: January 30, 2025, 06:47:54 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Re: Open MPT Library
« Reply #1 on: January 30, 2025, 06:43:49 pm »
Ok this library is exceed 500 kb i must use my ftp server;

download the library;
http://gigatron3k.free.fr/lazarus/OpenMPT.zip

The first release is not optimized yet, this mean when module started it fill pcm data to the buffer, i have not implemented
stop module !!

Find Modules with formats here : https://ftp.modland.com/pub/modules/

Have fun

« Last Edit: January 30, 2025, 06:50:54 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

TRon

  • Hero Member
  • *****
  • Posts: 4158
Re: Open MPT Library
« Reply #2 on: January 30, 2025, 09:59:02 pm »
... it contains over 100 functions not all implémented;
There you go !  :)
Today is tomorrow's yesterday.

Fred vS

  • Hero Member
  • *****
  • Posts: 3505
    • StrumPract is the musicians best friend
Re: Open MPT Library
« Reply #3 on: January 31, 2025, 12:04:30 am »
Hello Gigatron.

Thank you very much for giving fpc access to all these hidden treasures.
Now that you are the total explorer of these libraries, which one impresses you the most and is a must-have for any decent sound enthusiast?
I have already adopted the XMP library, what should be the next one?
« Last Edit: January 31, 2025, 12:06:45 am by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Re: Open MPT Library
« Reply #4 on: January 31, 2025, 04:52:36 pm »
Hello Gigatron.

Thank you very much for giving fpc access to all these hidden treasures.
Now that you are the total explorer of these libraries, which one impresses you the most and is a must-have for any decent sound enthusiast?
I have already adopted the XMP library, what should be the next one?

Thank you @Fred VS

My goal is to play all module formats existing in this galaxy :) with Lazarus FPC.. The one that is blocking me at the moment is
Ultimate Amiga Delitracker. I have already compiled the library, but there are problems with external players. I'm working on it.

For you Sidplayer (over 59000 files) , then Video Game Music (over 70000) .......

Regards
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Guva

  • Full Member
  • ***
  • Posts: 146
  • 🌈 ZX-Spectrum !!!
Re: Open MPT Library
« Reply #5 on: January 31, 2025, 05:24:07 pm »
Gigatron. Great job!
As always, everything works. Only in the example I replaced Channels = 2 and openmpt_module_read_interleaved_stereo(o_mod, 44100, frames, bufferData);

Ps: TRon. Thank you for header

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Re: Open MPT Library
« Reply #6 on: January 31, 2025, 06:01:35 pm »
Gigatron. Great job!
As always, everything works. Only in the example I replaced Channels = 2 and openmpt_module_read_interleaved_stereo(o_mod, 44100, frames, bufferData);

Ps: TRon. Thank you for header

Great !! thank you Guva i've updated the code :
Code: Pascal  [Select][+][-]
  1. GenSmp := openmpt_module_read_interleaved_stereo(o_mod,44100, NumSmp,@Form1.buffers[bufferIndex][0]); // Channels = 2

Sure I don't forget Tron, thank you for the big .h;

I have rarely seen such a kind and serious community


You guys are unique and fantastic. VGM player I did it at the hospital with an Intel I7 laptop PC to tell you that I like Lazarus FPC and its community;

Regards
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Re: Open MPT Library
« Reply #7 on: January 31, 2025, 06:11:25 pm »
Hi,
Here is an updated OpenMpt mod player;

Added Play,Stop , Pause and  openmpt_module_read_interleaved_stereo thx @Guva;
The openmpt.dll is not changed;
Some modules positions are not reset after the end eg: Oktalyzer Armin Sander : https://www.robotplanet.dk/amiga/oktalyzer/
Project and 1 song is attached in .zip format;

Main unit
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  9.   ExtCtrls, Spin, mmsystem, windows, openmpt;
  10.  
  11. const
  12.  
  13.   Channels = 2;
  14.   BitsPerSample = 16;
  15.   SampleRate = 44100; // number of samples per second
  16.   BufSize = 8192 ; //   multiple of 2
  17.   BufferCount = 2;
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     bt_stop: TButton;
  25.     bt_play: TButton;
  26.     bt_pause: TButton;
  27.     FloatSpinEdit1: TFloatSpinEdit;
  28.     Label1: TLabel;
  29.     Label2: TLabel;
  30.     Memo1: TMemo;
  31.     Timer1: TTimer;
  32.     TrackBar1: TTrackBar;
  33.     procedure bt_pauseClick(Sender: TObject);
  34.     procedure bt_playClick(Sender: TObject);
  35.     procedure bt_stopClick(Sender: TObject);
  36.     procedure FloatSpinEdit1Change(Sender: TObject);
  37.     procedure FormCreate(Sender: TObject);
  38.     procedure FormShow(Sender: TObject);
  39.     procedure Timer1Timer(Sender: TObject);
  40.     procedure TrackBar1Change(Sender: TObject);
  41.  
  42.   private
  43.  
  44.     buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
  45.     waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  46.     currentBuffer: Integer;
  47.  
  48.   public
  49.  
  50.   end;
  51.  
  52. var
  53.   Form1: TForm1;
  54.   waveOut: HWAVEOUT;
  55.   waveHeader: TWaveHdr;
  56.   ok_flag : boolean = false;
  57.  
  58.   o_mod : Pointer;
  59.   o_mod_paramindex  : Integer;
  60.   o_mod_info : String;
  61.   o_mod_repeat : Integer;
  62.   o_mod_duration, o_mod_position,o_mod_set_pos  : Single;
  63.  
  64.   ctl : POpenMPTModuleInitialCtl;
  65.   lgfct  : Pointer;
  66.   lgusr : Pointer;
  67.   sc  : Pointer;
  68.  
  69. implementation
  70.  
  71. {$R *.lfm}
  72.  
  73. { TForm1 }
  74.  
  75. procedure FillBuff(bufferIndex: Integer);
  76. var
  77.   GenSmp, NumSmp: Integer;
  78. begin
  79.   if ok_flag then
  80.   begin
  81.  
  82.     bufferIndex := Form1.currentBuffer;
  83.     NumSmp := BufSize div (Channels * (BitsPerSample div 16));
  84.  // GenSmp := openmpt_module_read_mono(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0]);
  85.  // GenSmp := openmpt_module_read_stereo(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0],@Form1.buffers[bufferIndex][0]);
  86.     GenSmp := openmpt_module_read_interleaved_stereo(o_mod,44100, NumSmp,@Form1.buffers[bufferIndex][0]); // Channels = 2
  87.  
  88.   end;
  89. end;
  90.  
  91. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  92. begin
  93.   if uMsg = WOM_DONE then
  94.   begin
  95.     FillBuff(Form1.currentBuffer);
  96.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  97.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  98.   end;
  99.   Result := 0;
  100. end;
  101.  
  102. procedure InitAudio;
  103. var
  104.   wFormat: TWaveFormatEx;
  105.   i: Integer;
  106. begin
  107.  
  108.  // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
  109.  
  110.   with wFormat do
  111.   begin
  112.     wFormatTag := WAVE_FORMAT_PCM;
  113.     nChannels := Channels;
  114.     nSamplesPerSec := SampleRate;
  115.     wBitsPerSample := BitsPerSample;
  116.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  117.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  118.     cbSize := 0;
  119.   end;
  120.  
  121.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  122.     raise Exception.Create('Erreur ouverture periph audio');
  123.  
  124.   // buffers
  125.   for i := 0 to BufferCount - 1 do
  126.   begin
  127.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  128.      with Form1.waveHeaders[i] do
  129.     begin
  130.       lpData := @Form1.buffers[i][0];
  131.       dwBufferLength := BufSize * SizeOf(SmallInt);
  132.       dwFlags := 0;
  133.     end;
  134.     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  135.   end;
  136.   Form1.currentBuffer := 0;
  137.    for i := 0 to BufferCount - 1 do
  138.       begin
  139.         FillBuff(i);
  140.         waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  141.       end;
  142.  
  143. end;
  144.  
  145. procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  146. var
  147.   MemoryStream: TMemoryStream;
  148. begin
  149.   MemoryStream := TMemoryStream.Create;
  150.   try
  151.     MemoryStream.LoadFromFile(FileName);
  152.     SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  153.     MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  154.   finally
  155.     MemoryStream.Free;
  156.   end;
  157. end;
  158.  
  159.  
  160. procedure TForm1.FormCreate(Sender: TObject);
  161. var
  162. Buf: array of Byte;
  163.   FileName: string;
  164.   GenSmp, NumSmp: Integer;
  165.  
  166. begin
  167.        FileName := 'demosong 1.okta';
  168.        try
  169.        LoadBinaryFileToBuffer(FileName, Buf);
  170.  
  171.   except
  172.     on E: Exception do
  173.       ShowMessage('Erreur Fichier : ' );
  174.   end;
  175.  // load module to memory from buffer !
  176.  o_mod := openmpt_module_create_from_memory2(@Buf[0],Length(Buf),nil,lgusr,nil,nil,nil,nil,ctl);
  177.  o_mod_repeat := openmpt_module_set_repeat_count(o_mod,-1); //-1 infinite ; 0 play once ; n>0 play once and repeat n times after
  178.  o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,50);
  179.    // Openmpt mod info one Shot !
  180.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('tracker'));
  181.  Memo1.Lines.Add(o_mod_info);
  182.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('type'));   //type_long
  183.  Memo1.Lines.Add(o_mod_info);
  184.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('artist'));
  185.  Memo1.Lines.Add(o_mod_info);
  186.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('title'));
  187.  Memo1.Lines.Add(o_mod_info);
  188.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('date'));
  189.  Memo1.Lines.Add(o_mod_info);
  190.  o_mod_info := openmpt_module_get_metadata(o_mod,Pchar('message')); //message_raw
  191.  Memo1.Lines.Add(o_mod_info);
  192.  
  193.  o_mod_duration := openmpt_module_get_duration_seconds(o_mod);
  194.  
  195.  Memo1.Lines.Add('Duration : ' +  FloatToStrF( o_mod_duration, ffFixed, 8, 2 ));
  196.  
  197. end;
  198.  
  199.  
  200.  
  201. procedure TForm1.FormShow(Sender: TObject);
  202. begin
  203.    InitAudio;
  204.    ok_flag := true;
  205.  
  206. end;
  207.  
  208. procedure TForm1.Timer1Timer(Sender: TObject);
  209.  
  210. begin
  211.  
  212.   if ok_flag then
  213.   begin
  214.  
  215.   o_mod_position := openmpt_module_get_position_seconds(o_mod);
  216.   label1.Caption := 'Position : ' + FloatToStrF(o_mod_position, ffFixed, 8, 2) ;
  217.  
  218.   end;
  219.  
  220. end;
  221.  
  222. procedure TForm1.TrackBar1Change(Sender: TObject);
  223. begin
  224.   if ok_flag then
  225.     o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,TrackBar1.Position); // 2 = stereo sep
  226.  
  227.     //RENDER_MASTERGAIN_MILLIBEL = 1,
  228.     //RENDER_STEREOSEPARATION_PERCENT = 2, 0-100
  229.     //RENDER_INTERPOLATIONFILTER_LENGTH = 3,
  230.     //RENDER_VOLUMERAMPING_STRENGTH = 4
  231.  
  232. end;
  233.  
  234.  
  235. procedure TForm1.FloatSpinEdit1Change(Sender: TObject);
  236. begin
  237.   if ok_flag then
  238.      o_mod_set_pos := openmpt_module_set_position_seconds(o_mod,FloatSpinEdit1.value);
  239. end;
  240.  
  241. procedure TForm1.bt_stopClick(Sender: TObject);
  242. var
  243.   i : integer;
  244. begin
  245.     ok_flag := false;
  246.    // waveOutReset(waveOut);
  247.     // free buffer data !!
  248.     for i := 0 to BufferCount - 1 do
  249.     begin
  250.       ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  251.     end;
  252.    waveOutClose(waveOut);
  253.    o_mod_set_pos := openmpt_module_set_position_seconds(o_mod, 0);
  254.    label1.Caption := 'Position : ' + '0.00' ;
  255. end;
  256.  
  257. procedure TForm1.bt_playClick(Sender: TObject);
  258. var
  259.   i : integer;
  260. begin
  261.  // if not Assigned(waveOut) then
  262.     InitAudio;
  263.   ok_flag := true;
  264.    o_mod_position := openmpt_module_get_position_seconds(o_mod);
  265.    o_mod_set_pos  := openmpt_module_set_position_seconds(o_mod,o_mod_position);
  266.  
  267. end;
  268.  
  269. procedure TForm1.bt_pauseClick(Sender: TObject);
  270. var
  271.   i : integer;
  272. begin
  273.     ok_flag := false;
  274.     waveOutPause(waveOut);
  275. end;
  276.  
  277. end.
  278.  

Openmpt Unit
Code: Pascal  [Select][+][-]
  1. unit OpenMPT;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. const
  11.   LIBOPENMPT_DLL = 'openmpt.dll';
  12.  
  13. type
  14.  
  15.   TOpenMptLogFunc = procedure(message: PChar; user: Pointer); cdecl;
  16.   TOpenMPTException = class(Exception);
  17.   TOpenMPTModule = Pointer;
  18.  
  19.   TUInt64 = UInt64;
  20.   TUInt32 = UInt32;
  21.   TSizeUInt = SizeUInt;
  22.  
  23.   TOpenMPTRenderParam = (
  24.     RENDER_MASTERGAIN_MILLIBEL = 1,
  25.     RENDER_STEREOSEPARATION_PERCENT = 2,
  26.     RENDER_INTERPOLATIONFILTER_LENGTH = 3,
  27.     RENDER_VOLUMERAMPING_STRENGTH = 4
  28.   );
  29.  
  30.   TOpenMPTProbeFlags = (
  31.     PROBE_FILE_HEADER_FLAGS_MODULES2 = $1,
  32.     PROBE_FILE_HEADER_FLAGS_CONTAINERS2 = $2,
  33.     PROBE_FILE_HEADER_FLAGS_DEFAULT2 = $3,
  34.     PROBE_FILE_HEADER_FLAGS_NONE2 = $0
  35.   );
  36.  
  37.   TOpenMPTCommandIndex = (
  38.     COMMAND_NOTE = 0,
  39.     COMMAND_INSTRUMENT = 1,
  40.     COMMAND_VOLUMEEFFECT = 2,
  41.     COMMAND_EFFECT = 3,
  42.     COMMAND_VOLUME = 4,
  43.     COMMAND_PARAMETER = 5
  44.   );
  45.  
  46.   TOpenMPTProbeResult = (
  47.     PROBE_FILE_HEADER_RESULT_SUCCESS = 1,
  48.     PROBE_FILE_HEADER_RESULT_FAILURE = 0,
  49.     PROBE_FILE_HEADER_RESULT_WANTMOREDATA = -1
  50.   );
  51.  
  52.   TOpenMPTModuleInitialCtl = record
  53.     ctl: PAnsiChar;
  54.     value: PAnsiChar;
  55.   end;
  56.   POpenMPTModuleInitialCtl = ^TOpenMPTModuleInitialCtl;
  57.  
  58.   POpenMptModule = Pointer;
  59.  
  60.   POpenMptData = ^TOpenMptData;
  61.   TOpenMptData = record
  62.     mod_: POpenMptModule;
  63.     message_api: Pointer;
  64.     ext: AnsiString;
  65.     channels: Integer;
  66.     sample_rate: Integer;
  67.     length: Single;
  68.     song_data: Pointer;
  69.   end;
  70.  
  71.   //TOpenMptStreamReadFunc = function(stream: Pointer; dst: Pointer; bytes: SizeUInt): SizeUInt; cdecl;
  72.   //TOpenMptStreamSeekFunc = function(stream: Pointer; offset: Int64; whence: Integer): Integer; cdecl;
  73.   //TOpenMptStreamTellFunc = function(stream: Pointer): Int64; cdecl;
  74.   //
  75.   //TOpenMptStreamCallbacks = record
  76.   //  read: TOpenMptStreamReadFunc;
  77.   //  seek: TOpenMptStreamSeekFunc;
  78.   //  tell: TOpenMptStreamTellFunc;
  79.   //end;
  80.   //
  81.   TOpenMptErrorFunc = function(error: Integer; user: Pointer): Integer; cdecl;
  82.  
  83. // Fonctions openmpt
  84.  
  85.  
  86. function openmpt_string_get(const key: PAnsiChar): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  87. function openmpt_get_supported_extensions_count: Integer; cdecl; external LIBOPENMPT_DLL;
  88. function openmpt_get_supported_extension(index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  89.  
  90. function openmpt_probe_file_header(flags: TUInt64; data: Pointer; size: TSizeUInt; filesize: TUInt64): Integer; cdecl; external LIBOPENMPT_DLL;
  91.  
  92. function openmpt_module_create(data: Pointer; size: TSizeUInt; logfunc: TOpenMptLogFunc; user: Pointer; ctls: POpenMPTModuleInitialCtl): TOpenMPTModule; cdecl; external LIBOPENMPT_DLL;
  93. procedure openmpt_module_destroy(module: TOpenMPTModule); cdecl; external LIBOPENMPT_DLL;
  94.  
  95. function openmpt_module_read_interleaved_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_stereo: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  96. function openmpt_module_read_interleaved_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_quad: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  97. function openmpt_module_read_interleaved_float_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_stereo: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  98. function openmpt_module_read_interleaved_float_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; interleaved_quad: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  99. function openmpt_module_read_mono(mod_: Pointer; samplerate: Integer; count: SizeUInt; mono: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  100. function openmpt_module_read_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  101. function openmpt_module_read_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right, rear_left, rear_right: PSmallInt): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  102. function openmpt_module_read_float_mono(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; mono: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  103. function openmpt_module_read_float_stereo(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  104. function openmpt_module_read_float_quad(mod_: TOpenMPTModule; samplerate: Integer; count: SizeUInt; left, right, rear_left, rear_right: PSingle): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  105. function openmpt_module_read_one_tick(mod_: pointer; samplerate: Integer): SizeUInt; cdecl; external LIBOPENMPT_DLL;
  106.  
  107. function openmpt_module_set_position_seconds(module: TOpenMPTModule; seconds: Double): Double; cdecl; external LIBOPENMPT_DLL;
  108. function openmpt_module_select_subsong(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  109. function openmpt_module_set_repeat_count(mod_: TOpenMPTModule; repeat_count: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  110. function openmpt_module_set_position_order_row(mod_: TOpenMPTModule; order, row: Integer): Double; cdecl; external LIBOPENMPT_DLL;
  111. function openmpt_module_set_render_param(mod_: TOpenMPTModule; param: Integer; value: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  112.  
  113. // gets
  114. function openmpt_get_library_version: TUInt32; cdecl; external LIBOPENMPT_DLL;
  115. function openmpt_get_core_version: TUInt32; cdecl; external LIBOPENMPT_DLL;
  116. function openmpt_module_get_metadata_keys(mod_: TOpenMPTModule): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  117. function openmpt_module_get_metadata(mod_: TOpenMPTModule; const key: PAnsiChar): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  118. function openmpt_module_get_render_param(mod_: TOpenMPTModule; param: Integer; value: PInteger): Integer; cdecl; external LIBOPENMPT_DLL;
  119. function openmpt_module_get_current_estimated_bpm(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  120. function openmpt_module_get_current_speed(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  121. function openmpt_module_get_current_tempo2(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  122. function openmpt_module_get_current_order(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  123. function openmpt_module_get_current_pattern(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  124. function openmpt_module_get_current_row(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  125. function openmpt_module_get_current_playing_channels(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  126. function openmpt_module_get_num_channels(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  127. function openmpt_module_get_num_orders(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  128. function openmpt_module_get_num_patterns(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  129. function openmpt_module_get_num_instruments(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  130. function openmpt_module_get_num_samples(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  131. function openmpt_module_get_subsong_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  132. function openmpt_module_get_channel_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  133. function openmpt_module_get_order_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  134. function openmpt_module_get_pattern_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  135. function openmpt_module_get_instrument_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  136. function openmpt_module_get_sample_name(mod_: TOpenMPTModule; index: Integer): PAnsiChar; cdecl; external LIBOPENMPT_DLL;
  137. function openmpt_module_get_repeat_count(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  138. function openmpt_module_get_duration_seconds(mod_: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  139. function openmpt_module_get_time_at_position(mod_: TOpenMPTModule; order, row: Integer): Double; cdecl; external LIBOPENMPT_DLL;
  140. function openmpt_module_get_selected_subsong(mod_: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  141. function openmpt_module_get_restart_order(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  142. function openmpt_module_get_restart_row(mod_: TOpenMPTModule; subsong: Integer): Integer; cdecl; external LIBOPENMPT_DLL;
  143. function openmpt_module_get_position_seconds(module: TOpenMPTModule): Double; cdecl; external LIBOPENMPT_DLL;
  144. function openmpt_module_get_num_subsongs(module: TOpenMPTModule): Integer; cdecl; external LIBOPENMPT_DLL;
  145.  
  146.  
  147. function openmpt_module_create2(st_callbacks: Pointer;st: Pointer;logf: Pointer;logu: Pointer; errfunc: TOpenMptErrorFunc; erruser: Pointer; error: PInteger;  error_message: PPChar; ctls: Pointer): Pointer; cdecl; external LIBOPENMPT_DLL;
  148. function openmpt_module_create_from_memory2(buff: Pointer; buffsize: NativeUInt;logfct: TOpenMPTLogFunc; logusr: Pointer; errfct: TOpenMPTErrorFunc; errusr: Pointer; err: PInteger; err_msg: PPAnsiChar; ctls: POpenMPTModuleInitialCtl): Pointer; cdecl; external LIBOPENMPT_DLL;
  149.  
  150.  
  151. implementation
  152.  
  153. end.
  154.  
  155.  
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Guva

  • Full Member
  • ***
  • Posts: 146
  • 🌈 ZX-Spectrum !!!
Re: Open MPT Library
« Reply #8 on: February 01, 2025, 05:52:54 am »
Player version using raylib

Code: Pascal  [Select][+][-]
  1. program OpenMpt;
  2. {$mode objfpc}{$H+}
  3. uses
  4.   {$IFDEF UNIX}
  5.   cthreads,
  6.   {$ENDIF}
  7.   Classes, SysUtils, CustApp, raylib, binding.libopenmpt, spectrum_vis, Math;
  8.  
  9. type
  10.   { TRayApplication }
  11.   TRayApplication = class(TCustomApplication)
  12.   protected
  13.     procedure DoRun; override;
  14.   public
  15.     playing: boolean;
  16.     stream: TAudioStream;
  17.     o_mod_repeat, o_mod_paramindex : Integer;
  18.     o_mod_set_pos  : Single;
  19.     ctl : Popenmpt_module_initial_ctl;
  20.     lgusr : Pointer;
  21.     Spectrum : TSpectrum;
  22.     constructor Create(TheOwner: TComponent); override;
  23.     destructor Destroy; override;
  24.   end;
  25.  
  26.   const
  27.     AppTitle = 'raylib - Open MPT Library';
  28.     Channels = 2;
  29.     BitsPerSample = 16;
  30.     SampleRate = 44100; // number of samples per second
  31.     BufSize = 8192; //   multiple of 2
  32.  
  33. var o_mod : Popenmpt_module;
  34.     Data: TFFTData;
  35.  
  36. procedure FillAudio(bufferData: Pointer; frames: LongWord); cdecl;
  37. begin
  38.   openmpt_module_read_interleaved_stereo(o_mod, 44100, frames, bufferData);
  39. end;
  40.  
  41. procedure FillAtach(bufferData: Pointer; frames: LongWord); cdecl;
  42. var samples, left, right: psingle;
  43.     centr: single;
  44.     frame: integer;
  45.     exponent: single = 1.0;                  // Audio exponentiation value
  46. begin
  47.   samples:= bufferData; // Samples internally stored as <float>s
  48.  for frame :=0 to  frames -1 do
  49.   begin
  50.     left := @samples[frame * 2];
  51.     right := @samples[frame * 2 + 1];
  52.     centr := left^ + right^;
  53.  
  54.     if centr< 0.0 then centr :=power(abs(centr),exponent) * -1.0
  55.     else
  56.     centr :=power(abs(centr),exponent) * 1.0;
  57.     Data[frame] := centr ;
  58.   end;
  59. end;
  60.  
  61.  
  62. constructor TRayApplication.Create(TheOwner: TComponent);
  63. begin
  64.   inherited Create(TheOwner);
  65.   InitWindow(800, 600, AppTitle); // for window settings, look at example - window flags
  66.   playing := False;
  67.  
  68.   InitAudioDevice;
  69.   SetAudioStreamBufferSizeDefault(BufSize);
  70.   Stream := LoadAudioStream(SampleRate, BitsPerSample, Channels);
  71.   SetAudioStreamCallback(Stream,@FillAudio);
  72.   AttachAudioStreamProcessor(Stream,@FillAtach);
  73.   Spectrum := TSpectrum.Create(80, 450, 100 , 100);
  74.   Spectrum.Pen := red;
  75.   Spectrum.LineFallOff := 4;
  76.   Spectrum.Mode:=1;
  77.   Spectrum.FrameClear:=FALSE;
  78.  
  79.   SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  80. end;
  81.    procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  82.   var
  83.     MemoryStream: TMemoryStream;
  84.   begin
  85.     MemoryStream := TMemoryStream.Create;
  86.     try
  87.       MemoryStream.LoadFromFile(FileName);
  88.       SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  89.       MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  90.     finally
  91.       MemoryStream.Free;
  92.     end;
  93.   end;
  94. procedure TRayApplication.DoRun;
  95. var droppedFiles: TFilePathList;
  96.     Buf: array of Byte;
  97. begin
  98.   while (not WindowShouldClose) do // Detect window close button or ESC key
  99.   begin
  100.    if IsFileDropped() then
  101.  begin
  102.    droppedFiles := LoadDroppedFiles();
  103.    if droppedFiles.count = 1 then
  104.    begin
  105.      LoadBinaryFileToBuffer(droppedFiles.paths[0], Buf);
  106.      o_mod := openmpt_module_create_from_memory2(@Buf[0],Length(Buf),nil,lgusr,nil,nil,nil,nil,ctl);
  107.      o_mod_repeat := openmpt_module_set_repeat_count(o_mod,-1); //-1 infinite ; 0 play once ; n>0 play once and repeat n times after
  108.      o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,50);
  109.      playing := true;
  110.      PlayAudioStream(stream);
  111.    end;
  112.    UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
  113.  end;
  114.     // Draw
  115.     BeginDrawing();
  116.       ClearBackground(RAYWHITE);
  117.       DrawText('Drop module file here for play.', GetScreenWidth - 10 - MeasureText('Drop module file here for play.',10), 10, 10, BLACK);
  118.      try
  119.       if  playing then
  120.       begin
  121.        DrawText(PChar('Tracker : ' + openmpt_module_get_metadata(o_mod,Pchar('tracker'))), 10, 20, 10, DARKGRAY);
  122.        DrawText(PChar('Type : ' + openmpt_module_get_metadata(o_mod,Pchar('type'))), 10, 30, 10, DARKGRAY);
  123.        DrawText(PChar('Artist : ' + openmpt_module_get_metadata(o_mod,Pchar('artist'))), 10, 40, 10, DARKGRAY);
  124.        DrawText(PChar('Ttitle : ' +openmpt_module_get_metadata(o_mod,Pchar('title'))), 10, 50, 10, DARKGRAY);
  125.        DrawText(PChar('Date : ' + openmpt_module_get_metadata(o_mod,Pchar('date'))), 10, 60, 10, DARKGRAY);
  126.        DrawText(Pchar('Position: ' + FloatToStrF(openmpt_module_get_position_seconds(o_mod),
  127.        ffFixed, 8, 0) + ' / '  +  FloatToStrF(openmpt_module_get_duration_seconds(o_mod), ffFixed, 8, 0 )), 10, 70, 10, DARKGRAY);
  128.        DrawText(PChar('BPM : ' + FloatToStrF(openmpt_module_get_current_estimated_bpm(o_mod),ffFixed, 8, 0)), 10, 80, 10, DARKGRAY);
  129.        DrawText(PChar('Message : '), 10, 90, 10, DARKGRAY);
  130.        DrawText(PChar(openmpt_module_get_metadata(o_mod,Pchar('message'))), 10, 100, 10, DARKGRAY);
  131.        Spectrum.Draw(Data);
  132.       end;
  133.      except
  134.        begin
  135.          playing := false;
  136.        end;
  137.      end;
  138.     EndDrawing();
  139.   end;
  140.   // Stop program loop
  141.   Terminate;
  142. end;
  143.  
  144. destructor TRayApplication.Destroy;
  145. begin
  146.   // De-Initialization
  147.   StopAudioStream(stream);
  148.   UnloadAudioStream(stream);   // Close raw audio stream and delete buffers from RAM
  149.   CloseAudioDevice();         // Close audio device (music streaming is automatically stopped)
  150.   openmpt_module_destroy(o_mod);
  151.   CloseWindow(); // Close window and OpenGL context
  152.   inherited Destroy;
  153. end;
  154.  
  155. var
  156.   Application: TRayApplication;
  157. begin
  158.   Application:=TRayApplication.Create(nil);
  159.   Application.Title:=AppTitle;
  160.   Application.Run;
  161.   Application.Free;
  162. end.
  163.  

Code: Pascal  [Select][+][-]
  1. unit spectrum_vis;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, RayLib;
  9. type
  10.   TFFTData  = array [0..512] of Single;
  11.   { TSpectrum }
  12.   TSpectrum = Class(TObject)
  13.     private
  14.       VisBuff : TRectangle;
  15.       BkgColor : TColorB;
  16.       SpecHeight : Integer;
  17.       PenColor : TColorB;
  18.       PeakColor: TColorB;
  19.       DrawType : Integer;
  20.       DrawRes : Integer;
  21.       FrmClear : Boolean;
  22.       UseBkg : Boolean;
  23.       PeakFall : Integer;
  24.       LineFall : Integer;
  25.       ColWidth : Integer;
  26.       ShowPeak : Boolean;
  27.       ColorUp, ColorDown: TColorB;
  28.       FFTPeacks : array [0..600] of Integer;
  29.       FFTFallOff : array [0..600] of Integer;
  30.  
  31.     public
  32.      Constructor Create (X, Y, Width, Height : Integer);
  33.      procedure Draw(FFTData: TFFTData);
  34.      property BackColor : TColor read BkgColor write BkgColor;
  35.      property Height : Integer read SpecHeight write SpecHeight;
  36.      property Width : Integer read ColWidth write ColWidth;
  37.      property Pen : TColorB read PenColor write PenColor;
  38.      property Peak : TColorB read PeakColor write PeakColor;
  39.      property Mode : Integer read DrawType write DrawType;
  40.      property Res : Integer read DrawRes write DrawRes;
  41.      property FrameClear : Boolean read FrmClear write FrmClear;
  42.      property PeakFallOff: Integer read PeakFall write PeakFall;
  43.      property LineFallOff: Integer read LineFall write LineFall;
  44.      property DrawPeak : Boolean read ShowPeak write ShowPeak;
  45.   end;
  46.  
  47.  var Spectrum : TSpectrum;
  48.  
  49. implementation
  50. constructor TSpectrum.Create(X, Y, Width, Height: Integer);
  51.       begin
  52.         VisBuff.Create(X, Y, Width, Height);
  53.           BkgColor := Black;
  54.           SpecHeight := 100;
  55.           PenColor := White;
  56.           PeakColor := ORANGE;
  57.           DrawType := 0;
  58.           DrawRes := 1;
  59.           FrmClear := True;
  60.           UseBkg := False;
  61.           PeakFall := 1;
  62.           LineFall := 3;
  63.           ColWidth := 4;
  64.           ShowPeak := True;
  65.       end;
  66.  
  67. procedure TSpectrum.Draw(FFTData: TFFTData);
  68. var i, YPos : LongInt; YVal : Single;
  69. begin
  70.   if FrmClear then
  71.     begin
  72.       DrawRectangleRec(VisBuff, BkgColor);
  73.     end;
  74.  
  75.   for i := 0 to 128 do
  76.   begin
  77.     YVal := Abs(FFTData[(i * DrawRes) + 5]);
  78.     YPos := Trunc((YVal) * VisBuff.Height * 1 {1500}); // todo
  79.     if YPos >= FFTPeacks[i] then FFTPeacks[i] := YPos
  80.     else FFTPeacks[i] := FFTPeacks[i] - PeakFall;
  81.  
  82.     if YPos >= FFTFallOff[i] then FFTFallOff[i] := YPos
  83.     else FFTFallOff[i] := FFTFallOff[i] - LineFall;
  84.  
  85.     if (VisBuff.Height - FFTPeacks[i]) > VisBuff.Height then FFTPeacks[i] := 0;
  86.     if (VisBuff.Height - FFTFallOff[i]) > VisBuff.Height then FFTFallOff[i] := 0;
  87.  
  88.     case DrawType of
  89.     0 : begin
  90.           if ShowPeak then
  91.           DrawPixelV(Vector2Create(VisBuff.X + i, VisBuff.Y + VisBuff.Height - FFTPeacks[i]), PeakColor);
  92.           DrawLineV(Vector2Create(VisBuff.X + i, VisBuff.Y + VisBuff.Height),
  93.           Vector2Create(VisBuff.X + i, VisBuff.Y + VisBuff.Height - FFTFallOff[i]), pen);
  94.     end;
  95.  
  96.     1 : begin
  97.           if ShowPeak then
  98.           begin
  99.             DrawRectangleGradientV(Trunc(VisBuff.X + i * (ColWidth+1)),
  100.             Trunc(VisBuff.Y + VisBuff.Height  - FFTPeacks[i]),ColWidth ,ColWidth,  BLANK, PeakColor);
  101.           end;
  102.  
  103.           if YPos > VisBuff.height then
  104.           begin
  105.             colorUp := RED;
  106.           end else
  107.           if YPos > VisBuff.height / 2  then
  108.           begin
  109.             colorUp := YELLOW;
  110.           end else
  111.           begin
  112.             ColorUp := Green;
  113.           end;
  114.           DrawRectangleGradientV( Round(VisBuff.X + i * (ColWidth+1))  ,
  115.           Round(VisBuff.Y + VisBuff.Height  - FFTFallOff[i]),ColWidth ,
  116.           FFTFallOff[i], ColorUp, ColorDown);
  117.         end;
  118.     end;
  119.   end;
  120. end;
  121.  
  122. end.
  123.  
« Last Edit: February 01, 2025, 01:19:17 pm by Guva »

TRon

  • Hero Member
  • *****
  • Posts: 4158
Re: Open MPT Library
« Reply #9 on: February 01, 2025, 07:42:03 am »
Thank you both Gigatron and Guva 👍

A word of warning though (also reason why it is important to actually read the SDK and headers  ;D ):
Quote
// * \section libopenmpt_c_strings Strings
// *
// * - All strings returned from libopenmpt are encoded in UTF-8.
// * - All strings passed to libopenmpt should also be encoded in UTF-8.
// * Behaviour in case of invalid UTF-8 is unspecified.
// * - libopenmpt does not enforce or expect any particular Unicode
// * normalization form.
// * - All strings returned from libopenmpt are dynamically allocated and must
// * be freed with openmpt_free_string(). Do NOT use the C standard library
// * free() for libopenmpt strings as that would make your code invalid on
// * windows when dynamically linking against libopenmpt which itself statically
// * links to the C runtime.
// * - All strings passed to libopenmpt are copied. No ownership is assumed or
// * transferred.

Thus as example:
Code: Pascal  [Select][+][-]
  1. var
  2.   answer : pchar;
  3. ...
  4.   answer := openmpt_module_get_metadata(omod, 'title');
  5.   if assigned(answer) then
  6.   begin
  7.     if length(answer) > 0
  8.     then
  9.       writeln('title : ', answer);
  10.     openmpt_free_string(answer);
  11.   end;
  12.  

Happy coding !
Today is tomorrow's yesterday.

Guva

  • Full Member
  • ***
  • Posts: 146
  • 🌈 ZX-Spectrum !!!
Re: Open MPT Library
« Reply #10 on: February 01, 2025, 04:27:42 pm »
Quote
A word of warning though (also reason why it is important to actually read the SDK and headers  ;D ):
But the logic is not clear, why unload the line that I read?

I also rewrote the example because there was a memory leak, and followed your example
Code: Pascal  [Select][+][-]
  1. program OpenMpt;
  2. {$mode objfpc}{$H+}
  3. uses
  4.   {$IFDEF UNIX}
  5.   cthreads,
  6.   {$ENDIF}
  7.   Classes, SysUtils, CustApp, raylib, binding.libopenmpt, spectrum_vis, Math;
  8.  
  9. type
  10.   { TRayApplication }
  11.   TRayApplication = class(TCustomApplication)
  12.   protected
  13.     procedure DoRun; override;
  14.   public
  15.     playing: boolean;
  16.     stream: TAudioStream;
  17.     o_mod_repeat : Integer;
  18.     o_mod_set_pos  : Single;
  19.     Spectrum : TSpectrum;
  20.     constructor Create(TheOwner: TComponent); override;
  21.     destructor Destroy; override;
  22.   end;
  23.  
  24.   const
  25.     AppTitle = 'raylib - Open MPT Library';
  26.     Channels = 2;
  27.     BitsPerSample = 16;
  28.     SampleRate = 44100; // number of samples per second
  29.     BufSize = 8192; //   multiple of 2
  30.  
  31. var o_mod : Popenmpt_module;
  32.     Data: TFFTData;
  33.  
  34. procedure FillAudio(bufferData: Pointer; frames: LongWord); cdecl;
  35. begin
  36.   openmpt_module_read_interleaved_stereo(o_mod, 44100, frames, bufferData);
  37. end;
  38.  
  39. procedure FillAtach(bufferData: Pointer; frames: LongWord); cdecl;
  40. var samples, left, right: psingle;
  41.     centr: single;
  42.     frame: integer;
  43.     exponent: single = 1.0;                  // Audio exponentiation value
  44. begin
  45.   samples:= bufferData; // Samples internally stored as <float>s
  46.  for frame :=0 to  frames -1 do
  47.   begin
  48.     left := @samples[frame * 2];
  49.     right := @samples[frame * 2 + 1];
  50.     centr := left^ + right^;
  51.  
  52.     if centr< 0.0 then centr :=power(abs(centr),exponent) * -1.0
  53.     else
  54.     centr :=power(abs(centr),exponent) * 1.0;
  55.     Data[frame] := centr ;
  56.   end;
  57. end;
  58.  
  59.  
  60. constructor TRayApplication.Create(TheOwner: TComponent);
  61. begin
  62.   inherited Create(TheOwner);
  63.   InitWindow(800, 600, AppTitle); // for window settings, look at example - window flags
  64.   playing := False;
  65.  
  66.   InitAudioDevice;
  67.   SetAudioStreamBufferSizeDefault(BufSize);
  68.  
  69.   Spectrum := TSpectrum.Create(80, 450, 100 , 100);
  70.   Spectrum.Pen := red;
  71.   Spectrum.LineFallOff := 4;
  72.   Spectrum.Mode:=1;
  73.   Spectrum.FrameClear:=FALSE;
  74.  
  75.   SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  76. end;
  77.    procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer: TBytes);
  78.   var
  79.     MemoryStream: TMemoryStream;
  80.   begin
  81.     MemoryStream := TMemoryStream.Create;
  82.     try
  83.       MemoryStream.LoadFromFile(FileName);
  84.       SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  85.       MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  86.     finally
  87.       MemoryStream.Free;
  88.     end;
  89.   end;
  90. procedure TRayApplication.DoRun;
  91. var droppedFiles: TFilePathList;
  92.     Buf: array of Byte;
  93.     Text: Pchar;
  94. begin
  95.   while (not WindowShouldClose) do // Detect window close button or ESC key
  96.   begin
  97.    if IsFileDropped() then
  98.  begin
  99.    droppedFiles := LoadDroppedFiles();
  100.    if droppedFiles.count = 1 then
  101.    begin
  102.      playing := false;
  103.      if o_mod > nil then
  104.      begin
  105.        StopAudioStream(stream);
  106.        UnloadAudioStream(stream);   // Close raw audio stream and delete buffers from RAM
  107.        openmpt_module_destroy(o_mod);  // Unload a previously created openmpt_module from memory.
  108.        o_mod := nil; buf := nil;
  109.      end;
  110.      LoadBinaryFileToBuffer(droppedFiles.paths[0], Buf);
  111.      Stream := LoadAudioStream(SampleRate, BitsPerSample, Channels);
  112.      SetAudioStreamCallback(Stream,@FillAudio);
  113.      AttachAudioStreamProcessor(Stream,@FillAtach);
  114.      o_mod := openmpt_module_create_from_memory2(@Buf[0],Length(Buf),nil,nil,nil,nil,nil,nil,nil);
  115.      o_mod_repeat := openmpt_module_set_repeat_count(o_mod,-1); //-1 infinite ; 0 play once ; n>0 play once and repeat n times after
  116.      playing := true;
  117.      PlayAudioStream(stream);
  118.    end;
  119.    UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
  120.  end;
  121.     // Draw
  122.     BeginDrawing();
  123.       ClearBackground(RAYWHITE);
  124.       DrawText('Drop module file here for play.', GetScreenWidth - 10 - MeasureText('Drop module file here for play.',10), 10, 10, BLACK);
  125.      try
  126.       if playing then
  127.       begin
  128.        Text := openmpt_module_get_metadata(o_mod,'tracker');
  129.        if (assigned(Text)) and (length(Text) > 0) then
  130.        begin
  131.          DrawText(PChar('Tracker : ' + Text), 10, 20, 10, DARKGRAY);
  132.          openmpt_free_string(Text);
  133.        end;
  134.  
  135.        Text := openmpt_module_get_metadata(o_mod,'type');
  136.        if (assigned(Text)) and (length(Text) > 0) then
  137.        begin
  138.          DrawText(PChar('Type : ' + Text), 10, 30, 10, DARKGRAY);
  139.          openmpt_free_string(Text);
  140.        end;
  141.  
  142.        Text := openmpt_module_get_metadata(o_mod,'title');
  143.        if (assigned(Text)) and (length(Text) > 0) then
  144.        begin
  145.          DrawText(PChar('Ttitle : ' + Text), 10, 40, 10, DARKGRAY);
  146.          openmpt_free_string(Text);
  147.        end;
  148.  
  149.        DrawText(Pchar('Position: ' + FloatToStrF(openmpt_module_get_position_seconds(o_mod),
  150.        ffFixed, 8, 0) + ' / '  +  FloatToStrF(openmpt_module_get_duration_seconds(o_mod),
  151.        ffFixed, 8, 0 )), 10, 50, 10, DARKGRAY);
  152.  
  153.        DrawText(PChar('BPM : ' + FloatToStrF(openmpt_module_get_current_estimated_bpm(o_mod),ffFixed, 8, 0)), 10, 60, 10, DARKGRAY);
  154.  
  155.        Text := openmpt_module_get_metadata(o_mod,'message');
  156.        if (assigned(Text)) and (length(Text) > 0) then
  157.        begin
  158.          DrawText(PChar('Message : '), 10, 70, 10, DARKGRAY);
  159.          DrawText(Text, MeasureText('Message : ',10) + 10, 70, 10, DARKGRAY);
  160.          openmpt_free_string(Text);
  161.        end;
  162.  
  163.        Spectrum.Draw(Data);
  164.       end;
  165.      except
  166.        begin
  167.          playing := false;
  168.        end;
  169.      end;
  170.     EndDrawing();
  171.   end;
  172.   // Stop program loop
  173.   Terminate;
  174. end;
  175.  
  176. destructor TRayApplication.Destroy;
  177. begin
  178.   // De-Initialization
  179.   StopAudioStream(stream);
  180.   UnloadAudioStream(stream);   // Close raw audio stream and delete buffers from RAM
  181.   CloseAudioDevice();         // Close audio device (music streaming is automatically stopped)
  182.   openmpt_module_destroy(o_mod);
  183.   CloseWindow(); // Close window and OpenGL context
  184.   inherited Destroy;
  185. end;
  186.  
  187. var
  188.   Application: TRayApplication;
  189. begin
  190.   Application:=TRayApplication.Create(nil);
  191.   Application.Title:=AppTitle;
  192.   Application.Run;
  193.   Application.Free;
  194. end.
  195.  
  196.  

TRon

  • Hero Member
  • *****
  • Posts: 4158
Re: Open MPT Library
« Reply #11 on: February 02, 2025, 06:18:48 am »
But the logic is not clear, why unload the line that I read?
Because the example code wrote the retrieved string using writeln and was done with it  :) (It was just a (simple) example to show the ropes).

I wanted to test your memory leak (e.g. first example code that you posted) but got scorned when static linking with your provided libraylib.a.
Code: Bash  [Select][+][-]
  1. fpc -B -Furaylib openmpt.pas
  2. Free Pascal Compiler version 3.2.2 [2021/05/16] for x86_64
  3. Copyright (c) 1993-2021 by Florian Klaempfl and others
  4. Target OS: Linux for x86-64
  5. Compiling openmpt.pas
  6. Compiling ./raylib/raylib.pas
  7. Compiling binding.libopenmpt.pas
  8. Compiling spectrum_vis.pas
  9. Linking openmpt
  10. /usr/bin/ld: ./raylib/../lib/libraylib.a(rcore.o): in function `gladLoadGLUserPtr':
  11. rcore.c:(.text+0x8e6f): undefined reference to `__isoc23_sscanf'
  12. /usr/bin/ld: ./raylib/../lib/libraylib.a(rcore.o): in function `LoadAutomationEventList':
  13. rcore.c:(.text+0x24551): undefined reference to `__isoc23_sscanf'
  14. /usr/bin/ld: rcore.c:(.text+0x245ba): undefined reference to `__isoc23_sscanf'
  15. /usr/bin/ld: ./raylib/../lib/libraylib.a(rtext.o): in function `LoadBMFont':
  16. rtext.c:(.text+0x88d9): undefined reference to `__isoc23_sscanf'
  17. /usr/bin/ld: rtext.c:(.text+0x8966): undefined reference to `__isoc23_sscanf'
  18. /usr/bin/ld: ./raylib/../lib/libraylib.a(rtext.o):rtext.c:(.text+0x89d6): more undefined references to `__isoc23_sscanf' follow
  19. /usr/bin/ld: ./raylib/../lib/libraylib.a(rglfw.o): in function `_glfwParseUriList':
  20. rglfw.c:(.text+0x4fb8): undefined reference to `__isoc23_strtol'
  21. /usr/bin/ld: ./raylib/../lib/libraylib.a(rglfw.o): in function `_glfwRefreshContextAttribs':
  22. rglfw.c:(.text+0x5cb2): undefined reference to `__isoc23_sscanf'
  23. /usr/bin/ld: ./raylib/../lib/libraylib.a(rglfw.o): in function `parseMapping':
  24. rglfw.c:(.text+0x8fbe): undefined reference to `__isoc23_strtoul'
  25. /usr/bin/ld: rglfw.c:(.text+0x8fd9): undefined reference to `__isoc23_strtoul'
  26. /usr/bin/ld: rglfw.c:(.text+0x90f4): undefined reference to `__isoc23_strtoul'
  27. openmpt.pas(163,1) Error: Error while linking
  28. openmpt.pas(163,1) Fatal: There were 1 errors compiling module, stopping
  29. Fatal: Compilation aborted
  30.  
Except all openmpt related stuff everything raylib related was pulled from your repository release 5.5 (fixa) nov 22th 2024 (fwiw the static library from the raylib repository was able to fix that for me)


Also (strange one): I needed to adjust the creation of the rectangle inside the spectrum analyzer create event otherwise:
Code: Bash  [Select][+][-]
  1. fpc -B -Furaylib openmpt.pas
  2. Free Pascal Compiler version 3.2.2 [2021/05/16] for x86_64
  3. Copyright (c) 1993-2021 by Florian Klaempfl and others
  4. Target OS: Linux for x86-64
  5. Compiling openmpt.pas
  6. Compiling ./raylib/raylib.pas
  7. Compiling binding.libopenmpt.pas
  8. Compiling spectrum_vis.pas
  9. spectrum_vis.pas(52,17) Error: identifier idents no member "Create"
  10. spectrum_vis.pas(124) Fatal: There were 1 errors compiling module, stopping
  11. Fatal: Compilation aborted
  12.  

PS:
I also rewrote the example because there was a memory leak, and followed your example
There still is. Did you forgot to free the Spectrum class ?

Also a wrapper function ccan make things easier, e.g. something like:
Code: Pascal  [Select][+][-]
  1. function openmpt_module_get_metadata(mod_: Popenmpt_module; const key: pchar): string; inline;
  2. var
  3.   retval : pchar;
  4. begin
  5.   retval := binding.libopenmpt.openmpt_module_get_metadata(mod_, key);
  6.   if assigned(retval) then
  7.   begin
  8.     SetString(openmpt_module_get_metadata, retval, StrLen(retval));
  9.     openmpt_free_string(retval);
  10.   end
  11.   else openmpt_module_get_metadata := '';
  12. end;
  13.  
« Last Edit: February 02, 2025, 07:29:55 am by TRon »
Today is tomorrow's yesterday.

Gigatron

  • Full Member
  • ***
  • Posts: 204
  • Amiga Rulez !!
Re: Open MPT Library
« Reply #12 on: February 02, 2025, 02:34:17 pm »
Thanks to both of you, thanks to exceptional brains @Guva and @Tron ; I correct my mistakes and I learn !!

The purpose of the human being is to learn from birth to the end... 10000 years would not be enough to learn everything but .... We must learn while life allows us to.

So Long live every human and Lazarus Pascal;

Here is the code based on the suggestion of @Tron ;

Main unit ;

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  9.   ExtCtrls, Spin, mmsystem, windows, openmpt;
  10.  
  11. const
  12.  
  13.   Channels = 2;
  14.   BitsPerSample = 16;
  15.   SampleRate = 44100; // number of samples per second
  16.   BufSize = 8192 ; //   multiple of 2
  17.   BufferCount = 2;
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     bt_stop: TButton;
  25.     bt_play: TButton;
  26.     bt_pause: TButton;
  27.     FloatSpinEdit1: TFloatSpinEdit;
  28.     Label1: TLabel;
  29.     Label2: TLabel;
  30.     Memo1: TMemo;
  31.     Timer1: TTimer;
  32.     TrackBar1: TTrackBar;
  33.     procedure bt_pauseClick(Sender: TObject);
  34.     procedure bt_playClick(Sender: TObject);
  35.     procedure bt_stopClick(Sender: TObject);
  36.     procedure FloatSpinEdit1Change(Sender: TObject);
  37.     procedure FormCreate(Sender: TObject);
  38.     procedure FormShow(Sender: TObject);
  39.     procedure Timer1Timer(Sender: TObject);
  40.     procedure TrackBar1Change(Sender: TObject);
  41.  
  42.   private
  43.  
  44.     buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
  45.     waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  46.     currentBuffer: Integer;
  47.  
  48.   public
  49.  
  50.   end;
  51.  
  52. var
  53.   Form1: TForm1;
  54.   waveOut: HWAVEOUT;
  55.   waveHeader: TWaveHdr;
  56.   ok_flag : boolean = false;
  57.  
  58.   o_mod : Pointer;
  59.   o_mod_paramindex  : Integer;
  60.   o_mod_info : String;
  61.   o_mod_repeat : Integer;
  62.   o_mod_duration, o_mod_position,o_mod_set_pos  : Single;
  63.  
  64.   ctl : POpenMPTModuleInitialCtl;
  65.   lgfct  : Pointer;
  66.   lgusr : Pointer;
  67.   sc  : Pointer;
  68.  
  69. implementation
  70.  
  71. {$R *.lfm}
  72.  
  73. { TForm1 }
  74.  
  75. procedure FillBuff(bufferIndex: Integer);
  76. var
  77.   GenSmp, NumSmp: Integer;
  78. begin
  79.   if ok_flag then
  80.   begin
  81.  
  82.     bufferIndex := Form1.currentBuffer;
  83.     NumSmp := BufSize div (Channels * (BitsPerSample div 16));
  84.  // GenSmp := openmpt_module_read_mono(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0]);
  85.  // GenSmp := openmpt_module_read_stereo(o_mod,44100,NumSmp, @Form1.buffers[bufferIndex][0],@Form1.buffers[bufferIndex][0]);
  86.     GenSmp := openmpt_module_read_interleaved_stereo(o_mod,44100, NumSmp,@Form1.buffers[bufferIndex][0]); // Channels = 2
  87.  
  88.   end;
  89. end;
  90.  
  91. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  92. begin
  93.   if uMsg = WOM_DONE then
  94.   begin
  95.     FillBuff(Form1.currentBuffer);
  96.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  97.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  98.   end;
  99.   Result := 0;
  100. end;
  101.  
  102. procedure InitAudio;
  103. var
  104.   wFormat: TWaveFormatEx;
  105.   i: Integer;
  106. begin
  107.  
  108.  // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
  109.  
  110.   with wFormat do
  111.   begin
  112.     wFormatTag := WAVE_FORMAT_PCM;
  113.     nChannels := Channels;
  114.     nSamplesPerSec := SampleRate;
  115.     wBitsPerSample := BitsPerSample;
  116.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  117.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  118.     cbSize := 0;
  119.   end;
  120.  
  121.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  122.     raise Exception.Create('Erreur ouverture periph audio');
  123.  
  124.   // buffers
  125.   for i := 0 to BufferCount - 1 do
  126.   begin
  127.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  128.      with Form1.waveHeaders[i] do
  129.     begin
  130.       lpData := @Form1.buffers[i][0];
  131.       dwBufferLength := BufSize * SizeOf(SmallInt);
  132.       dwFlags := 0;
  133.     end;
  134.     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  135.   end;
  136.   Form1.currentBuffer := 0;
  137.    for i := 0 to BufferCount - 1 do
  138.       begin
  139.         FillBuff(i);
  140.         waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  141.       end;
  142.  
  143. end;
  144.  
  145. procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  146. var
  147.   MemoryStream: TMemoryStream;
  148. begin
  149.   MemoryStream := TMemoryStream.Create;
  150.   try
  151.     MemoryStream.LoadFromFile(FileName);
  152.     SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  153.     MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  154.   finally
  155.     MemoryStream.Free;
  156.   end;
  157. end;
  158.  
  159. procedure DisplayMetadata(mod_: Pointer; const key: pchar; memo: TMemo);//inline;
  160. var
  161.   answer: pchar;
  162. begin
  163.   answer := openmpt_module_get_metadata(mod_, key);
  164.   if assigned(answer) then
  165.   begin
  166.     if length(answer) > 0 then
  167.       memo.Lines.Add(key + ' : ' + answer);
  168.     // free mem
  169.     openmpt_free_string(answer);
  170.   end
  171.   else
  172.   begin
  173.     memo.Lines.Add('No Data For :  ' + key);
  174.   end;
  175. end;
  176.  
  177. procedure TForm1.FormCreate(Sender: TObject);
  178. var
  179. Buf: array of Byte;
  180. FileName: string;
  181.  
  182. begin
  183.        FileName := 'amegas.mod';
  184.        try
  185.        LoadBinaryFileToBuffer(FileName, Buf);
  186.  
  187.   except
  188.     on E: Exception do
  189.       ShowMessage('Erreur Fichier : ' );
  190.   end;
  191.  // load module to memory from buffer !
  192.  o_mod := openmpt_module_create_from_memory2(@Buf[0],Length(Buf),nil,lgusr,nil,nil,nil,nil,ctl);
  193.  o_mod_repeat := openmpt_module_set_repeat_count(o_mod,-1); //-1 infinite ; 0 play once ; n>0 play once and repeat n times after
  194.  o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,50);
  195.  
  196.    DisplayMetadata(o_mod,'tracker',memo1);
  197.    DisplayMetadata(o_mod,'type',memo1);    //type_long
  198.    DisplayMetadata(o_mod,'artist',memo1);
  199.    DisplayMetadata(o_mod,'title',memo1);
  200.    DisplayMetadata(o_mod,'date',memo1);
  201.    DisplayMetadata(o_mod,'message',memo1);  // message_raw
  202.  
  203.  o_mod_duration := openmpt_module_get_duration_seconds(o_mod);
  204.  
  205.  Memo1.Lines.Add('Duration : ' +  FloatToStrF( o_mod_duration, ffFixed, 8, 2 ));
  206.  
  207. end;
  208.  
  209. procedure TForm1.FormShow(Sender: TObject);
  210. begin
  211.    InitAudio;
  212.    ok_flag := true;
  213.  
  214. end;
  215.  
  216. procedure TForm1.Timer1Timer(Sender: TObject);
  217. begin
  218.  
  219.   if ok_flag then
  220.   begin
  221.  
  222.   o_mod_position := openmpt_module_get_position_seconds(o_mod);
  223.   label1.Caption := 'Position : ' + FloatToStrF(o_mod_position, ffFixed, 8, 2) ;
  224.  
  225.   end;
  226.  
  227. end;
  228.  
  229. procedure TForm1.TrackBar1Change(Sender: TObject);
  230. begin
  231.   if ok_flag then
  232.     o_mod_paramindex := openmpt_module_set_render_param(o_mod,2,TrackBar1.Position); // 2 = stereo sep
  233.  
  234.     //RENDER_MASTERGAIN_MILLIBEL = 1,
  235.     //RENDER_STEREOSEPARATION_PERCENT = 2, 0-100
  236.     //RENDER_INTERPOLATIONFILTER_LENGTH = 3,
  237.     //RENDER_VOLUMERAMPING_STRENGTH = 4
  238.  
  239. end;
  240.  
  241.  
  242. procedure TForm1.FloatSpinEdit1Change(Sender: TObject);
  243. begin
  244.   if ok_flag then
  245.      o_mod_set_pos := openmpt_module_set_position_seconds(o_mod,FloatSpinEdit1.value);
  246. end;
  247.  
  248. procedure TForm1.bt_stopClick(Sender: TObject);
  249. var
  250.   i : integer;
  251. begin
  252.     ok_flag := false;
  253.     // free buffer data !!
  254.     for i := 0 to BufferCount - 1 do
  255.     begin
  256.       ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  257.     end;
  258.    waveOutClose(waveOut);
  259.    o_mod_set_pos := openmpt_module_set_position_seconds(o_mod, 0);
  260.    label1.Caption := 'Position : ' + '0.00' ;
  261. end;
  262.  
  263. procedure TForm1.bt_playClick(Sender: TObject);
  264. begin
  265.  // if not Assigned(waveOut) then
  266.     InitAudio;
  267.    ok_flag := true;
  268.    o_mod_position := openmpt_module_get_position_seconds(o_mod);
  269.    o_mod_set_pos  := openmpt_module_set_position_seconds(o_mod,o_mod_position);
  270. end;
  271.  
  272. procedure TForm1.bt_pauseClick(Sender: TObject);
  273. begin
  274.     ok_flag := false;
  275.     waveOutPause(waveOut);
  276. end;
  277.  
  278. end.
  279.  

Insert the command  openmpt_free_string to OPENMpt.pas like this :
Code: Pascal  [Select][+][-]
  1. procedure openmpt_free_string( str : Pchar );cdecl; external LIBOPENMPT_DLL;  


// end of transmission
« Last Edit: February 02, 2025, 03:48:35 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

 

TinyPortal © 2005-2018