What's new

My sound plugin

BlueYoshi

New member
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:
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:

ScottJC

At your service, dood!
Cool, a delphi audio plugin, need the DirectSound unit to actully compile it though...
 

MooCowSheep

New member
BlueYoshi said:
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.

Please do post the DLL file! I have been looking for a really fast audio plugin for my slow-ish system, and I think that this might do the trick!
 
OP
B

BlueYoshi

New member
Thanks for comments. I'm sorry I have to disappoint you, but I don't think my plugin is any faster than others on slow systems. I think it's pretty similiar to Jabo's DirectSound plugin.
My plugin does not perform any high level emulation like emulating N64's wavetable synthesizer, mixing the channels or sound effect processing. I think you should try Azimer's Audio 0.4 which is way much faster than low level plugins like mine cause it does all this stuff on x86 side using HLE.

Sayargh, yes you need DirectSound unit to compile it, which you can get from http://www.crazyentertainment.net/dx.php?ver=eight
 

ScottJC

At your service, dood!
Taken a look at it for a while now, it compiles and it works somewhat, can get banjo-kazooie to work, I'm coding blind at the moment though, need to find some technical docs on how sound plugins work :p
 

Top