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  }