void usb_send_func(MidiDevice * device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2) { MIDI_EventPacket_t event; event.Data1 = byte0; event.Data2 = byte1; event.Data3 = byte2; //if the length is undefined we assume it is a SYSEX message if (midi_packet_length(byte0) == UNDEFINED) { switch(cnt) { case 3: if (byte2 == SYSEX_END) event.Event = MIDI_EVENT(0, SYSEX_ENDS_IN_3); else event.Event = MIDI_EVENT(0, SYSEX_START_OR_CONT); break; case 2: if (byte1 == SYSEX_END) event.Event = MIDI_EVENT(0, SYSEX_ENDS_IN_2); else event.Event = MIDI_EVENT(0, SYSEX_START_OR_CONT); break; case 1: if (byte0 == SYSEX_END) event.Event = MIDI_EVENT(0, SYSEX_ENDS_IN_1); else event.Event = MIDI_EVENT(0, SYSEX_START_OR_CONT); break; default: return; //invalid cnt } } else { //deal with 'system common' messages //TODO are there any more? switch(byte0 & 0xF0){ case MIDI_SONGPOSITION: event.Event = MIDI_EVENT(0, SYS_COMMON_3); break; case MIDI_SONGSELECT: case MIDI_TC_QUARTERFRAME: event.Event = MIDI_EVENT(0, SYS_COMMON_2); break; default: event.Event = MIDI_EVENT(0, byte0); break; } } MIDI_Device_SendEventPacket(&USB_MIDI_Interface, &event); MIDI_Device_Flush(&USB_MIDI_Interface); MIDI_Device_USBTask(&USB_MIDI_Interface); USB_USBTask(); }
/** Task to manage an enumerated USB MIDI device once connected, to display received * note events from the host and send note changes in response to the board's joystick. */ void JoystickHost_Task(void) { if (USB_HostState != HOST_STATE_Configured) return; MIDI_EventPacket_t MIDIEvent; while (MIDI_Host_ReceiveEventPacket(&Keyboard_MIDI_Interface, &MIDIEvent)) { bool NoteOnEvent = (MIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON)); bool NoteOffEvent = (MIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_OFF)); /* Display note events from the host */ if (NoteOnEvent || NoteOffEvent) { printf_P(PSTR("MIDI Note %s - Channel %d, Pitch %d, Velocity %d\r\n"), NoteOnEvent ? "On" : "Off", ((MIDIEvent.Data1 & 0x0F) + 1), MIDIEvent.Data2, MIDIEvent.Data3); } } CheckJoystickMovement(); }
/** Main program entry point. This routine contains the overall program flow, including initial * setup of all components and the main program loop. */ int main(void) { SetupHardware(); LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); for (;;) { CheckJoystickMovement(); MIDI_EventPacket_t ReceivedMIDIEvent; while (MIDI_Device_ReceiveEventPacket(&Keyboard_MIDI_Interface, &ReceivedMIDIEvent)) { if ((ReceivedMIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON)) && (ReceivedMIDIEvent.Data3 > 0)) LEDs_SetAllLEDs(ReceivedMIDIEvent.Data2 > 64 ? LEDS_LED1 : LEDS_LED2); else LEDs_SetAllLEDs(LEDS_NO_LEDS); } MIDI_Device_USBTask(&Keyboard_MIDI_Interface); USB_USBTask(); } }
/** Main program entry point. This routine contains the overall program flow, including initial * setup of all components and the main program loop. */ int main(void) { SetupHardware(); LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); for (;;) { MIDI_EventPacket_t ReceivedMIDIEvent; if (MIDI_Device_ReceiveEventPacket(&Keyboard_MIDI_Interface, &ReceivedMIDIEvent)) { if ((ReceivedMIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON)) && ((ReceivedMIDIEvent.Data1 & 0x0F) == 0)) { DDSNoteData* LRUNoteStruct = &NoteData[0]; /* Find a free entry in the note table to use for the note being turned on */ for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++) { /* Check if the note is unused */ if (!(NoteData[i].Pitch)) { /* If a note is unused, it's age is essentially infinite - always prefer unused not entries */ LRUNoteStruct = &NoteData[i]; break; } else if (NoteData[i].LRUAge >= LRUNoteStruct->LRUAge) { /* If an older entry that the current entry has been found, prefer overwriting that one */ LRUNoteStruct = &NoteData[i]; } NoteData[i].LRUAge++; } /* Update the oldest note entry with the new note data and reset its age */ LRUNoteStruct->Pitch = ReceivedMIDIEvent.Data2; LRUNoteStruct->TableIncrement = (uint32_t)(BASE_INCREMENT * SCALE_FACTOR) + ((uint32_t)(BASE_INCREMENT * NOTE_OCTIVE_RATIO * SCALE_FACTOR) * (ReceivedMIDIEvent.Data2 - BASE_PITCH_INDEX)); LRUNoteStruct->TablePosition = 0; LRUNoteStruct->LRUAge = 0; /* Turn on indicator LED to indicate note generation activity */ LEDs_SetAllLEDs(LEDS_LED1); } else if ((ReceivedMIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_OFF)) && ((ReceivedMIDIEvent.Data1 & 0x0F) == 0)) { bool FoundActiveNote = false; /* Find the note in the note table to turn off */ for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++) { if (NoteData[i].Pitch == ReceivedMIDIEvent.Data2) NoteData[i].Pitch = 0; else if (NoteData[i].Pitch) FoundActiveNote = true; } /* If all notes off, turn off the indicator LED */ if (!(FoundActiveNote)) LEDs_SetAllLEDs(LEDS_NO_LEDS); } } MIDI_Device_USBTask(&Keyboard_MIDI_Interface); USB_USBTask(); } }
// .Event = MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON), // VirtualCable 0 // .Data1 = MIDI_COMMAND_NOTE_ON | MIDI_CHANNEL(1), // .Data2 = 0b00000001, // .Data3 = vel, // }; // MIDI_Device_SendEventPacket(&Keyboard_MIDI_Interface, &MIDIEvent); // отправить пакет // MIDI_Device_Flush(&Keyboard_MIDI_Interface); // } // отправить в кабель 0 инструмент instr CONTROL CHANGE с заданным vel void cc_send(int instr, int vel) { MIDI_EventPacket_t MIDIEvent = (MIDI_EventPacket_t) // сформировать пакет { .Event = MIDI_EVENT(0, MIDI_COMMAND_CONTROL_CHANGE), // VirtualCable 0 .Data1 = MIDI_COMMAND_CONTROL_CHANGE | MIDI_CHANNEL(1), // канал 1 .Data2 = instr, .Data3 = vel, }; MIDI_Device_SendEventPacket(&Keyboard_MIDI_Interface, &MIDIEvent); // отправить пакет MIDI_Device_Flush(&Keyboard_MIDI_Interface); } /** установить адрес входа мультиплексора */ void MUX_address(int address) { if (address & 0b0001) PORTF |= (1<<PF4); else PORTF &= ~(1<<PF4); if (address & 0b0010) PORTF |= (1<<PF5); else PORTF &= ~(1<<PF5); if (address & 0b0100) PORTF |= (1<<PF6); else PORTF &= ~(1<<PF6);
/** Task to handle the generation of MIDI note change events in response to presses of the board joystick, and send them * to the host. */ void MIDI_Task(void) { static uint8_t PrevJoystickStatus; /* Device must be connected and configured for the task to run */ if (USB_DeviceState != DEVICE_STATE_Configured) return; Endpoint_SelectEndpoint(MIDI_STREAM_IN_EPADDR); if (Endpoint_IsINReady()) { uint8_t MIDICommand = 0; uint8_t MIDIPitch; uint8_t JoystickStatus = Joystick_GetStatus(); uint8_t JoystickChanges = (JoystickStatus ^ PrevJoystickStatus); /* Get board button status - if pressed use channel 10 (percussion), otherwise use channel 1 */ uint8_t Channel = ((Buttons_GetStatus() & BUTTONS_BUTTON1) ? MIDI_CHANNEL(10) : MIDI_CHANNEL(1)); if (JoystickChanges & JOY_LEFT) { MIDICommand = ((JoystickStatus & JOY_LEFT)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3C; } if (JoystickChanges & JOY_UP) { MIDICommand = ((JoystickStatus & JOY_UP)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3D; } if (JoystickChanges & JOY_RIGHT) { MIDICommand = ((JoystickStatus & JOY_RIGHT)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3E; } if (JoystickChanges & JOY_DOWN) { MIDICommand = ((JoystickStatus & JOY_DOWN)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3F; } if (JoystickChanges & JOY_PRESS) { MIDICommand = ((JoystickStatus & JOY_PRESS)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3B; } /* Check if a MIDI command is to be sent */ if (MIDICommand) { MIDI_EventPacket_t MIDIEvent = (MIDI_EventPacket_t) { .Event = MIDI_EVENT(0, MIDICommand), .Data1 = MIDICommand | Channel, .Data2 = MIDIPitch, .Data3 = MIDI_STANDARD_VELOCITY, }; /* Write the MIDI event packet to the endpoint */ Endpoint_Write_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL); /* Send the data in the endpoint to the host */ Endpoint_ClearIN(); } /* Save previous joystick value for next joystick change detection */ PrevJoystickStatus = JoystickStatus; } /* Select the MIDI OUT stream */ Endpoint_SelectEndpoint(MIDI_STREAM_OUT_EPADDR); /* Check if a MIDI command has been received */ if (Endpoint_IsOUTReceived()) { MIDI_EventPacket_t MIDIEvent; /* Read the MIDI event packet from the endpoint */ Endpoint_Read_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL); /* Check to see if the sent command is a note on message with a non-zero velocity */ if ((MIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON)) && (MIDIEvent.Data3 > 0)) { /* Change LEDs depending on the pitch of the sent note */ LEDs_SetAllLEDs(MIDIEvent.Data2 > 64 ? LEDS_LED1 : LEDS_LED2); } else { /* Turn off all LEDs in response to non Note On messages */ LEDs_SetAllLEDs(LEDS_NO_LEDS); } /* If the endpoint is now empty, clear the bank */ if (!(Endpoint_BytesInEndpoint())) { /* Clear the endpoint ready for new packet */ Endpoint_ClearOUT(); } } }
// Parse via Arduino/Serial ISR(USART1_RX_vect, ISR_BLOCK) { // Device must be connected and configured for the task to run if (USB_DeviceState != DEVICE_STATE_Configured) return; const uint8_t extracted = UDR1; // Borrowed + Modified from Francois Best's Arduino MIDI Library // https://github.com/FortySevenEffects/arduino_midi_library if (mPendingMessageIndex == 0) { // Start a new pending message mPendingMessage[0] = extracted; // Check for running status first if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX))) { // Only these types allow Running Status // 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 we received another status byte, so the running status does not apply here. // It will be updated upon completion of this message. } 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. mCompleteMessage.Event = MIDI_EVENT(0, getTypeFromStatusByte(mPendingMessage[0])); mCompleteMessage.Data1 = mPendingMessage[0]; mCompleteMessage.Data2 = 0; mCompleteMessage.Data3 = 0; mPendingMessageValid = true; // We still need to reset these mPendingMessageIndex = 0; mPendingMessageExpectedLength = 0; return; break; // 2 bytes messages case ProgramChange: case AfterTouchChannel: case TimeCodeQuarterFrame: case SongSelect: mPendingMessageExpectedLength = 2; break; // 3 bytes messages case NoteOn: case NoteOff: case ControlChange: case PitchBend: case AfterTouchPoly: case SongPosition: mPendingMessageExpectedLength = 3; break; case SystemExclusive: break; case InvalidType: default: // Something bad happened break; } if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1)) { // Reception complete mCompleteMessage.Event = MIDI_EVENT(0, getTypeFromStatusByte(mPendingMessage[0])); mCompleteMessage.Data1 = mPendingMessage[0]; // status = channel + type mCompleteMessage.Data2 = mPendingMessage[1]; // Save Data3 only if applicable if (mPendingMessageExpectedLength == 3) mCompleteMessage.Data3 = mPendingMessage[2]; else mCompleteMessage.Data3 = 0; mPendingMessageIndex = 0; mPendingMessageExpectedLength = 0; mPendingMessageValid = true; return; } else { // Waiting for more data mPendingMessageIndex++; } } 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: // 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. mCompleteMessage.Event = MIDI_EVENT(0, getTypeFromStatusByte(extracted)); mCompleteMessage.Data1 = extracted; mCompleteMessage.Data2 = 0; mCompleteMessage.Data3 = 0; mPendingMessageValid = true; return; 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 >= (mPendingMessageExpectedLength - 1)) { mCompleteMessage.Event = MIDI_EVENT(0, getTypeFromStatusByte(mPendingMessage[0])); mCompleteMessage.Data1 = mPendingMessage[0]; mCompleteMessage.Data2 = mPendingMessage[1]; // Save Data3 only if applicable if (mPendingMessageExpectedLength == 3) mCompleteMessage.Data3 = mPendingMessage[2]; else mCompleteMessage.Data3 = 0; // Reset local variables mPendingMessageIndex = 0; mPendingMessageExpectedLength = 0; mPendingMessageValid = true; // Activate running status (if enabled for the received type) switch (getTypeFromStatusByte(mPendingMessage[0])) { 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; } else { // Not complete? Then update the index of the pending message. mPendingMessageIndex++; } } }
/** Task to read in note on/off messages from the attached MIDI device and print it to the serial port. * When the board joystick or buttons are pressed, note on/off messages are sent to the attached device. */ void MIDIHost_Task(void) { if (USB_HostState != HOST_STATE_Configured) return; Pipe_SelectPipe(MIDI_DATA_IN_PIPE); Pipe_Unfreeze(); if (Pipe_IsINReceived()) { MIDI_EventPacket_t MIDIEvent; Pipe_Read_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL); if (!(Pipe_BytesInPipe())) Pipe_ClearIN(); bool NoteOnEvent = (MIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_ON)); bool NoteOffEvent = (MIDIEvent.Event == MIDI_EVENT(0, MIDI_COMMAND_NOTE_OFF)); if (NoteOnEvent || NoteOffEvent) { printf_P(PSTR("MIDI Note %s - Channel %d, Pitch %d, Velocity %d\r\n"), NoteOnEvent ? "On" : "Off", ((MIDIEvent.Data1 & 0x0F) + 1), MIDIEvent.Data2, MIDIEvent.Data3); } } Pipe_Freeze(); Pipe_SelectPipe(MIDI_DATA_OUT_PIPE); Pipe_Unfreeze(); if (Pipe_IsOUTReady()) { uint8_t MIDICommand = 0; uint8_t MIDIPitch; static uint8_t PrevJoystickStatus; uint8_t JoystickStatus = Joystick_GetStatus(); uint8_t JoystickChanges = (JoystickStatus ^ PrevJoystickStatus); /* Get board button status - if pressed use channel 10 (percussion), otherwise use channel 1 */ uint8_t Channel = ((Buttons_GetStatus() & BUTTONS_BUTTON1) ? MIDI_CHANNEL(10) : MIDI_CHANNEL(1)); if (JoystickChanges & JOY_LEFT) { MIDICommand = ((JoystickStatus & JOY_LEFT)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3C; } if (JoystickChanges & JOY_UP) { MIDICommand = ((JoystickStatus & JOY_UP)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3D; } if (JoystickChanges & JOY_RIGHT) { MIDICommand = ((JoystickStatus & JOY_RIGHT)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3E; } if (JoystickChanges & JOY_DOWN) { MIDICommand = ((JoystickStatus & JOY_DOWN)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3F; } if (JoystickChanges & JOY_PRESS) { MIDICommand = ((JoystickStatus & JOY_PRESS)? MIDI_COMMAND_NOTE_ON : MIDI_COMMAND_NOTE_OFF); MIDIPitch = 0x3B; } /* Check if a MIDI command is to be sent */ if (MIDICommand) { MIDI_EventPacket_t MIDIEvent = (MIDI_EventPacket_t) { .Event = MIDI_EVENT(0, MIDICommand), .Data1 = MIDICommand | Channel, .Data2 = MIDIPitch, .Data3 = MIDI_STANDARD_VELOCITY, }; /* Write the MIDI event packet to the pipe */ Pipe_Write_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL); /* Send the data in the pipe to the device */ Pipe_ClearOUT(); } Pipe_Freeze(); /* Save previous joystick value for next joystick change detection */ PrevJoystickStatus = JoystickStatus; } }