unit _PCMOut;

interface

uses
  Windows, Forms,SysUtils,ExtCtrls,Dialogs,MMSystem,
  _LowLVPCM_Moonlight,ddsd,_WAOutputPlugin_Moonlight;

type
  TPCMOut = class
  private
    { Private 宣言 }
    MemSync:TMultiReadExclusiveWriteSynchronizer;
    NullTimer:TTimer;
    DDSD:TDDSD;
    LLPCM:TLowLVPCM;
    WAOUT:TWAOUT;
    WAOUTTimer:TTimer;
    NowAnalyzeFrame:LongInt;
    NowAnalyzeFFT:LongInt;
    NowUseDSound:integer;
    NowDSoundSafeTime:LongInt;
    NowPCMRate:dword;
    NowWAO_DLLName:string;
    WaveMonitor:TDDSDWaveData;
    PCMBufSize:integer;
    PCMBufCount:integer;
    DSPNullSound:boolean;
    EnableDSP:boolean;
    procedure UpdateMonitor(ofs,len:Cardinal);
    procedure UpdateNullMonitor(Sender: TObject);
    procedure UpdateLLPCMMonitor(Sender: TObject);
    procedure UpdateWaveMonitor(Sender:TDDSDGenWave; Player:TDDSDChannel; ofs,len:Cardinal);
    procedure UpdateWAOUTMonitor(Sender: TObject);
    procedure UpdateMonitorForAsio(ofs,len:Cardinal);
  public
    { Public 宣言 }
    PCMBuffer:array[0..256000*2] of SmallInt; // len=size(PCMBuffer) of 16bit stereo 1000ms
    CurrentPCMPos,CurrentPCMSize:integer;
    freqadd:integer;
    constructor Create;
    destructor Destroy; override;
    function  GetCurrentPCMOut:integer;
    function  Init(_NowUseDSound:integer;_NowAnalyzeFrame,_NowAnalyzeFFT,_NowDSoundSafeTime:LongInt;_NowPCMRate:dword;_NowWAO_DLLName:string):boolean;
    procedure StopDriver;
    procedure FreeMemory;
    procedure StartDSound;
    procedure SetEnableDSP(e:boolean);
    procedure MemWStart;
    procedure MemWEnd;
    procedure ASIO_OpenSetting;
    procedure UpdateAsioMonitor(ChannelType:integer;BufferSamplesCount:integer;ch0buf,ch1buf:Pointer);
    procedure WAOUT_ShowConfig(DLL:string);
    procedure WAOUT_ShowAbout(DLL:string);
    function WAOUT_GetModulesCount:integer;
    function WAOUT_GetModuleDLLName(idx:integer):string;
  end;

var
  PCMOut:TPCMOut;

implementation

uses MainWin,KDDWin,AsioSetDlgWin,
     _MDXWin_const, _MDXWinINI,_const,_SndDrv_const,_SndDrv,_Asio_Moonlight,_Vis;

procedure writelog(x:string);
var
  Filename:string;
  T:TextFile;
begin
  exit;
  Filename:=ExtractFileDir(Application.ExeName)+'\_pcmout.log';
  AssignFile(T,Filename);
  if FileExists(Filename)=False then
    Rewrite(T)
    else
    Append(T);
  Writeln(T,inttostr(gettickcount)+x);
  Flush(T);
  CloseFile(T);
end;

constructor TPCMOut.Create;
begin
  inherited Create;

  MemSync:=TMultiReadExclusiveWriteSynchronizer.Create;
  MemSync.BeginWrite;
  MemSync.EndWrite;

  freqadd:=0;

  NullTimer:=TTimer.Create(nil);
  NullTimer.Enabled:=False;
  NullTimer.OnTimer:=UpdateNullMonitor;

  WAOUTTimer:=TTimer.Create(nil);
  WAOUTTimer.Enabled:=False;
  WAOUTTimer.OnTimer:=UpdateWAOUTMonitor;

  LLPCM:=nil;
  DDSD:=nil;
  OutASIO:=nil;

  WAOUT:=TWAOUT.Create;
  WAOUT.initDLLs;

  WaveMonitor:=nil;

  NowAnalyzeFrame:=0;
  NowAnalyzeFFT:=0;
  NowUseDSound:=PCMOut_Null;
  NowDSoundSafeTime:=0;
  NowPCMRate:=0;
  PCMBufSize:=0;
  PCMBufCount:=0;
  DSPNullSound:=True;
  EnableDSP:=True;
end;

destructor TPCMOut.Destroy;
begin
  MemSync.BeginWrite;

  LLPCM:=nil;
  DDSD:=nil;
  if OutASIO<>nil then begin
    OutASIO.Free;
    OutASIO:=nil;
  end;

  WAOUT.freeDLLs;
  WAOUT.Free;
  WAOUT:=nil;

  WaveMonitor:=nil;

  NullTimer.Free;
  NullTimer:=nil;

  WAOutTimer.Free;
  WAOutTimer:=nil;

  MemSync.EndWrite;

  MemSync.Free;
  MemSync:=nil;

  inherited Destroy;
end;

function TPCMOut.GetCurrentPCMOut:integer;
begin
  MemSync.BeginRead;
  Result:=NowUseDSound;
  MemSync.EndRead;
end;

function TPCMOut.Init(_NowUseDSound:integer;_NowAnalyzeFrame,_NowAnalyzeFFT,_NowDSoundSafeTime:LongInt;_NowPCMRate:dword;_NowWAO_DLLName:string):boolean;
var
  change:boolean;
  TempSec:integer;
begin
  Result:=True;

  change:=False;
  if NowAnalyzeFrame<>_NowAnalyzeFrame then change:=True;
  if NowAnalyzeFFT<>_NowAnalyzeFFT then change:=True;
  if NowUseDSound<>_NowUseDSound then change:=True;
  if NowDSoundSafeTime<>_NowDSoundSafeTime then change:=True;
  if NowPCMRate<>_NowPCMRate then change:=True;
  if NowWAO_DLLName<>_NowWAO_DLLName then change:=True;

  if change=True then begin
    StopDriver;

    MemSync.BeginWrite;

    NowAnalyzeFrame:=_NowAnalyzeFrame;
    NowAnalyzeFFT:=_NowAnalyzeFFT;
    NowUseDSound:=_NowUseDSound;
    NowDSoundSafeTime:=_NowDSoundSafeTime;
    NowPCMRate:=_NowPCMRate;
    NowWAO_DLLName:=_NowWAO_DLLName;

    NullTimer.Enabled:=False;
    NullTimer.Interval:=NowDSoundSafeTime;

    TempSec:=NowDSoundSafeTime;
    TempSec:=(TempSec div 100)*100; // 100ms単位に丸め込み
    if TempSec<100 then TempSec:=100;
    if TempSec>1000 then TempSec:=1000;
    PCMBufSize:=(integer(NowPCMRate)*2*2)*TempSec div 1000; // (44khz*16bit*2ch)=1sec/1000ms*(num)ms
    PCMBufSize:=(PCMBufSize div (integer(NowPCMRate) div NowAnalyzeFrame))*(integer(NowPCMRate) div NowAnalyzeFrame); // 秒間フレームに合わせて丸め込み

    ZeroMemory(@PCMBuffer[0],NowPCMRate*2*2);

    PCMBufCount:=4;
    DSPNullSound:=True;
    EnableDSP:=True;

    case NowUseDSound of
      PCMOut_Null: begin end;
      PCMOut_WindowsSound: LLPCM:=TLowLVPCM.Create(Main);
      PCMOut_DirectSound: begin
        DDSD:=TDDSD.Create(Main);
        with DDSD do begin
          ChannelCount:=1;
          StickyFocus:=True;
          Use3D:=False;
          DebugOption:=[dsoHaltOnError];
        end;
        DDSD.forceInitialize;
        if DDSD.Initialized=False then Result:=False;
      end;
      PCMOut_ASIO: begin
        if OutASIO=nil then OutASIO:=TOutASIO.Create;
      end;
      PCMOut_WinAMPOutput: begin
        WAOUTTimer.Enabled:=False;
        WAOUTTimer.Interval:=NowDSoundSafeTime div 4;
        if NowWAO_DLLName='' then begin
          ShowMessage('DLLが選択されていません。');
          Result:=False;
          end else begin
          if WAOUT.SetActiveModule(NowWAO_DLLName)=False then begin
            Result:=False;
            end else begin
            Result:=True;
          end;
        end;
      end;
    end;

    MemSync.EndWrite;
  end;

  if Result=False then NowUseDSound:=PCMOut_Null;
end;

procedure TPCMOut.StartDSound;
var
  res:string;
begin
  NullTimer.Enabled:=False;
  NullTimer.Interval:=NowDSoundSafeTime;

  case NowUseDSound of
    PCMOut_Null: NullTimer.Enabled:=True;
    PCMOut_WindowsSound: begin
      MemSync.BeginWrite;
      LLPCM.BufferSize:=PCMBufSize;
      LLPCM.NumOfBuffers:=PCMBufCount;
      LLPCM.nChannels:=2;
      LLPCM.wBitsPerSample:=16;
      LLPCM.nSamplesPerSec:=NowPCMRate;
      LLPCM.DeviceID:=WAVE_MAPPER;
      LLPCM.UsewaveOutGetPostion:=False;
      LLPCM.OnBufferEmpty:=UpdateLLPCMMonitor;
      MemSync.EndWrite;
      // LLPCMはPlayメソッド内部でUpdateを呼び出し終了を待つので、スレッド保護してはいけない。
      LLPCM.Play;
    end;
    PCMOut_DirectSound: begin
      if assigned(WaveMonitor) then WaveMonitor.Free;

      MemSync.BeginWrite;
      DDSD.SetPrimaryBufferFotmat(NowPCMRate,16,True);
      WaveMonitor:=TDDSDWaveData.CreateStream(DDSD,NowPCMRate,16,True,PCMBufSize*2);
      WaveMonitor.OnUpdate:=UpdateWaveMonitor;
      DDSD[0].WaveData:=WaveMonitor;
      WaveMonitor.BlockCopy(0, @PCMBuffer, PCMBufSize);
      WaveMonitor.BlockCopy(PCMBufSize, @PCMBuffer, PCMBufSize);
      DDSD[0].Stop;
      DDSD[0].LoopPlay;
      MemSync.EndWrite;
    end;
    PCMOut_ASIO: begin
      res:=OutASIO.Play(NowDSoundSafeTime,NowPCMRate,OutASIOINI.DriverIndex,OutASIOINI.LeftCh,OutASIOINI.RightCh,OutASIOINI.LatencyBuffer);
      if res<>'' then begin
        NullTimer.Enabled:=True;
        ShowMessage('ASIOError:'+res);
      end;
    end;
    PCMOut_WinAMPOutput: begin
      WAOUT.Stop;
      WAOUT.Play(NowDSoundSafeTime,NowPCMRate);
      WAOUTTimer.Enabled:=True;
    end;
  end;
end;

procedure TPCMOut.StopDriver;
var
  WaitTick:dword;
begin
  NullTimer.Enabled:=False;
  NullTimer.Interval:=NowDSoundSafeTime;

  if assigned(DDSD) then begin
    DDSD[0].Stop;
    MemSync.BeginWrite;
    WaveMonitor.OnUpdate:=nil;
    while (DDSD[0].Status<>0) do begin
      sleep(16);
    end;
    WaitTick:=timeGetTime;
    while ((WaitTick+dword(NowDSoundSafeTime*2))>timeGetTime) do begin
      sleep(16);
    end;
    DDSD[0].WaveData:=nil;
    WaveMonitor.Free;
    WaveMonitor:=nil;
    DDSD.Free;
    DDSD:=nil;
    MemSync.EndWrite;
  end;

  if assigned(LLPCM) then begin
    LLPCM.Stop;
    MemSync.BeginWrite;
    LLPCM.OnBufferEmpty:=nil;
    while (LLPCM.lhWaveOut<>0) do begin
      sleep(16);
    end;
    WaitTick:=timeGetTime;
    while ((WaitTick+dword(NowDSoundSafeTime*2))>timeGetTime) do begin
      sleep(16);
    end;
    LLPCM.Free;
    LLPCM:=nil;
    MemSync.EndWrite;
  end;

  if assigned(OutASIO) then begin
    if OutASIO.isAssignedDriver=True then OutASIO.Stop;
  end;

  if assigned(WAOUT) then begin
  end;

  WAOUTTimer.Enabled:=False;
  WAOUT.Stop;
end;

procedure TPCMOut.FreeMemory;
begin
  exit;
end;

procedure TPCMOut.UpdateMonitor(ofs,len:Cardinal);
var
  DriverEnabled:boolean;
  CopyCount:Integer;
  GetSamples:integer;
  SamplePos,SampleOne:integer;
  SetTick,SetTickOne:single;
  CalcSystemOnly:boolean;
  cnt:integer;
  maxvol:SmallInt;
  KDDEndFlag:boolean;
  NextTimerEnabled:boolean;
begin
  if len=0 then exit;

  ZeroMemory(addr(PCMBuffer[0]),len);

  writelog('');
  writelog('start');

  NextTimerEnabled:=False;

  DriverEnabled:=True;
  if Main.FilereadBusyFlag=True then DriverEnabled:=False;
  if Main.goEtcWindowFlag=True then DriverEnabled:=False;
  if sdEnd=True then DriverEnabled:=False;

  GetSamples:=len div 4; // データ長をサンプル数に変換
  SamplePos:=0;
  SampleOne:=integer(NowPCMRate) div NowAnalyzeFrame; // １フレームのサンプル数

  case NowUseDSound of
    PCMOut_Null:         SetTick:=timeGetTime;
    PCMOut_WindowsSound: SetTick:=timeGetTime+dword(NowDSoundSafeTime*(PCMBufCount-2));
    PCMOut_DirectSound:  SetTick:=timeGetTime;
    PCMOut_ASIO:         SetTick:=timeGetTime;
    PCMOut_WinAMPOutput: SetTick:=timeGetTime-dword(NowDSoundSafeTime)+dword(WAOUT.GetDifferenceMS);
    else                 SetTick:=timeGetTime;
  end;

//  if (GetSamples div SampleOne)=0 then showmessage('(GetSamples div SampleOne)=0 div error.');

  SetTickOne:=NowDSoundSafeTime/(GetSamples div SampleOne);

  CalcSystemOnly:=False;
  with Main do begin
    if isCompactWindow=True then CalcSystemOnly:=True;
    if (Application.Active=False) and (VisINI.AlreadyRefresh=False) then CalcSystemOnly:=True;
    if sdGetQuietMode=True then CalcSystemOnly:=True;
    if Visible=False then CalcSystemOnly:=True;
  end;

  if DriverEnabled=False then begin // null PCMBuffer
    while SamplePos<>GetSamples do begin
      inc(SamplePos,SampleOne);
      SetTick:=SetTick+SetTickOne;
      if Main.FileReadBusyFlag=False then
        Vis.CalcImage(trunc(SetTick),sdGetDriverMode,True,CalcSystemOnly,NowAnalyzeFFT,PCMBuffer,SamplePos,SampleOne);
    end;
    if (WADSPPluginLoaded=True) and (EnableDSP=True) then begin
      if DSPNullSound=False then begin
        // WADSPを使わないときはコメントアウトする
        if NowPCMRate<=176650 then begin
          WADSPPlugin.Render(PCMBuffer,len,NowPCMRate);
          end else begin
          WADSPPlugin.Render(PCMBuffer,len,176650);
        end;
        maxvol:=0;
        for cnt:=0 to (len-1) div 2 do begin
          if maxvol<PCMBuffer[cnt] then maxvol:=PCMBuffer[cnt];
        end;
        if maxvol<=32 then DSPNullSound:=True;
      end;
    end;
    end else begin // Make Buffer
    while SamplePos<GetSamples do begin
  writelog('pcmbuf');
      if (sdEnd=False) and (sdPause=False) then begin
        sdGetPCM(PCMBuffer[SamplePos*2],SampleOne,NowPCMRate);
        sdMMLTrap;
        DSPNullSound:=False;
        if sdEnd=True then NextTimerEnabled:=True;
      end;
  writelog('calcimage');
      CurrentPCMPos:=SamplePos;
      CurrentPCMSize:=SampleOne;
      Vis.CalcImage(trunc(SetTick),sdGetDriverMode,(sdGetStop or sdGetPause),CalcSystemOnly,NowAnalyzeFFT,PCMBuffer,SamplePos,SampleOne);
      inc(SamplePos,SampleOne);
      SetTick:=SetTick+SetTickOne;
    end;

  writelog('volume');
    with SndEff do begin
      if VolumeTableUse=True then begin
        for CopyCount:=0 to (len-1) div 2 do
          PCMBuffer[CopyCount]:=VolumeTable[word(PCMBuffer[CopyCount])];
      end;
    end;

  writelog('endflag');
    if sdEnd=False then begin
      KDDEndFlag:=True;
      if Main.KDDLoaded=True then begin
        if (sdGetDriverMode=DriverModeMXDRVm) or (sdGetDriverMode=DriverModeMXDRVg) then begin
          if MainINI.UseKDD=True then begin
            KDDEndFlag:=KDD.GetEndFlag;
          end;
        end;
      end;
      if KDDEndFlag=True then begin
        if sdFadeout=False then begin
          if MainINI.PlayMode<>PlayMode_Single then begin
            if MainINI.LoopMax<=sdLoop then begin
              sdStartFadeout;
            end;
          end;
        end;
      end;
    end;

  writelog('wadsp');
    if (WADSPPluginLoaded=True) and (EnableDSP=True) then begin
      if DSPNullSound=False then begin
        // WADSPを使わないときはコメントアウトする
        if NowPCMRate<=176650 then begin
          WADSPPlugin.Render(PCMBuffer,len,NowPCMRate);
          end else begin
          WADSPPlugin.Render(PCMBuffer,len,176650);
        end;
        if (sdEnd=True) or (sdPause=True) then begin
          maxvol:=0;
          for cnt:=0 to (len-1) div 2 do begin
            if maxvol<PCMBuffer[cnt] then maxvol:=PCMBuffer[cnt];
          end;
          if maxvol<=32 then DSPNullSound:=True;
        end;
      end;
    end;
  end;

  PeekLevel.SeekPeekAndSetVolume(PCMBuffer[0],GetSamples);

  if NextTimerEnabled=True then Main.NextTimerFromPCMOut.Enabled:=True;

  writelog('end');
end;

procedure TPCMOut.UpdateNullMonitor(Sender: TObject);
begin
  MemSync.BeginWrite;
  UpdateMonitor(0,PCMBufSize);
  MemSync.EndWrite;
end;

procedure TPCMOut.UpdateLLPCMMonitor(Sender: TObject);
begin
  MemSync.BeginWrite;
  UpdateMonitor(0,PCMBufSize);
  if assigned(LLPCM) then begin
    LLPCM.SetBuffer(@PCMBuffer,PCMBufSize);
  end;
  MemSync.EndWrite;
end;

procedure TPCMOut.UpdateWaveMonitor(Sender:TDDSDGenWave; Player:TDDSDChannel; ofs,len:Cardinal);
begin
  MemSync.BeginWrite;
  UpdateMonitor(ofs,len);
  if assigned(DDSD) then begin
    if assigned(WaveMonitor) then begin
      WaveMonitor.BlockCopy(ofs,@PCMBuffer,len);
    end;
  end;
  MemSync.EndWrite;
end;

var
  asiotick:dword;

procedure TPCMOut.UpdateMonitorForAsio(ofs,len:Cardinal);
var
  DriverEnabled:boolean;
  CopyCount:Integer;
  GetSamples:integer;
  SamplePos,SampleOne:integer;
  SetTick:single;
  CalcSystemOnly:boolean;
  cnt:integer;
  maxvol:SmallInt;
  KDDEndFlag:boolean;
  NextTimerEnabled:boolean;
begin
  if len=0 then exit;

  ZeroMemory(addr(PCMBuffer[0]),len);

  writelog('');
  writelog('start');

  NextTimerEnabled:=False;

  DriverEnabled:=True;
  if Main.FilereadBusyFlag=True then DriverEnabled:=False;
  if Main.goEtcWindowFlag=True then DriverEnabled:=False;
  if sdEnd=True then DriverEnabled:=False;

  GetSamples:=len div 4; // データ長をサンプル数に変換
  SamplePos:=0;
  SampleOne:=GetSamples;

  case NowUseDSound of
    PCMOut_ASIO:         SetTick:=timeGetTime-dword(NowDSoundSafeTime);
    else                 SetTick:=timeGetTime;
  end;

  CalcSystemOnly:=False;
  with Main do begin
    if isCompactWindow=True then CalcSystemOnly:=True;
    if (Application.Active=False) and (VisINI.AlreadyRefresh=False) then CalcSystemOnly:=True;
    if sdGetQuietMode=True then CalcSystemOnly:=True;
    if Visible=False then CalcSystemOnly:=True;
  end;

  if DriverEnabled=False then begin // null PCMBuffer
    for cnt:=0 to (len div 2)-1 do begin
      PCMBuffer[cnt]:=0;
    end;
    if Main.FileReadBusyFlag=False then begin
      if integer(timeGetTime-asiotick)>(1000 div NowAnalyzeFrame) then begin
        asiotick:=timeGetTime;
        Vis.CalcImage(trunc(SetTick),sdGetDriverMode,True,CalcSystemOnly,NowAnalyzeFFT,PCMBuffer,SamplePos,SampleOne);
        end else begin
        Vis.CalcImageFFTDataSetOnly(True,CalcSystemOnly,PCMBuffer,SamplePos,SampleOne);
      end;
    end;
    if (WADSPPluginLoaded=True) and (EnableDSP=True) then begin
      if DSPNullSound=False then begin
        if NowPCMRate<=176650 then begin
          WADSPPlugin.Render(PCMBuffer,len,NowPCMRate);
          end else begin
          WADSPPlugin.Render(PCMBuffer,len,176650);
        end;
        maxvol:=0;
        for cnt:=0 to (len-1) div 2 do begin
          if maxvol<PCMBuffer[cnt] then maxvol:=PCMBuffer[cnt];
        end;
        if maxvol<=32 then DSPNullSound:=True;
      end;
    end;
    end else begin // Make Buffer
  writelog('pcmbuf');
      if (sdEnd=False) and (sdPause=False) then begin
        sdGetPCM(PCMBuffer[SamplePos*2],SampleOne,NowPCMRate);
        sdMMLTrap;
        DSPNullSound:=False;
        if sdEnd=True then NextTimerEnabled:=True;
      end;
  writelog('calcimage');
      CurrentPCMPos:=SamplePos;
      CurrentPCMSize:=SampleOne;
      if integer(timeGetTime-asiotick)>(1000 div NowAnalyzeFrame) then begin
        asiotick:=timeGetTime;
        Vis.CalcImage(trunc(SetTick),sdGetDriverMode,(sdGetStop or sdGetPause),CalcSystemOnly,NowAnalyzeFFT,PCMBuffer,SamplePos,SampleOne);
        end else begin
        Vis.CalcImageFFTDataSetOnly((sdGetStop or sdGetPause),CalcSystemOnly,PCMBuffer,SamplePos,SampleOne);
      end;

  writelog('volume');
      with SndEff do begin
        if VolumeTableUse=True then begin
          for CopyCount:=0 to (len-1) div 2 do
            PCMBuffer[CopyCount]:=VolumeTable[word(PCMBuffer[CopyCount])];
        end;
      end;

  writelog('endflag');
      if sdEnd=False then begin
        KDDEndFlag:=True;
        if Main.KDDLoaded=True then begin
          if (sdGetDriverMode=DriverModeMXDRVm) or (sdGetDriverMode=DriverModeMXDRVg) then begin
            if MainINI.UseKDD=True then begin
              KDDEndFlag:=KDD.GetEndFlag;
            end;
          end;
        end;
        if KDDEndFlag=True then begin
          if sdFadeout=False then begin
            if MainINI.PlayMode<>PlayMode_Single then begin
              if MainINI.LoopMax<=sdLoop then begin
                sdStartFadeout;
              end;
            end;
          end;
        end;

  writelog('wadsp');
      if (WADSPPluginLoaded=True) and (EnableDSP=True) then begin
        if DSPNullSound=False then begin
          // WADSPを使わないときはコメントアウトする
          if NowPCMRate<=176650 then begin
            WADSPPlugin.Render(PCMBuffer,len,NowPCMRate);
            end else begin
            WADSPPlugin.Render(PCMBuffer,len,176650);
          end;
          if (sdEnd=True) or (sdPause=True) then begin
            maxvol:=0;
            for cnt:=0 to (len-1) div 2 do begin
              if maxvol<PCMBuffer[cnt] then maxvol:=PCMBuffer[cnt];
            end;
            if maxvol<=32 then DSPNullSound:=True;
          end;
        end;
      end;
    end;
  end;

  PeekLevel.SeekPeekAndSetVolume(PCMBuffer[0],GetSamples);

  if NextTimerEnabled=True then Main.NextTimerFromPCMOut.Enabled:=True;

  writelog('end');
end;

procedure TPCMOut.UpdateAsioMonitor(ChannelType:integer;BufferSamplesCount:integer;ch0buf,ch1buf:Pointer);
begin
  MemSync.BeginWrite;
  UpdateMonitorForAsio(0,BufferSamplesCount*2*2);
  MemSync.EndWrite;
  if assigned(OutASIO) then begin
    OutASIO.SetBuffer(PCMBuffer,ChannelType,BufferSamplesCount,ch0buf,ch1buf);
  end;
end;

procedure TPCMOut.UpdateWAOUTMonitor(Sender: TObject);
begin
  MemSync.BeginWrite;
  if PCMBufSize<=WAOUT.GetCanWriteLength then begin
    UpdateMonitor(0,PCMBufSize);
    if assigned(WAOUT) then begin
      WAOUT.SetBuffer(@PCMBuffer,PCMBufSize);
    end;
  end;
  MemSync.EndWrite;
end;

procedure TPCMOut.SetEnableDSP(e:boolean);
begin
  MemSync.BeginWrite;
  EnableDSP:=e;
  MemSync.EndWrite;
end;

procedure TPCMOut.MemWStart;
begin
  writelog('beginwrite');
  MemSync.BeginWrite;
  writelog('beginwriting');
end;

procedure TPCMOut.MemWEnd;
begin
  writelog('endwrite');
  MemSync.EndWrite;
  writelog('edwrited');
end;

procedure TPCMOut.ASIO_OpenSetting;
begin
  if OutAsio=nil then OutAsio:=TOutAsio.Create;
  Application.CreateForm(TAsioSetDlg, AsioSetDlg);
  AsioSetDlg.SetCreate;
  AsioSetDlg.Show;
  while (AsioSetDlg.Visible=True) do begin
    Application.ProcessMessages;
    sleep(100);
  end;
  AsioSetDlg.Release;
end;

procedure TPCMOut.WAOUT_ShowConfig(DLL:string);
begin
  WAOUT.ShowConfig(DLL);
end;

procedure TPCMOut.WAOUT_ShowAbout(DLL:string);
begin
  WAOUT.ShowAbout(DLL);
end;

function TPCMOut.WAOUT_GetModulesCount:integer;
begin
  Result:=WAOUT.GetModulesCount;
end;

function TPCMOut.WAOUT_GetModuleDLLName(idx:integer):string;
begin
  Result:=WAOUT.GetModuleDLLName(idx);
end;

end.
