/// <summary>Creates an object creation expression for an event.</summary> /// <param name="ev">The event to create.</param> /// <returns>The object creation expression for the event.</returns> private static CodeObjectCreateExpression CreateSystemEvent(MidiEvent ev) { CodeObjectCreateExpression newEvent = null; CodeExpression delta = new CodePrimitiveExpression(ev.DeltaTime); // SYSTEM EXCLUSIVE if (ev is SystemExclusiveMidiEvent) { SystemExclusiveMidiEvent midiEvent = (SystemExclusiveMidiEvent)ev; newEvent = new CodeObjectCreateExpression( typeof(SystemExclusiveMidiEvent), new CodeExpression[] { delta, CreateDataArray(midiEvent.Data) }); } // Return the event return(newEvent); }
/// <summary>Parses a byte array into a track's worth of events.</summary> /// <param name="data">The data to be parsed.</param> /// <returns>The track containing the parsed events.</returns> public static MidiTrack ParseToTrack(byte [] data) { long pos = 0; // current position in data bool running = false; // whether we're in running status int status = 0; // the current status byte bool sysExContinue = false; // whether we're in a multi-segment system exclusive message byte [] sysExData = null; // system exclusive data up to this point from a multi-segment message try { // Create the new track MidiTrack track = new MidiTrack(); // Process all bytes, turning them into events while (pos < data.Length) { // Read in the delta time long deltaTime = ReadVariableLength(data, ref pos); // Get the next character byte nextValue = data[pos]; // Are we continuing a sys ex? If so, the next value better be 0x7F if (sysExContinue && (nextValue != 0x7f)) { throw new MidiParserException("Expected to find a system exclusive continue byte.", pos); } // Are we in running status? Determine whether we're running and // what the current status byte is. if ((nextValue & 0x80) == 0) { // We're now in running status... if the last status was 0, uh oh! if (status == 0) { throw new MidiParserException("Status byte required for running status.", pos); } // Keep the last iteration's status byte, and now we're in running mode running = true; } else { // Not running, so store the current status byte and mark running as false status = nextValue; running = false; } // Grab the 4-bit identifier byte messageType = (byte)((status >> 4) & 0xF); MidiEvent tempEvent = null; // Handle voice events if (messageType >= 0x8 && messageType <= 0xE) { if (!running) { pos++; // if we're running, we don't advance; if we're not running, we do } byte channel = (byte)(status & 0xF); // grab the channel from the status byte tempEvent = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos); } // Handle meta events else if (status == 0xFF) { pos++; byte eventType = data[pos]; pos++; tempEvent = ParseMetaEvent(deltaTime, eventType, data, ref pos); } // Handle system exclusive events else if (status == 0xF0) { pos++; long length = ReadVariableLength(data, ref pos); // figure out how much data to read // If this is single-segment message, process the whole thing if (data[pos + length - 1] == 0xF7) { sysExData = new byte[length - 1]; Array.Copy(data, (int)pos, sysExData, 0, (int)length - 1); tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); } // It's multi-segment, so add the new data to the previously aquired data else { // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) { sysExData.CopyTo(newSysExData, 0); } Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; sysExContinue = true; } pos += length; } // Handle system exclusive continuations else if (status == 0xF7) { if (!sysExContinue) { sysExData = null; } // Figure out how much data there is pos++; long length = ReadVariableLength(data, ref pos); // Add to previously aquired sys ex data int oldLength = (sysExData == null ? 0 : sysExData.Length); byte [] newSysExData = new byte[oldLength + length]; if (sysExData != null) { sysExData.CopyTo(newSysExData, 0); } Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length); sysExData = newSysExData; // Make it a system message if necessary (i.e. if we find an end marker) if (data[pos + length - 1] == 0xF7) { tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData); sysExData = null; sysExContinue = false; } } // Nothing we know about else { throw new MidiParserException("Invalid status byte found.", pos); } // Add the newly parsed event if we got one if (tempEvent != null) { track.Events.Add(tempEvent); } } // Return the newly populated track return(track); } // Let MidiParserExceptions through catch (MidiParserException) { throw; } // Wrap all other exceptions in MidiParserExceptions catch (Exception exc) { throw new MidiParserException("Failed to parse MIDI file.", exc, pos); } }