unit _waveenc;

interface

uses
  Windows, Messages, SysUtils, Forms,Variants, Classes, Controls, Dialogs;

{$WARN UNIT_PLATFORM OFF}
{$WARN SYMBOL_PLATFORM OFF}

const RiffHeaderSize=11;
const PCMBufSize=1024*4;

type
  TWaveProgressEvent = procedure (TotalClock,MesureClock:integer) of Object;

type
  Twaveenc = class
  private
    { Private 宣言 }
    RiffHeader:array[0..RiffHeaderSize] of DWORD;
    FileSize:integer;
    WriteFile:TFileStream;
    PCMBuf:array of SmallInt;
    MainThreadPriority:integer;
  public
    { Public 宣言 }
    OnProgress:TWaveProgressEvent;
    Cancelled:boolean;
    procedure Init;
    procedure StartWaveMode(rate:integer);
    procedure EndWaveMode;
    function  CreateWave(WaveFilename:string;setLoop:integer;setPCMRate:integer;setVolume:integer;setUseFadeout:boolean;setEnableDSP:boolean;StartBlankTime,EndBlankTime:integer):boolean;
  end;

implementation

uses MainWin,
     _MainFileCtl,_MDXWinINI,_const, _SndDrv_const,_SndDrv,_PCMOut;

procedure Twaveenc.Init;
begin
  OnProgress:=nil;
end;

procedure Twaveenc.StartWaveMode(rate:integer);
var
  Count:integer;
  function SwapHiLow(d:dword):dword;
  begin
    Result:=dword((int64(d) div $1000000 and $FF)+((int64(d) div $10000 and $FF)*$100)+((int64(d) div $100 and $FF)*$10000)+((int64(d) and $FF)*$1000000));
  end;
begin
  // RiffWave Header of 44.1khz 16bit stereo
  RiffHeader[ 0]:=$52494646; // RIFF Header
  RiffHeader[ 1]:=$00000000; // TotalFileSize-8;
  RiffHeader[ 2]:=$57415645; // WAVE Header
  RiffHeader[ 3]:=$666D7420; // fmt  Header
  RiffHeader[ 4]:=$10000000;
  RiffHeader[ 5]:=$01000200; // (word)wFormatTag,(word)nChannels
  RiffHeader[ 6]:=$44AC0000; // (dword)nSamplesPerSec(44kHz)
  RiffHeader[ 7]:=$10B10200; // (dword)nAvgBytesPerSec(44kHz*4)
  RiffHeader[ 8]:=$04001000; // (word)nBlockAlign,(word)wBitsPerSample
  RiffHeader[ 9]:=$64617461; // data Header
  RiffHeader[10]:=$00000000; // WaveSize (bytesize)

  RiffHeader[ 6]:=SwapHiLow(rate);
  RiffHeader[ 7]:=SwapHiLow(rate*4);

  // Swap Hi Low
  for Count:=0 to RiffHeaderSize-1 do begin
    RiffHeader[Count]:=SwapHiLow(RiffHeader[Count]);
  end;

  MainThreadPriority:=GetPriorityClass(GetCurrentProcess);
  SetPriorityClass(GetCurrentProcess,IDLE_PRIORITY_CLASS);
  Main.RewriteTimer.Enabled:=False;
end;

procedure Twaveenc.EndWaveMode;
begin
  sdStop;
  SetPriorityClass(GetCurrentProcess,MainThreadPriority);
  Main.RewriteTimer.Enabled:=True;
end;

function Twaveenc.CreateWave(WaveFilename:string;setLoop:integer;setPCMRate:integer;setVolume:integer;setUseFadeout:boolean;setEnableDSP:boolean;StartBlankTime,EndBlankTime:integer):boolean;
var
  cnt:integer;
  TotalClock,MesureClock:integer;
  Drive:string;
  DSec,DByte,DFClus,DUClus:dword;
  DBytePerClus:Extended;
  PlayEnd:boolean;
  SoundStart:boolean;
  msgtick:dword;
  CurrentBufSize:integer;
  procedure WriteRiffHeadder;
  begin
    RiffHeader[ 1]:=(RiffHeaderSize*4)+FileSize-8;
    RiffHeader[10]:=FileSize-$2c;
    WriteFile.position:=0;
    WriteFile.WriteBuffer(RiffHeader,RiffHeaderSize*4);
  end;
  function GetDSPMaxVolume(bufsize:integer):SmallInt;
  var
    cnt:integer;
  begin
    Result:=0;
    for cnt:=0 to (bufsize-1) div 2 do begin
      if Result<PCMBuf[cnt] then Result:=PCMBuf[cnt];
    end;
  end;
  procedure ClearWADSP(bufsize:integer);
  var
    cnt:integer;
    maxvol:SmallInt;
  begin
    if WADSPPluginLoaded=False then begin
      end else begin
      maxvol:=8;
      while (maxvol>=4) do begin
        for cnt:=0 to 16-1 do begin
          ZeroMemory(addr(PCMBuf[0]),bufsize);
          if setPCMRate<=176650 then begin
            WADSPPlugin.Render(PCMBuf[0],bufsize,setPCMRate);
            end else begin
            WADSPPlugin.Render(PCMBuf[0],bufsize,176650);
          end;
        end;
        maxvol:=GetDSPMaxVolume(bufsize);
        OnProgress(4,maxvol);
      end;
    end;
  end;
  function CheckDrive(drv:string;needsize:integer):boolean;
  begin
    Result:=True;
    if GetDiskFreeSpace(addr(drv[1]),DSec,DByte,DFClus,DUClus)=False then begin
      MessageDlg('保存先ドライブが異常です。'+CRLF+'SETUPで保存先ドライブを確認して下さい。',mtError,[mbAbort],0);
      Result:=False;
      exit;
    end;
    DBytePerClus:=DSec*DByte;
    if (DBytePerClus*DFClus)<=needsize then begin
      MessageDlg('保存先ドライブに空き容量がありません。',mtError,[mbAbort],0);
      Result:=False;
      exit;
    end;
  end;
  function WriteWave(bufsize:integer):boolean;
  begin
    Result:=True;
    try
      WriteFile.WriteBuffer(PCMBuf[0],bufsize);
      inc(FileSize,bufsize);
      except
      else begin
        MessageDlg('何故か保存に失敗しました…。',mtError,[mbAbort],0);
        Result:=False;
      end;
    end;
  end;
  function WriteBlank(ms:integer):boolean;
  var
    bufsize:integer;
  begin
    Result:=True;
    bufsize:=trunc(setPCMRate*ms div 1000)*4;
    if bufsize=0 then exit;

    SetLength(PCMBuf,bufsize);
    ZeroMemory(addr(PCMBuf[0]),bufsize);
    if WriteWave(bufsize)=False then begin
      Result:=False;
    end;
    SetLength(PCMBuf,CurrentBufSize);
  end;
begin
  if SetPCMRate=0 then begin
    ShowMessage('Error:出力周波数が0Hzです。再設定してください。');
    Result:=False;
    exit;
  end;

  Drive:=UpperCase(WaveFilename[1])+':\';
  if CheckDrive(Drive,1024*1024)=False then begin // 1MBくらい要求してもいいよね？
    Result:=False;
    exit;
  end;

  if Main.gFileInfo.isNetworkMode=False then begin
    if FileExists(Main.gFileInfo.GetPlayPath)=False then begin
      MessageDlg('変換元ファイルが存在しません。'+CRLF+Main.gFileInfo.GetPlayPath,mtError,[mbAbort],0);
      Result:=False;
      exit;
    end;
    end else begin
    if Main.Network.NetworkEnable=False then begin
      Result:=False;
      exit;
    end;
  end;

  if FileExists(WaveFilename)=True then DeleteFile(WaveFilename);
  try
    WriteFile:=TFileStream.Create(WaveFilename,fmCreate or fmShareExclusive);
    except
    else begin
      MessageDlg('変換先ファイル書き込みエラーです。'+chr(13)+'共有違反や読み込み専用属性がないかどうか確認してください。',mtError,[mbAbort],0);
      Result:=False;
      exit;
    end;
  end;
  WriteFile.Position:=0;
  FileSize:=0;
  CurrentBufSize:=0;

  // 先頭無音領域
  if WriteBlank(StartBlankTime)=False then begin
    Result:=False;
    WriteFile.Free;
    DeleteFile(WaveFilename);
    exit;
  end;

  MainFileCtl.DirectPlayFile(Main.gFileInfo.GetFullPath);
  while Main.FilereadBusyFlag=True do begin
    Application.ProcessMessages;
    Sleep(16);
  end;

  CurrentBufSize:=128*4;
  SetLength(PCMBuf,CurrentBufSize);
  sdGetPCM(PCMBuf[0],CurrentBufSize div 4,setPCMRate);

  Cancelled:=False;
  PlayEnd:=False;
  SoundStart:=False;
  Result:=True;

  msgtick:=0;

  PCMOut.SetEnableDSP(False);
  CurrentBufSize:=4*4;
  SetLength(PCMBuf,CurrentBufSize);
  ClearWADSP(CurrentBufSize);

  SndEff.CalcVolumeTable(setVolume);

  while (0=0) do begin

    Application.ProcessMessages;

    if Cancelled=True then begin
      WriteFile.Free;
      DeleteFile(WaveFilename);
      sdStop;
      ShowMessage('変換を中止しました。');
      Result:=False;
      break;
    end;

    MesureClock:=sdGetMesureClock;
    TotalClock:=sdGetTotalClock;

    if (GetTickCount-msgtick)>100 then begin
      msgtick:=GetTickCount;
      OnProgress(TotalClock,MesureClock);
    end;

    ZeroMemory(addr(PCMBuf[0]),CurrentBufSize);
    if sdEnd=True then begin
      end else begin
      sdGetPCM(PCMBuf[0],CurrentBufSize div 4,setPCMRate);
      sdMMLTrap;

      if sdFadeout=False then begin
        if setLoop<=sdLoop then begin
          if setUseFadeout=False then begin
            sdStop;
            end else begin
            sdStartFadeout;
          end;
        end;
      end;
    end;

    with SndEff do begin
      if VolumeTableUse=True then begin
        for cnt:=0 to (CurrentBufSize-1) div 2 do
          PCMBuf[cnt]:=VolumeTable[word(PCMBuf[cnt])];
      end;
    end;

    if SoundStart=False then begin
      PlayEnd:=sdEnd;
      if GetDSPMaxVolume(CurrentBufSize)>5 then SoundStart:=True;
    end;

    if SoundStart=True then begin
      if (WADSPPluginLoaded=False) or (setEnableDSP=False) then begin
        PlayEnd:=sdEnd;
        end else begin
        if setPCMRate<=176650 then begin
          WADSPPlugin.Render(PCMBuf,CurrentBufSize,setPCMRate);
          end else begin
          WADSPPlugin.Render(PCMBuf,CurrentBufSize,176650);
        end;
        if sdEnd=False then begin
          PlayEnd:=False;
          end else begin
          if GetDSPMaxVolume(CurrentBufSize)<=4 then begin
            PlayEnd:=True;
            end else begin
            PlayEnd:=False;
          end;
        end;
      end;

      if CheckDrive(Drive,CurrentBufSize+128)=False then begin
        Result:=False;
        WriteFile.Free;
        DeleteFile(WaveFilename);
        break;
      end;

      if WriteWave(CurrentBufSize)=False then begin
        Result:=False;
        WriteFile.Free;
        DeleteFile(WaveFilename);
        break;
      end;

      if CurrentBufSize<>PCMBufSize then begin
        CurrentBufSize:=PCMBufSize;
        SetLength(PCMBuf,CurrentBufSize);
      end;
    end;

    if (PlayEnd=True) or (TotalClock>999999) then begin
      // 先頭無音領域
      if WriteBlank(EndBlankTime)=False then begin
        Result:=False;
        WriteFile.Free;
        DeleteFile(WaveFilename);
        break;
      end;
      WriteRiffHeadder;
      WriteFile.Free;
      Result:=True;
      break;
    end;
  end;

  CurrentBufSize:=128*4;
  SetLength(PCMBuf,CurrentBufSize);
  ClearWADSP(CurrentBufSize);

  SndEff.CalcVolumeTable(MainINI.TotalVolume);
  PCMOut.SetEnableDSP(True);
end;

end.
