github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/usb/adc/midi/messages.go (about) 1 package midi 2 3 import ( 4 "errors" 5 ) 6 7 // From USB-MIDI section 4.1 "Code Index Number (CIN) Classifications" 8 const ( 9 CINSystemCommon2 = 0x2 10 CINSystemCommon3 = 0x3 11 CINSysExStart = 0x4 12 CINSysExEnd1 = 0x5 13 CINSysExEnd2 = 0x6 14 CINSysExEnd3 = 0x7 15 CINNoteOff = 0x8 16 CINNoteOn = 0x9 17 CINPoly = 0xA 18 CINControlChange = 0xB 19 CINProgramChange = 0xC 20 CINChannelPressure = 0xD 21 CINPitchBendChange = 0xE 22 CINSingleByte = 0xF 23 ) 24 25 // Standard MIDI channel messages 26 const ( 27 MsgNoteOff = 0x80 28 MsgNoteOn = 0x90 29 MsgPolyAftertouch = 0xA0 30 MsgControlChange = 0xB0 31 MsgProgramChange = 0xC0 32 MsgChannelAftertouch = 0xD0 33 MsgPitchBend = 0xE0 34 MsgSysExStart = 0xF0 35 MsgSysExEnd = 0xF7 36 ) 37 38 // Standard MIDI control change messages 39 const ( 40 CCModulationWheel = 0x01 41 CCBreathController = 0x02 42 CCFootPedal = 0x04 43 CCPortamentoTime = 0x05 44 CCDataEntry = 0x06 45 CCVolume = 0x07 46 CCBalance = 0x08 47 CCPan = 0x0A 48 CCExpression = 0x0B 49 CCEffectControl1 = 0x0C 50 CCEffectControl2 = 0x0D 51 CCGeneralPurpose1 = 0x10 52 CCGeneralPurpose2 = 0x11 53 CCGeneralPurpose3 = 0x12 54 CCGeneralPurpose4 = 0x13 55 CCBankSelect = 0x20 56 CCModulationDepthRange = 0x21 57 CCBreathControllerDepth = 0x22 58 CCFootPedalDepth = 0x24 59 CCEffectsLevel = 0x5B 60 CCTremeloLevel = 0x5C 61 CCChorusLevel = 0x5D 62 CCCelesteLevel = 0x5E 63 CCPhaserLevel = 0x5F 64 CCDataIncrement = 0x60 65 CCDataDecrement = 0x61 66 CCNRPNLSB = 0x62 67 CCNRPNMSB = 0x63 68 CCRPNLSB = 0x64 69 CCRPNMSB = 0x65 70 CCAllSoundOff = 0x78 71 CCResetAllControllers = 0x79 72 CCAllNotesOff = 0x7B 73 CCChannelVolume = 0x7F 74 ) 75 76 var ( 77 errInvalidMIDICable = errors.New("invalid MIDI cable") 78 errInvalidMIDIChannel = errors.New("invalid MIDI channel") 79 errInvalidMIDIVelocity = errors.New("invalid MIDI velocity") 80 errInvalidMIDIControl = errors.New("invalid MIDI control number") 81 errInvalidMIDIControlValue = errors.New("invalid MIDI control value") 82 errInvalidMIDIPatch = errors.New("invalid MIDI patch number") 83 errInvalidMIDIPitchBend = errors.New("invalid MIDI pitch bend value") 84 errInvalidMIDISysExData = errors.New("invalid MIDI SysEx data") 85 ) 86 87 // NoteOn sends a channel note on message. 88 // The cable parameter is the cable number 0-15. 89 // The channel parameter is the MIDI channel number 1-16. 90 func (m *midi) NoteOn(cable, channel uint8, note Note, velocity uint8) error { 91 switch { 92 case cable > 15: 93 return errInvalidMIDICable 94 case channel == 0 || channel > 16: 95 return errInvalidMIDIChannel 96 case velocity > 127: 97 return errInvalidMIDIVelocity 98 } 99 100 m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINNoteOn, MsgNoteOn|(channel-1&0xf), byte(note)&0x7f, velocity&0x7f 101 _, err := m.Write(m.msg[:]) 102 return err 103 } 104 105 // NoteOff sends a channel note off message. 106 // The cable parameter is the cable number 0-15. 107 // The channel parameter is the MIDI channel number 1-16. 108 func (m *midi) NoteOff(cable, channel uint8, note Note, velocity uint8) error { 109 switch { 110 case cable > 15: 111 return errInvalidMIDICable 112 case channel == 0 || channel > 16: 113 return errInvalidMIDIChannel 114 case velocity > 127: 115 return errInvalidMIDIVelocity 116 } 117 118 m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINNoteOff, MsgNoteOff|(channel-1&0xf), byte(note)&0x7f, velocity&0x7f 119 _, err := m.Write(m.msg[:]) 120 return err 121 } 122 123 // ControlChange sends a channel continuous controller message. 124 // The cable parameter is the cable number 0-15. 125 // The channel parameter is the MIDI channel number 1-16. 126 // The control parameter is the controller number 0-127. 127 // The value parameter is the controller value 0-127. 128 func (m *midi) ControlChange(cable, channel, control, value uint8) error { 129 switch { 130 case cable > 15: 131 return errInvalidMIDICable 132 case channel == 0 || channel > 16: 133 return errInvalidMIDIChannel 134 case control > 127: 135 return errInvalidMIDIControl 136 case value > 127: 137 return errInvalidMIDIControlValue 138 } 139 140 m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINControlChange, MsgControlChange|(channel-1&0xf), control&0x7f, value&0x7f 141 _, err := m.Write(m.msg[:]) 142 return err 143 } 144 145 // ProgramChange sends a channel program change message. 146 // The cable parameter is the cable number 0-15. 147 // The channel parameter is the MIDI channel number 1-16. 148 // The patch parameter is the program number 0-127. 149 func (m *midi) ProgramChange(cable, channel uint8, patch uint8) error { 150 switch { 151 case cable > 15: 152 return errInvalidMIDICable 153 case channel == 0 || channel > 16: 154 return errInvalidMIDIChannel 155 case patch > 127: 156 return errInvalidMIDIPatch 157 } 158 159 m.msg[0], m.msg[1], m.msg[2] = (cable&0xf<<4)|CINProgramChange, MsgProgramChange|(channel-1&0xf), patch&0x7f 160 _, err := m.Write(m.msg[:3]) 161 return err 162 } 163 164 // PitchBend sends a channel pitch bend message. 165 // The cable parameter is the cable number 0-15. 166 // The channel parameter is the MIDI channel number 1-16. 167 // The bend parameter is the 14-bit pitch bend value (maximum 0x3FFF). 168 // Setting bend above 0x2000 (up to 0x3FFF) will increase the pitch. 169 // Setting bend below 0x2000 (down to 0x0000) will decrease the pitch. 170 func (m *midi) PitchBend(cable, channel uint8, bend uint16) error { 171 switch { 172 case cable > 15: 173 return errInvalidMIDICable 174 case channel == 0 || channel > 16: 175 return errInvalidMIDIChannel 176 case bend > 0x3FFF: 177 return errInvalidMIDIPitchBend 178 } 179 180 m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINPitchBendChange, MsgPitchBend|(channel-1&0xf), byte(bend&0x7f), byte(bend>>8)&0x7f 181 _, err := m.Write(m.msg[:]) 182 return err 183 } 184 185 // SysEx sends a System Exclusive message. 186 // The cable parameter is the cable number 0-15. 187 // The data parameter is a slice with the data to send. 188 // It needs to start with the manufacturer ID, which is either 189 // 1 or 3 bytes in length. 190 // The data slice should not include the SysEx start (0xF0) or 191 // end (0xF7) bytes, only the data in between. 192 func (m *midi) SysEx(cable uint8, data []byte) error { 193 switch { 194 case cable > 15: 195 return errInvalidMIDICable 196 case len(data) < 3: 197 return errInvalidMIDISysExData 198 } 199 200 // write start 201 m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExStart, MsgSysExStart 202 m.msg[2], m.msg[3] = data[0], data[1] 203 if _, err := m.Write(m.msg[:]); err != nil { 204 return err 205 } 206 207 // write middle 208 i := 2 209 for ; i < len(data)-2; i += 3 { 210 m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExStart, data[i] 211 m.msg[2], m.msg[3] = data[i+1], data[i+2] 212 if _, err := m.Write(m.msg[:]); err != nil { 213 return err 214 } 215 } 216 // write end 217 switch len(data) - i { 218 case 2: 219 m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd3, data[i] 220 m.msg[2], m.msg[3] = data[i+1], MsgSysExEnd 221 case 1: 222 m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd2, data[i] 223 m.msg[2], m.msg[3] = MsgSysExEnd, 0 224 case 0: 225 m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd1, MsgSysExEnd 226 m.msg[2], m.msg[3] = 0, 0 227 } 228 if _, err := m.Write(m.msg[:]); err != nil { 229 return err 230 } 231 232 return nil 233 }