Skip to content

File syn_usb_midi.c

File List > drivers > syn_usb_midi.c

Go to the documentation of this file

#include "syn_usb_midi.h"

#include <string.h>

static SYN_Status usb_midi_setup_handler(void *ctx, const SYN_USB_SetupPacket *pkt, uint8_t *resp,
                                         uint16_t *rlen)
{
    (void)ctx;
    (void)pkt;
    (void)resp;
    (void)rlen;
    return SYN_OK;
}

static void usb_midi_data_out_handler(void *ctx, uint8_t ep, const uint8_t *data, uint16_t len)
{
    SYN_USB_MIDI *midi = (SYN_USB_MIDI *)ctx;
    if ((midi == NULL) || (data == NULL) || (len == 0U)) {
        return;
    }
    if (ep == midi->ep_out) {
        uint16_t copy_len = len > SYN_USB_MIDI_MAX_PACKET_SIZE ? SYN_USB_MIDI_MAX_PACKET_SIZE : len;
        memcpy(midi->rx_buf, data, copy_len);
        midi->rx_len = copy_len;
    }
}

static void usb_midi_data_in_handler(void *ctx, uint8_t ep)
{
    SYN_USB_MIDI *midi = (SYN_USB_MIDI *)ctx;
    if (midi == NULL) {
        return;
    }
    if (ep == midi->ep_in) {
        midi->tx_len = 0U;
    }
}

static SYN_Status usb_midi_configured_handler(void *ctx, uint8_t config)
{
    (void)ctx;
    (void)config;
    return SYN_OK;
}

SYN_Status syn_usb_midi_init(SYN_USB_MIDI *midi)
{
    if (midi == NULL) {
        return SYN_INVALID_PARAM;
    }
    memset(midi, 0, sizeof(*midi));
    midi->ep_in = 0x81U;  /* Bulk IN Endpoint 1 */
    midi->ep_out = 0x01U; /* Bulk OUT Endpoint 1 */
    midi->iface_num = 0U;
    midi->cable_num = 0U;
    return SYN_OK;
}

SYN_Status syn_usb_midi_register(SYN_USB_Device *dev, SYN_USB_MIDI *midi)
{
    if ((dev == NULL) || (midi == NULL)) {
        return SYN_INVALID_PARAM;
    }

    SYN_USB_ClassDriver driver;
    memset(&driver, 0, sizeof(driver));
    driver.iface_start = midi->iface_num;
    driver.iface_count = 2U; /* Control + Streaming interface */
    driver.ctx = midi;
    driver.setup = usb_midi_setup_handler;
    driver.data_out = usb_midi_data_out_handler;
    driver.data_in = usb_midi_data_in_handler;
    driver.configured = usb_midi_configured_handler;

    static const uint8_t midi_desc[9] = {0x09, 0x04, 0x00, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00};
    return syn_usb_register_class(dev, &driver, midi_desc, (uint16_t)sizeof(midi_desc));
}

static SYN_Status queue_midi_packet(SYN_USB_MIDI *midi, uint8_t cin, uint8_t status, uint8_t param1,
                                    uint8_t param2)
{
    if (midi == NULL) {
        return SYN_INVALID_PARAM;
    }
    if (midi->tx_len + 4U > SYN_USB_MIDI_MAX_PACKET_SIZE) {
        return SYN_ERROR; /* Buffer full */
    }

    uint8_t header = (uint8_t)(((midi->cable_num & 0x0FU) << 4) | (cin & 0x0FU));
    midi->tx_buf[midi->tx_len++] = header;
    midi->tx_buf[midi->tx_len++] = status;
    midi->tx_buf[midi->tx_len++] = param1;
    midi->tx_buf[midi->tx_len++] = param2;

    return SYN_OK;
}

SYN_Status syn_usb_midi_send_note_on(SYN_USB_MIDI *midi, uint8_t channel, uint8_t note,
                                     uint8_t velocity)
{
    if ((channel > 15U) || (note > 127U) || (velocity > 127U)) {
        return SYN_INVALID_PARAM;
    }
    uint8_t status = (uint8_t)(0x90U | channel);
    return queue_midi_packet(midi, SYN_USB_MIDI_CIN_NOTE_ON, status, note, velocity);
}

SYN_Status syn_usb_midi_send_note_off(SYN_USB_MIDI *midi, uint8_t channel, uint8_t note,
                                      uint8_t velocity)
{
    if ((channel > 15U) || (note > 127U) || (velocity > 127U)) {
        return SYN_INVALID_PARAM;
    }
    uint8_t status = (uint8_t)(0x80U | channel);
    return queue_midi_packet(midi, SYN_USB_MIDI_CIN_NOTE_OFF, status, note, velocity);
}

SYN_Status syn_usb_midi_send_cc(SYN_USB_MIDI *midi, uint8_t channel, uint8_t controller,
                                uint8_t value)
{
    if ((channel > 15U) || (controller > 127U) || (value > 127U)) {
        return SYN_INVALID_PARAM;
    }
    uint8_t status = (uint8_t)(0xB0U | channel);
    return queue_midi_packet(midi, SYN_USB_MIDI_CIN_CONTROL_CHANGE, status, controller, value);
}

SYN_Status syn_usb_midi_send_pitch_bend(SYN_USB_MIDI *midi, uint8_t channel, int16_t value)
{
    if ((channel > 15U) || (value < -8192) || (value > 8191)) {
        return SYN_INVALID_PARAM;
    }
    uint16_t unsigned_val = (uint16_t)(value + 8192);
    uint8_t lsb = (uint8_t)(unsigned_val & 0x7FU);
    uint8_t msb = (uint8_t)((unsigned_val >> 7) & 0x7FU);
    uint8_t status = (uint8_t)(0xE0U | channel);

    return queue_midi_packet(midi, SYN_USB_MIDI_CIN_PITCH_BEND, status, lsb, msb);
}

SYN_Status syn_usb_midi_parse_packet(const uint8_t raw_bytes[4], SYN_USB_MIDI_Packet *packet)
{
    if ((raw_bytes == NULL) || (packet == NULL)) {
        return SYN_INVALID_PARAM;
    }
    packet->header = raw_bytes[0];
    packet->midi0 = raw_bytes[1];
    packet->midi1 = raw_bytes[2];
    packet->midi2 = raw_bytes[3];
    return SYN_OK;
}