Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Hardcode MidiHeader (https://www.delphipraxis.net/159796-hardcode-midiheader.html)

EWeiss 13. Apr 2011 09:21

Hardcode MidiHeader
 
Habe mich mal im Internet auf diversen Seiten schlau gemacht über die zusammensetzung der Midi Header

Das ist dabei herausgekommen.
Wenn sich jemand auskennt ..
Sind die Daten so korrekt?

Delphi-Quellcode:
procedure ExpMidiFile;
var
  Bytes: array[0..10000] of byte;

begin
  // MIDI File Header Chunk (14 bytes)
  // MTHD
  Bytes[1] := ord('M');
  Bytes[2] := ord('T');
  Bytes[3] := ord('h');
  Bytes[4] := ord('d');

  // Headerlänge 6 bytes
  Bytes[5] := 0;
  Bytes[6] := 0;
  Bytes[7] := 0;
  Bytes[8] := 6;

  // 6 byte Header
  // 0 - Einzelne Spur
  // 1 - Mehrere Spuren, Syncron
  // 2 - Mehrere Spuren, ASyncron
  Bytes[9] := 0;
  Bytes[10] := 1;

  Bytes[11] := 0;
  Bytes[12] := 9; // Spur Nummer

  Bytes[13] := 0;
  Bytes[14] := 48; // Anzahl der Delta-Zeit sekunde pro Viertelnote

  // Spur 1
  // MTRK
  Bytes[15] := ord('M');
  Bytes[16] := ord('T');
  Bytes[17] := ord('r');
  Bytes[18] := ord('k');

  // Anzahl der Bytes in der Spur
  Bytes[19] := 0;
  Bytes[20] := 0;
  Bytes[21] := 0;
  Bytes[22] := 19;

  // Geschwindigkeit
  Bytes[23] := 0;  // delta Zeit
  Bytes[24] := 255; // FF (Meta Befehl)
  Bytes[25] := 81; // Tempo Befehl
  Bytes[26] := 3;  // 3 bytes für Tempo Beschreibung

  // Zeit Signatur
  Bytes[30] := 0;  // delta Zeit
  Bytes[31] := 255; // FF (Meta Befehl)
  Bytes[32] := 88; // Zeit Signatur Befehl
  Bytes[33] := 4;  // 4 bytes
  Bytes[34] := 4;  // Zähler
  Bytes[35] := 2;  // Nenner (2 = 1/4, 3 = 1/8, 4 = 1/16, etc.)
  Bytes[36] := 24; // ?
  Bytes[37] := 8;  // 32 Note in einer 4 Note;

  // Spur Ende
  Bytes[38] := 0;   // delta Zeit
  Bytes[39] := 255; // Spurende Befehl
  Bytes[40] := 47;
  Bytes[41] := 0;
Bin erst am Anfang und ist nicht so leicht umzusetzen.
Für alle bytes habe ich keine Infos gefunden.
Bringt mir ja nichts wenn mein Header falsch ist und die MIDI nachher nicht als Midi erkannt wird.

gruss

generic 13. Apr 2011 09:57

AW: Hardcode MidiHeader
 
das ist eine ungünstige variante:
Delphi-Quellcode:
 Bytes: array[0..10000] of byte;
Du solltest lieber einen Record verweden, welche die einzelnen Feld mit Namen definiert.

Pseudocode:
Delphi-Quellcode:
type
midi = packed record
// MIDI File Header Chunk
  Header: array[1..4] of char;
// Headerlänge
  HeaderSize: cardinal; // hier evtl. auf little/big endian achten!
  [...]
end;
Dann wenn du es nutzt initialisieren -> also in rec.header den String schreiben.
Wenn du die Daten dann weiter nutzt brauchst du nur den Zeiger auf den Record weiterverarbeiten.


Bei dem Datenaufbau, wirst du mehrere Records brauchen bzw. diese sind dann nützlich.

himitsu 13. Apr 2011 10:15

AW: Hardcode MidiHeader
 
Bytes[0] hast du vergessen :stupid:

EWeiss 13. Apr 2011 10:29

AW: Hardcode MidiHeader
 
Zitat:

Zitat von generic (Beitrag 1094787)
das ist eine ungünstige variante:
Delphi-Quellcode:
 Bytes: array[0..10000] of byte;
Du solltest lieber einen Record verweden, welche die einzelnen Feld mit Namen definiert.

Pseudocode:
Delphi-Quellcode:
type
midi = packed record
// MIDI File Header Chunk
  Header: array[1..4] of char;
// Headerlänge
  HeaderSize: cardinal; // hier evtl. auf little/big endian achten!
  [...]
end;
Dann wenn du es nutzt initialisieren -> also in rec.header den String schreiben.
Wenn du die Daten dann weiter nutzt brauchst du nur den Zeiger auf den Record weiterverarbeiten.


Bei dem Datenaufbau, wirst du mehrere Records brauchen bzw. diese sind dann nützlich.

Danke werde mal schaun ob ich das mit dem Record auf die reihe kriege.
Sagte ja Hardcoded und aus Informationen aus dem Net zusammen gesucht.

Gibt ja ne Componente hier aber kann ich so nicht verwenden.

Zitat:

Bytes[0] hast du vergessen
Was in 0 für Daten stehen ist mir nicht bekannt.
Muss dann das array halt auf array[1..10000] setzen.

Hatte eigentlich vor den Header von Hand aus weiter zu verarbeiten.
Delphi-Quellcode:
      Bytes[CurrentByte] := ord('M');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('T');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('r');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('k');
      inc(CurrentByte);

      CurrentByte := CurrentByte + 4;

gruss

fkerber 13. Apr 2011 10:36

AW: Hardcode MidiHeader
 
Hi,

vielleicht hilft dir ja das hier:

http://www.skytopia.com/project/articles/midi.html
oder das hier:
http://duskblue.org/proj/toymidi/midiformat.pdf


LG, Frederic

EWeiss 13. Apr 2011 10:39

AW: Hardcode MidiHeader
 
Zitat:

Zitat von fkerber (Beitrag 1094806)
Hi,

vielleicht hilft dir ja das hier:

http://www.skytopia.com/project/articles/midi.html
oder das hier:
http://duskblue.org/proj/toymidi/midiformat.pdf


LG, Frederic

PDF habe ich daher stammen auch einige Informationen.
Der Link oben scheint Interessant zu sein.

Danke.

gruss

EWeiss 13. Apr 2011 19:07

AW: Hardcode MidiHeader
 
Jetzt dürft ihr alle nochmal über mich und meinen Programmierstil herziehen.

Delphi-Quellcode:
unit uMidi;

interface
uses Windows, Classes, SKAeroAPI, Math, SysUtils, uGlobal, uSound;

var
  CurrentByte:       Integer;
  DeltaTime:         Integer;
  LastDeltaTime:     Integer;
  TotalBytes:        Integer;
  MidiInst:          Integer;
  TrackDataStartByte: Integer;
  MidiIsDrum:        BOOL;
  MidiDrumNum:       Integer;
  Bytes:             array[1..10000] of byte;
  DeltaBinArray:     array[0..3] of string;
  DeltaTimeArray:    array[0..3] of byte;
  DB:                Integer;
  TempZ:             Integer;
 
function ConvertToBinary(BInput: Integer): string;
procedure ExpMidiFile(MidiFile: string);

implementation
uses uMidiTracker;

procedure MidiFileStartNote(ActiveSpur, ActiveNote: Integer);
var
  Flip: Integer;

begin

  if MidiIsDrum then
  begin
    Flip := MidiDrumNum;
    TempZ := 9;
  end else
  begin
    Flip := 127 - Grid[ActiveSpur, ActiveNote];
    TempZ := ActiveSpur;
  end;
  // Start Noten Befehl + Channel
  Bytes[CurrentByte] := 144 + TempZ;
  inc(CurrentByte);
  Bytes[CurrentByte] := Flip + Basenote; // Note + Basenote
  inc(CurrentByte);
  Bytes[CurrentByte] := 127; // Geschwindigkeit
  inc(CurrentByte);

end;

procedure MidiFileStopNote(ActiveSpur: Integer);
var
  Flip: Integer;

begin

  if MidiIsDrum then
  begin
    Flip := MidiDrumNum;
    TempZ := 9;
  end else
  begin
    Flip := 127 - (MidiTracker.OldNote[ActiveSpur] mod 1000);
    TempZ := ActiveSpur;
  end;
  // Stop Noten Befehl + Channel
  Bytes[CurrentByte] := 128 + TempZ;
  inc(CurrentByte);
  Bytes[CurrentByte] := Flip + Basenote; // Note + Basenote
  inc(CurrentByte);
  Bytes[CurrentByte] := 0; // Geschwindigkeit
  inc(CurrentByte);

end;

procedure ConvertDeltaTime;
var
  DB, DA, DC, DD: Integer;
  Temp:  string;

begin
    // 12 Delta schläge pro 16tel Note
    DB := DeltaTime * 12;
    Temp := ConvertToBinary(DB);

    DA := Length(Temp) mod 7;
    if DA <> 0 then
      Temp := StringOfChar('0', 7 - DA) + Temp;

    DD := Length(Temp) div 7 - 1;
    for DA := 0 to DD do
    begin
      DeltaBinArray[DA] := MidStr(Temp, DA * 7 + 1, 7);
      if DA = DD then
        DeltaBinArray[DA] := '0' + DeltaBinArray[DA]
      else
      DeltaBinArray[DA] := '1' + DeltaBinArray[DA];
    end;

    // Konvertiere Binärzeichenfolge in DeltaBinArray zu Bytes ins DeltaTimeArray
    for DA := 0 to DD do
    begin
      DB := 0;
      for DC := 1 to 8 do
      begin
        if MidStr(DeltaBinArray[DA], DC, 1) = '1' then
          DB := DB + trunc(Math.Power(2, (8 - DC)));
      end;
      DeltaTimeArray[DA] := DB;
    end;

    for DA := 0 to DD do
    begin
      Bytes[CurrentByte] := DeltaTimeArray[DA];
      inc(CurrentByte);
    end;

end;


procedure ConvertTempo;
var
  DB:    Integer;
  DA, DC: Integer;
  MPQN:  Integer;
  BytesArray: array[0..2] of string; // microsekunden pro viertel Note
  Temp:  string;
 
begin

  MPQN := 60000000 div MidiTracker.Tempo;

  DB := MPQN;
  Temp := ConvertToBinary(DB);

  Temp := StringOfChar('0', 24 - Length(Temp)) + Temp;

  for DA := 0 to 2 do
    BytesArray[DA] := MidStr(Temp, DA * 8 + 1, 8);

  // Konvertiere Binärzeichenfolge in BytesArray zu Bytes
  for DA := 0 to 2 do
  begin
    DB := 0;
    for DC := 1 to 8 do
    begin
      if MidStr(BytesArray[DA], DC, 1) = '1' then
        DB := DB + trunc(Math.Power(2, (8 - DC)));
    end;
    Bytes[27 + DA] := DB;
  end;

end;

function ConvertToBinary(BInput: Integer): string;
var
  Power: Integer;
  BTemp: string;
  BNum:  Integer;
  BA, BB: Integer;

begin
  BNum := BInput;

  Power := 0;
  while trunc(Math.Power(2, Power)) < BNum do
    inc(Power);

  dec(Power);

  if Power < 0 then
    Power := 0;

  BTemp := '';

  for BA := Power downto 0 do
  begin
    BB := trunc(Math.Power(2, BA));
    if BNum >= BB then
    begin
      BTemp := BTemp + '1';
      BNum := BNum - BB;
    end else
    BTemp := BTemp + '0'
  end;
  Result := BTemp;

end;

procedure ConvertTrackLength;
var
  DA, DC: Integer;
  TotalBytesArray: array[0..3] of string;
  Temp: string;
 
begin
  // Ins binär formate konvertieren
  DB := TotalBytes;
  Temp := ConvertToBinary(DB);

  Temp := StringOfChar('0', 32 - length(Temp)) + Temp;

  for DA := 0 to 3 do
    TotalBytesArray[DA] := MidStr(Temp, DA * 8 + 1, 8);

  // Konvertiere Binärzeichenfolge in TotalBytesArray zu Bytes
  for DA := 0 to 3 do
  begin
    DB := 0;
    for DC := 1 to 8 do
    begin
      if MidStr(TotalBytesArray[DA], DC, 1) = '1' then
        DB := DB + trunc(Math.Power(2, (8 - DC)));
    end;
    Bytes[TrackDataStartByte - (4 - DA)] := DB;
  end;

end;

procedure ExpMidiFile(MidiFile: string);
var
  IntZ: Integer;
  IntA: Integer;
  f:   TFileStream;

begin
  // MIDI File Header Chunk (14 bytes)
  // MTHD
  Bytes[1] := ord('M');
  Bytes[2] := ord('T');
  Bytes[3] := ord('h');
  Bytes[4] := ord('d');

  // Headerlänge 6 bytes
  Bytes[5] := 0;
  Bytes[6] := 0;
  Bytes[7] := 0;
  Bytes[8] := 6;

  // 6 byte Header
  // 0 - Einzelne Spur
  // 1 - Mehrere Spuren, Syncron
  // 2 - Mehrere Spuren, ASyncron
  Bytes[9] := 0;
  Bytes[10] := 1;

  Bytes[11] := 0;
  Bytes[12] := 9; // Spur Nummer

  Bytes[13] := 0;
  Bytes[14] := 48; // Anzahl der Delta-Zeit sekunde pro Viertelnote

  // Spur 1
  // MTRK
  Bytes[15] := ord('M');
  Bytes[16] := ord('T');
  Bytes[17] := ord('r');
  Bytes[18] := ord('k');

  // Anzahl der Bytes in der Spur
  Bytes[19] := 0;
  Bytes[20] := 0;
  Bytes[21] := 0;
  Bytes[22] := 19;

  // Geschwindigkeit
  Bytes[23] := 0;  // delta Zeit
  Bytes[24] := 255; // FF (Meta Befehl)
  Bytes[25] := 81; // Tempo Befehl
  Bytes[26] := 3;  // 3 bytes für Tempo Beschreibung

  ConvertTempo; // 3 bytes fürs Tempo schreiben

  // Zeit Signatur
  Bytes[30] := 0;  // delta Zeit
  Bytes[31] := 255; // FF (Meta Befehl)
  Bytes[32] := 88; // Zeit Signatur Befehl
  Bytes[33] := 4;  // 4 bytes
  Bytes[34] := 4;  // Zähler
  Bytes[35] := 2;  // Nenner (2 = 1/4, 3 = 1/8, 4 = 1/16, etc.)
  Bytes[36] := 24; // ?
  Bytes[37] := 8;  // 32 Note in einer 4 Note;

  // Spur Ende
  Bytes[38] := 0;  // delta Zeit
  Bytes[39] := 255; // Spurende Befehl
  Bytes[40] := 47;
  Bytes[41] := 0;

  With MidiTracker do
  begin
    // Spuren schreiben
    CurrentByte := 41;
    MidiTracker.FindEndOfSong;
    inc(MidiTracker.SongLength);

    // Spuren einlesen
    for IntZ := 0 to 7 do
    begin
      inc(CurrentByte);

      OldNote[IntZ] := -1;
      OldInst[IntZ] := -1;
      LastDeltaTime := 0;
      TotalBytes := 0;

      // MTRK
      Bytes[CurrentByte] := ord('M');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('T');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('r');
      inc(CurrentByte);
      Bytes[CurrentByte] := ord('k');
      inc(CurrentByte);
      CurrentByte := CurrentByte + 4;

      TrackDataStartByte := CurrentByte;

      if SKAERO_GetCheckButtonStatus(SKAERO_GetMainItem(MainHandle,
        IntZ + ID_HIDETRACK_FIRST)) = False then
      begin
        for IntA := 0 to SongLength do
        begin
          // NotenTyp suchen
          if OldNote[IntZ] = -1 then
            OldNoteType := BlankNote;
          if (OldNote[IntZ] >= 0) and (OldNote[IntZ] < 1000) then
            OldNoteType := StartingNote;
          if OldNote[IntZ] >= 1000 then
            OldNoteType := ContinuingNote;

          if IntA = SongLength then
          begin
            NoteType := BlankNote;
          end else
          begin
            if Grid[IntZ, IntA] = -1 then
              NoteType := BlankNote;
            if (Grid[IntZ, IntA] >= 0) and (Grid[IntZ, IntA] < 1000) then
              NoteType := StartingNote;
            if Grid[IntZ, IntA] >= 1000 then
              NoteType := ContinuingNote;
          end;

          // Ändere das Instrument wenn nötig
          //  wird eine neue Note gespielt und das Instrument unterscheidet sich vom alten
          MidiInst := (InstGrid[IntZ, IntA]);
          if (NoteType = StartingNote) and (MidiInst <> OldInst[IntZ]) then
          begin
            // Instrument wählen
            if MidiInst < 128 then
            begin
              DeltaTime := IntA - LastDeltaTime;
              ConvertDeltaTime;
              // Intrumenten Befehl + Channel
              Bytes[CurrentByte] := 192 + IntZ;
              inc(CurrentByte);
              Bytes[CurrentByte] := MidiInst;
              inc(CurrentByte);
              LastDeltaTime := IntA;
              MidiIsDrum := False;
            end else
            begin
              // Perkussions Instrument
              MidiIsDrum := True;
              MidiDrumNum := MidiInst - 93;
            end;
          end;

          //Stop die Alte Note und starte Neue
          if (OldNoteType <> BlankNote) and (NoteType = StartingNote) then
          begin
            // Stope die Alte Note
            DeltaTime := IntA - LastDeltaTime;
            ConvertDeltaTime;
            MidiFileStopNote(IntZ);
            // Starte neue Note
            Bytes[CurrentByte] := 0; // Delta zeit
            inc(CurrentByte);
            MidiFileStartNote(IntZ, IntA);
            LastDeltaTime := IntA;
          end;

          // Stop die Alte Note aber keine Neue
          if (OldNoteType <> BlankNote) and (NoteType = BlankNote) then
          begin
            DeltaTime := IntA - LastDeltaTime;
            ConvertDeltaTime;
            MidiFileStopNote(IntZ);
            LastDeltaTime := IntA;
          end;

          // Starte neue Note aber stoppe nicht die Alte
          if (OldNoteType = BlankNote) and (NoteType = StartingNote) then
          begin
            DeltaTime := IntA - LastDeltaTime;
            ConvertDeltaTime;
            MidiFileStartNote(IntZ, IntA);
            LastDeltaTime := IntA;
          end;

          // Setze die Alte Note als Aktuelle note
          OldNote[IntZ] := Grid[IntZ, IntA];

          // Setze das Alte Instrument als Aktuelles
          if NoteType = StartingNote then
            OldInst[IntZ] := InstGrid[IntZ, IntA];
        end; // End for IntA
      end; // End SKAERO_GetCheckButtonStatus

      // Spur ende
      DeltaTime := SongLength - LastDeltaTime; // Delta Zeit
      ConvertDeltaTime;
      Bytes[CurrentByte] := 255;
      inc(CurrentByte);
      Bytes[CurrentByte] := 47;
      inc(CurrentByte);
      Bytes[CurrentByte] := 0;

      TotalBytes := CurrentByte - TrackDataStartByte + 1;
      ConvertTrackLength;
    end; // for IntZ
  end; // End with MidiTracker

  // Datei schreiben
  f := TFileStream.Create(MidiFile, fmCreate or fmOpenReadWrite);
  f.Write(Bytes[1], CurrentByte);
  f.Free;

end;

end.
Und es funktioniert 100% auch ohne Header als Record und der ganze andere Kram
der für mich in manchen Situationen uninteressant und unnötig ist.
Warum eine Komponente vom 500KB in ein Projekt integrieren wenn es auch anders geht.

gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz