From a96a2bd87b546e8a109a3c2e680e7615d11316ab Mon Sep 17 00:00:00 2001 From: mlundinse Date: Fri, 10 May 2013 17:06:53 +0200 Subject: Use 32 bit midiusb messages Integrated Donald Delmar Davis work to use 32bit midimessages for MIDI USB, more cleanup is stiill needed --- libmaple/include/libmaple/midi_specs.h | 151 ++++++++++++++++++++++++++++ libmaple/include/libmaple/usb_midi_device.h | 14 +-- libmaple/usb/stm32f1/usb_midi_device.c | 96 +++++++++--------- wirish/include/wirish/usb_midi.h | 17 ++-- wirish/usb_midi.cpp | 25 ++--- 5 files changed, 224 insertions(+), 79 deletions(-) create mode 100644 libmaple/include/libmaple/midi_specs.h diff --git a/libmaple/include/libmaple/midi_specs.h b/libmaple/include/libmaple/midi_specs.h new file mode 100644 index 0000000..293060d --- /dev/null +++ b/libmaple/include/libmaple/midi_specs.h @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------MidiSpecs.h + * + * These defines are based on specs created by the USB and MMA standards organizations. + * There are not a lot of other ways to code them so liscensing this is rather ludicrous. + * However, in order to be able to embed this in client projects, and avoid the stupidity + * of enforced open everything I will declare the following about this file. + * + * Copyright (c) 2011 Donald Delmar Davis, Suspect Devices + * + * 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: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * 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. + * + */ + +/* + * USB midi commands from + * MIDI10.pdf "Universal Serial Bus Device Class Definition for MIDI Devices" + * REV 1. (1999) + * http://www.usb.org/developers/devclass_docs/midi10.pdf + * + */ + +#ifndef __LETS_MIDI_SPECS_H__ +#define __LETS_MIDI_SPECS_H__ +#include +#include +// rework this for the different architectures.... +#if defined(__GNUC__) +typedef struct +{ + unsigned cin : 4; // this is the low nibble. + unsigned cable : 4; +// uint8_t cin; + uint8_t midi0; + uint8_t midi1; + uint8_t midi2; +} __attribute__ ((__packed__)) MIDI_EVENT_PACKET_t ; +#else +typedef struct // may need to be adjusted for other compilers and bitfield order... +{ + unsigned cable : 4; + unsigned cin : 4; + uint8_t midi0; + uint8_t midi1; + uint8_t midi2; +} MIDI_EVENT_PACKET_t ; +#endif + + +#define CIN_MISC_FUNCTION 0x00 /* Reserved for future extension. */ +#define CIN_CABLE_EVENT 0x01 /* Reserved for future extension. */ +#define CIN_2BYTE_SYS_COMMON 0x02 /* 2Bytes -- MTC, SongSelect, etc. */ +#define CIN_3BYTE_SYS_COMMON 0x03 /* 3Bytes -- SPP, etc. */ +#define CIN_SYSEX 0x04 /* 3Bytes */ +#define CIN_SYSEX_ENDS_IN_1 0x05 /* 1Bytes */ +#define CIN_SYSEX_ENDS_IN_2 0x06 /* 2Bytes */ +#define CIN_SYSEX_ENDS_IN_3 0x07 /* 3Bytes */ +#define CIN_NOTE_OFF 0x08 /* 3Bytes */ +#define CIN_NOTE_ON 0x09 /* 3Bytes */ +#define CIN_AFTER_TOUCH 0x0A /* 3Bytes */ +#define CIN_CONTROL_CHANGE 0x0B /* 3Bytes */ +#define CIN_PROGRAM_CHANGE 0x0C /* 2Bytes */ +#define CIN_CHANNEL_PRESSURE 0x0D /* 2Bytes */ +#define CIN_PITCH_WHEEL 0x0E /* 3Bytes */ +#define CIN_1BYTE 0x0F /* 1Bytes */ +//#define CIN_IS_SYSEX(cin) ((cin == CIN_SYSEX)||(cin == CIN_SYSEX_ENDS_IN_1)||(cin == CIN_SYSEX_ENDS_IN_2)||(cin == CIN_SYSEX_ENDS_IN_3)) +#define CIN_IS_SYSEX(cin) ( ((cin) & ~(0x08)) && ((cin) &0x04) ) + +/* + * MIDI V1 message definitions these are from the MMA document + * http://www.midi.org/techspecs/midimessages.php + */ +#define MIDIv1_BAUD_RATE 31250 + +/* + * parse midi (v1) message macros + */ +#define MIDIv1_IS_STATUS(b) ((b) & 0x80) +#define MIDIv1_IS_VOICE(b) ((b) <= 0xEF) +#define MIDIv1_VOICE_COMMAND(b) ((b) & 0xF0) +#define MIDIv1_VOICE_CHANNEL(b) ((b) & 0x0F) +#define MIDIv1_IS_SYSCOMMON(b) (((b) >= 0xF0) & ((b) < 0xF8)) +#define MIDIv1_IS_REALTIME(b) ((b) >= 0xF8) + +/* + * Voice category messages + */ +#define MIDIv1_NOTE_OFF 0x80 /* 2 bytes data -- CIN_NOTE_OFF */ +#define MIDIv1_NOTE_ON 0x90 /* 2 bytes data -- CIN_NOTE_ON */ +#define MIDIv1_AFTER_TOUCH 0xA0 /* 2 bytes data -- CIN_AFTER_TOUCH */ +#define MIDIv1_CONTROL_CHANGE 0xB0 /* 2 bytes data -- CIN_CONTROL_CHANGE */ +#define MIDIv1_PROGRAM_CHANGE 0xC0 /* 1 byte data -- CIN_PROGRAM_CHANGE */ +#define MIDIv1_CHANNEL_PRESSURE 0xD0 /* 1 byte data -- CIN_CHANNEL_PRESSURE */ +#define MIDIv1_PITCH_WHEEL 0xE0 /* 2 bytes data -- CIN_PITCH_WHEEL */ + +/* + * System common category messages + */ +#define MIDIv1_SYSEX_START 0xF0 +#define MIDIv1_SYSEX_END 0xF7 +#define MIDIv1_MTC_QUARTER_FRAME 0xF1 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */ +#define MIDIv1_SONG_POSITION_PTR 0xF2 /* 2 bytes data -- CIN_3BYTE_SYS_COMMON */ +#define MIDIv1_SONG_SELECT 0xF3 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */ +#define MIDIv1_TUNE_REQUEST 0xF6 /* no data -- CIN_1BYTE */ + +/* + * Realtime category messages, can be sent anytime + */ +#define MIDIv1_CLOCK 0xF8 /* no data -- CIN_1BYTE */ +#define MIDIv1_TICK 0xF9 /* no data -- CIN_1BYTE */ +#define MIDIv1_START 0xFA /* no data -- CIN_1BYTE */ +#define MIDIv1_CONTINUE 0xFB /* no data -- CIN_1BYTE */ +#define MIDIv1_STOP 0xFC /* no data -- CIN_1BYTE */ +#define MIDIv1_ACTIVE_SENSE 0xFE /* no data -- CIN_1BYTE */ +#define MIDIv1_RESET 0xFF /* no data -- CIN_1BYTE */ + +/* + * sysex universal id's + */ +#define MIDIv1_UNIVERSAL_REALTIME_ID 0x7F +#define MIDIv1_UNIVERSAL_NON_REALTIME_ID 0x7E +#define MIDIv1_UNIVERSAL_ALL_CHANNELS 0x7F +/* + * Susbset of universal sysex (general info request) + * As described http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec.htm + */ +#define USYSEX_NON_REAL_TIME 0x7E +#define USYSEX_REAL_TIME 0x7F +#define USYSEX_ALL_CHANNELS 0x7F +#define USYSEX_GENERAL_INFO 0x06 +#define USYSEX_GI_ID_REQUEST 0x01 +#define USYSEX_GI_ID_RESPONSE 0x02 + + +#endif + + diff --git a/libmaple/include/libmaple/usb_midi_device.h b/libmaple/include/libmaple/usb_midi_device.h index 3d48bd7..652921b 100644 --- a/libmaple/include/libmaple/usb_midi_device.h +++ b/libmaple/include/libmaple/usb_midi_device.h @@ -36,6 +36,7 @@ #define _LIBMAPLE_USB_MIDI_DEVICE_H_ #include +#include #include #include @@ -44,8 +45,9 @@ extern "C" { #endif typedef union { - uint8 byte[4]; - uint32 data; + uint8 byte[4]; + uint32 data; + MIDI_EVENT_PACKET_t packet; } USB_MIDI_Event_Packet; /* @@ -88,10 +90,10 @@ void usb_midi_enable(gpio_dev*, uint8); void usb_midi_disable(gpio_dev*, uint8); void usb_midi_putc(char ch); -uint32 usb_midi_tx(const uint8* buf, uint32 len); -uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len); -uint32 usb_midi_rx(uint8* buf, uint32 len); -uint32 usb_midi_peek(uint8* buf, uint32 len); +uint32 usb_midi_tx(const uint32* buf, uint32 len); +uint32 usb_midi_tx_buffered(const uint32* buf, uint32 len); +uint32 usb_midi_rx(uint32* buf, uint32 len); +uint32 usb_midi_peek(uint32* buf, uint32 len); uint32 usb_midi_data_available(void); /* in RX buffer */ uint16 usb_midi_get_pending(void); diff --git a/libmaple/usb/stm32f1/usb_midi_device.c b/libmaple/usb/stm32f1/usb_midi_device.c index 9d46cc2..bd2855e 100644 --- a/libmaple/usb/stm32f1/usb_midi_device.c +++ b/libmaple/usb/stm32f1/usb_midi_device.c @@ -34,10 +34,10 @@ * the result made cleaner. */ -#define USETXBUFFER +//#define USETXBUFFER #include - +#include #include #include #include @@ -90,20 +90,19 @@ static void usbSetDeviceAddress(void); /* I/O state */ /* Received data */ -static volatile uint8 midiBufferRx[USB_MIDI_RX_EPSIZE]; +static volatile uint32 midiBufferRx[USB_MIDI_RX_EPSIZE/4]; /* Read index into midiBufferRx */ static volatile uint32 rx_offset = 0; /* Transmit data */ -static volatile uint8 midiBufferTx[USB_MIDI_TX_EPSIZE]; +static volatile uint32 midiBufferTx[USB_MIDI_TX_EPSIZE/4]; /* Write index into midiBufferTx */ static volatile uint32 tx_offset = 0; /* Number of bytes left to transmit */ -static volatile uint32 n_unsent_bytes = 0; +static volatile uint32 n_unsent_packets = 0; /* Are we currently sending an IN packet? */ static volatile uint8 transmitting = 0; /* Number of unread bytes */ -static volatile uint32 n_unread_bytes = 0; - +static volatile uint32 n_unread_packets = 0; /* * Endpoint callbacks */ @@ -193,20 +192,17 @@ void usb_midi_disable(gpio_dev *disc_dev, uint8 disc_bit) { gpio_write_bit(disc_dev, disc_bit, 1); } -void usb_midi_putc(char ch) { - while (!usb_midi_tx_buffered((uint8*)&ch, 1)) - ; -/* - while (!usb_midi_tx((uint8*)&ch, 1)) - ; -*/ -} +//void usb_midi_putc(char ch) { +// while (!usb_midi_tx((uint8*)&ch, 1)) +// ; +//} /* This function is non-blocking. * * It copies data from a usercode buffer into the USB peripheral TX * buffer, and returns the number of bytes copied. */ -uint32 usb_midi_tx(const uint8* buf, uint32 len) { +uint32 usb_midi_tx(const uint32* buf, uint32 packets) { + uint32 bytes=packets*4; /* Last transmission hasn't finished, so abort. */ if (usb_midi_is_transmitting()) { /* Copy to TxBuffer */ @@ -215,23 +211,24 @@ uint32 usb_midi_tx(const uint8* buf, uint32 len) { } /* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */ - if (len > USB_MIDI_TX_EPSIZE) { - len = USB_MIDI_TX_EPSIZE; + if (bytes > USB_MIDI_TX_EPSIZE) { + bytes = USB_MIDI_TX_EPSIZE; + packets=bytes/4; } /* Queue bytes for sending. */ - if (len) { - usb_copy_to_pma(buf, len, USB_MIDI_TX_ADDR); + if (packets) { + usb_copy_to_pma((uint8 *)buf, bytes, USB_MIDI_TX_ADDR); } // We still need to wait for the interrupt, even if we're sending // zero bytes. (Sending zero-size packets is useful for flushing // host-side buffers.) - usb_set_ep_tx_count(USB_MIDI_TX_ENDP, len); - n_unsent_bytes = len; + usb_set_ep_tx_count(USB_MIDI_TX_ENDP, bytes); + n_unsent_packets = packets; transmitting = 1; usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID); - - return len; + + return packets; } /* @@ -243,7 +240,7 @@ uint32 usb_midi_tx(const uint8* buf, uint32 len) { * TODO: Use the modules EP double buffering instead * * */ -uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len) { +uint32 usb_midi_tx_buffered(const uint32* buf, uint32 len) { if (usb_midi_is_transmitting()) { uint8 count; /* Copy to TxBuffer */ @@ -267,13 +264,13 @@ uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len) { /* Queue bytes for sending. */ if (len) { - usb_copy_to_pma(buf, len, USB_MIDI_TX_ADDR); + usb_copy_to_pma((uint8 *)buf, 4*len, USB_MIDI_TX_ADDR); } // We still need to wait for the interrupt, even if we're sending // zero bytes. (Sending zero-size packets is useful for flushing // host-side buffers.) usb_set_ep_tx_count(USB_MIDI_TX_ENDP, len); - n_unsent_bytes = len; + n_unsent_packets = len; transmitting = 1; usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID); @@ -304,7 +301,7 @@ uint32 usb_midi_tx_send_buffer() { #endif uint32 usb_midi_data_available(void) { - return n_unread_bytes; + return n_unread_packets; } uint8 usb_midi_is_transmitting(void) { @@ -312,24 +309,24 @@ uint8 usb_midi_is_transmitting(void) { } uint16 usb_midi_get_pending(void) { - return n_unsent_bytes; + return n_unsent_packets; } /* Nonblocking byte receive. * * Copies up to len bytes from our private data buffer (*NOT* the PMA) * into buf and deq's the FIFO. */ -uint32 usb_midi_rx(uint8* buf, uint32 len) { +uint32 usb_midi_rx(uint32* buf, uint32 packets) { /* Copy bytes to buffer. */ - uint32 n_copied = usb_midi_peek(buf, len); - + uint32 n_copied = usb_midi_peek(buf, packets); + /* Mark bytes as read. */ - n_unread_bytes -= n_copied; + n_unread_packets -= n_copied; rx_offset += n_copied; /* If all bytes have been read, re-enable the RX endpoint, which * was set to NAK when the current batch of bytes was received. */ - if (n_unread_bytes == 0) { + if (n_unread_packets == 0) { usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE); usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID); rx_offset = 0; @@ -341,18 +338,17 @@ uint32 usb_midi_rx(uint8* buf, uint32 len) { /* Nonblocking byte lookahead. * * Looks at unread bytes without marking them as read. */ -uint32 usb_midi_peek(uint8* buf, uint32 len) { +uint32 usb_midi_peek(uint32* buf, uint32 packets) { int i; - - if (len > n_unread_bytes) { - len = n_unread_bytes; + if (packets > n_unread_packets) { + packets = n_unread_packets; } - - for (i = 0; i < len; i++) { + + for (i = 0; i < packets; i++) { buf[i] = midiBufferRx[i + rx_offset]; } - - return len; + + return packets; } /* @@ -360,7 +356,7 @@ uint32 usb_midi_peek(uint8* buf, uint32 len) { */ static void midiDataTxCb(void) { - n_unsent_bytes = 0; + n_unsent_packets = 0; transmitting = 0; #ifdef USETXBUFFER usb_midi_tx_send_buffer(); @@ -368,21 +364,23 @@ static void midiDataTxCb(void) { } static void midiDataRxCb(void) { + usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_NAK); - n_unread_bytes = usb_get_ep_rx_count(USB_MIDI_RX_ENDP); + n_unread_packets = usb_get_ep_rx_count(USB_MIDI_RX_ENDP) / 4; /* This copy won't overwrite unread bytes, since we've set the RX * endpoint to NAK, and will only set it to VALID when all bytes * have been read. */ - usb_copy_from_pma((uint8*)midiBufferRx, n_unread_bytes, + + usb_copy_from_pma((uint8*)midiBufferRx, n_unread_packets * 4, USB_MIDI_RX_ADDR); - if (n_unread_bytes == 0) { + if (n_unread_packets == 0) { usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE); usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID); rx_offset = 0; } - + } /* NOTE: Nothing specific to this device class in this function, but depenedent on the device, move to usb_lib or stm32fxx*/ @@ -443,8 +441,8 @@ static void usbReset(void) { SetDeviceAddress(0); /* Reset the RX/TX state */ - n_unread_bytes = 0; - n_unsent_bytes = 0; + n_unread_packets = 0; + n_unsent_packets = 0; rx_offset = 0; tx_offset = 0; transmitting = 0; diff --git a/wirish/include/wirish/usb_midi.h b/wirish/include/wirish/usb_midi.h index 0e2d4f0..eca197c 100644 --- a/wirish/include/wirish/usb_midi.h +++ b/wirish/include/wirish/usb_midi.h @@ -42,7 +42,7 @@ /** * @brief Virtual serial terminal. */ -class USBMidi : public Print { +class USBMidi { public: USBMidi(void); @@ -50,14 +50,13 @@ public: void end(void); uint32 available(void); - - uint32 read(void *buf, uint32 len); - uint8 read(void); - - void write(uint8); - void write(const char *str); - void write(const void*, uint32); - + + uint32 readPackets(void *buf, uint32 len); + uint32 readPacket(void); + + void writePacket(uint32); + void writePackets(const void*, uint32); + uint8 isConnected(); uint8 pending(); }; diff --git a/wirish/usb_midi.cpp b/wirish/usb_midi.cpp index 5a48efb..5b0fc97 100644 --- a/wirish/usb_midi.cpp +++ b/wirish/usb_midi.cpp @@ -64,15 +64,11 @@ void USBMidi::end(void) { #endif } -void USBMidi::write(uint8 ch) { - this->write(&ch, 1); +void USBMidi::writePacket(uint32 p) { + this->writePackets(&p, 1); } -void USBMidi::write(const char *str) { - this->write(str, strlen(str)); -} - -void USBMidi::write(const void *buf, uint32 len) { +void USBMidi::writePackets(const void *buf, uint32 len) { if (!this->isConnected() || !buf) { return; } @@ -84,8 +80,7 @@ void USBMidi::write(const void *buf, uint32 len) { uint32 sent = 0; while (txed < len && (millis() - start < USB_TIMEOUT)) { -// sent = usb_midi_tx_buffered((const uint8*)buf + txed, len - txed); - sent = usb_midi_tx((const uint8*)buf + txed, len - txed); + sent = usb_midi_tx((const uint32*)buf + txed, len - txed); txed += sent; if (old_txed != txed) { start = millis(); @@ -106,24 +101,24 @@ uint32 USBMidi::available(void) { return usb_midi_data_available(); } -uint32 USBMidi::read(void *buf, uint32 len) { +uint32 USBMidi::readPackets(void *buf, uint32 len) { if (!buf) { return 0; } uint32 rxed = 0; while (rxed < len) { - rxed += usb_midi_rx((uint8*)buf + rxed, len - rxed); + rxed += usb_midi_rx((uint32*)buf + rxed, len - rxed); } return rxed; } /* Blocks forever until 1 byte is received */ -uint8 USBMidi::read(void) { - uint8 b; - this->read(&b, 1); - return b; +uint32 USBMidi::readPacket(void) { + uint32 p; + this->readPackets(&p, 1); + return p; } uint8 USBMidi::pending(void) { -- cgit v1.2.3