I'm making N64 audio plugin with Delphi. It works pretty nicely but I need some technical help with it. I don't know if the way how I handle AiLenChanged is correct. I simply leech all audio data to prebuffer and set SndLen register to 0.
It seems that even if "Limit FPS" is enabled, the game outputs audio way too fast and the prebuffer comes too long. The sound quality itself is fine though.
Increasing playback frequency helps a bit, for example multiplying game's dacrate with 1.3 eliminates latency but I don't think that's correct way to fix this.
Here's the full source:
It seems that even if "Limit FPS" is enabled, the game outputs audio way too fast and the prebuffer comes too long. The sound quality itself is fine though.
Increasing playback frequency helps a bit, for example multiplying game's dacrate with 1.3 eliminates latency but I don't think that's correct way to fix this.
Here's the full source:
Code:
library DolphinAudio;
uses DirectSound, Windows, MMSystem, SysUtils;
type
TPluginInfo = record
Version : Word;
PluginType : Word;
Name : array [0..100] of Char;
NormalMemory : Boolean;
MemoryBSwapped : Boolean;
end;
TAudioInfo = record
HWND : THandle;
HInstance : HInst;
MemoryBSwapped : Boolean;
Header : pByte;
RDRam : pByte;
DMem : pByte;
IMem : pByte;
MiIntrReg : ^Cardinal;
AiDramAddrReg : ^Cardinal;
AiLenReg : ^Cardinal;
AiControlReg : ^Cardinal;
AiStatusReg : ^Cardinal;
AiDacrateReg : ^Cardinal;
AiBitrateReg : ^Cardinal;
CheckInterrupts : procedure;
end;
pAudioInfo = ^TAudioInfo;
pPluginInfo = ^TPluginInfo;
const
BufferSize = 1024 * 8;//8kb
PlugInTypeAudio = 3;
SystemNtsc = 0;
SystemPal = 1;
SystemMPal = 2;
MiIntrAi = 4;
AiStatusFifoFull = $80000000;
AiStatusDmaBusy = $40000000;
var
DSound : IDirectSound = nil;
DSoundBuffer : IDirectSoundBuffer = nil;
DSoundNotify : IDirectSoundNotify = nil;
Frequency : Cardinal;
Dacrate : Cardinal;
SndLen : Cardinal;
ReadAddr : pByte;
AudioInfo : TAudioInfo;
HR : HResult;
PreBuffer : array of Byte;
Events : array [0..2] of THandle;
Notifies : array [0..3] of TDSBPositionNotify;
procedure Msg(Msg: string);
begin
MessageBox(0,pChar(Msg),'Audio Plugin',MB_ICONINFORMATION);
end;
procedure SetBuffers;
var
BD: TDSBufferDesc;
WF: TWaveFormatEx;
HR: HResult;
begin
FillChar(BD,SizeOf(TDSBufferDesc),0);
BD.dwSize := SizeOf(TDSBufferDesc);
BD.dwFlags := DSBCAPS_GLOBALFOCUS or DSBCAPS_CTRLPOSITIONNOTIFY or DSBCAPS_CTRLFREQUENCY;
BD.dwBufferBytes := BufferSize;
BD.lpwfxFormat := @WF;
FillChar(WF,SizeOf(TWaveFormatEx),0);
WF.wFormatTag := WAVE_FORMAT_PCM;
WF.nChannels := 2;
WF.nSamplesPerSec := Frequency;
WF.wBitsPerSample := 16;
WF.nBlockAlign := (WF.wBitsPerSample div 8) * WF.nChannels;
WF.nAvgBytesPerSec := WF.nSamplesPerSec * WF.nBlockAlign;
HR := DSound.CreateSoundBuffer(BD,DSoundBuffer,nil);
if Failed(HR) then
begin
Msg('DirectSound.CreateSoundBuffer failed. ' + DSErrorString(HR));
Exit;
end;
Notifies[0].dwOffset := (BufferSize div 2) - 1;
Notifies[0].hEventNotify := Events[0];
Notifies[1].dwOffset := (BufferSize) - 1;
Notifies[1].hEventNotify := Events[1];
Notifies[2].dwOffset := DSBPN_OFFSETSTOP;
Notifies[2].hEventNotify := Events[2];
if Failed(DSoundBuffer.QueryInterface(IID_IDirectSoundNotify,DSoundNotify)) then
begin
Msg('DirectSoundBuffer.QueryInterface (IDirectSoundNotify) failed.');
Exit;
end;
if Failed(DSoundNotify.SetNotificationPositions(2,@Notifies)) then
begin
Msg('DirectSoundNotify.SetNotificationPositions failed.');
Exit;
end;
end;
procedure AiDacrateChanged(SystemType: Integer); cdecl;
begin
if Dacrate <> AudioInfo.AiDacrateReg^ then
begin
Dacrate := AudioInfo.AiDacrateReg^;
case SystemType of
SystemNtsc: Frequency := Round(48681812 / (Dacrate + 1));
SystemPal: Frequency := Round(49656530 / (Dacrate + 1));
SystemMPal: Frequency := Round(48628316 / (Dacrate + 1));
end;
SetBuffers;
DSoundBuffer.SetFrequency(Frequency);
end;
end;
procedure AiUpdate(Wait: Boolean); cdecl;
var
Evt,S,I: Cardinal;
Data,Data2: Pointer;
BytesLocked,BytesLocked2: Cardinal;
begin
if Wait then
Evt := MsgWaitForMultipleObjects(2,Events,False,INFINITE,QS_ALLINPUT)
else
Evt := MsgWaitForMultipleObjects(2,Events,False,0,QS_ALLINPUT);
Evt := Evt - WAIT_OBJECT_0;
if (Evt = 2) or (Length(PreBuffer) < BufferSize div 2) then Exit;
case Evt of
0:
begin
if Failed(DSoundBuffer.Lock(0,BufferSize div 2,Data,BytesLocked,Data2,BytesLocked,0)) then
begin
Msg('DirectSoundBuffer.Lock failed.');
Exit;
end;
Move(PreBuffer[0],Data^,BufferSize div 2);
Move(PreBuffer[BufferSize div 2],PreBuffer[0],Length(PreBuffer) - BufferSize div 2);
SetLength(PreBuffer,Length(PreBuffer) - BufferSize div 2);
DSoundBuffer.Unlock(Data,BytesLocked,nil,0);
end;
1:
begin
if Failed(DSoundBuffer.Lock(BufferSize div 2,BufferSize div 2,Data,BytesLocked,Data2,BytesLocked,0)) then
begin
Msg('DirectSoundBuffer.Lock failed.');
Exit;
end;
Move(PreBuffer[0],Data^,BufferSize div 2);
Move(PreBuffer[BufferSize div 2],PreBuffer[0],Length(PreBuffer) - BufferSize div 2);
SetLength(PreBuffer,Length(PreBuffer) - BufferSize div 2);
DSoundBuffer.Unlock(Data,BytesLocked,nil,0);
end;
end;
DSoundBuffer.GetStatus(S);
if S and DSBSTATUS_PLAYING = 0 then
DSoundBuffer.Play(0,0,DSBPLAY_LOOPING);
end;
procedure AiLenChanged; cdecl;
var
I: Integer;
begin
if AudioInfo.AiLenReg^ = 0 then Exit;
AudioInfo.AiStatusReg^ := AudioInfo.AiStatusReg^ or AiStatusFifoFull;
SndLen := AudioInfo.AiLenReg^ and $3FFF8;
ReadAddr := Pointer(Integer(AudioInfo.RDRam) + (AudioInfo.AiDramAddrReg^ and $00FFFFF8));
if SndLen <> 0 then
begin
I := Length(PreBuffer);
SetLength(PreBuffer,I + SndLen);
Move(ReadAddr^,PreBuffer[I],SndLen);
SndLen := 0;
AudioInfo.AiStatusReg^ := AudioInfo.AiStatusReg^ and (not AiStatusFifoFull);
AudioInfo.MiIntrReg^ := AudioInfo.MiIntrReg^ or MiIntrAi;
AudioInfo.CheckInterrupts;
AiUpdate(False);
end;
end;
function AiReadLength: Cardinal; cdecl;
begin
Result := SndLen;
end;
procedure CloseDLL; cdecl;
begin
if DSoundBuffer <> nil then
begin
DSoundBuffer.Stop;
end;
//release the interfaces
DSoundBuffer := nil;
DSound := nil;
end;
procedure DllAbout(Parent: THandle); cdecl;
begin
Msg('Dolphin Audio v0.1 beta');
end;
procedure GetDllInfo(PluginInfo: pPluginInfo); cdecl;
begin
PluginInfo.Version := $0101;
PluginInfo.PluginType := PlugInTypeAudio;
PluginInfo.Name := 'Dolphin Audio 0.1';
PluginInfo.NormalMemory := True;
PluginInfo.MemoryBSwapped := True;
end;
function InitiateAudio(Info: TAudioInfo): Boolean; cdecl;
var
I: Integer;
begin
AudioInfo := Info;
DirectSoundCreate(nil,DSound,nil);
DSound.SetCooperativeLevel(AudioInfo.HWND,DSSCL_PRIORITY);
for I := 0 to 2 do
Events[I] := CreateEvent(nil,False,False,nil);
Dacrate := 0;
Result := True;
end;
procedure ProcessAList; cdecl;
begin
end;
procedure RomClosed; cdecl;
begin
end;
exports
AiDacrateChanged,
AiLenChanged,
AiReadLength,
AiUpdate,
CloseDLL,
DllAbout,
GetDllInfo,
ProcessAList,
RomClosed,
InitiateAudio;
begin
end.
Last edited: