private void Decode(byte[] message) { byte status = message[0]; switch (status & 0b1111_0000) { case Midi.Status.NoteOffBitmask: if (NoteOffMessage.TryDecode(message, out var noteOffMessage)) { NoteOff?.Invoke(this, in noteOffMessage); } break; case Midi.Status.NoteOnBitmask: if (NoteOnMessage.TryDecoce(message, out var noteOnMessage)) { NoteOn?.Invoke(this, in noteOnMessage); } break; case Midi.Status.PolyphonicKeyPressureBitmask: if (PolyphonicKeyPressureMessage.TryDecode(message, out var polyphonicKeyPressureMessage)) { PolyphonicKeyPressure?.Invoke(this, in polyphonicKeyPressureMessage); } break; case Midi.Status.ControlChangeBitmask: if (ControlChangeMessage.TryDecode(message, out var controlChangeMessage)) { _nrpnInterpreters[(int)controlChangeMessage.Channel].HandleControlChangeMessage(in controlChangeMessage); } break; case Midi.Status.ProgramChangeBitmask: if (ProgramChangeMessage.TryDecode(message, out var programChangeMessage)) { ProgramChange?.Invoke(this, in programChangeMessage); } break; case Midi.Status.ChannelPressureBitmask: if (ChannelPressureMessage.TryDecode(message, out var channelPressureMessage)) { ChannelPressure?.Invoke(this, in channelPressureMessage); } break; case Midi.Status.PitchBendChange: if (PitchBendMessage.TryDecode(message, out var pitchBendMessage)) { PitchBend?.Invoke(this, in pitchBendMessage); } break; default: Log.Error("Unknown message type {Bitmask}", $"{status & 0b1111_0000:X2}"); break; } }
private TrackEvent ReadTrackEvent() { var deltaTime = ReadVariableLengthEncoding(); // "Running Status": The last status byte is implied var statusByte = _lastStatusByte; var firstByte = _source.ReadByte(); // Check for new status byte if ((firstByte & 0x80) == 1) { statusByte = firstByte; firstByte = _source.ReadByte(); } var statusUpper = statusByte >> 4; // Save running status if (statusUpper > 0x7 && statusUpper < 0xF) { _lastStatusByte = statusByte; } // Parse the event var statusLower = (byte)(statusByte & 0xF); MidiEvent ev = null; switch (statusUpper) { case 0x8: ev = new NoteOff(statusLower, firstByte, _source.ReadByte()); break; case 0x9: ev = new NoteOn(statusLower, firstByte, _source.ReadByte()); break; case 0xA: ev = new PolyphonicKeyPressure(statusLower, firstByte, _source.ReadByte()); break; case 0xB: if (firstByte < 120) { ev = new ControlChange(statusLower, firstByte, _source.ReadByte()); } else { ev = new ChannelMode(statusLower, firstByte, _source.ReadByte()); } break; case 0xC: ev = new ProgramChange(statusLower, firstByte); break; case 0xD: ev = new ChannelPressure(statusLower, firstByte); break; case 0xE: ev = new PitchBend(statusLower, firstByte, _source.ReadByte()); break; case 0xF: switch (statusLower) { case 0x0: case 0x7: ev = new SysEx(statusLower == 0, _source.ReadBytes(ReadVariableLengthEncoding(firstByte))); break; case 0xF: var len = ReadVariableLengthEncoding(); var data = _source.ReadBytes(len); switch (firstByte) { case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: ev = new TextEvent(Encoding.UTF8.GetString(data)); // TODO: Let user choose encoding break; case 0x20: ev = new ChannelPrefix(data[0]); break; case 0x2F: ev = new EndOfTrack(); break; case 0x51: ev = new SetTempo(MidiBytesConverter.ReadBigEndian24Bit(data)); break; case 0x54: ev = new SmpteOffset((byte)((data[0] >> 5) & 0x3), (byte)(data[0] & 0x1F), data[1], data[2], data[3], data[4]); break; case 0x58: ev = new TimeSignature(data[0], data[1], data[2], data[3]); break; case 0x59: ev = new KeySignature((sbyte)data[0], data[1] == 1); break; case 0x7F: ev = new SequencerSpecificMetaEvent(data); break; default: // TODO: Unrecognized metadata kind, non-fatal error break; } break; default: // At this point, if the event was not recognized: FATAL ERROR! throw new NotImplementedException(); } break; default: // At this point, if the event was not recognized: FATAL ERROR! throw new NotImplementedException(); } return(new TrackEvent(deltaTime, ev)); }
private void Decode(byte[] message) { byte status = message[0]; switch (status & 0b1111_0000) { case Midi.Status.NoteOffBitmask: if (NoteOffMessage.TryDecode(message, out var noteOffMessage)) { NoteOff?.Invoke(this, in noteOffMessage); } break; case Midi.Status.NoteOnBitmask: if (NoteOnMessage.TryDecode(message, out var noteOnMessage)) { NoteOn?.Invoke(this, in noteOnMessage); } break; case Midi.Status.PolyphonicKeyPressureBitmask: if (PolyphonicKeyPressureMessage.TryDecode(message, out var polyphonicKeyPressureMessage)) { PolyphonicKeyPressure?.Invoke(this, in polyphonicKeyPressureMessage); } break; case Midi.Status.ControlChangeBitmask: if (ControlChangeMessage.TryDecode(message, out var controlChangeMessage)) { _nrpnInterpreters[(int)controlChangeMessage.Channel].HandleControlChangeMessage(in controlChangeMessage); } break; case Midi.Status.ProgramChangeBitmask: if (ProgramChangeMessage.TryDecode(message, out var programChangeMessage)) { ProgramChange?.Invoke(this, in programChangeMessage); } break; case Midi.Status.ChannelPressureBitmask: if (ChannelPressureMessage.TryDecode(message, out var channelPressureMessage)) { ChannelPressure?.Invoke(this, in channelPressureMessage); } break; case Midi.Status.PitchBendChange: if (PitchBendMessage.TryDecode(message, out var pitchBendMessage)) { PitchBend?.Invoke(this, in pitchBendMessage); } break; case Midi.Status.System: switch (status) { case Midi.Status.SysExStart: if (SysExMessage.TryDecode(message, out var sysExMessage)) { SysEx?.Invoke(this, in sysExMessage); } break; case Midi.Status.MidiTimeCodeQuarterFrame: if (MidiTimeCodeQuarterFrameMessage.TryDecode(message, out var timeCodeQuarterFrameMessage)) { MidiTimeCodeQuarterFrame?.Invoke(this, in timeCodeQuarterFrameMessage); } break; case Midi.Status.SongPositionPointer: if (SongPositionPointerMessage.TryDecode(message, out var songPositionPointerMessage)) { SongPositionPointer?.Invoke(this, in songPositionPointerMessage); } break; case Midi.Status.SongSelect: if (SongSelectMessage.TryDecode(message, out var songSelectMessage)) { SongSelect?.Invoke(this, in songSelectMessage); } break; case Midi.Status.TuneRequest: if (TuneRequestMessage.TryDecode(message, out var tuneRequestMessage)) { TuneRequest?.Invoke(this, in tuneRequestMessage); } break; default: Log.Error("Unknown system message type {Status}", $"{status:X2}"); break; } break; default: Log.Error("Unknown message type {Bitmask}", $"{status & 0b1111_0000:X2}"); break; } }