Autor Beitrag
Noodel
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Fr 23.01.09 19:24 
Ich möchte eine MIDI-Datei in Delphi per FileStream einlesen und danach analysieren: So möchte ich die einzelnen Stimmen/Instrumente rausfiltern und die MIDI-Kommandos (Taste drücken, Taste loslassen, welche Taste usw.) den Instrumenten zuordnen.
Alle wichtigen MIDI-Behlfe und der Aufbau einer MIDI-Datei sind hier beschrieben.
Mein Ansatz ist bisher so:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
function SwapInt(value:Integer):Integer;
type Tswaprec=packed record
      case boolean of
        True:(int:Integer);
        False:(b:array[1..4of byte);
     end;
var swaprec,swaprecRes:TSwapRec;
    i:integer;
begin
  swaprec.int:=value;
  for i:=1 to 4 do
    swaprecRes.b[i]:=swaprec.b[5-i];
  result:=swaprecRes.int;
end;

function SwapWord(Value:Word):Word;
type Tswaprec=packed record
      case boolean of
        True:(w:word);
        False:(b:array[1..2of byte);
     end;
var swaprec,swaprecRes:TSwapRec;
    i:integer;
begin
  swaprec.w:=value;
  for i:=1 to 2 do
    swaprecRes.b[i]:=swaprec.b[3-i];
  result:=swaprecRes.w;
end;

procedure TForm1.Button1Click(Sender: TObject);
var midi:TFileStream;
    s:string;
    i:Integer;
    w:word;
begin
  midi:=tFilestream.create('d:\mission.mid',fmopenread);
  try
    setlength(s,4);
    midi.Read(s[1],4);
    if s<>'MThd' then raise Exception.create('MThd-Fehler');
    midi.Read(i,4);
    i:=swapInt(i);
    if i<>6 then raise Exception.create('Headersize -Fehler');
    midi.Read(w,2);
    w:=swapWord(w);
    case w of
      0:showmessage('eine Spur');
      1:showmessage('mehrere Spuren, synchron');
      2:showmessage('mehrere Spuren, nicht synchron');
      else raise Exception.create('MIDI-Format unbekannt');
    end;
    midi.Read(w, 2);
    w := swapWord(w);
    if (w > 0AND (w < 26then begin
      showmessage(IntToStr(w)+' Tracks');
    end;
    midi.Read(w, 2);
    w := swapWord(w);
    showmessage(IntToStr(w)+' Delta-time ticks pro Viertelnote');
  finally
    midi.free;
  end;
end;

So lese ich aber bisher nur den Header-Chunk aus. Für die Track-Chunks fehlt mir der Ansatz. Soll ich da mit einer while-Schleife durchgehen? Muss ich da immer 2 Bytes oder 4 Bytes zusammen auslesen? Oder muss ich es als ASCII interpretieren?

Ich hoffe, ihr könnt mir helfen. Danke im Voraus für eure Antworten!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 23.01.09 19:48 
user profile iconNoodel hat folgendes geschrieben Zum zitierten Posting springen:
Für die Track-Chunks fehlt mir der Ansatz. Soll ich da mit einer while-Schleife durchgehen?
Sieht so aus. ;-)

user profile iconNoodel hat folgendes geschrieben Zum zitierten Posting springen:
Muss ich da immer 2 Bytes oder 4 Bytes zusammen auslesen?
Zitat:
Wie der Header besitzen die ersten 4 Byte ein ASCII-Äquivalent. Dieses mal ist es "MTrk". Die 4 Bytes nach "MTrk" beschreibt die Länge des Tracks (ohne den Track-Kopf) in Bytes.
Du könntest also z.B. beides einfach als Integerwert auslesen. Da MTrk konstant ist, kannst du das auch in einem Rutsch mit dem entsprechenden Integerwert vergleichen. ;-)
Und durch die Länge weißt du auch wie viel an Daten zu dem aktuellen Chunk kommen.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Fr 23.01.09 21:17 
Danke!

Ich hab es jetzt so gemacht, wie du meintest: 4 Bytes als Integer-Wert ausgelesen. So sieht der Code aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
    while midi.Position < midi.Size do begin
      midi.Read(i, 4);
      i := swapWord(i);
      Memo1.Lines.Add(IntToStr(i));
    end;

Ist das so richtig?

Wenn ich die Werte dann einzeln ausgeben lasse, kommt das raus:
20739
48560
20739
48514
20739
48514
...
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 23.01.09 21:22 
Naja, Moment, du musst immer zweimal die 4 Byte als Integer lesen und dann den restlichen Chunk. Dessen Größe steht ja im zweiten Integer.
Was macht swapWord? Ich müsste vielleicht nochmal in die Doku schauen, aber erstmal hatte ich da nix davon gesehen.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Fr 23.01.09 23:08 
Sorry, falls das jetzt dumme Fragen sind, aber ich kenne mich überhaupt nicht aus mit Hex-Daten und dem Auslesen aus Dateien:

1) Die swap-Funktionen sind zum Umdrehen von Big Endian auf Little Endian.
2) Wie groß sind denn die "Pakete", in denen ich den restlichen Chunk einlese? Sind es immer 4 Byte, 2 Byte, ...?
Auf der Seite mit der MIDI-Aufbau-Beschreibung steht ja folgendes:
"jedes MIDI-Kommando hat 2 Teile. Der linken Nybble (Ein Nybble = 4 Bit) enthält das Kommando und rechte Nybble enthält den Kanal auf welchem das Kommando ausgeführt werden soll. Es gibt 16 Midi-Kanäle und 8 Midi-Kommandos (das Kommandonybble muss einen MSB von 1 haben). In der folgenden Tabelle gibt "x" den Kanal an."
Die beiden Teile des Kommandos haben ja jeweils 0,5 Byte. Muss ich die Teile einzeln auslesen oder zusammen (als 1-Byte-Paket)?
Du hast mir jetzt zwar gesagt, wie es sein sollte, aber ich versteh das noch nicht ganz. Könntest du das bitte mal in Code schreiben (Pseudo oder Delphi)? Das wäre nett.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: So 25.01.09 13:39 
Hat keiner eine Idee? Irgendwas mache ich nämlich noch falsch. Wenn ich 4 Bytes einlese und auch wenn ich 2 Bytes einlese, kommt immer eine Zahlenkette raus, die aber nicht rauskommen sollte.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 25.01.09 21:20 
Schau dir lieber die englische Version an, die deutsche ist teilweise schlicht falsch übersetzt...
Original:
Zitat:
According to the MIDI spec, the entire delta-time should be at most 4 bytes long.
Übersetzung:
Zitat:
Die Delta-Zeit sollte mindestens 4 Bytes lang sein.
Also statt höchstens wie im Original mindestens...

Diese Seite scheint auch hilfreich zu sein:
www.camx.de/kurs_kap5.html

Ich hab mal versucht die Spezifikation umzusetzen. Ich habe das nicht getestet, sondern einfach so hingeschrieben, deshalb mag auch totaler Blödsinn dabei herauskommen oder es umständlicher sein als nötig. ;-)
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
procedure TForm1.Button1Click(Sender: TObject);

  procedure OpenMidi(FileName: string);

    function SwapCardinal(Value: Cardinal): Cardinal;
    type
      TCardinalBytes = array[0..3of Byte;
    begin
      TCardinalBytes(Result)[0] := TCardinalBytes(Value)[3];
      TCardinalBytes(Result)[1] := TCardinalBytes(Value)[2];
      TCardinalBytes(Result)[2] := TCardinalBytes(Value)[1];
      TCardinalBytes(Result)[3] := TCardinalBytes(Value)[0];
    end;

    function SwapWord(Value: Word): Word;
    begin
      Result := (Value shr 8or (Value shl 8);
    end;

  type
    TMidiFileFormat = (mffSingleTrack = 0, mffMultiTrackSync = 1, mffMultiTrackAsync = 2);
  var
    MidiFile: TFileStream;
    MidiFileSize: Int64;
    TempByteValue: Byte;
    FileFormat: TMidiFileFormat;
    DeltaTimeTicks, TrackCount, TempWordValue: Word;
    Signature, ChunkSize, DeltaValue: Cardinal;
    EventChannel, EventData1, EventData2: Byte;
  begin
    MidiFile := TFileStream.Create(FileName, fmOpenRead);
    try
      MidiFileSize := MidiFile.Size;

      // ### Header ###
      if MidiFileSize < 8 then
        raise Exception.Create('Datei kleiner als 8 Byte!');
      // Signatur
      MidiFile.ReadBuffer(Signature, SizeOf(Signature));
      if SwapCardinal(Signature) <> $4D546864 then
        raise Exception.Create('Falsche Header-Signatur!');
      // Header-Chunk-Größe
      MidiFile.ReadBuffer(ChunkSize, SizeOf(ChunkSize));
      ChunkSize := SwapCardinal(ChunkSize);
      if MidiFileSize - MidiFile.Position < ChunkSize then
        raise Exception.Create('Restliche Dateigröße kleiner als die angegebene Chunkgröße!');
      // Dateiformat
      MidiFile.ReadBuffer(TempWordValue, SizeOf(TempWordValue));
      FileFormat := TMidiFileFormat(SwapWord(TempWordValue));
      // Trackanzahl
      MidiFile.ReadBuffer(TrackCount, SizeOf(TrackCount));
      TrackCount := SwapWord(TrackCount);
      // delta-time ticks pro Viertelnote
      MidiFile.ReadBuffer(DeltaTimeTicks, SizeOf(DeltaTimeTicks));
      DeltaTimeTicks := SwapWord(DeltaTimeTicks);

      // ### Tracks ###
      while MidiFile.Position < MidiFileSize do
      begin
        // Signatur
        MidiFile.ReadBuffer(Signature, SizeOf(Signature));
        if SwapCardinal(Signature) <> $4D54726B then
          raise Exception.Create('Falsche Track-Chunk-Signatur!');
        // Track-Chunk-Größe
        MidiFile.ReadBuffer(ChunkSize, SizeOf(ChunkSize));
        ChunkSize := SwapCardinal(ChunkSize);
        if MidiFileSize - MidiFile.Position < ChunkSize then
          raise Exception.Create('Restliche Dateigröße kleiner als die angegebene Chunkgröße!');

        // ### Events ###
        // Delta-Time
        MidiFile.ReadBuffer(TempByteValue, SizeOf(TempByteValue));
        Dec(ChunkSize);
        DeltaValue := TempByteValue;
        if TempByteValue and $80 = $80 then
        begin
          DeltaValue := DeltaValue and $7F;
          repeat
            MidiFile.ReadBuffer(TempByteValue, SizeOf(TempByteValue));
            Dec(ChunkSize);
            DeltaValue := DeltaValue shl 7 + (TempByteValue and $7F);
          until (ChunkSize = 0or (TempByteValue and $80 = 0);
        end;

        while ChunkSize > 0 do
        begin
          // Command byte
          MidiFile.ReadBuffer(TempByteValue, SizeOf(TempByteValue));
          Dec(ChunkSize);
          EventChannel := TempByteValue and $F0;
          case TempByteValue shr 4 of
            $8:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                Dec(ChunkSize, 2);
                ShowMessage('Note off für Channel ' + IntToStr(EventChannel)
                  + ' - Data1: ' + IntToStr(EventData1) + ', Data2: ' + IntToStr(EventData2));
              end;
            $9:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                Dec(ChunkSize, 2);
                ShowMessage('Note on für Channel ' + IntToStr(EventChannel)
                  + ' - Data1: ' + IntToStr(EventData1) + ', Data2: ' + IntToStr(EventData2));
              end;
            $A:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                Dec(ChunkSize, 2);
                ShowMessage('Key after-touch für Channel ' + IntToStr(EventChannel)
                  + ' - Data1: ' + IntToStr(EventData1) + ', Data2: ' + IntToStr(EventData2));
              end;
            $B:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                Dec(ChunkSize, 2);
                ShowMessage('Control Change für Channel ' + IntToStr(EventChannel)
                  + ' - Data1: ' + IntToStr(EventData1) + ', Data2: ' + IntToStr(EventData2));
              end;
            $C:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                Dec(ChunkSize);
                ShowMessage('Program (patch) change für Channel ' + IntToStr(EventChannel)
                  + ' - Data: ' + IntToStr(EventData1));
              end;
            $D:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                Dec(ChunkSize);
                ShowMessage('Channel after-touch für Channel ' + IntToStr(EventChannel)
                  + ' - Data: ' + IntToStr(EventData1));
              end;
            $E:
              begin
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                Dec(ChunkSize, 2);
                ShowMessage('Pitch wheel change für Channel ' + IntToStr(EventChannel)
                  + ' - Data1: ' + IntToStr(EventData1) + ', Data2: ' + IntToStr(EventData2));
              end;
            $F:
              begin
                // Meta-Event
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.Position := MidiFile.Position + EventData1; // testweise überspringen
                Dec(ChunkSize, 1 + EventData1);
                ShowMessage('Meta-Event der Länge ' + IntToStr(EventData1));
              end;
          end;
        end;

        // Rest des Chunks testweise einfach überspringen
        MidiFile.Position := MidiFile.Position + ChunkSize;
      end;

      MidiFile.Free;
    except
      on E: Exception do
      begin
        ShowMessage('Fehler: ' + E.Message);
        MidiFile.Free;
      end;
    end;
  end;

begin
  OpenMidi('d:\mission.mid');
end;
Was hast du eigentlich vor? Also ich meine, weil du das selbst schreiben willst, schließlich gibts ja auch Komponenten zum Abspielen usw.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: So 25.01.09 22:10 
Super, danke!!!!! :)
Das ist genau der Code denn ich brauchte. Jetzt ist mein Problem eigentlich gelöst. Den Rest werde ich wohl selbst schaffen. Ich möchte die MIDI-Kommandos nicht einfach abspielen, sondern daraus Noten erzeugen lassen und noch andere Dinge damit machen. Das Ganze soll später werden wie die Software "Synthesia" (www.synthesiagame.com/). Auf der Seite ist auch ein Video, da kannst du dir ja mal ansehen, was das Programm macht.

Dafür das dein Code nicht getestet ist, ist er super: Hat direkt funktioniert. Eine Frage zu dem Code habe ich aber noch:
Wofür ist die Variable FileFormat? Brauche ich die oder kann ich die weglassen? Weil du benutzt sie später nicht mehr!?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 25.01.09 22:41 
Naja, das ist die Unterscheidung zwischen Multitrackdateien synchron, synchron und Singletrackdateien. Da ich die Datei nicht wirklich weiter auswerte, brauche ich die Unterscheidung auch nicht, für dich könnte es durchaus interessant werden.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 26.01.09 21:22 
OK, danke! Leider hab ich jetzt doch noch ein Problem mit dem Code gefunden:

Wenn ich alle Showmessage-Befehle bei den Events auskommentiere, dann wird ja im Case-Of-Teil nichts mehr ausgegeben. Dann erscheint die folgende Meldung direkt nach dem Öffnen der Midi-Datei per MidiOpen():
"Im Projekt name.exe ist eine Exception der Klasse EReadError aufgetreten. Meldung: 'Stream-Lesefehler'. Prozess wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen."
Woran liegt das?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 26.01.09 21:27 
Wie gesagt: ich hatte es nicht getestet. Hast du denn einmal debuggt wo genau der Fehler auftritt? Und du hast nur die ShowMessages gelöscht? Dann vermute ich, dass der Fehler einfach weiter hinten auftritt und du die ShowMessages nicht so lange weggeklickt hattest.

Woran das dann liegt musst du in der Datei an der entsprechenden Position schauen. Du kannst ja z.B. Debugausgaben in eine Datei schreiben, dann siehst du wo der Fehler auftritt.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 26.01.09 23:00 
Danke für die schnelle Antwort!
Ich hab die Stelle gefunden: Die Exception wird ausgeführt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
    except
      on E: Exception do
      begin
        ShowMessage('Fehler: ' + E.Message);
        MidiFile.Free;
      end;
    end;

Wenn ich diesen Teil (oder nur den Showmessage-Befehl da drin) auskommentiere, kommt der Fehler nicht mehr. Warum wird diese Exception überhaupt ausgeführt? Ist weiter oben irgendwo ein Fehler, sodass die Exception "on E" in Kraft tritt?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 26.01.09 23:13 
Der Fehler tritt dann weiter auf, nur wird er nicht mehr angezeigt. Wenn das Programm in dem Except Teil angekommen ist, dann ist es schon zu spät. Die Frage ist wo der Fehler vorher auftritt.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Di 27.01.09 22:47 
Hmm, ich hab den Code jetzt ein paar Mal durchgeguckt. Ich finde einfach keine Stelle, wo dieser Fehler auftreten könnte. Wär super, wenn sich jemand von euch den Code noch mal angucken könnte.
Außerdem bin ich mir nicht sicher, ob mit diesem Code von jaenicke die Daten komplett richtig ausgelesen werden. Meiner Meinung nach erkennt das viel zu viele Channels. Ich hab mal mein Programm angehängt. Da ist auch eine Beispiel-MIDI-Datei bei, falls ihr keine MIDIs habt. Vielleicht könnt ihr das Programm ja mal testen. Einfach starten und auf "Datei" => "MIDI laden" klicken.
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 28.01.09 03:43 
Wie wäre es mit debuggen? :roll:
20 Sekunden debuggen sagen mir: Am Anfang wird ein ChunkSize von 50 eingelesen. Danach ein metaevent. Dessen Größe wird aber mit 84 angegeben. So groß ist der Chunk aber gar nicht. :nixweiss:

Wie ich das gesehen habe habe ich mal ganz schnell als Video aufgenommen, vielleicht hilft dir das ja...
www.sj-berlin.de/ser...midi/Midi_Debug.html

// EDIT:
Ok, ich habe noch einmal geschaut. Das Metaevent hat noch einen Typ vor der Länge. 84 = $54 heißt SMPTE Offset. D.h. es müssen ebenfalls zwei Bytes gelesen werden und der zweite ist erst die Größe.
Ich habe übrigens auch nicht diese Seite noch gefunden dazu:
253.ccarh.org/handout/smf/
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 28.01.09 18:30 
Danke!!! :D
Mit dem Video das war super, da hab ichs natürlich direkt verstanden. Danke auch noch für den Tipp (Edit). Ich habe es jetzt so gemacht, so müsste es stimmen, oder?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
            $F:
              begin
                // Meta-Event
                MidiFile.ReadBuffer(EventData1, SizeOf(EventData1));
                MidiFile.ReadBuffer(EventData2, SizeOf(EventData2));
                MidiFile.Position := MidiFile.Position + EventData2; // testweise überspringen
                Dec(ChunkSize, 2 + EventData2);
                ShowMessage('Meta-Event der Länge ' + IntToStr(EventData2));
              end;


PS: Sorry, dass ich nicht debuggt habe. Ich hab auch nicht Borland Developer Studio, sondern das einfache Borland Delphi. Ich habe da die Debug-Funktion noch nie benutzt. Bisher hab ich sie nie gebraucht und weiß deshalb auch nicht, wie man damit umgeht. Ich werds mir aber mal angucken.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 28.01.09 18:55 
user profile iconNoodel hat folgendes geschrieben Zum zitierten Posting springen:
so müsste es stimmen, oder?
Ja, ich denke schon. Zumindest gab es ja so keinen Fehler mehr. Verglichen mit den MIDI-Events habe ich nicht, da fehlt mir ein guter Viewer. ;-)

// EDIT:
Ach ja: Aufgefallen ist mir, dass auf einem Channel ständig Note aus kam (und das vor dem ersten Note an) und auf anderen Note an. Wenn in den Metaevents der Rest gesteuert wird, dann kann das natürlich trotzdem stimmen. Das hab ich mir nicht weiter angeschaut.

user profile iconNoodel hat folgendes geschrieben Zum zitierten Posting springen:
PS: Sorry, dass ich nicht debuggt habe. Ich hab auch nicht Borland Developer Studio, sondern das einfache Borland Delphi. Ich habe da die Debug-Funktion noch nie benutzt. Bisher hab ich sie nie gebraucht und weiß deshalb auch nicht, wie man damit umgeht. Ich werds mir aber mal angucken.
Das ist Delphi 2006, von der Funktionalität und dem Aussehen her identisch mit dem kostenlosen Turbo Delphi Explorer, dort kannst du nur keine Packages und damit keine Fremdkomponenten nachinstallieren (aber natürlich per Code erzeugen). Genauer habe ich das hier geschrieben:
www.delphi-library.d...ewtopic.php?p=539975

Der Unterschied beim Debuggen im Vergleich zu älteren Delphiversionen ist z.B., dass man dort nicht per Mouseover den Variableninhalt sieht und Klassen auch nicht bequem auswerten kann. Strg + F7 zum Auswerten eines markierten Ausdrucks / einer Variable sowie die Überwachung von Ausdrücken und natürlich das schrittweise durchgehen per F8 gibts aber da auch schon. ;-)
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 28.01.09 20:43 
OK, dann kann ich ja nächstes Mal selber debuggen. :)

Zu deinem Edit:
Ich glaube auch, dass die Channels nicht stimmen. Denn du liest in dem Code zuerst den EventChannel aus und dann erst das Midi-Kommando. Außerdem reichen die Channels so bis 128 oder sogar noch höher, das ist sehr komisch.
Eigentlich sollten die Channels aber in dem Command Byte drinstecken, das du auch per "Case of" unterscheidest ($8, $9 usw). Kann es sein, dass dieser Wert, den du als Channel hast, eigentlich der Delta-Wert ist? Der sollte nämlich immer vor dem Kommando stehen und das würde passen. Deshalb wäre z.B. bei "0x8n kk vv" der Kanal "n" und nicht der Wert, der vor diesem Command Byte steht.
Das ist auch bei Wikipedia gut beschreiben (Tabelle):
de.wikipedia.org/wik...nterface#Statusbytes

Hast du das in deinem Code richtig oder ist da etwas vertauscht? Wenn etwas vertauscht ist: Weißt du vielleicht, was ich ändern muss, damit es passt?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 28.01.09 20:51 
Stimmt, ganz richtig ist es nicht, aber nicht so wie du jetzt vermutest. ;-)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
          MidiFile.ReadBuffer(TempByteValue, SizeOf(TempByteValue));
          Dec(ChunkSize);
          EventChannel := TempByteValue and $F0;
          case TempByteValue shr 4 of
Ich lese das eine Byte auf einmal aus. Dann lese ich den Channel aus. Allerdings ist die Bitmaske falsch. So nehme ich die linken 4 Bit statt den rechten. Also muss dort $0F bzw. $F hin. ;-)
Danach stimmt es wieder, denn ich schiebe die Bits nach rechts und habe damit nur noch die linken 4 Bit in den vorher rechten 4 Bit. Natürlich hätte ich auch wieder and nehmen und mit $80 etc. vergleichen können.

Beispiel, TempByteValue schreibe ich als 8 Bits mit deren Indizes "12345678":
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
TempByteValue and $F0;
12345678 and $F0 = 12345678 and 11110000 = 12340000
richtig wäre aber, dass die rechten Bits übrigbleiben.

TempByteValue shr 4
12345678 shr 4 = 00001234
denn die Bits werden ja um 4 Plätze nach rechts geschoben.
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mi 28.01.09 21:04 
OK, also musste ich noch das "$F0" in "$0F" ändern und alles stimmt!? Den Rest gleicht das "shr 4" wieder aus, sodass alles richtig ist!?
Jetzt müsste alles stimmen, du kannst es ja mal ausprobieren, hab das aktualisierte Programm wieder angehängt.

Und wie komme ich jetzt an den korrekten DeltaValue? Bei dem aktuellen Code ist er nämlich immer 0, das kann ja nicht richtig sein. Man braucht den DeltaValue ja, weil mit ihm und den DeltaTimeTicks/Viertelnote den aktuellen Timestamp für jedes Kommando berechnet.
Einloggen, um Attachments anzusehen!