//DirectInputによるジュイスティック・パッド・キーボード・マウスコンポーネント

unit Ddidex;

interface

uses  Windows, Messages, SysUtils, Classes, Controls, Forms, DInput, OLE2,
      D3DTypes, INIFiles, QDArc;


//デバッグ用変数(DDIDEX.DebugOptionプロパティで制御されます)
var
  DDID_HaltOnError:Boolean;        {エラーが起きたらすぐにHaltする}

  //以下の二つは、片方のみ有効、両方Trueの時はExceptOnErrorを優先する
  DDID_DebugMessage:Boolean;  {デバッグメッセージをファイルに書き出す}
  DDID_ExceptOnError:Boolean;     {エラーが起きたら例外を発生する}

//デバッグオプション
type TDDIDDebugOption = set of (dioHaltOnError, dioDebugMessage, dioExceptOnError);


const

  //ボタン番号
  DI_B1=0;
  DI_B2=1;
  DI_B3=2;
  DI_B4=3;
  DI_B5=4;
  DI_B6=5;
  DI_B7=6;
  DI_B8=7;
  DI_B9=8;
  DI_B10=9;
  DI_B11=10;
  DI_B12=11;
  DI_B13=12;
  DI_B14=13;
  DI_B15=14;
  DI_B16=15;
  DI_B17=16;
  DI_B18=17;
  DI_B19=18;
  DI_B20=19;
  DI_B21=20;
  DI_B22=21;
  DI_B23=22;
  DI_B24=23;
  DI_B25=24;
  DI_B26=25;
  DI_B27=26;
  DI_B28=27;
  DI_B29=28;
  DI_B30=29;
  DI_B31=30;
  DI_B32=31;

  DI_UP=32;
  DI_DOWN=33;
  DI_LEFT=34;
  DI_RIGHT=35;

  //キーボードのみ
  DI_UPLEFT=36;
  DI_UPRIGHT=37;
  DI_DOWNLEFT=38;
  DI_DOWNRIGHT=39;


  DI_KEYB = 0;
  DI_MOUSE = 1;
  DI_JOY1 = 2;
  DI_JOY2 = 3;
  DI_JOY3 = 4;
  DI_JOY4 = 5;
  DI_JOY5 = 6;
  DI_JOY6 = 7;
  DI_JOY7 = 8;
  DI_JOY8 = 9;
  DI_JOY9 = 10;
  DI_JOY10 = 11;
  DI_JOY11 = 12;
  DI_JOY12 = 13;
  DI_JOY13 = 14;
  DI_JOY14 = 15;
  DI_JOY15 = 16;
  DI_JOY16 = 17;

type
  EDDID = class(Exception);

//デバイスを特定するためのレコード
//type Integer{TDDIDDeviceType} = DI_KEYB .. DI_JOY16;

//スティックの状態を示すレコード
type TDDIDStick = record
  X,Y,Z,RX,RY,RZ:LongInt;
  Slider:Array[0..1] of LongInt;
  POV:Array[0..3] of DWord;
end;



type TDDIDEX = class(TComponent)
  private
    FAutoInitialize:Boolean;  //自動的に初期化を行う？

    FDInput:IDirectInput;
    FKeyBoard:IDirectInputDevice;
    FMouse:IDirectInputDevice;
    FJoyStickNames:TStrings;
    FJoySticks:Array[2..17] of IDirectInputDevice2;

    FKeyTbl:Array[0..1,0..39]of Integer;  //キーボードのどのキーを、ゲームパッドのどこと同じとみなすか
    FJoyState:DIJOYSTATE;                 //ジョイスティックの状態
    FJoyStickCount:Integer;               //つながっているジョイスティックの本数
    FIsFFJoy:Array [DI_KEYB..DI_JOY16] of Boolean;  //各デバイスがフォースフィードバック対応か

    FBackGround:Boolean;                  //バックグラウンドでも入力が受け取れる

    FEnumeratingFFJoy:Boolean;  //流れ制御用
    FDeadZone:Integer;          //デッドゾーンの大きさ
    FLastScaned:Integer{TDDIDDeviceType};
    //FIsFFJoyStick: Boolean;
    FDebugOption: TDDIDDebugOption;  //最後にスキャンしたデバイス
    function getStick:TDDIDStick;
    function getAStick:TDDIDStick;
    function getKeyAssign(iSurfix,iButton:UINT):Integer;
    function getButtons(idx:Integer):Boolean;
    function getJoySticks(idx:Integer{TDDIDDeviceType}):IDirectInputDevice2;
    function getAutoCenter(idx:Integer):Boolean;
    procedure setAutoCenter(idx:Integer; v:Boolean);
    procedure setKeyAssign(iSurfix,iButton:UINT; vkey:Integer);
    function PreScan(OrFlag:Boolean; Target:Integer{TDDIDDeviceType}):Boolean; //Scan,OrScanの下請け
    function FIsFFJoyStick(idx:Integer{TDDIDDeviceType}):Boolean;
    procedure SetDebugOption(const Value: TDDIDDebugOption);   //そのデバイスがFF対応か調べる
  public
    constructor Create(AOwner:TComponent); override;
    destructor Destroy; override;
    procedure Loaded; override;
    procedure Initialize; //初期化
    function Scan(Target:Integer{TDDIDDeviceType}):Boolean;        //あるデバイスの状態を、Stick,Buttonsプロパティに代入
    function OrScan(Targets:Array of Integer{TDDIDDeviceType}):Boolean;  //Orを取りつつ入力を得る


    property IsFFJoyStick[idx:Integer{TDDIDDeviceType}]:Boolean read FIsFFJoyStick;
{    procedure AddFF(Target:Integer{TDDIDDeviceType; fx:);  //エフェクトの登録
    procedure StartFF(Target:Integer{TDDIDDeviceType; iFx:Integer);   //エフェクトを開始
    procedure StopFF(Target:Integer{TDDIDDeviceType; iFx:Integer);    //エフェクトの停止
}
    property DInput:IDirectInput read FDInput;            //IDirectInputインターフェイス
    property Stick:TDDIDStick read getStick;              //スティックの状態
    property AStick:TDDIDStick read getAStick;            //スティックの状態(アナログ値)
    property KeyAssign[iSurfix,iButton:UINT]:Integer read getKeyAssign write setKeyAssign;  //キーボードとボタン・スティックの対応関係
    property JoyStickCount:Integer read FJoyStickCount;     //接続されているジョイスティックの台数
    property JoyStickNames:TStrings read FJoyStickNames;    //接続されているジョイスティックの名前
    property Buttons[idx:Integer]:Boolean read getButtons;  //ボタンの押下状態
    property JoySticks[idx:Integer{TDDIDDeviceType}]:IDirectInputDevice2 read getJoySticks;
    property KeyBoard:IDirectInputDevice read FKeyBoard;
    property AutoCenter[idx:Integer]:Boolean read getAutoCenter write setAutoCenter;  //オートセンター機能のON/OFF
  published
    property DebugOption:TDDIDDebugOption read FDebugOption write SetDebugOption;
    property DeadZone:Integer read FDeadZone write FDeadZone default 5; //デッドゾーンを1/100単位で指定
    property AutoInitialize:Boolean read FAutoInitialize write FAutoInitialize default True;
    property BackGround:Boolean read FBackGround write FBackGround default False;
end;



//FFXの種類
type TDDIDFFXStyle = (xsConstantForce, xsRampForce,
                      xsSquare,xsSine,xsTriangle,xsSawtoothUp,xsSawtoothDown,
                      xsSpring,xsDamper,xsInertia,xsFriction);

//FFXのカテゴリ
type TDDIDFFXCategory = (xcConstantForce,xcRampForce,xcPeriodic,xcCondition);




//FFX
type TDDIDFFX = class
  private
    FOwner:TDDIDEX;             //オブザーバ
    FEffect:IDirectInputEffect; //カプセル化の対象

    FApplied:Boolean;           //すでにいずれかのジョイスティックに乗っけられた

    constant:DICONSTANTFORCE;   //コンスタント・フォースの場合に使用
    ramp:DIRAMPFORCE;           //傾斜フォースの場合に使用
    periodic:DIPERIODIC;        //周期的フォースの場合に使用
    condition:Array[0..1] of DICONDITION;      //条件フォースの場合に使用 X,Yの二つ分

    env:DIENVELOPE;             //エンベロープ
    FAxes:Array[0..2] of DWord; //軸(X,Y,Z固定...現在は、X,Yの二つしか使ってない)
    FDirection:Array[0..2] of Integer;  //エフェクトの方向(デカルト座標)

    FFXGuid:TGuid;              //FFXのGUID;
    FFFXStyle:TDDIDFFXStyle;
    FFFXCategory:TDDIDFFXCategory;
    fx:DIEFFECT;

    //fxそのもののプロパティ用
    procedure SetDirection(v:TPoint);
    function  GetDirection:TPoint;
    procedure SetDuration(v:DWord);
    function  GetDuration:DWord;
    procedure SetGain(v:DWord);
    function  GetGain:DWord;
    procedure SetSamplePeriod(v:DWord);
    function  GetSamplePeriod:DWord;
    procedure SetTriggerButton(v:Integer);
    function  GetTriggerButton:Integer;
    procedure SetTriggerRepeatInterval(v:DWord);
    function  GetTriggerRepeatInterval:DWord;
    procedure SetFFFXStyle(v:TDDIDFFXStyle);

    //エンベロープ用
    procedure SetAttackLevel(v:DWord);
    function  GetAttackLevel:DWord;
    procedure SetAttackTime(v:DWord);
    function  GetAttackTime:DWord;
    procedure SetFadeLevel(v:DWord);
    function  GetFadeLevel:DWord;
    procedure SetFadeTime(v:DWord);
    function  GetFadeTime:DWord;

    //ConstantForce
    procedure SetMagnitude(v:Integer);
    function  GetMagnitude:Integer;
    //RampForce
    procedure SetStartMagnitude(v:Integer);
    function  GetStartMagnitude:Integer;
    procedure SetEndMagnitude(v:Integer);
    function  GetEndMagnitude:Integer;
    //Periodic
    procedure SetPhase(v:DWord);
    function  GetPhase:Dword;
    procedure SetPeriod(v:DWord);
    function  GetPeriod:Dword;
    procedure SetOffSet(v:Integer);
    function  GetOffSet:Integer;
    procedure SetDeltaMagnitude(v:DWord);
    function  GetDeltaMagnitude:DWord;
    //Condition
    procedure SetPositiveCoefficient(idx:Integer; v:Integer);
    function  GetPositiveCoefficient(idx:Integer):Integer;
    procedure SetNegativeCoefficient(idx:Integer; v:Integer);
    function  GetNegativeCoefficient(idx:Integer):Integer;
    procedure SetPositiveSaturation(idx:Integer; v:DWord);
    function  GetPositiveSaturation(idx:Integer):DWord;
    procedure SetNegativeSaturation(idx:Integer; v:DWord);
    function  GetNegativeSaturation(idx:Integer):DWord;
    procedure SetDeadBand(idx:Integer; v:Integer);
    function  GetDeadBand(idx:Integer):Integer;
    procedure SetConditionOffset(idx:Integer; v:Integer);
    function  GetConditionOffset(idx:Integer):Integer;
  public
    constructor Create(owner:TDDIDEX);
    destructor Destroy; override;

    procedure Apply(idx:Integer); //idxで指定される入力機器に、エフェクトをのっける
    procedure Unload;             //エフェクトを解除する

    procedure Start(iterate:Integer; exclusive:Boolean);  //エフェクトの再生
    procedure Stop;                                       //エフェクトの停止

    procedure LoadFromFile(fname:String);     //ファイルからエフェクトを読む
    procedure LoadFromQDA(qdaName,id:String); //QDAファイルからエフェクトを読む
    procedure SaveToFile(fname:String);   //ファイルにエフェクトを保存

    property Style:TDDIDFFXStyle read FFFXStyle write SetFFFXStyle;
    property Category:TDDIDFFXCategory read FFFXCategory;
    property Applied:Boolean  read FApplied;

    //DIEffect本体に関するもの
    property Direction:TPoint read GetDirection write SetDirection;
    property Duration:DWord read GetDuration write SetDuration;
    property Gain:DWord read GetGain write SetGain;
    property SamplePeriod:DWord read GetSamplePeriod write SetSamplePeriod;
    property TriggerButton:Integer read GetTriggerButton write SetTriggerButton;
    property TriggerRepeatInterval:DWord read GetTriggerRepeatInterval write SetTriggerRepeatInterval;


    //エンベロープ関係
    property AttackLevel:DWord  read GetAttackLevel write SetAttackLevel;
    property AttackTime:DWord   read GetAttackTime  write SetAttackTime;
    property FadeLevel:DWord    read GetFadeLevel   write SetFadeLevel;
    property FadeTime:DWord     read GetFadeTime    write SetFadeTime;

    //ConstantForce関係
    property Magnitude:Integer  read GetMagnitude   write SetMagnitude; //Periodicでも使うよ

    //RampForce関係
    property StartMagnitude:Integer read GetStartMagnitude write SetStartMagnitude;
    property EndMagnitude:Integer   read GetEndMagnitude   write SetEndMagnitude;

    //Periodic関係
    property Offset:Integer       read GetOffSet        write SetOffSet;
    property DeltaMagnitude:DWord read GetDeltaMagnitude write SetDeltaMagnitude;
    property Phase:DWord          read GetPhase         write SetPhase;
    property Period:DWord         read GetPeriod        write SetPeriod;

    //Condition関係
    property PositiveCoefficient[idx:Integer]:Integer  read GetPositiveCoefficient   write SetPositiveCoefficient;
    property NegativeCoefficient[idx:Integer]:Integer  read GetNegativeCoefficient   write SetNegativeCoefficient;
    property PositiveSaturation[idx:Integer]:DWord   read GetPositiveSaturation    write SetPositiveSaturation;
    property NegativeSaturation[idx:Integer]:DWord   read GetNegativeSaturation    write SetNegativeSaturation;
    property ConditionOffset[idx:Integer]:Integer read GetConditionOffSet  write SetConditionOffset;
    property DeadBand[idx:Integer]:Integer           read GetDeadBand              write SetDeadBand;

end;


procedure Register; //コンポーネントのたしなみ

implementation


var
  DebugCount:Integer = 0;
  ErrDDID:EDDID;       //例外オブジェクト


(****************************************************************************
  登録
 ****************************************************************************)
procedure Register;
begin
  RegisterComponents('Quadruple D',[TDDIDEX]);
end;

(******************************************************************************
  デバッグ用補助関数
******************************************************************************)
procedure PutDebugMessage(mes:String);
var
  F:TextFile;
  fname:String;
begin

  //デバッグメッセージの出力が禁止になっているなら、何もしない
  if DDID_DebugMessage then begin

    //EXEファイルのあるディレクトリのdddd.debugというファイルに一行書き込む
    fname:=ExtractFilePath(Application.Exename) + 'ddid.debug';

    //ファイルを開く
    AssignFile(F,fname);
    //もし、プログラムの実行後、はじめての書き込みなら、ファイルをクリア
    if debugCount = 0 then
      ReWrite(F)
    else
      Append(F);

    Inc(debugCount);

    //一行書き込む
    Writeln(F,Format('debug#%.5d ... %s',[debugCount,mes]));

    //ファイル閉じる
    CloseFile(F);

  end;

  
  //例外の発生がOnなら、例外を発生させる
  if DDID_ExceptOnError then begin
    ErrDDID:=EDDID.Create(mes);
    raise ErrDDID;
  end;

end;

function TRYDI(err:HRESULT; failMes:String):HRESULT;
begin
  result:=err;

  if err = DI_OK then
    exit;

	PutDebugMessage(failMes+Format('(Error Code(%x))',[err]));

  if DDID_HaltOnError then
    Halt; //安全のため、Haltしちゃう

end;


//フォースフィードバックエフェクトの列挙(未使用)
function EnumEffects(const ei:DIEFFECTINFO; arg:Pointer):BOOL; stdcall;
begin
  result:=True;
end;

//ジョイスティックの列挙
function EnumJoyStick( const id:DIDEVICEINSTANCE; arg:Pointer):BOOL; stdcall;
var
  caller:TDDIDEX; //呼び出し元オブジェクト
  dev1:IDirectInputDevice;
  i:Integer;
  dwFlags:DWord;
  procedure SetRange(obj:DWord);
  var
    range:DIPROPRANGE;
  begin
    with range do begin
      with diph do begin
        dwSize:=Sizeof(DIPropRange);
        dwHeaderSize:=Sizeof(DIPropHeader);
        dwObj:=obj;
        dwHow:=DIPH_BYOFFSET;
      end;
      lMin:=-32768;
      lMax:=32767;
    end;
    caller.FJoySticks[caller.FJoyStickCount+2].SetProperty(DIPROP_RANGE,@range);
  end;
begin
  result:=Bool(DIENUM_CONTINUE);

  caller:=arg;

  //いままでにFFジョイスティックとして作られたデバイスなら、飛ばす
  if not caller.FEnumeratingFFJoy then begin
    for i:=2 to caller.FJoyStickCount+1 do begin
      if caller.FIsFFJoy[i] And (caller.FJoyStickNames[i-2] = StrPas(id.tszProductName)) then
        Exit;
    end;
  end;

  //GUIDからデバイスを作る
  if caller.FDInput.CreateDevice(@id.guidInstance, dev1, Nil) <> DI_OK then
    Exit;

  //QIしてDirectInputDevice2に進化
  dev1.QueryInterFace(IID_IDirectInputDevice2, caller.FJoySticks[caller.FJoyStickCount+2]);
  dev1.Release;

  //データフォーマットの設定・協調レベルの設定
	TRYDI(caller.FJoySticks[caller.FJoyStickCount+2].SetDataFormat(@c_dfDIJoyStick),'DirectInputDeviceのデータフォーマット(ジョイスティック)の指定に失敗しました');

  //ジョイスティックは、FFの事を考えて、まずEXCLUSIVEで初期化(FFあり)し、失敗したらNONEXCLUSIVEで初期化

  if Caller.FBackGround then
    dwFlags:=DISCL_BACKGROUND
  else
    dwFlags:=DISCL_FOREGROUND;

	if caller.FJoySticks[caller.FJoyStickCount+2].SetCooperativeLevel( TForm(caller.Owner).Handle, DISCL_EXCLUSIVE Or dwFlags) <> DI_OK then
  	TRYDI(caller.FJoySticks[caller.FJoyStickCount+2].SetCooperativeLevel( TForm(caller.Owner).Handle, DISCL_NONEXCLUSIVE Or dwFlags),'DirectInputDeviceの協調レベルの設定に失敗しました');

  //レンジの設定
  setRange(DIJOFS_X);
  setRange(DIJOFS_Y);
  setRange(DIJOFS_Z);
  setRange(DIJOFS_RX);
  setRange(DIJOFS_RY);
  setRange(DIJOFS_RZ);
  setRange(DIJOFS_SLIDER(0));
  setRange(DIJOFS_SLIDER(1));

  //DDIDEXコンポーネントに格納
  caller.FJoySticks[caller.FJoyStickCount+2].Acquire();
  caller.FJoySticknames.Add(StrPas(id.tszProductName));
  if caller.FEnumeratingFFJoy then
    caller.FIsFFJoy[caller.FJoyStickCount+2]:=True
  else
    caller.FIsFFJoy[caller.FJoyStickCount+2]:=False;

  Inc(caller.FJoyStickCount);

end;

(******************************************************************************
  Create
******************************************************************************)
constructor TDDIDEX.Create(AOwner:TComponent);
var
  i,count:Integer;
begin
  inherited Create(AOwner);

  //二重貼りのチェック
  if (csDesigning in ComponentState) then begin
    count:=0;
    for i:=0 to Owner.ComponentCount-1 do begin
      if Owner.Components[i] is TDDIDEX then begin
        Inc(count);
      end;
    end;

    if count >=2 then begin
      raise Exception.Create('DDIDEXコンポーネントを、同一フォームに2つ以上貼る事はできません');
      exit;
    end;
  end;

  //プロパティの初期化
  DeadZone:=5;          //デフォルトのデッドゾーンを設定
  AutoInitialize:=True; //デフォルト値の設定

  BackGround:=False;
  DebugOption:=[dioHaltOnError];
end;

procedure TDDIDEX.Loaded;
begin
  inherited;

  if not (csDesigning in ComponentState) then begin
    DebugOption:=DebugOption;

    if AutoInitialize then
      Initialize;
  end;

end;

(******************************************************************************
  Destroy
******************************************************************************)
destructor TDDIDEX.Destroy;
var
  i:Integer;
begin

  if not (csDesigning in ComponentState) then begin
    if FKeyBoard <> Nil then begin
      FKeyBoard.Unacquire;
      FKeyBoard.Release;
    end;

    if FMouse <> Nil then begin
      FMouse.Unacquire;
      FMouse.Release;
    end;

    for i:=0 to FJoyStickCount-1 do begin
      FJoySticks[i+2].UnAcquire;
      FJoySticks[i+2].Release;
    end;

    if FDInput<>Nil then
      FDinput.Release;
  end;

  FJoyStickNames.Free;
  inherited Destroy;
end;


(******************************************************************************
  Initialize
******************************************************************************)
procedure TDDIDEX.Initialize;
var
  i,j:Integer;
  dwFlags:DWord;
begin

    //もし、既に初期化済みなら、既に存在するインターフェイスを取り消す。
    if FKeyBoard <> Nil then begin
      FKeyBoard.Unacquire;
      FKeyBoard.Release;
    end;

    if FMouse <> Nil then begin
      FMouse.Unacquire;
      FMouse.Release;
    end;

    for i:=0 to FJoyStickCount-1 do begin
      FJoySticks[i+2].UnAcquire;
      FJoySticks[i+2].Release;
    end;

    if FDInput<>Nil then
      FDinput.Release;


    //初期化スタート

    //キーアサインテーブルの初期化
    for j:=0 to 1 do
      for i:=0 to 39 do
        FKeyTbl[j,i]:=0;

    FKeyTbl[0,0]:=DIK_Z; //VK_Z;
    FKeyTbl[0,1]:=DIK_X; //VK_X;
    FKeyTbl[0,2]:=DIK_C; //VK_C;
    FKeyTbl[0,3]:=DIK_A; //VK_A;
    FKeyTbl[0,4]:=DIK_S; //VK_S;
    FKeyTbl[0,5]:=DIK_D; //VK_D;
    FKeyTbl[0,6]:=DIK_1; //VK_1;
    FKeyTbl[0,7]:=DIK_2; //VK_2;
    FKeyTbl[0,8]:=DIK_3; //VK_3;
    FKeyTbl[0,9]:=DIK_4; //VK_4;
    FKeyTbl[0,10]:=DIK_5; //VK_5;
    FKeyTbl[0,11]:=DIK_6; //VK_6;
    FKeyTbl[0,12]:=DIK_7; //VK_7;
    FKeyTbl[0,13]:=DIK_8; //VK_8;
    FKeyTbl[0,14]:=DIK_9; //VK_9;
    FKeyTbl[0,15]:=DIK_0; //VK_0;

    FKeyTbl[1,0]:=DIK_SPACE;
    FKeyTbl[1,1]:=DIK_ESCAPE;


    FKeyTbl[0,32]:=DIK_UP;
    FKeyTbl[0,33]:=DIK_DOWN;
    FKeyTbl[0,34]:=DIK_LEFT;
    FKeyTbl[0,35]:=DIK_RIGHT;

    FKeyTbl[1,32]:=DIK_NUMPAD8;
    FKeyTbl[1,33]:=DIK_NUMPAD2;
    FKeyTbl[1,34]:=DIK_NUMPAD4;
    FKeyTbl[1,35]:=DIK_NUMPAD6;
    FKeyTbl[1,36]:=DIK_NUMPAD7;
    FkeyTbl[1,37]:=DIK_NUMPAD9;
    FkeyTbl[1,38]:=DIK_NUMPAD1;
    FkeyTbl[1,39]:=DIK_NUMPAD3;


    //DirectInputの初期化
    TRYDI(DirectInputCreate(HInstance,DIRECTINPUT_VERSION,FDInput,Nil),'DirectInputの作成に失敗しました');

    //キーボードの初期化
    TRYDI(FDInput.CreateDevice( @GUID_SysKeyboard, FKeyBoard, Nil),'DirectInputDevice(キーボード)の作成に失敗しました');
    TRYDI(FKeyBoard.SetDataFormat(@c_dfDIKeyboard),'DirectInputDeviceのデータフォーマット(キーボード)の指定に失敗しました');

    if FBackGround then
      dwFlags:=DISCL_NONEXCLUSIVE Or DISCL_BACKGROUND
    else
      dwFlags:=DISCL_NONEXCLUSIVE Or DISCL_FOREGROUND;

    TRYDI(FKeyBoard.SetCooperativeLevel( TForm(Owner).Handle, dwFlags),'DirectInputDeviceの協調レベルの設定に失敗しました');


    //マウスの初期化
    TRYDI(FDInput.CreateDevice( @GUID_SysMouse, FMouse, Nil),'DirectInputDevice(マウス)の作成に失敗しました');
    TRYDI(FMouse.SetDataFormat(@c_dfDIMouse),'DirectInputDeviceのデータフォーマット(マウス)の指定に失敗しました');
    TRYDI(FMouse.SetCooperativeLevel( TForm(Owner).Handle,DISCL_NONEXCLUSIVE Or DISCL_FOREGROUND),'DirectInputDeviceの協調レベルの設定に失敗しました');

    //ジョイスティックの初期化
    For i:=DI_KEYB to DI_JOY16 do
      FIsFFJoy[i]:=False;

    FJoyStickNames:=TStringList.Create;
    FJoyStickCount:=0;

    //FF付きをまず列挙
    FEnumeratingFFJoy:=True;
    FDInput.EnumDevices(DIDEVTYPE_JOYSTICK,EnumJoyStick, Self, DIEDFL_ATTACHEDONLY Or DIEDFL_FORCEFEEDBACK);
    //そうでないのを列挙
    FEnumeratingFFJoy:=False;
    FDInput.EnumDevices(DIDEVTYPE_JOYSTICK,EnumJoyStick, Self, DIEDFL_ATTACHEDONLY);

    FKeyBoard.Acquire();
    FMouse.Acquire();
end;


(******************************************************************************
  DebugOptionの変更
******************************************************************************)
procedure TDDIDEX.SetDebugOption(const Value: TDDIDDebugOption);
begin
  FDebugOption := Value;

  DDID_DebugMessage := dioDebugMessage in Value;
  DDID_ExceptOnError := dioExceptOnError in Value;
  DDID_HaltOnError := dioHaltOnError in Value;
end;


(******************************************************************************
  Scan
******************************************************************************)
function TDDIDEX.PreScan(OrFlag:Boolean; Target:Integer{TDDIDDeviceType}):Boolean; //Scan,OrScanの下請け
var
  i:Integer;
  KeyBuf:Array[0..255] of Byte;
  ms:DIMOUSESTATE;
  js:DIJOYSTATE;
  kx,ky:Integer;  //キーボード用入力変数
  l_thre,h_thre:Integer;  //デッドソーン判定用
  function IsPressed(i:Integer):Boolean;
  begin
    result := (KeyBuf[FKeyTbl[0,i]] Or KeyBuf[FKeyTbl[1,i]]) >= $80;
  end;

  //DeadZoneに入ってたら、0にして返す
  //DeadZoneの外の値は、0〜32768の範囲にする
  function filtering(v:Integer):Integer;
  begin
    if FDeadZone = 0 then begin
      result:=v;
      exit;
    end;

    if (v >= l_thre) And (v <= h_thre) Or (FDeadZone >= 100) then
      result:=0
    else if v < 0 then begin
      result:= (v - l_thre) * 100 div (100-FDeadZone)
    end else begin
      result:= (v - h_thre) * 100 div (100-FDeadZone);
    end;
  end;
begin
  result:=True;

  FLastScaned:=Target;

  if not OrFlag then
    ZeroMemory(@FJoyState,sizeof(FJoyState));

  Case Target of
    //キーボード
    DI_KEYB: begin
      if FKeyBoard = Nil then begin
        result:=False;
        Exit;
      end;

      if (FKeyBoard.GetDeviceState(256,@KeyBuf) <> DI_OK) then begin
        FKeyBoard.Acquire();
        if (FKeyBoard.GetDeviceState(256,@KeyBuf) <> DI_OK) then begin
          result:=False;
          Exit;
        end;
      end;

      //ボタンの状態を得る
      for i:=0 to 31 do
        FJoyState.rgbButtons[i]:=FJoyState.rgbButtons[i] Or KeyBuf[FKeyTbl[0,i]] Or KeyBuf[FKeyTbl[1,i]];

      //スティックの状態を得る
      kx:=0;  ky:=0;
      if isPressed(32) Or isPressed(36) Or isPressed(37) then //上
        Dec(ky,$7FFF);
      if isPressed(33) Or isPressed(38) Or isPressed(39) then //下
        Inc(ky,$7FFF);
      if isPressed(34) Or isPressed(36) Or isPressed(38) then //左
        Dec(kx,$7FFF);
      if isPressed(35) Or isPressed(37) Or isPressed(39) then //右
        Inc(kx,$7FFF);

      with FJoyState do begin
        lX:=lX Or kx;
        lY:=lY Or ky;
      end;

    end;

    //マウス
    DI_MOUSE: begin
      if FMouse = Nil then begin
        result:=False;
        Exit;
      end;

      if (FMouse.GetDeviceState(sizeof(ms),@ms) <> DI_OK) then begin
        FMouse.Acquire();
        result:=False;
        Exit;
      end;

      FJoyState.lX:=ms.lX;
      FJoyState.lY:=ms.lY;
      FJoyState.lZ:=ms.lZ;
      For i:=0 to 3 do
        FJoyState.rgbButtons[i]:=FJoyState.rgbButtons[i] Or ms.rgbButtons[i];
    end;

    //ジョイスティック
    else begin
      //しきい値の計算
      l_thre:=-32768 * FDeadZone div 100;
      h_thre:=32767 * FDeadZone div 100;

      if FJoySticks[target] = Nil then begin
        result:=False;
        Exit;
      end;

      FJoySticks[target].Poll();
      if (FJoySticks[target].GetDeviceState(sizeof(js),@js) <> DI_OK) then begin
        FJoySticks[target].Acquire();
        result:=False;
        Exit;
      end;

      //デッドゾーンによるフィルタリング
      with js do begin
        lX:=filtering(lX);
        lY:=filtering(lY);
        lZ:=filtering(lZ);
        lrX:=filtering(lrX);
        lrY:=filtering(lrY);
        lrZ:=filtering(lrZ);
        rglSlider[0]:=filtering(rglSlider[0]);
        rglSlider[1]:=filtering(rglSlider[1]);
      end;

      if OrFlag then begin
        //Orモードなら、Orをとらなきゃ
        with FJoyState do begin
          lX:=lX Or js.lX;
          lY:=lY Or js.lY;
          lZ:=lZ Or js.lZ;
          lRx:=lRx Or js.lRx;
          lRy:=lRy Or js.lRy;
          lRz:=lRz Or js.lRz;
          For i:=0 to 1 do
            rglSlider[i]:=rglSlider[i] Or js.rglSlider[i];
          For i:=0 to 3 do
            rgdwPOV[i]:=rgdwPOV[i] Or js.rgdwPOV[i];
          For i:=0 to 31 do
            rgbButtons[i]:=rgbButtons[i] Or js.rgbButtons[i];

        end;
      end else begin
        //そうでないなら上書き
        FJoyState:=js;
      end;
    end;

  end;

end;

//単純にスキャン
function TDDIDEX.Scan(Target:Integer{TDDIDDeviceType}):Boolean;
begin
  result:=PreScan(False,Target);
end;

//Orをとりつつスキャン
function TDDIDEX.OrScan(Targets:array of Integer{TDDIDDeviceType}):Boolean;
var
  i:Integer;
begin

  result:=False;

  For i:=Low(Targets) to High(Targets) do begin
    if i = Low(Targets) then
      result:=PreScan(False,Targets[i])
    else
      result:=PreScan(True,Targets[i]) Or result;
  end;
end;



function TDDIDEX.getAStick:TDDIDStick;
begin
  with FJoyState,result do begin
    X:=lX;
    Y:=lY;
    Z:=lZ;
    RX:=lRx;
    RY:=lRy;
    RZ:=lRz;
    Slider[0]:=rglSlider[0];
    Slider[1]:=rglSlider[1];
    POV[0]:=rgdwPOV[0];
    POV[1]:=rgdwPOV[1];
    POV[2]:=rgdwPOV[2];
    POV[3]:=rgdwPOV[3];
  end;
end;

function TDDIDEX.getStick:TDDIDStick;
  function filtering(v:Integer):Integer;  //-1,0,1の三通りの値にする
  begin
    if v = 0 then
      result:=0
    else if v<0 then
      result:=-1
    else
      result:=1;
  end;
begin

  with FJoyState,result do begin
    X:=filtering(lX);
    Y:=filtering(lY);
    Z:=filtering(lZ);
    RX:=filtering(lRx);
    RY:=filtering(lRy);
    RZ:=filtering(lRz);
    Slider[0]:=filtering(rglSlider[0]);
    Slider[1]:=filtering(rglSlider[1]);
    POV[0]:=rgdwPOV[0];
    POV[1]:=rgdwPOV[1];
    POV[2]:=rgdwPOV[2];
    POV[3]:=rgdwPOV[3];
  end;
end;

function TDDIDEX.getButtons(idx:Integer):Boolean;
begin
  result:=FJoyState.rgbButtons[idx] >= $80;
end;


//キーアサイン
function TDDIDEX.getKeyAssign(iSurfix,iButton:UINT):Integer;
begin
  result:=FKeyTbl[iSurfix,iButton];
end;

procedure TDDIDEX.setKeyAssign(iSurfix,iButton:UINT; vkey:Integer);
begin
  FKeyTbl[iSurfix,iButton]:=vkey;
end;

//idxで示されるデバイスが、FFジョイスティックか調べる
function TDDIDEX.FIsFFJoyStick(idx:Integer{TDDIDDeviceType}):Boolean;
begin
  result:=False;
  if (idx < 0) Or (idx >= JoyStickCount+2) then
    Exit;
  result:=FIsFFJoy[idx];
end;

//idx台目のジョイスティックを得る
function TDDIDEx.getJoySticks(idx:Integer{TDDIDDeviceType}):IDirectInputDevice2;
begin
  if (idx < 2) Or (idx>=FJoyStickCount+2) then begin
    PutDebugMessage('無効なジョイスティック番号です。');
    result:=Nil;
    Exit;
  end;
  result:=FJoySticks[idx];
end;

//オートセンター
function TDDIDEX.getAutoCenter(idx:Integer):Boolean;
var
  dp:DIPROPDWORD;
begin

  //FF非対応ジョイスティックなら、必ずFalse
  if not IsFFJoyStick[idx] then begin
    result:=False;
    Exit;
  end;


  with dp do begin
    with diph do begin
      dwSize:=sizeof(dp);
      dwHeaderSize:=sizeof(DIPROPHEADER);
      dwObj:=0;
      dwHow:=DIPH_DEVICE;
    end;
  end;

  FJoySticks[idx].GetProperty(DIPROP_AUTOCENTER, @dp.diph);

  result:= (dp.dwData = DIPROPAUTOCENTER_ON);
end;

procedure TDDIDEX.setAutoCenter(idx:Integer; v:Boolean);
var
  dp:DIPROPDWORD;
begin
  //FF非対応ジョイスティックなら、なにもしない
  if not IsFFJoyStick[idx] then
    Exit;

  FJoySticks[idx].UnAcquire;

  with dp do begin
    with diph do begin
      dwSize:=sizeof(dp);
      dwHeaderSize:=sizeof(DIPROPHEADER);
      dwObj:=0;
      dwHow:=DIPH_DEVICE;
    end;

    if v then
      dwData:=DIPROPAUTOCENTER_ON
    else
      dwData:=DIPROPAUTOCENTER_OFF;
  end;

  FJoySticks[idx].SetProperty(DIPROP_AUTOCENTER, @dp.diph);

  FJoySticks[idx].Acquire;
end;




(******************************************************************************
  FFX
******************************************************************************)
constructor TDDIDFFX.Create(owner:TDDIDEX);
var
  i:Integer;
begin
  inherited Create;

  //メンバの初期化
  FOWner:=owner;
  FApplied:=False;
  FEffect:=Nil;

  ZeroMemory(@fx,sizeof(fx));

  Style:=xsSine;  //デフォルトはサイン波

  FAxes[0]:=DIJOFS_X;
  FAxes[1]:=DIJOFS_Y;
  FAxes[2]:=DIJOFS_Z;

  FDirection[0]:=0;   //X軸の向き
  FDirection[1]:=-1;  //Y軸の向き
  FDirection[2]:=0;   //Z軸の向き


  with fx do begin
    dwSize:=sizeof(fx);
    dwFlags:=DIEFF_CARTESIAN Or DIEFF_OBJECTOFFSETS;
    dwDuration:=INFINITE;
    dwSamplePeriod:=0;
    dwGain:=DI_FFNOMINALMAX;
    dwTriggerButton:=DIEB_NOTRIGGER;
    dwTriggerRepeatInterval:=0;
    cAxes:=2;
    rgdwAxes:=@FAxes;
    rglDirection:=@FDirection;
    if FFFXCategory = xcCondition then
      lpEnvelope:=Nil
    else
      lpEnvelope:=@env;
  end;

  //エンベロープの初期化
  with env do begin
    dwSize:=sizeof(DIENVELOPE);
    dwAttackLevel:=0;
    dwAttackTime:=0;  //DI_SECONDS div 2;
    dwFadeLevel:=0;
    dwFadeTime:=0; //DI_SECONDS;
  end;

  //カテゴリ別の中身の初期化
  constant.lMagnitude:=DI_FFNOMINALMAX;

  ramp.lStart:=0;
  ramp.lEnd:=DI_FFNOMINALMAX;

  periodic.dwMagnitude:=DI_FFNOMINALMAX;
  periodic.lOffset:=0;
  periodic.dwPhase:=0;
  periodic.dwPeriod:=DI_SECONDS div 8;

  for i:=0 to 1 do begin
    condition[i].lOffset:=0;
    condition[i].lPositiveCoefficient:=DI_FFNOMINALMAX;
    condition[i].lNegativeCoefficient:=DI_FFNOMINALMAX;
    condition[i].dwPositiveSaturation:=DI_FFNOMINALMAX;
    condition[i].dwNegativeSaturation:=DI_FFNOMINALMAX;
    condition[i].lDeadBand:=0;
  end;

end;

destructor TDDIDFFX.Destroy;
begin
  if FEffect <> Nil then
    FEffect.Release;

  inherited;
end;


//idxで指定される入力機器に、エフェクトをのっける
procedure TDDIDFFX.Apply(idx:Integer);
begin

  //FF対応ジョイスティック以外に載せろって言われてもねぇ、お客さん
  if not FOwner.IsFFJoyStick[idx] then
    Exit;

  //すでに他のデバイスに乗せられているエフェクトは、もう使えない。
  if FApplied then begin
    TRYDI(1,'エフェクトオブジェクトは、すでに他のジョイスティックで使われています');
    exit;
  end;

  TRYDI(FOwner.JoySticks[idx].CreateEffect(@FFXGuid, @fx, FEffect, Nil),'フォースフィードバックエフェクトの作成に失敗しました');

  FApplied:=True;
end;


procedure TDDIDFFX.Unload;
begin

  if not FApplied then
    exit;

  if FEffect<>Nil then
    FEffect.Unload;

  FEffect.Release;
  FEffect:=Nil;
  FApplied:=False;
end;


//エフェクトの再生
procedure TDDIDFFX.Start(iterate:Integer; exclusive:Boolean);
begin
  if (FEffect <> Nil) And FApplied then begin
    if exclusive then
      FEffect.Start(iterate,DIES_NODOWNLOAD Or DIES_SOLO)
    else
      FEffect.Start(iterate,DIES_NODOWNLOAD);
  end;
end;

//エフェクトの停止
procedure TDDIDFFX.Stop;
begin
  if (FEffect <> Nil) And FApplied then begin
    FEffect.Stop;
  end;

end;


//ファイルからエフェクトを読む
procedure TDDIDFFX.LoadFromFile(fname:String);
var
  ini:TIniFile;
  i:Integer;
begin
  if FApplied then begin
    TRYDI(1,'Applyした後でLoadFromFileを呼ぶことは出来ません、一旦、Unloadしてください');
    exit;
  end;

  //パスを付けないと、Windowsディレクトリを読んじゃうんだよね〜
  if ExtractFilePath(fname) = '' then
    fname:=ExtractFilePath(Application.ExeName) + fname;

  //ファイルがあるか、チェック
  if not FileExists(fname) then begin
    TRYDI(1,'ファイル ' + fname + ' は、開けません');
    exit;
  end;

  ini:=TINIFile.Create(fname);


  //ini.ReadInteger('general', 'version', 140);

  case ini.ReadInteger('general','style',Ord(xsSine)) of
    Ord(xsConstantForce):   Style:=xsConstantForce;
    Ord(xsRampForce):       Style:=xsRampForce;
    Ord(xsSquare):          Style:=xsSquare;
    Ord(xsSine):            Style:=xsSine;
    Ord(xsTriangle):        Style:=xsTriangle;
    Ord(xsSawtoothUp):      Style:=xsSawtoothUp;
    Ord(xsSawtoothDown):    Style:=xsSawtoothDown;
    Ord(xsSpring):          Style:=xsSpring;
    Ord(xsDamper):          Style:=xsDamper;
    Ord(xsInertia):         Style:=xsInertia;
    Ord(xsFriction):        Style:=xsFriction;
  end;

  Direction:=Point(ini.ReadInteger('general','directionX',0),ini.ReadInteger('general','directionY',-1));


  fx.dwDuration:=ini.ReadInteger('fx','dwDuration',INFINITE);
  fx.dwSamplePeriod:=ini.ReadInteger('fx','dwSamplePeriod',0);
  fx.dwGain:=ini.ReadInteger('fx','dwGain',DI_FFNOMINALMAX);
  fx.dwTriggerButton:=ini.ReadInteger('fx','dwTriggerButton',DIEB_NOTRIGGER);
  fx.dwTriggerRepeatInterval:=ini.ReadInteger('fx','dwTriggerRepeatInterval',0);

  env.dwAttackLevel:=ini.ReadInteger('envelope','dwAttackLevel',0);
  env.dwAttackTime:=ini.ReadInteger('envelope','dwAttackTime',0);
  env.dwFadeLevel:=ini.ReadInteger('envelope','dwFadeLevel',0);
  env.dwFadeTime:=ini.ReadInteger('envelope','dwFadeTime',0);

  constant.lMagnitude:=ini.ReadInteger('constantForce','lMagnitude',DI_FFNOMINALMAX);

  ramp.lStart:=ini.ReadInteger('rampForce','lStart',0);
  ramp.lEnd:=ini.ReadInteger('rampForce','lEnd',DI_FFNOMINALMAX);

  periodic.dwMagnitude:=ini.ReadInteger('periodic','dwMagnitude',DI_FFNOMINALMAX);
  periodic.lOffset:=ini.ReadInteger('periodic','lOffset',0);
  periodic.dwPhase:=ini.ReadInteger('periodic','dwPhase',0);
  periodic.dwPeriod:=ini.ReadInteger('periodic','dwPeriod',DI_SECONDS div 8);

  For i:=0 to 1 do begin
    condition[i].lOffset:=ini.ReadInteger('condition' + IntToStr(i),'lOffset',0);
    condition[i].lPositiveCoefficient:=ini.ReadInteger('condition' + IntToStr(i),'lPositiveCoefficient',DI_FFNOMINALMAX);
    condition[i].lNegativeCoefficient:=ini.ReadInteger('condition' + IntToStr(i),'lNegativeCoefficient',DI_FFNOMINALMAX);
    condition[i].dwPositiveSaturation:=ini.ReadInteger('condition' + IntToStr(i),'dwPositiveSaturation',DI_FFNOMINALMAX);
    condition[i].dwNegativeSaturation:=ini.ReadInteger('condition' + IntToStr(i),'dwNegativeSaturation',DI_FFNOMINALMAX);
    condition[i].lDeadBand:=ini.ReadInteger('condition' + IntToStr(i),'lDeadBand',0);
  end;

  ini.Free;
end;

procedure TDDIDFFX.LoadFromQDA(qdaName,id:String); //QDAファイルからエフェクトを読む
var
  TempPath:Array[0..MAX_PATH] of Char;
  TempName:Array[0..MAX_PATH] of Char;
  ms:TMemoryStream; //解凍結果を入れたストリーム
  fs:TFileStream;
  err:EDDID;
begin
  try
    ms:=ExtractFromQDAFile(QDAName,id);
  except
    err:=EDDID.Create(Format('アーカイブファイル %s からの %s のオープンに失敗しました',[QDAName,id]));
    raise err;
  end;

  //テンポラリファイルの作成
  try
    GetTempPath(MAX_PATH,@TempPath);
    GetTempFileName(@TempPath,PChar('tmp'),0,@TempName);

    fs:=TFileStream.Create(StrPas(TempName), fmCreate);
    fs.Write(ms.Memory^,ms.Size);
    fs.Free;
  except
    err:=EDDID.Create(Format('FFXデータの展開に失敗しました',[QDAName,id]));
    raise err;
  end;

  ms.Free;
  //テンポラリから読みこむ
  LoadFromFile(TempName);

  //テンポラリの削除
  DeleteFile(StrPas(TempName));
end;



//ファイルにエフェクトを保存
procedure TDDIDFFX.SaveToFile(fname:String);
var
  ini:TIniFile;
  i:Integer;
begin
  //パスを付けないと、Windowsディレクトリにできちゃうんだよね〜
  if ExtractFilePath(fname) = '' then
    fname:=ExtractFilePath(Application.ExeName) + fname;

  ini:=TINIFile.Create(fname);

  ini.WriteInteger('general', 'version', 140);

  ini.WriteInteger('general','style',Ord(Style));
  ini.WriteInteger('general','directionX',Direction.X);
  ini.WriteInteger('general','directionY',Direction.Y);


  ini.WriteInteger('fx','dwDuration',fx.dwDuration);
  ini.WriteInteger('fx','dwSamplePeriod',fx.dwSamplePeriod);
  ini.WriteInteger('fx','dwGain',fx.dwGain);
  ini.WriteInteger('fx','dwTriggerButton',fx.dwTriggerButton);
  ini.WriteInteger('fx','dwTriggerRepeatInterval',fx.dwTriggerRepeatInterval);

  ini.WriteInteger('envelope','dwAttackLevel',env.dwAttackLevel);
  ini.WriteInteger('envelope','dwAttackTime',env.dwAttackTime);
  ini.WriteInteger('envelope','dwFadeLevel',env.dwFadeLevel);
  ini.WriteInteger('envelope','dwFadeTime',env.dwFadeTime);

  ini.WriteInteger('constantForce','lMagnitude',constant.lMagnitude);

  ini.WriteInteger('rampForce','lStart',ramp.lStart);
  ini.WriteInteger('rampForce','lEnd',ramp.lEnd);

  ini.WriteInteger('periodic','dwMagnitude',periodic.dwMagnitude);
  ini.WriteInteger('periodic','lOffset',periodic.lOffset);
  ini.WriteInteger('periodic','dwPhase',periodic.dwPhase);
  ini.WriteInteger('periodic','dwPeriod',periodic.dwPeriod);

  For i:=0 to 1 do begin
    ini.WriteInteger('condition' + IntToStr(i),'lOffset',condition[i].lOffset);
    ini.WriteInteger('condition' + IntToStr(i),'lPositiveCoefficient',condition[i].lPositiveCoefficient);
    ini.WriteInteger('condition' + IntToStr(i),'lNegativeCoefficient',condition[i].lNegativeCoefficient);
    ini.WriteInteger('condition' + IntToStr(i),'dwPositiveSaturation',condition[i].dwPositiveSaturation);
    ini.WriteInteger('condition' + IntToStr(i),'dwNegativeSaturation',condition[i].dwNegativeSaturation);
    ini.WriteInteger('condition' + IntToStr(i),'lDeadBand',condition[i].lDeadBand);
  end;


  ini.Free;
end;


(******************************************************************************
  怒涛のプロパティ連動関数郡
******************************************************************************)



procedure TDDIDFFX.SetDirection(v:TPoint);
begin
  FDirection[0]:=v.X;
  FDirection[1]:=v.Y;
  //FDirection[2]:=v.Z;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_DIRECTION);
  end;
end;


function  TDDIDFFX.GetDirection:TPoint;
begin

  result.X:=FDirection[0];
  result.Y:=FDirection[1];
  //result.Z:=FDirection[2];
end;


procedure TDDIDFFX.SetDuration(v:DWord);
begin
  fx.dwDuration:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_DURATION);
  end;
end;

function  TDDIDFFX.GetDuration:DWord;
begin
  result:=fx.dwDuration;
end;


procedure TDDIDFFX.SetGain(v:DWord);
begin
  fx.dwGain:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_GAIN);
  end;
end;

function  TDDIDFFX.GetGain:DWord;
begin
  result:=fx.dwGain;
end;

procedure TDDIDFFX.SetSamplePeriod(v:DWord);
begin
  fx.dwSamplePeriod:=v;
  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_SAMPLEPERIOD);
  end;
end;

function  TDDIDFFX.GetSamplePeriod:DWord;
begin
  result:=fx.dwSamplePeriod;
end;

procedure TDDIDFFX.SetTriggerButton(v:Integer);
var
  ofs:DWORD;
begin
  case v of
    0..31:  ofs:=DIJOFS_BUTTON(v);
  else
    ofs:=DIEB_NOTRIGGER;
  end;

  fx.dwTriggerButton:=ofs;
  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TRIGGERBUTTON);
  end;
end;

function  TDDIDFFX.GetTriggerButton:Integer;
var
  i:Integer;
begin
  for i:=0 to 31 do begin
    if fx.dwTriggerButton = DIJOFS_BUTTON(i) then begin
      result:=i;
      exit;
    end;
  end;
  result:=-1;
end;

procedure TDDIDFFX.SetTriggerRepeatInterval(v:DWord);
begin
  fx.dwTriggerRepeatInterval:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TRIGGERREPEATINTERVAL);
  end;
end;

function  TDDIDFFX.GetTriggerRepeatInterval:DWord;
begin
  result:=fx.dwTriggerRepeatInterval;
end;

procedure TDDIDFFX.SetFFFXStyle(v:TDDIDFFXStyle);
begin
  FFFXStyle:=v;

  if FApplied then begin
    TRYDI(1,'Applyした後でStyleオブジェクトを変更することは出来ません、Styleを変更する前に、Unloadしてください');
    //FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;

  with fx do begin
    case FFFXStyle of
      xsConstantForce:  begin FFXGuid:=GUID_ConstantForce;  FFFXCategory:=xcConstantForce;    end;
      xsRampForce:      begin FFXGuid:=GUID_RampForce;      FFFXCategory:=xcRampForce;        end;
      xsSquare:         begin FFXGuid:=GUID_Square;         FFFXCategory:=xcPeriodic;         end;
      xsSine:           begin FFXGuid:=GUID_Sine;           FFFXCategory:=xcPeriodic;         end;
      xsTriangle:       begin FFXGuid:=GUID_Triangle;       FFFXCategory:=xcPeriodic;         end;
      xsSawtoothUp:     begin FFXGuid:=GUID_SawtoothUp;     FFFXCategory:=xcPeriodic;         end;
      xsSawtoothDown:   begin FFXGuid:=GUID_SawtoothDown;   FFFXCategory:=xcPeriodic;         end;
      xsSpring:         begin FFXGuid:=GUID_Spring;         FFFXCategory:=xcCondition;        end;
      xsDamper:         begin FFXGuid:=GUID_Damper;         FFFXCategory:=xcCondition;        end;
      xsInertia:        begin FFXGuid:=GUID_Inertia;        FFFXCategory:=xcCondition;        end;
      xsFriction:       begin FFXGuid:=GUID_Friction;       FFFXCategory:=xcCondition;        end;
    end;
    case FFFXCategory of
      xcConstantForce: begin cbTypeSpecificParams:=sizeof(DICONSTANTFORCE);   lpvTypeSpecificParams:=@constant;   end;
      xcRampForce:     begin cbTypeSpecificParams:=sizeof(DIRAMPFORCE);       lpvTypeSpecificParams:=@ramp;       end;
      xcPeriodic:      begin cbTypeSpecificParams:=sizeof(DIPERIODIC);        lpvTypeSpecificParams:=@periodic;   end;
      xcCondition:     begin cbTypeSpecificParams:=sizeof(DICONDITION);       lpvTypeSpecificParams:=@condition;  end;
    end;
  end;


end;






//エンベロープ用
procedure TDDIDFFX.SetAttackLevel(v:DWord);
begin
  env.dwAttackLevel:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_ENVELOPE);
  end;
end;

function  TDDIDFFX.GetAttackLevel:DWord;
begin
  result:=env.dwAttackLevel;
end;

procedure TDDIDFFX.SetAttackTime(v:DWord);
begin
  env.dwAttackTime:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_ENVELOPE);
  end;
end;

function  TDDIDFFX.GetAttackTime:DWord;
begin
  result:=env.dwAttackTime;
end;

procedure TDDIDFFX.SetFadeLevel(v:DWord);
begin
  env.dwFadeLevel:=v;
  
  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_ENVELOPE);
  end;
end;

function  TDDIDFFX.GetFadeLevel:DWord;
begin
  result:=env.dwFadeLevel;
end;

procedure TDDIDFFX.SetFadeTime(v:DWord);
begin
  env.dwFadeTime:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_ENVELOPE);
  end;
end;

function  TDDIDFFX.GetFadeTime:DWord;
begin
  result:=env.dwFadeTime;
end;




//ConstantForce
procedure TDDIDFFX.SetMagnitude(v:Integer);
begin
  constant.lMagnitude:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetMagnitude:Integer;
begin
  result:=constant.lMagnitude;
end;

//RampForce
procedure TDDIDFFX.SetStartMagnitude(v:Integer);
begin
  ramp.lStart:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetStartMagnitude:Integer;
begin
  result:=ramp.lStart;
end;

procedure TDDIDFFX.SetEndMagnitude(v:Integer);
begin
  ramp.lEnd:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetEndMagnitude:Integer;
begin
  result:=ramp.lEnd;
end;


//Periodic
procedure TDDIDFFX.SetPhase(v:DWord);
begin
  periodic.dwPhase:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetPhase:Dword;
begin
  result:=periodic.dwPhase;
end;

procedure TDDIDFFX.SetPeriod(v:DWord);
begin
  periodic.dwPeriod:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetPeriod:Dword;
begin
  result:=periodic.dwPeriod;
end;


procedure TDDIDFFX.SetOffSet(v:Integer);
begin
  periodic.lOffset:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetOffSet:Integer;
begin
  result:=periodic.lOffset;
end;


procedure TDDIDFFX.SetDeltaMagnitude(v:DWord);
begin
  periodic.dwMagnitude:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;


function  TDDIDFFX.GetDeltaMagnitude:DWord;
begin
  result:=periodic.dwMagnitude;
end;

//Condition
procedure TDDIDFFX.SetPositiveCoefficient(idx:Integer; v:Integer);
begin
  condition[idx].lPositiveCoefficient:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetPositiveCoefficient(idx:Integer):Integer;
begin
  result:=condition[idx].lPositiveCoefficient;
end;

procedure TDDIDFFX.SetNegativeCoefficient(idx:Integer; v:Integer);
begin
  condition[idx].lNegativeCoefficient:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetNegativeCoefficient(idx:Integer):Integer;
begin
  result:=condition[idx].lNegativeCoefficient;
end;

procedure TDDIDFFX.SetPositiveSaturation(idx:Integer; v:DWord);
begin
  condition[idx].dwPositiveSaturation:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetPositiveSaturation(idx:Integer):DWord;
begin
  result:=condition[idx].dwPositiveSaturation;
end;

procedure TDDIDFFX.SetNegativeSaturation(idx:Integer; v:DWord);
begin
  condition[idx].dwNegativeSaturation:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetNegativeSaturation(idx:Integer):DWord;
begin
  result:=condition[idx].dwNegativeSaturation;
end;

procedure TDDIDFFX.SetDeadBand(idx:Integer; v:Integer);
begin
  condition[idx].lDeadBand:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetDeadBand(idx:Integer):Integer;
begin
  result:=condition[idx].lDeadBand;
end;


procedure TDDIDFFX.SetConditionOffset(idx:Integer; v:Integer);
begin
  condition[idx].lOffset:=v;

  if FApplied then begin
    FEffect.SetParameters(@fx, DIEP_TYPESPECIFICPARAMS);
  end;
end;

function  TDDIDFFX.GetConditionOffset(idx:Integer):Integer;
begin
  result:=condition[idx].lOffset;
end;


end.
