⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unitmiditrackstream.pas

📁 Delphi的另一款钢琴软件
💻 PAS
📖 第 1 页 / 共 3 页
字号:
    if ((sts = midiNoteOff) or ((sts = midiNoteOn) and (p^.data.b3 = 0))) and (p^.OnOffEvent^.pos < startPos) then
      Dec (blockSize, sizeof (TMidiEventData));

    Inc (p);
    Dec (endEvent)
  end;

  result := blockSize
end;


(*---------------------------------------------------------------------*
 | function CompareEvents (): Integer                                  |
 |                                                                     |
 | 'Compare' function used in sorting orphan lists.                    |
 |                                                                     |
 | Parameters:                                                         |
 |   p1, p2                    Events to compare.                      |
 *---------------------------------------------------------------------*)
function CompareEvents (p1, p2 : pointer) : Integer;
begin
  if PMidiEventData (p1)^.pos > PMidiEventData (p2)^.pos then
    result := 1
  else
    if PMidiEventData (p1)^.pos < PMidiEventData (p2)^.pos then
      result :=-1
    else
      if Integer (p1) > Integer (p2) then
        result := 1
      else
        result := -1
end;

(*---------------------------------------------------------------------*
 | procedure TMidiTrackStream.GetRangeData ();                         |
 |                                                                     |
 | Fill DDE buffer from range. Buffer ends up with:                    |
 |    Header                                                           |
 |    Events                                                           |
 |    Orphans                                                          |
 |    Sysex Data                                                       |
 |                                                                     |
 | Parameters:                                                         |
 |   buffer                    The buffer to fill                      |
 |   startPos                  Range start position.                   |
 |   startEvent                First event in the range                |
 |   endEvent                  Last event in the range.                |
 |   endPos                    Range end position                      |
 *---------------------------------------------------------------------*)
procedure TMidiTrackStream.GetRangeData (buffer : PChar; startPos, startEvent, endEvent, endPos : Integer);
var
  i, bufferedEvents : Integer;
  pSrc, pDst : PMidiEventData;
  buf : PChar;
  OrphanList : TList;
  sts : byte;

begin
  buf := buffer;
                             // Set up DDE header in buffer
  with PMidiEventClipboardHeader (buf)^ do
  begin
    noEvents := endEvent - startEvent + 1; // Save nominal event count.  Stubs
                                           // will be removed and orphans added...
    startPosn := startPos
  end;

                                           // Copy events into buffer
  pSrc := Event [startEvent];
  pDst := PMidiEventData (buffer + sizeof (TMidiEventClipboardHeader));

  OrphanList := Nil;
  bufferedEvents := 0;

  for i := startEvent to endEvent do
  begin
    sts := pSrc^.data.status and midiStatusMask;
    if (sts = midiNoteOn) and (pSrc^.OnOffEvent^.pos > endPos) then
    begin                                  // Add orphaned note-off to orphan list
      if not Assigned (OrphanList) then
      begin
        OrphanList := TList.Create;
        OrphanList.Capacity := 64
      end;
      OrphanList.Add (pSrc^.OnOffEvent)
    end;

    if not (((sts = midiNoteOff) or ((sts = midiNoteOn) and (pSrc^.data.b3 = 0))) and (pSrc^.OnOffEvent^.pos < startPos)) then
    begin                                   // Copy event if it's not a 'stub'
      Move (pSrc^, pDst^, sizeof (TMidiEventData));
      Inc (pDst);
      Inc (bufferedEvents)
    end
    else                                    // Remove stub from header event count.
      Dec (PMidiEventClipboardHeader (buf)^.noEvents);

    Inc (pSrc);
  end;

  if Assigned (OrphanList) then             // Copy orphans to buffer
  try
                                            // Add orphans count to header event count
    with PMidiEventClipboardHeader (buf)^ do
      Inc (noEvents, OrphanList.Count);

    if OrphanList.Count > 1 then            // .. because of VCL bug...
      OrphanList.Sort (CompareEvents);

    for i := 0 to OrphanList.Count - 1 do   // Copy orphans
    begin
      Move (OrphanList [i]^, pDst^, sizeof (TMidiEventData));
      Inc (pDst);
    end
  finally
    OrphanList.Free
  end;


  pSrc := PMidiEventData (buffer + sizeof (TMidiEventClipboardHeader));
  buffer := PChar (pDst);                   // sysex data comes after orphans
                                            // Move sysex data for each event into buffer
  for i := 0 to bufferedEvents - 1 do
  begin
    if pSrc^.sysexSize > 0 then
    begin
      move (pSrc^.data.sysex^,buffer^, pSrc^.sysexSize);
      pSrc^.data.sysex := buffer;           // Make the buffered event point to
                                            // the buffered sysex, instead of it's
                                            // original sysex.
      Inc (buffer, pSrc^.sysexSize)
    end;

    Inc (pSrc)
  end
end;

(*---------------------------------------------------------------------*
 | procedure TMidiTrackStream.DeleteRange ();                          |
 |                                                                     |
 | Delete data between the specified range.  Don't delete stubs, but   |
 | delete orphans.                                                     |
 |                                                                     |
 | Parameters:                                                         |
 |   startPos                  Range start position.                   |
 |   endPos                    Range end position                      |
 *---------------------------------------------------------------------*)
procedure TMidiTrackStream.DeleteRange (startPos, endPos : Integer);
var
  n1, n2, i : Integer;
  p, p1, pClearRange : PMidiEventData;
  deleteNoEvents : Integer;
  sts : byte;
  eventNo : Integer;
  OrphanList : TList;
begin
  if GetEventRange (startPos, endPos, n1, n2) then
  begin
    deleteNoEvents := n2 - n1 + 1;  // Nominal no of events to delete
    p := Event [n1];
    pClearRange := p;

    OrphanList := Nil;

    // First pass.  Delete thhe sysex data, get the orphan list, and calculate
    // the 'clear range'.  The clear range can be deleted in one hit.  It starts
    // after the last stub.

    for i := 0 to deleteNoEvents - 1 do
    begin
      sts := p^.data.status and midiStatusMask;
      if (sts = midiNoteOn) and (p^.OnOffEvent^.pos > endPos) then
      begin                                         // Add orphan to list.
        if not Assigned (OrphanList) then
        begin
          OrphanList := TList.Create;
          OrphanList.Capacity := 64
        end;
        OrphanList.Add (p^.OnOffEvent)
      end;

      if not (((sts = midiNoteOff) or ((sts = midiNoteOn) and (p^.data.b3 = 0))) and (p^.OnOffEvent^.pos < startPos)) then
      begin                              // Not a stub.  Delete it's sysex
        if p^.sysexSize <> 0 then        // and mark it for deletion by setting sysexSize to MaxWord
          FreeMem (p^.data.sysex);
        p^.sysexSize := MaxWord
      end
      else
      begin
        pClearRange  := p;
        Inc (pClearRange)
      end;
      Inc (p);
    end;

    // Delete orphans - they're off the end of the range, and may not be consecutive
    if Assigned (OrphanList) then
    try
      if OrphanList.Count > 1 then
        OrphanList.Sort (CompareEvents);

      for i := OrphanList.Count - 1 downto 0 do
      begin
        p1 := OrphanList [i];
        Inc (p1);
        eventNo := (Integer (p1) - Integer (Event [0])) div sizeof (TMidiEventData);
        Move (p1^, OrphanList [i]^, (EventCount - eventNo - 1) * sizeof (TMidiEventData));
        Dec (fEventCount)
      end
    finally
      OrphanList.Free
    end;

    // Delete clear range - ie. most of the events.  the clear range starts after the
    // last stub.
    eventNo := (Integer (pClearRange) - Integer (Event [0])) div sizeof (TMidiEventData);
    if p <> pClearRange then
    begin
      Move (p^, pClearRange^, (EventCount - n2 - 1) * sizeof (TMidiEventData));
      Dec (fEventCount, n2 - eventNo + 1)
    end;

    // Delete events before the clear range that are marked for deletion (ie. which aren't stubs)
    p := Event [n1];
    i := n1;
    while i < eventNo do
    begin
      if ((p^.data.status and midiStatusMask) <> midiSysex) and ( p^.SysexSize = MaxWord) then
      begin
        p1 := p;
        Inc (p1);
        Move (p1^, p^, (EventCount - i) * sizeof (TMidiEventData));
        Dec (fEventCount);
        Dec (EventNo)
      end
      else
      begin
        Inc (i);
        Inc (p)
      end
    end;

    CalcOnOffPointers;
    fChanges := True;

    // ** TODO - maybe decrease buffer size here.
  end
end;

procedure TMidiTrackStream.CopyToClipboard (startPos, endPos : Integer);
var
  startEvent, endEvent, blockSize : Integer;
  data : THandle;
  ptr : PChar;
begin
  if GetEventRange (startPos, endPos, startEvent, endEvent) then
  begin
    blockSize := CalcRangeDataSize (startEvent, endEvent, startPos, endPos);
                            // Allocate DDE buffer
    data := GlobalAlloc (GMEM_MOVEABLE or GMEM_DDESHARE, blockSize);
    ptr := GlobalLock (data);
    if ptr <> Nil then
    try
      GetRangeData (ptr, startPos, startEvent, endEvent, endPos);
      GlobalUnlock (data);
    except
      GlobalUnlock (data);
      GlobalFree (data);
      raise
    end;

    Clipboard.SetAsHandle (trackClipboardFormat, data);
  end
end;

procedure TMidiTrackStream.DeleteToClipboard (startPos, endPos : Integer);
begin
  CopyToClipboard (startPos, endPos);
  DeleteRange (startPos, endPos)
end;

procedure TMidiTrackStream.CutToClipboard (startPos, endPos : Integer);
begin
  CopyToClipboard (startPos, endPos);
  DeleteRange (startPos, endPos);
  // ** TODO Shrink events
end;

procedure TMidiTrackStream.PasteFromClipboard (Pos : Integer);
var
  Handle : THandle;
begin
  Clipboard.Open;
  try
    Handle := Clipboard.GetAsHandle (trackClipboardFormat);
    if Handle <> 0 then
    begin
      MessageBeep ($ffff);
      // ** Todo - finish the thang
    end
  finally
    Clipboard.Close
  end
end;

(*---------------------------------------------------------------------*
 | procedure TMidiTrackStream.EraseNonMetaEvents ();                   |
 |                                                                     |
 | Erase all events from a track except for meta events.               |
 *---------------------------------------------------------------------*)
procedure TMidiTrackStream.EraseNonMetaEvents;
var
  i, count : Integer;
  sp, ep : PMidiEventData;
begin
  fChanges := True;
  count := EventCount;
  sp := Memory;
  i := 0;
  while i < count do
  begin
    if sp^.data.status <> midiMeta then
                                    // Not a meta event.  Find the next meta
                                    // meta event, and delete everything up to it.
    begin
      ep := sp;
      while i < count do
      begin
        Inc (ep);
        Dec (count);
        if ep^.data.status = midiMeta then break
      end;
                                    // Found the next meta event.
      if i < count then Move (ep^, sp^, (count - i) * sizeof (TMidiEventData));
    end;
    Inc (sp);
    Inc (i)
  end;
  fEventCount := count
end;

procedure TMidiTrackStream.CalcOnOffPointers;
var
  i, count, t, s, b, n : Integer;
  sp : PMidiEventData;
  noteOns : array [0..4, TNote] of PMidiEventData;
  noteOnCount : array [TNote] of byte;
begin
  count := EventCount;
  sp := Memory;
  i := 0;
  ZeroMemory (@noteOnCount, sizeof (noteOnCount));
  while i < count do
  begin
    t := 0;
    s := sp^.data.status and midiStatusMask;
    if s = midiNoteOn then
      if sp^.data.b3 <> 0 then
        t := 1
      else
        t := 2
    else
      if s = midiNoteOff then t := 2;

    case t of
      1 :
        begin
          b := sp^.data.b2;
          n := NoteOnCount [b];
          if n < 5 then
          begin
            noteOns [n, b] := sp;
            NoteOnCount [b] := n + 1
          end
        end;

      2 :
        begin
          b := sp^.data.b2;
          n := NoteOnCount [b];
          if n > 0 then
          begin
            Dec (n);
            sp^.OnOffEvent := NoteOns [n, b];
            NoteOns [n, b]^.OnOffEvent := sp;
            NoteOnCount [b] := n
          end
        end
    end;

    Inc (i);
    Inc (sp);

  end;

  try
    for i := Low (TNote) to High (TNote) do
      if NoteOnCount [i] <> 0 then
//        raise Exception.Create ('Note ons don''t match note offs');
  except
    raise
  end
end;

function TMidiTrackStream.IndexOf (p : PMidiEventData) : Integer;
var
  q : PMidiEventData;
begin
  q := Memory;
  result := (Integer (p) - Integer (q)) div sizeof (TMidiEventData);
end;

procedure TMidiTrackStream.BeginUpdate;
begin
  Inc (fUpdateCount)
end;

procedure TMidiTrackStream.EndUpdate;
begin
  if fUpdateCount > 0 then
  begin
    Dec (fUpdateCount);
    if fUpdateCount = 0 then
      CalcOnOffPointers
  end
end;

procedure TMidiTrackStream.CancelUpdate;
begin
  if fUpdateCount > 0 then
    Dec (fUpdateCount);
end;

initialization
  trackClipboardFormat := RegisterClipboardFormat ('PowerseqMidiTrackData');
end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -