/* create a new raw MIDI track */ public static RawMIDITrackRec NewRawMIDITrack() { RawMIDITrackRec RawTrack = new RawMIDITrackRec(); RawTrack.EventList = new List <RawMIDIEventRec>(); return(RawTrack); }
/* import midi file from existing file pointer */ public static void ImportMIDIFileSpecified(string Where) { RawMIDIScoreRec Score = NewRawMIDIScore(); using (Stream stream = new FileStream(Where, FileMode.Open, FileAccess.Read, FileShare.Read, Constants.BufferSize)) { using (BinaryReader BufferedFile = new BinaryReader(stream)) { MIDIParseErrorType result = MIDIParseErrorType.eMIDIParseFileReadError; try { result = ParseMIDIFile(BufferedFile, Score); } catch (InvalidDataException) { // generally - unexpected eof Debug.Assert(result == MIDIParseErrorType.eMIDIParseFileReadError); } switch (result) { default: Debug.Assert(false); throw new ArgumentException(); case MIDIParseErrorType.eMIDIParseNoError: break; case MIDIParseErrorType.eMIDIParseFileReadError: MessageBox.Show( "An error occurred while reading the file. Attempting to import as much of the file as possible.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error); break; case MIDIParseErrorType.eMIDIParseBadFormat: MessageBox.Show( "The file is not a valid MIDI file. Attempting to import as much of the file as possible.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error); break; } } } if (GetRawMIDIScoreNumTracks(Score) > 0) { /* create the new document to put the tracks into */ bool TrackWasCreated = false; Document Document = new Document(); /* iterate over MIDI tracks */ int TrackLimit = GetRawMIDIScoreNumTracks(Score); for (int TrackScan = 0; TrackScan < TrackLimit; TrackScan++) { /* get track */ RawMIDITrackRec Track = GetRawMIDIScoreIndexedTrack(Score, TrackScan); /* process channels */ for (short ChannelScan = 1; ChannelScan <= 16; ChannelScan++) { bool KeepFlag; IntervalTrackRec IntervalTrack = NewIntervalTrack(); ConvertRawToInterval(Track, IntervalTrack, ChannelScan, out KeepFlag); /* process cooked track into one of our track objects */ if (KeepFlag) { QuantizedTrackRec QuantizedTrack; TrackObjectRec DocumentTrack; /* make sure we can handle this track */ if (MIDITimingType.eMIDIMeteredTime != RawMIDIScoreGetTimingMode(Score)) { MessageBox.Show( "Can't import real-time MIDI files.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } QuantizedTrack = NewQuantizedTrack(); ConvertIntervalToQuantized(IntervalTrack, QuantizedTrack, RawMIDIScoreGetPartsPerQuarterNote(Score)); DocumentTrack = new TrackObjectRec(Document); Document.TrackList.Add(DocumentTrack); TrackWasCreated = true; ConvertQuantToNote(QuantizedTrack, DocumentTrack); /* set track release point 1 to be from end */ DocumentTrack.DefaultReleasePoint1ModeFlag = NoteFlags.eRelease1FromEnd; } } } /* clear the dirty flag, since nothing was actually modified */ Document.Modified = false; /* remove track if we didn't actually find anything in the file */ if (!TrackWasCreated) { MessageBox.Show( "No tracks were found in the MIDI file.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } MainWindow MainWindow = new MainWindow(Document, null); MainWindow.Show(); } }
/* convert raw track into an interval track. MIDITrackNum should be in 1..16 */ /* *KeepFlag is set if there were real events in this track. */ public static void ConvertRawToInterval( RawMIDITrackRec RawTrack, IntervalTrackRec IntervalTrack, short MIDITrackNum, out bool KeepFlag) { int Limit; PitchEventRec[] EventTracker = new PitchEventRec[128]; Debug.Assert((MIDITrackNum >= 1) || (MIDITrackNum <= 16)); MIDITrackNum -= 1; KeepFlag = false; /* initialize correlation tracker */ for (int i = 0; i < 128; i += 1) { EventTracker[i].NoteOn = false; } /* scan all low level events */ Limit = GetRawMIDITrackLength(RawTrack); for (int i = 0; i < Limit; i += 1) { byte TypeByte; RawMIDIEventRec RawEvent = GetRawMIDITrackIndexedEvent(RawTrack, i); byte StatusByte = GetRawMIDIEventStatus(RawEvent); switch ((StatusByte >> 4) & 0x0f) { /* ignore events we don't know about */ default: break; /* 1000nnnn: note off event for channel nnnn followed by 2 data bytes */ case 0x08: NoteOffPoint: if ((StatusByte & 0x0f) == MIDITrackNum) { short Pitch; short ReleaseVelocity; /* data byte 1: velocity 0..127 */ /* data byte 2: pitch 0..127 */ if (GetRawMIDIEventMessageLength(RawEvent) >= 2) { ReleaseVelocity = (short)(0x7f & GetRawMIDIEventMessageByte(RawEvent, 1)); } else { ReleaseVelocity = 0; } if (GetRawMIDIEventMessageLength(RawEvent) >= 1) { Pitch = (short)(0x7f & GetRawMIDIEventMessageByte(RawEvent, 0)); } else { Pitch = 0; } if (EventTracker[Pitch].NoteOn) { IntEventRec IntervalEvent = NewIntervalNoteEvent( EventTracker[Pitch].StartTime, GetRawMIDIEventTime(RawEvent) - EventTracker[Pitch].StartTime, Pitch, EventTracker[Pitch].StartVelocity, ReleaseVelocity); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); EventTracker[Pitch].NoteOn = false; } else { /* no note-on event? */ } KeepFlag = true; } break; /* 1001nnnn: note on event for channel nnnn followed by 2 data bytes */ case 0x09: if ((StatusByte & 0x0f) == MIDITrackNum) { short Pitch; short Velocity; /* data byte 1: velocity 0..127 */ /* data byte 2: pitch 0..127 */ if (GetRawMIDIEventMessageLength(RawEvent) >= 2) { Velocity = (short)(0x7f & GetRawMIDIEventMessageByte(RawEvent, 1)); } else { Velocity = 64; } if (Velocity == 0) { /* note on velocity 0 actually means note off */ goto NoteOffPoint; } if (GetRawMIDIEventMessageLength(RawEvent) >= 1) { Pitch = (short)(0x7f & GetRawMIDIEventMessageByte(RawEvent, 0)); } else { Pitch = 0; } if (!EventTracker[Pitch].NoteOn) { EventTracker[Pitch].StartTime = GetRawMIDIEventTime(RawEvent); EventTracker[Pitch].StartVelocity = Velocity; EventTracker[Pitch].NoteOn = true; } else { /* note already on? */ } KeepFlag = true; } break; /* 1010nnnn: polyphonic key pressure/after touch followed by */ /* 2 data bytes */ case 0x0a: break; /* 1011nnnn: control change followed by 2 data bytes */ case 0x0b: break; /* 1110nnnn: pitch bend change followed by 2 data bytes */ case 0x0e: break; /* 1100nnnn: program change followed by 1 data byte */ case 0x0c: break; /* 1101nnnn: channel pressure/after touch followed by 1 data byte */ case 0x0d: break; /* assorted thangs */ case 0x0f: switch (StatusByte & 0x0f) { default: break; /* 11110000: initial or solitary SYSEX message. followed by a */ /* variable length field. solitary message is terminated by */ /* 0xf7 (which is not part of the message, but is included in */ /* length). continuing message does not end with 0xf7. */ /* NOTE: SYSEX also cancels the running status. */ /* 11110111: continuing SYSEX message. same format as initial. */ case 0x00: case 0x07: { StringBuilder sb = new StringBuilder(); if ((StatusByte & 0x0f) == 0x00) { sb.AppendLine("System Exclusive $F0"); } else { sb.AppendLine("System Exclusive $F7"); } int c = GetRawMIDIEventMessageLength(RawEvent); for (int j = 0; j < c; j += 1) { byte b = GetRawMIDIEventMessageByte(RawEvent, j); sb.Append('$'); sb.Append(HexBuf[(b >> 4) & 0x0f]); sb.Append(HexBuf[b & 0x0f]); if (((j + 1) % SYSEXBYTESPERLINE) == 0) { sb.AppendLine(); } else { sb.Append(' '); } } IntEventRec IntervalEvent = NewIntervalCommentEvent(GetRawMIDIEventTime(RawEvent), sb.ToString()); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); } break; /* 11110010: song position pointer, followed by 2 data bytes */ case 0x02: { StringBuilder String; string Buffer; IntEventRec IntervalEvent; String = new StringBuilder(); String.Append("Song Position Pointer" + Environment.NewLine); if (GetRawMIDIEventMessageLength(RawEvent) >= 2) { String.Append((int)((GetRawMIDIEventMessageByte(RawEvent, 0) & 0x7f) | ((GetRawMIDIEventMessageByte(RawEvent, 1) & 0x7f) << 7))); } else { String.Append((int)0); } Buffer = String.ToString(); IntervalEvent = NewIntervalCommentEvent(GetRawMIDIEventTime(RawEvent), Buffer); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); } break; /* 11110011: song select, followed by 2 data bytes */ case 0x03: { StringBuilder String; string Buffer; IntEventRec IntervalEvent; String = new StringBuilder(); String.Append("Song Select" + Environment.NewLine); if (GetRawMIDIEventMessageLength(RawEvent) >= 2) { String.Append((int)((GetRawMIDIEventMessageByte(RawEvent, 0) & 0x7f) | ((GetRawMIDIEventMessageByte(RawEvent, 1) & 0x7f) << 7))); } else { String.Append((int)0); } Buffer = String.ToString(); IntervalEvent = NewIntervalCommentEvent(GetRawMIDIEventTime(RawEvent), Buffer); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); } break; /* 11110110: tune request, no data */ case 0x06: { string String; IntEventRec IntervalEvent; String = "Tune Request"; IntervalEvent = NewIntervalCommentEvent(GetRawMIDIEventTime(RawEvent), String); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); } break; /* 11111111: meta event. followed by type byte (0..127) and */ /* then variable length specifier, then data bytes. */ case 0x0f: /* check out the auxilliary type */ TypeByte = GetRawMIDIEventTypeByte(RawEvent); switch (TypeByte) { default: break; /* (FF) 01 <len> <text>: text event */ /* (FF) 02 <len> <text>: copyright */ /* (FF) 03 <len> <text>: sequence/track name */ /* (FF) 04 <len> <text>: instrument name */ /* (FF) 05 <len> <text>: lyric */ /* (FF) 06 <len> <text>: marker */ /* (FF) 07 <len> <text>: cue point */ case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: { string String; char[] Text; IntEventRec IntervalEvent; string Combined; int Length; int Scan2; switch (GetRawMIDIEventTypeByte(RawEvent)) { default: Debug.Assert(false); throw new InvalidOperationException(); /* (FF) 01 <len> <text>: text event */ /* (FF) 02 <len> <text>: copyright */ /* (FF) 03 <len> <text>: sequence/track name */ /* (FF) 04 <len> <text>: instrument name */ /* (FF) 05 <len> <text>: lyric */ /* (FF) 06 <len> <text>: marker */ /* (FF) 07 <len> <text>: cue point */ case 0x01: String = "Comment" + Environment.NewLine; break; case 0x02: String = "Copright" + Environment.NewLine; break; case 0x03: String = "Track Name" + Environment.NewLine; break; case 0x04: String = "Instrument Name" + Environment.NewLine; break; case 0x05: String = "Lyric" + Environment.NewLine; break; case 0x06: String = "Marker" + Environment.NewLine; break; case 0x07: String = "Cue Point" + Environment.NewLine; break; } Length = GetRawMIDIEventMessageLength(RawEvent); if (Length > 0) { /* don't deal with the first character since it */ /* isn't part of the text */ Length -= 1; } Text = new char[Length + Environment.NewLine.Length]; for (Scan2 = 0; Scan2 < Length; Scan2 += 1) { Text[Scan2] = (char)GetRawMIDIEventMessageByte(RawEvent, Scan2 + 1 /*skip first character*/); } Text[Length] = Environment.NewLine[0]; Text[Length + 1] = Environment.NewLine[1]; Combined = String.Concat(String, new String(Text)); IntervalEvent = NewIntervalCommentEvent(GetRawMIDIEventTime(RawEvent), Combined); IntervalTrackInsertEventSorted(IntervalTrack, IntervalEvent); } break; /* (FF) 00 <02> ss ss: sequence number */ /* (FF) 2F <00>: end of track */ /* (FF) 51 <03> tt tt tt: set tempo (microsec/quarter note) */ /* (FF) 54 <05> hr mn se fr ff: SMTPE offset */ /* (FF) 58 <04> nn dd cc bb: time signature */ /* nn/dd is numerator/denominator. dd is a negative power */ /* of two (2=quarter note, 3=eighth note). */ /* cc = midi clocks per metronome click. */ /* bb = number of 32nd notes per quarter note (24 clocks) */ /* (FF) 59 <02> sf mi: key signature */ /* sf: -7..-1 flats, 0 = normal, 1..7 = sharps */ /* mi = 0 or major, 1 for minor */ /* (FF) 7F <len> <data>: sequencer meta event */ } break; } break; } } }
/* append a new track to the score object */ public static void RawMIDIScoreAppendTrack( RawMIDIScoreRec RawScore, RawMIDITrackRec Track) { RawScore.TrackList.Add(Track); }
/* get an indexed event from the track */ public static RawMIDIEventRec GetRawMIDITrackIndexedEvent( RawMIDITrackRec RawTrack, int Index) { return(RawTrack.EventList[Index]); }
/* get the number of events in the track */ public static int GetRawMIDITrackLength(RawMIDITrackRec RawTrack) { return(RawTrack.EventList.Count); }
/* append a new MIDI event to the track */ public static void RawMIDITrackAppendEvent( RawMIDITrackRec RawTrack, RawMIDIEventRec Event) { RawTrack.EventList.Add(Event); }