unit _PMD;

interface

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

type
  TInfomation=record
    FileSize:integer;
    Comment:array[0..5] of string;
    PCMMode,PCMFilename:array[0..2] of string;
  end;

type
  TPMD = class
  private
    { Private 宣言 }
    MemSync:TMultiReadExclusiveWriteSynchronizer;
    MesureClock:integer;
    PMDWork:POPEN_WORK;
    MCh:array[0..15] of TMCh;
    TransTempo:integer;
    function VolumeNormlize(Ch,Volume:integer):integer;
    function GetChPart(Ch:integer):PQQ;
    function GetParamStr0(Ch:integer):string;
    function GetParamStr1(Ch:integer):string;
    function GetTotalms:integer;
    function GetTempo:integer;
    function PMDGetLoopCount:integer;
  public
    { Public 宣言 }
    RhythmSampleLoaded:boolean;
    Infomation:TInfomation;
    function  GetPPCFilename(PMDFilename:string):string;
    function  GetPPSFilename(PMDFilename:string):string;
    function  GetPPZFilename(PMDFilename:string):string;
    function  GetTitle(Filename:string):string;
    function  GetTitleBuf(buf:PByteArray;bufcount:integer;Filename:string):string;
    function  GetTitleInESC(Filename:string):string;
    function  GetTitleInESCBuf(buf:PByteArray;bufcount:integer;Filename:string):string;
    function  Init(PCMRate:integer):boolean;
    function  ChangeRate(rate:integer):boolean;
    procedure RefrectionSetting(PlayOnlyFlag:boolean);
    procedure FreeDLL;
    procedure PMD_GetPCMData(var buf:SmallInt;len:integer);
    procedure SetPCMLoadPath;
    procedure LoadRhythm;
    function  PMDPlay:boolean;
    procedure PMDStop;
    procedure StartFadeout;
    procedure MMLSeek(SeekClock:integer);
    procedure MMLTrap;
    procedure SetMute(ch:integer;flag:boolean);
    procedure GetVisual(var pSetVisual:pVisual);
    procedure GetVisualSystem(var pSetVisual:pVisual);
    function  GetTotalClock:integer;
    function  GetMesureClock:integer;
    function  GetDispClock:integer;
    function  GetCopyright:string;
    procedure LoadInfomation;
    procedure SetTransTempo(_TransTempo:integer);
  end;

implementation

uses MainWin,
     _MDXWinINI,_m_Tools,_const,_Vis;

// ---------------------
// ThreadFree
// ---------------------

function TPMD.GetPPCFilename(PMDFilename:string):string;
var
  tmp:array[0..1024] of char;
  cnt:integer;
begin
  for cnt:=0 to 1024 do begin
    tmp[cnt]:=char(0);
  end;

  fgetmemo3(tmp,pchar(PMDFilename),0);

  cnt:=0;
  result:='';
  while (tmp[cnt]<>#00) do begin
    result:=result+tmp[cnt];
    inc(cnt);
  end;
end;

function TPMD.GetPPSFilename(PMDFilename:string):string;
var
  tmp:array[0..1024] of char;
  cnt:integer;
begin
  for cnt:=0 to 1024 do begin
    tmp[cnt]:=char(0);
  end;

  fgetmemo3(tmp,pchar(PMDFilename),-1);

  cnt:=0;
  result:='';
  while (tmp[cnt]<>#00) do begin
    result:=result+tmp[cnt];
    inc(cnt);
  end;
end;

function TPMD.GetPPZFilename(PMDFilename:string):string;
var
  tmp:array[0..1024] of char;
  cnt:integer;
begin
  for cnt:=0 to 1024 do begin
    tmp[cnt]:=char(0);
  end;

  fgetmemo3(tmp,pchar(PMDFilename),-2);

  cnt:=0;
  result:='';
  while (tmp[cnt]<>#00) do begin
    result:=result+tmp[cnt];
    inc(cnt);
  end;
end;

function TPMD.GetTitle(Filename:string):string;
var
  fs:TFileStream;
  bufcount:integer;
  buf:PByteArray;
begin
  if FileExists(Filename)=False then begin
    Result:='';
    exit;
  end;

  fs:=TFileStream.Create(Filename,fmOpenRead or fmShareDenyWrite);
  bufcount:=fs.Size;
  GetMem(buf,BufCount);
  fs.ReadBuffer(buf[0],bufcount);
  fs.Free;
  Result:=GetTitleBuf(buf,bufcount,Filename);
  FreeMem(buf);
end;

function TPMD.GetTitleBuf(buf:PByteArray;bufcount:integer;Filename:string):string;
var
  MemoMsg:array[0..1024] of Char;
  PMemoMsg:PChar;
  lin,cnt:integer;
begin
  for lin:=0 to 2 do begin
    for cnt:=0 to 1024 do begin
      MemoMsg[cnt]:=char(0);
    end;
    PMemoMsg:=MemoMsg;
    getmemo3(PMemoMsg,buf,bufcount,lin+1);
    if lin<=1 then begin
      Result:=Result+PMemoMsg+'|';
      end else begin
      Result:=Result+PMemoMsg;
    end;
  end;

  if (trim(Result)='||') or (trim(Result)='') then Result:='['+ExtractFilename(Filename)+']';
end;

function TPMD.GetTitleInESC(Filename:string):string;
var
  fs:TFileStream;
  bufcount:integer;
  buf:PByteArray;
begin
  if FileExists(Filename)=False then begin
    Result:='';
    exit;
  end;

  fs:=TFileStream.Create(Filename,fmOpenRead or fmShareDenyWrite);
  bufcount:=fs.Size;
  GetMem(buf,BufCount);
  fs.ReadBuffer(buf[0],bufcount);
  fs.Free;
  Result:=GetTitleInESCBuf(buf,bufcount,Filename);
  FreeMem(buf);
end;

function TPMD.GetTitleInESCBuf(buf:PByteArray;bufcount:integer;Filename:string):string;
var
  MemoMsg:array[0..1024] of Char;
  PMemoMsg:PChar;
  lin,cnt:integer;
begin
  for lin:=0 to 2 do begin
    for cnt:=0 to 1024 do begin
      MemoMsg[cnt]:=char(0);
    end;
    PMemoMsg:=MemoMsg;
    getmemo2(PMemoMsg,buf,bufcount,lin+1);
    if lin<=1 then begin
      Result:=Result+PMemoMsg+'|';
      end else begin
      Result:=Result+PMemoMsg;
    end;
  end;

  if (trim(Result)='||') or (trim(Result)='') then Result:='['+ExtractFilename(Filename)+']';
end;

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

function TPMD.VolumeNormlize(Ch,Volume:integer):integer;
begin
  Result:=Volume;
  if (Ch=3) or (Ch=4) or (Ch=5) then Result:=Volume*8;
  if Ch=6 then Result:=Volume*4;
end;

function TPMD.GetChPart(Ch:integer):PQQ;
begin
  if PMDWork=nil then begin
    result:=nil;
    exit;
  end;

  case Ch of
    0: Result:=PMDWork.MusPart[0]; // FM1
    1: Result:=PMDWork.MusPart[1]; // FM2
    2: Result:=PMDWork.MusPart[2]; // FM3
    3: Result:=PMDWork.MusPart[6]; // SSG1
    4: Result:=PMDWork.MusPart[7]; // SSG2
    5: Result:=PMDWork.MusPart[8]; // SSG3
    6: Result:=PMDWork.MusPart[9]; // ADPCM
    7: Result:=PMDWork.MusPart[10]; // Rhythm
    8: Result:=PMDWork.MusPart[3]; // FM1
    9: Result:=PMDWork.MusPart[4]; // FM5
    10:Result:=PMDWork.MusPart[5]; // FM6
    11:Result:=PMDWork.MusPart[16]; // PPZ1
    12:Result:=PMDWork.MusPart[17];
    13:Result:=PMDWork.MusPart[18];
    14:Result:=PMDWork.MusPart[19];
    15:Result:=PMDWork.MusPart[20]; // PPZ5
    else Result:=nil;
  end;
end;

function TPMD.GetParamStr0(Ch:integer):string;
var
  QQ:PQQ;
  cmd:PByteArray;
  cnt:integer;
  keyno:integer;
begin
  if Ch>=8 then begin
    Result:='';
    exit;
  end;

  QQ:=GetChPart(Ch);
  keyno:=QQ.onkai;
  if keyno=$ff then keyno:=0;
  Keyno:=keyno-3;
  if Keyno<0 then Keyno:=0;
  keyno:=((keyno and $f0) shr 4)*12+(keyno and $0f);
  Result:=format('K%3d D:%4d   Pitch:%4d Data:',[keyno,QQ.detune,QQ.porta_num]);
  if integer(QQ.address)=0 then begin
    Result:=Result+'0000:000000';
    end else begin
    cmd:=pointer(integer(QQ.address));
    for cnt:=0 to 4 do begin
      Result:=Result+IntToHex(cmd[cnt],2);
      if cnt=1 then Result:=Result+':';
    end;
  end;
end;

function TPMD.GetParamStr1(Ch:integer):string;
var
  QQ:PQQ;
  len:integer;
begin
  if Ch>=8 then begin
    Result:='';
    exit;
  end;

  QQ:=GetChPart(Ch);
  if QQ.leng<0 then
    len:=0
    else
    len:=QQ.leng;
  Result:=format('@%3d v%3d-%3d L:%3d P:%s',[QQ.voicenum,VolumeNormlize(Ch,QQ.volume),QQ.volpush,len,IntToHex(integer(QQ.address),8)]);
end;

function TPMD.GetTotalms:integer;
begin
  Result:=getpos;
end;

function TPMD.GetTempo:integer;
begin
  Result:=PMDWork.tempo_d;
end;

function TPMD.PMDGetLoopCount:integer;
begin
  Result:=getloopcount;
end;

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

function TPMD.Init(PCMRate:integer):boolean;
begin
  MemSync:=TMultiReadExclusiveWriteSynchronizer.Create;

  MemSync.BeginWrite;

  if PMD_LoadDLL=False then begin
    Result:=False;
    end else begin
    Result:=True;
    RhythmSampleLoaded:=False;
    pmdwininit(nil);
    setpcmrate(PCMRate);
    RefrectionSetting(False);
    PMDWork:=getopenwork;
  end;

  TransTempo:=0;

  MemSync.EndWrite;
end;


function TPMD.ChangeRate(rate:integer):boolean;
begin
  MemSync.BeginWrite;
  setpcmrate(rate);
  MemSync.EndWrite;
  Result:=True;
end;

procedure TPMD.RefrectionSetting(PlayOnlyFlag:boolean);
begin
  MemSync.BeginWrite;

  if FMPMDINI.LoadInitADPCM=True then pmdwininit(nil);

  if PlayOnlyFlag=False then begin
    SetPCMLoadPath;
    LoadRhythm;
    setfmcalc55k(FMPMDINI.FMCalc55k);
  end;

  with FMPMDINI do begin
    setppsinterpolation(Interpolation);
    setp86interpolation(Interpolation);
    setppzinterpolation(Interpolation);

    setppsuse(PPSUse);
    setrhythmwithssgeffect(RhythmWithSSGEffect);
    setpmd86pcmmode(PMD86PCMMode);

    setfmvoldown(VolFM);
    setssgvoldown(VolSSG);
    setrhythmvoldown(VolRhythm);
    setadpcmvoldown(VolADPCM);
    setppzvoldown(VolPPZ);
  end;

  PMDWork:=getopenwork;
  MemSync.EndWrite;
end;

procedure TPMD.FreeDLL;
begin
  MemSync.BeginWrite;
  PMD_FreeDLL;
  MemSync.EndWrite;

  MemSync.Free;
end;

procedure TPMD.PMD_GetPCMData(var buf:SmallInt;len:integer);
begin
  SetTransTempo(TransTempo);
  MemSync.BeginWrite;
  getpcmdata(addr(buf),len);
  MemSync.EndWrite;
end;

procedure TPMD.SetPCMLoadPath;
var
  setcnt:integer;
  cnt:integer;
  PPCMPath:array[0..PCMFMPPMDPathCount+3] of PChar;
  PCMPath:array[0..PCMFMPPMDPathCount+3] of string;
begin
  MemSync.BeginWrite;

  setcnt:=0;
  if Main.gFileInfo.isNetworkMode=True then begin
    PCMPath[setcnt]:=Main.Network.CFG.FMPPMD_PCMPath+chr(0);
    PPCMPath[setcnt]:=PChar(PCMPath[setcnt]);
    inc(setcnt);
  end;
  for cnt:=0 to PCMFMPPMDPathCount-1 do begin
    PCMPath[setcnt]:=FMPMDINI.PCMFMPPMDPath[cnt]+chr(0);
    PPCMPath[setcnt]:=PChar(PCMPath[setcnt]);
    inc(setcnt);
  end;
  PCMPath[setcnt]:=chr(0);
  PPCMPath[setcnt]:=PChar(PCMPath[setcnt]);
  setpcmdir(@PPCMPath[0]);

  MemSync.EndWrite;
end;

procedure TPMD.LoadRhythm;
begin
  MemSync.BeginWrite;

  if loadrhythmsample(PChar(FMPMDINI.PCMYM2608Path)) then
    RhythmSampleLoaded:=True
    else
    RhythmSampleLoaded:=False;

  MemSync.EndWrite;
end;

function TPMD.PMDPlay;
var
  Res:integer;
  LenClock,LoopClock:integer;
  cont:boolean;
begin
  MemSync.BeginWrite;

  RefrectionSetting(True);

  LenClock:=0;
  LoopClock:=0;
  if getlength(PChar(Main.gFileInfo.GetPlayPath),LenClock,LoopClock)=False then begin
    Main.InfomationVisible('PMDerr:曲長の取得に失敗しました。');
    Result:=False;
    end else begin
    MesureClock:=LenClock+LoopClock;

    SetPCMLoadPath;
    Res:=music_load(PChar(Main.gFileInfo.GetPlayPath));
    if Res=PMDWIN_OK then begin
      cont:=True;
      end else begin
      case Res of
        ERR_OPEN_MUSIC_FILE: begin
          Main.InfomationVisible('PMDerr:ファイルを開けません');
          cont:=False;
        end;
        ERR_WRONG_MUSIC_FILE: begin
          Main.InfomationVisible('PMDerr:PMD以外の形式のファイルです');
          cont:=False;
        end;
        ERR_OPEN_PPC_FILE: begin
          Main.InfomationVisible('PMDerr:PPCを読み込めませんでした');
          cont:=True;
        end;
        ERR_OPEN_P86_FILE: begin
          Main.InfomationVisible('PMDerr:P86を読み込めませんでした');
          cont:=True;
        end;
        ERR_OPEN_PPS_FILE: begin
          Main.InfomationVisible('PMDerr:PPSを読み込めませんでした');
          cont:=True;
        end;
        ERR_OPEN_PPZ1_FILE: begin
          Main.InfomationVisible('PMDerr:PPZ1を読み込めませんでした');
          cont:=True;
        end;
        ERR_OPEN_PPZ2_FILE: begin
          Main.InfomationVisible('PMDerr:PPZ2を読み込めませんでした');
          cont:=True;
        end;
        ERR_WRONG_PPC_FILE: begin
          Main.InfomationVisible('PMDerr:PPCファイルが異常です');
          cont:=True;
        end;
        ERR_WRONG_P86_FILE: begin
          Main.InfomationVisible('PMDerr:P86ファイルが異常です');
          cont:=True;
        end;
        ERR_WRONG_PPZ1_FILE: begin
          Main.InfomationVisible('PMDerr:PPZ1ファイルが異常です');
          cont:=True;
        end;
        ERR_WRONG_PPZ2_FILE: begin
          Main.InfomationVisible('PMDerr:PPZ2ファイルが異常です');
          cont:=True;
        end;
        WARNING_PPC_ALREADY_LOAD: begin
//          Main.InfomationVisible('PMDerr:PPCはすでに読み込まれています');
          cont:=True;
        end;
        WARNING_P86_ALREADY_LOAD: begin
//          Main.InfomationVisible('PMDerr:P86はすでに読み込まれています');
          cont:=True;
        end;
        WARNING_PPS_ALREADY_LOAD: begin
//          Main.InfomationVisible('PMDerr:PPSはすでに読み込まれています');
          cont:=True;
        end;
        WARNING_PPZ1_ALREADY_LOAD: begin
//          Main.InfomationVisible('PMDerr:PPZ1はすでに読み込まれています');
          cont:=True;
        end;
        WARNING_PPZ2_ALREADY_LOAD: begin
//          Main.InfomationVisible('PMDerr:PPZ2はすでに読み込まれています');
          cont:=True;
        end;
        ERR_OUT_OF_MEMORY: begin
          Main.InfomationVisible('PMDerr:メモリが足りません');
          cont:=False;
        end;
        ERR_OTHER: begin
          Main.InfomationVisible('PMDerr:未定義（メールお願いします）');
          cont:=False;
        end;
        else begin
          Main.InfomationVisible('PMDerr('+inttostr(res)+'):未定義（メールお願いします）');
          cont:=False;
        end;
      end;
    end;

    if cont=False then begin
      Result:=False;
      end else begin
      Result:=True;
      LoadInfomation;
      music_start();
      PMDWork:=getopenwork;
    end;
  end;

  MemSync.EndWrite;
end;

procedure TPMD.PMDStop;
begin
  MemSync.BeginWrite;
  music_stop();
  MemSync.EndWrite;
end;

procedure TPMD.StartFadeout;
begin
  MemSync.BeginWrite;
  if sdFadeout=False then begin
    sdFadeout:=True;
    fadeout2(5000);
  end;
  MemSync.EndWrite;
end;

procedure TPMD.MMLSeek(SeekClock:integer);
begin
  MemSync.BeginWrite;
  setpos(SeekClock);
  MemSync.EndWrite;
end;

procedure TPMD.MMLTrap;
var
  QQ:PQQ;
  Ch:integer;
  PlaingFlag:boolean;
begin
  if sdEnd=True then begin
    MemSync.BeginWrite;
    sdLoop:=0;
    sdFadeout:=False;
    sdEnd:=True;
    MemSync.EndWrite;
    exit;
  end;

  MemSync.BeginWrite;

  PMDWork:=getopenwork;
  sdLoop:=getloopcount;

  PlaingFlag:=False;
  for Ch:=0 to 15 do begin
    QQ:=GetChPart(Ch);
    if (QQ.leng>=0) and (integer(QQ.address)<>0) then PlaingFlag:=True;
  end;
  if PMDWork.play_flag=0 then PlaingFlag:=False;
  sdEnd:=not PlaingFlag;

  MemSync.EndWrite;
end;

procedure TPMD.SetMute(ch:integer;flag:boolean);
var
  Mode:integer;
begin
  case ch of
    0: Mode:=0; // FM1
    1: Mode:=1; // FM2
    2: Mode:=2; // FM3
    3: Mode:=6; // SSG1
    4: Mode:=7; // SSG2
    5: Mode:=8; // SSG3
    6: Mode:=9; // ADPCM
    7: Mode:=10; // Rhythm
    8: Mode:=3; // FM4
    9: Mode:=4; // FM5
    10:Mode:=5; // FM6
    11:Mode:=16; // PPZ1
    12:Mode:=17;
    13:Mode:=18;
    14:Mode:=19;
    15:Mode:=20; // PPZ5
    else Mode:=-1;
  end;

  if Mode=-1 then exit;
  if flag=True then begin
    maskon(Mode);
    end else begin
    maskoff(Mode);
  end;
end;

procedure TPMD.GetVisual(var pSetVisual:pVisual);
var
  QQ:PQQ;
  Ch:integer;
  tNoteNo,tVoiceNo:integer;
  function GetNowPanpot(Ch:integer):integer;
  begin
    if (ch=3) or (ch=4) or (ch=5) or (ch=7) then begin // SSG and Drum
      Result:=3;
      end else begin
      case(QQ.fmpan and $c0) of
        $80: Result:=1; // L
        $40: Result:=2; // R
        $c0: Result:=3; // Mid
        else Result:=0; // Disable
      end;
    end;
  end;
begin
  MemSync.BeginRead;

  for Ch:=0 to 15 do begin
    QQ:=GetChPart(Ch);
    with pSetVisual.Channel[ch] do begin
      mes0:=GetParamStr0(Ch);
      mes1:=GetParamStr1(Ch);
    end;
    if QQ.onkai=$ff then begin // noteoff
      MCh[Ch].NoteonFlag:=False;
      Vis.SetAnalyzeNoteoff(Ch);
      end else begin
      tNoteNo:=QQ.onkai;
      tNoteNo:=((tNoteNo and $f0) shr 4)*12+(tNoteNo and $0f);
      tNoteNo:=tNoteNo-3;
      if tNoteNo<0 then tNoteNo:=0;
      tVoiceNo:=QQ.voicenum;
      pSetVisual.NoteKeyno[ch]:=tNoteNo*64+(QQ.detune+QQ.porta_num);
      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,byte(VolumeNormlize(Ch,QQ.volume)));

        with PSetVisual.Channel[Ch] do begin
          Panpot:=GetNowPanpot(Ch);
          Voice:=tVoiceNo;
          Keycode:=tNoteNo;
          if Keycode=$ff then Keycode:=0;
        end;
      end;
    end;
  end;

  MemSync.EndRead;
end;

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

  pSetVisual.PlayTime:=GetTotalms;
  pSetVisual.TotalClock:=GetTotalClock;
  pSetVisual.MesureClock:=GetMesureClock;
  pSetVisual.DispClock:=GetDispClock;
  pSetVisual.Tempo:=GetTempo;
  pSetVisual.LoopCount:=PMDGetLoopCount;

  MemSync.EndRead;
end;

function TPMD.GetTotalClock:integer;
begin
  MemSync.BeginRead;
  Result:=getpos;
  MemSync.EndRead;
end;

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

function TPMD.GetDispClock:integer;
begin
  MemSync.BeginRead;
  Result:=getpos2;
  MemSync.EndRead;
end;

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

  Result:=PMD_GetCopyright;
  if RhythmSampleLoaded=True then
    Result:=Result+'YM2608 RhythmSample loaded.'+CRLF
    else
    Result:=Result+'YM2608 RhythmSample not found.'+CRLF;

  MemSync.EndRead;
end;

procedure TPMD.LoadInfomation;
var
  MemoMsg:array[0..1024] of Char;
  PMemoMsg:PChar;
  cnt:integer;
begin
  if FileExists(Main.gFileInfo.GetPlayPath)=False then exit;

  MemSync.BeginRead;

  Infomation.FileSize:=GetFileSize(Main.gFileInfo.GetPlayPath);

  for cnt:=0 to 1024 do begin
    MemoMsg[cnt]:=char(0);
  end;
  PMemoMsg:=PChar(@MemoMsg[0]);

  fgetmemo3(PMemoMsg,nil,1);
  Infomation.Comment[0]:='Title   :'+PMemoMsg;
  fgetmemo3(PMemoMsg,nil,2);
  Infomation.Comment[1]:='Composer:'+PMemoMsg;
  fgetmemo3(PMemoMsg,nil,3);
  Infomation.Comment[2]:='Arranger:'+PMemoMsg;
  fgetmemo3(PMemoMsg,nil,4);
  Infomation.Comment[3]:='Memo1   :'+PMemoMsg;
  fgetmemo3(PMemoMsg,nil,5);
  Infomation.Comment[4]:='Memo2   :'+PMemoMsg;
  fgetmemo3(PMemoMsg,nil,6);
  Infomation.Comment[5]:='Memo3   :'+PMemoMsg;

  for cnt:=0 to 2 do begin
    case cnt of
      0: begin
        Infomation.PCMMode[0]:='PCM,PPC';
        fgetmemo3(PMemoMsg,nil,0);
      end;
      1: begin
        Infomation.PCMMode[1]:='PPS';
        fgetmemo3(PMemoMsg,nil,-1);
      end;
      2: begin
        Infomation.PCMMode[2]:='PPZ';
        fgetmemo3(PMemoMsg,nil,-2);
      end;
    end;
    Infomation.PCMFilename[cnt]:=PMemoMsg;
  end;

  MemSync.EndRead;
end;

procedure TPMD.SetTransTempo(_TransTempo:integer);
var
  t:integer;
begin
  if PMDWork=nil then exit;

  MemSync.BeginWrite;
  TransTempo:=_TransTempo;
  t:=PMDWork.tempo_d_push+TransTempo;
  if t<$00 then t:=$00;
  if t>$ff then t:=$ff;
  PMDWork.tempo_d:=t;
  MemSync.EndWrite;
end;

end.
