Forum > Audio and Video

AY_FLY Library

(1/6) > >>

Gigatron:
Hi,
Before taking my vacation I would like to share with you a music player:

Ay_Fly from  Copyright (C) 2008 by Deryabin Andrew  andrew@it-optima.ru
This project library is compiled for X64 from me with visual studio 2019, exported some functions of the ayfly.dll;
The result is ok now for listen different music formats; ASC, AY, PSC, PT1,PT2,PT3, SQT, STC, STP, VTX (vortex tracker2 by Sergey
Bulba), and YM from Leonard Oxygene;
aylet player, Vortex, YM, PSC, ASC Sound Master, Pro Tracker 2, Pro Tracker 3, ST Song Compiler, Sound Tracker Pro, Pro Sound Creator, SQ Tracker, Pro Tracker 1

https://bulba.untergrund.net/music_e.htm


I made sure some mistakes but wait a bit :)

So let's share the units and library.dll + 2 musics from Klim and c-Jeff of Phat! + the_project


--- 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, ExtCtrls,windows,mmsystem,ayfly;  const  REPLAY_RATE = 44100;  CHANNELS = 2;  REPLAY_DEPTH  = 16;  BuffSize = 65536*2; //   multiple of 2  BufferCount = 4; // 2 type   { TForm1 }   TForm1 = class(TForm)    Timer1: TTimer;    procedure FormCreate(Sender: TObject);  private     buffers: array[0..BufferCount-1] of array[0..BuffSize-1] of SmallInt;     waveHeaders: array[0..BufferCount-1] of TWaveHdr;     currentBuffer: Integer;  public   end; var  Form1: TForm1;   waveOut: HWAVEOUT;  waveHeader: TWaveHdr;  ok_flag : boolean = true;  playing: Boolean = true;  // ayfly  songInfo: PAYSongInfo;  songName: PChar;  FileStream: TFileStream;  implementation {$R *.lfm} { TForm1 } /// audio init et le reste !!procedure HandleError(const Str: PAnsiChar);begin  if Str <> nil then  begin    ShowMessage('Error: Wrong Format ? '+ Str);    Halt(1);  end;end; procedure FillBuffer(bufferIndex: Integer); begin  if playing then  begin    //le buffer avec des données audio     ay_rendersongbuffer(songInfo, @Form1.buffers[bufferIndex][0], BuffSize * SizeOf(SmallInt) );     Form1.waveHeaders[bufferIndex].dwFlags := Form1.waveHeaders[bufferIndex].dwFlags and (not WHDR_DONE);   end;end; function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;begin  if uMsg = WOM_DONE then  begin    FillBuffer(Form1.currentBuffer);    waveOutWrite(waveOut, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));    Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;  end;   Result := 0;end; procedure InitAudio;var  wFormat: TWaveFormatEx;  i: Integer;begin  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);   with wFormat do  begin    wFormatTag := WAVE_FORMAT_PCM; // 1   nChannels := CHANNELS; // stereo 2   nSamplesPerSec := REPLAY_RATE; // 44100   wBitsPerSample := REPLAY_DEPTH; // 16 bits   nBlockAlign := nChannels * (wBitsPerSample div 8); // Taille echantillon   nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;   cbSize := 0;  end;   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then   ShowMessage('Error: Audio initialization failed');   // buffers  for i := 0 to BufferCount - 1 do  begin    ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));     with Form1.waveHeaders[i] do    begin      lpData := @Form1.buffers[i][0];      dwBufferLength := BuffSize * SizeOf(SmallInt) ;      dwFlags := 0 ;    end;     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));  end;    Form1.currentBuffer := 0;end; procedure CloseAudio;begin  waveOutUnprepareHeader(waveOut, @waveHeader, SizeOf(TWaveHdr));  waveOutClose(waveOut);end;   procedure TForm1.FormCreate(Sender: TObject); begin  InitAudio;    // Init song   SongInfo := ay_initsong('doubdesh.pt3', 44100,SongInfo);   ay_rendersongbuffer(songInfo, @Form1.buffers[0][0], BuffSize * SizeOf(SmallInt)  );  // FillBuffer(0);    waveOutWrite(waveOut, @waveHeaders[0], SizeOf(TWaveHdr));  // start audio !end;  end. 
Ayfly 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 ayfly; {$mode objfpc}{$H+} interface uses  ctypes; const  ayflydll = 'ayfly.dll'; type   AY_CHAR = PChar;    ELAPSED_CALLBACK = function(arg: Pointer): Boolean; cdecl;  STOPPED_CALLBACK = procedure(arg: Pointer); cdecl;   AYSongInfo = record    Author: AY_CHAR;         // Auteur    Name: AY_CHAR;           // Nom    FilePath: AY_CHAR;       // Chemin du fichier de la chanson    PrgName: AY_CHAR;        // Nom programme    TrackName: AY_CHAR;      // Nom piste    CompName: AY_CHAR;       // Nom du compilateur    NumSongs: culong;        // Nombre de chansons    CurrentSong: culong;     // Chanson actuelle    Length: culong;          // Longueur de la chanson    Loop: culong;            // Position de la boucle    timeElapsed: culong;     // Temps ecoule    e_callback: ELAPSED_CALLBACK;  // Callback pour la fin de chanson    s_callback: STOPPED_CALLBACK;  // Callback pour l'arrêt de chanson  end;  PAYSongInfo = ^AYSongInfo;   //   DLL   un-finished functions;  function ay_initsong(FilePath: AY_CHAR; sr: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_initsong';  function ay_initsongindirect(module: PByte; sr: Cardinal; size: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_initsongindirect';  function ay_getsonginfo(FilePath: AY_CHAR): PAYSongInfo; cdecl; external ayflydll name 'ay_getsonginfo';  function ay_getsonginfoindirect(module: PByte; size: Cardinal; sr: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_getsonginfoindirect';  function ay_getsongname(info: PAYSongInfo): AY_CHAR; cdecl; external ayflydll name 'ay_getsongname';  procedure ay_closesong(var info: PAYSongInfo); cdecl; external ayflydll name 'ay_closesong';  procedure ay_startsong(info: PAYSongInfo); cdecl; external ayflydll name 'ay_startsong';  function ay_songstarted(info: PAYSongInfo): Boolean; cdecl; external ayflydll name 'ay_songstarted';  function ay_rendersongbuffer(info: Pointer; buffer: PByte; buffer_length: Cardinal): Cardinal; cdecl; external ayflydll name 'ay_rendersongbuffer';  implementation end.  

 

Gigatron:
Hi,
Well it seems that there is a problem with the buffer which causes a slight delay when listening to the music, I will look into that when I return. For now I modified the code a little.

Main unit; no change on ayfly.pas :


--- 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, ExtCtrls, StdCtrls,  windows, mmsystem, ayfly;  const  REPLAY_RATE = 48000;  CHANNELS = 2;  REPLAY_DEPTH  = 16;  BuffSize = 65536*2; //   multiple of 2  BufferCount = 2; type   { TForm1 }   TForm1 = class(TForm)    Memo1: TMemo;    Timer1: TTimer;    procedure FormCreate(Sender: TObject);  private     buffers: array[0..BufferCount-1] of array[0..BuffSize-1] of UInt32;     waveHeaders: array[0..BufferCount-1] of TWaveHdr;     currentBuffer: Integer;  public   end; var  Form1: TForm1;  waveOut: HWAVEOUT;  waveHeader: TWaveHdr;  playing: Boolean = true;  // ayfly  songInfo: PAYSongInfo; implementation {$R *.lfm} { TForm1 } /// audio init et le reste !!procedure HandleError(const Str: PAnsiChar);begin  if Str <> nil then  begin    ShowMessage('Error: Wrong Format ? '+ Str);    Halt(1);  end;end; procedure FillBuffer(bufferIndex: Integer);begin  if playing then  begin    //le buffer avec des données audio     bufferIndex := Form1.currentBuffer;     ay_rendersongbuffer(songInfo, @Form1.buffers[bufferIndex][0], BuffSize * SizeOf(UInt32) );  end;end; function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;begin  if uMsg = WOM_DONE then  begin    FillBuffer(Form1.currentBuffer);    waveOutWrite(waveOut, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));    Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;  end;  Result := 0;end; procedure InitAudio;var  wFormat: TWaveFormatEx;  i: Integer;begin // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);  // if needed   with wFormat do  begin   wFormatTag := WAVE_FORMAT_PCM; // 1   nChannels := CHANNELS; // stereo 2   nSamplesPerSec := REPLAY_RATE; // 48000   wBitsPerSample := REPLAY_DEPTH; // 16 bits   nBlockAlign := nChannels * (wBitsPerSample div 8); // Taille echantillon   nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;   cbSize := 0;  end;   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then   ShowMessage('Error: Audio initialization failed');   // buffers  for i := 0 to BufferCount - 1 do  begin    ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));     with Form1.waveHeaders[i] do    begin      lpData := @Form1.buffers[i][0];      dwBufferLength := BuffSize * SizeOf(UInt32); // ** to-do type ! Unsigned int      dwFlags := 0 ;    end;     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));  end;    Form1.currentBuffer := 0;end; procedure CloseAudio;begin  waveOutUnprepareHeader(waveOut, @waveHeader, SizeOf(TWaveHdr));  waveOutClose(waveOut);end; procedure TForm1.FormCreate(Sender: TObject);begin   InitAudio;   // Init song   SongInfo := ay_initsong('cs.pt3', REPLAY_RATE,SongInfo);   fillbuffer(0);   waveOutWrite(waveOut, @waveHeaders[0], SizeOf(TWaveHdr));  // start audio !   // song infos   Memo1.Clear;   Memo1.Lines.Add('Song name : ' + (SongInfo^.Name));   Memo1.Lines.Add('Song author : ' + (SongInfo^.Author));end; end.

Gigatron:
Well, after visiting 3 fantastic countries, Hungary, Serbia and Turkey. I reloaded the cortex with energy to continue programming :)

So the bug is now fixed with faster than light;
Projet.zip and songs example is attached in zip file:

The 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, ExtCtrls, StdCtrls,  windows, mmsystem, ayfly;  const  REPLAY_RATE = 44100;  CHANNELS = 2;  REPLAY_DEPTH  = 16;  BuffSize = 32768; //   multiple of 2  BufferCount = 2; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    Button2: TButton;    Memo1: TMemo;    Memo2: TMemo;    Timer1: TTimer;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure FormShow(Sender: TObject);    procedure Timer1Timer(Sender: TObject);  private     buffers: array[0..BufferCount-1] of array[0..BuffSize-1] of Byte;     waveHeaders: array[0..BufferCount-1] of TWaveHdr;     currentBuffer: Integer;  public   end; var  Form1: TForm1;  waveOut: HWAVEOUT;  waveHeader: TWaveHdr;  playing: Boolean = false;  // ayfly  songInfo: PAYSongInfo; implementation {$R *.lfm} { TForm1 } /// audio init et le reste !!procedure HandleError(const Str: PAnsiChar);begin  if Str <> nil then  begin    ShowMessage('Error: Wrong Format ? '+ Str);    Halt(1);  end;end; procedure FillBuffer(bufferIndex: Integer);begin  if playing then  begin    //le buffer avec des données audio     bufferIndex := Form1.currentBuffer;     ay_rendersongbuffer(songInfo, @Form1.buffers[bufferIndex][0], BuffSize * SizeOf(Byte) );  end;end; function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;begin  if uMsg = WOM_DONE then  begin    FillBuffer(Form1.currentBuffer);    waveOutWrite(waveOut, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));    Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;  end;  Result := 0;end; procedure InitAudio;var  wFormat: TWaveFormatEx;  i: Integer;begin // SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);  // if needed   with wFormat do  begin   wFormatTag := WAVE_FORMAT_PCM; // 1   nChannels := CHANNELS; // stereo 2   nSamplesPerSec := REPLAY_RATE; // 44100   wBitsPerSample := REPLAY_DEPTH; // 16 bits   nBlockAlign := nChannels * (wBitsPerSample div 8); // Taille echantillon   nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;   cbSize := 0;  end;   if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then   ShowMessage('Error: Audio initialization failed');   // buffers  for i := 0 to BufferCount - 1 do  begin    ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));     with Form1.waveHeaders[i] do    begin      lpData := @Form1.buffers[i][0];      dwBufferLength := BuffSize * SizeOf(Byte);      dwFlags := 0 ;    end;     waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));  end;     Form1.currentBuffer := 0;    for i := 0 to BufferCount - 1 do     begin     FillBuffer(i);     waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));    end;end; procedure CloseAudio;begin  waveOutUnprepareHeader(waveOut, @waveHeader, SizeOf(TWaveHdr));  waveOutClose(waveOut);end; procedure TForm1.FormCreate(Sender: TObject);begin    // Init song  Turbo-sound 2xAy chips for this song !! 6 channels      SongInfo := ay_initsong('Yerzmyey - 5 New Linel.pt3', REPLAY_RATE,SongInfo);end; procedure TForm1.Button1Click(Sender: TObject);  // AY = 0begin    ay_setchiptype(Songinfo,0);end; procedure TForm1.Button2Click(Sender: TObject); // YM = 1begin    ay_setchiptype(Songinfo,1);end; procedure TForm1.FormShow(Sender: TObject);begin     InitAudio;     playing := true;    // song infos   Memo1.Clear;   Memo1.Lines.Add('Song Name : ' + (SongInfo^.Name));   Memo1.Lines.Add('Song Author : ' + (SongInfo^.Author));   Memo1.Lines.Add('Song Compiler : ' + (SongInfo^.CompName));   Memo1.Lines.Add('Song Len : ' +  IntToStr(SongInfo^.Length));   Memo1.Lines.Add('Song Loop Pos : ' + IntToStr(SongInfo^.Loop));   Memo1.Lines.Add('Number Of Song : ' + IntToStr(SongInfo^.NumSongs)); end; procedure TForm1.Timer1Timer(Sender: TObject);begin  Memo2.Clear;  If playing then  begin    Memo2.Lines.Add('Elapsed Time : '+ IntToStr(ay_getelapsedtime(SongInfo) div 50));  end;end;// If subsongs exist then Songinfo^.CurrentSong = subsong number;// ay_resetsong(SongInfo); end.
Ay Fly 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 ayfly; {$mode objfpc}{$H+} interface uses  ctypes; const  ayflydll = 'ayfly.dll'; type   AY_CHAR = PChar;   ELAPSED_CALLBACK = function(arg: Pointer): Boolean; cdecl;  STOPPED_CALLBACK = procedure(arg: Pointer); cdecl;   AYSongInfo = record    Author: AY_CHAR;         // Auteur    Name: AY_CHAR;           // Nom    FilePath: AY_CHAR;       // Chemin du fichier de la chanson    PrgName: AY_CHAR;        // Nom programme    TrackName: AY_CHAR;      // Nom piste    CompName: AY_CHAR;       // Nom du compilateur    NumSongs: culong;        // Nombre de chansons    CurrentSong: culong;     // Chanson actuelle    Length: culong;          // Longueur de la chanson    Loop: culong;            // Position de la boucle    timeElapsed: culong;     // Temps ecoule    e_callback: ELAPSED_CALLBACK;  // Callback pour la fin de chanson    s_callback: STOPPED_CALLBACK;  // Callback pour l'arrêt de chanson  end;  PAYSongInfo = ^AYSongInfo;   //   DLL   unifinished functions;  function ay_initsong(FilePath: AY_CHAR; sr: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_initsong';  function ay_initsongindirect(module: PByte; samplerate: Cardinal; size: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_initsongindirect';  function ay_getsonginfo(FilePath: AY_CHAR): PAYSongInfo; cdecl; external ayflydll name 'ay_getsonginfo';  function ay_getsonginfoindirect(module: PByte; size: Cardinal; sr: Cardinal; player: Pointer): PAYSongInfo; cdecl; external ayflydll name 'ay_getsonginfoindirect';  function ay_getsongname(info: PAYSongInfo): AY_CHAR; cdecl; external ayflydll name 'ay_getsongname';  function ay_getelapsedtime(info: PAYSongInfo): Cardinal; cdecl; external ayflydll name 'ay_getelapsedtime';  procedure ay_closesong(info: PAYSongInfo); cdecl; external ayflydll name 'ay_closesong';  procedure ay_startsong(info: PAYSongInfo); cdecl; external ayflydll name 'ay_startsong';  function ay_songstarted(info: PAYSongInfo): Boolean; cdecl; external ayflydll name 'ay_songstarted';  function ay_rendersongbuffer(info: Pointer; buffer: PByte; buffer_length: Cardinal): Cardinal; cdecl; external ayflydll name 'ay_rendersongbuffer';   procedure ay_resetsong(info: PAYSongInfo); cdecl; external ayflydll name 'ay_resetsong';   procedure ay_setchiptype(info: PAYSongInfo ;chip : integer); cdecl; external ayflydll name 'ay_setchiptype';  procedure ay_setsamplerate(info: PAYSongInfo ;srate : Cardinal); cdecl; external ayflydll name 'ay_setsamplerate'; implementation end.  

Guva:
I still haven't figured out how to compile it in dynamics.

https://github.com/l29ah/ayfly

TRon:

--- Quote from: Guva on November 07, 2024, 02:32:44 pm ---I still haven't figured out how to compile it in dynamics.

--- End quote ---
I am a bit confused about what you wrote/meant there.

Do you mean that you do not know how to change the header files so that the library can manually be loaded at runtime ?

Navigation

[0] Message Index

[#] Next page

Go to full version