Forum > Audio and Video
AY_FLY Library
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