Skip to content

File midi.h

File List > external-docs > libDaisy > src > hid > midi.h

Go to the documentation of this file

Source Code

#pragma once
#ifndef DSY_MIDI_H
#define DSY_MIDI_H

#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include "per/uart.h"
#include "util/ringbuffer.h"
#include "util/FIFO.h"
#include "hid/midi_parser.h"
#include "hid/usb_midi.h"
#include "sys/dma.h"
#include "sys/system.h"

namespace daisy
{
class MidiUartTransport
{
  public:
    typedef void (*MidiRxParseCallback)(uint8_t* data,
                                        size_t   size,
                                        void*    context);

    MidiUartTransport() {}
    ~MidiUartTransport() {}

    struct Config
    {
        UartHandler::Config::Peripheral periph;
        Pin                             rx;
        Pin                             tx;

        uint8_t* rx_buffer;

        size_t rx_buffer_size;

        Config();
    };

    inline void Init(Config config)
    {
        UartHandler::Config uart_config;

        //defaults
        uart_config.baudrate   = 31250;
        uart_config.stopbits   = UartHandler::Config::StopBits::BITS_1;
        uart_config.parity     = UartHandler::Config::Parity::NONE;
        uart_config.mode       = UartHandler::Config::Mode::TX_RX;
        uart_config.wordlength = UartHandler::Config::WordLength::BITS_8;

        //user settings
        uart_config.periph        = config.periph;
        uart_config.pin_config.rx = config.rx;
        uart_config.pin_config.tx = config.tx;

        rx_buffer      = config.rx_buffer;
        rx_buffer_size = config.rx_buffer_size;

        std::fill(rx_buffer, rx_buffer + rx_buffer_size, 0);

        uart_.Init(uart_config);
    }

    inline void StartRx(MidiRxParseCallback parse_callback, void* context)
    {
        parse_context_  = context;
        parse_callback_ = parse_callback;
        dsy_dma_clear_cache_for_buffer((uint8_t*)this,
                                       sizeof(MidiUartTransport));
        uart_.DmaListenStart(
            rx_buffer, rx_buffer_size, MidiUartTransport::rxCallback, this);
    }

    inline bool RxActive() { return uart_.IsListening(); }

    inline void FlushRx() {}

    inline void Tx(uint8_t* buff, size_t size) { uart_.PollTx(buff, size); }

  private:
    UartHandler         uart_;
    uint8_t*            rx_buffer;
    size_t              rx_buffer_size;
    void*               parse_context_;
    MidiRxParseCallback parse_callback_;

    static void rxCallback(uint8_t*            data,
                           size_t              size,
                           void*               context,
                           UartHandler::Result res)
    {
        MidiUartTransport* transport
            = reinterpret_cast<MidiUartTransport*>(context);
        if(res == UartHandler::Result::OK)
        {
            if(transport->parse_callback_)
            {
                transport->parse_callback_(
                    data, size, transport->parse_context_);
            }
        }
    }
};

template <typename Transport>
class MidiHandler
{
  public:
    MidiHandler() {}
    ~MidiHandler() {}

    struct Config
    {
        typename Transport::Config transport_config;
    };

    void Init(Config config)
    {
        config_ = config;
        transport_.Init(config_.transport_config);
        parser_.Init();
    }

    void StartReceive()
    {
        transport_.StartRx(MidiHandler::ParseCallback, this);
    }

    void Listen()
    {
        // In case of UART Error, (particularly
        //  overrun error), UART disables itself.
        // Flush the buff, and restart.
        if(!transport_.RxActive())
        {
            parser_.Reset();
            transport_.FlushRx();
            StartReceive();
        }
    }

    bool HasEvents() const { return event_q_.GetNumElements() > 0; }

    bool RxActive() { return transport_.RxActive(); }

    MidiEvent PopEvent() { return event_q_.PopFront(); }

    void SendMessage(uint8_t* bytes, size_t size)
    {
        transport_.Tx(bytes, size);
    }

    void Parse(uint8_t byte)
    {
        MidiEvent event;
        if(parser_.Parse(byte, &event))
        {
            event_q_.PushBack(event);
        }
    }

  private:
    Config               config_;
    Transport            transport_;
    MidiParser           parser_;
    FIFO<MidiEvent, 256> event_q_;

    static void ParseCallback(uint8_t* data, size_t size, void* context)
    {
        MidiHandler* handler = reinterpret_cast<MidiHandler*>(context);
        for(size_t i = 0; i < size; i++)
        {
            handler->Parse(data[i]);
        }
    }
};

using MidiUartHandler = MidiHandler<MidiUartTransport>;
using MidiUsbHandler  = MidiHandler<MidiUsbTransport>;
} // namespace daisy
#endif