Added Teensy usb core sources.
This commit is contained in:
		
							parent
							
								
									561d4bb015
								
							
						
					
					
						commit
						522dcdf095
					
				|  | @ -0,0 +1,4 @@ | |||
| #define CORE_TEENSY_HID | ||||
| #define CORE_TEENSY_MIDI | ||||
| #define CORE_TEENSY_KEYBOARD | ||||
| //#define CORE_TEENSY_JOYSTICK
 | ||||
|  | @ -0,0 +1,597 @@ | |||
| /* USB Serial Example for Teensy USB Development Board
 | ||||
|  * http://www.pjrc.com/teensy/usb_serial.html
 | ||||
|  * Copyright (c) 2008 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: | ||||
|  *  | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include "usb_common.h" | ||||
| #include "usb_private.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Endpoint Buffer Configuration | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| static const uint8_t PROGMEM endpoint_config_table[] = { | ||||
| 	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(DEBUG_TX_SIZE) | DEBUG_TX_BUFFER, | ||||
| 	1, EP_TYPE_INTERRUPT_OUT, EP_SIZE(DEBUG_RX_SIZE) | DEBUG_RX_BUFFER, | ||||
| 	1, EP_TYPE_BULK_IN,       EP_SIZE(MIDI_TX_SIZE) | MIDI_TX_BUFFER, | ||||
| 	1, EP_TYPE_BULK_OUT,      EP_SIZE(MIDI_RX_SIZE) | MIDI_RX_BUFFER | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Descriptor Data | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| // Descriptors are the data that your computer reads when it auto-detects
 | ||||
| // this USB device (called "enumeration" in USB lingo).  The most commonly
 | ||||
| // changed items are editable at the top of this file.  Changing things
 | ||||
| // in here should only be done by those who've read chapter 9 of the USB
 | ||||
| // spec and relevant portions of any USB class specifications!
 | ||||
| 
 | ||||
| static uint8_t PROGMEM device_descriptor[] = { | ||||
| 	18,					// bLength
 | ||||
| 	1,					// bDescriptorType
 | ||||
| 	0x00, 0x02,				// bcdUSB
 | ||||
| 	0,					// bDeviceClass
 | ||||
| 	0,					// bDeviceSubClass
 | ||||
| 	0,					// bDeviceProtocol
 | ||||
| 	ENDPOINT0_SIZE,				// bMaxPacketSize0
 | ||||
| 	LSB(VENDOR_ID), MSB(VENDOR_ID),		// idVendor
 | ||||
| 	LSB(PRODUCT_ID), MSB(PRODUCT_ID),	// idProduct
 | ||||
| 	0x00, 0x01,				// bcdDevice
 | ||||
| 	0,					// iManufacturer
 | ||||
| 	1,					// iProduct
 | ||||
| 	0,					// iSerialNumber
 | ||||
| 	1					// bNumConfigurations
 | ||||
| }; | ||||
| 
 | ||||
| static uint8_t PROGMEM debug_hid_report_desc[] = { | ||||
|         0x06, 0xC9, 0xFF,                       // Usage Page 0xFFC9 (vendor defined)
 | ||||
|         0x09, 0x04,                             // Usage 0x04
 | ||||
|         0xA1, 0x5C,                             // Collection 0x5C
 | ||||
|         0x75, 0x08,                             // report size = 8 bits (global)
 | ||||
|         0x15, 0x00,                             // logical minimum = 0 (global)
 | ||||
|         0x26, 0xFF, 0x00,                       // logical maximum = 255 (global)
 | ||||
|         0x95, DEBUG_TX_SIZE,                    // report count (global)
 | ||||
|         0x09, 0x75,                             // usage (local)
 | ||||
|         0x81, 0x02,                             // Input
 | ||||
|         0x95, DEBUG_RX_SIZE,                    // report count (global)
 | ||||
|         0x09, 0x76,                             // usage (local)
 | ||||
|         0x91, 0x02,                             // Output
 | ||||
|         0x95, 0x04,                             // report count (global)
 | ||||
|         0x09, 0x76,                             // usage (local)
 | ||||
|         0xB1, 0x02,                             // Feature
 | ||||
|         0xC0                                    // end collection
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #define CONFIG1_DESC_SIZE		( 9 + 74 + 32 ) | ||||
| #define DEBUG_HID_DESC_OFFSET		( 9 + 74 + 9 ) | ||||
| 
 | ||||
| static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = { | ||||
| 	// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
 | ||||
| 	9, 					// bLength;
 | ||||
| 	2,					// bDescriptorType;
 | ||||
| 	LSB(CONFIG1_DESC_SIZE),			// wTotalLength
 | ||||
| 	MSB(CONFIG1_DESC_SIZE), | ||||
| 	NUM_INTERFACE,				// bNumInterfaces
 | ||||
| 	1,					// bConfigurationValue
 | ||||
| 	0,					// iConfiguration
 | ||||
| 	0xC0,					// bmAttributes
 | ||||
| 	50,					// bMaxPower
 | ||||
| 
 | ||||
| 	// This MIDI stuff is a copy of the example from the Audio Class
 | ||||
| 	// MIDI spec 1.0 (Nov 1, 1999), Appendix B, pages 37 to 43.
 | ||||
| 
 | ||||
|         // Standard MS Interface Descriptor,
 | ||||
|         9,                                      // bLength
 | ||||
|         4,                                      // bDescriptorType
 | ||||
|         MIDI_INTERFACE,                         // bInterfaceNumber
 | ||||
|         0,                                      // bAlternateSetting
 | ||||
|         2,                                      // bNumEndpoints
 | ||||
|         0x01,                                   // bInterfaceClass (0x01 = Audio)
 | ||||
|         0x03,                                   // bInterfaceSubClass (0x03 = MIDI)
 | ||||
|         0x00,                                   // bInterfaceProtocol (unused for MIDI)
 | ||||
|         0,                                      // iInterface
 | ||||
| 
 | ||||
| 	// MIDI MS Interface Header, USB MIDI 6.1.2.1, page 21, Table 6-2
 | ||||
| 	7,					// bLength
 | ||||
| 	0x24,					// bDescriptorType = CS_INTERFACE
 | ||||
| 	0x01,					// bDescriptorSubtype = MS_HEADER 
 | ||||
| 	0x00, 0x01,				// bcdMSC = revision 01.00
 | ||||
| 	0x41, 0x00,				// wTotalLength
 | ||||
| 
 | ||||
| 	// MIDI IN Jack Descriptor, B.4.3, Table B-7 (embedded), page 40
 | ||||
| 	6,					// bLength
 | ||||
| 	0x24,					// bDescriptorType = CS_INTERFACE
 | ||||
| 	0x02,					// bDescriptorSubtype = MIDI_IN_JACK
 | ||||
| 	0x01,					// bJackType = EMBEDDED
 | ||||
| 	1,					// bJackID, ID = 1
 | ||||
| 	0,					// iJack
 | ||||
| 
 | ||||
| 	// MIDI IN Jack Descriptor, B.4.3, Table B-8 (external), page 40
 | ||||
| 	6,					// bLength
 | ||||
| 	0x24,					// bDescriptorType = CS_INTERFACE
 | ||||
| 	0x02,					// bDescriptorSubtype = MIDI_IN_JACK
 | ||||
| 	0x02,					// bJackType = EXTERNAL
 | ||||
| 	2,					// bJackID, ID = 2
 | ||||
| 	0,					// iJack
 | ||||
| 
 | ||||
| 	// MIDI OUT Jack Descriptor, B.4.4, Table B-9, page 41
 | ||||
| 	9, | ||||
| 	0x24,					// bDescriptorType = CS_INTERFACE
 | ||||
| 	0x03,					// bDescriptorSubtype = MIDI_OUT_JACK
 | ||||
| 	0x01,					// bJackType = EMBEDDED
 | ||||
| 	3,					// bJackID, ID = 3
 | ||||
| 	1,					// bNrInputPins = 1 pin
 | ||||
| 	2,					// BaSourceID(1) = 2
 | ||||
| 	1,					// BaSourcePin(1) = first pin
 | ||||
| 	0,					// iJack
 | ||||
| 
 | ||||
| 	// MIDI OUT Jack Descriptor, B.4.4, Table B-10, page 41
 | ||||
| 	9, | ||||
| 	0x24,					// bDescriptorType = CS_INTERFACE
 | ||||
| 	0x03,					// bDescriptorSubtype = MIDI_OUT_JACK
 | ||||
| 	0x02,					// bJackType = EXTERNAL
 | ||||
| 	4,					// bJackID, ID = 4
 | ||||
| 	1,					// bNrInputPins = 1 pin
 | ||||
| 	1,					// BaSourceID(1) = 1
 | ||||
| 	1,					// BaSourcePin(1) = first pin
 | ||||
| 	0,					// iJack
 | ||||
| 
 | ||||
|         // Standard Bulk OUT Endpoint Descriptor, B.5.1, Table B-11, pae 42
 | ||||
|         9,                                      // bLength
 | ||||
|         5,                                      // bDescriptorType = ENDPOINT 
 | ||||
|         MIDI_RX_ENDPOINT,                       // bEndpointAddress
 | ||||
|         0x02,                                   // bmAttributes (0x02=bulk)
 | ||||
|         MIDI_RX_SIZE, 0,                        // wMaxPacketSize
 | ||||
|         0,                                      // bInterval
 | ||||
| 	0,					// bRefresh
 | ||||
| 	0,					// bSynchAddress
 | ||||
| 
 | ||||
| 	// Class-specific MS Bulk OUT Endpoint Descriptor, B.5.2, Table B-12, page 42
 | ||||
| 	5,					// bLength
 | ||||
| 	0x25,					// bDescriptorSubtype = CS_ENDPOINT
 | ||||
| 	0x01,					// bJackType = MS_GENERAL
 | ||||
| 	1,					// bNumEmbMIDIJack = 1 jack
 | ||||
| 	1,					// BaAssocJackID(1) = jack ID #1
 | ||||
| 
 | ||||
|         // Standard Bulk IN Endpoint Descriptor, B.5.1, Table B-11, pae 42
 | ||||
|         9,                                      // bLength
 | ||||
|         5,                                      // bDescriptorType = ENDPOINT 
 | ||||
|         MIDI_TX_ENDPOINT | 0x80,                // bEndpointAddress
 | ||||
|         0x02,                                   // bmAttributes (0x02=bulk)
 | ||||
|         MIDI_TX_SIZE, 0,                        // wMaxPacketSize
 | ||||
|         0,                                      // bInterval
 | ||||
| 	0,					// bRefresh
 | ||||
| 	0,					// bSynchAddress
 | ||||
| 
 | ||||
| 	// Class-specific MS Bulk IN Endpoint Descriptor, B.5.2, Table B-12, page 42
 | ||||
| 	5,					// bLength
 | ||||
| 	0x25,					// bDescriptorSubtype = CS_ENDPOINT
 | ||||
| 	0x01,					// bJackType = MS_GENERAL
 | ||||
| 	1,					// bNumEmbMIDIJack = 1 jack
 | ||||
| 	3,					// BaAssocJackID(1) = jack ID #3
 | ||||
| 
 | ||||
| 
 | ||||
|         // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
 | ||||
|         9,                                      // bLength
 | ||||
|         4,                                      // bDescriptorType
 | ||||
|         DEBUG_INTERFACE,                        // bInterfaceNumber
 | ||||
|         0,                                      // bAlternateSetting
 | ||||
|         2,                                      // bNumEndpoints
 | ||||
|         0x03,                                   // bInterfaceClass (0x03 = HID)
 | ||||
|         0x00,                                   // bInterfaceSubClass
 | ||||
|         0x00,                                   // bInterfaceProtocol
 | ||||
|         0,                                      // iInterface
 | ||||
|         // HID interface descriptor, HID 1.11 spec, section 6.2.1
 | ||||
|         9,                                      // bLength
 | ||||
|         0x21,                                   // bDescriptorType
 | ||||
|         0x11, 0x01,                             // bcdHID
 | ||||
|         0,                                      // bCountryCode
 | ||||
|         1,                                      // bNumDescriptors
 | ||||
|         0x22,                                   // bDescriptorType
 | ||||
|         sizeof(debug_hid_report_desc),          // wDescriptorLength
 | ||||
|         0, | ||||
|         // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
 | ||||
|         7,                                      // bLength
 | ||||
|         5,                                      // bDescriptorType
 | ||||
|         DEBUG_TX_ENDPOINT | 0x80,               // bEndpointAddress
 | ||||
|         0x03,                                   // bmAttributes (0x03=intr)
 | ||||
|         DEBUG_TX_SIZE, 0,                       // wMaxPacketSize
 | ||||
|         DEBUG_TX_INTERVAL,                      // bInterval
 | ||||
|         // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
 | ||||
|         7,                                      // bLength
 | ||||
|         5,                                      // bDescriptorType
 | ||||
|         DEBUG_RX_ENDPOINT,                      // bEndpointAddress
 | ||||
|         0x03,                                   // bmAttributes (0x03=intr)
 | ||||
|         DEBUG_RX_SIZE, 0,                       // wMaxPacketSize
 | ||||
|         DEBUG_RX_INTERVAL,                      // bInterval
 | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| // If you're desperate for a little extra code memory, these strings
 | ||||
| // can be completely removed if iManufacturer, iProduct, iSerialNumber
 | ||||
| // in the device desciptor are changed to zeros.
 | ||||
| struct usb_string_descriptor_struct { | ||||
| 	uint8_t bLength; | ||||
| 	uint8_t bDescriptorType; | ||||
| 	int16_t wString[]; | ||||
| }; | ||||
| static struct usb_string_descriptor_struct PROGMEM string0 = { | ||||
| 	4, | ||||
| 	3, | ||||
| 	{0x0409} | ||||
| }; | ||||
| static struct usb_string_descriptor_struct PROGMEM string1 = { | ||||
| 	sizeof(STR_PRODUCT), | ||||
| 	3, | ||||
| 	STR_PRODUCT | ||||
| }; | ||||
| 
 | ||||
| // This table defines which descriptor data is sent for each specific
 | ||||
| // request from the host (in wValue and wIndex).
 | ||||
| static struct descriptor_list_struct { | ||||
| 	uint16_t	wValue; | ||||
| 	uint16_t	wIndex; | ||||
| 	const uint8_t	*addr; | ||||
| 	uint8_t		length; | ||||
| } PROGMEM descriptor_list[] = { | ||||
| 	{0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)}, | ||||
| 	{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)}, | ||||
|         {0x2200, DEBUG_INTERFACE, debug_hid_report_desc, sizeof(debug_hid_report_desc)}, | ||||
|         {0x2100, DEBUG_INTERFACE, config1_descriptor+DEBUG_HID_DESC_OFFSET, 9}, | ||||
| 	{0x0300, 0x0000, (const uint8_t *)&string0, 4}, | ||||
| 	{0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_PRODUCT)}, | ||||
| }; | ||||
| #define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct)) | ||||
| 
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Variables - these are the only non-stack RAM usage | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| // zero when we are not configured, non-zero when enumerated
 | ||||
| volatile uint8_t usb_configuration USBSTATE; | ||||
| volatile uint8_t usb_suspended USBSTATE; | ||||
| 
 | ||||
| // the time remaining before we transmit any partially full
 | ||||
| // packet, or send a zero length packet.
 | ||||
| volatile uint8_t debug_flush_timer USBSTATE; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Public Functions - these are the API intended for the user | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // initialize USB serial
 | ||||
| void usb_init(void) | ||||
| { | ||||
| 	uint8_t u; | ||||
| 
 | ||||
| 	u = USBCON; | ||||
| 	if ((u & (1<<USBE)) && !(u & (1<<FRZCLK))) return; | ||||
| 	HW_CONFIG(); | ||||
|         USB_FREEZE();				// enable USB
 | ||||
|         PLL_CONFIG();				// config PLL
 | ||||
|         while (!(PLLCSR & (1<<PLOCK))) ;	// wait for PLL lock
 | ||||
|         USB_CONFIG();				// start USB clock
 | ||||
|         UDCON = 0;				// enable attach resistor
 | ||||
| 	usb_configuration = 0; | ||||
| 	usb_suspended = 0; | ||||
| 	debug_flush_timer = 0; | ||||
| 	UDINT = 0; | ||||
|         UDIEN = (1<<EORSTE)|(1<<SOFE); | ||||
| 	//sei();  // init() in wiring.c does this
 | ||||
| } | ||||
| 
 | ||||
| void usb_shutdown(void) | ||||
| { | ||||
| 	UDIEN = 0;		// disable interrupts
 | ||||
| 	UDCON = 1;		// disconnect attach resistor
 | ||||
| 	USBCON = 0;		// shut off USB periperal
 | ||||
| 	PLLCSR = 0;		// shut off PLL
 | ||||
| 	usb_configuration = 0; | ||||
| 	usb_suspended = 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Public API functions moved to usb_api.cpp
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Private Functions - not intended for general user consumption.... | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // USB Device Interrupt - handle all device-level events
 | ||||
| // the transmit buffer flushing is triggered by the start of frame
 | ||||
| //
 | ||||
| ISR(USB_GEN_vect) | ||||
| { | ||||
| 	uint8_t intbits, t, i; | ||||
| 	static uint8_t div4=0; | ||||
| 
 | ||||
|         intbits = UDINT; | ||||
|         UDINT = 0; | ||||
|         if (intbits & (1<<EORSTI)) { | ||||
| 		UENUM = 0; | ||||
| 		UECONX = 1; | ||||
| 		UECFG0X = EP_TYPE_CONTROL; | ||||
| 		UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER; | ||||
| 		UEIENX = (1<<RXSTPE); | ||||
| 		usb_configuration = 0; | ||||
|         } | ||||
|         if ((intbits & (1<<SOFI)) && usb_configuration) { | ||||
|                 t = debug_flush_timer; | ||||
|                 if (t) { | ||||
|                         debug_flush_timer = --t; | ||||
|                         if (!t) { | ||||
|                                 UENUM = DEBUG_TX_ENDPOINT; | ||||
|                                 while ((UEINTX & (1<<RWAL))) { | ||||
|                                         UEDATX = 0; | ||||
|                                 } | ||||
|                                 UEINTX = 0x3A; | ||||
|                         } | ||||
|                 } | ||||
|                 UENUM = MIDI_TX_ENDPOINT; | ||||
| 		if (UEBCLX) UEINTX = 0x3A; | ||||
|         } | ||||
| 	if (intbits & (1<<SUSPI)) { | ||||
| 		// USB Suspend (inactivity for 3ms)
 | ||||
| 		UDIEN = (1<<WAKEUPE); | ||||
| 		usb_configuration = 0; | ||||
| 		usb_suspended = 1; | ||||
| 		#if (F_CPU >= 8000000L) | ||||
| 		// WAKEUPI does not work with USB clock freeze 
 | ||||
| 		// when CPU is running less than 8 MHz.
 | ||||
| 		// Is this a hardware bug?
 | ||||
| 		USB_FREEZE();			// shut off USB
 | ||||
| 		PLLCSR = 0;			// shut off PLL
 | ||||
| 		#endif | ||||
| 		// to properly meet the USB spec, current must
 | ||||
| 		// reduce to less than 2.5 mA, which means using
 | ||||
| 		// powerdown mode, but that breaks the Arduino
 | ||||
| 		// user's paradigm....
 | ||||
| 	} | ||||
| 	if (usb_suspended && (intbits & (1<<WAKEUPI))) { | ||||
| 		// USB Resume (pretty much any activity)
 | ||||
| 		#if (F_CPU >= 8000000L) | ||||
| 		PLL_CONFIG(); | ||||
| 		while (!(PLLCSR & (1<<PLOCK))) ; | ||||
| 		USB_CONFIG(); | ||||
| 		#endif | ||||
| 		UDIEN = (1<<EORSTE)|(1<<SOFE)|(1<<SUSPE); | ||||
| 		usb_suspended = 0; | ||||
| 		return; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Misc functions to wait for ready and send/receive packets
 | ||||
| static inline void usb_wait_in_ready(void) | ||||
| { | ||||
| 	while (!(UEINTX & (1<<TXINI))) ; | ||||
| } | ||||
| static inline void usb_send_in(void) | ||||
| { | ||||
| 	UEINTX = ~(1<<TXINI); | ||||
| } | ||||
| static inline void usb_wait_receive_out(void) | ||||
| { | ||||
| 	while (!(UEINTX & (1<<RXOUTI))) ; | ||||
| } | ||||
| static inline void usb_ack_out(void) | ||||
| { | ||||
| 	UEINTX = ~(1<<RXOUTI); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // USB Endpoint Interrupt - endpoint 0 is handled here.  The
 | ||||
| // other endpoints are manipulated by the user-callable
 | ||||
| // functions, and the start-of-frame interrupt.
 | ||||
| //
 | ||||
| ISR(USB_COM_vect) | ||||
| { | ||||
|         uint8_t intbits; | ||||
| 	const uint8_t *list; | ||||
|         const uint8_t *cfg; | ||||
| 	uint8_t i, n, len, en; | ||||
| 	uint8_t *p; | ||||
| 	uint8_t bmRequestType; | ||||
| 	uint8_t bRequest; | ||||
| 	uint16_t wValue; | ||||
| 	uint16_t wIndex; | ||||
| 	uint16_t wLength; | ||||
| 	uint16_t desc_val; | ||||
| 	const uint8_t *desc_addr; | ||||
| 	uint8_t	desc_length; | ||||
| 
 | ||||
| 	UENUM = 0; | ||||
| 	intbits = UEINTX; | ||||
| 	if (intbits & (1<<RXSTPI)) { | ||||
| 		bmRequestType = UEDATX; | ||||
| 		bRequest = UEDATX; | ||||
| 		read_word_lsbfirst(wValue, UEDATX); | ||||
| 		read_word_lsbfirst(wIndex, UEDATX); | ||||
| 		read_word_lsbfirst(wLength, UEDATX); | ||||
| 		UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI)); | ||||
| 		if (bRequest == GET_DESCRIPTOR) { | ||||
| 			list = (const uint8_t *)descriptor_list; | ||||
| 			for (i=0; ; i++) { | ||||
| 				if (i >= NUM_DESC_LIST) { | ||||
| 					UECONX = (1<<STALLRQ)|(1<<EPEN);  //stall
 | ||||
| 					return; | ||||
| 				} | ||||
| 				pgm_read_word_postinc(desc_val, list); | ||||
| 				if (desc_val != wValue) { | ||||
| 					list += sizeof(struct descriptor_list_struct)-2; | ||||
| 					continue; | ||||
| 				} | ||||
| 				pgm_read_word_postinc(desc_val, list); | ||||
| 				if (desc_val != wIndex) { | ||||
| 					list += sizeof(struct descriptor_list_struct)-4; | ||||
| 					continue; | ||||
| 				} | ||||
| 				pgm_read_word_postinc(desc_addr, list); | ||||
| 				desc_length = pgm_read_byte(list); | ||||
| 				break; | ||||
| 			} | ||||
| 			len = (wLength < 256) ? wLength : 255; | ||||
| 			if (len > desc_length) len = desc_length; | ||||
| 			list = desc_addr; | ||||
| 			do { | ||||
| 				// wait for host ready for IN packet
 | ||||
| 				do { | ||||
| 					i = UEINTX; | ||||
| 				} while (!(i & ((1<<TXINI)|(1<<RXOUTI)))); | ||||
| 				if (i & (1<<RXOUTI)) return;	// abort
 | ||||
| 				// send IN packet
 | ||||
| 				n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE; | ||||
| 				for (i = n; i; i--) { | ||||
| 					pgm_read_byte_postinc(UEDATX, list); | ||||
| 				} | ||||
| 				len -= n; | ||||
| 				usb_send_in(); | ||||
| 			} while (len || n == ENDPOINT0_SIZE); | ||||
| 			return; | ||||
|                 } | ||||
| 		if (bRequest == SET_ADDRESS) { | ||||
| 			usb_send_in(); | ||||
| 			usb_wait_in_ready(); | ||||
| 			UDADDR = wValue | (1<<ADDEN); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (bRequest == SET_CONFIGURATION && bmRequestType == 0) { | ||||
| 			usb_configuration = wValue; | ||||
| 			debug_flush_timer = 0; | ||||
| 			usb_send_in(); | ||||
| 			cfg = endpoint_config_table; | ||||
| 			for (i=1; i<NUM_ENDPOINTS; i++) { | ||||
| 				UENUM = i; | ||||
| 				pgm_read_byte_postinc(en, cfg); | ||||
| 				UECONX = en; | ||||
| 				if (en) { | ||||
| 					pgm_read_byte_postinc(UECFG0X, cfg); | ||||
| 					pgm_read_byte_postinc(UECFG1X, cfg); | ||||
| 				} | ||||
| 			} | ||||
|         		UERST = 0x1E; | ||||
|         		UERST = 0; | ||||
| 			return; | ||||
| 		} | ||||
| 		if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80) { | ||||
| 			usb_wait_in_ready(); | ||||
| 			UEDATX = usb_configuration; | ||||
| 			usb_send_in(); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (bRequest == GET_STATUS) { | ||||
| 			usb_wait_in_ready(); | ||||
| 			i = 0; | ||||
| 			if (bmRequestType == 0x82) { | ||||
| 				UENUM = wIndex; | ||||
| 				if (UECONX & (1<<STALLRQ)) i = 1; | ||||
| 				UENUM = 0; | ||||
| 			} | ||||
| 			UEDATX = i; | ||||
| 			UEDATX = 0; | ||||
| 			usb_send_in(); | ||||
| 			return; | ||||
| 		} | ||||
| 		if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE) | ||||
| 		  && bmRequestType == 0x02 && wValue == 0) { | ||||
| 			i = wIndex & 0x7F; | ||||
| 			if (i >= 1 && i <= MAX_ENDPOINT) { | ||||
| 				usb_send_in(); | ||||
| 				UENUM = i; | ||||
| 				if (bRequest == SET_FEATURE) { | ||||
| 					UECONX = (1<<STALLRQ)|(1<<EPEN); | ||||
| 				} else { | ||||
| 					UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN); | ||||
| 					UERST = (1 << i); | ||||
| 					UERST = 0; | ||||
| 				} | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
|                 if (wIndex == DEBUG_INTERFACE) { | ||||
|                         if (bRequest == HID_GET_REPORT && bmRequestType == 0xA1) { | ||||
|                                 len = wLength; | ||||
|                                 do { | ||||
|                                         // wait for host ready for IN packet
 | ||||
|                                         do { | ||||
|                                                 i = UEINTX; | ||||
|                                         } while (!(i & ((1<<TXINI)|(1<<RXOUTI)))); | ||||
|                                         if (i & (1<<RXOUTI)) return;    // abort
 | ||||
|                                         // send IN packet
 | ||||
|                                         n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE; | ||||
|                                         for (i = n; i; i--) { | ||||
|                                                 UEDATX = 0; | ||||
|                                         } | ||||
|                                         len -= n; | ||||
|                                         usb_send_in(); | ||||
|                                 } while (len || n == ENDPOINT0_SIZE); | ||||
|                                 return; | ||||
|                         } | ||||
|                         if (bRequest == HID_SET_REPORT && bmRequestType == 0x21) { | ||||
| 				if (wValue == 0x0300 && wLength == 0x0004) { | ||||
| 					uint8_t b1, b2, b3, b4; | ||||
|                                         usb_wait_receive_out(); | ||||
| 					b1 = UEDATX; | ||||
| 					b2 = UEDATX; | ||||
| 					b3 = UEDATX; | ||||
| 					b4 = UEDATX; | ||||
|                                         usb_ack_out(); | ||||
|                                         usb_send_in(); | ||||
| 					if (b1 == 0xA9 && b2 == 0x45 && b3 == 0xC2 && b4 == 0x6B) | ||||
| 						_reboot_Teensyduino_(); | ||||
| 					if (b1 == 0x8B && b2 == 0xC5 && b3 == 0x1D && b4 == 0x70) | ||||
| 						_restart_Teensyduino_(); | ||||
| 				} | ||||
| 			} | ||||
|                 } | ||||
|         } | ||||
| 	UECONX = (1<<STALLRQ) | (1<<EPEN);	// stall
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,270 @@ | |||
| /* USB API for Teensy USB Development Board
 | ||||
|  * http://www.pjrc.com/teensy/teensyduino.html
 | ||||
|  * Copyright (c) 2008 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: | ||||
|  *  | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| #include <avr/io.h> | ||||
| #include <avr/pgmspace.h> | ||||
| #include <stdint.h> | ||||
| #include "usb_common.h" | ||||
| #include "usb_private.h" | ||||
| #include "usb_api.h" | ||||
| #include "wiring.h" | ||||
| 
 | ||||
| #include "usb_midi.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void usb_serial_class::begin(long speed) | ||||
| { | ||||
| 	// make sure USB is initialized
 | ||||
| 	usb_init(); | ||||
| 	uint16_t begin_wait = (uint16_t)millis(); | ||||
| 	while (1) { | ||||
| 		if (usb_configuration) { | ||||
| 			delay(200);  // a little time for host to load a driver
 | ||||
| 			return; | ||||
| 		} | ||||
| 		if (usb_suspended) { | ||||
| 			uint16_t begin_suspend = (uint16_t)millis(); | ||||
| 			while (usb_suspended) { | ||||
| 				// must remain suspended for a while, because
 | ||||
| 				// normal USB enumeration causes brief suspend
 | ||||
| 				// states, typically under 0.1 second
 | ||||
| 				if ((uint16_t)millis() - begin_suspend > 250) { | ||||
| 					return; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// ... or a timout (powered by a USB power adaptor that
 | ||||
| 		// wiggles the data lines to keep a USB device charging)
 | ||||
| 		if ((uint16_t)millis() - begin_wait > 2500) return; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void usb_serial_class::end(void) | ||||
| { | ||||
| 	usb_shutdown(); | ||||
| 	delay(25); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static volatile uint8_t prev_byte=0; | ||||
| 
 | ||||
| // number of bytes available in the receive buffer
 | ||||
| uint8_t usb_serial_class::available(void) | ||||
| { | ||||
|         uint8_t c; | ||||
| 
 | ||||
| 	c = prev_byte;  // assume 1 byte static volatile access is atomic
 | ||||
| 	if (c) return 1; | ||||
| 	c = readnext(); | ||||
| 	if (c) { | ||||
| 		prev_byte = c; | ||||
| 		return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| // get the next character, or -1 if nothing received
 | ||||
| int usb_serial_class::read(void) | ||||
| { | ||||
| 	uint8_t c; | ||||
| 
 | ||||
| 	c = prev_byte; | ||||
| 	if (c) { | ||||
| 		prev_byte = 0; | ||||
| 		return c; | ||||
| 	} | ||||
| 	c = readnext(); | ||||
| 	if (c) return c; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| // get the next character, or 0 if nothing
 | ||||
| uint8_t usb_serial_class::readnext(void) | ||||
| { | ||||
|         uint8_t c, c2, intr_state; | ||||
| 
 | ||||
|         // interrupts are disabled so these functions can be
 | ||||
|         // used from the main program or interrupt context,
 | ||||
|         // even both in the same program!
 | ||||
|         intr_state = SREG; | ||||
|         cli(); | ||||
|         if (!usb_configuration) { | ||||
|                 SREG = intr_state; | ||||
|                 return 0; | ||||
|         } | ||||
|         UENUM = DEBUG_RX_ENDPOINT; | ||||
| try_again: | ||||
|         if (!(UEINTX & (1<<RWAL))) { | ||||
|                 // no packet in buffer
 | ||||
|                 SREG = intr_state; | ||||
|                 return 0; | ||||
|         } | ||||
|         // take one byte out of the buffer
 | ||||
|         c = UEDATX; | ||||
| 	if (c == 0) { | ||||
| 		// if we see a zero, discard it and
 | ||||
| 		// discard the rest of this packet
 | ||||
| 		UEINTX = 0x6B; | ||||
| 		goto try_again; | ||||
| 	} | ||||
|         // if this drained the buffer, release it
 | ||||
|         if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B; | ||||
|         SREG = intr_state; | ||||
|         return c; | ||||
| } | ||||
| 
 | ||||
| // discard any buffered input
 | ||||
| void usb_serial_class::flush() | ||||
| { | ||||
|         uint8_t intr_state; | ||||
| 
 | ||||
|         if (usb_configuration) { | ||||
|                 intr_state = SREG; | ||||
|                 cli(); | ||||
|                 //UENUM = CDC_RX_ENDPOINT;
 | ||||
|                 while ((UEINTX & (1<<RWAL))) { | ||||
|                         UEINTX = 0x6B; | ||||
|                 } | ||||
|                 SREG = intr_state; | ||||
|         } | ||||
| } | ||||
| 
 | ||||
| // transmit a character.
 | ||||
| void usb_serial_class::write(uint8_t c) | ||||
| { | ||||
|         //static uint8_t previous_timeout=0;
 | ||||
|         uint8_t timeout, intr_state; | ||||
| 
 | ||||
|         // if we're not online (enumerated and configured), error
 | ||||
|         if (!usb_configuration) return; | ||||
|         // interrupts are disabled so these functions can be
 | ||||
|         // used from the main program or interrupt context,
 | ||||
|         // even both in the same program!
 | ||||
|         intr_state = SREG; | ||||
|         cli(); | ||||
|         UENUM = DEBUG_TX_ENDPOINT; | ||||
|         // if we gave up due to timeout before, don't wait again
 | ||||
| #if 0 | ||||
| 	// this seems to be causig a lockup... why????
 | ||||
|         if (previous_timeout) { | ||||
|                 if (!(UEINTX & (1<<RWAL))) { | ||||
|                         SREG = intr_state; | ||||
|                         return; | ||||
|                 } | ||||
|                 previous_timeout = 0; | ||||
|         } | ||||
| #endif | ||||
|         // wait for the FIFO to be ready to accept data
 | ||||
|         timeout = UDFNUML + TRANSMIT_TIMEOUT; | ||||
|         while (1) { | ||||
|                 // are we ready to transmit?
 | ||||
|                 if (UEINTX & (1<<RWAL)) break; | ||||
|                 SREG = intr_state; | ||||
|                 // have we waited too long?  This happens if the user
 | ||||
|                 // is not running an application that is listening
 | ||||
|                 if (UDFNUML == timeout) { | ||||
|                         //previous_timeout = 1;
 | ||||
|                         return; | ||||
|                 } | ||||
|                 // has the USB gone offline?
 | ||||
|                 if (!usb_configuration) return; | ||||
|                 // get ready to try checking again
 | ||||
|                 intr_state = SREG; | ||||
|                 cli(); | ||||
|                 UENUM = DEBUG_TX_ENDPOINT; | ||||
|         } | ||||
|         // actually write the byte into the FIFO
 | ||||
|         UEDATX = c; | ||||
|         // if this completed a packet, transmit it now!
 | ||||
|         if (!(UEINTX & (1<<RWAL))) { | ||||
| 		UEINTX = 0x3A; | ||||
|         	debug_flush_timer = 0; | ||||
| 	} else { | ||||
|         	debug_flush_timer = TRANSMIT_FLUSH_TIMEOUT; | ||||
| 	} | ||||
|         SREG = intr_state; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // These are Teensy-specific extensions to the Serial object
 | ||||
| 
 | ||||
| // immediately transmit any buffered output.
 | ||||
| // This doesn't actually transmit the data - that is impossible!
 | ||||
| // USB devices only transmit when the host allows, so the best
 | ||||
| // we can do is release the FIFO buffer for when the host wants it
 | ||||
| void usb_serial_class::send_now(void) | ||||
| { | ||||
|         uint8_t intr_state; | ||||
| 
 | ||||
|         intr_state = SREG; | ||||
|         cli(); | ||||
|         if (debug_flush_timer) { | ||||
|                 UENUM = DEBUG_TX_ENDPOINT; | ||||
| 		while ((UEINTX & (1<<RWAL))) { | ||||
| 			UEDATX = 0; | ||||
| 		} | ||||
|                 UEINTX = 0x3A; | ||||
|                 debug_flush_timer = 0; | ||||
|         } | ||||
|         SREG = intr_state; | ||||
| } | ||||
| 
 | ||||
| uint32_t usb_serial_class::baud(void) | ||||
| { | ||||
| 	return (DEBUG_TX_SIZE * 1000 / DEBUG_TX_INTERVAL); | ||||
| } | ||||
| 
 | ||||
| uint8_t usb_serial_class::stopbits(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| uint8_t usb_serial_class::paritytype(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| uint8_t usb_serial_class::numbits(void) | ||||
| { | ||||
| 	return 8; | ||||
| } | ||||
| 
 | ||||
| uint8_t usb_serial_class::dtr(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| uint8_t usb_serial_class::rts(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Preinstantiate Objects //////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| usb_serial_class	usbSerial = usb_serial_class(); | ||||
| MIDI_Class			usbMIDI = MIDI_Class(); | ||||
| //usb_midi_class		usbMIDI = usb_midi_class();
 | ||||
| 
 | ||||
|  | @ -0,0 +1,36 @@ | |||
| #ifndef USBserial_h_ | ||||
| #define USBserial_h_ | ||||
| 
 | ||||
| #include <inttypes.h> | ||||
| 
 | ||||
| #include "Print.h" | ||||
| 
 | ||||
| class MIDI_Class; | ||||
| extern MIDI_Class usbMIDI; | ||||
| 
 | ||||
| class usb_serial_class : public Print | ||||
| { | ||||
| public: | ||||
| 	// standard Arduino functions
 | ||||
| 	void begin(long); | ||||
| 	void end(void); | ||||
| 	uint8_t available(void); | ||||
| 	int read(void); | ||||
| 	void flush(void); | ||||
| 	virtual void write(uint8_t); | ||||
| 	// Teensy extensions
 | ||||
| 	void send_now(void); | ||||
| 	uint32_t baud(void); | ||||
| 	uint8_t stopbits(void); | ||||
| 	uint8_t paritytype(void); | ||||
| 	uint8_t numbits(void); | ||||
| 	uint8_t dtr(void); | ||||
| 	uint8_t rts(void); | ||||
| private: | ||||
| 	uint8_t readnext(void); | ||||
| }; | ||||
| 
 | ||||
| extern usb_serial_class usbSerial; | ||||
| 
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,899 @@ | |||
| /*!
 | ||||
|  *  @file		usb_midi.cpp | ||||
|  *  Project		Teensy MIDI Core | ||||
|  *	@brief		MIDI Library for Teensy - USB side | ||||
|  *	Version		3.1 | ||||
|  *  @author		Francois Best  | ||||
|  *	@date		28/04/11 | ||||
|  *  License		GPL Forty Seven Effects - 2011 | ||||
|  */ | ||||
| 
 | ||||
| #include "usb_midi.h" | ||||
| #include <stdlib.h> | ||||
| #include "WConstants.h"  | ||||
| #include "usb_api.h" | ||||
| 
 | ||||
| #define USE_SERIAL_PORT usbSerial | ||||
| 
 | ||||
| /*! Main instance (the class comes pre-instantiated). */ | ||||
| MIDI_Class usbMIDI; | ||||
| 
 | ||||
| 
 | ||||
| /*! Default constructor for MIDI_Class. */ | ||||
| MIDI_Class::MIDI_Class() {  | ||||
| #if USE_CALLBACKS | ||||
| 	// Initialise callbacks to NULL pointer
 | ||||
| 	mNoteOffCallback = NULL; | ||||
| 	mNoteOnCallback = NULL; | ||||
| 	mAfterTouchPolyCallback = NULL; | ||||
| 	mControlChangeCallback = NULL; | ||||
| 	mProgramChangeCallback = NULL; | ||||
| 	mAfterTouchChannelCallback = NULL; | ||||
| 	mPitchBendCallback = NULL; | ||||
| 	mSystemExclusiveCallback = NULL; | ||||
| 	mTimeCodeQuarterFrameCallback = NULL; | ||||
| 	mSongPositionCallback = NULL; | ||||
| 	mSongSelectCallback = NULL; | ||||
| 	mTuneRequestCallback = NULL; | ||||
| 	mClockCallback = NULL; | ||||
| 	mStartCallback = NULL; | ||||
| 	mContinueCallback = NULL; | ||||
| 	mStopCallback = NULL; | ||||
| 	mActiveSensingCallback = NULL; | ||||
| 	mSystemResetCallback = NULL; | ||||
| #endif | ||||
| } | ||||
| /*! Default destructor for MIDI_Class.\n
 | ||||
|  This is not really useful for the Arduino, as it is never called... | ||||
|  */ | ||||
| MIDI_Class::~MIDI_Class() { } | ||||
| 
 | ||||
| 
 | ||||
| /*! Call the begin method in the setup() function of the Arduino.
 | ||||
|  All parameters are set to their default values: | ||||
|  - Input channel set to 1 if no value is specified | ||||
|  - Full thru mirroring | ||||
|  */ | ||||
| void MIDI_Class::begin(const byte inChannel) { | ||||
| 	 | ||||
| 	// Initialise the Serial port
 | ||||
| 	USE_SERIAL_PORT.begin(MIDI_BAUDRATE); | ||||
| 	 | ||||
| 	 | ||||
| #if COMPILE_MIDI_OUT | ||||
| 	 | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif // USE_RUNNING_STATUS
 | ||||
| 	 | ||||
| #endif // COMPILE_MIDI_OUT
 | ||||
| 	 | ||||
| 	 | ||||
| #if COMPILE_MIDI_IN | ||||
| 	 | ||||
| 	mInputChannel = inChannel; | ||||
| 	mRunningStatus_RX = InvalidType; | ||||
| 	mPendingMessageIndex = 0; | ||||
| 	mPendingMessageExpectedLenght = 0; | ||||
| 	 | ||||
| 	mMessage.valid = false; | ||||
| 	mMessage.type = InvalidType; | ||||
| 	mMessage.channel = 0; | ||||
| 	mMessage.data1 = 0; | ||||
| 	mMessage.data2 = 0; | ||||
| 	 | ||||
| #endif // COMPILE_MIDI_IN
 | ||||
| 	 | ||||
| 	 | ||||
| #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
 | ||||
| 	 | ||||
| 	mThruFilterMode = Full; | ||||
| 	mThruActivated = true; | ||||
| 	 | ||||
| #endif // Thru
 | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #if COMPILE_MIDI_OUT | ||||
| 
 | ||||
| // Private method for generating a status byte from channel and type
 | ||||
| const byte MIDI_Class::genstatus(const kMIDIType inType,const byte inChannel) { | ||||
| 	return ((byte)inType | ((inChannel-1) & 0x0F)); | ||||
| } | ||||
| 
 | ||||
| /* Generate and send a custom MIDI mMessage.
 | ||||
|  \param type		The message type (see type defines for reference) | ||||
|  \param data1	The first data byte. | ||||
|  \param data2	The second data byte (if the message contains only 1 data byte, set this one to 0). | ||||
|  \param channel	The output channel on which the message will be sent (values from 1 to 16). Note: you cannot send to OMNI. | ||||
|  */ | ||||
| void MIDI_Class::send(kMIDIType type, byte data1, byte data2, byte channel) { | ||||
| 	 | ||||
| 	// Then test if channel is valid
 | ||||
| 	if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) { | ||||
| 		 | ||||
| #if USE_RUNNING_STATUS	 | ||||
| 		mRunningStatus_TX = InvalidType; | ||||
| #endif  | ||||
| 		 | ||||
| 		return; // Don't send anything
 | ||||
| 	} | ||||
| 	 | ||||
| 	if (type <= PitchBend) { | ||||
| 		// Channel messages
 | ||||
| 		 | ||||
| 		// Protection: remove MSBs on data
 | ||||
| 		data1 &= 0x7F; | ||||
| 		data2 &= 0x7F; | ||||
| 		 | ||||
| 		byte statusbyte = genstatus(type,channel); | ||||
| 		 | ||||
| #if USE_RUNNING_STATUS | ||||
| 		// Check Running Status
 | ||||
| 		if (mRunningStatus_TX != statusbyte) { | ||||
| 			// New message, memorise and send header
 | ||||
| 			mRunningStatus_TX = statusbyte; | ||||
| 			USE_SERIAL_PORT.write(mRunningStatus_TX); | ||||
| 		} | ||||
| #else | ||||
| 		// Don't care about running status, send the Control byte.
 | ||||
| 		USE_SERIAL_PORT.write(statusbyte); | ||||
| #endif | ||||
| 		 | ||||
| 		// Then send data
 | ||||
| 		USE_SERIAL_PORT.write(data1); | ||||
| 		if (type != ProgramChange && type != AfterTouchChannel) { | ||||
| 			USE_SERIAL_PORT.write(data2); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	if (type >= TuneRequest && type <= SystemReset) { | ||||
| 		// System Real-time and 1 byte.
 | ||||
| 		sendRealTime(type); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| /*! Send a Note On message 
 | ||||
|  \param NoteNumber	Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n
 | ||||
|  \param Velocity		Note attack velocity (0 to 127). A NoteOn with 0 velocity is considered as a NoteOff. | ||||
|  \param Channel		The channel on which the message will be sent (1 to 16).  | ||||
|  */ | ||||
| void MIDI_Class::sendNoteOn(byte NoteNumber,byte Velocity,byte Channel) { send(NoteOn,NoteNumber,Velocity,Channel); } | ||||
| 
 | ||||
| /*! Send a Note Off message (a real Note Off, not a Note On with null velocity)
 | ||||
|  \param NoteNumber	Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n
 | ||||
|  \param Velocity		Release velocity (0 to 127). | ||||
|  \param Channel		The channel on which the message will be sent (1 to 16). | ||||
|  */ | ||||
| void MIDI_Class::sendNoteOff(byte NoteNumber,byte Velocity,byte Channel) { send(NoteOff,NoteNumber,Velocity,Channel); } | ||||
| 
 | ||||
| /*! Send a Program Change message 
 | ||||
|  \param ProgramNumber	The Program to select (0 to 127). | ||||
|  \param Channel			The channel on which the message will be sent (1 to 16). | ||||
|  */ | ||||
| void MIDI_Class::sendProgramChange(byte ProgramNumber,byte Channel) { send(ProgramChange,ProgramNumber,0,Channel); } | ||||
| 
 | ||||
| /*! Send a Control Change message 
 | ||||
|  \param ControlNumber	The controller number (0 to 127). See the detailed description here: http://www.somascape.org/midi/tech/spec.html#ctrlnums
 | ||||
|  \param ControlValue		The value for the specified controller (0 to 127). | ||||
|  \param Channel			The channel on which the message will be sent (1 to 16).  | ||||
|  */ | ||||
| void MIDI_Class::sendControlChange(byte ControlNumber, byte ControlValue,byte Channel) { send(ControlChange,ControlNumber,ControlValue,Channel); } | ||||
| 
 | ||||
| /*! Send a Polyphonic AfterTouch message (applies to only one specified note)
 | ||||
|  \param NoteNumber		The note to apply AfterTouch to (0 to 127). | ||||
|  \param Pressure			The amount of AfterTouch to apply (0 to 127). | ||||
|  \param Channel			The channel on which the message will be sent (1 to 16).  | ||||
|  */ | ||||
| void MIDI_Class::sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel) { send(AfterTouchPoly,NoteNumber,Pressure,Channel); } | ||||
| 
 | ||||
| /*! Send a MonoPhonic AfterTouch message (applies to all notes)
 | ||||
|  \param Pressure			The amount of AfterTouch to apply to all notes. | ||||
|  \param Channel			The channel on which the message will be sent (1 to 16).  | ||||
|  */ | ||||
| void MIDI_Class::sendAfterTouch(byte Pressure,byte Channel) { send(AfterTouchChannel,Pressure,0,Channel); } | ||||
| 
 | ||||
| /*! Send a Pitch Bend message using an integer value.
 | ||||
|  \param PitchValue	The amount of bend to send (in an integer format), between 0 (maximum downwards bend) and 16383 (max upwards bend), center value is 8192. | ||||
|  \param Channel		The channel on which the message will be sent (1 to 16). | ||||
|  */ | ||||
| void MIDI_Class::sendPitchBend(unsigned int PitchValue,byte Channel) { | ||||
| 	 | ||||
| 	send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel); | ||||
| 	 | ||||
| } | ||||
| /*! Send a Pitch Bend message using a floating point value.
 | ||||
|  \param PitchValue	The amount of bend to send (in a floating point format), between -1 (maximum downwards bend) and +1 (max upwards bend), center value is 0. | ||||
|  \param Channel		The channel on which the message will be sent (1 to 16). | ||||
|  */ | ||||
| void MIDI_Class::sendPitchBend(double PitchValue,byte Channel) { | ||||
| 	 | ||||
| 	unsigned int pitchval = (PitchValue+1.f)*8192; | ||||
| 	if (pitchval > 16383) pitchval = 16383;		// overflow protection
 | ||||
| 	sendPitchBend(pitchval,Channel); | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| /*! Generate and send a System Exclusive frame.
 | ||||
|  \param length	The size of the array to send | ||||
|  \param array	The byte array containing the data to send | ||||
|  \param ArrayContainsBoundaries  When set to 'true', 0xF0 & 0xF7 bytes (start & stop SysEx) will NOT be sent (and therefore must be included in the array).\ | ||||
|  default value is set to 'false' for compatibility with previous versions of the library. | ||||
|  */ | ||||
| void MIDI_Class::sendSysEx(byte length, byte * array, bool ArrayContainsBoundaries) { | ||||
| 	if (!ArrayContainsBoundaries) USE_SERIAL_PORT.write(0xF0); | ||||
| 	for (byte i=0;i<length;i++) USE_SERIAL_PORT.write(array[i]); | ||||
| 	if (!ArrayContainsBoundaries) USE_SERIAL_PORT.write(0xF7); | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*! Send a Tune Request message. When a MIDI unit receives this message, it should tune its oscillators (if equipped with any) */ | ||||
| void MIDI_Class::sendTuneRequest() { sendRealTime(TuneRequest); } | ||||
| 
 | ||||
| /*! Send a MIDI Time Code Quarter Frame. See MIDI Specification for more information.
 | ||||
|  \param TypeNibble	MTC type | ||||
|  \param ValuesNibble	MTC data | ||||
|  */ | ||||
| void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) { | ||||
| 	 | ||||
| 	byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) ); | ||||
| 	sendTimeCodeQuarterFrame(data); | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| /*! Send a MIDI Time Code Quarter Frame. See MIDI Specification for more information.
 | ||||
|  \param data	 if you want to encode directly the nibbles in your program, you can send the byte here. | ||||
|  */ | ||||
| void MIDI_Class::sendTimeCodeQuarterFrame(byte data) { | ||||
| 	 | ||||
| 	USE_SERIAL_PORT.write((byte)TimeCodeQuarterFrame); | ||||
| 	USE_SERIAL_PORT.write(data); | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*! Send a Song Position Pointer message.
 | ||||
|  \param Beats	The number of beats since the start of the song. | ||||
|  */ | ||||
| void MIDI_Class::sendSongPosition(unsigned int Beats) { | ||||
| 	 | ||||
| 	USE_SERIAL_PORT.write((byte)SongPosition); | ||||
| 	USE_SERIAL_PORT.write(Beats & 0x7F); | ||||
| 	USE_SERIAL_PORT.write((Beats >> 7) & 0x7F); | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*! Send a Song Select message */ | ||||
| void MIDI_Class::sendSongSelect(byte SongNumber) { | ||||
| 	 | ||||
| 	USE_SERIAL_PORT.write((byte)SongSelect); | ||||
| 	USE_SERIAL_PORT.write(SongNumber & 0x7F); | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*! Send a Real Time (one byte) message. \n You can also send a Tune Request with this method.
 | ||||
|  \param Type The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset.  | ||||
|  */ | ||||
| void MIDI_Class::sendRealTime(kMIDIType Type) { | ||||
| 	switch (Type) { | ||||
| 		case TuneRequest: // Not really real-time, but one byte anyway.
 | ||||
| 		case Clock: | ||||
| 		case Start: | ||||
| 		case Stop:	 | ||||
| 		case Continue: | ||||
| 		case ActiveSensing: | ||||
| 		case SystemReset: | ||||
| 			USE_SERIAL_PORT.write((byte)Type); | ||||
| 			break; | ||||
| 		default: | ||||
| 			// Invalid Real Time marker
 | ||||
| 			break; | ||||
| 	} | ||||
| #if USE_RUNNING_STATUS | ||||
| 	mRunningStatus_TX = InvalidType; | ||||
| #endif	 | ||||
| } | ||||
| 
 | ||||
| #endif // COMPILE_MIDI_OUT
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #if COMPILE_MIDI_IN | ||||
| 
 | ||||
| /*! Read a MIDI message from the serial port using the main input channel (see setInputChannel() for reference). \n
 | ||||
|  Returned value: true if any valid message has been stored in the structure, false if not. | ||||
|  A valid message is a message that matches the input channel. \n\n | ||||
|  If the Thru is enabled and the messages matches the filter, it is sent back on the MIDI output. | ||||
|  */ | ||||
| bool MIDI_Class::read() { | ||||
| 	return read(mInputChannel); | ||||
| } | ||||
| 
 | ||||
| /*! Reading/thru-ing method, the same as read() with a given input channel to read on. */ | ||||
| bool MIDI_Class::read(const byte inChannel) { | ||||
| 	 | ||||
| 	if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled.
 | ||||
| 	 | ||||
| 	 | ||||
| 	if (parse(inChannel)) { | ||||
| 		if (input_filter(inChannel)) { | ||||
| 			 | ||||
| #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) | ||||
| 			thru_filter(inChannel); | ||||
| #endif | ||||
| 			 | ||||
| #if USE_CALLBACKS | ||||
| 			launchCallback(); | ||||
| #endif | ||||
| 			 | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| // Private method: MIDI parser
 | ||||
| bool MIDI_Class::parse(byte inChannel) {  | ||||
| 	 | ||||
| 	// If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
 | ||||
| 	if (USE_SERIAL_PORT.available() == 128) { | ||||
| 		USE_SERIAL_PORT.flush(); | ||||
| 	}	 | ||||
| 	 | ||||
| 	if (USE_SERIAL_PORT.available() <= 0) { | ||||
| 		// No data available.
 | ||||
| 		return false; | ||||
| 	} | ||||
| 	else { | ||||
| 		 | ||||
| 		/* Parsing algorithm:
 | ||||
| 		 Get a byte from the serial buffer. | ||||
| 		 * If there is no pending message to be recomposed, start a new one. | ||||
| 		 - Find type and channel (if pertinent) | ||||
| 		 - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty. | ||||
| 		 * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it. | ||||
| 		 */ | ||||
| 		 | ||||
| 		 | ||||
| 		byte extracted = USE_SERIAL_PORT.read(); | ||||
| 		 | ||||
| 		if (mPendingMessageIndex == 0) { // Start a new pending message
 | ||||
| 			mPendingMessage[0] = extracted; | ||||
| 			 | ||||
| 			// Check for running status first
 | ||||
| 			switch (getTypeFromStatusByte(mRunningStatus_RX)) { | ||||
| 					// Only these types allow Running Status:
 | ||||
| 				case NoteOff: | ||||
| 				case NoteOn: | ||||
| 				case AfterTouchPoly: | ||||
| 				case ControlChange: | ||||
| 				case ProgramChange: | ||||
| 				case AfterTouchChannel: | ||||
| 				case PitchBend:	 | ||||
| 					 | ||||
| 					// If the status byte is not received, prepend it to the pending message
 | ||||
| 					if (extracted < 0x80) { | ||||
| 						mPendingMessage[0] = mRunningStatus_RX; | ||||
| 						mPendingMessage[1] = extracted; | ||||
| 						mPendingMessageIndex = 1; | ||||
| 					} | ||||
| 					// Else: well, we received another status byte, so the running status does not apply here.
 | ||||
| 					// It will be updated upon completion of this message.
 | ||||
| 					 | ||||
| 					break; | ||||
| 					 | ||||
| 				default: | ||||
| 					// No running status
 | ||||
| 					break; | ||||
| 			} | ||||
| 			 | ||||
| 			 | ||||
| 			switch (getTypeFromStatusByte(mPendingMessage[0])) { | ||||
| 					 | ||||
| 					// 1 byte messages
 | ||||
| 				case Start: | ||||
| 				case Continue: | ||||
| 				case Stop: | ||||
| 				case Clock: | ||||
| 				case ActiveSensing: | ||||
| 				case SystemReset: | ||||
| 				case TuneRequest: | ||||
| 					// Handle the message type directly here.
 | ||||
| 					mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); | ||||
| 					mMessage.channel = 0; | ||||
| 					mMessage.data1 = 0; | ||||
| 					mMessage.data2 = 0; | ||||
| 					mMessage.valid = true; | ||||
| 					reset_input_attributes(); | ||||
| 					return true; | ||||
| 					break; | ||||
| 					 | ||||
| 					// 2 bytes messages
 | ||||
| 				case ProgramChange: | ||||
| 				case AfterTouchChannel: | ||||
| 				case TimeCodeQuarterFrame: | ||||
| 				case SongSelect: | ||||
| 					mPendingMessageExpectedLenght = 2; | ||||
| 					break; | ||||
| 					 | ||||
| 					// 3 bytes messages
 | ||||
| 				case NoteOn: | ||||
| 				case NoteOff: | ||||
| 				case ControlChange: | ||||
| 				case PitchBend: | ||||
| 				case AfterTouchPoly: | ||||
| 				case SongPosition: | ||||
| 					mPendingMessageExpectedLenght = 3; | ||||
| 					break; | ||||
| 					 | ||||
| 				case SystemExclusive: | ||||
| 					mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
 | ||||
| 					mRunningStatus_RX = InvalidType; | ||||
| 					break; | ||||
| 					 | ||||
| 				case InvalidType: | ||||
| 				default: | ||||
| 					// This is obviously wrong. Let's get the hell out'a here.
 | ||||
| 					reset_input_attributes(); | ||||
| 					return false; | ||||
| 					break; | ||||
| 			} | ||||
| 			 | ||||
| 			// Then update the index of the pending message.
 | ||||
| 			mPendingMessageIndex++; | ||||
| 			 | ||||
| 			// And call the parser, again.
 | ||||
| 			return parse(inChannel); | ||||
| 			 | ||||
| 		} | ||||
| 		else {  | ||||
| 			 | ||||
| 			 | ||||
| 			// First, test if this is a status byte
 | ||||
| 			if (extracted >= 0x80) { | ||||
| 				 | ||||
| 				// Reception of status bytes in the middle of an uncompleted message
 | ||||
| 				// are allowed only for interleaved Real Time message or EOX
 | ||||
| 				switch (extracted) { | ||||
| 					case Clock: | ||||
| 					case Start: | ||||
| 					case Continue: | ||||
| 					case Stop: | ||||
| 					case ActiveSensing: | ||||
| 					case SystemReset: | ||||
| 						 | ||||
| 						/*
 | ||||
| 						 This is tricky. Here we will have to extract the one-byte message, | ||||
| 						 pass it to the structure for being read outside the MIDI class, | ||||
| 						 and recompose the message it was interleaved into. | ||||
| 						  | ||||
| 						 Oh, and without killing the running status..  | ||||
| 						  | ||||
| 						 This is done by leaving the pending message as is, it will be completed on next calls. | ||||
| 						 */ | ||||
| 						 | ||||
| 						mMessage.type = (kMIDIType)extracted; | ||||
| 						mMessage.data1 = 0; | ||||
| 						mMessage.data2 = 0; | ||||
| 						mMessage.channel = 0; | ||||
| 						mMessage.valid = true; | ||||
| 						return true; | ||||
| 						 | ||||
| 						break; | ||||
| 						 | ||||
| 						// End of Exclusive
 | ||||
| 					case 0xF7: | ||||
| 						if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { | ||||
| 							 | ||||
| 							// Store System Exclusive array in midimsg structure
 | ||||
| 							for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) { | ||||
| 								mMessage.sysex_array[i] = mPendingMessage[i]; | ||||
| 							} | ||||
| 							 | ||||
| 							mMessage.type = SystemExclusive; | ||||
| 							mMessage.data1 = mPendingMessageIndex+1;	// Get length
 | ||||
| 							mMessage.data2 = 0; | ||||
| 							mMessage.channel = 0; | ||||
| 							mMessage.valid = true; | ||||
| 							 | ||||
| 							reset_input_attributes(); | ||||
| 							 | ||||
| 							return true; | ||||
| 						} | ||||
| 						else { | ||||
| 							// Well well well.. error.
 | ||||
| 							reset_input_attributes(); | ||||
| 							return false; | ||||
| 						} | ||||
| 						 | ||||
| 						break; | ||||
| 					default: | ||||
| 						break; | ||||
| 				} | ||||
| 				 | ||||
| 				 | ||||
| 				 | ||||
| 			} | ||||
| 			 | ||||
| 			 | ||||
| 			// Add extracted data byte to pending message
 | ||||
| 			mPendingMessage[mPendingMessageIndex] = extracted; | ||||
| 			 | ||||
| 			 | ||||
| 			// Now we are going to check if we have reached the end of the message
 | ||||
| 			if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { | ||||
| 				 | ||||
| 				// "FML" case: fall down here with an overflown SysEx..
 | ||||
| 				// This means we received the last possible data byte that can fit the buffer.
 | ||||
| 				// If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
 | ||||
| 				if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { | ||||
| 					reset_input_attributes(); | ||||
| 					return false; | ||||
| 				} | ||||
| 				 | ||||
| 				 | ||||
| 				 | ||||
| 				 | ||||
| 				mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); | ||||
| 				mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message
 | ||||
| 				 | ||||
| 				mMessage.data1 = mPendingMessage[1]; | ||||
| 				 | ||||
| 				// Save data2 only if applicable
 | ||||
| 				if (mPendingMessageExpectedLenght == 3)	mMessage.data2 = mPendingMessage[2]; | ||||
| 				else mMessage.data2 = 0; | ||||
| 				 | ||||
| 				// Reset local variables
 | ||||
| 				mPendingMessageIndex = 0; | ||||
| 				mPendingMessageExpectedLenght = 0; | ||||
| 				 | ||||
| 				mMessage.valid = true; | ||||
| 				 | ||||
| 				// Activate running status (if enabled for the received type)
 | ||||
| 				switch (mMessage.type) { | ||||
| 					case NoteOff: | ||||
| 					case NoteOn: | ||||
| 					case AfterTouchPoly: | ||||
| 					case ControlChange: | ||||
| 					case ProgramChange: | ||||
| 					case AfterTouchChannel: | ||||
| 					case PitchBend:	 | ||||
| 						// Running status enabled: store it from received message
 | ||||
| 						mRunningStatus_RX = mPendingMessage[0]; | ||||
| 						break; | ||||
| 						 | ||||
| 					default: | ||||
| 						// No running status
 | ||||
| 						mRunningStatus_RX = InvalidType; | ||||
| 						break; | ||||
| 				} | ||||
| 				return true; | ||||
| 			} | ||||
| 			else { | ||||
| 				// Then update the index of the pending message.
 | ||||
| 				mPendingMessageIndex++; | ||||
| 				 | ||||
| 				// And call the parser, again.
 | ||||
| 				return parse(inChannel); | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 	} | ||||
| 	 | ||||
| 	// What are our chances to fall here?
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Private method: check if the received message is on the listened channel
 | ||||
| bool MIDI_Class::input_filter(byte inChannel) { | ||||
| 	 | ||||
| 	 | ||||
| 	// This method handles recognition of channel (to know if the message is destinated to the Arduino)
 | ||||
| 	 | ||||
| 	 | ||||
| 	if (mMessage.type == InvalidType) return false; | ||||
| 	 | ||||
| 	 | ||||
| 	// First, check if the received message is Channel
 | ||||
| 	if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { | ||||
| 		 | ||||
| 		// Then we need to know if we listen to it
 | ||||
| 		if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { | ||||
| 			return true; | ||||
| 			 | ||||
| 		} | ||||
| 		else { | ||||
| 			// We don't listen to this channel
 | ||||
| 			return false; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 	else { | ||||
| 		 | ||||
| 		// System messages are always received
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| // Private method: reset input attributes
 | ||||
| void MIDI_Class::reset_input_attributes() { | ||||
| 	mPendingMessageIndex = 0; | ||||
| 	mPendingMessageExpectedLenght = 0; | ||||
| 	mRunningStatus_RX = InvalidType; | ||||
| } | ||||
| 
 | ||||
| // Getters
 | ||||
| /*! Getter method: access to the message type stored in the structure. \n Returns an enumerated type. */ | ||||
| kMIDIType MIDI_Class::getType() { return mMessage.type; } | ||||
| /*! Getter method: access to the channel of the message stored in the structure. */ | ||||
| byte MIDI_Class::getChannel() { return mMessage.channel; } | ||||
| /*! Getter method: access to the first data byte of the message stored in the structure. \n If the message is SysEx, the length of the array is stocked there. */ | ||||
| byte MIDI_Class::getData1() { return mMessage.data1; } | ||||
| /*! Getter method: access to the second data byte of the message stored in the structure. */ | ||||
| byte MIDI_Class::getData2() { return mMessage.data2; } | ||||
| /*! Getter method: access to the System Exclusive byte array. Array length is stocked in Data1. */ | ||||
| byte * MIDI_Class::getSysExArray() { return mMessage.sysex_array; } | ||||
| /*! Check if a valid message is stored in the structure. */ | ||||
| bool MIDI_Class::check() { return mMessage.valid; } | ||||
| 
 | ||||
| // Setters
 | ||||
| /*! Set the value for the input MIDI channel 
 | ||||
|  \param Channel the channel value. Valid values are 1 to 16,  | ||||
|  MIDI_CHANNEL_OMNI if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable MIDI input. | ||||
|  */ | ||||
| void MIDI_Class::setInputChannel(const byte Channel) { mInputChannel = Channel; } | ||||
| 
 | ||||
| 
 | ||||
| #if USE_CALLBACKS | ||||
| 
 | ||||
| void MIDI_Class::setHandleNoteOff(void (*fptr)(byte ch, byte note, byte vel))			{ mNoteOffCallback = fptr; } | ||||
| void MIDI_Class::setHandleNoteOn(void (*fptr)(byte ch, byte note, byte vel))			{ mNoteOnCallback = fptr; } | ||||
| void MIDI_Class::setHandleAfterTouchPoly(void (*fptr)(byte ch, byte note, byte vel))	{ mAfterTouchPolyCallback = fptr; } | ||||
| void MIDI_Class::setHandleControlChange(void (*fptr)(byte ch, byte, byte))				{ mControlChangeCallback = fptr; } | ||||
| void MIDI_Class::setHandleProgramChange(void (*fptr)(byte ch, byte))					{ mProgramChangeCallback = fptr; } | ||||
| void MIDI_Class::setHandleAfterTouchChannel(void (*fptr)(byte ch, byte))				{ mAfterTouchChannelCallback = fptr; } | ||||
| void MIDI_Class::setHandlePitchBend(void (*fptr)(byte ch, word))						{ mPitchBendCallback = fptr; } | ||||
| void MIDI_Class::setHandleSystemExclusive(void (*fptr)(byte * array, byte size))		{ mSystemExclusiveCallback = fptr; } | ||||
| void MIDI_Class::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data))					{ mTimeCodeQuarterFrameCallback = fptr; } | ||||
| void MIDI_Class::setHandleSongPosition(void (*fptr)(word beats))						{ mSongPositionCallback = fptr; } | ||||
| void MIDI_Class::setHandleSongSelect(void (*fptr)(byte song_number))					{ mSongSelectCallback = fptr; } | ||||
| void MIDI_Class::setHandleTuneRequest(void (*fptr)(void))								{ mTuneRequestCallback = fptr; } | ||||
| void MIDI_Class::setHandleClock(void (*fptr)(void))										{ mClockCallback = fptr; } | ||||
| void MIDI_Class::setHandleStart(void (*fptr)(void))										{ mStartCallback = fptr; } | ||||
| void MIDI_Class::setHandleContinue(void (*fptr)(void))									{ mContinueCallback = fptr; } | ||||
| void MIDI_Class::setHandleStop(void (*fptr)(void))										{ mStopCallback = fptr; } | ||||
| void MIDI_Class::setHandleActiveSensing(void (*fptr)(void))								{ mActiveSensingCallback = fptr; } | ||||
| void MIDI_Class::setHandleSystemReset(void (*fptr)(void))								{ mSystemResetCallback = fptr; } | ||||
| 
 | ||||
| 
 | ||||
| /*! Detach an external function from the given type.\n
 | ||||
|  Use this method to cancel the effects of connectCallback. | ||||
|  \param Type		The type of message to unbind. When a message of this type is received, no function will be called. | ||||
|  */ | ||||
| void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) { | ||||
| 	 | ||||
| 	switch (Type) { | ||||
| 		case NoteOff:				mNoteOffCallback = NULL;	break; | ||||
| 		case NoteOn:				mNoteOnCallback = NULL;	break; | ||||
| 		case AfterTouchPoly:		mAfterTouchPolyCallback = NULL;	break; | ||||
| 		case ControlChange:			mControlChangeCallback = NULL;	break; | ||||
| 		case ProgramChange:			mProgramChangeCallback = NULL;	break; | ||||
| 		case AfterTouchChannel:		mAfterTouchChannelCallback = NULL;	break; | ||||
| 		case PitchBend:				mPitchBendCallback = NULL;	break; | ||||
| 		case SystemExclusive:		mSystemExclusiveCallback = NULL;	break; | ||||
| 		case TimeCodeQuarterFrame:	mTimeCodeQuarterFrameCallback = NULL;	break; | ||||
| 		case SongPosition:			mSongPositionCallback = NULL;	break; | ||||
| 		case SongSelect:			mSongSelectCallback = NULL;	break; | ||||
| 		case TuneRequest:			mTuneRequestCallback = NULL;	break; | ||||
| 		case Clock:					mClockCallback = NULL;	break; | ||||
| 		case Start:					mStartCallback = NULL;	break; | ||||
| 		case Continue:				mContinueCallback = NULL;	break; | ||||
| 		case Stop:					mStopCallback = NULL;	break; | ||||
| 		case ActiveSensing:			mActiveSensingCallback = NULL;	break; | ||||
| 		case SystemReset:			mSystemResetCallback = NULL;	break; | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| // Private - launch callback function based on received type.
 | ||||
| void MIDI_Class::launchCallback() { | ||||
| 	 | ||||
| 	// The order is mixed to allow frequent messages to trigger their callback faster.
 | ||||
| 	 | ||||
| 	switch (mMessage.type) { | ||||
| 			// Notes
 | ||||
| 		case NoteOff:				if (mNoteOffCallback != NULL)				mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2);	break; | ||||
| 		case NoteOn:				if (mNoteOnCallback != NULL)				mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2);	break; | ||||
| 			 | ||||
| 			// Real-time messages
 | ||||
| 		case Clock:					if (mClockCallback != NULL)					mClockCallback();			break;			 | ||||
| 		case Start:					if (mStartCallback != NULL)					mStartCallback();			break; | ||||
| 		case Continue:				if (mContinueCallback != NULL)				mContinueCallback();		break; | ||||
| 		case Stop:					if (mStopCallback != NULL)					mStopCallback();			break; | ||||
| 		case ActiveSensing:			if (mActiveSensingCallback != NULL)			mActiveSensingCallback();	break; | ||||
| 			 | ||||
| 			// Continuous controllers
 | ||||
| 		case ControlChange:			if (mControlChangeCallback != NULL)			mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2);	break; | ||||
| 		case PitchBend:				if (mPitchBendCallback != NULL)				mPitchBendCallback(mMessage.channel,(mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7));	break; | ||||
| 		case AfterTouchPoly:		if (mAfterTouchPolyCallback != NULL)		mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2);	break; | ||||
| 		case AfterTouchChannel:		if (mAfterTouchChannelCallback != NULL)		mAfterTouchChannelCallback(mMessage.channel,mMessage.data1);	break; | ||||
| 			 | ||||
| 		case ProgramChange:			if (mProgramChangeCallback != NULL)			mProgramChangeCallback(mMessage.channel,mMessage.data1);	break; | ||||
| 		case SystemExclusive:		if (mSystemExclusiveCallback != NULL)		mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1);	break; | ||||
| 			 | ||||
| 			// Occasional messages
 | ||||
| 		case TimeCodeQuarterFrame:	if (mTimeCodeQuarterFrameCallback != NULL)	mTimeCodeQuarterFrameCallback(mMessage.data1);	break; | ||||
| 		case SongPosition:			if (mSongPositionCallback != NULL)			mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7));	break; | ||||
| 		case SongSelect:			if (mSongSelectCallback != NULL)			mSongSelectCallback(mMessage.data1);	break; | ||||
| 		case TuneRequest:			if (mTuneRequestCallback != NULL)			mTuneRequestCallback();	break; | ||||
| 			 | ||||
| 		case SystemReset:			if (mSystemResetCallback != NULL)			mSystemResetCallback();	break; | ||||
| 		case InvalidType: | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #endif // USE_CALLBACKS
 | ||||
| 
 | ||||
| 
 | ||||
| #endif // COMPILE_MIDI_IN
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
 | ||||
| 
 | ||||
| /*! Set the filter for thru mirroring
 | ||||
|  \param inThruFilterMode a filter mode | ||||
|  See kThruFilterMode for detailed description. | ||||
|  */ | ||||
| void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) {  | ||||
| 	mThruFilterMode = inThruFilterMode; | ||||
| 	if (mThruFilterMode != Off) mThruActivated = true; | ||||
| 	else mThruActivated = false; | ||||
| } | ||||
| /*! Set the filter for thru mirroring
 | ||||
|  \param inThruFilterMode a filter mode | ||||
|  See kThruFilterMode for detailed description. \n | ||||
|  This method uses a byte parameter and is for compatibility only, please use kThruFilterMode for future programs. | ||||
|  */ | ||||
| void MIDI_Class::setThruFilterMode(byte inThruFilterMode) {  | ||||
| 	mThruFilterMode = (kThruFilterMode)inThruFilterMode; | ||||
| 	if (mThruFilterMode != Off) mThruActivated = true; | ||||
| 	else mThruActivated = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*! Setter method: turn message mirroring on. */ | ||||
| void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode) {  | ||||
| 	mThruActivated = true; | ||||
| 	mThruFilterMode = inThruFilterMode; | ||||
| } | ||||
| /*! Setter method: turn message mirroring off. */ | ||||
| void MIDI_Class::turnThruOff() { | ||||
| 	mThruActivated = false;  | ||||
| 	mThruFilterMode = Off; | ||||
| } | ||||
| 
 | ||||
| // This method is called upon reception of a message and takes care of Thru filtering and sending.
 | ||||
| void MIDI_Class::thru_filter(byte inChannel) { | ||||
| 	 | ||||
| 	/*
 | ||||
| 	 This method handles Soft-Thru filtering. | ||||
| 	  | ||||
| 	 Soft-Thru filtering: | ||||
| 	 - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off | ||||
| 	 - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting | ||||
| 	  | ||||
| 	 */ | ||||
| 	 | ||||
| #if TEENSY_SUPPORT && TEENSY_MIDI_TO_USB | ||||
| 	// Pass the message to the USB side if enabled
 | ||||
| 	 | ||||
| #endif | ||||
| 	 | ||||
| 	// If the feature is disabled, don't do anything.
 | ||||
| 	if (!mThruActivated || (mThruFilterMode == Off)) return; | ||||
| 	 | ||||
| 	 | ||||
| 	// First, check if the received message is Channel
 | ||||
| 	if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { | ||||
| 		 | ||||
| 		 | ||||
| 		bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)); | ||||
| 		 | ||||
| 		// Now let's pass it to the output
 | ||||
| 		switch (mThruFilterMode) { | ||||
| 			case Full: | ||||
| 				send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); | ||||
| 				return; | ||||
| 				break; | ||||
| 			case SameChannel: | ||||
| 				if (filter_condition) { | ||||
| 					send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); | ||||
| 					return; | ||||
| 				} | ||||
| 				break; | ||||
| 			case DifferentChannel: | ||||
| 				if (!filter_condition) { | ||||
| 					send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); | ||||
| 					return; | ||||
| 				} | ||||
| 			case Off: | ||||
| 				// Do nothing
 | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 	else { | ||||
| 		 | ||||
| 		// Send the message to the output
 | ||||
| 		if (mThruFilterMode != Off) { | ||||
| 			switch (mMessage.type) { | ||||
| 					// Real Time and 1 byte
 | ||||
| 				case Clock: | ||||
| 				case Start: | ||||
| 				case Stop: | ||||
| 				case Continue: | ||||
| 				case ActiveSensing: | ||||
| 				case SystemReset: | ||||
| 				case TuneRequest:	 | ||||
| 					sendRealTime(mMessage.type); | ||||
| 					return; | ||||
| 					break; | ||||
| 					 | ||||
| 				case SystemExclusive: | ||||
| 					// Send SysEx (0xF0 and 0xF7 are included in the buffer)
 | ||||
| 					sendSysEx(mMessage.data1,mMessage.sysex_array,true);  | ||||
| 					return; | ||||
| 					break; | ||||
| 					 | ||||
| 				case SongSelect: | ||||
| 					sendSongSelect(mMessage.data1); // TODO: check this
 | ||||
| 					return; | ||||
| 					break; | ||||
| 					 | ||||
| 				case SongPosition: | ||||
| 					sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));	// TODO: check this
 | ||||
| 					return; | ||||
| 					break; | ||||
| 					 | ||||
| 				case TimeCodeQuarterFrame: | ||||
| 					sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2); // TODO: check this
 | ||||
| 					return; | ||||
| 					break; | ||||
| 				default: | ||||
| 					break; | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #endif // Thru
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,302 @@ | |||
| /*!
 | ||||
|  *  @file		usb_midi.h | ||||
|  *  Project		Teensy MIDI Core | ||||
|  *	@brief		MIDI Library for Teensy - USB side | ||||
|  *	Version		3.1 | ||||
|  *  @author		Francois Best  | ||||
|  *	@date		28/04/11 | ||||
|  *  License		GPL Forty Seven Effects - 2011 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TEENSY_LIB_MIDI_USB_FSE_H_ | ||||
| #define _TEENSY_LIB_MIDI_USB_FSE_H_ | ||||
| 
 | ||||
| #include <inttypes.h>  | ||||
| 
 | ||||
| 
 | ||||
| /*  
 | ||||
|     ############################################################### | ||||
|     #                                                             # | ||||
|     #    CONFIGURATION AREA                                       # | ||||
|     #                                                             # | ||||
|     #    Here are a few settings you can change to customize      # | ||||
|     #    the library for your own project. You can for example    # | ||||
|     #    choose to compile only parts of it so you gain flash     # | ||||
|     #    space and optimise the speed of your sketch.             # | ||||
|     #                                                             # | ||||
|     ############################################################### | ||||
|  */ | ||||
| 
 | ||||
| #define CONVERT_USB_TO_MIDI     1           // Set this to 1 to forward incoming messages on the USB MIDI to the UART.
 | ||||
| #define CONVERT_MIDI_TO_USB     1           // Set this to 1 to forward incoming messages on the UART to the USB MIDI.
 | ||||
| 
 | ||||
| #define COMPILE_MIDI_IN         1           // Set this setting to 1 to use the MIDI input.
 | ||||
| #define COMPILE_MIDI_OUT        1           // Set this setting to 1 to use the MIDI output. 
 | ||||
| #define COMPILE_MIDI_THRU       1           // Set this setting to 1 to use the MIDI Soft Thru feature
 | ||||
|                                             // Please note that the Thru will work only when both COMPILE_MIDI_IN and COMPILE_MIDI_OUT set to 1.
 | ||||
| 
 | ||||
| 
 | ||||
| #define USE_RUNNING_STATUS		1			// Running status enables short messages when sending multiple values
 | ||||
|                                             // of the same type and channel.
 | ||||
|                                             // Set to 0 if you have troubles with controlling you hardware.
 | ||||
| 
 | ||||
| 
 | ||||
| #define USE_CALLBACKS           1           // Set this to 1 if you want to use callback handlers (to bind your functions to the library).
 | ||||
|                                             // To use the callbacks, you need to have COMPILE_MIDI_IN set to 1
 | ||||
| 
 | ||||
| 
 | ||||
| // END OF CONFIGURATION AREA 
 | ||||
| // (do not modify anything under this line unless you know what you are doing)
 | ||||
| 
 | ||||
| 
 | ||||
| #define MIDI_BAUDRATE			31250 | ||||
| 
 | ||||
| #define MIDI_CHANNEL_OMNI		0 | ||||
| #define MIDI_CHANNEL_OFF		17 // and over
 | ||||
| 
 | ||||
| #define MIDI_SYSEX_ARRAY_SIZE	60 | ||||
| 
 | ||||
| /*! Type definition for practical use (because "unsigned char" is a bit long to write.. )*/ | ||||
| typedef uint8_t byte; | ||||
| typedef uint16_t word; | ||||
| 
 | ||||
| /*! Enumeration of MIDI types */ | ||||
| enum kMIDIType { | ||||
| 	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
 | ||||
| 	InvalidType           = 0x00    // For notifying errors
 | ||||
| }; | ||||
| 
 | ||||
| /*! Enumeration of Thru filter modes */ | ||||
| enum kThruFilterMode { | ||||
| 	Off                   = 0,  // Thru disabled (nothing passes through).
 | ||||
| 	Full                  = 1,  // Fully enabled Thru (every incoming message is sent back).
 | ||||
| 	SameChannel           = 2,  // Only the messages on the Input Channel will be sent back.
 | ||||
| 	DifferentChannel      = 3   // All the messages but the ones on the Input Channel will be sent back.
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*! The midimsg structure contains decoded data of a MIDI message read from the serial port with read() or thru(). \n */ | ||||
| struct midimsg { | ||||
| 	/*! The MIDI channel on which the message was recieved. \n Value goes from 1 to 16. */ | ||||
| 	byte channel;  | ||||
| 	/*! The type of the message (see the define section for types reference) */ | ||||
| 	kMIDIType type; | ||||
| 	/*! The first data byte.\n Value goes from 0 to 127.\n If the message is SysEx, this byte contains the array length. */ | ||||
| 	byte data1; | ||||
| 	/*! The second data byte. If the message is only 2 bytes long, this one is null.\n Value goes from 0 to 127. */ | ||||
| 	byte data2; | ||||
| 	/*! System Exclusive dedicated byte array. \n Array length is stocked in data1. */ | ||||
| 	byte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; | ||||
| 	/*! This boolean indicates if the message is valid or not. There is no channel consideration here, validity means the message respects the MIDI norm. */ | ||||
| 	bool valid; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*! The main class for MIDI handling.
 | ||||
| 	See member descriptions to know how to use it, | ||||
| 	or check out the examples supplied with the library. | ||||
|  */ | ||||
| class MIDI_Class { | ||||
| 	 | ||||
| 	 | ||||
| public: | ||||
| 	// Constructor and Destructor
 | ||||
| 	MIDI_Class(); | ||||
| 	~MIDI_Class(); | ||||
| 	 | ||||
| 	 | ||||
| 	void begin(const byte inChannel = 1); | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| /* ####### OUTPUT COMPILATION BLOCK ####### */	 | ||||
| #if COMPILE_MIDI_OUT | ||||
| 
 | ||||
| public:	 | ||||
| 	 | ||||
| 	void sendNoteOn(byte NoteNumber,byte Velocity,byte Channel); | ||||
| 	void sendNoteOff(byte NoteNumber,byte Velocity,byte Channel); | ||||
| 	void sendProgramChange(byte ProgramNumber,byte Channel); | ||||
| 	void sendControlChange(byte ControlNumber, byte ControlValue,byte Channel); | ||||
| 	void sendPitchBend(unsigned int PitchValue,byte Channel); | ||||
| 	void sendPitchBend(double PitchValue,byte Channel); | ||||
| 	void sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel); | ||||
| 	void sendAfterTouch(byte Pressure,byte Channel); | ||||
| 	void sendSysEx(byte length, byte * array,bool ArrayContainsBoundaries = false);	 | ||||
| 	void sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble); | ||||
| 	void sendTimeCodeQuarterFrame(byte data); | ||||
| 	void sendSongPosition(unsigned int Beats); | ||||
| 	void sendSongSelect(byte SongNumber); | ||||
| 	void sendTuneRequest(); | ||||
| 	void sendRealTime(kMIDIType Type); | ||||
| 	 | ||||
| 	 | ||||
| private: | ||||
| 	 | ||||
| 	const byte genstatus(const kMIDIType inType,const byte inChannel); | ||||
| 	void send(kMIDIType type, byte param1, byte param2, byte channel); | ||||
| 	 | ||||
| 	// Attributes
 | ||||
| #if USE_RUNNING_STATUS | ||||
| 	byte			mRunningStatus_TX; | ||||
| #endif // USE_RUNNING_STATUS
 | ||||
| 
 | ||||
| #endif	// COMPILE_MIDI_OUT
 | ||||
| 	 | ||||
| 
 | ||||
| 	 | ||||
| /* ####### INPUT COMPILATION BLOCK ####### */ | ||||
| #if COMPILE_MIDI_IN	 | ||||
| 	 | ||||
| public: | ||||
| 	 | ||||
| 	bool read(); | ||||
| 	bool read(const byte Channel); | ||||
| 	 | ||||
| 	// Getters
 | ||||
| 	kMIDIType getType(); | ||||
| 	byte getChannel(); | ||||
| 	byte getData1(); | ||||
| 	byte getData2(); | ||||
| 	byte * getSysExArray(); | ||||
| 	bool check(); | ||||
| 	 | ||||
| 	byte getInputChannel() { return mInputChannel; } | ||||
| 	 | ||||
| 	// Setters
 | ||||
| 	void setInputChannel(const byte Channel); | ||||
| 	 | ||||
| 	 | ||||
| #if USE_CALLBACKS | ||||
| 	 | ||||
| 	void setHandleNoteOff(void (*fptr)(byte ch, byte note, byte vel)); | ||||
| 	void setHandleNoteOn(void (*fptr)(byte ch, byte note, byte vel)); | ||||
| 	void setHandleAfterTouchPoly(void (*fptr)(byte ch, byte note, byte vel)); | ||||
| 	void setHandleControlChange(void (*fptr)(byte ch, byte, byte)); | ||||
| 	void setHandleProgramChange(void (*fptr)(byte ch, byte)); | ||||
| 	void setHandleAfterTouchChannel(void (*fptr)(byte ch, byte)); | ||||
| 	void setHandlePitchBend(void (*fptr)(byte ch, word)); | ||||
| 	void setHandleSystemExclusive(void (*fptr)(byte * array, byte size)); | ||||
| 	void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); | ||||
| 	void setHandleSongPosition(void (*fptr)(word beats)); | ||||
| 	void setHandleSongSelect(void (*fptr)(byte song_number)); | ||||
| 	void setHandleTuneRequest(void (*fptr)(void)); | ||||
| 	void setHandleClock(void (*fptr)(void)); | ||||
| 	void setHandleStart(void (*fptr)(void)); | ||||
| 	void setHandleContinue(void (*fptr)(void)); | ||||
| 	void setHandleStop(void (*fptr)(void)); | ||||
| 	void setHandleActiveSensing(void (*fptr)(void)); | ||||
| 	void setHandleSystemReset(void (*fptr)(void)); | ||||
| 	 | ||||
| 	void disconnectCallbackFromType(kMIDIType Type); | ||||
| 	 | ||||
| #endif // USE_CALLBACKS
 | ||||
| 	 | ||||
| private: | ||||
| 	 | ||||
| 	inline const kMIDIType getTypeFromStatusByte(const byte inStatus) { | ||||
| 		if ((inStatus < 0x80)  | ||||
| 			|| (inStatus == 0xF4)  | ||||
| 			|| (inStatus == 0xF5)  | ||||
| 			|| (inStatus == 0xF9)  | ||||
| 			|| (inStatus == 0xFD)) return InvalidType; // data bytes and undefined.
 | ||||
| 		if (inStatus < 0xF0) return (kMIDIType)(inStatus & 0xF0);	// Channel message, remove channel nibble.
 | ||||
| 		else return (kMIDIType)inStatus; | ||||
| 	} | ||||
| 	 | ||||
| 	bool input_filter(byte inChannel); | ||||
| 	bool parse(byte inChannel); | ||||
| 	void reset_input_attributes(); | ||||
| 	 | ||||
| 	// Attributes
 | ||||
| 	byte			mRunningStatus_RX; | ||||
| 	byte			mInputChannel; | ||||
| 	 | ||||
| 	byte			mPendingMessage[MIDI_SYSEX_ARRAY_SIZE]; | ||||
| 	byte			mPendingMessageExpectedLenght; | ||||
| 	byte			mPendingMessageIndex; | ||||
| 	 | ||||
| 	midimsg			mMessage; | ||||
| 	 | ||||
| #if USE_CALLBACKS | ||||
| 	 | ||||
| 	void launchCallback(); | ||||
| 	 | ||||
| 	void (*mNoteOffCallback)(byte ch, byte note, byte vel); | ||||
| 	void (*mNoteOnCallback)(byte ch, byte note, byte vel); | ||||
| 	void (*mAfterTouchPolyCallback)(byte ch, byte note, byte vel); | ||||
| 	void (*mControlChangeCallback)(byte ch, byte, byte); | ||||
| 	void (*mProgramChangeCallback)(byte ch, byte); | ||||
| 	void (*mAfterTouchChannelCallback)(byte ch, byte); | ||||
| 	void (*mPitchBendCallback)(byte ch, word); | ||||
| 	void (*mSystemExclusiveCallback)(byte * array, byte size); | ||||
| 	void (*mTimeCodeQuarterFrameCallback)(byte data); | ||||
| 	void (*mSongPositionCallback)(word beats); | ||||
| 	void (*mSongSelectCallback)(byte song_number); | ||||
| 	void (*mTuneRequestCallback)(void); | ||||
| 	void (*mClockCallback)(void); | ||||
| 	void (*mStartCallback)(void); | ||||
| 	void (*mContinueCallback)(void); | ||||
| 	void (*mStopCallback)(void); | ||||
| 	void (*mActiveSensingCallback)(void); | ||||
| 	void (*mSystemResetCallback)(void); | ||||
| 	 | ||||
| #endif // USE_CALLBACKS
 | ||||
| 	 | ||||
| 	 | ||||
| #endif // COMPILE_MIDI_IN
 | ||||
| 	 | ||||
| 
 | ||||
| /* ####### THRU COMPILATION BLOCK ####### */ | ||||
| #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
 | ||||
| 	 | ||||
| public: | ||||
| 	 | ||||
| 	// Getters
 | ||||
| 	kThruFilterMode getFilterMode() { return mThruFilterMode; } | ||||
| 	bool getThruState() { return mThruActivated; } | ||||
| 	 | ||||
| 	 | ||||
| 	// Setters
 | ||||
| 	void turnThruOn(kThruFilterMode inThruFilterMode = Full); | ||||
| 	void turnThruOff(); | ||||
| 	 | ||||
| 	void setThruFilterMode(const byte inThruFilterMode);	// For compatibility only, avoid in future programs.
 | ||||
| 	void setThruFilterMode(const kThruFilterMode inThruFilterMode); | ||||
| 	 | ||||
| 	 | ||||
| private: | ||||
| 	 | ||||
| 	void thru_filter(byte inChannel); | ||||
| 	 | ||||
| 	bool				mThruActivated; | ||||
| 	kThruFilterMode		mThruFilterMode; | ||||
| 	 | ||||
| #endif // Thru
 | ||||
| 	 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| extern MIDI_Class usbMIDI; | ||||
| 
 | ||||
| #endif // _TEENSY_LIB_MIDI_USB_H_
 | ||||
|  | @ -0,0 +1,84 @@ | |||
| #ifndef usb_serial_h__ | ||||
| #define usb_serial_h__ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C"{ | ||||
| #endif | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Configurable Options | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| #define VENDOR_ID               0x16C0 | ||||
| #define PRODUCT_ID              0x0485 | ||||
| #define TRANSMIT_FLUSH_TIMEOUT  4   /* in milliseconds */ | ||||
| #define TRANSMIT_TIMEOUT        25   /* in milliseconds */ | ||||
| 
 | ||||
| 
 | ||||
| /**************************************************************************
 | ||||
|  * | ||||
|  *  Endpoint Buffer Configuration | ||||
|  * | ||||
|  **************************************************************************/ | ||||
| 
 | ||||
| // These buffer sizes are best for most applications, but perhaps if you
 | ||||
| // want more buffering on some endpoint at the expense of others, this
 | ||||
| // is where you can make such changes.  The AT90USB162 has only 176 bytes
 | ||||
| // of DPRAM (USB buffers) and only endpoints 3 & 4 can double buffer.
 | ||||
| 
 | ||||
| 
 | ||||
| // 0: control
 | ||||
| // 1: debug IN
 | ||||
| // 2: debug OUT
 | ||||
| // 3: midi IN
 | ||||
| // 4: midi OUT
 | ||||
| 
 | ||||
| #if defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) | ||||
| 
 | ||||
| #define STR_PRODUCT             L"Teensy MIDI" | ||||
| #define ENDPOINT0_SIZE          64 | ||||
| 
 | ||||
| #define DEBUG_INTERFACE		1 | ||||
| #define DEBUG_TX_ENDPOINT	1 | ||||
| #define DEBUG_TX_SIZE		64 | ||||
| #define DEBUG_TX_BUFFER		EP_DOUBLE_BUFFER | ||||
| #define DEBUG_TX_INTERVAL	1 | ||||
| #define DEBUG_RX_ENDPOINT	2 | ||||
| #define DEBUG_RX_SIZE		32 | ||||
| #define DEBUG_RX_BUFFER		EP_DOUBLE_BUFFER | ||||
| #define DEBUG_RX_INTERVAL	2 | ||||
| 
 | ||||
| #define MIDI_INTERFACE		0 | ||||
| #define MIDI_TX_ENDPOINT	3 | ||||
| #define MIDI_TX_SIZE		64 | ||||
| #define MIDI_TX_BUFFER		EP_DOUBLE_BUFFER | ||||
| #define MIDI_RX_ENDPOINT	4 | ||||
| #define MIDI_RX_SIZE		64 | ||||
| #define MIDI_RX_BUFFER		EP_DOUBLE_BUFFER | ||||
| 
 | ||||
| #define NUM_ENDPOINTS		5 | ||||
| #define NUM_INTERFACE		2 | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // setup
 | ||||
| void usb_init(void);			// initialize everything
 | ||||
| void usb_shutdown(void);		// shut off USB
 | ||||
| 
 | ||||
| // variables
 | ||||
| extern volatile uint8_t usb_configuration; | ||||
| extern volatile uint8_t usb_suspended; | ||||
| extern volatile uint8_t debug_flush_timer; | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
		Reference in New Issue
	
	 Francois Best
						Francois Best