AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Hardcode MidiHeader

Ein Thema von EWeiss · begonnen am 13. Apr 2011 · letzter Beitrag vom 13. Apr 2011
Antwort Antwort
EWeiss
(Gast)

n/a Beiträge
 
#1

Hardcode MidiHeader

  Alt 13. Apr 2011, 09:21
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

Geändert von EWeiss (13. Apr 2011 um 09:39 Uhr)
  Mit Zitat antworten Zitat
generic

Registriert seit: 24. Mär 2004
Ort: bei Hannover
2.415 Beiträge
 
Delphi XE5 Professional
 
#2

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 09:57
das ist eine ungünstige variante:
 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.
Coding BOTT - Video Tutorials rund um das Programmieren - https://www.youtube.com/@codingbott

Geändert von generic (13. Apr 2011 um 10:01 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.142 Beiträge
 
Delphi 12 Athens
 
#3

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 10:15
Bytes[0] hast du vergessen
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#4

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 10:29
das ist eine ungünstige variante:
 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

Geändert von EWeiss (13. Apr 2011 um 10:37 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von fkerber
fkerber
(CodeLib-Manager)

Registriert seit: 9. Jul 2003
Ort: Ensdorf
6.723 Beiträge
 
Delphi XE Professional
 
#5

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 10:36
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
Frederic Kerber
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#6

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 10:39
PDF habe ich daher stammen auch einige Informationen.
Der Link oben scheint Interessant zu sein.

Danke.

gruss
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#7

AW: Hardcode MidiHeader

  Alt 13. Apr 2011, 19:07
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) = '1then
          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) = '1then
        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) = '1then
        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
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:45 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