/* set MIDI metered timing */ public static void RawMIDIScoreSetMeteredTime( RawMIDIScoreRec RawScore, int PartsPerQuarterNote) { RawScore.TimingMode = MIDITimingType.eMIDIMeteredTime; RawScore.PartsPerQuarterNote = PartsPerQuarterNote; }
/* set the MIDI file type */ public static void RawMIDIScoreSetFormatType( RawMIDIScoreRec RawScore, MIDIFileFormatType Format) { Debug.Assert((Format == MIDIFileFormatType.eMIDIFormat0SingleTrack) || (Format == MIDIFileFormatType.eMIDIFormat1ParallelTracks) || (Format == MIDIFileFormatType.eMIDIFormat2SequentialTracks)); RawScore.FileFormat = Format; }
/* set MIDI real time */ public static void RawMIDIScoreSetRealTime( RawMIDIScoreRec RawScore, int FramesPerSecond, int TicksPerFrame) { RawScore.TimingMode = MIDITimingType.eMIDIRealTime; RawScore.FramesPerSecond = FramesPerSecond; RawScore.TicksPerFrame = TicksPerFrame; }
/* create a new MIDI score object */ public static RawMIDIScoreRec NewRawMIDIScore() { RawMIDIScoreRec RawScore = new RawMIDIScoreRec(); RawScore.TrackList = new List <RawMIDITrackRec>(); RawScore.TimingMode = MIDITimingType.eMIDIMeteredTime; RawScore.PartsPerQuarterNote = 24; RawScore.FramesPerSecond = 25; RawScore.TicksPerFrame = 40; RawScore.FileFormat = MIDIFileFormatType.eMIDIFormat1ParallelTracks; return(RawScore); }
/* 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(); } }
/* attempt to parse a MIDI file */ public static MIDIParseErrorType ParseMIDIFile( BinaryReader InputFile, RawMIDIScoreRec Score) { string CharBuff; int HeaderChunkLength; short FileFormat; short TrackCount; ushort TimingCode; /* parse header */ /* 'MThd' */ CharBuff = InputFile.ReadFixedStringASCII(4); if (!String.Equals(CharBuff, "MThd")) { return(MIDIParseErrorType.eMIDIParseBadFormat); } /* 4-byte big endian header length (minus 8 for MThd & this length) */ HeaderChunkLength = InputFile.ReadInt32BigEndian(); if (HeaderChunkLength != 6) { return(MIDIParseErrorType.eMIDIParseBadFormat); } /* 2-byte unsigned big endian format indicator */ /* 0 = type 0 format (single multi-channel track) */ /* 1 = type 1 format (multiple tracks in parallel) */ /* 2 = type 2 format (multiple tracks in sequence) */ FileFormat = InputFile.ReadInt16BigEndian(); switch (FileFormat) { default: return(MIDIParseErrorType.eMIDIParseBadFormat); break; case 0: RawMIDIScoreSetFormatType(Score, MIDIFileFormatType.eMIDIFormat0SingleTrack); break; case 1: RawMIDIScoreSetFormatType(Score, MIDIFileFormatType.eMIDIFormat1ParallelTracks); break; case 2: RawMIDIScoreSetFormatType(Score, MIDIFileFormatType.eMIDIFormat2SequentialTracks); break; } /* 2-byte unsigned big endian track count */ TrackCount = InputFile.ReadInt16BigEndian(); /* 2-byte timing code */ /* if MSB in 0..127, then value is ticks per quarter note */ /* else */ /* if MSB in 128..255, then value is real time as follows: */ /* negation of MSB (256 - MSB) = frames per second */ /* LSB = ticks per frame */ /* officially, MSB should be -24, -25, -29, or -30 */ TimingCode = InputFile.ReadUInt16BigEndian(); if ((((TimingCode >> 8) & 0xff) >= 0) && (((TimingCode >> 8) & 0xff) <= 127)) { RawMIDIScoreSetMeteredTime(Score, TimingCode); } else { RawMIDIScoreSetRealTime(Score, 256 - ((TimingCode >> 8) & 0xff), TimingCode & 0xff); } /* parse tracks */ /* loop for all tracks */ while (TrackCount > 0) { RawMIDITrackRec Track; int TrackChunkLength; int TimeAccumulator; byte RunningStatus = 0; bool RunningStatusValid; /* allocate track */ Track = NewRawMIDITrack(); RawMIDIScoreAppendTrack(Score, Track); /* get chunk type -- skip unknown chunk types */ do { /* get header string */ CharBuff = InputFile.ReadFixedStringASCII(4); /* 4-byte big endian header length (minus 8 for header & this) */ TrackChunkLength = InputFile.ReadInt32BigEndian(); /* skip unknown chunks */ if (!String.Equals(CharBuff, "MTrk")) { while (TrackChunkLength > 0) { InputFile.ReadByte(); TrackChunkLength -= 1; } } } while (!String.Equals(CharBuff, "MTrk")); /* parse track contents */ /* while there is stuff in the track to read */ TimeAccumulator = 0; RunningStatusValid = false; while (TrackChunkLength > 0) { int DeltaTime; byte UnsignedChar; RawMIDIEventRec Event; /* parse the delta time */ DeltaTime = 0; do { UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; DeltaTime = (DeltaTime << 7) | (UnsignedChar & 0x7f); } while ((UnsignedChar & 0x80) != 0); /* find out what the absolute time is */ TimeAccumulator += DeltaTime; /* get the next byte */ UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; /* is this byte a status byte? (yes if high bit set) */ if (0 != (0x80 & UnsignedChar)) { /* it is a status byte */ RunningStatusValid = true; RunningStatus = UnsignedChar; /* read another byte since we consumed this one */ UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; } else { if (!RunningStatusValid) { /* first byte must be a status byte */ return(MIDIParseErrorType.eMIDIParseBadFormat); } } /* allocate event */ Event = NewRawMIDIEvent(TimeAccumulator, RunningStatus); RawMIDITrackAppendEvent(Track, Event); /* handle event */ switch ((RunningStatus >> 4) & 0x0f) { default: /* unknown status */ return(MIDIParseErrorType.eMIDIParseBadFormat); /* 1000nnnn: note off event for channel nnnn followed by 2 data bytes */ /* 1001nnnn: note on event for channel nnnn followed by 2 data bytes */ /* 1010nnnn: polyphonic key pressure/after touch followed by */ /* 2 data bytes */ /* 1011nnnn: control change followed by 2 data bytes */ /* 1110nnnn: pitch bend change followed by 2 data bytes */ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0e: RawMIDIEventAppendByte(Event, UnsignedChar); UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; RawMIDIEventAppendByte(Event, UnsignedChar); break; /* 1100nnnn: program change followed by 1 data byte */ /* 1101nnnn: channel pressure/after touch followed by 1 data byte */ case 0x0c: case 0x0d: RawMIDIEventAppendByte(Event, UnsignedChar); break; /* assorted things */ case 0x0f: switch (RunningStatus & 0x0f) { default: return(MIDIParseErrorType.eMIDIParseBadFormat); /* 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: { int SysExLength; /* cancel running status */ RunningStatusValid = false; /* read SYSEX length */ SysExLength = UnsignedChar & 0x7f; if (0 != (UnsignedChar & 0x80)) { do { UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; SysExLength = (SysExLength << 7) | (UnsignedChar & 0x7f); } while ((UnsignedChar & 0x80) != 0); } /* read in bytes for SYSEX message */ while (SysExLength > 0) { UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; RawMIDIEventAppendByte(Event, UnsignedChar); SysExLength -= 1; } } break; /* system common messages */ /* 11110010: song position pointer, followed by 2 data bytes */ /* 11110011: song select, followed by 2 data bytes */ case 0x02: case 0x03: RawMIDIEventAppendByte(Event, UnsignedChar); UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; RawMIDIEventAppendByte(Event, UnsignedChar); break; /* 11110110: tune request, no data */ case 0x06: break; /* 11111111: meta event. followed by type byte (0..127) and */ /* then variable length specifier, then data bytes. */ case 0x0f: { int MetaLength; /* cancel running status */ RunningStatusValid = false; /* put meta event type */ RawMIDIEventAppendByte(Event, UnsignedChar); SetRawMIDIEventTypeByte(Event, UnsignedChar); /* get length of meta event */ MetaLength = 0; do { UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; MetaLength = (MetaLength << 7) | (UnsignedChar & 0x7f); } while ((UnsignedChar & 0x80) != 0); /* read all meta event bytes in */ while (MetaLength > 0) { UnsignedChar = InputFile.ReadByte(); TrackChunkLength -= 1; RawMIDIEventAppendByte(Event, UnsignedChar); MetaLength -= 1; } } break; } break; } } /* end of track parsing */ /* decrement count */ TrackCount -= 1; } /* done */ return(MIDIParseErrorType.eMIDIParseNoError); }
/* get an indexed track out of the score */ public static RawMIDITrackRec GetRawMIDIScoreIndexedTrack( RawMIDIScoreRec RawScore, int Index) { return(RawScore.TrackList[Index]); }
/* get the number of tracks in the score object */ public static int GetRawMIDIScoreNumTracks(RawMIDIScoreRec RawScore) { return(RawScore.TrackList.Count); }
/* append a new track to the score object */ public static void RawMIDIScoreAppendTrack( RawMIDIScoreRec RawScore, RawMIDITrackRec Track) { RawScore.TrackList.Add(Track); }
/* get the MIDI file type */ public static MIDIFileFormatType RawMIDIScoreGetFormatType(RawMIDIScoreRec RawScore) { return(RawScore.FileFormat); }
/* get MIDI ticks per frame */ public static int RawMIDIScoreGetTicksPerFrame(RawMIDIScoreRec RawScore) { Debug.Assert(RawScore.TimingMode == MIDITimingType.eMIDIRealTime); return(RawScore.TicksPerFrame); }
/* get MIDI frames per second */ public static int RawMIDIScoreGetFramesPerSecond(RawMIDIScoreRec RawScore) { Debug.Assert(RawScore.TimingMode == MIDITimingType.eMIDIRealTime); return(RawScore.FramesPerSecond); }
/* get MIDI parts per quarter note */ public static int RawMIDIScoreGetPartsPerQuarterNote(RawMIDIScoreRec RawScore) { Debug.Assert(RawScore.TimingMode == MIDITimingType.eMIDIMeteredTime); return(RawScore.PartsPerQuarterNote); }
/* get MIDI timing mode */ public static MIDITimingType RawMIDIScoreGetTimingMode(RawMIDIScoreRec RawScore) { return(RawScore.TimingMode); }