// MDXWin Constructor

unit _SndDrv;

interface

uses
  Windows,SysUtils,Types,Dialogs,
  _SndEff,_Vis_const,_MXDRVm,_MXDRVg,_FMP,_PMD,_SPC,_WADSPPlugin;

var
  SndEff:TSndEff;

var
  MXDRVLoadFlag,FMPLoadFlag,PMDLoadFlag,SPCLoadFlag:boolean;

var
  WADSPPlugin:TWADSPPlugin;
  WADSPPluginLoaded:boolean;

var
  MXDRVm:TMXDRVm;
  FMP:TFMP;
  PMD:TPMD;
  SPC:TSPC;
  MXDRVg:TMXDRVg;

procedure sdSetDriverMode(_DriverMode:integer);
function sdGetDriverMode:integer;
function sdGetPlay:boolean;
function sdGetStop:boolean;
function sdGetPause:boolean;

function sdGetQuietMode:boolean;
procedure sdSetQuietMode(qm:boolean);

procedure sdToggleMute(ch:integer);
procedure sdSetMute(ch:integer;MuteFlag:boolean);
function sdGetMute(ch:integer):boolean;
procedure sdSetAllMute(MuteFlag:boolean);
procedure sdToggleAllMute;
procedure sdRefreshAllMute;
procedure sdRefreshMute(ch:integer);

procedure sdSndEffCreate;
procedure sdSndEffFree;

procedure sdCreateWADSPPlugin(hwnd:HWND);
procedure sdFreeWADSPPlugin;

procedure sdLoadDLL_MXDRV;
procedure sdLoadDLL_FMP;
procedure sdLoadDLL_PMD;
procedure sdLoadDLL_SPC;
procedure sdReflectionSetting;

procedure sdGetPCM(var buf:SmallInt;len:LongInt;rate:integer);
procedure sdMMLTrap;
procedure sdStartFadeout;
function sdGetMesureClock:integer;
function sdGetTotalClock:integer;
function sdPlay:boolean;
function sdNetworkPlay:boolean;
procedure sdStop;
procedure sdRequestFadeoutStop;
procedure sdTogglePause;
procedure sdSeek(SeekClock:integer);
procedure sdGetVisual(var pSetVisual:pVisual);
procedure sdGetVisualSystem(var pSetVisual:pVisual);
function sdGetDriverInfomation:string;
function sdGetCopyright(DriverMode:integer):string;

function sdChangeRate(rate:integer;dsound:integer;_WAO_DLLName:string):boolean;

procedure sdSetTransTempo(t:integer);
function sdGetTransTempo:integer;
procedure sdSetTotalVolume(v:integer);
function sdGetTotalVolume:integer;

procedure sdMXDRVmSetADPCMSRCMode(m:integer);

procedure sdASIO_OpenSetting;

procedure sdGetChMode(ch:integer;var ChMode:string;var ChTrack:integer);

type
  TPeekLevel=class
  private
    { Private 宣言 }
    MemSync:TMultiReadExclusiveWriteSynchronizer;
    Enabled:boolean;
    VolBoot,VolCurrent,VolLast:word;
  public
    { Public 宣言 }
    constructor Create;
    destructor Destroy; override;
    procedure SetEnabled(e:boolean);
    function GetEnabled:boolean;
    procedure ResetAll;
    procedure ResetCurrent;
    procedure SetVolume(v:word);
    procedure SeekPeekAndSetVolume(var buf:SmallInt;samples:integer);
    function GetVolBoot:word;
    function GetVolCurrent:word;
    function GetVolLast:word;
    function GetSetVolume(v:integer):integer;
  end;

var
  PeekLevel:TPeekLevel;

implementation

uses MainWin,_PCMOut,
     _SndDrv_const,_m_Tools,_const, _MDXWinINI,_Asio_Moonlight,_Vis;

type
  PDWordArray = ^TDWordArray;
  TDWordArray = array[0..65536*10] of dword;
  PSmallIntArray = ^TSmallIntArray;
  TSmallIntArray = array[0..65536*10] of SmallInt;

var
  DriverMode:integer;
  TransTempo:integer;
  TotalVolume:integer;
  QuietMode:boolean;

procedure sdSetDriverMode(_DriverMode:integer);
begin
  DriverMode:=_DriverMode;
end;

function sdGetDriverMode:integer;
begin
  Result:=DriverMode;
end;

function sdGetPlay:boolean;
begin
  Result:=not sdEnd;
end;

function sdGetStop:boolean;
begin
  Result:=sdEnd;
end;

function sdGetPause:boolean;
begin
  Result:=sdPause;
end;

function sdGetQuietMode:boolean;
begin
  PCMOut.MemWStart;
  Result:=QuietMode;
  PCMOut.MemWEnd;
end;

procedure sdSetQuietMode(qm:boolean);
begin
  PCMOut.MemWStart;
  QuietMode:=qm;
  PCMOut.MemWEnd;
end;

procedure sdToggleMute(ch:integer);
begin
  if (ch<0) or (16<=ch) then exit;

  PCMOut.MemWStart;
  sdMuteFlag[ch]:=not sdMuteFlag[ch];
  PCMOut.MemWEnd;
  sdRefreshMute(ch);
end;

procedure sdSetMute(ch:integer;MuteFlag:boolean);
begin
  if (ch<0) or (16<=ch) then exit;

  PCMOut.MemWStart;
  sdMuteFlag[ch]:=MuteFlag;
  PCMOut.MemWEnd;
  sdRefreshMute(ch);
end;

function sdGetMute(ch:integer):boolean;
begin
  if (ch<0) or (16<=ch) then begin
    Result:=False;
    exit;
  end;

  PCMOut.MemWStart;
  Result:=sdMuteFlag[ch];
  PCMOut.MemWEnd;
end;

procedure sdSetAllMute(MuteFlag:boolean);
var
  ch:integer;
begin
  PCMOut.MemWStart;
  for ch:=0 to 15 do begin
    sdMuteFlag[ch]:=MuteFlag;
  end;
  PCMOut.MemWEnd;
  sdRefreshAllMute;
end;

procedure sdToggleAllMute;
var
  ch:integer;
begin
  PCMOut.MemWStart;
  for ch:=0 to 15 do begin
    sdMuteFlag[ch]:=not sdMuteFlag[ch];
  end;
  PCMOut.MemWEnd;

  sdRefreshAllMute;
end;

procedure sdRefreshAllMute;
var
  ch:integer;
begin
  for ch:=0 to 15 do begin
    sdRefreshMute(ch);
  end;
end;

procedure sdRefreshMute(ch:integer);
begin
  if (ch<0) or (16<=ch) then exit;

  PCMOut.MemWStart;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.SetMute(Ch,sdMuteFlag[ch]);
    DriverModeFMP:    if FMPLoadFlag=True then FMP.SetMute(Ch,sdMuteFlag[ch]);
    DriverModePMD:    if PMDLoadFlag=True then PMD.SetMute(Ch,sdMuteFlag[ch]);
    DriverModeSPC:    if SPCLoadFlag=True then SPC.SetMute(Ch,sdMuteFlag[ch]);
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.SetMute(Ch,sdMuteFlag[ch]);
    else begin end;
  end;
  PCMOut.MemWEnd;
end;

procedure sdSndEffCreate;
begin
  SndEff:=TSndEff.Create;
  SndEff.Init;
end;

procedure sdSndEffFree;
begin
  SndEff.FreeMemory;
  SndEff.Free;
  SndEff:=nil;
end;

procedure sdCreateWADSPPlugin(hwnd:HWND);
begin
  PCMOut.MemWStart;

  if WADSPPlugin<>nil then sdFreeWADSPPlugin;

  WADSPPlugin:=TWADSPPlugin.Create;

  if MainINI.WADSPRegInit=True then begin
    MainINI.WADSPRegInit:=False;
    WADSPPlugin.InitRegistry(True);
  end;

  if WADSPPlugin.Init(hwnd)=True then begin
    WADSPPluginLoaded:=True;
    end else begin
    WADSPPluginLoaded:=False;
    WADSPPlugin.Free;
    WADSPPlugin:=nil;
  end;

  PCMOut.MemWEnd;
end;

procedure sdFreeWADSPPlugin;
begin
  PCMOut.MemWStart;
  if WADSPPlugin<>nil then begin
    WADSPPluginLoaded:=False;
    WADSPPlugin.Close;
    WADSPPlugin.Free;
    WADSPPlugin:=nil;
  end;
  PCMOut.MemWEnd;
end;

procedure sdLoadDLL_MXDRV;
begin
  if MXDRVLoadFlag=True then exit;

  if MXDRVgINI.Enabled=False then begin
    MXDRVm:=TMXDRVm.Create;
    if MXDRVm.Init(PCMRate_MXDRV,MainINI.UseRomeo,Main.NowDSoundSafeTime)=False then begin
      MXDRVm.FreeMemory;
      MXDRVm.Free;
      MXDRVm:=nil;
      ShowMessage('MXDRVm.dllが見つからないか初期化できませんでした。');
      exit;
    end;
    end else begin
    MXDRVg:=TMXDRVg.Create;
    if MXDRVg.Init(PCMRate_MXDRV)=False then begin
      MXDRVg.FreeMemory;
      MXDRVg.Free;
      MXDRVg:=nil;
      ShowMessage('mxdrv.dllが見つからないか初期化できませんでした。');
      exit;
    end;
  end;

  MXDRVLoadFlag:=True;
end;

procedure sdLoadDLL_FMP;
begin
  if FMPLoadFlag=True then exit;

  FMP:=TFMP.Create;
  if FMP.Init(PCMRate_FMPPMD)=False then begin
    FMP.FreeDLL;
    FMP.Free;
    FMP:=nil;
    exit;
  end;
  FMPLoadFlag:=True;
end;

procedure sdLoadDLL_PMD;
begin
  if PMDLoadFlag=True then exit;

  PMD:=TPMD.Create;
  if PMD.Init(PCMRate_FMPPMD)=False then begin
    PMD.FreeDLL;
    PMD.Free;
    PMD:=nil;
    exit;
  end;
  PMDLoadFlag:=True;
end;

procedure sdLoadDLL_SPC;
begin
  if SPCLoadFlag=True then exit;

  SPC:=TSPC.Create;
  if SPC.Init(PCMRate_SPC)=False then begin
    SPC.FreeDLL;
    SPC.Free;
    SPC:=nil;
  end;
  SPCLoadFlag:=True;
end;

procedure sdReflectionSetting;
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.RefrectionSetting;
    DriverModeFMP:    if FMPLoadFlag=True then FMP.RefrectionSetting(False);
    DriverModePMD:    if PMDLoadFlag=True then PMD.RefrectionSetting(False);
    DriverModeSPC:    if SPCLoadFlag=True then SPC.RefrectionSetting;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.RefrectionSetting;
    else begin
    end;
  end;
  PCMOut.MemWEnd;
end;

{
procedure sdGetPCM(var buf:SmallInt;len:LongInt;rate:integer);
begin
  case DriverMode of
    DriverModeMXDRVm: MXDRVm.GetPCM(addr(buf),len);
    DriverModeFMP:    FMP.GetPCMData(buf,len);
    DriverModePMD:    PMD.PMD_GetPCMData(buf,len);
    DriverModeSPC:    SPC.SPC_GetPCMData(buf,len);
    DriverModeMXDRVg: MXDRVg.GetPCM(addr(buf),len);
  end;
end;
}

var
  last0,last1:SmallInt;
  sbuf:array of smallint;
  fsbuf:array of single;

procedure sdGetPCM(var buf:SmallInt;len:LongInt;rate:integer);
var
  sfreq,dfreq:integer;
  freqper:single;
  slen,dlen:integer;
  dbuf:PSmallIntArray;
  procedure NearestSRC;
  var
    dcnt:integer;
    pdwsrc,pdwdst:PDWordArray;
  begin
    pdwsrc:=addr(sbuf[0]);
    pdwdst:=addr(buf);
    for dcnt:=0 to dlen-1 do begin
      pdwdst[dcnt]:=pdwsrc[trunc(dcnt*freqper)+1];
    end;
  end;
  procedure LinerSRC;
  var
    dcnt:integer;
    scnt:single;
    cpsrc:integer;
    cpmod:single;
  begin
    setlength(fsbuf,(slen+3)*2+32);
    for dcnt:=0 to (slen+3)*2-1 do begin
      fsbuf[dcnt]:=sbuf[dcnt];
    end;
    for dcnt:=0 to 32-1 do begin
      fsbuf[(slen+3)*2+dcnt]:=fsbuf[(slen+3)*2-1];
    end;
    dbuf:=addr(buf);
    scnt:=0;

    for dcnt:=0 to (dlen div 1)-1 do begin
      cpsrc:=trunc(scnt)*2;
      cpmod:=frac(scnt);
      dbuf[dcnt*2+0]:=trunc((fsbuf[cpsrc+2+0]*(1-cpmod))+(fsbuf[cpsrc+2+2]*cpmod));
      dbuf[dcnt*2+1]:=trunc((fsbuf[cpsrc+2+1]*(1-cpmod))+(fsbuf[cpsrc+2+3]*cpmod));
      scnt:=scnt+freqper;
    end;
  end;
  procedure SplineSRC;
  var
    dcnt:integer;
    scnt:single;
    cpsrc:integer;
    cpmod:single;
    ir:Single;      //1-rate
    r2,r3:Single;   //rate^2　, rate^3
    dest:single;
  begin
    setlength(fsbuf,(slen+3)*2+32);
    for dcnt:=0 to (slen+3)*2-1 do begin
      fsbuf[dcnt]:=sbuf[dcnt];
    end;
    for dcnt:=0 to 32-1 do begin
      fsbuf[(slen+3)*2+dcnt]:=fsbuf[(slen+3)*2-1];
    end;
    dbuf:=addr(buf);
    scnt:=0;

    for dcnt:=0 to dlen-1 do begin
      cpsrc:=trunc(scnt)*2;
      cpmod:=frac(scnt);

      ir:=1-cpmod;
      r2:=cpmod*cpmod;
      r3:=r2*cpmod;

      dest:=fsbuf[cpsrc+0+0]*ir*ir*ir;
      dest:=dest+(fsbuf[cpsrc+2+0]*(3*r3-6*r2+4));
      dest:=dest+(fsbuf[cpsrc+4+0]*(-3*(r3-r2-cpmod)+1));
      dest:=dest+(fsbuf[cpsrc+6+0]*r3);
      dbuf[dcnt*2+0]:=trunc(dest/6.0);
      dest:=fsbuf[cpsrc+0+1]*ir*ir*ir;
      dest:=dest+(fsbuf[cpsrc+2+1]*(3*r3-6*r2+4));
      dest:=dest+(fsbuf[cpsrc+4+1]*(-3*(r3-r2-cpmod)+1));
      dest:=dest+(fsbuf[cpsrc+6+1]*r3);
      dbuf[dcnt*2+1]:=trunc(dest/6.0);

      scnt:=scnt+freqper;
    end;
  end;
begin
  if len=0 then exit;

  if rate=0 then begin
    ZeroMemory(addr(buf),len*4);
    exit;
  end;

  case DriverMode of
    DriverModeUnknown: sfreq:=0;
    DriverModeMXDRVm: sfreq:=PCMRate_MXDRV;
    DriverModeFMP:    sfreq:=PCMRate_FMPPMD;
    DriverModePMD:    sfreq:=PCMRate_FMPPMD;
    DriverModeSPC:    sfreq:=PCMRate_SPC;
    DriverModeMXDRVg: sfreq:=PCMRate_MXDRV;
    else sfreq:=0;
  end;

  if sfreq=0 then begin
    ZeroMemory(addr(buf),len*4);
    exit;
  end;

  dfreq:=rate;

  if sfreq=dfreq then begin
    case DriverMode of
      DriverModeMXDRVm: MXDRVm.GetPCM(addr(buf),len);
      DriverModeFMP:    FMP.GetPCMData(buf,len);
      DriverModePMD:    PMD.PMD_GetPCMData(buf,len);
      DriverModeSPC:    SPC.SPC_GetPCMData(buf,len);
      DriverModeMXDRVg: MXDRVg.GetPCM(addr(buf),len);
    end;
    exit;
  end;

  freqper:=sfreq/dfreq;
  slen:=trunc(len*freqper);
  dlen:=len;

  setlength(sbuf,(slen+3)*2);
  case DriverMode of
    DriverModeMXDRVm: MXDRVm.GetPCM(addr(sbuf[2]),slen);
    DriverModeFMP:    FMP.GetPCMData(sbuf[2],slen);
    DriverModePMD:    PMD.PMD_GetPCMData(sbuf[2],slen);
    DriverModeSPC:    SPC.SPC_GetPCMData(sbuf[2],slen);
    DriverModeMXDRVg: MXDRVg.GetPCM(addr(sbuf[2]),slen);
  end;

  sbuf[0]:=last0;
  sbuf[1]:=last1;
  last0:=sbuf[(slen+0)*2+0];
  last1:=sbuf[(slen+0)*2+1];
  sbuf[(slen+1)*2+0]:=last0;
  sbuf[(slen+1)*2+1]:=last1;
  sbuf[(slen+2)*2+0]:=last0;
  sbuf[(slen+2)*2+1]:=last1;

  case MainINI.UseSRCMode of
    SRCMode_Nearest: NearestSRC;
    SRCMode_Liner: LinerSRC;
    SRCMode_Spline: SplineSRC;
  end;

end;

procedure sdMMLTrap;
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.MMLTrap;
    DriverModeFMP:    if FMPLoadFlag=True then FMP.MMLTrap;
    DriverModePMD:    if PMDLoadFlag=True then PMD.MMLTrap;
    DriverModeSPC:    if SPCLoadFlag=True then SPC.MMLTrap;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.MMLTrap;
    else begin
    end;
  end;
  PCMOut.MemWEnd;
end;

procedure sdStartFadeout;
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.StartFadeout;
    DriverModeFMP:    if FMPLoadFlag=True then FMP.StartFadeout;
    DriverModePMD:    if PMDLoadFlag=True then PMD.StartFadeout;
    DriverModeSPC:    if SPCLoadFlag=True then SPC.StartFadeout;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.StartFadeout;
    else begin
    end;
  end;
  PCMOut.MemWEnd;
end;

function sdGetMesureClock:integer;
begin
  Result:=1;

  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then Result:=MXDRVm.GetMesureClock;
    DriverModeFMP:    if FMPLoadFlag=True then Result:=FMP.GetMesureClock;
    DriverModePMD:    if PMDLoadFlag=True then Result:=PMD.GetMesureClock;
    DriverModeSPC:    if SPCLoadFlag=True then Result:=SPC.GetMesureClock;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then Result:=MXDRVg.GetMesureClock;
    else begin
    end;
  end;
end;

function sdGetTotalClock:integer;
begin
  Result:=1;

  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then Result:=MXDRVm.GetTotalClock;
    DriverModeFMP:    if FMPLoadFlag=True then Result:=FMP.GetTotalClock;
    DriverModePMD:    if PMDLoadFlag=True then Result:=PMD.GetTotalClock;
    DriverModeSPC:    if SPCLoadFlag=True then Result:=SPC.GetTotalClock;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then Result:=MXDRVg.GetTotalClock;
    else begin
    end;
  end;
end;

function sdPlay:boolean;
begin
  if PCMOut.GetCurrentPCMOut=PCMOut_Null then sdChangeRate(0,PCMOut_already,'');

  PeekLevel.ResetCurrent;
  sdReflectionSetting;
  sdSetTransTempo(TransTempo);

  sdLoop:=0;
  sdEnd:=False;
  sdFadeout:=False;
  sdPause:=False;
  sdReqFadeStop:=False;

  case DriverMode of
    DriverModeUnknown: Result:=False;
    DriverModeMXDRVm:  Result:=MXDRVm.MDXPlay;
    DriverModeFMP:     Result:=FMP.FMPPlay;
    DriverModePMD:     Result:=PMD.PMDPlay;
    DriverModeSPC:     Result:=SPC.SPCPlay;
    DriverModeMXDRVg:  Result:=MXDRVg.MDXPlay;
    else begin
      Result:=False;
    end;
  end;

  sdRefreshAllMute;

  sdLoop:=0;
  sdEnd:=False;
  sdFadeout:=False;
  sdPause:=False;
  sdReqFadeStop:=False;
end;

function sdNetworkPlay:boolean;
begin
  if PCMOut.GetCurrentPCMOut=PCMOut_Null then sdChangeRate(0,PCMOut_already,'');

  PeekLevel.ResetCurrent;
  sdReflectionSetting;
  sdSetTransTempo(TransTempo);

  sdLoop:=0;
  sdEnd:=False;
  sdFadeout:=False;
  sdPause:=False;
  sdReqFadeStop:=False;

  case DriverMode of
    DriverModeUnknown: Result:=False;
    DriverModeMXDRVm:  Result:=Main.Network.MXDRVPlay(DriverModeMXDRVm);
    DriverModeFMP:     Result:=Main.Network.FMPPlay;
    DriverModePMD:     Result:=Main.Network.PMDPlay;
    DriverModeSPC:     Result:=Main.Network.SPCPlay;
    DriverModeMXDRVg:  Result:=Main.Network.MXDRVPlay(DriverModeMXDRVg);
    else begin
      Result:=False;
    end;
  end;

  sdRefreshAllMute;

  sdLoop:=0;
  sdEnd:=False;
  sdFadeout:=False;
  sdPause:=False;
  sdReqFadeStop:=False;
end;

procedure sdStop;
begin
  PCMOut.MemWStart;
  if MXDRVLoadFlag=True then begin
    if (MXDRVm<>nil) and (MainINI.UseRomeo=True) then MXDRVm.SetTimerBEnabled(False);
  end;
  sdLoop:=0;
  sdEnd:=True;
  sdFadeout:=False;
  sdPause:=False;
  sdReqFadeStop:=False;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.MDXStop;
    DriverModeFMP:    if FMPLoadFlag=True then FMP.FMPStop;
    DriverModePMD:    if PMDLoadFlag=True then PMD.PMDStop;
    DriverModeSPC:    if SPCLoadFlag=True then SPC.SPCStop;
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.MDXStop;
    else begin
    end;
  end;
  PCMOut.MemWEnd;

  Main.PlayButtonsCallback;
end;

procedure sdRequestFadeoutStop;
begin
  PCMOut.MemWStart;
  if sdEnd=False then begin
    sdReqFadeStop:=True;
    sdStartFadeout;
  end;
  PCMOut.MemWEnd;
end;

procedure sdTogglePause;
begin
  PCMOut.MemWStart;
  if sdEnd=False then begin
    sdPause:=not sdPause;
    if MXDRVLoadFlag=True then begin
      if (MXDRVm<>nil) and (MainINI.UseRomeo=True) then MXDRVm.SetTimerBEnabled(not sdPause);
    end;
    end else begin
    sdPause:=False;
    if MXDRVLoadFlag=True then begin
      if (MXDRVm<>nil) and (MainINI.UseRomeo=True) then MXDRVm.SetTimerBEnabled(False);
    end;
  end;
  PCMOut.MemWEnd;

  Main.PlayButtonsCallback;
end;

procedure sdSeek(SeekClock:integer);
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeUnknown: begin end;
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.MMLSeek(SeekClock);
    DriverModeFMP:    if FMPLoadFlag=True then FMP.MMLSeek(SeekClock);
    DriverModePMD:    if PMDLoadFlag=True then PMD.MMLSeek(SeekClock);
    DriverModeSPC:    if SPCLoadFlag=True then SPC.MMLSeek(SeekClock);
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.MMLSeek(SeekClock);
    else begin
    end;
  end;
  PCMOut.MemWEnd;
  sdRefreshAllMute;
end;

procedure sdGetVisual(var pSetVisual:pVisual);
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.GetVisual(PSetVisual);
    DriverModeFMP:    if FMPLoadFlag=True then FMP.GetVisual(PSetVisual);
    DriverModePMD:    if PMDLoadFlag=True then PMD.GetVisual(PSetVisual);
    DriverModeSPC:    if SPCLoadFlag=True then SPC.GetVisual(PSetVisual);
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.GetVisual(PSetVisual);
  end;
  PCMOut.MemWEnd;
end;

procedure sdGetVisualSystem(var pSetVisual:pVisual);
begin
  PCMOut.MemWStart;
  case DriverMode of
    DriverModeMXDRVm: if MXDRVLoadFlag=True then MXDRVm.GetVisualSystem(PSetVisual);
    DriverModeFMP:    if FMPLoadFlag=True then FMP.GetVisualSystem(PSetVisual);
    DriverModePMD:    if PMDLoadFlag=True then PMD.GetVisualSystem(PSetVisual);
    DriverModeSPC:    if SPCLoadFlag=True then SPC.GetVisualSystem(PSetVisual);
    DriverModeMXDRVg: if MXDRVLoadFlag=True then MXDRVg.GetVisualSystem(PSetVisual);
  end;
  PCMOut.MemWEnd;
end;

function sdGetDriverInfomation:string;
var
  mes:string;
  cnt:integer;
  procedure al(msg:string);
  begin
    Result:=Result+msg+CRLF;
  end;
begin
  PCMOut.MemWStart;

  Result:='';

  case DriverMode of
    DriverModeUnknown: begin
      al('');
      al('DriverMode:UnknownDriver [.dll]');
      al('');
    end;
    DriverModeMXDRVm: begin
      with MXDRVm.Infomation do begin
        al('');
        al('DriverMode:MXDRVm [MXDRVm.dll]');
        al('');
        al('Filename='+ExtractFilename(Main.gFileInfo.GetFullPath)+' ('+Main.gFileInfo.GetFullPath+')');
        al(format('FileSize=%dbyte.(%dkbyte)',[FileSize,FileSize div 1024]));
        mes:='PDXFilename=';
        if PDXFilename='' then begin
          mes:=mes+'no use.';
          end else begin
          mes:=mes+ExtractFilename(ChangeFileExt(PDXFilename,'.pdx'))+' ';
          if PDXFileLoadFlag then
            mes:=mes+'loaded.'
            else
            mes:=mes+'not found.';
          mes:=mes+' ('+ChangeFileExt(PDXFilename,'.pdx')+')';
        end;
        al(mes);
        al(format('PDXFileSize=%dbyte.(%dkbyte)',[PDXFileSize,PDXFileSize div 1024]));
        al('');
        al('Title...');
        al(Main.gFileInfo.GetFullTitle);
      end;
    end;
    DriverModeFMP: begin
      with FMP.Infomation do begin
        al('');
        al('DriverMode:FMP [WinFMP.DLL]');
        al('');
        al('Filename='+thExtractFilename(Main.gFileInfo.GetFullPath)+' ('+Main.gFileInfo.GetFullPath+')');
        al(format('FileSize=%dbyte.(%dkbyte)',[FileSize,FileSize div 1024]));
        al('');
        for cnt:=0 to 2 do begin
          mes:=PCMMode[cnt]+'_Filename=';
          if PCMFilename[cnt]='' then begin
            mes:=mes+'no use.';
            end else begin
            mes:=mes+PCMFilename[cnt]+' ';
            if PCMLoadFlag[cnt]=True then
              mes:=mes+'loaded.'
              else
              mes:=mes+'not found.';
          end;
          al(mes);
        end;
        al('');
        al('Comments...');
        for cnt:=0 to 2 do
          al(Comment[cnt]);
      end;
    end;
    DriverModePMD: begin
      with PMD.Infomation do begin
        al('');
        al('DriverMode:PMD [PMDWin.DLL]');
        al('');
        al('Filename='+thExtractFilename(Main.gFileInfo.GetFullPath)+' ('+Main.gFileInfo.GetFullPath+')');
        al(format('FileSize=%dbyte.(%dkbyte)',[FileSize,FileSize div 1024]));
        al('');
        for cnt:=0 to 2 do begin
          mes:=PCMMode[cnt]+'_Filename=';
          if PCMFilename[cnt]='' then begin
            mes:=mes+'no use.';
            end else begin
            mes:=mes+PCMFilename[cnt];
          end;
          al(mes);
        end;
        al('');
        for cnt:=0 to 5 do
          al(Comment[cnt]);
      end;
    end;
    DriverModeSPC: begin
      with SPC.Infomation do begin
        al('');
        al('DriverMode:SPC [SNESAPU.DLL]');
        al('');
        al('Filename='+thExtractFilename(Main.gFileInfo.GetFullPath)+' ('+Main.gFileInfo.GetFullPath+')');
        al(format('FileSize=%dbyte.(%dkbyte)',[FileSize,FileSize div 1024]));
        al('');
        with SPC.SPCInfo do begin
          if HeaderID='' then begin
            al('HeaderID  :InvalidHeader');
            end else begin
            al('HeaderID  :'+HeaderID);
            with ID666 do begin
              if ID666Exists=False then begin
                al('not exists ID666Tag');
                end else begin
                al('SongTitle :'+SongTitle);
                al('GameTitle :'+GameTitle);
                al('DumperName:'+DumperName);
                al('Comments  :'+Comments);
                al('ArtistName:'+ArtistName);
              end;
              al('');
              al('SongLength   :'+IntToStr(SongLenSec)+'sec.');
              al('FadeoutLength:'+IntToStr(FadeoutLenMSec)+'msec.');
            end;
          end;
        end;
      end;
    end;
    DriverModeMXDRVg: begin
      with MXDRVg.Infomation do begin
        al('');
        al('DriverMode:MXDRVg [mxdrv.dll]');
        al('');
        al('Filename='+ExtractFilename(Main.gFileInfo.GetFullPath)+' ('+Main.gFileInfo.GetFullPath+')');
        al(format('FileSize=%dbyte.(%dkbyte)',[FileSize,FileSize div 1024]));
        mes:='PDXFilename=';
        if PDXFilename='' then begin
          mes:=mes+'no use.';
          end else begin
          mes:=mes+ExtractFilename(ChangeFileExt(PDXFilename,'.pdx'))+' ';
          if PDXFileLoadFlag then
            mes:=mes+'loaded.'
            else
            mes:=mes+'not found.';
          mes:=mes+' ('+ChangeFileExt(PDXFilename,'.pdx')+')';
        end;
        al(mes);
        al(format('PDXFileSize=%dbyte.(%dkbyte)',[PDXFileSize,PDXFileSize div 1024]));
        al('');
        al('Title...');
        al(Main.gFileInfo.GetCompactTitle);
      end;
    end;
    else begin
      al('');
      al('DriverMode:UnknownDriver [.dll]');
      al('');
    end;
  end;
  PCMOut.MemWEnd;
end;

function sdGetCopyright(DriverMode:integer):string;
begin
  PCMOut.MemWStart;

  Result:='no DriverDLL';

  case DriverMode of
    DriverModeUnknown: begin
      Result:='Unknown'+CRLF;
    end;
    DriverModeMXDRVm: if MXDRVm<>nil then Result:=MXDRVm.GetCopyright;
    DriverModeFMP:    if FMP<>nil then Result:=FMP.GetCopyright;
    DriverModePMD:    if PMD<>nil then Result:=PMD.GetCopyright;
    DriverModeSPC:    if SPC<>nil then Result:=SPC.GetCopyright;
    DriverModeMXDRVg: if MXDRVg<>nil then Result:=MXDRVg.GetCopyright;
    else begin
      Result:='Unknown'+CRLF;
    end;
  end;
  PCMOut.MemWEnd;
end;


function sdChangeRate(rate:integer;dsound:integer;_WAO_DLLName:string):boolean;
begin
{$ifdef mdxwin}
  if dsound<>PCMOut_already then begin
    MainINI.UseDSound:=dsound;
    Main.NowUseDSound:=dsound;
  end;
  if _WAO_DLLName<>'' then begin
    MainINI.WAO_DLLName:=_WAO_DLLName;
    Main.NowWAO_DLLName:=_WAO_DLLName;
  end;
  if rate<>0 then begin
    MainINI.DSoundRate:=rate;
    Main.NowDSoundRate:=rate;
  end;

  if Main.NowUseDSound=PCMOut_ASIO then begin
    if OutAsio=nil then OutAsio:=TOutAsio.Create;
    if ASIO_GetOpenAsioLoaded=True then begin
      OutAsio.GetDriverInfo(OutAsioINI.DriverIndex);
      rate:=Main.NowDSoundRate;
      if OutAsio.EnableSampleRate(rate)=False then begin
        rate:=OutAsio.FindSampleRate;
        if rate=0 then begin
          ShowMessage('OutAsioError:出力周波数'+inttostr(Main.NowDSoundRate)+'Hzは利用不可でしたが、利用可能周波数も見つかりませんでした。');
          end else begin
          ShowMessage('OutAsioInfo:出力周波数'+inttostr(Main.NowDSoundRate)+'Hzは利用不可だったので、'+inttostr(rate)+'Hzに再設定しました。');
          MainINI.DSoundRate:=rate;
          Main.NowDSoundRate:=rate;
          Main.PCMOutMenuCallback;
        end;
      end;
    end;
  end;

  if PCMOut.Init(Main.NowUseDSound,Main.NowAnalyzeFrame,Main.NowAnalyzeFFT,Main.NowDSoundSafeTime,Main.NowDSoundRate,Main.NowWAO_DLLName)=False then begin
    ShowMessage('音声デバイスの初期化に失敗しました。'+#13+#10+'一時的に無音ドライバに切り替えました。');
    MainINI.UseDSound:=PCMOut_Null;
    Main.NowUseDSound:=PCMOut_Null;
    PCMOut.Init(Main.NowUseDSound,Main.NowAnalyzeFrame,Main.NowAnalyzeFFT,Main.NowDSoundSafeTime,Main.NowDSoundRate,Main.NowWAO_DLLName);
    Result:=False;
    end else begin
    PCMOut.StartDSound;
    Result:=True;
  end;
{$else}
  Result:=True;
{$endif}
end;

procedure sdSetTransTempo(t:integer);
begin
{$ifdef mdxwin}
  PCMOut.MemWStart;

  if t<=-128 then TransTempo:=-128;
  if 128<=t then TransTempo:=128;

  TransTempo:=t;
  if (MXDRVLoadFlag=True) and (MXDRVm<>nil) then MXDRVm.SetTransTempo(TransTempo);
  if (MXDRVLoadFlag=True) and (MXDRVg<>nil) then MXDRVg.SetTransTempo(TransTempo);
  if FMPLoadFlag=True then FMP.SetTransTempo(TransTempo);
  if PMDLoadFlag=True then PMD.SetTransTempo(TransTempo);
  if SPCLoadFlag=True then SPC.SetTransTempo(TransTempo);

  PCMOut.MemWEnd;
{$endif}
end;

function sdGetTransTempo:integer;
begin
  Result:=TransTempo;
end;

procedure sdSetTotalVolume(v:integer);
begin
  PCMOut.MemWStart;

  if v<0 then v:=0;
  if (1024*8)<v then v:=1024*8;
  TotalVolume:=v;

  SndEff.CalcVolumeTable(v);

  PCMOut.MemWEnd;
end;

function sdGetTotalVolume:integer;
begin
  Result:=TotalVolume;
end;

procedure sdMXDRVmSetADPCMSRCMode(m:integer);
begin
  if MXDRVm<>nil then MXDRVm.SetADPCMSRCMode(m);
end;

procedure sdASIO_OpenSetting;
begin
{$ifdef mdxwin}
  PCMOut.ASIO_OpenSetting;
{$endif}
end;

procedure sdGetChMode(ch:integer;var ChMode:string;var ChTrack:integer);
begin
  ChMode:='';
  ChTrack:=0;

  case sdGetDriverMode of
    DriverModeUnknown: begin
      ChMode:='nil';
      ChTrack:=ch;
    end;
    DriverModeMXDRVm: begin
      if ch<8 then begin
        ChMode:='OPM';
        ChTrack:=ch;
        end else begin
        ChMode:='ADPCM';
        ChTrack:=ch-8;
      end;
    end;
    DriverModeFMP: begin
      if (0<=ch) and (ch<=2) then begin
        ChMode:='FM';
        ChTrack:=ch;
      end;
      if (3<=ch) and (ch<=5) then begin
        ChMode:='SSG';
        ChTrack:=ch-3;
      end;
      if ch=6 then begin
        ChMode:='PPZ';
        ChTrack:=0;
      end;
      if ch=7 then begin
        ChMode:='PCM';
        ChTrack:=0;
      end;
      if (8<=ch) and (ch<=10) then begin
        ChMode:='FM';
        ChTrack:=ch-8+3;
      end;
      if (11<=ch) and (ch<=15) then begin
        ChMode:='PPZ';
        ChTrack:=ch-11+1;
      end;
    end;
    DriverModePMD: begin
      if (0<=ch) and (ch<=2) then begin
        ChMode:='FM';
        ChTrack:=ch;
      end;
      if (3<=ch) and (ch<=5) then begin
        ChMode:='SSG';
        ChTrack:=ch-3;
      end;
      if ch=6 then begin
        ChMode:='PCM';
        ChTrack:=0;
      end;
      if ch=7 then begin
        ChMode:='RHY';
        ChTrack:=0;
      end;
      if (8<=ch) and (ch<=10) then begin
        ChMode:='FM';
        ChTrack:=ch-8+3;
      end;
      if (11<=ch) and (ch<=15) then begin
        ChMode:='PPZ';
        ChTrack:=ch-11;
      end;
    end;
    DriverModeSPC: begin
      if (0<=ch) and (ch<=7) then begin
        ChMode:='PCM';
        ChTrack:=ch;
      end;
      if (8<=ch) and (ch<=15) then begin
        ChMode:='nil';
        ChTrack:=ch-8;
      end;
    end;
    DriverModeMXDRVg: begin
      if ch<8 then begin
        ChMode:='OPM';
        ChTrack:=ch;
        end else begin
        ChMode:='ADPCM';
        ChTrack:=ch-8;
      end;
    end;
    else begin
      ChMode:='nil';
      ChTrack:=ch;
    end;
  end;

  inc(ChTrack);
end;

constructor TPeekLevel.Create;
begin
  inherited Create;

  MemSync:=TMultiReadExclusiveWriteSynchronizer.Create;

  Enabled:=False;
  ResetAll;
end;

destructor TPeekLevel.Destroy;
begin
  MemSync.Free;

  inherited Destroy;
end;

procedure TPeekLevel.SetEnabled(e:boolean);
begin
  MemSync.BeginWrite;
  Enabled:=e;
  MemSync.EndWrite;
end;

function TPeekLevel.GetEnabled:boolean;
begin
  MemSync.BeginRead;
  Result:=Enabled;
  MemSync.EndRead;
end;

procedure TPeekLevel.ResetAll;
begin
  MemSync.BeginWrite;
  VolBoot:=0;
  VolCurrent:=0;
  VolLast:=0;
  MemSync.EndWrite;
end;

procedure TPeekLevel.ResetCurrent;
begin
  MemSync.BeginWrite;
  VolCurrent:=0;
  MemSync.EndWrite;
end;

procedure TPeekLevel.SetVolume(v:word);
begin
  MemSync.BeginWrite;
  if VolBoot<v then VolBoot:=v;
  if VolCurrent<v then VolCurrent:=v;
  VolLast:=v;
  MemSync.EndWrite;
end;

procedure TPeekLevel.SeekPeekAndSetVolume(var buf:SmallInt;samples:integer);
var
  pbuf:PSmallInt;
  cnt:integer;
  peek:word;
begin
  if GetEnabled=False then exit;

  pbuf:=@buf;
  peek:=0;
  for cnt:=0 to (samples*2)-1 do begin
    if peek<abs(pbuf^) then peek:=abs(pbuf^);
    inc(pbuf);
  end;

  SetVolume(peek);
end;

function TPeekLevel.GetVolBoot:word;
begin
  MemSync.BeginRead;
  Result:=VolBoot;
  MemSync.EndRead;
end;

function TPeekLevel.GetVolCurrent:word;
begin
  MemSync.BeginRead;
  Result:=VolCurrent;
  MemSync.EndRead;
end;

function TPeekLevel.GetVolLast:word;
begin
  MemSync.BeginRead;
  Result:=VolLast;
  MemSync.EndRead;
end;

function TPeekLevel.GetSetVolume(v:integer):integer;
var
  pv:single;
begin
  pv:=v/32768;
  if pv<=0.02 then pv:=1;
  Result:=trunc(1024/pv);
end;

initialization
  SndEff:=nil;
  MXDRVLoadFlag:=False;
  FMPLoadFlag:=False;
  PMDLoadFlag:=False;
  SPCLoadFlag:=False;
  WADSPPlugin:=nil;
  WADSPPluginLoaded:=False;
  MXDRVm:=nil;
  FMP:=nil;
  PMD:=nil;
  SPC:=nil;
  MXDRVg:=nil;
  TransTempo:=0;
  TotalVolume:=1024;
  PeekLevel:=nil;
  QuietMode:=False;

finalization
  if PeekLevel<>nil then PeekLevel.Free;

end.





