~dhruvin/go-midi

6be5ee3ac61f27968204fa6981547cd900ad2d3e — Dhruvin Gandhi 4 months ago a570419 main
perf(midi): simplify conditions

- merge field requirements into message requirements
- remove negation from condition check
- simplify causes using invalidStatusByte and invalidDataByte
M midi/gen_channel_mode.go => midi/gen_channel_mode.go +16 -19
@@ 79,10 79,10 @@ func isValidAllSoundOff(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteAllSoundOff) {
	if firstFourBits&data[0] != statusByteAllSoundOff {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerAllSoundOff && data[2] == 0) {
	if data[1] != controllerAllSoundOff || data[2] != 0 {
		return ErrInvalidData
	}
	return nil


@@ 138,10 138,10 @@ func isValidResetAllControllers(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteResetAllControllers) {
	if firstFourBits&data[0] != statusByteResetAllControllers {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerResetAllControllers && data[2] == 0) {
	if data[1] != controllerResetAllControllers || data[2] != 0 {
		return ErrInvalidData
	}
	return nil


@@ 206,10 206,10 @@ func isValidLocalControl(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteLocalControl) {
	if firstFourBits&data[0] != statusByteLocalControl {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerLocalControl && firstBit&data[2] == dataByte) {
	if data[1] != controllerLocalControl || (data[2] != 0 && data[2] != 127) {
		return ErrInvalidData
	}
	return nil


@@ 266,10 266,10 @@ func isValidAllNotesOff(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteAllNotesOff) {
	if firstFourBits&data[0] != statusByteAllNotesOff {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerAllNotesOff && data[2] == 0) {
	if data[1] != controllerAllNotesOff || data[2] != 0 {
		return ErrInvalidData
	}
	return nil


@@ 325,10 325,10 @@ func isValidOmniModeOff(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteOmniModeOff) {
	if firstFourBits&data[0] != statusByteOmniModeOff {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerOmniModeOff && data[2] == 0) {
	if data[1] != controllerOmniModeOff || data[2] != 0 {
		return ErrInvalidData
	}
	return nil


@@ 384,10 384,10 @@ func isValidOmniModeOn(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteOmniModeOn) {
	if firstFourBits&data[0] != statusByteOmniModeOn {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerOmniModeOn && data[2] == 0) {
	if data[1] != controllerOmniModeOn || data[2] != 0 {
		return ErrInvalidData
	}
	return nil


@@ 455,15 455,12 @@ func isValidMonoModeOn(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteMonoModeOn) {
	if firstFourBits&data[0] != statusByteMonoModeOn {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerMonoModeOn && firstBit&data[2] == dataByte) {
	if data[1] != controllerMonoModeOn || data[2] > byte(MaxChannelCount) {
		return ErrInvalidData
	}
	if !(secondAndThirdBits&data[2] == 0) {
		return ErrInvalidChannelCount
	}
	return nil
}



@@ 521,10 518,10 @@ func isValidPolyModeOn(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusBytePolyModeOn) {
	if firstFourBits&data[0] != statusBytePolyModeOn {
		return ErrInvalidStatus
	}
	if !(data[1] == controllerPolyModeOn && data[2] == 0) {
	if data[1] != controllerPolyModeOn || data[2] != 0 {
		return ErrInvalidData
	}
	return nil

M midi/gen_channel_mode_test.go => midi/gen_channel_mode_test.go +24 -33
@@ 201,7 201,7 @@ func TestAllSoundOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 210,11 210,11 @@ func TestAllSoundOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 415,7 415,7 @@ func TestResetAllControllers(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 424,11 424,11 @@ func TestResetAllControllers(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 668,7 668,7 @@ func TestLocalControl(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 677,11 677,11 @@ func TestLocalControl(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 886,7 886,7 @@ func TestAllNotesOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 895,11 895,11 @@ func TestAllNotesOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 1100,7 1100,7 @@ func TestOmniModeOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1109,11 1109,11 @@ func TestOmniModeOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 1314,7 1314,7 @@ func TestOmniModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1323,11 1323,11 @@ func TestOmniModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 1577,7 1577,7 @@ func TestMonoModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1586,24 1586,15 @@ func TestMonoModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,
			},
			"ErrInvalidChannelCount": {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[2] = invalidChannelCountByte | data[2]
					},
				},
				midi.ErrInvalidChannelCount,
			},
		}
		for name, tc := range tests {
			t.Run(name, func(t *testing.T) {


@@ 1804,7 1795,7 @@ func TestPolyModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1813,11 1804,11 @@ func TestPolyModeOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = 0
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = 1
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,

M midi/gen_channel_voice.go => midi/gen_channel_voice.go +16 -25
@@ 254,10 254,10 @@ func isValidNoteOff(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteNoteOff) {
	if firstFourBits&data[0] != statusByteNoteOff {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte) {
	if data[1] >= invalidDataByte || data[2] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil


@@ 344,10 344,10 @@ func isValidNoteOn(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteNoteOn) {
	if firstFourBits&data[0] != statusByteNoteOn {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte) {
	if data[1] >= invalidDataByte || data[2] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil


@@ 434,10 434,10 @@ func isValidPolyphonicKeyPressure(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusBytePolyphonicKeyPressure) {
	if firstFourBits&data[0] != statusBytePolyphonicKeyPressure {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte) {
	if data[1] >= invalidDataByte || data[2] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil


@@ 524,15 524,12 @@ func isValidSingleByteControlChange(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteSingleByteControlChange) {
	if firstFourBits&data[0] != statusByteSingleByteControlChange {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte) {
	if data[1] < byte(MinSingleByteController) || data[1] > byte(MaxSingleByteController) || data[2] >= invalidDataByte {
		return ErrInvalidData
	}
	if !(data[1] >= byte(MinSingleByteController) && data[1] <= byte(MaxSingleByteController)) {
		return ErrInvalidController
	}
	return nil
}



@@ 620,18 617,12 @@ func isValidTwoByteControlChange(data []byte) error {
	if len(data) != 6 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteTwoByteControlChange && firstFourBits&data[3] == statusByteTwoByteControlChange) {
	if firstFourBits&data[0] != statusByteTwoByteControlChange || data[3] != data[0] {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte && firstBit&data[4] == dataByte && firstBit&data[5] == dataByte) {
	if data[1] > byte(MaxTwoByteController) || data[2] >= invalidDataByte || data[4] != data[1]+32 || data[5] >= invalidDataByte {
		return ErrInvalidData
	}
	if !(lastFourBits&data[0] == lastFourBits&data[3]) {
		return ErrInvalidChannel
	}
	if !(data[1] <= byte(MaxTwoByteController) && data[4] == data[1]+32) {
		return ErrInvalidController
	}
	return nil
}



@@ 707,10 698,10 @@ func isValidProgramChange(data []byte) error {
	if len(data) != 2 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteProgramChange) {
	if firstFourBits&data[0] != statusByteProgramChange {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte) {
	if data[1] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil


@@ 780,10 771,10 @@ func isValidChannelPressure(data []byte) error {
	if len(data) != 2 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusByteChannelPressure) {
	if firstFourBits&data[0] != statusByteChannelPressure {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte) {
	if data[1] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil


@@ 854,10 845,10 @@ func isValidPitchBendChange(data []byte) error {
	if len(data) != 3 {
		return ErrInvalidLength
	}
	if !(firstFourBits&data[0] == statusBytePitchBendChange) {
	if firstFourBits&data[0] != statusBytePitchBendChange {
		return ErrInvalidStatus
	}
	if !(firstBit&data[1] == dataByte && firstBit&data[2] == dataByte) {
	if data[1] >= invalidDataByte || data[2] >= invalidDataByte {
		return ErrInvalidData
	}
	return nil

M midi/gen_channel_voice_test.go => midi/gen_channel_voice_test.go +25 -60
@@ 673,7 673,7 @@ func TestNoteOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 682,11 682,11 @@ func TestNoteOff(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 995,7 995,7 @@ func TestNoteOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1004,11 1004,11 @@ func TestNoteOn(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 1317,7 1317,7 @@ func TestPolyphonicKeyPressure(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1326,11 1326,11 @@ func TestPolyphonicKeyPressure(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 1639,7 1639,7 @@ func TestSingleByteControlChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1648,28 1648,15 @@ func TestSingleByteControlChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,
			},
			"ErrInvalidController": {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = byte(midi.MinSingleByteController) - 1
					},
					func(in *[]byte) {
						data := *in
						data[1] = byte(midi.MaxSingleByteController) + 1
					},
				},
				midi.ErrInvalidController,
			},
		}
		for name, tc := range tests {
			t.Run(name, func(t *testing.T) {


@@ 1974,11 1961,11 @@ func TestTwoByteControlChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
					func(in *[]byte) {
						data := *in
						data[3] = invalidStatusByte & data[3]
						data[3] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 1987,45 1974,23 @@ func TestTwoByteControlChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[4] = invalidDataByte | data[4]
						data[4] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[5] = invalidDataByte | data[5]
						data[5] = invalidDataByte
					},
				},
				midi.ErrInvalidData,
			},
			"ErrInvalidChannel": {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[3] = (firstFourBits & data[3]) | byte(midi.MaxChannel)
					},
				},
				midi.ErrInvalidChannel,
			},
			"ErrInvalidController": {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = byte(midi.MaxTwoByteController) + 1
					},
					func(in *[]byte) {
						data := *in
						data[4] = data[1]
					},
				},
				midi.ErrInvalidController,
			},
		}
		for name, tc := range tests {
			t.Run(name, func(t *testing.T) {


@@ 2279,7 2244,7 @@ func TestProgramChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 2288,7 2253,7 @@ func TestProgramChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 2542,7 2507,7 @@ func TestChannelPressure(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 2551,7 2516,7 @@ func TestChannelPressure(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
				},
				midi.ErrInvalidData,


@@ 2805,7 2770,7 @@ func TestPitchBendChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[0] = invalidStatusByte & data[0]
						data[0] = invalidStatusByte
					},
				},
				midi.ErrInvalidStatus,


@@ 2814,11 2779,11 @@ func TestPitchBendChange(t *testing.T) {
				[]func(*[]byte){
					func(in *[]byte) {
						data := *in
						data[1] = invalidDataByte | data[1]
						data[1] = invalidDataByte
					},
					func(in *[]byte) {
						data := *in
						data[2] = invalidDataByte | data[2]
						data[2] = invalidDataByte
					},
				},
				midi.ErrInvalidData,

M midi/generate.yaml => midi/generate.yaml +81 -105
@@ 97,15 97,15 @@ files:
      - byte: 2
        value: byte(velocity)
    requirements:
    - condition: firstFourBits & data[0] == statusByteNoteOff
    - condition: firstFourBits & data[0] != statusByteNoteOff
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte || data[2] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: NoteOn
    constants:


@@ 131,15 131,15 @@ files:
      - byte: 2
        value: byte(velocity)
    requirements:
    - condition: firstFourBits & data[0] == statusByteNoteOn
    - condition: firstFourBits & data[0] != statusByteNoteOn
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte || data[2] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: PolyphonicKeyPressure
    constants:


@@ 165,15 165,15 @@ files:
      - byte: 2
        value: byte(pressure)
    requirements:
    - condition: firstFourBits & data[0] == statusBytePolyphonicKeyPressure
    - condition: firstFourBits & data[0] != statusBytePolyphonicKeyPressure
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte || data[2] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: SingleByteControlChange
    constants:


@@ 192,12 192,6 @@ files:
      setters:
      - byte: 1
        value: byte(controller)
      requirements:
      - condition: data[1] >= byte(MinSingleByteController) && data[1] <= byte(MaxSingleByteController)
        error: ErrInvalidController
        causes:
        - data[1] = byte(midi.MinSingleByteController) - 1
        - data[1] = byte(midi.MaxSingleByteController) + 1
    - name: value
      type: SingleByteControllerValue
      getter: self[2]


@@ 205,15 199,15 @@ files:
      - byte: 2
        value: byte(value)
    requirements:
    - condition: firstFourBits & data[0] == statusByteSingleByteControlChange
    - condition: firstFourBits & data[0] != statusByteSingleByteControlChange
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] < byte(MinSingleByteController) || data[1] > byte(MaxSingleByteController) || data[2] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: TwoByteControlChange
    constants:


@@ 228,11 222,6 @@ files:
        value: statusByteTwoByteControlChange | byte(channel)
      - byte: 3
        value: statusByteTwoByteControlChange | byte(channel)
      requirements:
      - condition: lastFourBits & data[0] == lastFourBits & data[3]
        error: ErrInvalidChannel
        causes:
        - data[3] = (firstFourBits & data[3]) | byte(midi.MaxChannel)
    - name: controller
      type: TwoByteController
      getter: self[1]


@@ 241,12 230,6 @@ files:
        value: byte(controller)
      - byte: 4
        value: byte(controller + 32)
      requirements:
      - condition: data[1] <= byte(MaxTwoByteController) && data[4] == data[1] + 32
        error: ErrInvalidController
        causes:
        - data[1] = byte(midi.MaxTwoByteController) + 1
        - data[4] = data[1]
    - name: value
      type: TwoByteControllerValue
      getter: uint16(self[5]) << 7 | uint16(self[2])


@@ 256,18 239,18 @@ files:
      - byte: 5
        value: byte(value >> 7)
    requirements:
    - condition: firstFourBits & data[0] == statusByteTwoByteControlChange && firstFourBits & data[3] == statusByteTwoByteControlChange
    - condition: firstFourBits & data[0] != statusByteTwoByteControlChange || data[3] != data[0]
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
      - data[3] = invalidStatusByte & data[3]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte && firstBit & data[4] == dataByte && firstBit & data[5] == dataByte
      - data[0] = invalidStatusByte
      - data[3] = invalidStatusByte
    - condition: data[1] > byte(MaxTwoByteController) || data[2] >= invalidDataByte || data[4] != data[1] + 32 || data[5] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]
      - data[4] = invalidDataByte | data[4]
      - data[5] = invalidDataByte | data[5]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte
      - data[4] = invalidDataByte
      - data[5] = invalidDataByte

  - name: ProgramChange
    constants:


@@ 287,14 270,14 @@ files:
      - byte: 1
        value: byte(program)
    requirements:
    - condition: firstFourBits & data[0] == statusByteProgramChange
    - condition: firstFourBits & data[0] != statusByteProgramChange
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[1] = invalidDataByte

  - name: ChannelPressure
    constants:


@@ 314,14 297,14 @@ files:
      - byte: 1
        value: byte(pressure)
    requirements:
    - condition: firstFourBits & data[0] == statusByteChannelPressure
    - condition: firstFourBits & data[0] != statusByteChannelPressure
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[1] = invalidDataByte

  - name: PitchBendChange
    constants:


@@ 343,17 326,15 @@ files:
      - byte: 2
        value: byte((pitchBend + 8192) >> 7)
    requirements:
    - condition: firstFourBits & data[0] == statusBytePitchBendChange
    - condition: firstFourBits & data[0] != statusBytePitchBendChange
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: firstBit & data[1] == dataByte && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] >= invalidDataByte || data[2] >= invalidDataByte
      error: ErrInvalidData
      causes:
      - data[1] = invalidDataByte | data[1]
      - data[2] = invalidDataByte | data[2]


      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

- name: channel_mode
  types:


@@ 388,15 369,15 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusByteAllSoundOff
    - condition: firstFourBits & data[0] != statusByteAllSoundOff
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerAllSoundOff && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerAllSoundOff || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: ResetAllControllers
    constants:


@@ 417,15 398,15 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusByteResetAllControllers
    - condition: firstFourBits & data[0] != statusByteResetAllControllers
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerResetAllControllers && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerResetAllControllers || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: LocalControl
    constants:


@@ 450,15 431,15 @@ files:
    - byte: 1
      value: controllerLocalControl
    requirements:
    - condition: firstFourBits & data[0] == statusByteLocalControl
    - condition: firstFourBits & data[0] != statusByteLocalControl
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerLocalControl && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerLocalControl || (data[2] != 0 && data[2] != 127)
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: AllNotesOff
    constants:


@@ 479,15 460,15 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusByteAllNotesOff
    - condition: firstFourBits & data[0] != statusByteAllNotesOff
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerAllNotesOff && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerAllNotesOff || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: OmniModeOff
    constants:


@@ 508,15 489,15 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusByteOmniModeOff
    - condition: firstFourBits & data[0] != statusByteOmniModeOff
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerOmniModeOff && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerOmniModeOff || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: OmniModeOn
    constants:


@@ 537,15 518,15 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusByteOmniModeOn
    - condition: firstFourBits & data[0] != statusByteOmniModeOn
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerOmniModeOn && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerOmniModeOn || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: MonoModeOn
    constants:


@@ 566,24 547,19 @@ files:
      setters:
      - byte: 2
        value: byte(channelCount)
      requirements:
      - condition: secondAndThirdBits & data[2] == 0
        error: ErrInvalidChannelCount
        causes:
        - data[2] = invalidChannelCountByte | data[2]
    setters:
    - byte: 1
      value: controllerMonoModeOn
    requirements:
    - condition: firstFourBits & data[0] == statusByteMonoModeOn
    - condition: firstFourBits & data[0] != statusByteMonoModeOn
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerMonoModeOn && firstBit & data[2] == dataByte
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerMonoModeOn || data[2] > byte(MaxChannelCount)
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = invalidDataByte | data[2]
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

  - name: PolyModeOn
    constants:


@@ 604,12 580,12 @@ files:
    - byte: 2
      value: 0
    requirements:
    - condition: firstFourBits & data[0] == statusBytePolyModeOn
    - condition: firstFourBits & data[0] != statusBytePolyModeOn
      error: ErrInvalidStatus
      causes:
      - data[0] = invalidStatusByte & data[0]
    - condition: data[1] == controllerPolyModeOn && data[2] == 0
      - data[0] = invalidStatusByte
    - condition: data[1] != controllerPolyModeOn || data[2] != 0
      error: ErrInvalidData
      causes:
      - data[1] = 0
      - data[2] = 1
      - data[1] = invalidDataByte
      - data[2] = invalidDataByte

M midi/midi.go => midi/midi.go +4 -2
@@ 13,8 13,10 @@ const (
)

const (
	statusByte byte = 0x80
	dataByte        = 0x00
	statusByte        byte = 0x80
	dataByte               = 0x00
	invalidStatusByte      = dataByte
	invalidDataByte        = statusByte
)

var (

M tools/generate/internal/config.go => tools/generate/internal/config.go +4 -5
@@ 32,11 32,10 @@ type configSetter struct {
}

type configField struct {
	Name         string
	Type         string
	Getter       string
	Setters      []configSetter
	Requirements []configRequirement
	Name    string
	Type    string
	Getter  string
	Setters []configSetter
}

type configConstant struct {

M tools/generate/internal/gen.go.tmpl => tools/generate/internal/gen.go.tmpl +1 -8
@@ 129,17 129,10 @@ func isValid{{ .Name }}(data []byte) error {
		return ErrInvalidLength
	}
	{{- range .Requirements }}
	if !({{ .Condition }}) {
	if {{ .Condition }} {
		return {{ .Error }}
	}
	{{- end }}
	{{- range .Fields }}
	{{- range .Requirements }}
	if !({{ .Condition }}) {
		return {{ .Error }}
	}
	{{- end }}
	{{- end }}
	return nil
}


M tools/generate/internal/gen_test.go.tmpl => tools/generate/internal/gen_test.go.tmpl +0 -15
@@ 292,21 292,6 @@ func Test{{ .Name }}(t *testing.T) {
				{{ $pkg }}.{{ .Error }},
			},
			{{- end }}
			{{- range .Fields }}
			{{- range .Requirements }}
			"{{ .Error }}": {
				[]func(*[]byte) {
					{{- range .Causes }}
					func(in *[]byte) {
						data := *in
						{{ . }}
					},
					{{- end }}
				},
				{{ $pkg }}.{{ .Error }},
			},
			{{- end }}
			{{- end }}
		}
		for name, tc := range tests {
			t.Run(name, func(t *testing.T) {