unit _MXDRVg;

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

interface

uses
  Windows, SysUtils, Classes,_SndDrv_const,_MXDRVg_Moonlight, _Vis_const;

type
  TPlayInfo=record
    fPlayTimems:double;
    TotalClock,MesureClock:DWORD;
    DispClock:DWORD;
  end;

type
  TInfomation=record
    FileSize:integer;
    PDXFileLoadFlag:boolean;
    PDXFilename:string;
    PDXFileSize:integer;
  end;

  TMXDRVg = class
  private
    { Private 宣言 }
    MemSync:TMultiReadExclusiveWriteSynchronizer;
    StartedMXDRV:boolean;
    MCh:array[0..15] of TMCh;
    OPMVolTbl:array[0..15] of byte;
    PlayInfo:TPlayInfo;
    NowPCMRate:dword;
    wch:array[0..15] of PMXWORK_CHNAME;
    wgl:PMXWORK_GLOBALNAME;
    TransTempo:integer;
    procedure MainInfomationVisible(MesStr:string);
    function  MDXLoad(MDXFilename:string):boolean;
    function  PDXLoad(MDXFilename:string;var pdxbuf:array of byte;var pdxsize:integer):boolean;
    procedure RefreshWorkParams;
    function  GetParamStr0(Ch:byte):string;
    function  GetParamStr1(Ch:byte):string;
    function  GetLoopCount:integer;
    function  GetEndFlag:boolean;
    function  GetTempo:byte;
    function  GetPlayTimems:double;
    function  GetNowVolume(Ch:integer):integer;
    function  GetNowVoice(Ch:integer):integer;
    function  GetNowKeycode(Ch:integer):integer;
  public
    Infomation:TInfomation;
    function  Init(PCMRate:integer):boolean;
    function  ChangeRate(rate:integer):boolean;
    procedure RefrectionSetting;
    procedure FreeMemory;
    procedure GetPCM(buf:PByteArray;len:LongInt);
    procedure StartFadeout;
    function  MDXPlay:boolean;
    procedure MDXStop;
    procedure MMLSeek(SeekClock:integer);
    procedure MMLTrap;
    procedure SetMute(ch:integer;flag:boolean);
    procedure SetTransTempo(_TransTempo:integer);
    procedure GetVisual(var pSetVisual:pVisual);
    procedure GetVisualSystem(var pSetVisual:pVisual);
    function  GetTotalClock:integer;
    function  GetMesureClock:integer;
    function  GetDispClock:integer;
    function  GetCopyright:string;
  end;

implementation

uses MainWin,
     _SimpleDialog,_MDXWinINI,_const,_m_Tools,_Vis,_MXDRVm_LZX043;

// --------------------
// private
// --------------------

procedure TMXDRVg.MainInfomationVisible(MesStr:string);
begin
  Main.InfomationVisible(MesStr);
end;

function TMXDRVg.MDXLoad(MDXFilename:string):boolean;
var
  DummyTitle:string;
  MDXData:array of byte;
  MDXDataPos:integer;
  MDXDataSize:integer;
  TmpByte:byte;
  Add:dword;
  mdxbuf,pdxbuf:array of byte;
  mdxsize,pdxsize:integer;
  Segment:word;
  cnt:integer;
  havepdx:boolean;
  procedure MDXLoadError(mes:string);
  begin
    MainInfomationVisible('MDXError.'+mes);
  end;
  procedure LoadMDXData(Filename:string);
  var
    MDXFile:TFileStream;
  begin
    MDXFile:=TFileStream.Create(Filename,fmOpenRead or fmShareDenyNone);
    MDXDataSize:=MDXFile.Size;
    SetLength(MDXData,MDXDataSize+1);
    MDXFile.ReadBuffer(MDXData[0],MDXDataSize);
    MDXFile.Free;
    MDXDataPos:=0;
  end;
  function ReadByte:byte;
  begin
    if MDXDataPos>=MDXDataSize then begin
      MDXLoadError('MDXReadByte:OverPointer');
      Result:=$00;
      end else begin
      Result:=MDXData[MDXDataPos];
    end;
    inc(MDXDataPos);
  end;
  function ReadWord(pos:integer):word;
  begin
    MDXDataPos:=pos;
    if (MDXDataPos+1)>=MDXDataSize then begin
      MDXLoadError('MDXReadWord:OverPointer');
      Result:=$00;
      end else begin
      Result:=(word(MDXData[MDXDataPos+0]) shl 8)+word(MDXData[MDXDataPos+1]);
    end;
    inc(MDXDataPos,2);
  end;
  procedure AutoDecordLZX;
  var
    unLZXbuf:array of byte;
    LZXOffset,unLZXSize:integer;
  begin
    LZXOffset:=MDXDataPos;
    unLZXSize:=GetunLZXSize(MDXData,LZXOffset);
    if unLZXSize<>0 then begin
      SetLength(unLZXbuf,unLZXSize+1);
      LZXdecord(MDXData,LZXOffset,unLZXbuf);
      MDXDataSize:=LZXOffset+unLZXSize;
      SetLength(MDXData,MDXDataSize+2);
      MoveMemory(@MDXData[LZXOffset],@unLZXbuf[0],unLZXSize+1);
    end;
  end;
begin
  Infomation.FileSize:=GetFileSize(MDXFilename);

  Result:=False;

  with PlayInfo do begin
    fPlayTimems:=0;
    TotalClock:=0;
    MesureClock:=0;
    DispClock:=0;
  end;

  SetLength(mdxbuf,MDXBufferMax);
  SetLength(pdxbuf,PDXBufferMax);

  // Fileopen
  if FileExists(MDXFilename)=False then begin
    MainInfomationVisible('NotFound '+MDXFilename);
    exit;
  end;
  LoadMDXData(MDXFilename);

  // Title Structure Read
  SetLength(DummyTitle,$ff);
  TmpByte:=ReadByte;
  if TmpByte=0 then begin // 先頭が０というのはおかしい（タイトルがない時は$0dのはず）
    MDXLoadError('先頭バイトが$00です。');
    exit;
  end;
  Add:=0;
  while (TmpByte<>$0d) do begin
    if Add<$ff then // ２５５文字以上のタイトルは切り捨てる
      DummyTitle[Add+1]:=char(TmpByte);
    TmpByte:=ReadByte;
    inc(Add);
    if Add>1024 then Add:=1024; // １ｋｂ以上のタイトルはおかしい
  end;
  if Add<$ff then
    SetLength(DummyTitle,Add)
    else
    SetLength(DummyTitle,$ff);

  if ReadByte<>$0a then begin // タイトル終了時、１文字目に $0a以外はおかしい
    MDXLoadError('タイトル終了が$0a,1a以外です。');
    exit;
  end;
  if ReadByte<>$1a then begin // タイトル終了時、２文字目に $1a以外はおかしい
    MDXLoadError('タイトル終了が$0a,1a以外です。');
    exit;
  end;

  // PCMFilename Structure Read
  TmpByte:=ReadByte;
  if TmpByte=$00 then begin
    end else begin
    Add:=0;
    while (TmpByte<>$00) do begin
      TmpByte:=ReadByte;
      inc(Add);
      if Add>=127 then begin // 127文字以上のＰＤＸファイル名はありえない
        MDXLoadError('PDXファイル名が127文字以上です。');
        exit;
      end;
    end;
  end;

  // Check LZH Header
  Segment:=MDXDataPos;
  AutoDecordLZX;
  if MDXData[MDXDataSize-1]=$0F then begin // LZXで欠け落ちたデータを補完
    inc(MDXDataSize,4);
    SetLength(MDXData,MDXDataSize+1);
    MDXData[MDXDataSize-4]:=$0F;
    MDXData[MDXDataSize-3]:=$0F;
    MDXData[MDXDataSize-2]:=$0F;
    MDXData[MDXDataSize-1]:=$0F;
  end;

  MDXDataPos:=Segment;

  mdxbuf[0]:=$00;
  mdxbuf[1]:=$00;
  mdxbuf[2]:=$00; // PDXuse=$00 unuse=$ff
  mdxbuf[3]:=$00; // PDXuse=$00 unuse=$ff
  mdxbuf[4]:=$00; // bodyptr high
  mdxbuf[5]:=$08; // bodyptr low
  mdxbuf[6]:=$00;
  mdxbuf[7]:=$08;

  mdxsize:=$0008;
  for cnt:=MDXDataPos to MDXDataSize-1 do begin
    mdxbuf[mdxsize]:=MDXData[cnt];
    inc(mdxsize);
  end;

  if ansipos('by moonlight',lowercase(DummyTitle))<>0 then begin
    MainInfomationVisible('サンプルデータはMXDRVgでは再生できません。');
    Result:=False;
    exit;
  end;

  MXDRV_Stop();
  MXDRV_SetWorkPCM8(MXDRVgINI.usePCM8);

  havepdx:=PDXLoad(MDXFilename,pdxbuf,pdxsize);
  if havepdx=True then begin
    PlayInfo.MesureClock:=MXDRV_MeasurePlayTime(@mdxbuf[0],mdxsize,@pdxbuf[0],pdxsize,2,True);
    MXDRV_Play(@mdxbuf[0],mdxsize,@pdxbuf[0],pdxsize);
    end else begin
    PlayInfo.MesureClock:=MXDRV_MeasurePlayTime(@mdxbuf[0],mdxsize,@pdxbuf[0],0,2,True);
    MXDRV_Play(@mdxbuf[0],mdxsize,@pdxbuf[0],0);
  end;

  RefreshWorkParams;
  Result:=True;
end;

function TMXDRVg.PDXLoad(MDXFilename:string;var pdxbuf:array of byte;var pdxsize:integer):boolean;
var
  PDXFilename:string;
  ExistFlag:boolean;
  PDXData:array of byte;
  PDXDataPos:integer;
  PDXDataSize:integer;
  cnt:integer;
  procedure CheckExists(Filename:string);
  begin
    if ExistFlag=False then begin
      if FileExists(Filename)=True then begin
        PDXFilename:=Filename;
        ExistFlag:=True;
      end;
    end;
  end;
  procedure LoadPDXData(Filename:string);
  var
    PDXFile:TFileStream;
  begin
    PDXFile:=TFileStream.Create(Filename,fmOpenRead or fmShareDenyNone);
    PDXDataSize:=PDXFile.Size;
    SetLength(PDXData,PDXDataSize+1);
    PDXFile.ReadBuffer(PDXData[0],PDXDataSize);
    PDXFile.Free;
    PDXDataPos:=0;
  end;
  procedure AutoDecordLZX;
  var
    unLZXbuf:array of byte;
    LZXOffset,unLZXSize:integer;
  begin
    LZXOffset:=0;
    unLZXSize:=GetunLZXSize(PDXData,LZXOffset);
    if unLZXSize<>0 then begin
      SetLength(unLZXbuf,unLZXSize+1);
      LZXdecord(PDXData,LZXOffset,unLZXbuf);
      PDXDataSize:=LZXOffset+unLZXSize;
      SetLength(PDXData,PDXDataSize+2);
      MoveMemory(@PDXData[LZXOffset],@unLZXbuf[0],unLZXSize+1);
    end;
  end;
begin
  Infomation.PDXFilename:='';
  Infomation.PDXFileSize:=0;
  Infomation.PDXFileLoadFlag:=False;

  ExistFlag:=False;

  PDXFilename:=ChangeFileExt(GetPDXFilename(MDXFilename),'');
  if PDXFilename='' then begin
    Result:=False;
    exit;
  end;

  if Main.gFileInfo.isNetworkMode=True then begin
    CheckExists(Main.Network.CFG.MXDRV_PDXPath+PDXFilename+'.pdx'); // ネットワークパス
    CheckExists(Main.Network.CFG.MXDRV_PDXPath+copy(PDXFilename,1,8)+'.pdx'); // ネットワークパス（８文字カット）
  end;

  CheckExists(ExtractFilePath(Main.gFileInfo.GetPlayPath)+PDXFilename+'.pdx'); // MDXと同じフォルダ
  CheckExists(MXDRVmINI.PDXFilePath+'\'+PDXFilename+'.pdx'); // PDXファイルパス
  CheckExists(ExtractFilePath(Main.gFileInfo.GetPlayPath)+copy(PDXFilename,1,8)+'.pdx'); // MDXと同じフォルダ（８文字カット）
  CheckExists(MXDRVmINI.PDXFilePath+'\'+copy(PDXFilename,1,8)+'.pdx'); // PDXファイルパス（８文字カット）

  if ExistFlag=False then begin // PDXが見つからない
    if ExtractFilename(PDXFilename)<>'bos' then begin
      MainInfomationVisible('NotFound '+ExtractFilename(PDXFilename)+'.pdx');
    end;
    Result:=False;
    exit;
  end;

  LoadPDXData(PDXFilename);
  AutoDecordLZX;

  pdxbuf[0]:=$00;
  pdxbuf[1]:=$00;
  pdxbuf[2]:=$00;
  pdxbuf[3]:=$00;
  pdxbuf[4]:=$00; // bodyptr high
  pdxbuf[5]:=$09; // bodyptr low
  pdxbuf[6]:=$00; // pdxnamelen high
  pdxbuf[7]:=$00; // pdxnamelen low
  pdxbuf[8]:=$00; // pdxname

  pdxsize:=$0009;
  for cnt:=PDXDataPos to PDXDataSize-1 do begin
    pdxbuf[pdxsize]:=PDXData[cnt];
    inc(pdxsize);
  end;

  Infomation.PDXFilename:=PDXFilename;
  Infomation.PDXFileSize:=GetFileSize(PDXFilename);
  Infomation.PDXFileLoadFlag:=True;
  Result:=True;
end;

procedure TMXDRVg.RefreshWorkParams;
var
  ChCnt:integer;
begin
  for ChCnt:=0 to 15 do begin
    MXDRV_GetWorkChName(ChCnt,wch[ChCnt]);
  end;
  MXDRV_GetWorkGlobalName(wgl);
end;

function TMXDRVg.GetParamStr0(Ch:byte):string;
begin
  if Ch>=8 then begin
    Result:='';
    exit;
  end;

  Result:=Format('K:%2d  D:%4d  P:%8d Data:%s:%s',
                 [GetNowKeycode(Ch),smallint(wch[Ch].D),SLONG(wch[Ch].BendOffset) div $100,
                  IntToHex((dword(wch[Ch].Ptr[0])*$10000)+(dword(wch[Ch].Ptr[1])*$100)+dword(wch[Ch].Ptr[2]),6),
                  IntToHex((dword(wch[Ch].Ptr[3])*$100)+dword(wch[Ch].Ptr[4]),4)]);
end;

function TMXDRVg.GetParamStr1(Ch:byte):string;
begin
  if Ch>=8 then begin
    Result:='';
    exit;
  end;

  Result:=Format('@%3d  @v:%3d SPLFO:%8d   Offset:%s',
                 [GetNowVoice(Ch),GetNowVolume(Ch),SLONG(wch[Ch].PLFOOffset) div $100,IntToHex(integer(addr(wch[Ch].Ptr)),4)]);
end;

function TMXDRVg.GetLoopCount:integer;
begin
  if wgl=nil then begin
    Result:=0;
    exit;
  end;

  Result:=wgl.LoopCount;
end;

function TMXDRVg.GetEndFlag:boolean;
begin
  if wgl=nil then begin
    Result:=False;
    exit;
  end;

  if wgl.EndFlag=MXDRV_True then
    Result:=True
    else
    Result:=False;
end;

function TMXDRVg.GetTempo:byte;
begin
  if wgl=nil then begin
    Result:=0;
    exit;
  end;

  Result:=wgl.Tempo;
end;

function TMXDRVg.GetPlayTimems:double;
begin
  Result:=PlayInfo.fPlayTimems;
end;

function TMXDRVg.GetNowVolume(Ch:integer):integer;
begin
  if wch[Ch].Volume>=$80 then begin
    Result:=$ff-wch[Ch].Volume;
    end else begin
    Result:=$7f-OPMVolTbl[wch[Ch].Volume];
  end;
end;

function TMXDRVg.GetNowVoice(Ch:integer):integer;
begin
  if wgl=nil then begin
    Result:=0;
    exit;
  end;

  if Ch<=7 then begin // FM
    if (dword(@wch[Ch].VoicePtr[0])<dword(@wgl.VoiceData[0])) or (dword(@wch[Ch].VoicePtr[0])=$00000000) or (dword(@wgl.VoiceData[0])=$00000000) then begin
      Result:=0;
      end else begin
      Result:=((dword(@wch[Ch].VoicePtr[0])-dword(@wgl.VoiceData[0])) div $1b) mod 13;
    end;
    end else begin // PCM
    Result:=wch[Ch].PCMBank;
  end;
end;

function TMXDRVg.GetNowKeycode(Ch:integer):integer;
begin
  Result:=(wch[Ch].NoteD+27) shr 6;
end;

// --------------------
// public
// --------------------

function TMXDRVg.Init(PCMRate:integer):boolean;
var
  res:string;
begin
  MemSync:=TMultiReadExclusiveWriteSynchronizer.Create;

  MemSync.BeginWrite;

  NowPCMRate:=PCMRate;
  OPMVolTbl[ 0]:=$2a; OPMVolTbl[ 1]:=$28; OPMVolTbl[ 2]:=$25; OPMVolTbl[ 3]:=$22;
  OPMVolTbl[ 4]:=$20; OPMVolTbl[ 5]:=$1d; OPMVolTbl[ 6]:=$1a; OPMVolTbl[ 7]:=$18;
  OPMVolTbl[ 8]:=$15; OPMVolTbl[ 9]:=$12; OPMVolTbl[10]:=$10; OPMVolTbl[11]:=$0d;
  OPMVolTbl[12]:=$0a; OPMVolTbl[13]:=$08; OPMVolTbl[14]:=$05; OPMVolTbl[15]:=$02;

  StartedMXDRV:=MXDRV_LoadDLL;
  if StartedMXDRV=True then begin
    with MXDRVgINI do begin
      res:=MXDRV_StartMXDRV(NowPCMRate,0,5,0,MDXBufMax,PDXBufMax,False);
    end;
    if res<>'' then begin
      ShowMessage('mxdrv.dll初期化エラー',res);
      StartedMXDRV:=False;
      end else begin
      RefreshWorkParams;
    end;
  end;

  Result:=StartedMXDRV;

  MemSync.EndWrite;
end;

function TMXDRVg.ChangeRate(rate:integer):boolean;
var
  res:string;
begin
  MemSync.BeginWrite;

  MXDRV_End;

  Result:=True;

  NowPCMRate:=rate;
  with MXDRVgINI do begin
    res:=MXDRV_StartMXDRV(NowPCMRate,0,5,0,MDXBufMax,PDXBufMax,False);
  end;
  if res<>'' then begin
    ShowMessage('mxdrv.dll初期化エラー',res);
    Result:=False;
    end else begin
    RefreshWorkParams;
  end;

  MemSync.EndWrite;
end;

procedure TMXDRVg.RefrectionSetting;
begin
  MemSync.BeginWrite;
  MemSync.EndWrite;
end;

procedure TMXDRVg.FreeMemory;
begin
  MemSync.BeginWrite;
  if StartedMXDRV=True then begin
    MXDRV_End;
    StartedMXDRV:=False;
  end;
  MXDRV_FreeDLL;
  MemSync.EndWrite;

  MemSync.Free;
end;

procedure TMXDRVg.GetPCM(buf:PByteArray;len:LongInt);
begin
  SetTransTempo(TransTempo);
  MemSync.BeginWrite;
  MXDRV_GetPCM(buf,len);
  MemSync.EndWrite;
end;

procedure TMXDRVg.StartFadeout;
begin
  MemSync.BeginWrite;
  if sdFadeout=False then begin
    sdFadeout:=True;
    MXDRV_Fadeout;
  end;
  MemSync.EndWrite;
end;

function TMXDRVg.MDXPlay;
begin
  MemSync.BeginWrite;
  PlayInfo.fPlayTimems:=0;
  PlayInfo.TotalClock:=0;
  PlayInfo.MesureClock:=0;
  PlayInfo.DispClock:=0;
  Result:=MDXLoad(Main.gFileInfo.GetPlayPath);
  MemSync.EndWrite;
end;

procedure TMXDRVg.MDXStop;
begin
  MemSync.BeginWrite;
  MXDRV_Stop;
  MemSync.EndWrite;
end;

procedure TMXDRVg.MMLSeek(SeekClock:integer);
begin
  MemSync.BeginWrite;
  MXDRV_PlayAt(SeekClock,99,True);
  MemSync.EndWrite;
end;

procedure TMXDRVg.MMLTrap;
begin
  if sdEnd=True then begin
    MemSync.BeginWrite;
    sdLoop:=0;
    sdFadeout:=False;
    sdEnd:=True;
    MemSync.EndWrite;
    exit;
  end;

  MemSync.BeginWrite;

  RefreshWorkParams;
  sdLoop:=GetLoopCount;
  if GetEndFlag=True then begin
    sdEnd:=True;
    end else begin
    sdEnd:=False;
  end;

  if wgl<>nil then begin
    PlayInfo.TotalClock:=trunc(wgl.PLAYTIME/4000*1024);
    PlayInfo.fPlayTimems:=PlayInfo.TotalClock;
    PlayInfo.DispClock:=wgl.PlayClock;
  end;

  MemSync.EndWrite;
end;

procedure TMXDRVg.SetMute(ch:integer;flag:boolean);
begin
  MXDRV_SetMute(ch,flag);
end;

procedure TMXDRVg.SetTransTempo(_TransTempo:integer);
var
  pKey:PMXWORK_KEY;
begin
  MemSync.BeginWrite;
  TransTempo:=_TransTempo;

  MXDRV_GetWorkVKey(pKey);
  pKey.Shift:=0;
  pKey.CTRL:=0;
  pKey.OPT1:=0;
  pKey.OPT2:=0;

  if (TransTempo<-16) then begin
    pKey.CTRL:=1;
    pKey.OPT1:=1;
  end;
  if (-16<=TransTempo) and (TransTempo<=-1) then begin
    pKey.SHIFT:=1;
    pKey.OPT1:=1;
  end;
  if (1<=TransTempo) and (TransTempo<=16) then begin
    pKey.SHIFT:=1;
    pKey.OPT2:=1;
  end;
  if (16<TransTempo) then begin
    pKey.CTRL:=1;
    pKey.OPT2:=1;
  end;

  MemSync.EndWrite;
end;

procedure TMXDRVg.GetVisual(var pSetVisual:pVisual);
var
  Ch:integer;
  tNoteNo,tVoiceNo:integer;
  function GetNowPanpot(Ch:integer):integer;
  begin
    if Ch<=7 then begin // FM
      Result:=wch[Ch].Panpot and $c0 shr 6;
      if (Result<0) or (3<Result) then Result:=0;
      end else begin // PCM
      Result:=3;
    end;
  end;
begin
  MemSync.BeginRead;

  for Ch:=0 to 15 do begin
    with pSetVisual.Channel[ch] do begin
      if Ch<=7 then begin
        mes0:=GetParamStr0(Ch);
        mes1:=GetParamStr1(Ch);
        end else begin
        mes0:='';
        mes1:='';
      end;
    end;
    if (wch[Ch].FlagsKey and $08)=0 then begin // noteoff
      MCh[Ch].NoteonFlag:=False;
      Vis.SetAnalyzeNoteoff(Ch);
      end else begin
      tNoteNo:=GetNowKeycode(Ch);
      tVoiceNo:=GetNowVoice(Ch);
      if Ch<=7 then begin
        pSetVisual.NoteKeyno[Ch]:=wch[Ch].NoteAll;
        end else begin
        pSetVisual.NoteKeyno[Ch]:=tNoteNo*64;
      end;
      if (MCh[Ch].NoteonFlag=False) or (MCh[Ch].Noteno<>tNoteNo) or (MCh[Ch].Voiceno<>tVoiceNo) then begin // noteon
        MCh[Ch].NoteonFlag:=True;
        MCh[Ch].Noteno:=tNoteNo;
        MCh[Ch].Voiceno:=tVoiceNo;
        Vis.SetAnalyzeVolume(Ch,GetNowVolume(Ch));

        with pSetVisual.Channel[Ch] do begin
          Panpot:=GetNowPanpot(Ch);
          Voice:=tVoiceNo;
          Keycode:=tNoteNo;
        end;
      end;
    end;
  end;

  MemSync.EndRead;
end;

procedure TMXDRVg.GetVisualSystem(var pSetVisual:pVisual);
begin
  MemSync.BeginRead;

  pSetVisual.PlayTime:=trunc(GetPlayTimems);
  pSetVisual.TotalClock:=GetTotalClock;
  pSetVisual.MesureClock:=GetMesureClock;
  pSetVisual.DispClock:=GetDispClock;
  pSetVisual.Tempo:=GetTempo;
  pSetVisual.LoopCount:=GetLoopCount;

  MemSync.EndRead;
end;

function TMXDRVg.GetTotalClock:integer;
begin
  MemSync.BeginRead;
  Result:=PlayInfo.TotalClock;
  MemSync.EndRead;
end;

function TMXDRVg.GetMesureClock:integer;
begin
  MemSync.BeginRead;
  Result:=PlayInfo.MesureClock;
  MemSync.EndRead;
end;

function TMXDRVg.GetDispClock:integer;
begin
  MemSync.BeginRead;
  Result:=PlayInfo.DispClock;
  MemSync.EndRead;
end;

function TMXDRVg.GetCopyright:string;
begin
  MemSync.BeginRead;

  Result:='';
  Result:=Result+'X68k MXDRV music driver version 2.06+17 Rel.X5-S'+CRLF;
  Result:=Result+'(c)1988-92 milk.,K.MAEKAWA, Missy.M, Yatsube'+CRLF+CRLF;
  Result:=Result+'Converted for Win32 [MXDRVg] V2.00a'+CRLF;
  Result:=Result+'Copyright (C) 2000-2002 GORRY.'+CRLF;

  MemSync.EndRead;
end;

end.
