Recent

Author Topic: Adlib Player  (Read 1394 times)

Gigatron

  • Full Member
  • ***
  • Posts: 161
  • Amiga Rulez !!
Adlib Player
« on: December 12, 2024, 01:57:12 am »
Hi,
Back to C++, C and then to FPC for making something nice to listening old OPL fm modules .. used on PCs,
will give some extra info later ;
What is Adlib/Adplug   ? : https://adplug.github.io/
Supported file format not tested yet ;

AdLib / OPL2 sound chip Format file support .HSC, .SNG, .IMF/.WLF/.ADLIB, .A2M, .AMD, .BAM, .CMF, .D00, .DFM, .HSP, .KSM, .MAD, .LAA, .MKJ, .CFF, .DMO, .S3M, .DTM, .MTK, .RAD, .RAW, .SAT, .XAD, .LDS, .M, .ROL, .XSM, .DRO, .MSC, and .RIX.

Find Adlib Modules here : https://modland.com/pub/modules/Ad%20Lib/
Tested 2 extension .HSC and XSM ;

I used Visual studio 2019 , Hippoplayer Adplug and replay sources to extract some functions in adlib.dll. on X64 platforms ;
Just simple functions to play FM OPL modules are exported to .dll the replay routine is a bit faster than original will be fixed asap;

The library and 2 modules are attached in .zip file;
The modules must be in project drawer .dll too ;



** I am working with UADE ... a really nightmare ... done 40 % !!! hope it's will ready before 2025 !

Have Fun

Gigatron


The 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, ExtCtrls,adlib,
  9.   mmsystem, windows,DynLibs;
  10.  
  11. const
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // Nombre d'échantillons par seconde
  15.   BufSize = 8192;    // Taille du tampon audio x 2
  16.   BufferCount = 2;
  17.  
  18. type
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)
  22.     Timer1: TTimer;
  23.     procedure FormCreate(Sender: TObject);
  24.     procedure FormShow(Sender: TObject);
  25.     procedure Timer1Timer(Sender: TObject);
  26.     procedure Process_OPL(BufferIndex: Integer);
  27.   private
  28.     buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
  29.     waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  30.     currentBuffer: Integer;
  31.   public
  32.   end;
  33.  
  34. var
  35.   Form1: TForm1;
  36.   waveOut: HWAVEOUT;
  37.   ok_flag: Boolean = false;
  38.   libHandle: TLibHandle;
  39.   error: DWORD;
  40.   core: boolean = false;
  41.   p_tick : integer = 0;
  42.   n_sample  : SmallInt;
  43.   opl : Pointer; // file pointer !!
  44.  
  45. implementation
  46.  
  47. {$R *.lfm}
  48.  
  49. procedure HandleError(const Str: PAnsiChar);
  50. begin
  51.   if Str <> nil then
  52.   begin
  53.     ShowMessage('Error: ' + Str);
  54.     Halt(1);
  55.   end;
  56. end;
  57.  
  58. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  59. begin
  60.   if uMsg = WOM_DONE then
  61.   begin
  62.     Form1.Process_OPL(Form1.currentBuffer);
  63.     waveOutWrite(waveOut, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  64.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  65.   end;
  66.   Result := 0;
  67. end;
  68.  
  69. procedure InitAudio;
  70. var
  71.   wFormat: TWaveFormatEx;
  72.   i: Integer;
  73. begin
  74.   SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
  75.  
  76.   with wFormat do
  77.   begin
  78.     wFormatTag := WAVE_FORMAT_PCM;
  79.     nChannels := Channels;
  80.     nSamplesPerSec := SampleRate;
  81.     wBitsPerSample := BitsPerSample;
  82.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  83.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  84.     cbSize := 0;
  85.   end;
  86.  
  87.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  88.     raise Exception.Create('Erreur lors de l''ouverture du périphérique audio');
  89.  
  90.   // Préparation des tampons
  91.   for i := 0 to BufferCount - 1 do
  92.   begin
  93.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  94.     with Form1.waveHeaders[i] do
  95.     begin
  96.       lpData := @Form1.buffers[i][0];
  97.       dwBufferLength := BufSize  ;
  98.       dwFlags := 0;
  99.  
  100.     end;
  101.     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  102.   end;
  103.     Form1.currentBuffer := 0;
  104.     for i := 0 to BufferCount - 1 do
  105.      begin
  106.       waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  107.     end;
  108. end;
  109.  
  110. procedure TForm1.FormCreate(Sender: TObject);
  111. var
  112.   FileName: string;
  113.  begin
  114.  
  115.   libHandle := LoadLibrary('adlib.dll');
  116.   if libHandle = 0 then
  117.   begin
  118.     error := GetLastError;
  119.     ShowMessage('Erreur chargement de la DLL: ' + IntToStr(error));
  120.   end;
  121.  
  122.   core := CreateCore(true,2); // create OPL   20 channels max !! Type_Opl2
  123.  
  124.  // FileName := 'neo_intro.xms'; // the famous Neo-Intro when PC had FM OPL soundchip and AMIGA PAULA !!! :)
  125.  // Or this module file
  126.  FileName := 'ezerious.hsc';  // Hannes Seifert / Input
  127.  
  128.  
  129.   ShowMessage('init OK ' );
  130.   Set_OPLchip(0);
  131.   Init_Opl;
  132.   opl :=  Load_Mod(Pchar(filename) );
  133.   ShowMessage('file Ok ' );
  134.   ShowMessage('All Ok ' );
  135.  
  136.   end;
  137.  
  138. procedure TForm1.FormShow(Sender: TObject);
  139. begin
  140.      InitAudio;
  141.      ok_flag := true;
  142.  
  143. end;
  144.  
  145. procedure TForm1.Process_OPL(BufferIndex: Integer);
  146. var
  147.  n: SmallInt;
  148. begin
  149.  
  150.        n := 2048;
  151.        Write_pcm(@buffers[BufferIndex][0], n   );
  152.  
  153.          if  Player_Update then
  154.          begin
  155.            inc(p_tick,2); // future
  156.          end;
  157. end;
  158.  
  159. procedure TForm1.Timer1Timer(Sender: TObject);
  160. begin
  161.  
  162. end;
  163.  
  164. end.
  165.  

Adlib Unit;

Code: Pascal  [Select][+][-]
  1. unit adlib;
  2.  
  3.  
  4. {$mode objfpc}{$H+}
  5.  
  6. interface
  7.  
  8. uses
  9.     windows;
  10.  
  11. const
  12.  
  13.    ADLIBDLL = 'adlib.dll';
  14.  
  15.  
  16. function CreateCore(isStereo: Boolean;core : integer): Boolean; cdecl; external ADLIBDLL;
  17. procedure Init_Opl; cdecl; external ADLIBDLL;
  18. procedure Write_Pcm(buf: Pointer; samples: SmallInt); cdecl; external ADLIBDLL;
  19. function Load_Mod(filename : Pchar  ): Pointer;  cdecl; external ADLIBDLL;
  20.  
  21. procedure Set_OPLchip(chip: integer); cdecl; external ADLIBDLL;
  22. function Player_Update: Boolean; cdecl; external ADLIBDLL;
  23.  
  24.  
  25. implementation
  26.  
  27. end.
« Last Edit: December 14, 2024, 03:54:23 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 161
  • Amiga Rulez !!
Re: Adlib Player
« Reply #1 on: December 17, 2024, 01:18:32 am »
Ok

Here is the second version of Adlib player i've just added some new commands about modules informations:
Each modules have differents Opl Timer, to decrease speed ADD some bytes to BufferSize ; Or Substract to increase speed !
 

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, ExtCtrls,adlib,
  9.   mmsystem, windows,DynLibs;
  10.  
  11. const
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // Nombre d'échantillons par seconde
  15.   BufSize = 8192+1024    ;    // Taille du tampon audio x 2 // buffersize == module speed !! (neointro.hsc) is now ok !!
  16.   BufferCount = 4;
  17.  
  18. type
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)
  22.     Timer1: TTimer;
  23.     procedure FormCreate(Sender: TObject);
  24.     procedure FormShow(Sender: TObject);
  25.     procedure Timer1Timer(Sender: TObject);
  26.     procedure Opl_Process(BufferIndex: Integer);
  27.  
  28.   private
  29.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of Byte;
  30.     waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  31.     currentBuffer: Integer;
  32.   public
  33.  
  34.   end;
  35.  
  36. var
  37.   Form1: TForm1;
  38.   waveOut: HWAVEOUT;
  39.   ok_flag: Boolean = false;
  40.   libHandle: TLibHandle;
  41.   error: DWORD;
  42.   core: boolean = false;
  43.   opl : Pointer; // file pointer !!
  44.   tick : integer;
  45.  
  46.  
  47. implementation
  48.  
  49. {$R *.lfm}
  50.  
  51. procedure HandleError(const Str: PAnsiChar);
  52. begin
  53.   if Str <> nil then
  54.   begin
  55.     ShowMessage('Error: ' + Str);
  56.     Halt(1);
  57.   end;
  58. end;
  59.  
  60. procedure SavePCMBuffer(const Buffer: Pointer; BufferSize: Integer; const FileName: string);
  61. var
  62.   FileHandle: TFileStream;
  63. begin
  64.   try
  65.     FileHandle := TFileStream.Create(FileName, fmOpenWrite or fmCreate);
  66.     try
  67.       // Écrire les données du tampon
  68.       FileHandle.Write(Buffer^, BufferSize);
  69.     finally
  70.       FileHandle.Free;
  71.     end;
  72.   except
  73.     on E: Exception do
  74.       ShowMessage('Erreur lors de la sauvegarde du fichier PCM: ' + E.Message);
  75.   end;
  76. end;
  77.  
  78.  
  79.  
  80. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  81. begin
  82.  
  83.   if uMsg = WOM_DONE then
  84.   begin
  85.  
  86.     Form1.Opl_Process(Form1.currentBuffer);
  87.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  88.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  89.    end;
  90.  
  91.   Result := 0;
  92. end;
  93.  
  94. procedure InitAudio;
  95. var
  96.   wFormat: TWaveFormatEx;
  97.   i: Integer;
  98. begin
  99.    SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_NORMAL);
  100.  
  101.   with wFormat do
  102.   begin
  103.     wFormatTag := WAVE_FORMAT_PCM;
  104.     nChannels := Channels;
  105.     nSamplesPerSec := SampleRate;
  106.     wBitsPerSample := BitsPerSample;
  107.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  108.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  109.     cbSize := 0;
  110.   end;
  111.  
  112.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  113.     raise Exception.Create('Erreur lors de l''ouverture du périphérique audio');
  114.  
  115.   // buffers init
  116.   for i := 0 to BufferCount - 1 do
  117.   begin
  118.      ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  119.      with Form1.waveHeaders[i] do
  120.     begin
  121.       lpData := @Form1.buffers[i][0];
  122.       dwBufferLength := BufSize  ;
  123.       dwFlags := 0;
  124.     end;
  125.    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  126.    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  127.   end;
  128.   Form1.currentBuffer := 0;
  129.  
  130. end;
  131.  
  132. procedure TForm1.FormCreate(Sender: TObject);
  133. var
  134.   FileName : string;
  135.  begin
  136.  
  137.   libHandle := LoadLibrary('adlib.dll');
  138.   if libHandle = 0 then
  139.   begin
  140.     error := GetLastError;
  141.     ShowMessage('Erreur chargement DLL: ' + IntToStr(error));
  142.   end;
  143.  
  144.  //     FileName := 'kya.dmo';     // Benjamin Gérardin Twin-tracker
  145.  //  FileName := 'neo_intro.xms'; // the famous Neo-Intro when PC had FM OPL soundchip and AMIGA PAULA !!! :)
  146.   //  FileName := 'ezerious.hsc'; // Hannes Seifert / Input
  147.      FileName := 'neo intro.hsc';   // Neo-Intro hsc format
  148.  
  149.   core := CreateCore(44100,true,true,0); // create OPL core : srate / 16bit? / stereo? / core type 0,1,2,3
  150.     {Harekiet's, 0}        // CWemuopl
  151.     {Ken Silverman's, 1}   // CKemuopl
  152.     {Jarek Burczynski's, 2}// CEmuopl
  153.     {Nuked OPL3, 3}        // CNemuopl
  154.  
  155.   ShowMessage('Init OPL Core OK !! ' );
  156.   Set_OPLchip(2); // TYPE_OPL2, TYPE_OPL3, TYPE_DUAL_OPL2  -- 0/1/2 --
  157.   Init_Opl();
  158.  
  159.   opl :=  Load_Mod(Pchar(filename));
  160.  // Opl_Process(Form1.currentBuffer);
  161. end;
  162.  
  163. procedure TForm1.FormShow(Sender: TObject);
  164. begin
  165.      InitAudio;
  166.      ok_flag := true;
  167.  
  168. end;
  169.  
  170. procedure TForm1.Opl_Process(BufferIndex: Integer);
  171. var
  172.   smp_to_read: Integer;
  173. begin
  174.   smp_to_read := BufSize div (Channels * (BitsPerSample div 8));
  175.   Write_pcm(@buffers[BufferIndex][0], smp_to_read);
  176.   Player_Update();
  177. end;
  178.  
  179. procedure TForm1.Timer1Timer(Sender: TObject);
  180. begin
  181.  
  182. end;
  183.  
  184. end.
  185.  

Adlib Unit :

Code: Pascal  [Select][+][-]
  1. unit adlib;
  2.  
  3.  
  4. {$mode objfpc}{$H+}
  5.  
  6. interface
  7.  
  8. uses
  9.     windows;
  10.  
  11. const
  12.  
  13.    ADLIBDLL = 'adlib.dll';
  14.  
  15. type
  16.  
  17. PAplayer = record
  18. title : string;
  19. end;
  20. TAPlayer = ^PAplayer;
  21.  
  22.  
  23.  
  24. function  CreateCore(srate: integer; isBeat: Boolean; isStereo: Boolean; core : integer): Boolean; cdecl; external ADLIBDLL;
  25. procedure Init_Opl; cdecl; external ADLIBDLL;
  26. procedure Write_Pcm(buf: Pointer; samples: SmallInt); cdecl; external ADLIBDLL;
  27. function  Load_Mod(filename : Pchar  ): Pointer;  cdecl; external ADLIBDLL;
  28.  
  29. procedure Set_OPLchip(chip: integer); cdecl; external ADLIBDLL;
  30. //procedure Surround_Offset(offset : PDouble);cdecl; external ADLIBDLL;
  31. function Player_Update(): Boolean; cdecl; external ADLIBDLL;
  32. function Player_Get_Refresh: single; cdecl; external ADLIBDLL;
  33.  
  34. // music infos
  35. function Get_Music_Title(title: Pointer) :  Pchar; cdecl; external ADLIBDLL;
  36. function Get_Music_Type(tp: Pointer) :  Pchar; cdecl; external ADLIBDLL;
  37. function Get_Music_Author(aut: Pointer) :  Pchar; cdecl; external ADLIBDLL;
  38. function Get_Music_Desc(desc: Pointer) :  Pchar; cdecl; external ADLIBDLL;
  39. function Get_Music_Pattern(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  40. function Get_Music_Orders(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  41. function Get_Music_Order(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  42. function Get_Music_Row(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  43. function Get_Music_Speed(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  44. function Get_Music_Subsongs(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  45. function Get_Music_Subsong(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  46. function Get_Music_Instruments(pt: Pointer) :  Integer; cdecl; external ADLIBDLL;
  47.  
  48. implementation
  49.  
  50. end.
Sub Quantum Technology ! Gigatron 68000 Colmar France;

 

TinyPortal © 2005-2018