Forum > Audio and Video

Game Music Emulator

(1/4) > >>

Gigatron:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs,gme,mmsystem,windows;const  FileName = 'test.nsf'; // opens this file (can be any music type)  SampleRate = 44100; // number of samples per second  Track = 0; //     (0 = first)  BufSize = 2048; //   multiple of 2 type  TWaveWriter = class  private    FFileStream: TFileStream;     FSampleRate: LongWord;    FChannels: Word;    FBytesWritten: LongWord;  public    constructor Create(ASampleRate: LongWord; const AFileName: string);    destructor Destroy; override;    procedure EnableStereo;    procedure Write(const Buffer; Count: LongWord);    procedure Close;  end; type   { TForm1 }   TForm1 = class(TForm)    procedure FormCreate(Sender: TObject);  private   public   MyFile: AnsiString;   WavStream : TMemoryStream;  end; var  Form1: TForm1;    WaveWriter: TWaveWriter;  Buf: array[0..BufSize-1] of SmallInt; implementation {$R *.lfm}// console mode to doprocedure HandleError(const Str: PAnsiChar);begin  if Str <> nil then  begin    WriteLn('Error: ', Str);    ReadLn;    Halt(1);  end;end; { TWaveWriter } constructor TWaveWriter.Create(ASampleRate: LongWord; const AFileName: string);var  Header: array[0..43] of Byte;begin  FFileStream := TFileStream.Create(AFileName, fmCreate);  FSampleRate := ASampleRate;  FChannels := 2;  FBytesWritten := 0;   // Write WAVE header  ZeroMemory(@Header, SizeOf(Header));  //FillChar(Header, SizeOf(Header), 0);  Move('RIFF', Header[0], 4);  Move('WAVEfmt ', Header[8], 8);  PLongWord(@Header[16])^ := 16;  PWord(@Header[20])^ := 1;  // PCM  PWord(@Header[22])^ := FChannels;  PLongWord(@Header[24])^ := FSampleRate;  PLongWord(@Header[28])^ := FSampleRate * FChannels * 2;  PWord(@Header[32])^ := FChannels * 2;  PWord(@Header[34])^ := 16;  Move('data', Header[36], 4);   FFileStream.WriteBuffer(Header, SizeOf(Header));end; destructor TWaveWriter.Destroy;begin  Close;  FFileStream.Free;  inherited;end; procedure TWaveWriter.EnableStereo;begin  FChannels := 2;end; procedure TWaveWriter.Write(const Buffer; Count: LongWord);begin  FFileStream.WriteBuffer(Buffer, Count * SizeOf(SmallInt));  Inc(FBytesWritten, Count * SizeOf(SmallInt));end; procedure TWaveWriter.Close;var  FileSize: LongWord;begin  FileSize := FFileStream.Size - 8;  FFileStream.Seek(4, soFromBeginning);  FFileStream.WriteBuffer(FileSize, 4);  FFileStream.Seek(40, soFromBeginning);  FFileStream.WriteBuffer(FBytesWritten, 4);end; { TForm1 } procedure TForm1.FormCreate(Sender: TObject);var   Emu:  PMusic_Emu;begin   HandleError(gme_open_file(PAnsiChar(FileName), Emu,SampleRate)); // ShowMessage('Fichier ok');   // Vérifier le démarrage de la piste  HandleError(gme_start_track(Emu, Track)); // ShowMessage('Piste ok');   // Ouverture du fichier WAV  WaveWriter := TWaveWriter.Create(SampleRate, 'out.wav');  try     WaveWriter.EnableStereo;     // Enregistrement de 10 secondes    while gme_tell(Emu) < 10 * 1000 do    begin      // Remplir le tampon d'échantillons      HandleError(gme_play(Emu, BufSize, @Buf[0]));      WaveWriter.Write(Buf, BufSize);    end;   finally    // Nettoyage    gme_delete(Emu);    WaveWriter.Free;  end;   // lecture du wave genere    MyFile := Application.Location + 'out.wav';    WavStream := TMemoryStream.Create;    WavStream.LoadFromFile(MyFile);     PlaySound(WavStream.Memory, 0, SND_NODEFAULT or SND_ASYNC or SND_MEMORY); end; end. 

Gme Unit :


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit GME; {$mode objfpc}{$H+} interface const  GME_VERSION = $000603; // 1 byte major, 1 byte minor, 1 byte patch-level type  PMusic_Emu = ^TMusic_Emu;  TMusic_Emu = record    // to doo  end;   gme_err_t = PAnsiChar;   Pgme_info_t = ^Tgme_info_t;  Tgme_info_t = record    length: Integer;    intro_length: Integer;    loop_length: Integer;    play_length: Integer;    i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15: Integer;    system: PAnsiChar;    game: PAnsiChar;    song: PAnsiChar;    author: PAnsiChar;    copyright: PAnsiChar;    comment: PAnsiChar;    dumper: PAnsiChar;    s7, s8, s9, s10, s11, s12, s13, s14, s15: PAnsiChar;  end;   Tgme_equalizer_t = record    treble: Double;    bass: Double;    d2, d3, d4, d5, d6, d7, d8, d9: Double;  end;   gme_type_t = Pointer;   gme_reader_t = function(your_data: Pointer; out_: Pointer; count: Integer): gme_err_t; cdecl;  gme_user_cleanup_t = procedure(user_data: Pointer); cdecl; const  gme_info_only = -1; var  gme_ay_type: gme_type_t; external;  gme_gbs_type: gme_type_t; external;  gme_gym_type: gme_type_t; external;  gme_hes_type: gme_type_t; external;  gme_kss_type: gme_type_t; external;  gme_nsf_type: gme_type_t; external;  gme_nsfe_type: gme_type_t; external;  gme_sap_type: gme_type_t; external;  gme_spc_type: gme_type_t; external;  gme_vgm_type: gme_type_t; external;  gme_vgz_type: gme_type_t; external; //  gme_wrong_file_type: PAnsiChar;   'gme.dll' name 'gme_';  //function gme_wrong_file_type: PAnsiChar; cdecl; external 'gme.dll' name 'gme_wrong_file_type';  gme_wrong_file_type: PAnsiChar; external 'gme.dll' name 'gme_wrong_file_type'; // Basic operationsfunction gme_open_file(path: PAnsiChar; out out_: PMusic_Emu; sample_rate: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_open_file';function gme_track_count(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_track_count';function gme_start_track(emu: PMusic_Emu; index: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_start_track';function gme_play(emu: PMusic_Emu; count: Integer; out_: PSmallInt): gme_err_t; cdecl; external 'gme.dll' name 'gme_play';procedure gme_delete(emu: PMusic_Emu); cdecl; external 'gme.dll' name 'gme_delete'; // Track position/lengthprocedure gme_set_fade(emu: PMusic_Emu; start_msec: Integer); cdecl; external 'gme.dll' name 'gme_set_fade';procedure gme_set_autoload_playback_limit(emu: PMusic_Emu; do_autoload_limit: Integer); cdecl; external 'gme.dll' name 'gme_set_autoload_playback_limit';function gme_autoload_playback_limit(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_autoload_playback_limi';function gme_track_ended(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_track_ended';function gme_tell(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_tell';function gme_tell_samples(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_tell_samples';function gme_seek(emu: PMusic_Emu; msec: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_seek';function gme_seek_samples(emu: PMusic_Emu; n: Integer): gme_err_t; cdecl; external 'gme.dll' name 'gme_seek_samples'; // Informationalfunction gme_warning(emu: PMusic_Emu): PAnsiChar; cdecl; external 'gme.dll' name 'gme_warning';function gme_load_m3u(emu: PMusic_Emu; path: PAnsiChar): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_m3u';procedure gme_clear_playlist(emu: PMusic_Emu); cdecl; external 'gme.dll' name 'gme_clear_playlist';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';procedure gme_free_info(info: Pgme_info_t); cdecl; external 'gme.dll'  name 'gme_free_info'; // Advanced playbackprocedure gme_set_stereo_depth(emu: PMusic_Emu; depth: Double); cdecl; external 'gme.dll' name 'gme_set_stereo_depth';procedure gme_ignore_silence(emu: PMusic_Emu; ignore: Integer); cdecl; external 'gme.dll' name 'gme_ignore_silence';procedure gme_set_tempo(emu: PMusic_Emu; tempo: Double); cdecl; external 'gme.dll' name 'gme_set_tempo';function gme_voice_count(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_voice_count';function gme_voice_name(const emu: PMusic_Emu; i: Integer): PAnsiChar; cdecl; external 'gme.dll' name 'gme_voice_name';procedure gme_mute_voice(emu: PMusic_Emu; index: Integer; mute: Integer); cdecl; external 'gme.dll' name 'gme_mute_voice';procedure gme_mute_voices(emu: PMusic_Emu; muting_mask: Integer); cdecl; external 'gme.dll' name 'gme_mute_voices';procedure gme_equalizer(const emu: PMusic_Emu; out out_: Tgme_equalizer_t); cdecl; external 'gme.dll' name 'gme_equalizer';procedure gme_set_equalizer(emu: PMusic_Emu; const eq: Tgme_equalizer_t); cdecl; external 'gme.dll' name 'gme_set_equalizer';procedure gme_enable_accuracy(emu: PMusic_Emu; enabled: Integer); cdecl; external 'gme.dll' name 'gme_enable_accuracy'; // Game music typesfunction gme_type(const emu: PMusic_Emu): gme_type_t; cdecl; external 'gme.dll' name 'gme_type';function gme_type_list: gme_type_t; cdecl; external 'gme.dll' name 'gme_type_list';function gme_type_system(type_: gme_type_t): PAnsiChar; cdecl; external 'gme.dll' name 'gme_type_system';function gme_type_multitrack(type_: gme_type_t): Integer; cdecl; external 'gme.dll' name 'gme_type_multitrack';function gme_multi_channel(const emu: PMusic_Emu): Integer; cdecl; external 'gme.dll' name 'gme_multi_channel'; // Advanced file loadingfunction 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';function gme_identify_header(const header: Pointer): PAnsiChar; cdecl; external 'gme.dll' name 'gme_identify_header';function gme_identify_extension(path_or_extension: PAnsiChar): gme_type_t; cdecl; external 'gme.dll' name 'gme_identify_extension';function gme_type_extension(music_type: gme_type_t): PAnsiChar; cdecl; external 'gme.dll' name 'gme_type_extension';function gme_identify_file(path: PAnsiChar; out type_out: gme_type_t): gme_err_t; cdecl; external 'gme.dll' name 'gme_identify_file';function gme_new_emu(type_: gme_type_t; sample_rate: Integer): PMusic_Emu; cdecl; external 'gme.dll' name 'gme_new_emu';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';function gme_load_file(emu: PMusic_Emu; path: PAnsiChar): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_data';function gme_load_data(emu: PMusic_Emu; const data: Pointer; size: LongInt): gme_err_t; cdecl; external 'gme.dll' name 'gme_load_data';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';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'; // User dataprocedure gme_set_user_data(emu: PMusic_Emu; new_user_data: Pointer); cdecl; external 'gme.dll' name 'gme_set_user_data';function gme_user_data(const emu: PMusic_Emu): Pointer; cdecl;external 'gme.dll' name 'gme_user_data';procedure gme_set_user_cleanup(emu: PMusic_Emu; func: gme_user_cleanup_t); cdecl; external 'gme.dll' name 'gme_set_user_cleanup'; implementation end.

Guva:
 I see if gme_ay_type is a zx spectrum?

TRon:
@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.

Fred vS:

--- Quote from: TRon on September 17, 2024, 04:07:28 pm ---I am still working on the portaudio stuff...

--- End quote ---
Excellent (and best imho) choice!
There is a fpc portaudio header with dynamic loading here.

TRon:

--- Quote from: Fred vS on September 17, 2024, 05:46:30 pm ---Excellent (and best imho) choice!

--- End quote ---
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.

--- End quote ---
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).

Navigation

[0] Message Index

[#] Next page

Go to full version