Lazarus

Programming => Graphics and Multimedia => Audio and Video => Topic started by: Gigatron on September 17, 2024, 01:59:12 am

Title: Game Music Emulator
Post by: Gigatron on September 17, 2024, 01:59:12 am
Hi nice people,

Today i would like to share usefull stuff: Game Music Emulator from 'BLARGG C++ library' + todo include crédits
I have no time to play gme_file but this is my first attempt at least write .wav file and play.
Later i will share the next improved version ;
Supported formats by this library :

gme_ay_type,
   gme_gbs_type,
   gme_gym_type,
   gme_hes_type,
   gme_kss_type,
   gme_nsf_type,
   gme_nsfe_type,
   gme_sap_type,
   gme_spc_type,
   gme_vgm_type,
   gme_vgz_type;

// test.nsf   file included ; nsf = nintendo system
 

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,gme,mmsystem,windows;
  9. const
  10.   FileName = 'test.nsf'; // opens this file (can be any music type)
  11.   SampleRate = 44100; // number of samples per second
  12.   Track = 0; //     (0 = first)
  13.   BufSize = 2048; //   multiple of 2
  14.  
  15. type
  16.   TWaveWriter = class
  17.   private
  18.     FFileStream: TFileStream;
  19.  
  20.     FSampleRate: LongWord;
  21.     FChannels: Word;
  22.     FBytesWritten: LongWord;
  23.   public
  24.     constructor Create(ASampleRate: LongWord; const AFileName: string);
  25.     destructor Destroy; override;
  26.     procedure EnableStereo;
  27.     procedure Write(const Buffer; Count: LongWord);
  28.     procedure Close;
  29.   end;
  30.  
  31. type
  32.  
  33.   { TForm1 }
  34.  
  35.   TForm1 = class(TForm)
  36.     procedure FormCreate(Sender: TObject);
  37.   private
  38.  
  39.   public
  40.    MyFile: AnsiString;
  41.    WavStream : TMemoryStream;
  42.   end;
  43.  
  44. var
  45.   Form1: TForm1;
  46.  
  47.  
  48.   WaveWriter: TWaveWriter;
  49.   Buf: array[0..BufSize-1] of SmallInt;
  50.  
  51. implementation
  52.  
  53. {$R *.lfm}
  54. // console mode to do
  55. procedure HandleError(const Str: PAnsiChar);
  56. begin
  57.   if Str <> nil then
  58.   begin
  59.     WriteLn('Error: ', Str);
  60.     ReadLn;
  61.     Halt(1);
  62.   end;
  63. end;
  64.  
  65. { TWaveWriter }
  66.  
  67. constructor TWaveWriter.Create(ASampleRate: LongWord; const AFileName: string);
  68. var
  69.   Header: array[0..43] of Byte;
  70. begin
  71.   FFileStream := TFileStream.Create(AFileName, fmCreate);
  72.   FSampleRate := ASampleRate;
  73.   FChannels := 2;
  74.   FBytesWritten := 0;
  75.  
  76.   // Write WAVE header
  77.   ZeroMemory(@Header, SizeOf(Header));
  78.   //FillChar(Header, SizeOf(Header), 0);
  79.   Move('RIFF', Header[0], 4);
  80.   Move('WAVEfmt ', Header[8], 8);
  81.   PLongWord(@Header[16])^ := 16;
  82.   PWord(@Header[20])^ := 1;  // PCM
  83.   PWord(@Header[22])^ := FChannels;
  84.   PLongWord(@Header[24])^ := FSampleRate;
  85.   PLongWord(@Header[28])^ := FSampleRate * FChannels * 2;
  86.   PWord(@Header[32])^ := FChannels * 2;
  87.   PWord(@Header[34])^ := 16;
  88.   Move('data', Header[36], 4);
  89.  
  90.   FFileStream.WriteBuffer(Header, SizeOf(Header));
  91. end;
  92.  
  93. destructor TWaveWriter.Destroy;
  94. begin
  95.   Close;
  96.   FFileStream.Free;
  97.   inherited;
  98. end;
  99.  
  100. procedure TWaveWriter.EnableStereo;
  101. begin
  102.   FChannels := 2;
  103. end;
  104.  
  105. procedure TWaveWriter.Write(const Buffer; Count: LongWord);
  106. begin
  107.   FFileStream.WriteBuffer(Buffer, Count * SizeOf(SmallInt));
  108.   Inc(FBytesWritten, Count * SizeOf(SmallInt));
  109. end;
  110.  
  111. procedure TWaveWriter.Close;
  112. var
  113.   FileSize: LongWord;
  114. begin
  115.   FileSize := FFileStream.Size - 8;
  116.   FFileStream.Seek(4, soFromBeginning);
  117.   FFileStream.WriteBuffer(FileSize, 4);
  118.   FFileStream.Seek(40, soFromBeginning);
  119.   FFileStream.WriteBuffer(FBytesWritten, 4);
  120. end;
  121.  
  122. { TForm1 }
  123.  
  124. procedure TForm1.FormCreate(Sender: TObject);
  125. var
  126.    Emu:  PMusic_Emu;
  127. begin
  128.  
  129.   HandleError(gme_open_file(PAnsiChar(FileName), Emu,SampleRate));
  130.  // ShowMessage('Fichier ok');
  131.  
  132.   // Vérifier le démarrage de la piste
  133.   HandleError(gme_start_track(Emu, Track));
  134.  // ShowMessage('Piste ok');
  135.  
  136.   // Ouverture du fichier WAV
  137.   WaveWriter := TWaveWriter.Create(SampleRate, 'out.wav');
  138.   try
  139.      WaveWriter.EnableStereo;
  140.  
  141.     // Enregistrement de 10 secondes
  142.     while gme_tell(Emu) < 10 * 1000 do
  143.     begin
  144.       // Remplir le tampon d'échantillons
  145.       HandleError(gme_play(Emu, BufSize, @Buf[0]));
  146.       WaveWriter.Write(Buf, BufSize);
  147.     end;
  148.  
  149.   finally
  150.     // Nettoyage
  151.     gme_delete(Emu);
  152.     WaveWriter.Free;
  153.   end;
  154.    // lecture du wave genere
  155.     MyFile := Application.Location + 'out.wav';
  156.     WavStream := TMemoryStream.Create;
  157.     WavStream.LoadFromFile(MyFile);
  158.  
  159.     PlaySound(WavStream.Memory, 0, SND_NODEFAULT or SND_ASYNC or SND_MEMORY);
  160.  
  161. end;
  162.  
  163. end.
  164.  


Gme Unit :

Code: Pascal  [Select][+][-]
  1. unit GME;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. const
  8.   GME_VERSION = $000603; // 1 byte major, 1 byte minor, 1 byte patch-level
  9.  
  10. type
  11.   PMusic_Emu = ^TMusic_Emu;
  12.   TMusic_Emu = record
  13.     // to doo
  14.   end;
  15.  
  16.   gme_err_t = PAnsiChar;
  17.  
  18.   Pgme_info_t = ^Tgme_info_t;
  19.   Tgme_info_t = record
  20.     length: Integer;
  21.     intro_length: Integer;
  22.     loop_length: Integer;
  23.     play_length: Integer;
  24.     i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15: Integer;
  25.     system: PAnsiChar;
  26.     game: PAnsiChar;
  27.     song: PAnsiChar;
  28.     author: PAnsiChar;
  29.     copyright: PAnsiChar;
  30.     comment: PAnsiChar;
  31.     dumper: PAnsiChar;
  32.     s7, s8, s9, s10, s11, s12, s13, s14, s15: PAnsiChar;
  33.   end;
  34.  
  35.   Tgme_equalizer_t = record
  36.     treble: Double;
  37.     bass: Double;
  38.     d2, d3, d4, d5, d6, d7, d8, d9: Double;
  39.   end;
  40.  
  41.   gme_type_t = Pointer;
  42.  
  43.   gme_reader_t = function(your_data: Pointer; out_: Pointer; count: Integer): gme_err_t; cdecl;
  44.   gme_user_cleanup_t = procedure(user_data: Pointer); cdecl;
  45.  
  46. const
  47.   gme_info_only = -1;
  48.  
  49. var
  50.   gme_ay_type: gme_type_t; external;
  51.   gme_gbs_type: gme_type_t; external;
  52.   gme_gym_type: gme_type_t; external;
  53.   gme_hes_type: gme_type_t; external;
  54.   gme_kss_type: gme_type_t; external;
  55.   gme_nsf_type: gme_type_t; external;
  56.   gme_nsfe_type: gme_type_t; external;
  57.   gme_sap_type: gme_type_t; external;
  58.   gme_spc_type: gme_type_t; external;
  59.   gme_vgm_type: gme_type_t; external;
  60.   gme_vgz_type: gme_type_t; external;
  61.  
  62. //  gme_wrong_file_type: PAnsiChar;   'gme.dll' name 'gme_';
  63.  
  64.  //function gme_wrong_file_type: PAnsiChar; cdecl; external 'gme.dll' name 'gme_wrong_file_type';
  65.   gme_wrong_file_type: PAnsiChar; external 'gme.dll' name 'gme_wrong_file_type';
  66.  
  67. // Basic operations
  68. function gme_open_file(path: PAnsiChar; out out_: PMusic_Emu; sample_rate: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_open_file';
  69. function gme_track_count(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_track_count';
  70. function gme_start_track(emu: PMusic_Emu; index: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_start_track';
  71. function gme_play(emu: PMusic_Emu; count: Integer; out_: PSmallInt): gme_err_t; cdecl; external 'gme.dll' name 'gme_play';
  72. procedure gme_delete(emu: PMusic_Emu); cdecl; external 'gme.dll' name 'gme_delete';
  73.  
  74. // Track position/length
  75. procedure gme_set_fade(emu: PMusic_Emu; start_msec: Integer); cdecl; external 'gme.dll' name 'gme_set_fade';
  76. procedure gme_set_autoload_playback_limit(emu: PMusic_Emu; do_autoload_limit: Integer); cdecl; external 'gme.dll' name 'gme_set_autoload_playback_limit';
  77. function gme_autoload_playback_limit(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_autoload_playback_limi';
  78. function gme_track_ended(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_track_ended';
  79. function gme_tell(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_tell';
  80. function gme_tell_samples(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_tell_samples';
  81. function gme_seek(emu: PMusic_Emu; msec: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_seek';
  82. function gme_seek_samples(emu: PMusic_Emu; n: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_seek_samples';
  83.  
  84. // Informational
  85. function gme_warning(emu: PMusic_Emu): PAnsiChar; cdecl; external 'gme.dll' name 'gme_warning';
  86. function gme_load_m3u(emu: PMusic_Emu; path: PAnsiChar): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_m3u';
  87. procedure gme_clear_playlist(emu: PMusic_Emu); cdecl; external 'gme.dll' name 'gme_clear_playlist';
  88. function gme_track_info(const emu: PMusic_Emu; out out_: Pgme_info_t; track: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_track_info';
  89. procedure gme_free_info(info: Pgme_info_t); cdecl; external 'gme.dll'  name 'gme_free_info';
  90.  
  91. // Advanced playback
  92. procedure gme_set_stereo_depth(emu: PMusic_Emu; depth: Double); cdecl; external 'gme.dll' name 'gme_set_stereo_depth';
  93. procedure gme_ignore_silence(emu: PMusic_Emu; ignore: Integer); cdecl; external 'gme.dll' name 'gme_ignore_silence';
  94. procedure gme_set_tempo(emu: PMusic_Emu; tempo: Double); cdecl; external 'gme.dll' name 'gme_set_tempo';
  95. function gme_voice_count(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_voice_count';
  96. function gme_voice_name(const emu: PMusic_Emu; i: Integer): PAnsiChar; cdecl; external 'gme.dll' name 'gme_voice_name';
  97. procedure gme_mute_voice(emu: PMusic_Emu; index: Integer; mute: Integer); cdecl; external 'gme.dll' name 'gme_mute_voice';
  98. procedure gme_mute_voices(emu: PMusic_Emu; muting_mask: Integer); cdecl; external 'gme.dll' name 'gme_mute_voices';
  99. procedure gme_equalizer(const emu: PMusic_Emu; out out_: Tgme_equalizer_t); cdecl; external 'gme.dll' name 'gme_equalizer';
  100. procedure gme_set_equalizer(emu: PMusic_Emu; const eq: Tgme_equalizer_t); cdecl; external 'gme.dll' name 'gme_set_equalizer';
  101. procedure gme_enable_accuracy(emu: PMusic_Emu; enabled: Integer); cdecl; external 'gme.dll' name 'gme_enable_accuracy';
  102.  
  103. // Game music types
  104. function gme_type(const emu: PMusic_Emu): gme_type_t; cdecl; external 'gme.dll' name 'gme_type';
  105. function gme_type_list: gme_type_t; cdecl; external 'gme.dll' name 'gme_type_list';
  106. function gme_type_system(type_: gme_type_t): PAnsiChar; cdecl; external 'gme.dll' name 'gme_type_system';
  107. function gme_type_multitrack(type_: gme_type_t): Integer; cdecl; external 'gme.dll' name 'gme_type_multitrack';
  108. function gme_multi_channel(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_multi_channel';
  109.  
  110. // Advanced file loading
  111. function gme_open_data(const data: Pointer; size: LongInt; out out_: PMusic_Emu; sample_rate: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_open_data';
  112. function gme_identify_header(const header: Pointer): PAnsiChar; cdecl; external 'gme.dll' name 'gme_identify_header';
  113. function gme_identify_extension(path_or_extension: PAnsiChar): gme_type_t; cdecl; external 'gme.dll' name 'gme_identify_extension';
  114. function gme_type_extension(music_type: gme_type_t): PAnsiChar; cdecl; external 'gme.dll' name 'gme_type_extension';
  115. function gme_identify_file(path: PAnsiChar; out type_out: gme_type_t): gme_err_t; cdecl; external 'gme.dll' name 'gme_identify_file';
  116. function gme_new_emu(type_: gme_type_t; sample_rate: Integer): PMusic_Emu; cdecl; external 'gme.dll' name 'gme_new_emu';
  117. function gme_new_emu_multi_channel(type_: gme_type_t; sample_rate: Integer): PMusic_Emu; cdecl; external 'gme.dll' name 'gme_new_emu_multi_channel';
  118. function gme_load_file(emu: PMusic_Emu; path: PAnsiChar): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_data';
  119. function gme_load_data(emu: PMusic_Emu; const data: Pointer; size: LongInt): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_data';
  120. function gme_load_custom(emu: PMusic_Emu; reader: gme_reader_t; file_size: LongInt; your_data: Pointer): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_custom';
  121. function gme_load_m3u_data(emu: PMusic_Emu; const data: Pointer; size: LongInt): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_m3u_data';
  122.  
  123. // User data
  124. procedure gme_set_user_data(emu: PMusic_Emu; new_user_data: Pointer); cdecl; external 'gme.dll' name 'gme_set_user_data';
  125. function gme_user_data(const emu: PMusic_Emu): Pointer; cdecl;external 'gme.dll' name 'gme_user_data';
  126. procedure gme_set_user_cleanup(emu: PMusic_Emu; func: gme_user_cleanup_t); cdecl; external 'gme.dll' name 'gme_set_user_cleanup';
  127.  
  128. implementation
  129.  
  130. end.

Title: Re: Game Music Emulator
Post by: Guva on September 17, 2024, 03:56:07 pm
 I see if gme_ay_type is a zx spectrum?
Title: Re: Game Music Emulator
Post by: TRon on September 17, 2024, 04:07:28 pm
@Gigatron:
Thank you for the work. A bit disappointed to see windows (and static headers) only though  :P

fwiw the whole idea behind things like libao, portaudio and friends is to play audio in a platform agnostic manner (and yes that does require to install a library on windows (or place the dll next to your executable).

if you can decode a specific buffersize (as per your example) then it should be possible to play the decoded (wav) buffer live. In the case gme or the used sound-output solution  does not buffer the decoding then you can either do it manually or use something like a thread/timer.

I am still working on the portaudio stuff so I'll have a better look at these gme headers later.
Title: Re: Game Music Emulator
Post by: Fred vS on September 17, 2024, 05:46:30 pm
I am still working on the portaudio stuff...
Excellent (and best imho) choice!
There is a fpc portaudio header with dynamic loading here (https://github.com/fredvs/uos/blob/main/src/uos_portaudio.pas).
Title: Re: Game Music Emulator
Post by: TRon on September 17, 2024, 06:10:16 pm
Excellent (and best imho) choice!
I personally dislike portaudio (too much issues in the past).

I find libao much easier to use but that is personal preference.

Quote
There is a fpc portaudio header with dynamic loading here (https://github.com/fredvs/uos/blob/main/src/uos_portaudio.pas).
Yes, thank you for that fred vS.

Unfortunately there are some issues with those headers (conversion errors, you could compare when I cleaned up and posted mine) and I personally like consistency e.g. types prefixed with T and P (as i did for the other headers).
Title: Re: Game Music Emulator
Post by: Fred vS on September 17, 2024, 06:50:26 pm
Excellent (and best imho) choice!
I personally dislike portaudio (too much issues in the past).
I find libao much easier to use but that is personal preference.

Indeed color and taste are personal.
I liked Xiph when it planned opus format project but it seems that their passion is gone.
Last release of libao is 1.2.0 - January 27, 2014. and last commit in trunk is 5 years ago.
Last release of PortAudio is 19.7.0 - April 6, 2021 and last commit in trunk is 2 weeks ago.
Indeed PortAudio is not easy but it has much more custom access to devices than libao.

That said, libao is very good too.



Title: Re: Game Music Emulator
Post by: Gigatron on September 17, 2024, 11:02:11 pm
Hi ,

Thank you for your advices.... ok the first gme library was to record wav audio, this one is playing music like xmplayer.

*** Edit :  If the player is not playing and no error found, increase track number , the first defaut track is 0, some games have 256 tracks or subsong, you must find them... will study this soon !

  the project attached in zip format;
 
main unit for code analysis !

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, ExtCtrls,
  9.   gme, mmsystem, windows;
  10.  
  11. const
  12.  
  13.   Channels = 2;
  14.   BitsPerSample = 16;
  15.  
  16.   FileName = 'gof.spc'; // opens this file (can be any music type)
  17.   SampleRate = 44100; // number of samples per second
  18.   Track = 0; //     (0 = first)
  19.   BufSize = 16384 ; //   multiple of 2
  20.   BufferCount = 2;
  21.  
  22. type
  23.  
  24.   { TForm1 }
  25.  
  26.   TForm1 = class(TForm)
  27.     Label1: TLabel;
  28.     Timer1: TTimer;
  29.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  30.     procedure FormCreate(Sender: TObject);
  31.     procedure Timer1Timer(Sender: TObject);
  32.   private
  33.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
  34.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  35.      currentBuffer: Integer;
  36.   public
  37.  
  38.   end;
  39.  
  40. var
  41.   Form1: TForm1;
  42.   waveOut: HWAVEOUT;
  43.   waveHeader: TWaveHdr;
  44.   Emu:  PMusic_Emu;
  45.   info: Pgme_info_t;
  46.   ok_flag : boolean = false;
  47.  
  48. implementation
  49.  
  50. {$R *.lfm}
  51.  
  52. /// audio init et le reste !!
  53. procedure HandleError(const Str: PAnsiChar);
  54. begin
  55.   if Str <> nil then
  56.   begin
  57.     WriteLn('Error: ', Str);
  58.     ReadLn;
  59.     Halt(1);
  60.   end;
  61. end;
  62.  
  63. procedure FillBuffer(bufferIndex: Integer);
  64. begin
  65.   if ok_flag then
  66.   begin
  67.    gme_play(Emu, BufSize, @Form1.buffers[bufferIndex][0]);
  68.    Form1.waveHeaders[bufferIndex].dwFlags := Form1.waveHeaders[bufferIndex].dwFlags and (not WHDR_DONE);
  69.   end;
  70. end;
  71.  
  72. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  73. begin
  74.   if uMsg = WOM_DONE then
  75.   begin
  76.     FillBuffer(Form1.currentBuffer);
  77.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  78.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  79.   end;
  80.   Result := 0;
  81. end;
  82.  
  83. procedure InitAudio;
  84. var
  85.   wFormat: TWaveFormatEx;
  86.   i: Integer;
  87. begin
  88.  
  89.   SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
  90.  
  91.   with wFormat do
  92.   begin
  93.     wFormatTag := WAVE_FORMAT_PCM;
  94.     nChannels := Channels;
  95.     nSamplesPerSec := SampleRate;
  96.     wBitsPerSample := BitsPerSample;
  97.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  98.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  99.     cbSize := 0;
  100.   end;
  101.  
  102.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  103.     raise Exception.Create('Erreur ouverture periph audio');
  104.  
  105.   // buffers
  106.   for i := 0 to BufferCount - 1 do
  107.   begin
  108.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  109.      with Form1.waveHeaders[i] do
  110.     begin
  111.       lpData := @Form1.buffers[i][0];
  112.       dwBufferLength := BufSize * SizeOf(SmallInt);
  113.       dwFlags := 0;
  114.     end;
  115.     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  116.   end;
  117.   Form1.currentBuffer := 0;
  118. end;
  119.  
  120.  
  121. { TForm1 }
  122.  
  123. procedure TForm1.FormCreate(Sender: TObject);
  124. begin
  125.   InitAudio;
  126.   HandleError(gme_open_file(PAnsiChar(FileName), Emu, SampleRate));
  127.   HandleError(gme_start_track(Emu, Track));   /// **** Edit
  128.   ok_flag := true;
  129.  
  130.   // Remplir et envoyer les deux premiers buffers
  131.   FillBuffer(0);
  132.   FillBuffer(1);
  133.   waveOutWrite(waveOut, @waveHeaders[0], SizeOf(TWaveHdr));
  134.   waveOutWrite(waveOut, @waveHeaders[1], SizeOf(TWaveHdr));
  135.  
  136.   Timer1.Enabled := True;
  137.  
  138.      HandleError( gme_track_info( emu, &info, track ) );
  139.      label1.Caption := label1.Caption +'System : ' + (info^.systeme)+#10 ;
  140.      label1.Caption := label1.Caption + 'Game : ' + (info^.game)+#10;
  141.      label1.Caption := label1.Caption + 'Song : ' + (info^.song)+#10;
  142.      label1.Caption := label1.Caption +'Author : ' + (info^.author)+#10;
  143.      label1.Caption := label1.Caption +'Copyright : ' + (info^.copyright)+#10;
  144.      label1.Caption := label1.Caption +'Comment : ' + (info^.comment)+#10;
  145.      label1.Caption := label1.Caption +'Dumper : ' + (info^.dumper)+#10;
  146.  
  147.  
  148. end;
  149.  
  150. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  151. var
  152.   i: Integer;
  153. begin
  154.   Timer1.Enabled := False;
  155.  
  156.   for i := 0 to BufferCount - 1 do
  157.    waveOutUnprepareHeader(waveOut, @waveHeaders[i], SizeOf(TWaveHdr));
  158.    waveOutClose(waveOut);
  159.   gme_delete(Emu);
  160. end;
  161.  
  162. procedure TForm1.Timer1Timer(Sender: TObject);
  163. begin
  164.    if ok_flag then
  165.   begin
  166.  
  167.     // si fini then end
  168.     if gme_track_ended(Emu) <> 0 then
  169.     begin
  170.       Timer1.Enabled := False;
  171.  
  172.     end;
  173.   end;
  174. end;
  175.  
  176. end.
  177.  


Title: Re: Game Music Emulator
Post by: Fred vS on September 18, 2024, 12:28:56 am
Hi ,
  the project attached in zip format;
Excellent!
Many thanks, it works great!   ;D
Title: Re: Game Music Emulator
Post by: Fred vS on September 18, 2024, 04:15:46 am
Hello Gigatron.

Here your updated project with Linux target.
For Linux it uses the root alsa lib for audio.
Also gme.pas has dynamic loading.
Once again, many thanks for this great addition.

Fre;D
Title: Re: Game Music Emulator
Post by: TRon on September 18, 2024, 04:24:36 am
Ah, you just crossed me Fred  :)

my feeble attempt...
Title: Re: Game Music Emulator
Post by: Guva on September 18, 2024, 05:25:58 am
Music archive for ZX spectrum and Amstrad CPC

http://projectay.dl.spilsby.net/ProjectAY.zip

http://projectay.dl.spilsby.net/Update-06March2008.zip

SNES Music over 1500 files

http://snesmusic.org/v2/download.php?torrentNow=spcsets.torrent
Title: Re: Game Music Emulator
Post by: Gigatron on September 18, 2024, 05:32:28 pm
Ok , i'am very happy if this library is usefull for lazarus FPC users,
so thank you @Tron , @Fred vS and @Guva.

My initial goal was how to  calling external external .dll function from FPC and very inspired from @Guva Raylaz.pas unit (thx alot).

The next goal is Amiga delitracker ? maybe :) I can know export .dll function with visual studio 2019/2022 if got c++ sources just for
my system X64 , (X86 or Linux is not made)  this job is for @Tron or  @Fred vS :)

Regards

Gtr 
Title: Re: Game Music Emulator
Post by: TRon on September 18, 2024, 06:05:58 pm
Ok , i'am very happy if this library is usefull for lazarus FPC users,
It is. Most bindings are for that matter.

The unfortunate thing is that those bindings are (or seem) all scattered over the internets which makes them hard to find and also seems to get outdated pretty quick because nobody seems to know and/or use them.

Quote
The next goal is Amiga delitracker ? maybe :) I can know export .dll function with visual studio 2019/2022 if got c++ sources just for
my system X64 , (X86 or Linux is not made)  this job is for @Tron or  @Fred vS :)
Although for windows it is not required because of the delayed loading, please consider creating headers that load the library and its functions dynamically. That works the same for windows as it does for all other platforms and saves us a butt-load of time  :)
Title: Re: Game Music Emulator
Post by: TRon on September 18, 2024, 06:49:15 pm
The next goal is Amiga delitracker ? maybe :)
I completely overlooked that tiny part of the paragraph  :)

You mean delitracker itself or uade ?

Last time I checked there was not a separated library and static linking was not an options for uade (xmp did allow static linking, at least in the past, but I was not aware there was a library which ofc makes things much easier to handle)

Would be nice if you were able to get somewhere with uade because it is one of the best re-players/emulators around.
Title: Re: Game Music Emulator
Post by: Gigatron on September 18, 2024, 06:59:55 pm
The next goal is Amiga delitracker ? maybe :)
I completely overlooked that tiny part of the paragraph  :)

You mean delitracker itself or uade ?

Last time I checked there was not a separated library and static linking was not an options for uade (xmp did allow static linking, at least in the past, but I was not aware there was a library which ofc makes things much easier to handle)

Would be nice if you were able to get somewhere with uade because it is one of the best re-players/emulators around.

I will look inside uade code, if you know https://support.xmplay.com/ (https://support.xmplay.com/) there is a plugin called delix wich play all obscure format from amiga
it use starscream mc68000 emulator (for amiga hardware emulation) https://support.xmplay.com/files_view.php?file_id=499 (https://support.xmplay.com/files_view.php?file_id=499).
Eagle player on amiga and source is now open....
Maybe one day we can see delitracker for lazarus fpc , why not ? :)



Title: Re: Game Music Emulator
Post by: Fred vS on September 19, 2024, 01:00:26 am
Music archive for ZX spectrum and Amstrad CPC

http://projectay.dl.spilsby.net/ProjectAY.zip

http://projectay.dl.spilsby.net/Update-06March2008.zip

Yep, other meat for next winter, many thanks!  ;)

There is a console demo for Windows amd64 and Linux amd64 here (https://github.com/fredvs/gme_pas).
Title: Re: Game Music Emulator
Post by: Guva on December 07, 2024, 04:55:43 am
In the latest version of GME, something broke with the Naked emulator.
Therefore, GYM, VGM/VGZ files are not played correctly.

Compile the solution with another emulator.
cmake -DGME_YM2612_EMU=MAME .. or cmake -DGME_YM2612_EMU=GENS ..

headers for the latest version
Code: Pascal  [Select][+][-]
  1. unit binding.libgme;
  2.  
  3. // libgme 0.6.4 headers for fpc
  4.  
  5. {$mode objfpc}{$H+}
  6. {$packrecords c}
  7.  
  8. interface
  9.  
  10. uses
  11.   ctypes;
  12.  
  13. const
  14.   {$ifdef linux}
  15.       library_name = 'libgme.so.0.6.4';
  16.   {$endif}
  17.     {$ifdef windows}
  18.       library_name = 'gme.dll';
  19.   {$endif}
  20.  
  21. const
  22.   GME_VERSION = $000604;
  23.  
  24.  
  25. type
  26.   // Forward declarations
  27.   Pgme_type_t_ = Pointer;
  28.   PPgme_type_t_ = ^Pgme_type_t_;
  29.  
  30.   Pgme_info_t = ^gme_info_t;
  31.   PPgme_info_t = ^Pgme_info_t;
  32.  
  33.   Pgme_equalizer_t = ^gme_equalizer_t;
  34.  
  35.   gme_err_t = PUTF8Char;
  36.   PMusic_Emu = Pointer;
  37.   PPMusic_Emu = ^PMusic_Emu;
  38.  
  39.   (******** Informational ********)
  40.   _anonymous_type_1 = (
  41.     gme_info_only = -1);
  42.   P_anonymous_type_1 = ^_anonymous_type_1;
  43.  
  44.   gme_info_t = record
  45.     length: Integer;
  46.     intro_length: Integer;
  47.     loop_length: Integer;
  48.     play_length: Integer;
  49.     fade_length: Integer;
  50.     i5: Integer;
  51.     i6: Integer;
  52.     i7: Integer;
  53.     i8: Integer;
  54.     i9: Integer;
  55.     i10: Integer;
  56.     i11: Integer;
  57.     i12: Integer;
  58.     i13: Integer;
  59.     i14: Integer;
  60.     i15: Integer;
  61.     system: PUTF8Char;
  62.     game: PUTF8Char;
  63.     song: PUTF8Char;
  64.     author: PUTF8Char;
  65.     copyright: PUTF8Char;
  66.     comment: PUTF8Char;
  67.     dumper: PUTF8Char;
  68.     s7: PUTF8Char;
  69.     s8: PUTF8Char;
  70.     s9: PUTF8Char;
  71.     s10: PUTF8Char;
  72.     s11: PUTF8Char;
  73.     s12: PUTF8Char;
  74.     s13: PUTF8Char;
  75.     s14: PUTF8Char;
  76.     s15: PUTF8Char;
  77.   end;
  78.  
  79.   gme_equalizer_t = record
  80.     treble: Double;
  81.     bass: Double;
  82.     d2: Double;
  83.     d3: Double;
  84.     d4: Double;
  85.     d5: Double;
  86.     d6: Double;
  87.     d7: Double;
  88.     d8: Double;
  89.     d9: Double;
  90.   end;
  91.  
  92.   gme_type_t = Pointer;
  93.   Pgme_type_t = ^gme_type_t;
  94.  
  95.   gme_reader_t = function(your_data: Pointer; &out: Pointer; count: Integer): gme_err_t; cdecl;
  96.  
  97.   gme_user_cleanup_t = procedure(user_data: Pointer); cdecl;
  98.  
  99. var
  100. (******** Basic operations ********)
  101.   gme_open_file: function(path: PAnsiChar; &out: PPMusic_Emu; sample_rate: Integer): gme_err_t; cdecl;
  102.   gme_track_count: function(const p1: PMusic_Emu): Integer; cdecl;
  103.   gme_start_track: function(p1: PMusic_Emu; index: Integer): gme_err_t; cdecl;
  104.   gme_play: function(p1: PMusic_Emu; count: Integer; &out: PSmallint): gme_err_t; cdecl;
  105.   gme_delete: procedure(p1: PMusic_Emu); cdecl;
  106. (******** Track position/length ********)
  107.   gme_set_fade: procedure(p1: PMusic_Emu; start_msec: Integer); cdecl;
  108. (** See gme_set_fade.* @since 0.6.4*)
  109.   gme_set_fade_msecs: procedure(p1: PMusic_Emu; start_msec: Integer; length_msecs: Integer); cdecl;
  110. (**
  111.  * If do_autoload_limit is nonzero, then automatically load track length
  112.  * metadata (if present) and terminate playback once the track length has been
  113.  * reached. Otherwise playback will continue for an arbitrary period of time
  114.  * until a prolonged period of silence is detected.
  115.  *
  116.  * Not all individual emulators support this setting.
  117.  *
  118.  * By default, playback limits are loaded and applied.
  119.  *
  120.  * @since 0.6.3
  121.  *)
  122. gme_set_autoload_playback_limit: procedure(p1: PMusic_Emu; do_autoload_limit: Integer); cdecl;
  123. (** See gme_set_autoload_playback_limit.
  124.  * (This was actually added in 0.6.3, but wasn't exported because of a typo.)
  125.  * @since 0.6.4
  126.  *)
  127.   gme_autoload_playback_limit: function(const p1: PMusic_Emu): Integer; cdecl;
  128.   gme_track_ended: function(const p1: PMusic_Emu): Integer; cdecl;
  129.   gme_tell: function(const p1: PMusic_Emu): Integer; cdecl;
  130.   gme_tell_samples: function(const p1: PMusic_Emu): Integer; cdecl;
  131.   gme_seek: function(p1: PMusic_Emu; msec: Integer): gme_err_t; cdecl;
  132.   gme_seek_samples: function(p1: PMusic_Emu; n: Integer): gme_err_t; cdecl;
  133.   gme_warning: function(p1: PMusic_Emu): PUTF8Char; cdecl;
  134.   gme_load_m3u: function(p1: PMusic_Emu; path: PUTF8Char): gme_err_t; cdecl;
  135.   gme_clear_playlist: procedure(p1: PMusic_Emu); cdecl;
  136.   gme_track_info: function(const p1: PMusic_Emu; &out: PPgme_info_t; track: Integer): gme_err_t; cdecl;
  137.   gme_free_info: procedure(p1: Pgme_info_t); cdecl;
  138. (******** Advanced playback ********)
  139. gme_set_stereo_depth: procedure(p1: PMusic_Emu; depth: Double); cdecl;
  140. gme_ignore_silence: procedure(p1: PMusic_Emu; ignore: Integer); cdecl;
  141. gme_set_tempo: procedure(p1: PMusic_Emu; tempo: Double); cdecl;
  142. gme_voice_count: function(const p1: PMusic_Emu): Integer; cdecl;
  143. gme_voice_name: function(const p1: PMusic_Emu; i: Integer): PUTF8Char; cdecl;
  144. gme_mute_voice: procedure(p1: PMusic_Emu; index: Integer; mute: Integer); cdecl;
  145. gme_mute_voices: procedure(p1: PMusic_Emu; muting_mask: Integer); cdecl;
  146. gme_disable_echo: procedure(p1: PMusic_Emu; disable: Integer); cdecl;
  147. gme_equalizer: procedure(const p1: PMusic_Emu; &out: Pgme_equalizer_t); cdecl;
  148. gme_set_equalizer: procedure(p1: PMusic_Emu; const eq: Pgme_equalizer_t); cdecl;
  149. gme_enable_accuracy: procedure(p1: PMusic_Emu; enabled: Integer); cdecl;
  150. gme_type: function(const p1: PMusic_Emu): gme_type_t; cdecl;
  151. gme_type_list: function(): Pgme_type_t; cdecl;
  152. gme_type_system: function(p1: gme_type_t): PUTF8Char; cdecl;
  153. gme_type_multitrack: function(p1: gme_type_t): Integer; cdecl;
  154. gme_multi_channel: function(const p1: PMusic_Emu): Integer; cdecl;
  155. gme_open_data: function(const data: Pointer; size: Longint; &out: PPMusic_Emu; sample_rate: Integer): gme_err_t; cdecl;
  156. gme_identify_header: function(const header: Pointer): PUTF8Char; cdecl;
  157. gme_identify_extension: function(path_or_extension: PUTF8Char): gme_type_t; cdecl;
  158. (**
  159.  * Get typical file extension for a given music type.  This is not a replacement
  160.  * for a file content identification library (but see gme_identify_header).
  161.  *
  162.  * @since 0.6.3
  163.  *)
  164. gme_type_extension: function(music_type: gme_type_t): PUTF8Char; cdecl;
  165. gme_identify_file: function(path: PUTF8Char; type_out: Pgme_type_t): gme_err_t; cdecl;
  166. gme_new_emu: function(p1: gme_type_t; sample_rate: Integer): PMusic_Emu; cdecl;
  167. gme_new_emu_multi_channel: function(p1: gme_type_t; sample_rate: Integer): PMusic_Emu; cdecl;
  168. gme_load_file: function(p1: PMusic_Emu; path: PUTF8Char): gme_err_t; cdecl;
  169. gme_load_data: function(p1: PMusic_Emu; const data: Pointer; size: Longint): gme_err_t; cdecl;
  170. gme_load_tracks: function(me: PMusic_Emu; const data: Pointer; sizes: PLongint; count: Integer): gme_err_t; cdecl;
  171. gme_fixed_track_count: function(p1: gme_type_t): Integer; cdecl;
  172. gme_load_custom: function(p1: PMusic_Emu; p2: gme_reader_t; file_size: Longint; your_data: Pointer): gme_err_t; cdecl;
  173. gme_load_m3u_data: function(p1: PMusic_Emu; const data: Pointer; size: Longint): gme_err_t; cdecl;
  174. (******** User data ********)
  175. gme_set_user_data: procedure(p1: PMusic_Emu; new_user_data: Pointer); cdecl;
  176. gme_user_data: function(const p1: PMusic_Emu): Pointer; cdecl;
  177. gme_set_user_cleanup: procedure(p1: PMusic_Emu; func: gme_user_cleanup_t); cdecl;
  178.  
  179. procedure LoadLib(const aLibName: string);
  180.  
  181. implementation
  182.  
  183. uses
  184.   sysutils, dynlibs;
  185.  
  186. var
  187.   gme_handle: TLibHandle;
  188.  
  189.  
  190. procedure LoadLib(const aLibName: string);
  191. var LibName : string = '';
  192. begin
  193.   if FileExists('.' + PathDelim + aLibName)
  194.    then LibName := '.' + PathDelim + aLibName
  195.     else
  196.      if FileExists('lib' + PathDelim + aLibName)
  197.       then LibName := 'lib' + PathDelim + aLibName;
  198.  
  199.   if libName = ''
  200.     then libName := aLibName;
  201.  
  202.   gme_handle := LoadLibrary(PChar(LibName));
  203.  
  204.   if gme_handle = 0
  205.     then raise Exception.Create(Format('Could not load library: %s',[LibName]));
  206.  
  207.   //******** Basic operations ********/
  208.   pointer(gme_open_file)                   := GetProcAddress(gme_handle, 'gme_open_file');
  209.   pointer(gme_track_count)                 := GetProcAddress(gme_handle, 'gme_track_count');
  210.   pointer(gme_start_track)                 := GetProcAddress(gme_handle, 'gme_start_track');
  211.   pointer(gme_play)                        := GetProcAddress(gme_handle, 'gme_play');
  212.   pointer(gme_delete)                      := GetProcAddress(gme_handle, 'gme_delete');
  213.   //******** Track position/length ********/
  214.   pointer(gme_set_fade)                    := GetProcAddress(gme_handle, 'gme_set_fade');
  215.   pointer(gme_set_autoload_playback_limit) := GetProcAddress(gme_handle, 'gme_set_autoload_playback_limit');
  216.   pointer(gme_autoload_playback_limit)     := GetProcAddress(gme_handle, 'gme_autoload_playback_limit');
  217.   pointer(gme_track_ended)                 := GetProcAddress(gme_handle, 'gme_track_ended');
  218.   pointer(gme_tell)                        := GetProcAddress(gme_handle, 'gme_tell');
  219.   pointer(gme_tell_samples)                := GetProcAddress(gme_handle, 'gme_tell_samples');
  220.   pointer(gme_seek)                        := GetProcAddress(gme_handle, 'gme_seek');
  221.   pointer(gme_seek_samples)                := GetProcAddress(gme_handle, 'gme_seek_samples');
  222.   //******** Informational ********/
  223.   pointer(gme_warning)                     := GetProcAddress(gme_handle, 'gme_warning');
  224.   pointer(gme_load_m3u)                    := GetProcAddress(gme_handle, 'gme_load_m3u');
  225.   pointer(gme_clear_playlist)              := GetProcAddress(gme_handle, 'gme_clear_playlist');
  226.   pointer(gme_track_info)                  := GetProcAddress(gme_handle, 'gme_track_info');
  227.   pointer(gme_free_info)                   := GetProcAddress(gme_handle, 'gme_free_info');
  228.   //******** Advanced playback ********/
  229.   pointer(gme_set_stereo_depth)            := GetprocAddress(gme_handle, 'gme_set_stereo_depth');
  230.   pointer(gme_ignore_silence)              := GetprocAddress(gme_handle, 'gme_ignore_silence');
  231.   pointer(gme_set_tempo)                   := GetprocAddress(gme_handle, 'gme_set_tempo');
  232.   pointer(gme_voice_count)                 := GetprocAddress(gme_handle, 'gme_voice_count');
  233.   pointer(gme_voice_name)                  := GetprocAddress(gme_handle, 'gme_voice_name');
  234.   pointer(gme_mute_voice)                  := GetprocAddress(gme_handle, 'gme_mute_voice');
  235.   pointer(gme_mute_voices)                 := GetprocAddress(gme_handle, 'gme_mute_voices');
  236.   pointer(gme_equalizer)                   := GetprocAddress(gme_handle, 'gme_equalizer');
  237.   pointer(gme_set_equalizer)               := GetprocAddress(gme_handle, 'gme_set_equalizer');
  238.   pointer(gme_enable_accuracy)             := GetprocAddress(gme_handle, 'gme_enable_accuracy');
  239.   //******** Game music types ********/
  240.   pointer(gme_type)                        := GetProcAddress(gme_handle, 'gme_type');
  241.   pointer(gme_type_list)                   := GetProcAddress(gme_handle, 'gme_type_list');
  242.   pointer(gme_type_system)                 := GetProcAddress(gme_handle, 'gme_type_system');
  243.   pointer(gme_type_multitrack)             := GetProcAddress(gme_handle, 'gme_type_multitrack');
  244.   pointer(gme_multi_channel)               := GetProcAddress(gme_handle, 'gme_multi_channel');
  245.   //******** Advanced file loading ********/
  246.   pointer(gme_open_data)                   := GetProcAddress(gme_handle, 'gme_open_data');
  247.   pointer(gme_identify_header)             := GetProcAddress(gme_handle, 'gme_identify_header');
  248.   pointer(gme_identify_extension)          := GetProcAddress(gme_handle, 'gme_identify_extension');
  249.   pointer(gme_type_extension)              := GetProcAddress(gme_handle, 'gme_type_extension');
  250.   pointer(gme_identify_file)               := GetProcAddress(gme_handle, 'gme_identify_file');
  251.   pointer(gme_new_emu)                     := GetProcAddress(gme_handle, 'gme_new_emu');
  252.   pointer(gme_new_emu_multi_channel)       := GetProcAddress(gme_handle, 'gme_new_emu_multi_channel');
  253.   pointer(gme_load_file)                   := GetProcAddress(gme_handle, 'gme_load_file');
  254.   pointer(gme_load_data)                   := GetProcAddress(gme_handle, 'gme_load_data');
  255.   pointer(gme_load_custom)                 := GetProcAddress(gme_handle, 'gme_load_custom');
  256.   pointer(gme_load_m3u_data)               := GetProcAddress(gme_handle, 'gme_load_m3u_data');
  257.   //******** User data ********/
  258.   pointer(gme_set_user_data)               := GetProcAddress(gme_handle, 'gme_set_user_data');
  259.   pointer(gme_user_data)                   := GetProcAddress(gme_handle, 'gme_user_data');
  260.   pointer(gme_set_user_cleanup)            := GetProcAddress(gme_handle, 'gme_set_user_cleanup');
  261. end;
  262.  
  263. end.
  264.  
  265.  
Title: Re: Game Music Emulator
Post by: TRon on December 09, 2024, 01:23:56 am
In the latest version of GME, something broke with the Naked emulator.
When I initially read this, I thought to myself "what the h*ll is this talk about, it works just fine here".

That was until I realized that my library loading routine had a small typo and I forgot that I installed libgme using my package manager (which includes an updated libgme) :-[

Therefor thank you Guva very much for mentioning the issue and providing a fix/workaround for it as well 👍
Title: Re: Game Music Emulator
Post by: Gigatron on March 07, 2025, 11:02:30 pm
Hi
This player is now updated with some functions ; Base source for .dll (X86 64) from Replayer Arnaud/Neny
https://github.com/arnaud-neny/rePlayer/tree/main (https://github.com/arnaud-neny/rePlayer/tree/main)
Download modules from ;  https://ftp.modland.com/pub/modules/ (https://ftp.modland.com/pub/modules/)

Regards

TinyPortal © 2005-2018