Recent

Author Topic: SC68 Player  (Read 1663 times)

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
SC68 Player
« on: November 21, 2024, 06:31:30 pm »
Hi,
Another nightmare project to play Atari & Amiga SC68 modules player from Benjamin Gérard ;
This sc68.dll is compiled with visual studio 2019 for X64 and it's on beta stage this mean the
music is not playing correctly now (synch problem), if song end detected it crash !!

Some Amiga converted to SC68 module are supported :

David Whittaker, Delta Music 1.0, Delta Music 2.0, Digital Mugician, Fred Editor (Final), Future Composer 1.0 - 1.3
Future Composer 1.4, Hippel, JamCracker, SidMon 1.0, SidMon 2.0, SoundFX 1.x


https://sc68.atari.org/index.html

Modules are here : https://modland.scenesat.com/pub/modules/SC68/
I think there are 1776 files ;

example modules and project are atached in zip format;
sc68.dll is atached after this thread :)



Main unit :

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  8.   sc68, mmsystem, windows ;
  9.  
  10. const
  11.  
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // number of samples per second
  15.   BufSize = 8192  ; //   multiple of 2
  16.   BufferCount = 1;
  17.  
  18. type
  19.  
  20.   { TForm1 }
  21.  
  22.   TForm1 = class(TForm)
  23.     Timer1: TTimer;
  24.     procedure FormCreate(Sender: TObject);
  25.     procedure FormShow(Sender: TObject);
  26.     procedure Timer1Timer(Sender: TObject);
  27.   private
  28.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of byte;
  29.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  30.      currentBuffer: Integer;
  31.  
  32.  
  33.   public
  34.  
  35.   end;
  36.  
  37.  
  38. var
  39.   Form1: TForm1;
  40.   waveOut: HWAVEOUT;
  41.   waveHeader: TWaveHdr;
  42.  
  43.   ok_flag: Boolean = false;
  44.   fsize : integer;
  45.   // sc68
  46.  
  47.   sc_init: Tsc68Init;
  48.   sc_cr:   Tsc68Create;
  49.   sc_inst  : Pointer;
  50.   sc_code : Tsc68Code;
  51.   sc_minfos : Tsc68MusicInfo ; // music infos
  52.   SC68Instance: Psc68;
  53.  
  54. implementation
  55.  
  56. {$R *.lfm}
  57.  
  58. { TForm1 }
  59.  
  60. /// audio init et le reste !!
  61. procedure HandleError(const Str: PAnsiChar);
  62. begin
  63.   if Str <> nil then
  64.   begin
  65.     ShowMessage('Error: Wrong Format ? '+ Str);
  66.      Halt(1);
  67.   end;
  68. end;
  69.  
  70.  
  71. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  72. begin
  73.   if uMsg = WOM_DONE then
  74.   begin
  75.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr) );
  76.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  77.   end;
  78.   Result := 0;
  79. end;
  80.  
  81. procedure InitAudio;
  82. var
  83.   wFormat: TWaveFormatEx;
  84.   i: Integer;
  85. begin
  86.  
  87.   // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
  88.  
  89.   with wFormat do
  90.   begin
  91.     wFormatTag := WAVE_FORMAT_PCM;
  92.     nChannels := Channels;
  93.     nSamplesPerSec := SampleRate;
  94.     wBitsPerSample := BitsPerSample;
  95.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  96.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  97.     cbSize := 0;
  98.   end;
  99.  
  100.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  101.     raise Exception.Create('Erreur ouverture periph audio');
  102.  
  103.   // buffers
  104.   for i := 0 to BufferCount - 1 do
  105.   begin
  106.     ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  107.      with Form1.waveHeaders[i] do
  108.     begin
  109.       lpData := @Form1.buffers[i][0];
  110.       dwBufferLength := BufSize * SizeOf(Byte);
  111.       dwFlags := 0;
  112.     end;
  113.    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  114.    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  115.   end;
  116.   Form1.currentBuffer := 0;
  117.  
  118.  
  119. end;
  120.  
  121. procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  122. var
  123.   MemoryStream: TMemoryStream;
  124. begin
  125.   MemoryStream := TMemoryStream.Create;
  126.   try
  127.     MemoryStream.LoadFromFile(FileName);
  128.     SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  129.     MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  130.     fsize := MemoryStream.Size;
  131.   finally
  132.     MemoryStream.Free;
  133.   end;
  134. end;
  135.  
  136. procedure TForm1.FormCreate(Sender: TObject);
  137. var
  138.   FileName: string;
  139.   appname: array[0..8] of Char = 'Lazarus'#0;
  140.   argv: array[0..0] of PChar;
  141.   Buffer: array of Byte;
  142.    n: integer;
  143. begin
  144.  
  145.   // Initialise les paramètres SC68 un cauchemar !!!!
  146.   FillChar(sc_init, SizeOf(Tsc68Init), 0);
  147.   argv[0] := appname;
  148.   sc_init.argc := Length(argv);
  149.   sc_init.argv := @argv[0];
  150.   sc_init.Flags.NoLoadConfig:=false;
  151.   FileName := 'paradox.sc68';
  152.   sc_init.sampling_rate := 44100;
  153.  
  154.    // Initialisation de SC68
  155.   if sc68_init(sc_init) < 0 then
  156.   begin
  157.     ShowMessage('Erreur d''initialisation SC68');
  158.     Exit;
  159.   end;
  160.  
  161.   // Crée une instance SC68
  162.   FillChar(sc_cr, SizeOf(sc_cr), 0);
  163.    sc_cr.SamplingRate := 44100;
  164.    sc_cr.Name :=  PChar('Lazarus'#0);
  165.    SC68Instance := sc68_create(sc_cr);
  166.  
  167.   if SC68Instance = nil then
  168.   begin
  169.     ShowMessage('Erreur lors de la creation de l''instance SC68');
  170.     Exit;
  171.   end;
  172.  
  173.   // Charge le fichier SC68 en memoire dans buffer;
  174.    LoadBinaryFileToBuffer(FileName, Buffer );
  175.    if sc68_load_mem(SC68Instance, @Buffer[0], Length(Buffer)) < 0 then
  176.   begin
  177.     ShowMessage('Erreur lors du chargement du fichier SC68');
  178.     Exit;
  179.   end;
  180.  
  181. end;
  182.  
  183. procedure TForm1.FormShow(Sender: TObject);
  184. begin
  185.      InitAudio;
  186.      ok_flag := true;
  187.      Timer1.Enabled:= true;
  188.  
  189. end;
  190.  
  191. procedure TForm1.Timer1Timer(Sender: TObject);
  192.  var
  193.  n: integer;
  194.  Buffer: array  [0..BufSize-1] of byte;
  195. begin
  196.  
  197.    Timer1.Interval := Round((8192 / (44100 * 2 * 2)) * 1000);
  198.    n := BufSize div (Channels * (BitsPerSample div 8)) ;
  199.  
  200.   if sc68_process(SC68Instance, @Buffer[0], @n) < 0 then
  201.   begin
  202.     Timer1.Enabled:= false;
  203.     ShowMessage('Erreur traitement SC68 !! Or File Not Loaded');
  204.     Exit;
  205.   end;
  206.  
  207.   Move(Buffer[0], buffers[0][0], n * Channels * (BitsPerSample div 8));
  208.  
  209. end;
  210.  
  211. end.

sc68 unit:

 
Code: Pascal  [Select][+][-]
  1. unit sc68;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils;
  9.  
  10. type
  11.  
  12.   Psc68 = ^Tsc68;
  13.   Tsc68 = record
  14.   end;
  15.  
  16.   Tsc68Disk = Pointer;
  17.  
  18.   Tsc68MsgHandler = procedure(cat: Integer; sc68: Psc68; fmt: PChar; args: Pointer); cdecl;
  19.  
  20.   Tsc68InitFlags = record
  21.     NoLoadConfig: Boolean;
  22.     NoSaveConfig: Boolean;
  23.   end;
  24.  
  25.   { Paramètres d'initialisation de l'API }
  26.   Tsc68Init = record
  27.     MsgHandler: Tsc68MsgHandler;
  28.     DebugClrMask: Integer;
  29.     DebugSetMask: Integer;
  30.     Argc: Integer;
  31.     Argv: PChar;
  32.     Flags: Tsc68InitFlags;
  33.     shared_path : PChar;
  34.     user_path : PChar;
  35.     lmusic_path: PChar;
  36.     rmusic_path: PChar;
  37.     sampling_rate : UInt16;
  38.  
  39.   end;
  40.  
  41.   init68 = ^Tsc68Init;
  42.  
  43.   Tsc68Create = record
  44.     SamplingRate: Cardinal;
  45.     Name: PChar;
  46.     Log2Mem: Integer;
  47.     Emu68Debug: Integer;
  48.     Cookie: Pointer;
  49.   end;
  50.  
  51.   Tsc68Tag = record
  52.     Key: PChar;
  53.     Value: PChar;
  54.   end;
  55.  
  56.   Tsc68CInfo = record
  57.     Track: Cardinal;
  58.     TimeMs: Cardinal;
  59.     TimeStr: array[0..11] of Char;
  60.     UsesYM: Boolean;
  61.     UsesSTE: Boolean;
  62.     UsesAmiga: Boolean;
  63.     UsesASID: Boolean;
  64.     HardwareName: PChar;
  65.     TagCount: Integer;
  66.     Tags: ^Tsc68Tag;
  67.   end;
  68.  
  69.   Tsc68MusicInfo = record
  70.     Tracks: Integer;
  71.     Addr: Cardinal;
  72.     Rate: Cardinal;
  73.     Replay: PChar;
  74.     DiskInfo: Tsc68CInfo;
  75.     TrackInfo: Tsc68CInfo;
  76.  
  77.     Album: PChar;
  78.     Title: PChar;
  79.     Artist: PChar;
  80.     Format: PChar;
  81.     Genre: PChar;
  82.     Year: PChar;
  83.     Ripper: PChar;
  84.     Converter: PChar;
  85.     LastTag: PChar;
  86.   end;
  87.  
  88.   Psc68MusicInfo = ^Tsc68MusicInfo;
  89.   Tsc68MInfo = Tsc68MusicInfo;
  90.  
  91.   Tsc68Code = (
  92.     SC68_IDLE   = 1 shl 0,
  93.     SC68_CHANGE = 1 shl 1,
  94.     SC68_LOOP   = 1 shl 2,
  95.     SC68_END    = 1 shl 3,
  96.     SC68_SEEK   = 1 shl 4,
  97.     SC68_OK     = 0,
  98.     SC68_ERROR  = -1
  99.   );
  100.  
  101.   Tsc68Spr = (
  102.     SC68_SPR_QUERY   = -1,
  103.     SC68_SPR_DEFAULT = 0
  104.   );
  105.  
  106.   Tsc68Play = (
  107.     SC68_DSK_TRACK = 0,
  108.     SC68_DEF_LOOP  = 0,
  109.     SC68_INF_LOOP  = -1,
  110.     SC68_DEF_TRACK = -1,
  111.     SC68_CUR_TRACK = -2,
  112.     SC68_CUR_LOOP  = -2
  113.   );
  114.  
  115.   Tsc68PCM = (
  116.     SC68_PCM_S16 = 1,
  117.     SC68_PCM_F32 = 2
  118.   );
  119.  
  120.   Tsc68ASID = (
  121.     SC68_ASID_OFF   = 0,
  122.     SC68_ASID_ON    = 1,
  123.     SC68_ASID_FORCE = 2,
  124.     SC68_ASID_NO_A  = 4,
  125.     SC68_ASID_NO_B  = 8,
  126.     SC68_ASID_NO_C  = 16
  127.   );
  128.  
  129.   Tsc68Cntl = (
  130.     SC68_NOP = 0,
  131.     SC68_GET_LAST,
  132.     SC68_GET_NAME,
  133.     SC68_GET_TRACKS,
  134.     SC68_GET_TRACK,
  135.     SC68_GET_DEFTRK,
  136.     SC68_GET_LOOPS,
  137.     SC68_GET_LOOP,
  138.     SC68_GET_DISK,
  139.     SC68_GET_SPR,
  140.     SC68_SET_SPR,
  141.     SC68_GET_LEN,
  142.     SC68_GET_TRKLEN,
  143.     SC68_GET_DSKLEN,
  144.     SC68_GET_ORG,
  145.     SC68_GET_TRKORG,
  146.     SC68_GET_POS,
  147.     SC68_GET_DSKPOS,
  148.     SC68_GET_PLAYPOS,
  149.     SC68_SET_POS,
  150.     SC68_GET_PCM,
  151.     SC68_SET_PCM,
  152.     SC68_CAN_ASID,
  153.     SC68_GET_ASID,
  154.     SC68_SET_ASID,
  155.     SC68_GET_COOKIE,
  156.     SC68_SET_COOKIE,
  157.     SC68_EMULATORS,
  158.     SC68_CONFIG_LOAD,
  159.     SC68_CONFIG_SAVE,
  160.     SC68_ENUM_OPT,
  161.     SC68_GET_OPT,
  162.     SC68_SET_OPT_STR,
  163.     SC68_SET_OPT_INT,
  164.     SC68_DIAL,
  165.     SC68_CNTL_LAST
  166.   );
  167.  
  168.   function sc68_version: Integer; cdecl; external 'sc68.dll';
  169.   function sc68_versionstr: PChar; cdecl; external 'sc68.dll';
  170.   function sc68_init(var init: Tsc68Init): Integer; cdecl; external 'sc68.dll' name 'sc68_init';
  171.   procedure sc68_shutdown; cdecl; external 'sc68.dll';
  172.   function sc68_create(create: Tsc68Create): Pointer; cdecl; external 'sc68.dll' name 'sc68_create';
  173.   procedure sc68_destroy(sc68: Psc68); cdecl; external 'sc68.dll';
  174.   function sc68_cntl(sc68: Psc68; op: Integer; args: array of const): Integer; cdecl; external 'sc68.dll';
  175. //  function sc68_error(sc68: Psc68): PChar; cdecl; external 'sc68.dll';
  176.   function sc68_process(sc68: Psc68; buf: Pointer;   n: pointer): integer; cdecl; external 'sc68.dll' name 'sc68_process';
  177.   function sc68_play(sc68: Psc68; track, loop: Integer): Integer; cdecl; external 'sc68.dll';
  178.   function sc68_stop(sc68: Psc68): Integer; cdecl; external 'sc68.dll';
  179.   function sc68_music_info(sc68: Psc68; info: Psc68MusicInfo; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  180.   function sc68_tag_get(sc68: Psc68; tag: Tsc68Tag; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  181.   function sc68_tag(sc68: Psc68; key: PChar; track: Integer; disk: Tsc68Disk): PChar; cdecl; external 'sc68.dll';
  182.   function sc68_tag_enum(sc68: Psc68; tag: Tsc68Tag; track, idx: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  183.   function sc68_mimetype: PChar; cdecl; external 'sc68.dll';
  184.   function sc68_vfs(uri: PChar; mode, argc: Integer; args: array of const): Pointer; cdecl; external 'sc68.dll';
  185.   function sc68_is_our_uri(uri, exts: PChar; var is_remote: Integer): Integer; cdecl; external 'sc68.dll';
  186.   function sc68_load(sc68: Psc68; is_: Pointer): Integer; cdecl; external 'sc68.dll';
  187.   function sc68_load_uri(sc68: Psc68; uri: PChar): Integer; cdecl; external 'sc68.dll';
  188.   function sc68_load_mem(sc68: Psc68; buffer: Pointer; len: Integer): Integer; cdecl; external 'sc68.dll';
  189.   function sc68_load_disk(is_: Pointer): Tsc68Disk; cdecl; external 'sc68.dll';
  190.   function sc68_load_disk_uri(uri: PChar): Tsc68Disk; cdecl; external 'sc68.dll';
  191.   function sc68_disk_load_mem(buffer: Pointer; len: Integer): Tsc68Disk; cdecl; external 'sc68.dll';
  192.   procedure sc68_disk_free(disk: Tsc68Disk); cdecl; external 'sc68.dll';
  193.   function sc68_open(sc68: Psc68; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  194.   procedure sc68_close(sc68: Psc68); cdecl; external 'sc68.dll';
  195.   function sc68_ym_channels(sc68: Psc68; channels: Integer): Integer; cdecl; external 'sc68.dll';
  196.  
  197. implementation
  198.  
  199. end.
  200.  
« Last Edit: November 22, 2024, 07:25:47 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
Re: SC68 Player
« Reply #1 on: November 21, 2024, 06:33:31 pm »
And then the sc68.dll ;

The modules must be in the project directory ;
I am working to improve it, so stay tuned.

Regards

Gigatron
« Last Edit: November 21, 2024, 06:35:51 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
Re: SC68 Player
« Reply #2 on: November 22, 2024, 06:54:46 pm »
Okey, now some corrections are done to play sc68 module perfectly (the synchro problem is fixed , timer removed ) !
Music end detection flag was added ;

** Edit music info added ; Main unit & sc68 unit was changed !! so replace them by this one ;

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  8.   sc68, mmsystem, windows ;
  9.  
  10. const
  11.  
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // number of samples per second
  15.   BufSize = 8192    ; //   multiple of 2
  16.   BufferCount = 2;
  17.  
  18. type
  19.  
  20.   { TForm1 }
  21.  
  22.   TForm1 = class(TForm)
  23.     Memo1: TMemo;
  24.     Timer1: TTimer;
  25.     procedure FormCreate(Sender: TObject);
  26.     procedure FormShow(Sender: TObject);
  27.     procedure Timer1Timer(Sender: TObject);
  28.     procedure Process_SC68(BufferIndex: Integer);
  29.     procedure CloseAudio;
  30.  
  31.   private
  32.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of byte;
  33.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  34.      currentBuffer: Integer;
  35.      Music_Finished: Boolean;
  36.  
  37.   public
  38.  
  39.   end;
  40.  
  41.  
  42. var
  43.   Form1: TForm1;
  44.   waveOut: HWAVEOUT;
  45.   waveHeader: TWaveHdr;
  46.   fsize : integer;
  47.  
  48.   // sc68
  49.   sc_init: Tsc68Init;
  50.   sc_cr:   Tsc68Create;
  51.   sc_inst  : Pointer;
  52.   sc_code : Tsc68Code;
  53.   sc_minfos : Tsc68MusicInfo ; // music infos
  54.   sc_disc : Tsc68Disk;
  55.   SC68Instance: Psc68;
  56.  
  57. implementation
  58.  
  59. {$R *.lfm}
  60.  
  61. { TForm1 }
  62.  
  63. /// audio init et le reste !!
  64. procedure HandleError(const Str: PAnsiChar);
  65. begin
  66.   if Str <> nil then
  67.   begin
  68.     ShowMessage('Error: Wrong Format ? '+ Str);
  69.      Halt(1);
  70.   end;
  71. end;
  72.  
  73. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  74. begin
  75.   if uMsg = WOM_DONE then
  76.   begin
  77.     Form1.Process_SC68(Form1.currentBuffer);
  78.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  79.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  80.   end;
  81.   Result := 0;
  82. end;
  83.  
  84. procedure InitAudio;
  85. var
  86.   wFormat: TWaveFormatEx;
  87.   i: Integer;
  88. begin
  89.  
  90.   SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
  91.  
  92.   with wFormat do
  93.   begin
  94.     wFormatTag := 1; // pcm
  95.     nChannels := Channels;
  96.     nSamplesPerSec := SampleRate;
  97.     wBitsPerSample := BitsPerSample;
  98.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  99.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  100.     cbSize := 0;
  101.   end;
  102.  
  103.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  104.     raise Exception.Create('Erreur ouverture periph audio');
  105.  
  106.   // buffers init
  107.   for i := 0 to BufferCount - 1 do
  108.   begin
  109.      ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  110.      with Form1.waveHeaders[i] do
  111.     begin
  112.       lpData := @Form1.buffers[i][0];
  113.       dwBufferLength := BufSize * SizeOf(Byte);
  114.       dwFlags := 0;
  115.     end;
  116.    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  117.    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  118.   end;
  119.   Form1.currentBuffer := 0;
  120.  
  121. end;
  122.  
  123. procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer:   TBytes);
  124. var
  125.   MemoryStream: TMemoryStream;
  126. begin
  127.   MemoryStream := TMemoryStream.Create;
  128.   try
  129.     MemoryStream.LoadFromFile(FileName);
  130.     SetLength(Buffer, MemoryStream.Size); // Ajuste la taille du buffer
  131.     MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  132.     fsize := MemoryStream.Size;
  133.   finally
  134.     MemoryStream.Free;
  135.   end;
  136. end;
  137.  
  138. procedure TForm1.FormCreate(Sender: TObject);
  139. var
  140.   FileName: string;
  141.   appname: array[0..8] of Char = 'Lazarus'#0;
  142.   argv: array[0..0] of PChar;
  143.   Buffer: array of Byte;
  144.  
  145. begin
  146.  
  147.   // Initialise les paramètres SC68 un cauchemar !!!!
  148.   FillChar(sc_init, SizeOf(Tsc68Init), 0);
  149.   argv[0] := appname;
  150.   sc_init.argc := Length(argv);
  151.   sc_init.argv := @argv[0];
  152.   sc_init.Flags.NoLoadConfig:=false;
  153.   FileName := 'barbarian2.sc68';
  154.   sc_init.sampling_rate := 44100;
  155.  
  156.    // Initialisation de SC68
  157.   if sc68_init(sc_init) < 0 then
  158.   begin
  159.     ShowMessage('Erreur d''initialisation SC68');
  160.     Exit;
  161.   end;
  162.  // instance SC68
  163.   FillChar(sc_cr, SizeOf(sc_cr), 0);
  164.    sc_cr.SamplingRate := 44100;
  165.    sc_cr.Name :=  PChar('Lazarus'#0);
  166.    SC68Instance := sc68_create(sc_cr);
  167.  
  168.   if SC68Instance = nil then
  169.   begin
  170.     ShowMessage('Erreur lors de la creation de l''instance SC68');
  171.     Exit;
  172.   end;
  173.  
  174.   // Charge le fichier SC68 en memoire dans buffer;
  175.    LoadBinaryFileToBuffer(FileName, Buffer );
  176.    if sc68_load_mem(SC68Instance, @Buffer[0], Length(Buffer)) < 0 then
  177.   begin
  178.     ShowMessage('Erreur lors du chargement du fichier SC68');
  179.     Exit;
  180.   end;
  181.  
  182. end;
  183.  
  184. procedure TForm1.FormShow(Sender: TObject);
  185. begin
  186.      Music_Finished := false;
  187.      InitAudio;
  188.      sc68_music_info(SC68Instance,sc_minfos,0,sc_disc);
  189.  
  190.      memo1.clear;
  191.      memo1.Lines.Add( Pchar(sc_minfos.Album));
  192.      memo1.Lines.Add( Pchar(sc_minfos.Title));
  193.      memo1.Lines.Add( Pchar(sc_minfos.Artist));
  194.      memo1.Lines.Add( Pchar(sc_minfos.Format));
  195.      memo1.Lines.Add( Pchar(sc_minfos.Genre));
  196.      memo1.Lines.Add( Pchar(sc_minfos.Year));
  197.      memo1.Lines.Add( Pchar(sc_minfos.Ripper));
  198.      memo1.Lines.Add( Pchar(sc_minfos.Converter));
  199.      memo1.Lines.Add( Pchar(sc_minfos.LastTag));
  200.  
  201. end;
  202.  
  203. procedure TForm1.Process_SC68(BufferIndex: Integer);
  204. var
  205.   Samples: Integer;
  206.   resultCode: Tsc68Code;
  207. begin
  208.  
  209.   if not Music_Finished then
  210.   begin
  211.  
  212.        Samples := BufSize div (Channels * (BitsPerSample div 8));
  213.        resultCode := Tsc68Code(sc68_process(SC68Instance, @buffers[BufferIndex][0], @Samples));
  214.  
  215.    if (Int64(resultCode) and Int64(SC68_END)) <> 0 then
  216.      begin
  217.       Music_Finished := True;
  218.       ShowMessage('Musique terminée!');
  219.       CloseAudio;
  220.       Exit;
  221.      end;
  222.   end;
  223. end;
  224.  
  225. procedure TForm1.CloseAudio;
  226. var
  227.   i: Integer;
  228. begin
  229.   for i := 0 to BufferCount - 1 do
  230.   begin
  231.     waveOutUnprepareHeader(waveOut, @waveHeaders[i], SizeOf(TWaveHdr));
  232.   end;
  233.   waveOutClose(waveOut);
  234. end;
  235.  
  236.  
  237. // not need here !!!
  238. procedure TForm1.Timer1Timer(Sender: TObject);
  239. begin
  240. end;
  241.  
  242. end.



SC68 Unit :

Code: Pascal  [Select][+][-]
  1. unit sc68;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils;
  9.  
  10. type
  11.  
  12.   Psc68 = ^Tsc68;
  13.   Tsc68 = record
  14.   end;
  15.  
  16.   Tsc68Disk = Pointer;
  17.  
  18.   Tsc68MsgHandler = procedure(cat: Integer; sc68: Psc68; fmt: PChar; args: Pointer); cdecl;
  19.  
  20.   Tsc68InitFlags = record
  21.     NoLoadConfig: Boolean;
  22.     NoSaveConfig: Boolean;
  23.   end;
  24.  
  25.   { Paramètres d'initialisation de l'API }
  26.   Tsc68Init = record
  27.     MsgHandler: Tsc68MsgHandler;
  28.     DebugClrMask: Integer;
  29.     DebugSetMask: Integer;
  30.     Argc: Integer;
  31.     Argv: PChar;
  32.     Flags: Tsc68InitFlags;
  33.     shared_path : PChar;
  34.     user_path : PChar;
  35.     lmusic_path: PChar;
  36.     rmusic_path: PChar;
  37.     sampling_rate : UInt16;
  38.  
  39.   end;
  40.  
  41.   init68 = ^Tsc68Init;
  42.  
  43.   Tsc68Create = record
  44.     SamplingRate: Cardinal;
  45.     Name: PChar;
  46.     Log2Mem: Integer;
  47.     Emu68Debug: Integer;
  48.     Cookie: Pointer;
  49.   end;
  50.  
  51.   Tsc68Tag = record
  52.     Key: PChar;
  53.     Value: PChar;
  54.   end;
  55.  
  56.   Tsc68CInfo = record
  57.     Track: Cardinal;
  58.     TimeMs: Cardinal;
  59.     TimeStr: array[0..11] of Char;
  60.     UsesYM: Boolean;
  61.     UsesSTE: Boolean;
  62.     UsesAmiga: Boolean;
  63.     UsesASID: Boolean;
  64.     HardwareName: PChar;
  65.     TagCount: Integer;
  66.     Tags: ^Tsc68Tag;
  67.   end;
  68.  
  69.   Tsc68MusicInfo = record
  70.     Tracks: Integer;
  71.     Addr: Cardinal;
  72.     Rate: Cardinal;
  73.     Replay: PChar;
  74.     DiskInfo: Tsc68CInfo;
  75.     TrackInfo: Tsc68CInfo;
  76.  
  77.     Album: PChar;
  78.     Title: PChar;
  79.     Artist: PChar;
  80.     Format: PChar;
  81.     Genre: PChar;
  82.     Year: PChar;
  83.     Ripper: PChar;
  84.     Converter: PChar;
  85.     LastTag: PChar;
  86.   end;
  87.  
  88.   Psc68MusicInfo = ^Tsc68MusicInfo;
  89.   Tsc68MInfo = Tsc68MusicInfo;
  90.   type
  91.   Tsc68Code = (
  92.     SC68_IDLE   = 1 shl 0,
  93.     SC68_CHANGE = 1 shl 1,
  94.     SC68_LOOP   = 1 shl 2,
  95.     SC68_END    = 1 shl 3,
  96.     SC68_SEEK   = 1 shl 4,
  97.     SC68_OK     = 0,
  98.     SC68_ERROR  = -1
  99.   );
  100.  
  101.   Tsc68Spr = (
  102.     SC68_SPR_QUERY   = -1,
  103.     SC68_SPR_DEFAULT = 0
  104.   );
  105.  
  106.   Tsc68Play = (
  107.     SC68_DSK_TRACK = 0,
  108.     SC68_DEF_LOOP  = 0,
  109.     SC68_INF_LOOP  = -1,
  110.     SC68_DEF_TRACK = -1,
  111.     SC68_CUR_TRACK = -2,
  112.     SC68_CUR_LOOP  = -2
  113.   );
  114.  
  115.   Tsc68PCM = (
  116.     SC68_PCM_S16 = 1,
  117.     SC68_PCM_F32 = 2
  118.   );
  119.  
  120.   Tsc68ASID = (
  121.     SC68_ASID_OFF   = 0,
  122.     SC68_ASID_ON    = 1,
  123.     SC68_ASID_FORCE = 2,
  124.     SC68_ASID_NO_A  = 4,
  125.     SC68_ASID_NO_B  = 8,
  126.     SC68_ASID_NO_C  = 16
  127.   );
  128.  
  129.   Tsc68Cntl = (
  130.     SC68_NOP = 0,
  131.     SC68_GET_LAST,
  132.     SC68_GET_NAME,
  133.     SC68_GET_TRACKS,
  134.     SC68_GET_TRACK,
  135.     SC68_GET_DEFTRK,
  136.     SC68_GET_LOOPS,
  137.     SC68_GET_LOOP,
  138.     SC68_GET_DISK,
  139.     SC68_GET_SPR,
  140.     SC68_SET_SPR,
  141.     SC68_GET_LEN,
  142.     SC68_GET_TRKLEN,
  143.     SC68_GET_DSKLEN,
  144.     SC68_GET_ORG,
  145.     SC68_GET_TRKORG,
  146.     SC68_GET_POS,
  147.     SC68_GET_DSKPOS,
  148.     SC68_GET_PLAYPOS,
  149.     SC68_SET_POS,
  150.     SC68_GET_PCM,
  151.     SC68_SET_PCM,
  152.     SC68_CAN_ASID,
  153.     SC68_GET_ASID,
  154.     SC68_SET_ASID,
  155.     SC68_GET_COOKIE,
  156.     SC68_SET_COOKIE,
  157.     SC68_EMULATORS,
  158.     SC68_CONFIG_LOAD,
  159.     SC68_CONFIG_SAVE,
  160.     SC68_ENUM_OPT,
  161.     SC68_GET_OPT,
  162.     SC68_SET_OPT_STR,
  163.     SC68_SET_OPT_INT,
  164.     SC68_DIAL,
  165.     SC68_CNTL_LAST
  166.   );
  167.  
  168.   function sc68_version: Integer; cdecl; external 'sc68.dll';
  169.   function sc68_versionstr: PChar; cdecl; external 'sc68.dll';
  170.   function sc68_init(var init: Tsc68Init): Integer; cdecl; external 'sc68.dll' name 'sc68_init';
  171.   procedure sc68_shutdown; cdecl; external 'sc68.dll';
  172.   function sc68_create(create: Tsc68Create): Pointer; cdecl; external 'sc68.dll' name 'sc68_create';
  173.   procedure sc68_destroy(sc68: Psc68); cdecl; external 'sc68.dll';
  174.   function sc68_cntl(sc68: Psc68; op: Integer; args: array of const): Integer; cdecl; external 'sc68.dll';
  175. //  function sc68_error(sc68: Psc68): PChar; cdecl; external 'sc68.dll';
  176.   function sc68_process(sc68: Psc68; buf: Pointer;   n: pointer): integer; cdecl; external 'sc68.dll' name 'sc68_process';
  177.   function sc68_play(sc68: Psc68; track, loop: Integer): Integer; cdecl; external 'sc68.dll';
  178.   function sc68_stop(sc68: Psc68): Integer; cdecl; external 'sc68.dll';
  179.   function sc68_music_info(sc68: Psc68; info: Tsc68MusicInfo; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  180.   function sc68_tag_get(sc68: Psc68; tag: Tsc68Tag; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  181.   function sc68_tag(sc68: Psc68; key: PChar; track: Integer; disk: Tsc68Disk): PChar; cdecl; external 'sc68.dll';
  182.   function sc68_tag_enum(sc68: Psc68; tag: Tsc68Tag; track, idx: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  183.   function sc68_mimetype: PChar; cdecl; external 'sc68.dll';
  184.   function sc68_vfs(uri: PChar; mode, argc: Integer; args: array of const): Pointer; cdecl; external 'sc68.dll';
  185.   function sc68_is_our_uri(uri, exts: PChar; var is_remote: Integer): Integer; cdecl; external 'sc68.dll';
  186.   function sc68_load(sc68: Psc68; is_: Pointer): Integer; cdecl; external 'sc68.dll';
  187.   function sc68_load_uri(sc68: Psc68; uri: PChar): Integer; cdecl; external 'sc68.dll';
  188.   function sc68_load_mem(sc68: Psc68; buffer: Pointer; len: Integer): Integer; cdecl; external 'sc68.dll';
  189.   function sc68_load_disk(is_: Pointer): Tsc68Disk; cdecl; external 'sc68.dll';
  190.   function sc68_load_disk_uri(uri: PChar): Tsc68Disk; cdecl; external 'sc68.dll';
  191.   function sc68_disk_load_mem(buffer: Pointer; len: Integer): Tsc68Disk; cdecl; external 'sc68.dll';
  192.   procedure sc68_disk_free(disk: Tsc68Disk); cdecl; external 'sc68.dll';
  193.   function sc68_open(sc68: Psc68; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  194.   procedure sc68_close(sc68: Psc68); cdecl; external 'sc68.dll';
  195.   function sc68_ym_channels(sc68: Psc68; channels: Integer): Integer; cdecl; external 'sc68.dll';
  196.  
  197. implementation
  198.  
  199. end.
  200.  
« Last Edit: November 22, 2024, 07:19:21 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

TRon

  • Hero Member
  • *****
  • Posts: 3813
Re: SC68 Player
« Reply #3 on: November 22, 2024, 07:30:10 pm »
fwiw:
Code: Pascal  [Select][+][-]
  1.   if sc68_load_uri(decoder, pchar(Filename)) = 0
  2.   then writeln('file ', Filename, ' loaded to sc68 memory')
  3.   else writeln('ERROR: unable to load file into sc68 memory (', sc68_error(decoder), ')');
  4.  
I do not have to remember anything anymore thanks to total-recall.

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
Re: SC68 Player
« Reply #4 on: November 22, 2024, 07:41:26 pm »
fwiw:
Code: Pascal  [Select][+][-]
  1.   if sc68_load_uri(decoder, pchar(Filename)) = 0
  2.   then writeln('file ', Filename, ' loaded to sc68 memory')
  3.   else writeln('ERROR: unable to load file into sc68 memory (', sc68_error(decoder), ')');
  4.  

I always said you were a fantastic programmer :) merci


Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  8.   sc68, mmsystem, windows ;
  9.  
  10. const
  11.  
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // number of samples per second
  15.   BufSize = 8192    ; //   multiple of 2
  16.   BufferCount = 2;
  17.  
  18. type
  19.  
  20.   { TForm1 }
  21.  
  22.   TForm1 = class(TForm)
  23.     Label1: TLabel;
  24.     Memo1: TMemo;
  25.     Timer1: TTimer;
  26.     procedure FormCreate(Sender: TObject);
  27.     procedure FormShow(Sender: TObject);
  28.     procedure Timer1Timer(Sender: TObject);
  29.     procedure Process_SC68(BufferIndex: Integer);
  30.     procedure CloseAudio;
  31.  
  32.   private
  33.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of byte;
  34.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  35.      currentBuffer: Integer;
  36.      Music_Finished: Boolean;
  37.  
  38.   public
  39.  
  40.   end;
  41.  
  42.  
  43. var
  44.   Form1: TForm1;
  45.   waveOut: HWAVEOUT;
  46.   waveHeader: TWaveHdr;
  47.   fsize : integer;
  48.  
  49.   // sc68
  50.   sc_init: Tsc68Init;
  51.   sc_cr:   Tsc68Create;
  52.   sc_inst  : Pointer;
  53.   sc_code : Tsc68Code;
  54.   sc_minfos : Tsc68MusicInfo ; // music infos
  55.   sc_disc : Tsc68Disk;
  56.   SC68Instance: Psc68;
  57.  
  58. implementation
  59.  
  60. {$R *.lfm}
  61.  
  62. { TForm1 }
  63.  
  64. /// audio init et le reste !!
  65. procedure HandleError(const Str: PAnsiChar);
  66. begin
  67.   if Str <> nil then
  68.   begin
  69.     ShowMessage('Error: Wrong Format ? '+ Str);
  70.      Halt(1);
  71.   end;
  72. end;
  73.  
  74. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  75. begin
  76.   if uMsg = WOM_DONE then
  77.   begin
  78.     Form1.Process_SC68(Form1.currentBuffer);
  79.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  80.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  81.   end;
  82.   Result := 0;
  83. end;
  84.  
  85. procedure InitAudio;
  86. var
  87.   wFormat: TWaveFormatEx;
  88.   i: Integer;
  89. begin
  90.  
  91.   SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
  92.  
  93.   with wFormat do
  94.   begin
  95.     wFormatTag := 1; // pcm
  96.     nChannels := Channels;
  97.     nSamplesPerSec := SampleRate;
  98.     wBitsPerSample := BitsPerSample;
  99.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  100.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  101.     cbSize := 0;
  102.   end;
  103.  
  104.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  105.     raise Exception.Create('Erreur ouverture periph audio');
  106.  
  107.   // buffers init
  108.   for i := 0 to BufferCount - 1 do
  109.   begin
  110.      ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  111.      with Form1.waveHeaders[i] do
  112.     begin
  113.       lpData := @Form1.buffers[i][0];
  114.       dwBufferLength := BufSize * SizeOf(Byte);
  115.       dwFlags := 0;
  116.     end;
  117.    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  118.    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  119.   end;
  120.   Form1.currentBuffer := 0;
  121.  
  122. end;
  123.  
  124. procedure TForm1.FormCreate(Sender: TObject);
  125. var
  126.   FileName: string;
  127.   appname: array[0..8] of Char = 'Lazarus'#0;
  128.   argv: array[0..0] of PChar;
  129. begin
  130.  
  131.   // Initialise les paramètres SC68 un cauchemar !!!!
  132.   FillChar(sc_init, SizeOf(Tsc68Init), 0);
  133.   argv[0] := appname;
  134.   sc_init.argc := Length(argv);
  135.   sc_init.argv := @argv[0];
  136.   sc_init.Flags.NoLoadConfig:=false;
  137.   FileName := 'barbarian2.sc68';
  138.   sc_init.sampling_rate := 44100;
  139.  
  140.    // Initialisation de SC68
  141.   if sc68_init(sc_init) < 0 then
  142.   begin
  143.     ShowMessage('Erreur d''initialisation SC68');
  144.     Exit;
  145.   end;
  146.  // instance SC68
  147.   FillChar(sc_cr, SizeOf(sc_cr), 0);
  148.    sc_cr.SamplingRate := 44100;
  149.    sc_cr.Name :=  PChar('Lazarus'#0);
  150.    SC68Instance := sc68_create(sc_cr);
  151.  
  152.   if SC68Instance = nil then
  153.   begin
  154.     ShowMessage('Erreur lors de la creation de l''instance SC68');
  155.     Exit;
  156.   end;
  157.  
  158.   // Charge le fichier SC68  ;
  159.     sc68_load_uri(SC68Instance, pchar(Filename))  ;
  160.  
  161. end;
  162.  
  163. procedure TForm1.FormShow(Sender: TObject);
  164. begin
  165.      Music_Finished := false;
  166.      InitAudio;
  167.      sc68_music_info(SC68Instance,sc_minfos,0,sc_disc);
  168.  
  169.      memo1.clear;
  170.      memo1.Lines.Add( Pchar(sc_minfos.Album));
  171.      memo1.Lines.Add( Pchar(sc_minfos.Title));
  172.      memo1.Lines.Add( Pchar(sc_minfos.Artist));
  173.      memo1.Lines.Add( Pchar(sc_minfos.Format));
  174.      memo1.Lines.Add( Pchar(sc_minfos.Genre));
  175.      memo1.Lines.Add( Pchar(sc_minfos.Year));
  176.      memo1.Lines.Add( Pchar(sc_minfos.Ripper));
  177.      memo1.Lines.Add( Pchar(sc_minfos.Converter));
  178.      memo1.Lines.Add( Pchar(sc_minfos.LastTag));
  179.  
  180. end;
  181.  
  182. procedure TForm1.Process_SC68(BufferIndex: Integer);
  183. var
  184.   Samples: Integer;
  185.   resultCode: Tsc68Code;
  186. begin
  187.  
  188.   if not Music_Finished then
  189.   begin
  190.  
  191.        Samples := BufSize div (Channels * (BitsPerSample div 8));
  192.        resultCode := Tsc68Code(sc68_process(SC68Instance, @buffers[BufferIndex][0], @Samples));
  193.  
  194.    if (Int64(resultCode) and Int64(SC68_END)) <> 0 then
  195.      begin
  196.       Music_Finished := True;
  197.       ShowMessage('Musique terminée!');
  198.       CloseAudio;
  199.       Exit;
  200.      end;
  201.   end;
  202. end;
  203.  
  204. procedure TForm1.CloseAudio;
  205. var
  206.   i: Integer;
  207. begin
  208.   for i := 0 to BufferCount - 1 do
  209.   begin
  210.     waveOutUnprepareHeader(waveOut, @waveHeaders[i], SizeOf(TWaveHdr));
  211.   end;
  212.   waveOutClose(waveOut);
  213. end;
  214.  
  215.  
  216. // not need here !!!
  217. procedure TForm1.Timer1Timer(Sender: TObject);
  218. begin
  219. end;
  220.  
  221. end.
Sub Quantum Technology ! Gigatron 68000 Colmar France;

TRon

  • Hero Member
  • *****
  • Posts: 3813
Re: SC68 Player
« Reply #5 on: November 22, 2024, 07:57:51 pm »
Thank you for the compliment but all that I did was browsing through the  c-sources and reading what the developer(s) wrote  :)

Here are some other shortcuts:

Code: Pascal  [Select][+][-]
  1.   if sc68_init(nil) = 0
  2.   then writeln('sc68 initialized')
  3.   else writeln('ERROR: failed initializing sc68');
  4.  

and

Code: Pascal  [Select][+][-]
  1.   decoder := sc68_create(nil);
  2.   if assigned(decoder)
  3.   then writeln('sc68 instance created')
  4.   else writeln('ERROR: unable to create sc68 instance');
  5.  

Turns out that there is actually no need to provide any custom parameters (only if you really want/need to).

Standard output seems to be 44100 and signed 16 bit samples. The latter suggest to have support for floats but that isn't actually implemented.

Another observation is that none of my sound-fx modules (v1 and v2) seem to work as the emulator stumbles upon an invalid instruction.

In fact almost none of the Amiga released sc68 files seem to have their decoder backend implemented (as was reported by the original author as Amiga was not a priority)

fwiw the returned sc68_process status is driving me insane  :D
I do not have to remember anything anymore thanks to total-recall.

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
Re: SC68 Player
« Reply #6 on: November 24, 2024, 10:28:10 pm »
Hi, so this is the latest correction for the sc68 player, added subsong or number of tracks,
If number of tracks>1 then play the second track, hope you can improve this player now;

An html5, js version here : http://gigatron3k.free.fr/html5/Sc68player/

Go to UADE NOW !!! I hope I can do something..

Main unit :

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  8.   sc68, mmsystem, windows ;
  9.  
  10. const
  11.  
  12.   Channels = 2;
  13.   BitsPerSample = 16;
  14.   SampleRate = 44100; // number of samples per second
  15.   BufSize = 8192    ; //   multiple of 2
  16.   BufferCount = 2;
  17.  
  18. type
  19.  
  20.   { TForm1 }
  21.  
  22.   TForm1 = class(TForm)
  23.     Label1: TLabel;
  24.     Memo1: TMemo;
  25.     Timer1: TTimer;
  26.     procedure FormCreate(Sender: TObject);
  27.     procedure FormShow(Sender: TObject);
  28.     procedure Timer1Timer(Sender: TObject);
  29.     procedure Process_SC68(BufferIndex: Integer);
  30.     procedure CloseAudio;
  31.  
  32.   private
  33.      buffers: array[0..BufferCount-1] of array[0..BufSize-1] of byte;
  34.      waveHeaders: array[0..BufferCount-1] of TWaveHdr;
  35.      currentBuffer: Integer;
  36.      Music_Finished: Boolean;
  37.  
  38.   public
  39.  
  40.   end;
  41.  
  42.  
  43. var
  44.   Form1: TForm1;
  45.   waveOut: HWAVEOUT;
  46.   waveHeader: TWaveHdr;
  47.   fsize : integer;
  48.  
  49.   // sc68
  50.   sc_init: Tsc68Init;
  51.   sc_cr:   Tsc68Create;
  52.   sc_inst  : Pointer;
  53.   sc_code : Tsc68Code;
  54.   sc_minfos : Tsc68MusicInfo ; // music infos
  55.   sc_cinfos : Tsc68CInfo;
  56.   sc_disc : Tsc68Disk;
  57.   sc_play : Tsc68Play;
  58.   SC68Instance: Psc68;
  59.  
  60. implementation
  61.  
  62. {$R *.lfm}
  63.  
  64. { TForm1 }
  65.  
  66. /// audio init et le reste !!
  67. procedure HandleError(const Str: PAnsiChar);
  68. begin
  69.   if Str <> nil then
  70.   begin
  71.     ShowMessage('Error: Wrong Format ? '+ Str);
  72.      Halt(1);
  73.   end;
  74. end;
  75.  
  76. function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
  77. begin
  78.   if uMsg = WOM_DONE then
  79.   begin
  80.     Form1.Process_SC68(Form1.currentBuffer);
  81.     waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
  82.     Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  83.   end;
  84.   Result := 0;
  85. end;
  86.  
  87. procedure InitAudio;
  88. var
  89.   wFormat: TWaveFormatEx;
  90.   i: Integer;
  91. begin
  92.  
  93.   SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
  94.  
  95.   with wFormat do
  96.   begin
  97.     wFormatTag := 1; // pcm
  98.     nChannels := Channels;
  99.     nSamplesPerSec := SampleRate;
  100.     wBitsPerSample := BitsPerSample;
  101.     nBlockAlign := (wBitsPerSample * nChannels) div 8;
  102.     nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
  103.     cbSize := 0;
  104.   end;
  105.  
  106.   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  107.     raise Exception.Create('Erreur ouverture periph audio');
  108.  
  109.   // buffers init
  110.   for i := 0 to BufferCount - 1 do
  111.   begin
  112.      ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  113.      with Form1.waveHeaders[i] do
  114.     begin
  115.       lpData := @Form1.buffers[i][0];
  116.       dwBufferLength := BufSize * SizeOf(Byte);
  117.       dwFlags := 0;
  118.     end;
  119.    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  120.    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  121.   end;
  122.   Form1.currentBuffer := 0;
  123.  
  124. end;
  125.  
  126. procedure TForm1.FormCreate(Sender: TObject);
  127. var
  128.   FileName: string;
  129.   appname: array[0..8] of Char = 'Lazarus'#0;
  130.   argv: array[0..0] of PChar;
  131. begin
  132.  
  133.   // Initialise les paramètres SC68 un cauchemar !!!!
  134.   FillChar(sc_init, SizeOf(Tsc68Init), 0);
  135.   argv[0] := appname;
  136.   sc_init.argc := Length(argv);
  137.   sc_init.argv := @argv[0];
  138.   sc_init.Flags.NoLoadConfig:=false;
  139.   FileName := 'madness.sc68';
  140.   sc_init.sampling_rate := 44100;
  141.  
  142.    // Initialisation de SC68
  143.   if sc68_init(sc_init) < 0 then
  144.   begin
  145.     ShowMessage('Erreur d''initialisation SC68');
  146.     Exit;
  147.   end;
  148.  // instance SC68
  149.   FillChar(sc_cr, SizeOf(sc_cr), 0);
  150.    sc_cr.SamplingRate := 44100;
  151.    sc_cr.Name :=  PChar('Lazarus'#0);
  152.    SC68Instance := sc68_create(sc_cr);
  153.  
  154.   if SC68Instance = nil then
  155.   begin
  156.     ShowMessage('Erreur lors de la creation de l''instance SC68');
  157.     Exit;
  158.   end;
  159.  
  160.   // Charge le fichier SC68  ;
  161.   if sc68_load_uri(SC68Instance, pchar(Filename))  = 0 then
  162.  
  163.    ShowMessage('file '+ Filename + ' Loaded to sc68 memory')
  164.    else
  165.    ShowMessage('ERROR: Unable to Load File into sc68 memory ' );
  166.  
  167.    // get music information first and then get number of track
  168.    sc68_music_info(SC68Instance,sc_minfos,0,sc_disc);
  169.    // Force track number if >1 ; set to 2 ;
  170.    if sc_minfos.Tracks>1 then
  171.    sc68_play(SC68Instance,2,0) // madness has 3 tracks  , no-loop else 1
  172.    else
  173.    sc68_play(SC68Instance,-1,0); // default track
  174.  
  175. end;
  176.  
  177. procedure TForm1.FormShow(Sender: TObject);
  178. begin
  179.      Music_Finished := false;
  180.      InitAudio;
  181.      sc68_music_info(SC68Instance,sc_minfos,0,sc_disc);
  182.      memo1.clear;
  183.  
  184.      memo1.Lines.Add( Pchar(sc_minfos.Album));
  185.      memo1.Lines.Add( Pchar(sc_minfos.Title));
  186.      memo1.Lines.Add( Pchar(sc_minfos.Artist));
  187.      memo1.Lines.Add( Pchar(sc_minfos.Format));
  188.      memo1.Lines.Add( Pchar(sc_minfos.Genre));
  189.      memo1.Lines.Add( Pchar(sc_minfos.Year));
  190.      memo1.Lines.Add( Pchar(sc_minfos.Ripper));
  191.      memo1.Lines.Add( Pchar(sc_minfos.Converter));
  192.      memo1.Lines.Add( Pchar(sc_minfos.LastTag));
  193.      memo1.Lines.Add( 'Num Tracks :' + InttoStr(sc_minfos.Tracks));
  194.  
  195.  
  196.  
  197. end;
  198.  
  199. procedure TForm1.Process_SC68(BufferIndex: Integer);
  200. var
  201.   Samples: Integer;
  202.   resultCode: Tsc68Code;
  203.  
  204. begin
  205.  
  206.  
  207.   if not Music_Finished then
  208.   begin
  209.  
  210.        Samples := BufSize div (Channels * (BitsPerSample div 8));
  211.        resultCode := Tsc68Code(sc68_process(SC68Instance, @buffers[BufferIndex][0], @Samples));
  212.      //  resultCode := Tsc68Code(sc68_process(SC68Instance, @buffers[BufferIndex][0], @Samples) div 4  and Int64(SC68_END));
  213.  
  214.    if (Int64(resultCode) and Int64(SC68_END)) <> 0 then
  215.      begin
  216.       Music_Finished := True;
  217.       ShowMessage('Musique terminée!');
  218.       sc68_close(SC68Instance);
  219.       sc68_destroy(SC68Instance);
  220.       CloseAudio;
  221.       Exit;
  222.      end;
  223.   end;
  224. end;
  225.  
  226. procedure TForm1.CloseAudio;
  227. var
  228.   i: Integer;
  229. begin
  230.   for i := 0 to BufferCount - 1 do
  231.   begin
  232.     waveOutUnprepareHeader(waveOut, @waveHeaders[i], SizeOf(TWaveHdr));
  233.   end;
  234.   waveOutClose(waveOut);
  235. end;
  236.  
  237.  
  238. // not need here !!!
  239. procedure TForm1.Timer1Timer(Sender: TObject);
  240. begin
  241. end;
  242.  
  243. end.

sc68 unit :

Code: Pascal  [Select][+][-]
  1. unit sc68;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils;
  9.  
  10. type
  11.  
  12.   Psc68 = ^Tsc68;
  13.   Tsc68 = record
  14.   end;
  15.  
  16.   Tsc68Disk = Pointer;
  17.  
  18.   Tsc68MsgHandler = procedure(cat: Integer; sc68: Psc68; fmt: PChar; args: Pointer); cdecl;
  19.  
  20.   Tsc68InitFlags = record
  21.     NoLoadConfig: Boolean;
  22.     NoSaveConfig: Boolean;
  23.   end;
  24.  
  25.   { Paramètres d'initialisation de l'API }
  26.   Tsc68Init = record
  27.     MsgHandler: Tsc68MsgHandler;
  28.     DebugClrMask: Integer;
  29.     DebugSetMask: Integer;
  30.     Argc: Integer;
  31.     Argv: PChar;
  32.     Flags: Tsc68InitFlags;
  33.     shared_path : PChar;
  34.     user_path : PChar;
  35.     lmusic_path: PChar;
  36.     rmusic_path: PChar;
  37.     sampling_rate : UInt16;
  38.  
  39.   end;
  40.  
  41.   init68 = ^Tsc68Init;
  42.  
  43.   Tsc68Create = record
  44.     SamplingRate: Cardinal;
  45.     Name: PChar;
  46.     Log2Mem: Integer;
  47.     Emu68Debug: Integer;
  48.     Cookie: Pointer;
  49.   end;
  50.  
  51.   Tsc68Tag = record
  52.     Key: PChar;
  53.     Value: PChar;
  54.   end;
  55.  
  56.   Tsc68CInfo = record
  57.     Track: Cardinal;
  58.     TimeMs: Cardinal;
  59.     TimeStr: array[0..11] of Char;
  60.     UsesYM: Boolean;
  61.     UsesSTE: Boolean;
  62.     UsesAmiga: Boolean;
  63.     UsesASID: Boolean;
  64.     HardwareName: PChar;
  65.     TagCount: Integer;
  66.     Tags: ^Tsc68Tag;
  67.   end;
  68.  
  69.   Tsc68MusicInfo = record
  70.     Tracks: Integer;
  71.     Track : Integer;
  72.     Addr: Cardinal;
  73.     Rate: Cardinal;
  74.     Replay: PChar;
  75.     DiskInfo: Tsc68CInfo;
  76.     TrackInfo: Tsc68CInfo;
  77.  
  78.     Album: PChar;
  79.     Title: PChar;
  80.     Artist: PChar;
  81.     Format: PChar;
  82.     Genre: PChar;
  83.     Year: PChar;
  84.     Ripper: PChar;
  85.     Converter: PChar;
  86.     LastTag: PChar;
  87.   end;
  88.  
  89.   Psc68MusicInfo = ^Tsc68MusicInfo;
  90.   Tsc68MInfo = Tsc68MusicInfo;
  91.   type
  92.   Tsc68Code = (
  93.     SC68_IDLE   = 1 shl 0,
  94.     SC68_CHANGE = 1 shl 1,
  95.     SC68_LOOP   = 1 shl 2,
  96.     SC68_END    = 1 shl 3,
  97.     SC68_SEEK   = 1 shl 4,
  98.     SC68_OK     = 0,
  99.     SC68_ERROR  = -1
  100.   );
  101.  
  102.   Tsc68Spr = (
  103.     SC68_SPR_QUERY   = -1,
  104.     SC68_SPR_DEFAULT = 0
  105.   );
  106.  
  107.   Tsc68Play = (
  108.     SC68_DSK_TRACK = 0,
  109.     SC68_DEF_LOOP  = 0,
  110.     SC68_INF_LOOP  = -1,
  111.     SC68_DEF_TRACK = -1,
  112.     SC68_CUR_TRACK = -2,
  113.     SC68_CUR_LOOP  = -2
  114.   );
  115.  
  116.   Tsc68PCM = (
  117.     SC68_PCM_S16 = 1,
  118.     SC68_PCM_F32 = 2
  119.   );
  120.  
  121.   Tsc68ASID = (
  122.     SC68_ASID_OFF   = 0,
  123.     SC68_ASID_ON    = 1,
  124.     SC68_ASID_FORCE = 2,
  125.     SC68_ASID_NO_A  = 4,
  126.     SC68_ASID_NO_B  = 8,
  127.     SC68_ASID_NO_C  = 16
  128.   );
  129.  
  130.   Tsc68Cntl = (
  131.     SC68_NOP = 0,
  132.     SC68_GET_LAST,
  133.     SC68_GET_NAME,
  134.     SC68_GET_TRACKS,
  135.     SC68_GET_TRACK,
  136.     SC68_GET_DEFTRK,
  137.     SC68_GET_LOOPS,
  138.     SC68_GET_LOOP,
  139.     SC68_GET_DISK,
  140.     SC68_GET_SPR,
  141.     SC68_SET_SPR,
  142.     SC68_GET_LEN,
  143.     SC68_GET_TRKLEN,
  144.     SC68_GET_DSKLEN,
  145.     SC68_GET_ORG,
  146.     SC68_GET_TRKORG,
  147.     SC68_GET_POS,
  148.     SC68_GET_DSKPOS,
  149.     SC68_GET_PLAYPOS,
  150.     SC68_SET_POS,
  151.     SC68_GET_PCM,
  152.     SC68_SET_PCM,
  153.     SC68_CAN_ASID,
  154.     SC68_GET_ASID,
  155.     SC68_SET_ASID,
  156.     SC68_GET_COOKIE,
  157.     SC68_SET_COOKIE,
  158.     SC68_EMULATORS,
  159.     SC68_CONFIG_LOAD,
  160.     SC68_CONFIG_SAVE,
  161.     SC68_ENUM_OPT,
  162.     SC68_GET_OPT,
  163.     SC68_SET_OPT_STR,
  164.     SC68_SET_OPT_INT,
  165.     SC68_DIAL,
  166.     SC68_CNTL_LAST
  167.   );
  168.  
  169.   function sc68_version: Integer; cdecl; external 'sc68.dll';
  170.   function sc68_versionstr: PChar; cdecl; external 'sc68.dll';
  171.   function sc68_init(var init: Tsc68Init): Integer; cdecl; external 'sc68.dll' name 'sc68_init';
  172.   procedure sc68_shutdown; cdecl; external 'sc68.dll';
  173.   function sc68_create(create: Tsc68Create): Pointer; cdecl; external 'sc68.dll' name 'sc68_create';
  174.   procedure sc68_destroy(sc68: Psc68); cdecl; external 'sc68.dll';
  175.   function sc68_cntl(sc68: Psc68; op: Integer; args: array of const): Integer; cdecl; external 'sc68.dll';
  176.  // function sc68_error: PChar; cdecl; external 'sc68.dll' name 'sc68_error';
  177.   function sc68_process(sc68: Psc68; buf: Pointer;   n: pointer): integer; cdecl; external 'sc68.dll' name 'sc68_process';
  178.   function sc68_play(sc68: Psc68; track, loop: Integer): Integer; cdecl; external 'sc68.dll';
  179.   function sc68_stop(sc68: Psc68): Integer; cdecl; external 'sc68.dll';
  180.   function sc68_music_info(sc68: Psc68; info: Tsc68MusicInfo; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  181.   function sc68_tag_get(sc68: Psc68; tag: Tsc68Tag; track: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  182.   function sc68_tag(sc68: Psc68; key: PChar; track: Integer; disk: Tsc68Disk): PChar; cdecl; external 'sc68.dll';
  183.   function sc68_tag_enum(sc68: Psc68; tag: Tsc68Tag; track, idx: Integer; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  184.   function sc68_mimetype: PChar; cdecl; external 'sc68.dll';
  185.   function sc68_vfs(uri: PChar; mode, argc: Integer; args: array of const): Pointer; cdecl; external 'sc68.dll';
  186.   function sc68_is_our_uri(uri, exts: PChar; var is_remote: Integer): Integer; cdecl; external 'sc68.dll';
  187.   function sc68_load(sc68: Psc68; is_: Pointer): Integer; cdecl; external 'sc68.dll';
  188.   function sc68_load_uri(sc68: Psc68; uri: PChar): Integer; cdecl; external 'sc68.dll';
  189.   function sc68_load_mem(sc68: Psc68; buffer: Pointer; len: Integer): Integer; cdecl; external 'sc68.dll';
  190.   function sc68_load_disk(is_: Pointer): Tsc68Disk; cdecl; external 'sc68.dll';
  191.   function sc68_load_disk_uri(uri: PChar): Tsc68Disk; cdecl; external 'sc68.dll';
  192.   function sc68_disk_load_mem(buffer: Pointer; len: Integer): Tsc68Disk; cdecl; external 'sc68.dll';
  193.   procedure sc68_disk_free(disk: Tsc68Disk); cdecl; external 'sc68.dll';
  194.   function sc68_open(sc68: Psc68; disk: Tsc68Disk): Integer; cdecl; external 'sc68.dll';
  195.   procedure sc68_close(sc68: Psc68); cdecl; external 'sc68.dll';
  196.   function sc68_ym_channels(sc68: Psc68; channels: Integer): Integer; cdecl; external 'sc68.dll';
  197.  
  198. implementation
  199.  
  200. end.
  201.  
« Last Edit: November 24, 2024, 10:36:38 pm by Gigatron »
Sub Quantum Technology ! Gigatron 68000 Colmar France;

Gigatron

  • Full Member
  • ***
  • Posts: 162
  • Amiga Rulez !!
Re: SC68 Player
« Reply #7 on: November 24, 2024, 10:43:38 pm »
Thank you for the compliment but all that I did was browsing through the  c-sources and reading what the developer(s) wrote  :)

Another observation is that none of my sound-fx modules (v1 and v2) seem to work as the emulator stumbles upon an invalid instruction.


Hi @Tron ;   
Some Amiga formats played with sc68 must be converted to .sc68 , some soundfx are converted.
Example on my page ; http://gigatron3k.free.fr/html5/Sc68player/ speed is a little fast than original sc68 player !!
Will now work on UADE maybe one day all Amiga obsucre format can played with FPC :)
Regards
Sub Quantum Technology ! Gigatron 68000 Colmar France;

TRon

  • Hero Member
  • *****
  • Posts: 3813
Re: SC68 Player
« Reply #8 on: November 24, 2024, 11:13:13 pm »
Thank you for your work Gigatron, especially the sfx examples 👍

I'm having fun with sc68... it is pretty straightforward to add a custom replayer (as long as you know a little 68k assembler).

I am ashamed to admit that I didn't not really payed much attention to this replayer back in the day but the code and solutions used are pretty lean and clean.

I do not have to remember anything anymore thanks to total-recall.

 

TinyPortal © 2005-2018