Autor Beitrag
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19289
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 28.01.09 20:06 
Ist der Deltawert wirklich immer 0? Das Problem ist: Wenn der 0 ist, dann ist das Bit nicht gesetzt. Und das markiert eben das Ende des Werts.
Das der Wert am Anfang 0 ist, sollte richtig sein, aber immer natürlich nicht. Mal schauen...
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Do 29.01.09 17:09 
Ja, der DeltaValue ist immer 0. Zumindest in der while-Schleife (ChunkSize > 0) sollte der DeltaValue ja immer einen Wert > 0 haben. Aber auch da hat er immer den Wert 0. Irgendwo muss also noch ein kleiner Fehler sein. Ich weiß nur leider nicht wo.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19289
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 29.01.09 18:00 
Das Auslesen klappt aber offensichtlich. Um das zu verdeutlichen probiere einmal diesen Code aus. Der spielt auf dem internen Lautsprecher exakt die Melodie, die in der Datei drin ist, ab. Natürlich jetzt nicht mit den richtigen Noten, aber die Tonänderungen stimmen überein.
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:
  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 or fmShareDenyNone);
    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 $F;
          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);
                Windows.Beep(EventData1 * 10500);
                Sleep(500);

//                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.ReadBuffer(EventData2, SizeOf(EventData2));
                MidiFile.Position := MidiFile.Position + EventData2; // testweise überspringen
                Dec(ChunkSize, 2 + EventData2);
//                ShowMessage('Meta-Event der Länge ' + IntToStr(EventData2));
              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 + ' an Position ' + IntToStr(MidiFile.Position));
        MidiFile.Free;
      end;
    end;
    ShowMessage('Fertig!');
  end;
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Do 29.01.09 21:43 
Ja, das scheint wirklich zu stimmen. Aber man muss ja an die richtigen DeltaValues kommen, damit man weiß, wie lange eine Note gespielt wird. Wenn man den richtigen DeltaValue nicht hat, muss man (wie in diesem Beispiel-Code) eine festgelegte Zeit nehmen, die dann für jede Note gleich ist. Das war ja jetzt nur ein Test, aber beim späteren Programm soll das natürlich anders sein. Also muss man irgendwie die richtige DeltaTime rauskriegen, aber wie?
Also ich habe schon mal durchgeguckt: Dieses Mal scheint es nicht daran zu liegen, dass zwei Buchstaben vertauscht sind ("0F" statt "F0" oder so). Es muss also irgendeinen anderen Grund dafür geben, dass der DeltaValue immer 0 ist. Der ist doch 0, oder? Das kannst du ja in deinem Developer Studio überwachen (wie in deinem Video gezeigt).
Noodel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Sa 31.01.09 20:23 
Hat noch jemand eine Idee, woran es liegen könnte, dass der DeltaValue immer 0 ist?
Im Moment wird er ja hier ausgelesen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
        // 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;

Ich glaube aber, das ist nicht richtig. Denn jedes MIDI-Kommando hat noch eine eigene Delta-Zeitangabe. Die müsste man also auslesen, wenn man auch die einzelnen Kommandos ausliest (while ChunkSize > 0). Aber da wird ja im Moment schon alles ausgelesen. Ist vielleicht einer der Werte, die jetzt schon ausgelesen werden, die Delta-Zeit?
PeterK
Hält's aus hier
Beiträge: 1



BeitragVerfasst: Mo 13.07.09 16:52 
www.wilsonc.demon.co.uk/delphi3.htm

* MIDI Controls & Sequencer

D4 version, with lots of new features, and vastly enhanced 'sequencer' demo. Beta version of my MIDI controls for Delphi 3 & 4 (Windows 95 and NT). Read, write and manipulate MIDI files. Do MIDI playback, including fast forward, etc. Display MIDI data in piano roll and event list styles. Unlimited virtual midi output ports. All sorts of other goodies including the famous piano keyboard control. Source included.

Now includes variable playback tempo, Tempo Map Component, Controller Map Component.
Mitmischer 1703
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 754
Erhaltene Danke: 19

Win 7, Debian
Delphi Prism, Delphi 7, RAD Studio 2009 Academic, C#, C++, Java, HTML, PHP
BeitragVerfasst: Mo 13.07.09 17:00 
Hm, nach meinem Gehör sind alle Noten mit b-chen oder Kreuzchen (also cis/des...) immer ein c!

_________________
Die Lösung ist nicht siebzehn.
juerg5524
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Di 14.07.20 10:22 
So wie die DeltaZeit ausgelesen wird, muss auch die variable Zeit nach einem Ton-An-Kommando bestimmt werden. Damit der Ton klingt, einen "sleep" zu dieser Zeit einsetzen.

Übrigens: Beim Meta-Event muss ebenfalls die Länge mit variabler Länge bestimmt werden.
juerg5524
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Mi 22.07.20 12:06 
Zur überarbeiteten Source: entwickler-ecke.de/viewtopic.php?t=118277