Browse Source

Add USB MIDI (work in progress) for Teensy 4.0

main
PaulStoffregen 5 years ago
parent
commit
f44268059b
6 changed files with 967 additions and 65 deletions
  1. +1
    -1
      teensy4/WProgram.h
  2. +4
    -0
      teensy4/usb.c
  3. +5
    -5
      teensy4/usb_desc.c
  4. +59
    -59
      teensy4/usb_desc.h
  5. +509
    -0
      teensy4/usb_midi.c
  6. +389
    -0
      teensy4/usb_midi.h

+ 1
- 1
teensy4/WProgram.h View File

#include "usb_keyboard.h" #include "usb_keyboard.h"
#include "usb_mouse.h" #include "usb_mouse.h"
#include "usb_joystick.h" #include "usb_joystick.h"
//#include "usb_midi.h"
#include "usb_midi.h"
#include "usb_rawhid.h" #include "usb_rawhid.h"
//#include "usb_flightsim.h" //#include "usb_flightsim.h"
//#include "usb_mtp.h" //#include "usb_mtp.h"

+ 4
- 0
teensy4/usb.c View File

#include "usb_mouse.h" #include "usb_mouse.h"
#include "usb_joystick.h" #include "usb_joystick.h"
#include "usb_touch.h" #include "usb_touch.h"
#include "usb_midi.h"
#include "core_pins.h" // for delay() #include "core_pins.h" // for delay()
#include "avr/pgmspace.h" #include "avr/pgmspace.h"
#include <string.h> #include <string.h>
#if defined(MULTITOUCH_INTERFACE) #if defined(MULTITOUCH_INTERFACE)
usb_touchscreen_configure(); usb_touchscreen_configure();
#endif #endif
#if defined(MIDI_INTERFACE)
usb_midi_configure();
#endif
endpoint0_receive(NULL, 0, 0); endpoint0_receive(NULL, 0, 0);
return; return;
case 0x0880: // GET_CONFIGURATION case 0x0880: // GET_CONFIGURATION

+ 5
- 5
teensy4/usb_desc.c View File

5, // bDescriptorType = ENDPOINT 5, // bDescriptorType = ENDPOINT
MIDI_RX_ENDPOINT, // bEndpointAddress MIDI_RX_ENDPOINT, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk) 0x02, // bmAttributes (0x02=bulk)
MIDI_RX_SIZE, 0, // wMaxPacketSize
LSB(MIDI_RX_SIZE_480),MSB(MIDI_RX_SIZE_480),// wMaxPacketSize
0, // bInterval 0, // bInterval
0, // bRefresh 0, // bRefresh
0, // bSynchAddress 0, // bSynchAddress
5, // bDescriptorType = ENDPOINT 5, // bDescriptorType = ENDPOINT
MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk) 0x02, // bmAttributes (0x02=bulk)
MIDI_TX_SIZE, 0, // wMaxPacketSize
LSB(MIDI_TX_SIZE_480),MSB(MIDI_TX_SIZE_480),// wMaxPacketSize
0, // bInterval 0, // bInterval
0, // bRefresh 0, // bRefresh
0, // bSynchAddress 0, // bSynchAddress
5, // bDescriptorType = ENDPOINT 5, // bDescriptorType = ENDPOINT
MIDI_RX_ENDPOINT, // bEndpointAddress MIDI_RX_ENDPOINT, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk) 0x02, // bmAttributes (0x02=bulk)
MIDI_RX_SIZE, 0, // wMaxPacketSize
LSB(MIDI_RX_SIZE_12),MSB(MIDI_RX_SIZE_12),// wMaxPacketSize
0, // bInterval 0, // bInterval
0, // bRefresh 0, // bRefresh
0, // bSynchAddress 0, // bSynchAddress
5, // bDescriptorType = ENDPOINT 5, // bDescriptorType = ENDPOINT
MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk) 0x02, // bmAttributes (0x02=bulk)
MIDI_TX_SIZE, 0, // wMaxPacketSize
LSB(MIDI_TX_SIZE_12),MSB(MIDI_TX_SIZE_12),// wMaxPacketSize
0, // bInterval 0, // bInterval
0, // bRefresh 0, // bRefresh
0, // bSynchAddress 0, // bSynchAddress




__attribute__ ((section(".dmabuffers"), aligned(32))) __attribute__ ((section(".dmabuffers"), aligned(32)))
uint8_t usb_descriptor_buffer[CONFIG_DESC_SIZE];
uint8_t usb_descriptor_buffer[CONFIG_DESC_SIZE + 4096];







+ 59
- 59
teensy4/usb_desc.h View File

#define NUM_ENDPOINTS 4 #define NUM_ENDPOINTS 4
#define NUM_INTERFACE 2 #define NUM_INTERFACE 2
#define SEREMU_INTERFACE 1 // Serial emulation #define SEREMU_INTERFACE 1 // Serial emulation
#define SEREMU_TX_ENDPOINT 1
#define SEREMU_TX_ENDPOINT 2
#define SEREMU_TX_SIZE 64 #define SEREMU_TX_SIZE 64
#define SEREMU_TX_INTERVAL 1 #define SEREMU_TX_INTERVAL 1
#define SEREMU_RX_ENDPOINT 2 #define SEREMU_RX_ENDPOINT 2
#define MIDI_INTERFACE 0 // MIDI #define MIDI_INTERFACE 0 // MIDI
#define MIDI_NUM_CABLES 1 #define MIDI_NUM_CABLES 1
#define MIDI_TX_ENDPOINT 3 #define MIDI_TX_ENDPOINT 3
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 3
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_MIDI4) #elif defined(USB_MIDI4)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0
#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','4'} #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','4'}
#define PRODUCT_NAME_LEN 13 #define PRODUCT_NAME_LEN 13
#define EP0_SIZE 64 #define EP0_SIZE 64
#define NUM_ENDPOINTS 4
#define NUM_ENDPOINTS 3
#define NUM_INTERFACE 2 #define NUM_INTERFACE 2
#define SEREMU_INTERFACE 1 // Serial emulation #define SEREMU_INTERFACE 1 // Serial emulation
#define SEREMU_TX_ENDPOINT 1
#define SEREMU_TX_ENDPOINT 2
#define SEREMU_TX_SIZE 64 #define SEREMU_TX_SIZE 64
#define SEREMU_TX_INTERVAL 1 #define SEREMU_TX_INTERVAL 1
#define SEREMU_RX_ENDPOINT 2 #define SEREMU_RX_ENDPOINT 2
#define MIDI_INTERFACE 0 // MIDI #define MIDI_INTERFACE 0 // MIDI
#define MIDI_NUM_CABLES 4 #define MIDI_NUM_CABLES 4
#define MIDI_TX_ENDPOINT 3 #define MIDI_TX_ENDPOINT 3
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 3
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_MIDI16) #elif defined(USB_MIDI16)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0
#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','1','6'} #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','1','6'}
#define PRODUCT_NAME_LEN 14 #define PRODUCT_NAME_LEN 14
#define EP0_SIZE 64 #define EP0_SIZE 64
#define NUM_ENDPOINTS 4
#define NUM_ENDPOINTS 3
#define NUM_INTERFACE 2 #define NUM_INTERFACE 2
#define SEREMU_INTERFACE 1 // Serial emulation #define SEREMU_INTERFACE 1 // Serial emulation
#define SEREMU_TX_ENDPOINT 1
#define SEREMU_TX_ENDPOINT 2
#define SEREMU_TX_SIZE 64 #define SEREMU_TX_SIZE 64
#define SEREMU_TX_INTERVAL 1 #define SEREMU_TX_INTERVAL 1
#define SEREMU_RX_ENDPOINT 2 #define SEREMU_RX_ENDPOINT 2
#define MIDI_INTERFACE 0 // MIDI #define MIDI_INTERFACE 0 // MIDI
#define MIDI_NUM_CABLES 16 #define MIDI_NUM_CABLES 16
#define MIDI_TX_ENDPOINT 3 #define MIDI_TX_ENDPOINT 3
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 3
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_MIDI_SERIAL) #elif defined(USB_MIDI_SERIAL)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0
#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I'} #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I'}
#define PRODUCT_NAME_LEN 11 #define PRODUCT_NAME_LEN 11
#define EP0_SIZE 64 #define EP0_SIZE 64
#define NUM_ENDPOINTS 5
#define NUM_ENDPOINTS 4
#define NUM_INTERFACE 3 #define NUM_INTERFACE 3
#define CDC_IAD_DESCRIPTOR 1 #define CDC_IAD_DESCRIPTOR 1
#define CDC_STATUS_INTERFACE 0 #define CDC_STATUS_INTERFACE 0
#define CDC_DATA_INTERFACE 1 // Serial #define CDC_DATA_INTERFACE 1 // Serial
#define CDC_ACM_ENDPOINT 1
#define CDC_RX_ENDPOINT 2
#define CDC_ACM_ENDPOINT 2
#define CDC_RX_ENDPOINT 3
#define CDC_TX_ENDPOINT 3 #define CDC_TX_ENDPOINT 3
#define CDC_ACM_SIZE 16 #define CDC_ACM_SIZE 16
#define CDC_RX_SIZE 64 #define CDC_RX_SIZE 64
#define MIDI_INTERFACE 2 // MIDI #define MIDI_INTERFACE 2 // MIDI
#define MIDI_NUM_CABLES 1 #define MIDI_NUM_CABLES 1
#define MIDI_TX_ENDPOINT 4 #define MIDI_TX_ENDPOINT 4
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 5
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_MIDI4_SERIAL) #elif defined(USB_MIDI4_SERIAL)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0
#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','4'} #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','4'}
#define PRODUCT_NAME_LEN 13 #define PRODUCT_NAME_LEN 13
#define EP0_SIZE 64 #define EP0_SIZE 64
#define NUM_ENDPOINTS 5
#define NUM_ENDPOINTS 4
#define NUM_INTERFACE 3 #define NUM_INTERFACE 3
#define CDC_IAD_DESCRIPTOR 1 #define CDC_IAD_DESCRIPTOR 1
#define CDC_STATUS_INTERFACE 0 #define CDC_STATUS_INTERFACE 0
#define CDC_DATA_INTERFACE 1 // Serial #define CDC_DATA_INTERFACE 1 // Serial
#define CDC_ACM_ENDPOINT 1
#define CDC_RX_ENDPOINT 2
#define CDC_ACM_ENDPOINT 2
#define CDC_RX_ENDPOINT 3
#define CDC_TX_ENDPOINT 3 #define CDC_TX_ENDPOINT 3
#define CDC_ACM_SIZE 16 #define CDC_ACM_SIZE 16
#define CDC_RX_SIZE 64 #define CDC_RX_SIZE 64
#define MIDI_INTERFACE 2 // MIDI #define MIDI_INTERFACE 2 // MIDI
#define MIDI_NUM_CABLES 4 #define MIDI_NUM_CABLES 4
#define MIDI_TX_ENDPOINT 4 #define MIDI_TX_ENDPOINT 4
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 5
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_MIDI16_SERIAL) #elif defined(USB_MIDI16_SERIAL)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0
#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','1','6'} #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','x','1','6'}
#define PRODUCT_NAME_LEN 14 #define PRODUCT_NAME_LEN 14
#define EP0_SIZE 64 #define EP0_SIZE 64
#define NUM_ENDPOINTS 5
#define NUM_ENDPOINTS 4
#define NUM_INTERFACE 3 #define NUM_INTERFACE 3
#define CDC_IAD_DESCRIPTOR 1 #define CDC_IAD_DESCRIPTOR 1
#define CDC_STATUS_INTERFACE 0 #define CDC_STATUS_INTERFACE 0
#define CDC_DATA_INTERFACE 1 // Serial #define CDC_DATA_INTERFACE 1 // Serial
#define CDC_ACM_ENDPOINT 1
#define CDC_RX_ENDPOINT 2
#define CDC_ACM_ENDPOINT 2
#define CDC_RX_ENDPOINT 3
#define CDC_TX_ENDPOINT 3 #define CDC_TX_ENDPOINT 3
#define CDC_ACM_SIZE 16 #define CDC_ACM_SIZE 16
#define CDC_RX_SIZE 64 #define CDC_RX_SIZE 64
#define MIDI_INTERFACE 2 // MIDI #define MIDI_INTERFACE 2 // MIDI
#define MIDI_NUM_CABLES 16 #define MIDI_NUM_CABLES 16
#define MIDI_TX_ENDPOINT 4 #define MIDI_TX_ENDPOINT 4
#define MIDI_TX_SIZE 64
#define MIDI_RX_ENDPOINT 5
#define MIDI_RX_SIZE 64
#define ENDPOINT1_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
#define ENDPOINT3_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT4_CONFIG ENDPOINT_TRANSMIT_ONLY
#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_ONLY
#define MIDI_TX_SIZE_12 64
#define MIDI_TX_SIZE_480 512
#define MIDI_RX_ENDPOINT 4
#define MIDI_RX_SIZE_12 64
#define MIDI_RX_SIZE_480 512
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK


#elif defined(USB_RAWHID) #elif defined(USB_RAWHID)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0

+ 509
- 0
teensy4/usb_midi.c View File

/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "usb_dev.h"
#include "usb_midi.h"
#include "core_pins.h" // for yield()
#include <string.h> // for memcpy()
#include "avr/pgmspace.h" // for PROGMEM, DMAMEM, FASTRUN
#include "debug/printf.h"
#include "core_pins.h"

#ifdef MIDI_INTERFACE // defined by usb_dev.h -> usb_desc.h

uint8_t usb_midi_msg_cable;
uint8_t usb_midi_msg_channel;
uint8_t usb_midi_msg_type;
uint8_t usb_midi_msg_data1;
uint8_t usb_midi_msg_data2;
// TODO: separate sysex buffers for each cable...
uint8_t usb_midi_msg_sysex[USB_MIDI_SYSEX_MAX];
uint16_t usb_midi_msg_sysex_len;
void (*usb_midi_handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel) = NULL;
void (*usb_midi_handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel) = NULL;
void (*usb_midi_handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel) = NULL;
void (*usb_midi_handleControlChange)(uint8_t ch, uint8_t control, uint8_t value) = NULL;
void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program) = NULL;
void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure) = NULL;
void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch) = NULL;
void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete) = NULL;
void (*usb_midi_handleSysExComplete)(uint8_t *data, unsigned int size) = NULL;
void (*usb_midi_handleTimeCodeQuarterFrame)(uint8_t data) = NULL;
void (*usb_midi_handleSongPosition)(uint16_t beats) = NULL;
void (*usb_midi_handleSongSelect)(uint8_t songnumber) = NULL;
void (*usb_midi_handleTuneRequest)(void) = NULL;
void (*usb_midi_handleClock)(void) = NULL;
void (*usb_midi_handleStart)(void) = NULL;
void (*usb_midi_handleContinue)(void) = NULL;
void (*usb_midi_handleStop)(void) = NULL;
void (*usb_midi_handleActiveSensing)(void) = NULL;
void (*usb_midi_handleSystemReset)(void) = NULL;
void (*usb_midi_handleRealTimeSystem)(uint8_t rtb) = NULL;


//static usb_packet_t *rx_packet=NULL;
//static usb_packet_t *tx_packet=NULL;
static uint8_t transmit_previous_timeout=0;
static uint8_t tx_noautoflush=0;
extern volatile uint8_t usb_high_speed;


#define TX_NUM 4
#define TX_SIZE 512 /* should be a multiple of MIDI_TX_SIZE_480 */
static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32)));
DMAMEM static uint8_t txbuffer[TX_SIZE * TX_NUM] __attribute__ ((aligned(32)));
static uint8_t tx_head=0;
static uint16_t tx_available=0;
static uint16_t tx_packet_size=0;

#define RX_NUM 6
static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32)));
DMAMEM static uint8_t rx_buffer[RX_NUM * MIDI_RX_SIZE_480] __attribute__ ((aligned(32)));
static uint16_t rx_count[RX_NUM];
static uint16_t rx_index[RX_NUM];
static uint16_t rx_packet_size=0;
static volatile uint8_t rx_head;
static volatile uint8_t rx_tail;
static uint8_t rx_list[RX_NUM + 1];
static volatile uint32_t rx_available;
static void rx_queue_transfer(int i);
static void rx_event(transfer_t *t);


void usb_midi_configure(void)
{
printf("usb_midi_configure\n");
if (usb_high_speed) {
tx_packet_size = MIDI_TX_SIZE_480;
rx_packet_size = MIDI_RX_SIZE_480;
} else {
tx_packet_size = MIDI_TX_SIZE_12;
rx_packet_size = MIDI_RX_SIZE_12;
}
memset(tx_transfer, 0, sizeof(tx_transfer));
tx_head = 0;
tx_available = 0;
memset(rx_transfer, 0, sizeof(rx_transfer));
memset(rx_count, 0, sizeof(rx_count));
memset(rx_index, 0, sizeof(rx_index));
rx_head = 0;
rx_tail = 0;
rx_available = 0;
usb_config_rx(MIDI_RX_ENDPOINT, rx_packet_size, 0, rx_event);
usb_config_tx(MIDI_TX_ENDPOINT, tx_packet_size, 1, NULL);
int i;
//for (i=0; i < RX_NUM; i++) rx_queue_transfer(i);
// TODO: set up SOF interrupt....
transmit_previous_timeout = 0;
tx_noautoflush = 0;
}



// When the PC isn't listening, how long do we wait before discarding data?
#define TX_TIMEOUT_MSEC 40


void usb_midi_write_packed(uint32_t n)
{
printf("usb_midi_write_packed\n");
if (!usb_configuration) return;
tx_noautoflush = 1;
uint32_t head = tx_head;
transfer_t *xfer = tx_transfer + head;
uint32_t wait_begin_at = systick_millis_count;
while (!tx_available) {
uint32_t status = usb_transfer_status(xfer);
if (!(status & 0x80)) {
if (status & 0x68) {
// TODO: what if status has errors???
}
tx_available = tx_packet_size;
transmit_previous_timeout = 0;
break;
}
if (systick_millis_count - wait_begin_at > TX_TIMEOUT_MSEC) {
transmit_previous_timeout = 1;
}
if (transmit_previous_timeout) return;
if (!usb_configuration) return;
yield();
}
uint32_t *txdata = (uint32_t *)(txbuffer + (tx_head * TX_SIZE) + (TX_SIZE - tx_available));
*txdata = n;
tx_available -= 4;
if (tx_available == 0) {
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE);
usb_prepare_transfer(xfer, txbuf, tx_packet_size, 0);
arm_dcache_flush_delete(txbuf, TX_SIZE);
usb_transmit(MIDI_TX_ENDPOINT, xfer);
if (++head >= TX_NUM) head = 0;
tx_head = head;
}
tx_noautoflush = 0;
}

void usb_midi_flush_output(void)
{
printf("usb_midi_flush_output\n");
if (tx_noautoflush == 0 && tx_available > 0) {
printf(" tx, %d %d\n", tx_packet_size, tx_available);
uint32_t head = tx_head;
transfer_t *xfer = tx_transfer + head;
uint8_t *txbuf = txbuffer + (head * TX_SIZE);
uint32_t len = tx_packet_size - tx_available;
usb_prepare_transfer(xfer, txbuf, len, 0);
arm_dcache_flush_delete(txbuf, TX_SIZE);
usb_transmit(MIDI_TX_ENDPOINT, xfer);
if (++head >= TX_NUM) head = 0;
tx_head = head;
tx_available = 0;
}
}

void usb_midi_send_sysex_buffer_has_term(const uint8_t *data, uint32_t length, uint8_t cable)
{
cable = (cable & 0x0F) << 4;
while (length > 3) {
usb_midi_write_packed(0x04 | cable | (data[0] << 8) | (data[1] << 16) | (data[2] << 24));
data += 3;
length -= 3;
}
if (length == 3) {
usb_midi_write_packed(0x07 | cable | (data[0] << 8) | (data[1] << 16) | (data[2] << 24));
} else if (length == 2) {
usb_midi_write_packed(0x06 | cable | (data[0] << 8) | (data[1] << 16));
} else if (length == 1) {
usb_midi_write_packed(0x05 | cable | (data[0] << 8));
}
}

void usb_midi_send_sysex_add_term_bytes(const uint8_t *data, uint32_t length, uint8_t cable)
{
cable = (cable & 0x0F) << 4;

if (length == 0) {
usb_midi_write_packed(0x06 | cable | (0xF0 << 8) | (0xF7 << 16));
return;
} else if (length == 1) {
usb_midi_write_packed(0x07 | cable | (0xF0 << 8) | (data[0] << 16) | (0xF7 << 24));
return;
} else {
usb_midi_write_packed(0x04 | cable | (0xF0 << 8) | (data[0] << 16) | (data[1] << 24));
data += 2;
length -= 2;
}
while (length >= 3) {
usb_midi_write_packed(0x04 | cable | (data[0] << 8) | (data[1] << 16) | (data[2] << 24));
data += 3;
length -= 3;
}
if (length == 2) {
usb_midi_write_packed(0x07 | cable | (data[0] << 8) | (data[1] << 16) | (0xF7 << 24));
} else if (length == 1) {
usb_midi_write_packed(0x06 | cable | (data[0] << 8) | (0xF7 << 16));
} else {
usb_midi_write_packed(0x05 | cable | (0xF7 << 8));
}
}

void static sysex_byte(uint8_t b)
{
if (usb_midi_handleSysExPartial && usb_midi_msg_sysex_len >= USB_MIDI_SYSEX_MAX) {
// when buffer is full, send another chunk to partial handler.
(*usb_midi_handleSysExPartial)(usb_midi_msg_sysex, usb_midi_msg_sysex_len, 0);
usb_midi_msg_sysex_len = 0;
}
if (usb_midi_msg_sysex_len < USB_MIDI_SYSEX_MAX) {
usb_midi_msg_sysex[usb_midi_msg_sysex_len++] = b;
}
}


// called by USB interrupt when any packet is received
static void rx_event(transfer_t *t)
{
}


uint32_t usb_midi_available(void)
{
#if 0
uint32_t index;

if (!rx_packet) {
if (!usb_configuration) return 0;
rx_packet = usb_rx(MIDI_RX_ENDPOINT);
if (!rx_packet) return 0;
if (rx_packet->len == 0) {
usb_free(rx_packet);
rx_packet = NULL;
return 0;
}
}
index = rx_packet->index;
return rx_packet->len - index;
#endif
return 0;
}

uint32_t usb_midi_read_message(void)
{
#if 0
uint32_t n, index;

if (!rx_packet) {
if (!usb_configuration) return 0;
rx_packet = usb_rx(MIDI_RX_ENDPOINT);
if (!rx_packet) return 0;
if (rx_packet->len == 0) {
usb_free(rx_packet);
rx_packet = NULL;
return 0;
}
}
index = rx_packet->index;
n = ((uint32_t *)rx_packet->buf)[index/4];
index += 4;
if (index < rx_packet->len) {
rx_packet->index = index;
} else {
usb_free(rx_packet);
rx_packet = usb_rx(MIDI_RX_ENDPOINT);
}
return n;
#endif
return 0;
}

int usb_midi_read(uint32_t channel)
{
uint32_t n, index, ch, type1, type2, b1;
return 0;
#if 0
if (!rx_packet) {
if (!usb_configuration) return 0;
rx_packet = usb_rx(MIDI_RX_ENDPOINT);
if (!rx_packet) return 0;
if (rx_packet->len == 0) {
usb_free(rx_packet);
rx_packet = NULL;
return 0;
}
}
index = rx_packet->index;
n = ((uint32_t *)rx_packet->buf)[index/4];
//serial_print("midi rx, n=");
//serial_phex32(n);
//serial_print("\n");
index += 4;
if (index < rx_packet->len) {
rx_packet->index = index;
} else {
usb_free(rx_packet);
rx_packet = usb_rx(MIDI_RX_ENDPOINT);
}
#endif
type1 = n & 15;
type2 = (n >> 12) & 15;
b1 = (n >> 8) & 0xFF;
ch = (b1 & 15) + 1;
usb_midi_msg_cable = (n >> 4) & 15;
if (type1 >= 0x08 && type1 <= 0x0E) {
if (channel && channel != ch) {
// ignore other channels when user wants single channel read
return 0;
}
if (type1 == 0x08 && type2 == 0x08) {
usb_midi_msg_type = 0x80; // 0x80 = usbMIDI.NoteOff
if (usb_midi_handleNoteOff)
(*usb_midi_handleNoteOff)(ch, (n >> 16), (n >> 24));
} else
if (type1 == 0x09 && type2 == 0x09) {
if ((n >> 24) > 0) {
usb_midi_msg_type = 0x90; // 0x90 = usbMIDI.NoteOn
if (usb_midi_handleNoteOn)
(*usb_midi_handleNoteOn)(ch, (n >> 16), (n >> 24));
} else {
usb_midi_msg_type = 0x80; // 0x80 = usbMIDI.NoteOff
if (usb_midi_handleNoteOff)
(*usb_midi_handleNoteOff)(ch, (n >> 16), (n >> 24));
}
} else
if (type1 == 0x0A && type2 == 0x0A) {
usb_midi_msg_type = 0xA0; // 0xA0 = usbMIDI.AfterTouchPoly
if (usb_midi_handleVelocityChange)
(*usb_midi_handleVelocityChange)(ch, (n >> 16), (n >> 24));
} else
if (type1 == 0x0B && type2 == 0x0B) {
usb_midi_msg_type = 0xB0; // 0xB0 = usbMIDI.ControlChange
if (usb_midi_handleControlChange)
(*usb_midi_handleControlChange)(ch, (n >> 16), (n >> 24));
} else
if (type1 == 0x0C && type2 == 0x0C) {
usb_midi_msg_type = 0xC0; // 0xC0 = usbMIDI.ProgramChange
if (usb_midi_handleProgramChange)
(*usb_midi_handleProgramChange)(ch, (n >> 16));
} else
if (type1 == 0x0D && type2 == 0x0D) {
usb_midi_msg_type = 0xD0; // 0xD0 = usbMIDI.AfterTouchChannel
if (usb_midi_handleAfterTouch)
(*usb_midi_handleAfterTouch)(ch, (n >> 16));
} else
if (type1 == 0x0E && type2 == 0x0E) {
usb_midi_msg_type = 0xE0; // 0xE0 = usbMIDI.PitchBend
if (usb_midi_handlePitchChange) {
int value = ((n >> 16) & 0x7F) | ((n >> 17) & 0x3F80);
value -= 8192; // 0 to 16383 --> -8192 to +8191
(*usb_midi_handlePitchChange)(ch, value);
}
} else {
return 0;
}
return_message:
usb_midi_msg_channel = ch;
usb_midi_msg_data1 = (n >> 16);
usb_midi_msg_data2 = (n >> 24);
return 1;
}
if (type1 == 0x02 || type1 == 0x03 || (type1 == 0x05 && b1 >= 0xF1 && b1 != 0xF7)) {
// system common or system realtime message
system_common_or_realtime:
switch (b1) {
case 0xF1: // usbMIDI.TimeCodeQuarterFrame
if (usb_midi_handleTimeCodeQuarterFrame) {
(*usb_midi_handleTimeCodeQuarterFrame)(n >> 16);
}
break;
case 0xF2: // usbMIDI.SongPosition
if (usb_midi_handleSongPosition) {
(*usb_midi_handleSongPosition)(
((n >> 16) & 0x7F) | ((n >> 17) & 0x3F80));
}
break;
case 0xF3: // usbMIDI.SongSelect
if (usb_midi_handleSongSelect) {
(*usb_midi_handleSongSelect)(n >> 16);
}
break;
case 0xF6: // usbMIDI.TuneRequest
if (usb_midi_handleTuneRequest) {
(*usb_midi_handleTuneRequest)();
}
break;
case 0xF8: // usbMIDI.Clock
if (usb_midi_handleClock) {
(*usb_midi_handleClock)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xF8);
}
break;
case 0xFA: // usbMIDI.Start
if (usb_midi_handleStart) {
(*usb_midi_handleStart)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xFA);
}
break;
case 0xFB: // usbMIDI.Continue
if (usb_midi_handleContinue) {
(*usb_midi_handleContinue)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xFB);
}
break;
case 0xFC: // usbMIDI.Stop
if (usb_midi_handleStop) {
(*usb_midi_handleStop)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xFC);
}
break;
case 0xFE: // usbMIDI.ActiveSensing
if (usb_midi_handleActiveSensing) {
(*usb_midi_handleActiveSensing)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xFE);
}
break;
case 0xFF: // usbMIDI.SystemReset
if (usb_midi_handleSystemReset) {
(*usb_midi_handleSystemReset)();
} else if (usb_midi_handleRealTimeSystem) {
(*usb_midi_handleRealTimeSystem)(0xFF);
}
break;
default:
return 0; // unknown message, ignore it
}
usb_midi_msg_type = b1;
goto return_message;
}
if (type1 == 0x04) {
sysex_byte(n >> 8);
sysex_byte(n >> 16);
sysex_byte(n >> 24);
return 0;
}
if (type1 >= 0x05 && type1 <= 0x07) {
sysex_byte(b1);
if (type1 >= 0x06) sysex_byte(n >> 16);
if (type1 == 0x07) sysex_byte(n >> 24);
uint16_t len = usb_midi_msg_sysex_len;
usb_midi_msg_data1 = len;
usb_midi_msg_data2 = len >> 8;
usb_midi_msg_sysex_len = 0;
usb_midi_msg_type = 0xF0; // 0xF0 = usbMIDI.SystemExclusive
if (usb_midi_handleSysExPartial) {
(*usb_midi_handleSysExPartial)(usb_midi_msg_sysex, len, 1);
} else if (usb_midi_handleSysExComplete) {
(*usb_midi_handleSysExComplete)(usb_midi_msg_sysex, len);
}
return 1;
}
if (type1 == 0x0F) {
if (b1 >= 0xF8) {
// From Sebastian Tomczak, seb.tomczak at gmail.com
// http://little-scale.blogspot.com/2011/08/usb-midi-game-boy-sync-for-16.html
goto system_common_or_realtime;
}
if (b1 == 0xF0 || usb_midi_msg_sysex_len > 0) {
// From David Sorlien, dsorlien at gmail.com, http://axe4live.wordpress.com
// OSX sometimes uses Single Byte Unparsed to
// send bytes in the middle of a SYSEX message.
sysex_byte(b1);
}
}
return 0;
}


#endif // MIDI_INTERFACE

+ 389
- 0
teensy4/usb_midi.h View File

/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef USBmidi_h_
#define USBmidi_h_

#include "usb_desc.h"

#if defined(MIDI_INTERFACE)

#include <inttypes.h>

// maximum sysex length we can receive
#define USB_MIDI_SYSEX_MAX 290

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
void usb_midi_configure(void);
void usb_midi_write_packed(uint32_t n);
void usb_midi_send_sysex_buffer_has_term(const uint8_t *data, uint32_t length, uint8_t cable);
void usb_midi_send_sysex_add_term_bytes(const uint8_t *data, uint32_t length, uint8_t cable);
void usb_midi_flush_output(void);
int usb_midi_read(uint32_t channel);
uint32_t usb_midi_available(void);
uint32_t usb_midi_read_message(void);
extern uint8_t usb_midi_msg_cable;
extern uint8_t usb_midi_msg_channel;
extern uint8_t usb_midi_msg_type;
extern uint8_t usb_midi_msg_data1;
extern uint8_t usb_midi_msg_data2;
extern uint8_t usb_midi_msg_sysex[USB_MIDI_SYSEX_MAX];
extern uint16_t usb_midi_msg_sysex_len;
extern volatile uint8_t usb_configuration;
extern void (*usb_midi_handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleControlChange)(uint8_t ch, uint8_t control, uint8_t value);
extern void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program);
extern void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure);
extern void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch);
extern void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete);
extern void (*usb_midi_handleSysExComplete)(uint8_t *data, unsigned int size);
extern void (*usb_midi_handleTimeCodeQuarterFrame)(uint8_t data);
extern void (*usb_midi_handleSongPosition)(uint16_t beats);
extern void (*usb_midi_handleSongSelect)(uint8_t songnumber);
extern void (*usb_midi_handleTuneRequest)(void);
extern void (*usb_midi_handleClock)(void);
extern void (*usb_midi_handleStart)(void);
extern void (*usb_midi_handleContinue)(void);
extern void (*usb_midi_handleStop)(void);
extern void (*usb_midi_handleActiveSensing)(void);
extern void (*usb_midi_handleSystemReset)(void);
extern void (*usb_midi_handleRealTimeSystem)(uint8_t rtb);

#ifdef __cplusplus
}
#endif

// To test receiving on Linux, run "aseqdump -l" to list sequencer devices.
//
// Port Client name Port name
// 0:0 System Timer
// 0:1 System Announce
// 14:0 Midi Through Midi Through Port-0
// 24:0 Teensy MIDI Teensy MIDI MIDI 1
// 28:0 AKM320 AKM320 MIDI 1
//
// Then run "aseqdump -p 24:0" to view the MIDI messages.
//
// Waiting for data. Press Ctrl+C to end.
// Source Event Ch Data
// 24:0 Note on 0, note 61, velocity 99
// 24:0 Note off 0, note 61, velocity 0
// 24:0 Note on 0, note 62, velocity 99
// 24:0 Note off 0, note 62, velocity 0
// 24:0 Note on 0, note 64, velocity 99
// 24:0 Note off 0, note 64, velocity 0
//
// Quick-dirty way to transmit MIDI sysex:
// echo -n -e '\xF0abcd\xF7' > /dev/midi2

// C++ interface
#ifdef __cplusplus
class usb_midi_class
{
public:
// Message type names for compatibility with Arduino MIDI library 4.3.1
enum MidiType {
InvalidType = 0x00, // For notifying errors
NoteOff = 0x80, // Note Off
NoteOn = 0x90, // Note On
AfterTouchPoly = 0xA0, // Polyphonic AfterTouch
ControlChange = 0xB0, // Control Change / Channel Mode
ProgramChange = 0xC0, // Program Change
AfterTouchChannel = 0xD0, // Channel (monophonic) AfterTouch
PitchBend = 0xE0, // Pitch Bend
SystemExclusive = 0xF0, // System Exclusive
TimeCodeQuarterFrame = 0xF1, // System Common - MIDI Time Code Quarter Frame
SongPosition = 0xF2, // System Common - Song Position Pointer
SongSelect = 0xF3, // System Common - Song Select
TuneRequest = 0xF6, // System Common - Tune Request
Clock = 0xF8, // System Real Time - Timing Clock
Start = 0xFA, // System Real Time - Start
Continue = 0xFB, // System Real Time - Continue
Stop = 0xFC, // System Real Time - Stop
ActiveSensing = 0xFE, // System Real Time - Active Sensing
SystemReset = 0xFF, // System Real Time - System Reset
};
void begin(void) { }
void end(void) { }
void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0x80, note, velocity, channel, cable);
}
void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0x90, note, velocity, channel, cable);
}
void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xA0, note, pressure, channel, cable);
}
void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xA0, note, pressure, channel, cable);
}
void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xB0, control, value, channel, cable);
}
void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xC0, program, 0, channel, cable);
}
void sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xD0, pressure, 0, channel, cable);
}
void sendPitchBend(int value, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
if (value < -8192) {
value = -8192;
} else if (value > 8191) {
value = 8191;
}
value += 8192;
send(0xE0, value, value >> 7, channel, cable);
}
void sendSysEx(uint32_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0) __attribute__((always_inline)) {
if (cable >= MIDI_NUM_CABLES) return;
if (hasTerm) {
usb_midi_send_sysex_buffer_has_term(data, length, cable);
} else {
usb_midi_send_sysex_add_term_bytes(data, length, cable);
}
}
void sendRealTime(uint8_t type, uint8_t cable=0) __attribute__((always_inline)) __attribute__((always_inline)) {
switch (type) {
case 0xF8: // Clock
case 0xFA: // Start
case 0xFB: // Continue
case 0xFC: // Stop
case 0xFE: // ActiveSensing
case 0xFF: // SystemReset
send(type, 0, 0, 0, cable);
break;
default: // Invalid Real Time marker
break;
}
}
void sendTimeCodeQuarterFrame(uint8_t type, uint8_t value, uint8_t cable=0) __attribute__((always_inline)) __attribute__((always_inline)) {
send(0xF1, ((type & 0x07) << 4) | (value & 0x0F), 0, 0, cable);
}
void sendSongPosition(uint16_t beats, uint8_t cable=0) __attribute__((always_inline)) {
send(0xF2, beats, beats >> 7, 0, cable);
}
void sendSongSelect(uint8_t song, uint8_t cable=0) __attribute__((always_inline)) {
send(0xF3, song, 0, 0, cable);
}
void sendTuneRequest(uint8_t cable=0) __attribute__((always_inline)) {
send(0xF6, 0, 0, 0, cable);
}
void beginRpn(uint16_t number, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(101, number >> 7, channel, cable);
sendControlChange(100, number, channel, cable);
}
void sendRpnValue(uint16_t value, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(6, value >> 7, channel, cable);
sendControlChange(38, value, channel, cable);
}
void sendRpnIncrement(uint8_t amount, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(96, amount, channel, cable);
}
void sendRpnDecrement(uint8_t amount, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(97, amount, channel, cable);
}
void endRpn(uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(101, 0x7F, channel, cable);
sendControlChange(100, 0x7F, channel, cable);
}
void beginNrpn(uint16_t number, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(99, number >> 7, channel, cable);
sendControlChange(98, number, channel, cable);
}
void sendNrpnValue(uint16_t value, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(6, value >> 7, channel, cable);
sendControlChange(38, value, channel, cable);
}
void sendNrpnIncrement(uint8_t amount, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(96, amount, channel, cable);
}
void sendNrpnDecrement(uint8_t amount, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(97, amount, channel, cable);
}
void endNrpn(uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
sendControlChange(99, 0x7F, channel, cable);
sendControlChange(98, 0x7F, channel, cable);
}
void send(uint8_t type, uint8_t data1, uint8_t data2, uint8_t channel, uint8_t cable) __attribute__((always_inline)) {
if (cable >= MIDI_NUM_CABLES) return;
if (type < 0xF0) {
if (type < 0x80) return;
type &= 0xF0;
usb_midi_write_packed((type << 8) | (type >> 4) | ((cable & 0x0F) << 4)
| (((channel - 1) & 0x0F) << 8) | ((data1 & 0x7F) << 16)
| ((data2 & 0x7F) << 24));
} else if (type >= 0xF8 || type == 0xF6) {
usb_midi_write_packed((type << 8) | 0x0F | ((cable & 0x0F) << 4));
} else if (type == 0xF1 || type == 0xF3) {
usb_midi_write_packed((type << 8) | 0x02 | ((cable & 0x0F) << 4)
| ((data1 & 0x7F) << 16));
} else if (type == 0xF2) {
usb_midi_write_packed((type << 8) | 0x03 | ((cable & 0x0F) << 4)
| ((data1 & 0x7F) << 16) | ((data2 & 0x7F) << 24));
}
}
void send_now(void) __attribute__((always_inline)) {
usb_midi_flush_output();
}
uint8_t analog2velocity(uint16_t val, uint8_t range);
bool read(uint8_t channel=0) __attribute__((always_inline)) {
return usb_midi_read(channel);
}
uint8_t getType(void) __attribute__((always_inline)) {
return usb_midi_msg_type;
}
uint8_t getCable(void) __attribute__((always_inline)) {
return usb_midi_msg_cable;
}
uint8_t getChannel(void) __attribute__((always_inline)) {
return usb_midi_msg_channel;
}
uint8_t getData1(void) __attribute__((always_inline)) {
return usb_midi_msg_data1;
}
uint8_t getData2(void) __attribute__((always_inline)) {
return usb_midi_msg_data2;
}
uint8_t * getSysExArray(void) __attribute__((always_inline)) {
return usb_midi_msg_sysex;
}
uint16_t getSysExArrayLength(void) __attribute__((always_inline)) {
return usb_midi_msg_data2 << 8 | usb_midi_msg_data1;
}
void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
// type: 0x80 NoteOff
usb_midi_handleNoteOff = fptr;
}
void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
// type: 0x90 NoteOn
usb_midi_handleNoteOn = fptr;
}
void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
// type: 0xA0 AfterTouchPoly
usb_midi_handleVelocityChange = fptr;
}
void setHandleAfterTouchPoly(void (*fptr)(uint8_t channel, uint8_t note, uint8_t pressure)) {
// type: 0xA0 AfterTouchPoly
usb_midi_handleVelocityChange = fptr;
}
void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) {
// type: 0xB0 ControlChange
usb_midi_handleControlChange = fptr;
}
void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) {
// type: 0xC0 ProgramChange
usb_midi_handleProgramChange = fptr;
}
void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) {
// type: 0xD0 AfterTouchChannel
usb_midi_handleAfterTouch = fptr;
}
void setHandleAfterTouchChannel(void (*fptr)(uint8_t channel, uint8_t pressure)) {
// type: 0xD0 AfterTouchChannel
usb_midi_handleAfterTouch = fptr;
}
void setHandlePitchChange(void (*fptr)(uint8_t channel, int pitch)) {
// type: 0xE0 PitchBend
usb_midi_handlePitchChange = fptr;
}
void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) {
// type: 0xF0 SystemExclusive - multiple calls for message bigger than buffer
usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
}
void setHandleSystemExclusive(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) {
// type: 0xF0 SystemExclusive - multiple calls for message bigger than buffer
usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
}
void setHandleSystemExclusive(void (*fptr)(uint8_t *data, unsigned int size)) {
// type: 0xF0 SystemExclusive - single call, message larger than buffer is truncated
usb_midi_handleSysExComplete = fptr;
}
void setHandleTimeCodeQuarterFrame(void (*fptr)(uint8_t data)) {
// type: 0xF1 TimeCodeQuarterFrame
usb_midi_handleTimeCodeQuarterFrame = fptr;
}
void setHandleSongPosition(void (*fptr)(uint16_t beats)) {
// type: 0xF2 SongPosition
usb_midi_handleSongPosition = fptr;
}
void setHandleSongSelect(void (*fptr)(uint8_t songnumber)) {
// type: 0xF3 SongSelect
usb_midi_handleSongSelect = fptr;
}
void setHandleTuneRequest(void (*fptr)(void)) {
// type: 0xF6 TuneRequest
usb_midi_handleTuneRequest = fptr;
}
void setHandleClock(void (*fptr)(void)) {
// type: 0xF8 Clock
usb_midi_handleClock = fptr;
}
void setHandleStart(void (*fptr)(void)) {
// type: 0xFA Start
usb_midi_handleStart = fptr;
}
void setHandleContinue(void (*fptr)(void)) {
// type: 0xFB Continue
usb_midi_handleContinue = fptr;
}
void setHandleStop(void (*fptr)(void)) {
// type: 0xFC Stop
usb_midi_handleStop = fptr;
}
void setHandleActiveSensing(void (*fptr)(void)) {
// type: 0xFE ActiveSensing
usb_midi_handleActiveSensing = fptr;
}
void setHandleSystemReset(void (*fptr)(void)) {
// type: 0xFF SystemReset
usb_midi_handleSystemReset = fptr;
}
void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) {
// type: 0xF8-0xFF - if more specific handler not configured
usb_midi_handleRealTimeSystem = fptr;
};
};

extern usb_midi_class usbMIDI;


#endif // __cplusplus

#endif // MIDI_INTERFACE

#endif // USBmidi_h_


Loading…
Cancel
Save