//
// GogoDLL Rapper & Thread Component - TGogoTea - version.1.0
//     Copyright (C) 1999 by M.yanagisawa.(MoroboshiLUM)
//     All rights Reserverd
//
unit GogoTea;

interface

uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    GogoDefs;

const
   ETM_STARTENCODE = WM_USER + $100;  // エンコード開始
   ETM_ABORTENCODE = WM_USER + $101;  // エンコード中断
   ETM_QUIT        = WM_USER + $102;  // スレッド破棄

   ESM_EMPTYSTREAM = WM_USER + $200;  // EMPTYSTREAM
   ESM_ENCODEERROR = WM_USER + $201;  // エンコードエラーイベント
   ESM_FINALENCODE = WM_USER + $202;  // 完了イベント発生
   ESM_PROGRESS    = WM_USER + $203;  // 経過イベント

type
  EGogoTeaError = class(Exception); // 例外通知用
  TEncodeThread = class(TThread)        // Encode Thread
    protected
        procedure Execute; override;    // 実行部本体。
    public
        AllFrames : integer;
        hComponent : THandle;           // メインスレッド間通信用
  end;

  // RMPタグ情報
  TRMPInfoTag = packed record
    Chunk : DWORD;  //チャンク名
    Info  : Char;
  end;
  PRMPInfoTag = ^TRMPInfoTag;

  // Rmp/ID3 tag Info packs
  TTagInfoPac = record
    infosize : integer;
    infobody : Pointer;
  end;
  PTagInfoPac = ^TTagInfoPac;

  TExtFilter = class(TPersistent)
  private
    FEnabled : boolean;
    FLow : integer;
    FHigh : integer;
  published
    property Low : integer read FLow write FLow;
    property High : integer read FHigh write FHigh;
    property Enabled : boolean read FEnabled write FEnabled;
  end;

  TVBRBitrate = class(TPersistent)
  private
    FEnabled : boolean;
    FLow : integer;
    FHigh : integer;
  published
    property Enabled : boolean read FEnabled write FEnabled;
    property Low : integer read FLow write FLow;
    property High : integer read FHigh write FHigh;
  end;

  TMSSetting = class(TPersistent)
  private
    FEnabled : boolean;
    FMSThreshold : integer;
    FMSPower : integer;
  published
    property Enabled : boolean read FEnabled write FEnabled;
    property Threshold : integer read FMSThreshold write FMSThreshold;
    property Power : integer read FMSPower write FMSPower;
  end;

  // エンコードモード
  TEncodeMode = (_FILE,_MEMORY);

  // エンコードタイプ
  TEncodeType = (_MONO,_STEREO,_JOINT,_MSSTEREO,_DUALCHANNEL);

  // エンファシス設定
  TEmphasis = (_NO_EMPH,_5015MS_EMPH,_CCITT_EMPH);

  // コード最適化
  TUseUnit  = (_USE_MMX,_USE_3DNOW,_USE_KNI,_USE_EX_3DNOW,_USE_SPC1,_USE_SPC2);
  TSetOfUseUnit = set of TUseUnit;

  // エンコードフォーマット
  TEncodeFormat = ( _FORMAT_OF_MP3 , _FORMAT_OF_RIFF_WAVE , _FORMAT_OF_RIFF_RMP );

// 経過イベント(0-100%まで)
  TGogoTeaProgressEvent = procedure (Sender:TObject; nowFrames:integer; AllFrames:integer ) of Object;
// エンコードエラーイベント
  TGogoTeaEncodeError  = procedure (Sender:TObject; ErrorCode: integer) of Object;
// エンコード終了イベント(終了はここでみること)
// IsAbortedがTRUEのときは中断されたということ。
  TGogoTeaFinishEncode = procedure (Sender:TObject; IsAborted:boolean) of Object;

// Gogo DLL Support DLL Component class
TGogoTea = class( TComponent )
  private
    FEncodeThread : TEncodeThread;
    FSourceFile,FDestFile : string;
    FIsEncoding,FIsDetected : boolean;
    FProcessors : integer;
    FEmphasis : TEmphasis;
    FSetOfUseUnit : TSetOfUseUnit;
    FEncodeType : TEncodeType;
    FEncodeResult : integer;
    FBitrate : integer;
    FHWnd : THandle;
    FAllFrames : integer;
    FFrequency : integer;
    FUsePsy : boolean;
    FUseLPF16 : boolean;
    FUseVBR : boolean;
    FVBRLevel : integer;
    FPriority : TThreadPriority;
    FOnProgress : TGogoTeaProgressEvent;
    FOnError : TGogoTeaEncodeError;
    FOnFinish : TGogoTeaFinishEncode;
    FRingBufSize : DWORD;
    FGogoDLLPath : string;
// for Memory Encode
    FWPtr,FRPtr : DWORD; // 読み書きポインタ
    FPRingBuf : PChar;
    FCSInfo : TRTLCriticalSection;
    FEncodeMode : TEncodeMode;
// for v2.30 upper
    FEncodeFormat : TEncodeFormat;
    FRMPTagList : TList;
    FID3Tag : TTagInfoPac;
    FExtFilter : TExtFilter;
    FVBRBitrate : TVBRBitrate;
    FMSSetting : TMSSetting;
    procedure WndProc(var Msg:TMessage); // 通信用
    procedure SetEncodeFormat(form:TEncodeFormat);
    procedure SetEncodeMode(Mode:TEncodeMode);
    procedure SetBitrate(Bitrate:Integer);
    procedure SetEncodeType(EncodeType:TEncodeType);
    procedure SetSetOfUseUnit(SetOfUseUnit:TSetOfUseUnit);
    procedure SetEmphasis(Emphasis:TEmphasis);
    procedure SetProcessors(Processors:integer);
    procedure SetDestFile(Destination:string);
    procedure SetSourceFile(SourceFile:string);
    procedure SetUsePsy(IsUsePsy:boolean);
    procedure SetUseLPF16(IsUseLPF16:boolean);
    procedure SetUseVBR(IsUseVBR:boolean);
    procedure SetVBRLevel(Level:integer);
    procedure SetFrequency(Freq:integer);
    procedure SetPriority(Priority:TThreadPriority);
    procedure SetExtFilter(ExtFilter:TExtFilter);
    procedure SetVBRBitrate(VBRBitrate:TVBRBitrate);
    procedure SetMSSetting(MSSetting:TMSSetting);
    function GetUnitStates:DWORD;
    function GetVersionCode:DWORD;
    function GetVersionStr:string;
    procedure OnProgressStatus(Msg:TMessage);
    procedure OnFinishEncode(Msg:TMessage);
    procedure OnEncodeError(Msg:TMessage);
  public
    isAliveEncoder : boolean;
    AbortReq : boolean;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DisposeTags;
    procedure QuerySettings;
    procedure Initialize;
    procedure AbortEncode;
    procedure Execute;
    procedure AddRmpTagInfo(info:TTagInfoPac);
    procedure SetID3TagInfo(info:TTagInfoPac);
// Rapper
    function EncodeFrames:integer;
    function CloseEncoder:integer;
    function EndEncoder:integer;
    function DetectConfigure:integer;
    function SetConfigure(Verb:DWORD;PARAM1:DWORD;PARAM2:DWORD):integer;
    function GetConfigure(Verb:DWORD;var PARAM1):integer;
    function WriteBuffer(pBuf:PChar;writesize:integer):boolean;
    function ReadBuffer(pBuf:PChar;readsize:integer):boolean;
    function ReadBufferEx(pBuf:PChar;readsize:integer):integer;
  published
    property Priority : TThreadPriority read FPriority write SetPriority;
    property UnitStates : DWORD read GetUnitStates;
    property VersionString : string read GetVersionStr;
    property VersionCode : DWORD read GetVersionCode;
    property DLLPath : string read FGogoDLLPath;
    property AllFrames : integer read FAllFrames;
    property Frequency : integer read FFrequency write SetFrequency;
    property SourceFile : string read FSourceFile write SetSourceFile;
    property DestinationFile : string read FDestFile  write SetDestFile;
    property IsEncoding : boolean read FIsEncoding;
    property Processors : integer read FProcessors write SetProcessors;
    property Emphasis   : TEmphasis read FEmphasis write SetEmphasis;
    property UsePsyAnalize : boolean read FUsePsy write SetUsePsy;
    property UseLPF : boolean read FUseLPF16 write SetUseLPF16;
    property UseVBR : boolean read FUseVBR write SetUseVBR;
    property VBRLevel : integer read FVBRLevel write SetVBRLevel;
    property Optimization : TSetOfUseUnit read FSetOfUseUnit write SetSetOfUseUnit;
    property EncodeFormat : TEncodeFormat read FEncodeFormat write SetEncodeFormat;
    property EncodeMode : TEncodeMode read FEncodeMode write SetEncodeMode;
    property EncodeType : TEncodeType read FEncodeType write SetEncodeType;
    property EncodeResult : integer read FEncodeResult;
    property BitRate : integer read FBitrate write SetBitrate;
	property ExtFilter : TExtFilter read FExtFilter write SetExtFilter;
	property VBRBitrate : TVBRBitrate read FVBRBitrate write SetVBRBitrate;
    property MSSetting : TMSSetting read FMSSetting write SetMSSetting;
    property OnProgress : TGogoTeaProgressEvent read FOnProgress write FOnProgress;
    property OnError : TGogoTeaEncodeError read FOnError write FOnError;
    property OnFinish : TGogoTeaFinishEncode read FOnFinish write FOnFinish;
end;

procedure Register;

implementation

const
  EncodeErrMsg = 'エンコード中に設定は変更できません.';
  ParamErrMsg = '値が範囲外です.';
  INITIAL_BUFSIZE = $100000;  // 1Mバイト
var
  MyInstance : TGogoTea;  // コールバックから呼び出すための自分自身のインスタンス

// 各変数系の初期化
// 128kbps,JointStereo,All Unit Use,No Emphasis,No Use PsyAnalize,No Use LPF
// Src,Dest no file.
procedure TGogoTea.Initialize;
begin
  EnterCriticalSection(FCSInfo);
  if FPRingBuf=NIL then GetMem(FPRingBuf,FRingBufSize);
  DisposeTags;
  FRPtr:=0; FWPtr:=0;  // バッファクリア
  AbortReq:=FALSE;
  LeaveCriticalSection(FCSInfo);
  SetBitrate(128);
  SetEncodeType(_JOINT);
  SetSetOfUseUnit([_USE_MMX,_USE_KNI,_USE_3DNOW,_USE_EX_3DNOW]); //すべて
  SetEmphasis(_NO_EMPH);
  SetProcessors(1);
  SetUsePsy(FALSE);
  SetUseLPF16(FALSE);
  SetUseVBR(FALSE);
  SetVBRLevel(5);
  SetDestFile('');
  SetSourceFile('');
  SetPriority(tpIdle);
  SetFrequency(100);
  SetEncodeMode(_FILE);
  SetEncodeFormat(_FORMAT_OF_MP3);
  FAllFrames:=0;
  FExtFilter.Enabled:=FALSE;
  FExtFilter.Low:=55;
  FExtFilter.High:=70;
  FVBRBitrate.Enabled:=FALSE;
  FVBRBitrate.Low:=40;
  FVBRBitrate.High:=128;
  FMSSetting.Enabled:=FALSE;
  FMSSetting.Threshold:=75;
  FMSSetting.Power:=66;
  FEncodeResult:=ME_NOERR;
end;

procedure TGogoTea.AddRmpTagInfo(info:TTagInfoPac);
var
  p : PTagInfoPac;
begin
  if (info.infosize>0) and (info.infobody<>NIL) then begin
    if not Assigned(FRmpTagList) then FRmpTagList:=TList.Create;
    GetMem(p,sizeof(TTaginfoPac));
    p^:=info;
    GetMem(p^.infobody,info.infosize);
    Move(info.infobody^,p^.infobody^,info.infosize);
    FRmpTagList.Add(p);
  end
  else
    raise EGogoTeaError.Create(ParamErrMsg);
end;

procedure TGogoTea.SetID3TagInfo(info:TTagInfoPac);
begin
  if (info.infosize>0) and (info.infobody<>NIL) then begin
    if FID3Tag.InfoSize>0 then FreeMem(FID3Tag.infobody);
    FID3Tag:=info;  // set size.
    GetMem(FID3Tag.infobody,info.infosize);
    Move(info.infobody^,FID3Tag.infobody^,info.infosize);  // copy.
  end
  else
    raise EGogoTeaError.Create(ParamErrMsg);
end;

procedure TGogoTea.DisposeTags;
var
  i : integer;
begin
  if Assigned(FRMPTagList) then begin
    for i:=1 to FRMPTagList.Count do begin
      FreeMem(PTagInfoPac(FRMPTagList.Items[i-1])^.infobody);
      FreeMem(PTagInfoPac(FRMPTagList.Items[i-1]));
    end;
    FRMPTagList.Free;
  end;
  FRMPTagList:=NIL;
  if FID3Tag.infosize>0 then
    if FID3Tag.infoBody<>NIL then FreeMem(FID3Tag.infoBody);
  FID3Tag.infosize:=0;
  FID3Tag.infobody:=NIL;
end;

constructor TGogoTea.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEncodeThread:=NIL;
  FOnError:=NIL; FOnProgress:=NIL; FOnFinish:=NIL;
  FIsDetected:=FALSE; FIsEncoding:=FALSE;
  FRingBufSize:=INITIAL_BUFSIZE;
  MyInstance:=NIL; AbortReq:=FALSE;
  FExtFilter:=TExtFilter.Create;
  FVBRBitrate:=TVBRBitrate.Create;
  FMSSetting:=TMSSetting.Create;
  FGogoDLLPath:='';
// Tag Clear
  FRMPTagList:=NIL;
  FID3Tag.infosize:=0;
  FID3Tag.infobody:=NIL;
  InitializeCriticalSection(FCSInfo);  // リングバッファのアクセス同期用
  Initialize;
  if not (csDesigning in ComponentState) then begin
    // プログラム設計中は初期化しない
    // #Componentは設計中も動作しています
    if not InitGogoDefs then raise EGogoTeaError.Create('GOGO.DLLを初期化できません.');
    FGogoDLLPath:=GogoDLLPath;
    FEncodeThread:=TEncodeThread.Create(TRUE);
    FHWnd := AllocateHWnd(WndProc);
    FEncodeThread.hComponent:=FHWnd;
    MyInstance:=Self; // 自分自身を設定
    isAliveEncoder:=TRUE;
    FEncodeThread.Resume;
  end;
end;

destructor TGogoTea.Destroy;
begin
  inherited;
  isAliveEncoder:=FALSE;
  if not (csDesigning in ComponentState) then begin
    if FEncodeThread<>NIL then begin
      PostThreadMessage(FEncodeThread.ThreadID,ETM_QUIT,0,0);
      FEncodeThread.Terminate;
      FEncodeThread.WaitFor;    // スレッドが終了するまで、一応待つ。
      FEncodeThread.Free;
      FEncodeThread:=NIL;
    end;
    if FPRingBuf<>NIL then FreeMem(FPRingBuf);
    FPRingBuf:=NIL; // fin.
    DeAllocateHWnd(FHWnd);
    DeallocateGogoDLL;
  end;
  FExtFilter.Free;
  FVBRBitrate.Free;
  FMSSetting.Free;
  DeleteCriticalSection(FCSInfo);
end;

// このスレッドはコンポーネントが破棄されるまで生存。
procedure TEncodeThread.Execute;
var
  isExecuting : boolean;
  Frames : integer;
  Frequency : integer;
  Msg : TMsg;
  rslt : DWORD;
  Save8087CW : WORD;
begin
  Save8087CW:=Default8087CW;
  isExecuting:=FALSE;  Frames:=0;  Frequency:=1;
  Priority:=tpIDLE;     // Encode Priority To IDLE_MODE
  while not Terminated do begin
    // メッセージ処理
    while PeekMessage(Msg,0,0,0,PM_REMOVE) do begin
      if Msg.hWnd<>0 then begin
        TranslateMessage(msg);
        DispatchMessage(msg);
      end
      else begin
        // メインスレッドからの指令の実行
        case Msg.Message of
          ETM_STARTENCODE : begin  // エンコード開始
                              if not isExecuting then begin
                                Frequency:=Msg.LPARAM; // 経過ポスト単位
                                AllFrames:=Msg.WPARAM;
                                Frames:=0;
                                isExecuting:=TRUE;
                                asm
                                  fninit;
                                end;
                                Set8087CW($133f);
                                // 0をまず送信。
                                PostMessage(hComponent,ESM_PROGRESS,0,AllFrames);
                              end;
                              // 実行中にこのメッセージが来たら無視。
                            end;
          ETM_ABORTENCODE,  // エンコード中断
          ETM_QUIT : begin   // 停止
                       if isExecuting then begin
                         try
                         // Close系は例外発生しても無視。
                           MPGE_closeCoder;
                           MPGE_endCoder;
                         finally
                           asm
                             fninit;
                           end;
                           Set8087CW(Save8087CW);
                         end;
                       end;
                       Priority:=tpIDLE;        // 停止時に戻す
                       isExecuting:=FALSE;
                       if Msg.Message=ETM_QUIT then exit
                                               else PostMessage(hComponent,ESM_FINALENCODE,-1,0);
                     end;
        end;
      end;
    end;
    // エンコード処理
    if isExecuting then begin
      try
        rslt:=MPGE_processFrame;
      except
        rslt:=ME_INTERNALERROR; // 例外発生時はINTERNALERRORを返す
      end;
      inc(Frames);  // フレーム数加算
      case rslt of
        ME_NOERR       : begin
   //                      if Frequency>0 then
                             if (Frames mod Frequency)=0 then
                               PostMessage(hComponent,ESM_PROGRESS,Frames,AllFrames);
                         end;
        ME_EMPTYSTREAM : begin
                           PostMessage(hComponent,ESM_EMPTYSTREAM,0,0);
                           PostMessage(hComponent,ESM_PROGRESS,AllFrames,AllFrames);
                         end;
      else
        // ｴﾗｰｺｰﾄﾞをpostする。
        PostMessage(hComponent,ESM_ENCODEERROR,rslt,0);
        isExecuting:=FALSE;
      end;
      // 終了処理
      if rslt<>ME_NOERR then begin
        Sleep(10); // スレッド一時停止
        try
          MPGE_closeCoder;
          MPGE_endCoder;
        finally
          asm
            fninit;
          end;
          Set8087CW(Save8087CW);
        end;
        Priority:=tpIDLE;       // 停止時に戻す
        isExecuting:=FALSE;
        if rslt=ME_EMPTYSTREAM then rslt:=0
                               else rslt:=$FFFFFFFF;
        PostMessage(hComponent,ESM_FINALENCODE,rslt,0); // 完了イベント発生
        Frames:=0;
      end;
    end
    else
      Sleep(100); // 実行中以外は100msecごとに停止
  end;
  if isExecuting then begin
    try
      MPGE_closeCoder;
      MPGE_endCoder;
    finally
      asm
        fninit;
      end;
      Set8087CW(Save8087CW);
    end;
    PostMessage(hComponent,ESM_FINALENCODE,0,0);//一応。
    Sleep(1);
  end;
end;

// GogoDLLのコールバック関数
function CALLBACKFUNC_GOGOTEA(Buf: Pointer; nLength: DWORD ):MERET; cdecl;
begin
  Result:=ME_NOERR;
  if Assigned(MyInstance) then begin
    while MyInstance.isAliveEncoder do begin
      if  MyInstance.ReadBuffer(PChar(Buf),nLength) then break
      else if MyInstance.AbortReq then begin    // AbortReq=TRUEの時点で新規の書き込みはない。
        if MyInstance.ReadBufferEx(PChar(Buf),nLength)=0 then Result:=ME_EMPTYSTREAM;
        break;
      end
      else
      Sleep(1);
    end;
  end;
end;

// リングバッファへの書き込み。
// pBuf=NILのとき、書き込み終了。
function TGogoTea.WriteBuffer(pBuf:PChar;writesize:integer):boolean;
var
  n : integer;
  freesize: integer;
begin
  Result:=TRUE;
  if FEncodeMode<>_MEMORY then exit;
  EnterCriticalSection(FCSInfo);
  if (writesize>0) and (pBuf<>NIL) then begin
    freesize:=(FRPtr-1-FWPtr) and (INITIAL_BUFSIZE-1);
    if freesize>writesize then
      for n:=1 to writesize do begin
        FPRingBuf[FWPtr]:=pBuf[n-1];
        FWPtr:=(FWPtr+1) and (INITIAL_BUFSIZE-1);
      end
    else
       Result:=FALSE;
  end
  else begin
    if pBuf=NIL then AbortReq:=TRUE;
  end;
  LeaveCriticalSection(FCSInfo);
end;

// リングバッファからの読み出し。
function TGogoTea.ReadBuffer(pBuf:PChar;readsize:integer):boolean;
var
  n : integer;
  datasize: integer;
begin
  Result:=TRUE;
  if FEncodeMode<>_MEMORY then exit;
  EnterCriticalSection(FCSInfo);
  if readsize>0 then begin
    datasize:=(FWPtr-FRPtr) and (INITIAL_BUFSIZE-1);
    if datasize>=readsize then
      for n:=1 to readsize do begin
        pBuf[n-1]:=FPRingBuf[FRPtr];
        FRPtr:=(FRPtr+1) and (INITIAL_BUFSIZE-1);
      end
    else
       Result:=FALSE;
  end;
  LeaveCriticalSection(FCSInfo);
end;

// リングバッファからの読み出し(無条件読み出し。return=読みだしデータ数
function TGogoTea.ReadBufferEx(pBuf:PChar;readsize:integer):integer;
var
  n : integer;
  datasize,truereadsize: integer;
begin
  Result:=0;
  if FEncodeMode<>_MEMORY then exit;
  EnterCriticalSection(FCSInfo);
  if readsize>0 then begin
    datasize:=(FWPtr-FRPtr) and (INITIAL_BUFSIZE-1);
    if datasize<readsize then truereadsize:=datasize
                         else truereadsize:=readsize;
    for n:=1 to truereadsize do begin
        pBuf[n-1]:=FPRingBuf[FRPtr];
        FRPtr:=(FRPtr+1) and (INITIAL_BUFSIZE-1);
    end;
    if truereadsize<readsize then
        for n:=truereadsize to readsize-1 do pBuf[n]:=#0;
    Result:=truereadsize;
  end;
  LeaveCriticalSection(FCSInfo);
end;


procedure TGogoTea.QuerySettings;
var
  rinfo : PTagInfoPac;
  pac : TMcpInpdevUserFunc;
  LocalUnit : TSetOfUseUnit;
  i : integer;
begin
  if csDesigning in ComponentState then exit;
  if FIsEncoding or FIsDetected then begin
    raise EGogoTeaError.Create(EncodeErrMsg);
    exit;
  end;
  AbortReq:=FALSE;
  FEncodeResult:=MPGE_initializeWork;
  if FEncodeResult<>ME_NOERR then begin
    raise EGogoTeaError.Create('FPUがありません');
  end; // 初期化
  if FEncodeMode=_FILE then
    MPGE_setConfigure( MC_INPUTFILE, MC_INPDEV_FILE, DWORD(PChar(FSourceFile)))
  else begin
    with pac do begin
      pUserFunc:=@CALLBACKFUNC_GOGOTEA;
      nSize:=MC_INPDEV_MEMORY_NOSIZE;
      nBit:=16;
      nFreq:=44100;
      nChn:=2;
    end;
    MPGE_setConfigure( MC_INPUTFILE, MC_INPDEV_USERFUNC, DWORD(@pac));
  end;
  MPGE_setConfigure( MC_OUTPUTFILE, MC_OUTDEV_FILE, DWORD(PChar(FDestFile)));
  if FUsePsy then MPGE_setConfigure( MC_USEPSY, 1, 0)
             else MPGE_setConfigure( MC_USEPSY, 0, 0);
  MPGE_setConfigure( MC_BITRATE, FBitRate, 0);
  if FUseLPF16 then MPGE_setConfigure( MC_USELPF16, 1, 0)
               else MPGE_setConfigure( MC_USELPF16, 0, 0);
  if FUseVBR then MPGE_setConfigure( MC_VBR, FVBRLevel, 0);
  ///////// 適切に設定をしなおす。
  LocalUnit:=FSetOfUseUnit;
  if (UnitStates and tMMX)=0 then LocalUnit:=LocalUnit-[_USE_MMX];
  if (UnitStates and t3DN)=0 then LocalUnit:=LocalUnit-[_USE_3DNOW];
  if (UnitStates and tSSE)=0 then LocalUnit:=LocalUnit-[_USE_KNI];
  if (UnitStates and tE3DN)=0 then LocalUnit:=LocalUnit-[_USE_EX_3DNOW];
  if _USE_MMX in LocalUnit then MPGE_setConfigure( MC_USEMMX, 1, 0)
                           else MPGE_setConfigure( MC_USEMMX, 0, 0);
  if _USE_3DNOW in LocalUnit then MPGE_setConfigure( MC_USE3DNOW, 1, 0)
                             else MPGE_setConfigure( MC_USE3DNOW, 0, 0);
  if _USE_KNI in LocalUnit then MPGE_setConfigure( MC_USEKNI, 1, 0)
                           else MPGE_setConfigure( MC_USEKNI, 0, 0);
  if _USE_EX_3DNOW in LocalUnit then MPGE_setConfigure( MC_USEE3DNOW, 1, 0)
                                else MPGE_setConfigure( MC_USEE3DNOW, 0, 0);
  if _USE_SPC1 in LocalUnit then MPGE_setConfigure( MC_USESPC1, 1, 0)
                            else MPGE_setConfigure( MC_USESPC1, 0, 0);
  if _USE_SPC2 in LocalUnit then MPGE_setConfigure( MC_USESPC2, 1, 0)
                            else MPGE_setConfigure( MC_USESPC2, 0, 0);
  /////////
  case FEmphasis of
    _NO_EMPH     : MPGE_setConfigure(MC_EMPHASIS,MC_EMP_NONE,0);
    _5015MS_EMPH : MPGE_setConfigure(MC_EMPHASIS,MC_EMP_5015MS,0);
    _CCITT_EMPH  : MPGE_setConfigure(MC_EMPHASIS,MC_EMP_CCITT,0);
  end;
  case FEncodeType of
    _MONO : MPGE_setConfigure(MC_ENCODEMODE,MC_MODE_MONO,0);
    _STEREO : MPGE_setConfigure(MC_ENCODEMODE,MC_MODE_STEREO,0);
    _JOINT :  MPGE_setConfigure(MC_ENCODEMODE,MC_MODE_JOINT,0);
    _MSSTEREO : MPGE_setConfigure(MC_ENCODEMODE,MC_MODE_MSSTEREO,0);
    _DUALCHANNEL : MPGE_setConfigure(MC_ENCODEMODE,MC_MODE_DUALCHANNEL,0);
  end;
  MPGE_setConfigure(MC_CPU,FProcessors,0);
// 追加部分 v2.30以降
  if ((VersionCode and $ff00)>=$0200)
    and ((VersionCode and $00ff)>=$0030) then begin
    if FExtFilter.Enabled then
	  MPGE_setConfigure(MC_ENHANCEDFILTER,FExtFilter.Low,FExtFilter.High);
    if FVBRBitrate.Enabled and FUseVBR then
	  MPGE_setConfigure(MC_VBRBITRATE,FVBRBitrate.Low,FVBRBitrate.High);
    if FMSSetting.Enabled then
      MPGE_setConfigure(MC_MSTHRESHOLD,FMSSetting.Threshold,FMSSetting.Power);
  end;
  // フォーマット設定、RMP,ID3タグ登録(v.2.26)
  if ((VersionCode and $ff00)>=$0200)
    and ((VersionCode and $00ff)>=$0026) then begin
    case FEncodeFormat of
      _FORMAT_OF_RIFF_WAVE,
      _FORMAT_OF_RIFF_RMP :   begin
                    if FEncodeFormat=_FORMAT_OF_RIFF_WAVE then
                       MPGE_setConfigure(MC_OUTPUT_FORMAT,MC_OUTPUT_RIFF_WAVE,0)
                    else
  				       MPGE_setConfigure(MC_OUTPUT_FORMAT,MC_OUTPUT_RIFF_RMP,0);
    		        if Assigned(FRMPTagList) then begin
				  	  for i:=1 to FRMPTagList.Count do begin
					    rinfo:=PTagInfoPac(FRMPTagList.Items[i-1]);
					    MPGE_setConfigure(MC_RIFF_INFO,rinfo^.infosize,DWORD(rinfo^.infobody));
					  end;
    			    end;
    			  end;
      _FORMAT_OF_MP3      :   begin
  				    MPGE_setConfigure(MC_OUTPUT_FORMAT,MC_OUTPUT_NORMAL,0);
                    if FID3Tag.infosize>0 then begin
					  MPGE_setConfigure(MC_ADDTAG,FID3Tag.infosize,DWORD(FID3Tag.infobody));
    			    end;
    			  end;
    end;
  end;
  DisposeTags;
  try
    FEncodeResult:=MPGE_detectConfigure;
  except
    FEncodeResult:=ME_INTERNALERROR;    // 例外発生時はこれを返す。
  end;
  if FEncodeResult<>ME_NOERR then begin
    FIsDetected:=FALSE;
    raise EGogoTeaError.Create('設定に誤りがあります.');
  end
  else begin
    FRPtr:=0; FWPtr:=0;
    MPGE_getConfigure( MG_COUNT_FRAME, FAllFrames );
    FIsDetected:=TRUE;
  end;
end;

// エンコード開始
procedure TGogoTea.Execute;
begin
  if FEncodeMode=_FILE then QuerySettings;
  FEncodeResult:=ME_NOERR;
  FEncodeThread.Priority:=FPriority;
  FIsEncoding:=TRUE;    // 先に実行フラグを立てておく。
  PostThreadMessage(FEncodeThread.ThreadID,ETM_STARTENCODE,FAllFrames,FFrequency);
//  if FEncodeMode=_FILE then
//    PostThreadMessage(FEncodeThread.ThreadID,ETM_STARTENCODE,FAllFrames,FFrequency)
//  else
//    PostThreadMessage(FEncodeThread.ThreadID,ETM_STARTENCODE,FAllFrames,0); //メモリ圧縮では発行しない
end;

// エンコード中断
procedure TGogoTea.AbortEncode;
begin
  if FIsEncoding then  PostThreadMessage(FEncodeThread.ThreadID,ETM_ABORTENCODE,0,0);
end;

// 以下、単純設定。
procedure TGogoTea.SetSourceFile(SourceFile:string);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FSourceFile:=SourceFile;
end;

procedure TGogoTea.SetDestFile(Destination:string);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FDestFile:=Destination;
end;

procedure TGogoTea.SetProcessors(Processors:integer);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FProcessors:=Processors;
end;

procedure TGogoTea.SetEmphasis(Emphasis:TEmphasis);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FEmphasis:=Emphasis;
end;

procedure TGogoTea.SetSetOfUseUnit(SetOfUseUnit:TSetOfUseUnit);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FSetOfUseUnit:=SetOfUseUnit;
end;

procedure TGogoTea.SetEncodeType(EncodeType:TEncodeType);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FEncodeType:=EncodeType;
end;

procedure TGogoTea.SetBitrate(Bitrate:integer);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FBitrate:=Bitrate;
end;


procedure TGogoTea.SetExtFilter(ExtFilter:TExtFilter);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else
    with ExtFilter do begin
     if (Low<0) or (Low>100) or (High<0) or (High>100) then
       raise EGogoTeaError.Create(ParamErrMsg);
    FExtFilter.Assign(ExtFilter);
   end;
end;

procedure TGogoTea.SetVBRBitrate(VBRBitrate:TVBRBitrate);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else
    with VBRBitrate do begin
     if (Low<0)  or (High<0) then
       raise EGogoTeaError.Create(ParamErrMsg);
     FVBRBitrate.Assign(VBRBitrate);
   end;
end;

procedure TGogoTea.SetMSSetting(MSSetting:TMSSetting);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else with FMSSetting do begin
    if (Threshold<0) or (Threshold>100) 
      or (Power<0) or (Power>100) then raise EGogoTeaError.Create(ParamErrMsg)
    else FMSSetting.Assign(MSSetting);
  end;
end;

procedure TGogoTea.SetUsePsy(IsUsePsy:boolean);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FUsePsy:=IsUsePsy;
end;

procedure TGogoTea.SetEncodeMode(Mode:TEncodeMode);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FEncodeMode:=Mode;
end;

procedure TGogoTea.SetUseLPF16(IsUseLPF16:boolean);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FUseLPF16:=IsUseLPF16;
end;

procedure TGogoTea.SetUseVBR(IsUseVBR:boolean);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
                 else FUseVBR:=IsUseVBR;
end;

procedure TGogoTea.SetPriority(Priority:TThreadPriority);
begin
  FPriority:=Priority;
  if csDesigning in ComponentState then exit;
  if FEncodeThread<>NIL then FEncodeThread.Priority:=Priority;
end;

procedure TGogoTea.SetFrequency(Freq:integer);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else if Freq<1 then raise EGogoTeaError.Create(ParamErrMsg)
  else FFrequency:=Freq;
end;

procedure TGogoTea.SetEncodeFormat(form:TEncodeFormat);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else FEncodeFormat:=Form;
end;

procedure TGogoTea.SetVBRLevel(Level:integer);
begin
  if FIsEncoding then raise EGogoTeaError.Create(EncodeErrMsg)
  else if (Level<0) or (Level>9) then raise EGogoTeaError.Create(ParamErrMsg)
  else FVBRLevel:=Level;
end;

procedure TGogoTea.WndProc(var Msg:TMessage);
begin
  case Msg.msg of
    ESM_EMPTYSTREAM : begin end;
    ESM_ENCODEERROR : OnEncodeError(Msg);  // エンコードエラーイベント
    ESM_FINALENCODE : OnFinishEncode(Msg);  // 完了イベント発生
    ESM_PROGRESS : OnProgressStatus(Msg);  // 経過イベント
  else
    with Msg do Result := DefWindowProc(FHWnd, Msg, wParam, lParam);
  end
end;

procedure TGogoTea.OnEncodeError(Msg:TMessage);
begin
  FIsEncoding:=FALSE;   // エンコード中ﾌﾗｸﾞｸﾘｱ
  FIsDetected:=FALSE;  // 
  FEncodeResult:=Msg.WParam;    // ｴﾗｰｺｰﾄﾞ更新
  if Assigned(FOnError) then FOnError(Self,Msg.WParam);
end;

procedure TGogoTea.OnFinishEncode(Msg:TMessage);
begin
  FIsEncoding:=FALSE;   // エンコード中ﾌﾗｸﾞｸﾘｱ
  FIsDetected:=FALSE;  //
  if Assigned(FOnFinish) then FOnFinish(Self,Msg.WPARAM<>0);
end;

procedure TGogoTea.OnProgressStatus(Msg:TMessage);
begin
  if Assigned(FOnProgress) then FOnProgress(Self,Msg.WPARAM,Msg.LPARAM);
end;

function TGogoTea.GetVersionStr:string;
var
  buf : array [0..511] of Char;
  Code :Longint;
begin
  if csDesigning in ComponentState then begin
    Result:='';
    exit;
  end;
  if MPGE_getVersion(Code,@buf)=ME_NOERR then begin
    Result:=StrPas(buf);
  end
  else
    Result:='';
end;

function TGogoTea.GetVersionCode:DWORD;
var
  buf : array [0..511] of Char;
  Code : Longint;
begin
  if csDesigning in ComponentState then begin
    Result:=0;
    exit;
  end;
  if MPGE_getVersion(Code,@buf)=ME_NOERR then begin
    Result:=Code;
  end
  else
    Result:=0;
end;

function TGogoTea.GetUnitStates:DWORD;
var
  Code : Longint;
begin
  if csDesigning in ComponentState then begin
    Result:=0;
    exit;
  end;
  if MPGE_getUnitStates(Code)=ME_NOERR then begin
    Result:=Code;
  end
  else
    Result:=0;
end;

// Rapper
// unitstatus,versionなどはrappingしても使いようがないので
// Componentのプロパティでそのまま読んだ方がいいです(^^;
function TGogoTea.EncodeFrames:integer;
begin
  Result:=MPGE_processFrame;
end;

function TGogoTea.CloseEncoder:integer;
begin
  Result:=MPGE_closeCoder;
end;

function TGogoTea.EndEncoder:integer;
begin
  Result:=MPGE_endCoder;
end;

function TGogoTea.GetConfigure(Verb:DWORD;var PARAM1):integer;
begin
  Result:=MPGE_getConfigure( Verb, PARAM1 );
end;

function TGogoTea.SetConfigure(Verb:DWORD;PARAM1:DWORD;PARAM2:DWORD):integer;
begin
  Result:=MPGE_setConfigure(Verb,PARAM1,PARAM2);
end;

function TGogoTea.DetectConfigure:integer;
begin
  Result:=MPGE_detectConfigure;
end;

// コンポーネントの登録
procedure Register;
begin
  RegisterComponents('Samples', [TGogoTea]);
end;

end.
