unit _MXDRVg_Moonlight;

interface

uses
  Windows,SysUtils;

type
  UBYTE=byte;
  UWORD=word;
  ULONG=longword;
  SBYTE=shortint;
  SWORD=smallint;
  SLONG=longint;
  pUBYTE=pbytearray;//^UBYTE;

const MXDRV_False=0;
const MXDRV_True=1;

const MXDRV_LoadSuccess=0;
const MXDRV_LoadMDXBufferOverflow=1;
const MXDRV_LoadPDXBufferOverflow=2;

const MXDRV_WORK_FM=0; // FM8ch+PCM1ch
const MXDRV_WORK_PCM=1; // PCM7ch
const MXDRV_WORK_GLOBAL=2;
const MXDRV_WORK_KEY=3;
const MXDRV_WORK_OPM=4;
const MXDRV_WORK_PCM8=5;
const MXDRV_WORK_CREDIT=6;
const MXDRV_CALLBACK_OPMINT=7;

const MXDRV_ERR_MEMORY=1;

const X68SNDERR_PCMOUT=-1;
const X68SNDERR_TIMER=-2;
const X68SNDERR_MEMORY=-3;
const X68SNDERR_NOTACTIVE=-4;
const X68SNDERR_ALREADYACTIVE=-5;
const X68SNDERR_BADARG=-6;

const X68SNDERR_DLL=-1;
const X68SNDERR_FUNC=-2;

const MDXBufferMax=256*1024;
const PDXBufferMax=2048*1024;

type
  TX68REG=record
    d0:ULONG;
    d1:ULONG;
    d2:ULONG;
    d3:ULONG;
    d4:ULONG;
    d5:ULONG;
    d6:ULONG;
    d7:ULONG;
    a0:pUBYTE;
    a1:pUBYTE;
    a2:pUBYTE;
    a3:pUBYTE;
    a4:pUBYTE;
    a5:pUBYTE;
    a6:pUBYTE;
    a7:pUBYTE;
  end;

type
  TMXWORK_CH=record
    S0000:pUBYTE;    // Ptr
    S0004_b:UBYTE;    // PCM bank
    S0004:pUBYTE;    // voice ptr
    S0008:ULONG;    // bend delta
    S000c:ULONG;    // bend offset
    S0010:UWORD;    // D
    S0012:UWORD;    // note+D
    S0014:UWORD;    // note+D+bend+Pitch LFO offset
    S0016:UBYTE;    // flags b3=keyon/off
    S0017:UBYTE;    // flags
    S0018:UBYTE;    // ch
    S0019:UBYTE;    // carrier slot
    S001a:UBYTE;    // len
    S001b:UBYTE;    // gate
    S001c:UBYTE;    // p
    S001d:UBYTE;    // keyon slot
    S001e:UBYTE;    // Q
    S001f:UBYTE;    // Keyon delay
    S0020:UBYTE;    // Keyon delay counter
    S0021:UBYTE;    // PMS/AMS
    S0022:UBYTE;    // v
    S0023:UBYTE;    // v last
    S0024:UBYTE;    // LFO delay
    S0025:UBYTE;    // LFO delay counter
    S0026:pUBYTE;    // Pitch LFO Type
    S002a:ULONG;    // Pitch LFO offset start
    S002e:ULONG;    // Pitch LFO delta start
    S0032:ULONG;    // Pitch LFO delta
    S0036:ULONG;    // Pitch LFO offset
    S003a:UWORD;    // Pitch LFO length (cooked)
    S003c:UWORD;    // Pitch LFO length
    S003e:UWORD;    // Pitch LFO length counter
    S0040:UBYTE;    // Volume LFO Type
    S0044:UWORD;    // Volume LFO delta start
    S0046:UWORD;    // Volume LFO delta (cooked)
    S0048:UWORD;    // Volume LFO delta
    S004a:UWORD;    // Volume LFO offset
    S004c:UWORD;    // Volume LFO length
    S004e:UWORD;    // Volume LFO length counter
  end;

type
  PMXWORK_CHNAME=^TMXWORK_CHNAME;
  TMXWORK_CHNAME=record
    Ptr:pUBYTE;
    PCMBank:UBYTE;    
    VoicePtr:pUBYTE;
    BendDelta:ULONG;    
    BendOffset:ULONG;    
    D:UWORD;
    NoteD:UWORD;    
    NoteAll:UWORD;    
    FlagsKey:UBYTE;
    Flags:UBYTE;    
    Ch:UBYTE;    
    Carrier:UBYTE;    
    Len:UBYTE;    
    Gate:UBYTE;    
    Panpot:UBYTE;    
    KeyonSlot:UBYTE;    
    Q:UBYTE;    
    KeyonDelay:UBYTE;    
    KeyonDelayCounter:UBYTE;    
    PMSAMS:UBYTE;    
    Volume:UBYTE;
    VolumeLast:UBYTE;    
    LFODelay:UBYTE;    
    LFODelayCounter:UBYTE;    
    PLFOType:pUBYTE;    
    PLFOOffsetStart:ULONG;    
    PLFODeltaStart:ULONG;    
    PLFODelta:ULONG;    
    PLFOOffset:ULONG;
    PLFOLengthCooked:UWORD;    
    PLFOLength:UWORD;    
    PLFOLengthCounter:UWORD;    
    ALFOType:UBYTE;
    ALFODeltaStart:UWORD;    
    ALFODeltaCooked:UWORD;    
    ALFODelta:UWORD;    
    ALFOOffset:UWORD;    
    ALFOLength:UWORD;    
    ALFOLengthCounter:UWORD;
  end;

type
  TMXWORK_GLOBAL=record
    L001ba6:UWORD;
    L001ba8:ULONG;
    L001bac:pUBYTE;
    L001bb4:array [0..15] of UBYTE;
    L001df4:UBYTE;
    L001df6:array [0..15] of UBYTE;
    L001e06:UWORD;    // Channel Mask (true)
    L001e08:UBYTE;
    L001e09:UBYTE;
    L001e0a:UBYTE;
    L001e0b:UBYTE;
    L001e0c:UBYTE;    // @t
    L001e0d:UBYTE;
    L001e0e:UBYTE;
    L001e10:UBYTE;
    L001e12:UBYTE;    // Paused
    L001e13:UBYTE;    // End
    L001e14:UBYTE;    // Fadeout Offset
    L001e15:UBYTE;
    L001e17:UBYTE;    // Fadeout Enable
    L001e18:UBYTE;
    L001e19:UBYTE;
    L001e1a:UWORD;    // Channel Enable
    L001e1c:UWORD;    // Channel Mask
    L001e1e:array [0..1] of UWORD;    // Fadeout Speed
    L001e22:UWORD;
    L001e24:pUBYTE;
    L001e28:pUBYTE;
    L001e2c:pUBYTE;
    L001e30:pUBYTE;
    L001e34:pUBYTE;
    L001e38:pUBYTE;
    L00220c:ULONG;
    L002218:pUBYTE;
    L00221c:pUBYTE;
    L002220:ULONG; // L_MDXSIZE
    L002224:ULONG; // L_PDXSIZE
    L002228:pUBYTE;    // voice data
    L00222c:pUBYTE;
    L002230:UBYTE;
    L002231:UBYTE;
    L002232:UBYTE;
    L002233:array [0..8] of UBYTE;
    L00223c:array [0..11] of UBYTE;
    L002245:UBYTE;
    L002246:UWORD; // loop count
    FATALERROR:ULONG;
    FATALERRORADR:ULONG;
    PLAYTIME:ULONG; // 演奏時間
    MUSICTIMER:UBYTE;  // 演奏時間タイマー定数
    STOPMUSICTIMER:UBYTE;  // 演奏時間タイマー停止
    MEASURETIMELIMIT:ULONG; // 演奏時間計測中止時間
  end;

type
  PMXWORK_GLOBALNAME=^TMXWORK_GLOBALNAME;
  TMXWORK_GLOBALNAME=record
    PlayClock:UWORD;
    L001ba8:ULONG;
    L001bac:pUBYTE;
    L001bb4:array [0..15] of UBYTE;
    L001df4:UBYTE;
    L001df6:array [0..15] of UBYTE;
    ChMaskTrue:UWORD;
    L001e08:UBYTE;
    L001e09:UBYTE;
    L001e0a:UBYTE;
    L001e0b:UBYTE;
    Tempo:UBYTE;
    L001e0d:UBYTE;
    L001e0e:UBYTE;
    L001e10:UBYTE;
    Paused:UBYTE;
    EndFlag:UBYTE;
    FadeoutOffset:UBYTE;
    L001e15:UBYTE;
    FadeoutEnable:UBYTE;
    L001e18:UBYTE;
    L001e19:UBYTE;
    ChEnable:UWORD;
    ChMask:UWORD;
    FadeoutSpeed:array [0..1] of UWORD;
    L001e22:UWORD;
    L001e24:pUBYTE;
    L001e28:pUBYTE;
    L001e2c:pUBYTE;
    L001e30:pUBYTE;
    L001e34:pUBYTE;
    L001e38:pUBYTE;
    L00220c:ULONG;
    L002218:pUBYTE;
    L00221c:pUBYTE;
    MDXSize:ULONG;
    PDXSize:ULONG;
    VoiceData:pUBYTE;
    L00222c:pUBYTE;
    L002230:UBYTE;
    L002231:UBYTE;
    L002232:UBYTE;
    L002233:array [0..8] of UBYTE;
    L00223c:array [0..11] of UBYTE;
    L002245:UBYTE;
    LoopCount:UWORD;
    FATALERROR:ULONG;
    FATALERRORADR:ULONG;
    PLAYTIME:ULONG; // 演奏時間
    MUSICTIMER:UBYTE;  // 演奏時間タイマー定数
    STOPMUSICTIMER:UBYTE;  // 演奏時間タイマー停止
    MEASURETIMELIMIT:ULONG; // 演奏時間計測中止時間
  end;

type
  PMXWORK_KEY=^TMXWORK_KEY;
  TMXWORK_KEY=record
    OPT1:UBYTE;
    OPT2:UBYTE;
    SHIFT:UBYTE;
    CTRL:UBYTE;
    XF3:UBYTE;
    XF4:UBYTE;
    XF5:UBYTE;
  end;

type
  TMXWORK_OPM=array[0..255] of BYTE;

type
  TMXWORK_PCM8=UBYTE;

var
  HMXDRVg:HMODULE;
  MXDRV_Start:function(samprate,betw,pcmbuf,late,mdxbuf,pdxbuf,opmmode:integer):integer; cdecl;
  MXDRV_End:procedure; cdecl;
  MXDRV_GetPCM:function(buf:pbytearray;len:integer):integer; cdecl;
  MXDRV_Play:procedure(mdx:pbytearray;mdxsize:dword;pdx:pbytearray;pdxsize:dword); cdecl;
  MXDRV_GetWork:function(i:integer):pointer; cdecl;
  MXDRV:procedure(var reg:TX68REG); cdecl;
  MXDRV_MeasurePlayTime:function(mdx:pbytearray;mdxsize:dword;pdx:pbytearray;pdxsize:dword;loop:integer;fadeout:boolean):dword; cdecl;
  MXDRV_PlayAt:procedure(playat:dword;loop:integer;fadeout:boolean); cdecl;
  MXDRV_TotalVolume:function(vol:integer):integer; cdecl;

function  MXDRV_LoadDLL:boolean;
procedure MXDRV_FreeDLL;

function  MXDRV_StartMXDRV(samprate,betw,pcmbuf,late,mdxbuf,pdxbuf:integer;romeo:boolean):string;

procedure MXDRV_Call(a:ULONG);
procedure MXDRV_Call_2(a,b:ULONG);
procedure MXDRV_Replay;
procedure MXDRV_Stop;
procedure MXDRV_Pause;
procedure MXDRV_Cont;
procedure MXDRV_Fadeout;
procedure MXDRV_Fadeout2(a:ULONG);
procedure MXDRV_SetMute(ch:integer;flag:boolean);

function  MXDRV_GetWorkPCM8:boolean;
procedure MXDRV_SetWorkPCM8(mode:boolean);

procedure MXDRV_GetWorkCh(ChNo:integer;var Ch:TMXWORK_CH);
procedure MXDRV_GetWorkChName(ChNo:integer;var PCh:PMXWORK_CHNAME);
procedure MXDRV_GetWorkGlobal(var Global:TMXWORK_GLOBAL);
procedure MXDRV_GetWorkGlobalName(var PGlobal:PMXWORK_GLOBALNAME);
procedure MXDRV_GetWorkKey(var Key:TMXWORK_KEY);
procedure MXDRV_GetWorkVKey(var pKey:PMXWORK_KEY);
procedure MXDRV_GetWorkOpm(var Opm:TMXWORK_OPM);
function  MXDRV_GetWorkCredit:string;

implementation

function MXDRV_LoadDLL:boolean;
begin
  Result:=False;

  HMXDRVg:=LoadLibrary('mxdrv.dll');
  if HMXDRVg=0 then exit;

  MXDRV_Start:=GetProcAddress(HMXDRVg,'MXDRV_Start');
  MXDRV_End:=GetProcAddress(HMXDRVg,'MXDRV_End');
  MXDRV_GetPCM:=GetProcAddress(HMXDRVg,'MXDRV_GetPCM');
  MXDRV_Play:=GetProcAddress(HMXDRVg,'MXDRV_Play');
  MXDRV_GetWork:=GetProcAddress(HMXDRVg,'MXDRV_GetWork');
  MXDRV:=GetProcAddress(HMXDRVg,'MXDRV');
  MXDRV_MeasurePlayTime:=GetProcAddress(HMXDRVg,'MXDRV_MeasurePlayTime');
  MXDRV_PlayAt:=GetProcAddress(HMXDRVg,'MXDRV_PlayAt');
  MXDRV_TotalVolume:=GetProcAddress(HMXDRVg,'MXDRV_TotalVolume');

  if addr(MXDRV_Start)=nil then exit;
  if addr(MXDRV_End)=nil then exit;
  if addr(MXDRV_GetPCM)=nil then exit;
  if addr(MXDRV_Play)=nil then exit;
  if addr(MXDRV_GetWork)=nil then exit;
  if addr(MXDRV)=nil then exit;
  if addr(MXDRV_MeasurePlayTime)=nil then exit;
  if addr(MXDRV_PlayAt)=nil then exit;
  if addr(MXDRV_TotalVolume)=nil then exit;

  Result:=True;
end;

procedure MXDRV_FreeDLL;
begin
  if addr(HMXDRVg)=nil then exit;
  FreeLibrary(HMXDRVg);
end;

function MXDRV_StartMXDRV(samprate,betw,pcmbuf,late,mdxbuf,pdxbuf:integer;romeo:boolean):string;
var
  ret:integer;
begin
  // WAVE出力ならbetw=0,pcmbuf=5,late=0で起動すること。

  if romeo=True then
    ret:=MXDRV_Start(samprate,betw,pcmbuf,late,mdxbuf,pdxbuf,MXDRV_True)
    else
    ret:=MXDRV_Start(samprate,betw,pcmbuf,late,mdxbuf,pdxbuf,MXDRV_False);

  case ret of
    MXDRV_ERR_MEMORY:       Result:='mxdrv.dllが必要なメモリを確保できません。';
    10000+X68SNDERR_DLL:    Result:='X68Sound.dllのロードに失敗しました。';
    10000+X68SNDERR_FUNC:   Result:='X68Sound.dllに必要なAPIが含まれていません。X68Sound.dllのバージョンを確認してください。';
    10100+X68SNDERR_PCMOUT: Result:='X68Sound.dllのPCM出力を使用できません。';
    10100+X68SNDERR_TIMER:  Result:='X68Sound.dllがタイマを使用できません。';
    10100+X68SNDERR_MEMORY: Result:='X68Sound.dllが必要なメモリを確保できません。';
    else begin
      if ret=0 then begin
        Result:='';
        end else begin
        Result:='未定義エラー('+IntToStr(ret)+')';
      end;
    end;
  end;
end;

procedure MXDRV_Call(a:ULONG);
var
  reg:TX68REG;
begin
  reg.d0:=a;
  reg.d1:=$00;
  MXDRV(reg);
end;

procedure MXDRV_Call_2(a,b:ULONG);
var
  reg:TX68REG;
begin
  reg.d0:=a;
  reg.d1:=b;
  MXDRV(reg);
end;

procedure MXDRV_Replay;
begin
  MXDRV_Call($0f);
end;

procedure MXDRV_Stop;
begin
  MXDRV_Call($05);
end;

procedure MXDRV_Pause;
begin
  MXDRV_Call($06);
end;

procedure MXDRV_Cont;
begin
  MXDRV_Call($07);
end;

procedure MXDRV_Fadeout;
begin
  MXDRV_Call_2($0c,19);
end;

procedure MXDRV_Fadeout2(a:ULONG);
begin
  MXDRV_Call_2($0c,a);
end;

procedure MXDRV_SetMute(ch:integer;flag:boolean);
var
  PGlobal:PMXWORK_GLOBALNAME;
begin
  MXDRV_GetWorkGlobalName(PGlobal);
  if flag=True then begin
    PGlobal.ChMask:=PGlobal.ChMask or (1 shl ch);
    end else begin
    PGlobal.ChMask:=PGlobal.ChMask and (not (1 shl ch));
  end;
end;

procedure MXDRV_GetWorkCh(ChNo:integer;var Ch:TMXWORK_CH);
var
  p:pbytearray;
  size:integer;
begin
  if (ChNo<0) or (15<ChNo) then exit;

  if ChNo<=8 then begin // FM8ch+PCM1ch
    p:=MXDRV_GetWork(MXDRV_WORK_FM);
    end else begin // PCM7ch
    p:=MXDRV_GetWork(MXDRV_WORK_PCM);
  end;

  size:=(SizeOf(Ch) div 4)*4+4;

  case ChNo of
     0: MoveMemory(@Ch.S0000,@p[size*0],SizeOf(Ch)); // FM.1
     1: MoveMemory(@Ch.S0000,@p[size*1],SizeOf(Ch));
     2: MoveMemory(@Ch.S0000,@p[size*2],SizeOf(Ch));
     3: MoveMemory(@Ch.S0000,@p[size*3],SizeOf(Ch));
     4: MoveMemory(@Ch.S0000,@p[size*4],SizeOf(Ch));
     5: MoveMemory(@Ch.S0000,@p[size*5],SizeOf(Ch));
     6: MoveMemory(@Ch.S0000,@p[size*6],SizeOf(Ch));
     7: MoveMemory(@Ch.S0000,@p[size*7],SizeOf(Ch)); // FM.7
     8: MoveMemory(@Ch.S0000,@p[size*8],SizeOf(Ch)); // PCM.1
     9: MoveMemory(@Ch.S0000,@p[size*0],SizeOf(Ch)); // PCM.2
    10: MoveMemory(@Ch.S0000,@p[size*1],SizeOf(Ch));
    11: MoveMemory(@Ch.S0000,@p[size*2],SizeOf(Ch));
    12: MoveMemory(@Ch.S0000,@p[size*3],SizeOf(Ch));
    13: MoveMemory(@Ch.S0000,@p[size*4],SizeOf(Ch));
    14: MoveMemory(@Ch.S0000,@p[size*5],SizeOf(Ch));
    15: MoveMemory(@Ch.S0000,@p[size*6],SizeOf(Ch));
  end;
end;

procedure MXDRV_GetWorkChName(ChNo:integer;var PCh:PMXWORK_CHNAME);
var
  p:pbytearray;
  size:integer;
begin
  if (ChNo<0) or (15<ChNo) then exit;

  if ChNo<=8 then begin // FM8ch+PCM1ch
    p:=MXDRV_GetWork(MXDRV_WORK_FM);
    end else begin // PCM7ch
    p:=MXDRV_GetWork(MXDRV_WORK_PCM);
  end;

  size:=(SizeOf(TMXWORK_CHNAME) div 4)*4+4;

  case ChNo of
     0: PCh:=@p[size*0]; // FM.1
     1: PCh:=@p[size*1];
     2: PCh:=@p[size*2];
     3: PCh:=@p[size*3];
     4: PCh:=@p[size*4];
     5: PCh:=@p[size*5];
     6: PCh:=@p[size*6];
     7: PCh:=@p[size*7]; // FM.8
     8: PCh:=@p[size*8]; // PCM.1
     9: PCh:=@p[size*0]; // PCM.2
    10: PCh:=@p[size*1];
    11: PCh:=@p[size*2];
    12: PCh:=@p[size*3];
    13: PCh:=@p[size*4];
    14: PCh:=@p[size*5];
    15: PCh:=@p[size*6];
  end;
end;

function MXDRV_GetWorkPCM8:boolean;
var
  p:pbytearray;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_PCM8);
  if p[0]=MXDRV_True then
    Result:=True
    else
    Result:=False;
end;

procedure MXDRV_SetWorkPCM8(mode:boolean);
var
  p:pbytearray;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_PCM8);
  if mode=True then
    p[0]:=MXDRV_True
    else
    p[0]:=MXDRV_False;
end;

procedure MXDRV_GetWorkGlobal(var Global:TMXWORK_GLOBAL);
var
  p:pbytearray;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_GLOBAL);
  MoveMemory(@Global.L001ba6,@p[0],SizeOf(Global));
end;

procedure MXDRV_GetWorkGlobalName(var PGlobal:PMXWORK_GLOBALNAME);
begin
  PGlobal:=MXDRV_GetWork(MXDRV_WORK_GLOBAL);
end;

procedure MXDRV_GetWorkKey(var Key:TMXWORK_KEY);
var
  p:pbytearray;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_KEY);
  MoveMemory(@Key.OPT1,@p[0],SizeOf(Key));
end;

procedure MXDRV_GetWorkVKey(var pKey:PMXWORK_KEY);
begin
  pKey:=MXDRV_GetWork(MXDRV_WORK_KEY);
end;

procedure MXDRV_GetWorkOpm(var Opm:TMXWORK_OPM);
var
  p:pbytearray;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_OPM);
  MoveMemory(@Opm[0],@p[0],SizeOf(Opm));
end;

function MXDRV_GetWorkCredit:string;
var
  p:pbytearray;
  cnt:integer;
begin
  p:=MXDRV_GetWork(MXDRV_WORK_CREDIT);
  Result:='';
  cnt:=0;
  while (p[cnt]<>$00) do begin
    Result:=Result+char(p[cnt]);
    inc(cnt);
  end;
end;

end.



