private void AddMIDICommand(MIDICommandType type, int channel, int note, int velocity) { // Each of the three MIDI commands supported by VRChat is composed of three bytes. // The first four bits of the first byte determine the command type (NoteOn/NoteOff/ControlChange). // The last four bits of the first byte determine the channel (instrument) the command is targeting. // The second byte's 7 primary bits determine the note to be played. // The last byte's 7 primary bits detemrine the velocity of the command. if (currentOffset > buffer.Length - MIDI_BYTES_PER_COMMAND) { throw new InvalidOperationException("MIDICommandChunk is full"); } switch (type) { case MIDICommandType.NoteOff: buffer[currentOffset] = 0x80; break; case MIDICommandType.NoteOn: buffer[currentOffset] = 0x90; break; case MIDICommandType.ControlChange: buffer[currentOffset] = 0xB0; break; } buffer[currentOffset++] |= (byte)channel; buffer[currentOffset++] = (byte)note; buffer[currentOffset++] = (byte)velocity; }
private void Add(byte a, byte b, int extraBits = 0, MIDICommandType type = MIDICommandType.NoteOn) { // Since MIDI commands' note and velocity bytes only have 7 usable bits, // the highest bits of each two bytes are moved into the lowest two bits // of the MIDI channel. int channelA = (a & 0x80) >> 7; int channelB = (b & 0x80) >> 6; int channelHigh = (extraBits & 0x3) << 2; int channel = channelHigh | channelA | channelB; AddMIDICommand(type, channel, a & 0x7F, b & 0x7F); }