This commit is contained in:
RogueMaster
2022-11-10 01:36:59 -05:00
parent c9774a84c6
commit ebce6ea09c
17 changed files with 1644 additions and 0 deletions
+1
View File
@@ -17,6 +17,7 @@
- [Barcode generator: rfct, ux improvements, implement EAN-8. #154 (By msvsergey)](https://github.com/DarkFlippers/unleashed-firmware/pull/154)
- Settings: Rename from App [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/409)
- Added: [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC)
- Added: [USB Midi (By DrZlo13)](https://github.com/DrZlo13/flipper-zero-usb-midi)
<table width="100%" border="0" cellspacing="0">
<tr> <td colspan=2> <h3>This software is for experimental purposes only and is not meant for any illegal activity/purposes. We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.</h3> </td> </tr>
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+52
View File
@@ -0,0 +1,52 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
@@ -0,0 +1,14 @@
App(
appid="USB_Midi",
name="USB Midi",
apptype=FlipperAppType.PLUGIN,
entry_point="usb_midi_app",
requires=[
"gui",
],
stack_size=4 * 1024,
order=20,
fap_icon="usb_midi.png",
fap_category="Music",
fap_icon_assets="icons",
)
@@ -0,0 +1,3 @@
#include <stdint.h>
#define SYSEX_BUFFER_LEN 16
@@ -0,0 +1,144 @@
#include "message.h"
/** Returns the data within the MidiEvent as a NoteOffEvent struct */
NoteOffEvent AsNoteOff(MidiEvent* event) {
NoteOffEvent m;
m.channel = event->channel;
m.note = event->data[0];
m.velocity = event->data[1];
return m;
}
/** Returns the data within the MidiEvent as a NoteOnEvent struct */
NoteOnEvent AsNoteOn(MidiEvent* event) {
NoteOnEvent m;
m.channel = event->channel;
m.note = event->data[0];
m.velocity = event->data[1];
return m;
}
/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */
PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event) {
PolyphonicKeyPressureEvent m;
m.channel = event->channel;
m.note = event->data[0];
m.pressure = event->data[1];
return m;
}
/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/
ControlChangeEvent AsControlChange(MidiEvent* event) {
ControlChangeEvent m;
m.channel = event->channel;
m.control_number = event->data[0];
m.value = event->data[1];
return m;
}
/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/
ProgramChangeEvent AsProgramChange(MidiEvent* event) {
ProgramChangeEvent m;
m.channel = event->channel;
m.program = event->data[0];
return m;
}
/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/
ChannelPressureEvent AsChannelPressure(MidiEvent* event) {
ChannelPressureEvent m;
m.channel = event->channel;
m.pressure = event->data[0];
return m;
}
/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/
PitchBendEvent AsPitchBend(MidiEvent* event) {
PitchBendEvent m;
m.channel = event->channel;
m.value = ((uint16_t)(event->data[1]) << 7) + (event->data[0] - 8192);
return m;
}
SystemExclusiveEvent AsSystemExclusive(MidiEvent* event) {
SystemExclusiveEvent m;
m.length = event->sysex_message_len;
for(int i = 0; i < SYSEX_BUFFER_LEN; i++) {
m.data[i] = 0;
if(i < m.length) {
m.data[i] = event->sysex_data[i];
}
}
return m;
}
MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event) {
MTCQuarterFrameEvent m;
m.message_type = (event->data[0] & 0x70) >> 4;
m.value = (event->data[0]) & 0x0f;
return m;
}
SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event) {
SongPositionPointerEvent m;
m.position = ((uint16_t)(event->data[1]) << 7) | (event->data[0]);
return m;
}
SongSelectEvent AsSongSelect(MidiEvent* event) {
SongSelectEvent m;
m.song = event->data[0];
return m;
}
AllSoundOffEvent AsAllSoundOff(MidiEvent* event) {
AllSoundOffEvent m;
m.channel = event->channel;
return m;
}
ResetAllControllersEvent AsResetAllControllers(MidiEvent* event) {
ResetAllControllersEvent m;
m.channel = event->channel;
m.value = event->data[1];
return m;
}
LocalControlEvent AsLocalControl(MidiEvent* event) {
LocalControlEvent m;
m.channel = event->channel;
m.local_control_off = (event->data[1] == 0);
m.local_control_on = (event->data[1] == 127);
return m;
}
AllNotesOffEvent AsAllNotesOff(MidiEvent* event) {
AllNotesOffEvent m;
m.channel = event->channel;
return m;
}
OmniModeOffEvent AsOmniModeOff(MidiEvent* event) {
OmniModeOffEvent m;
m.channel = event->channel;
return m;
}
OmniModeOnEvent AsOmniModeOn(MidiEvent* event) {
OmniModeOnEvent m;
m.channel = event->channel;
return m;
}
MonoModeOnEvent AsMonoModeOn(MidiEvent* event) {
MonoModeOnEvent m;
m.channel = event->channel;
m.num_channels = event->data[1];
return m;
}
PolyModeOnEvent AsPolyModeOn(MidiEvent* event) {
PolyModeOnEvent m;
m.channel = event->channel;
return m;
}
@@ -0,0 +1,251 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
typedef enum {
NoteOff, /**< & */
NoteOn, /**< & */
PolyphonicKeyPressure, /**< & */
ControlChange, /**< & */
ProgramChange, /**< & */
ChannelPressure, /**< & */
PitchBend, /**< & */
SystemCommon, /**< & */
SystemRealTime, /**< & */
ChannelMode, /**< & */
MessageLast, /**< & */
} MidiMessageType;
typedef enum {
SystemExclusive, /**< & */
MTCQuarterFrame, /**< & */
SongPositionPointer, /**< & */
SongSelect, /**< & */
SCUndefined0, /**< & */
SCUndefined1, /**< & */
TuneRequest, /**< & */
SysExEnd, /**< & */
SystemCommonLast, /**< & */
} SystemCommonType;
typedef enum {
TimingClock, /**< & */
SRTUndefined0, /**< & */
Start, /**< & */
Continue, /**< & */
Stop, /**< & */
SRTUndefined1, /**< & */
ActiveSensing, /**< & */
Reset, /**< & */
SystemRealTimeLast, /**< & */
} SystemRealTimeType;
typedef enum {
AllSoundOff, /**< & */
ResetAllControllers, /**< & */
LocalControl, /**< & */
AllNotesOff, /**< & */
OmniModeOff, /**< & */
OmniModeOn, /**< & */
MonoModeOn, /**< & */
PolyModeOn, /**< & */
ChannelModeLast, /**< & */
} ChannelModeType;
/** Struct containing note, and velocity data for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t note; /**< & */
uint8_t velocity; /**< & */
} NoteOffEvent;
/** Struct containing note, and velocity data for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t note; /**< & */
uint8_t velocity; /**< & */
} NoteOnEvent;
/** Struct containing note, and pressure data for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel;
uint8_t note;
uint8_t pressure;
} PolyphonicKeyPressureEvent;
/** Struct containing control number, and value for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t control_number; /**< & */
uint8_t value; /**< & */
} ControlChangeEvent;
/** Struct containing new program number, for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t program; /**< & */
} ProgramChangeEvent;
/** Struct containing pressure (aftertouch), for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t pressure; /**< & */
} ChannelPressureEvent;
/** Struct containing pitch bend value for a given channel.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
int16_t value; /**< & */
} PitchBendEvent;
/** Struct containing sysex data.
Can be made from MidiEvent
*/
typedef struct {
int length;
uint8_t data[SYSEX_BUFFER_LEN]; /**< & */
} SystemExclusiveEvent;
/** Struct containing QuarterFrame data.
Can be made from MidiEvent
*/
typedef struct {
uint8_t message_type; /**< & */
uint8_t value; /**< & */
} MTCQuarterFrameEvent;
/** Struct containing song position data.
Can be made from MidiEvent
*/
typedef struct {
uint16_t position; /**< & */
} SongPositionPointerEvent;
/** Struct containing song select data.
Can be made from MidiEvent
*/
typedef struct {
uint8_t song; /**< & */
} SongSelectEvent;
/** Struct containing sound off data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
} AllSoundOffEvent;
/** Struct containing ResetAllControllersEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t value; /**< & */
} ResetAllControllersEvent;
/** Struct containing LocalControlEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
bool local_control_off; /**< & */
bool local_control_on; /**< & */
} LocalControlEvent;
/** Struct containing AllNotesOffEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
} AllNotesOffEvent;
/** Struct containing OmniModeOffEvent data.
* Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
} OmniModeOffEvent;
/** Struct containing OmniModeOnEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
} OmniModeOnEvent;
/** Struct containing MonoModeOnEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
uint8_t num_channels; /**< & */
} MonoModeOnEvent;
/** Struct containing PolyModeOnEvent data.
Can be made from MidiEvent
*/
typedef struct {
int channel; /**< & */
} PolyModeOnEvent;
/** Simple MidiEvent with message type, channel, and data[2] members.
*/
typedef struct {
MidiMessageType type;
int channel;
uint8_t data[2];
uint8_t sysex_data[SYSEX_BUFFER_LEN];
uint8_t sysex_message_len;
SystemCommonType sc_type;
SystemRealTimeType srt_type;
ChannelModeType cm_type;
} MidiEvent;
/** Returns the data within the MidiEvent as a NoteOffEvent struct */
NoteOffEvent AsNoteOff(MidiEvent* event);
/** Returns the data within the MidiEvent as a NoteOnEvent struct */
NoteOnEvent AsNoteOn(MidiEvent* event);
/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */
PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event);
/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/
ControlChangeEvent AsControlChange(MidiEvent* event);
/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/
ProgramChangeEvent AsProgramChange(MidiEvent* event);
/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/
ChannelPressureEvent AsChannelPressure(MidiEvent* event);
/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/
PitchBendEvent AsPitchBend(MidiEvent* event);
SystemExclusiveEvent AsSystemExclusive(MidiEvent* event);
MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event);
SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event);
SongSelectEvent AsSongSelect(MidiEvent* event);
AllSoundOffEvent AsAllSoundOff(MidiEvent* event);
ResetAllControllersEvent AsResetAllControllers(MidiEvent* event);
LocalControlEvent AsLocalControl(MidiEvent* event);
AllNotesOffEvent AsAllNotesOff(MidiEvent* event);
OmniModeOffEvent AsOmniModeOff(MidiEvent* event);
OmniModeOnEvent AsOmniModeOn(MidiEvent* event);
MonoModeOnEvent AsMonoModeOn(MidiEvent* event);
PolyModeOnEvent AsPolyModeOn(MidiEvent* event);
+149
View File
@@ -0,0 +1,149 @@
#include <stdlib.h>
#include "parser.h"
typedef enum {
ParserEmpty,
ParserHasStatus,
ParserHasData0,
ParserSysEx,
} ParserState;
const uint8_t kStatusByteMask = 0x80;
const uint8_t kMessageMask = 0x70;
const uint8_t kDataByteMask = 0x7F;
const uint8_t kSystemCommonMask = 0xF0;
const uint8_t kChannelMask = 0x0F;
const uint8_t kRealTimeMask = 0xF8;
const uint8_t kSystemRealTimeMask = 0x07;
struct MidiParser {
MidiMessageType status;
ParserState state;
MidiEvent incoming_message;
};
MidiParser* midi_parser_alloc(void) {
MidiParser* parser = malloc(sizeof(MidiParser));
parser->incoming_message.type = MessageLast;
parser->state = ParserEmpty;
return parser;
}
void midi_parser_free(MidiParser* parser) {
free(parser);
}
bool midi_parser_parse(MidiParser* parser, uint8_t byte) {
bool parsed = false;
MidiEvent* event = &parser->incoming_message;
switch(parser->state) {
case ParserEmpty:
// check byte for valid Status Byte
if(byte & kStatusByteMask) {
// Get MessageType, and Channel
event->channel = byte & kChannelMask;
event->type = (MidiMessageType)((byte & kMessageMask) >> 4);
// Validate, and move on.
if(event->type < MessageLast) {
parser->state = ParserHasStatus;
// Mark this status byte as running_status
parser->status = event->type;
if(parser->status == SystemCommon) {
event->channel = 0;
//system real time = 1111 1xxx
if(byte & 0x08) {
event->type = SystemRealTime;
parser->status = SystemRealTime;
event->srt_type = (SystemRealTimeType)(byte & kSystemRealTimeMask);
//short circuit to start
parser->state = ParserEmpty;
//queue_.push(incoming_message_);
parsed = true;
}
//system common
else {
event->sc_type = (SystemCommonType)(byte & 0x07);
//sysex
if(event->sc_type == SystemExclusive) {
parser->state = ParserSysEx;
event->sysex_message_len = 0;
}
//short circuit
else if(event->sc_type > SongSelect) {
parser->state = ParserEmpty;
//queue_.push(incoming_message_);
parsed = true;
}
}
}
}
// Else we'll keep waiting for a valid incoming status byte
} else {
// Handle as running status
event->type = parser->status;
event->data[0] = byte & kDataByteMask;
parser->state = ParserHasData0;
}
break;
case ParserHasStatus:
if((byte & kStatusByteMask) == 0) {
event->data[0] = byte & kDataByteMask;
if(parser->status == ChannelPressure || parser->status == ProgramChange ||
event->sc_type == MTCQuarterFrame || event->sc_type == SongSelect) {
//these are just one data byte, so we short circuit back to start
parser->state = ParserEmpty;
//queue_.push(incoming_message_);
parsed = true;
} else {
parser->state = ParserHasData0;
}
//ChannelModeMessages (reserved Control Changes)
if(parser->status == ControlChange && event->data[0] > 119) {
event->type = ChannelMode;
parser->status = ChannelMode;
event->cm_type = (ChannelModeType)(event->data[0] - 120);
}
} else {
// invalid message go back to start ;p
parser->state = ParserEmpty;
}
break;
case ParserHasData0:
if((byte & kStatusByteMask) == 0) {
event->data[1] = byte & kDataByteMask;
// At this point the message is valid, and we can add this MidiEvent to the queue
//queue_.push(incoming_message_);
parsed = true;
}
// Regardless, of whether the data was valid or not we go back to empty
// because either the message is queued for handling or its not.
parser->state = ParserEmpty;
break;
case ParserSysEx:
// end of sysex
if(byte == 0xf7) {
parser->state = ParserEmpty;
//queue_.push(incoming_message_);
parsed = true;
} else {
if(event->sysex_message_len < SYSEX_BUFFER_LEN) {
event->sysex_data[event->sysex_message_len] = byte;
event->sysex_message_len++;
}
}
break;
default:
break;
}
return parsed;
}
MidiEvent* midi_parser_get_message(MidiParser* parser) {
return &parser->incoming_message;
}
@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "message.h"
typedef struct MidiParser MidiParser;
MidiParser* midi_parser_alloc(void);
void midi_parser_free(MidiParser* parser);
bool midi_parser_parse(MidiParser* parser, uint8_t data);
MidiEvent* midi_parser_get_message(MidiParser* parser);
@@ -0,0 +1,40 @@
#include "usb_message.h"
CodeIndex code_index_from_data(uint8_t data) {
return (CodeIndex)(data & 0b00001111);
}
uint8_t cable_id_from_data(uint8_t data) {
return (data >> 4) & 0b00001111;
}
uint8_t usb_message_data_size(CodeIndex code_index) {
uint8_t data_size = 0;
switch(code_index) {
case CodeIndexCommon1Byte:
/* case CodeIndexSysExEnd1Byte: */
case CodeIndexSingleByte:
data_size = 1;
break;
case CodeIndexSysEx2Byte:
case CodeIndexSysExEnd2Byte:
case CodeIndexProgramChange:
case CodeIndexChannelPressure:
data_size = 2;
break;
case CodeIndexSysEx3Byte:
case CodeIndexSysExStart:
case CodeIndexSysExEnd3Byte:
case CodeIndexNoteOff:
case CodeIndexNoteOn:
case CodeIndexPolyKeyPress:
case CodeIndexControlChange:
case CodeIndexPitchBendChange:
data_size = 3;
break;
default:
break;
}
return data_size;
}
@@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
typedef enum {
CodeIndexMisc = 0x0, /**< Reserved, MIDI Size: 1, 2, 3 */
CodeIndexCableEvent = 0x1, /**< Reserved, MIDI Size: 1, 2, 3 */
CodeIndexSysEx2Byte = 0x2, /**< MIDI Size: 2 */
CodeIndexSysEx3Byte = 0x3, /**< MIDI Size: 3 */
CodeIndexSysExStart = 0x4, /**< MIDI Size: 3 */
CodeIndexCommon1Byte = 0x5, /**< MIDI Size: 1 */
CodeIndexSysExEnd1Byte = 0x5, /**< MIDI Size: 1 */
CodeIndexSysExEnd2Byte = 0x6, /**< MIDI Size: 2 */
CodeIndexSysExEnd3Byte = 0x7, /**< MIDI Size: 3 */
CodeIndexNoteOff = 0x8, /**< MIDI Size: 3 */
CodeIndexNoteOn = 0x9, /**< MIDI Size: 3 */
CodeIndexPolyKeyPress = 0xA, /**< MIDI Size: 3 */
CodeIndexControlChange = 0xB, /**< MIDI Size: 3 */
CodeIndexProgramChange = 0xC, /**< MIDI Size: 2 */
CodeIndexChannelPressure = 0xD, /**< MIDI Size: 2 */
CodeIndexPitchBendChange = 0xE, /**< MIDI Size: 3 */
CodeIndexSingleByte = 0xF, /**< MIDI Size: 1 */
} CodeIndex;
CodeIndex code_index_from_data(uint8_t data);
uint8_t cable_id_from_data(uint8_t data);
uint8_t usb_message_data_size(CodeIndex code_index);
@@ -0,0 +1,234 @@
/** @defgroup usb_audio_defines USB Audio Type Definitions
@brief <b>Defined Constants and Types for the USB Audio Type Definitions</b>
@ingroup USB_defines
@version 1.0.0
@author @htmlonly &copy; @endhtmlonly 2014
Daniel Thompson <daniel@redfelineninja.org.uk>
Seb Holzapfel <schnommus@gmail.com>
@date 19 April 2014
LGPL License Terms @ref lgpl_license
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2014 Daniel Thompson <daniel@redfelineninja.org.uk>
* Copyright (C) 2018 Seb Holzapfel <schnommus@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#ifndef LIBOPENCM3_USB_AUDIO_H
#define LIBOPENCM3_USB_AUDIO_H
#include <stdint.h>
/*
* Definitions from the USB_AUDIO_ or usb_audio_ namespace come from:
* "Universal Serial Bus Class Definitions for Audio Devices, Revision 1.0"
*/
/* Table A-1: Audio Interface Class Code */
#define USB_CLASS_AUDIO 0x01
/* Table A-2: Audio Interface Subclass Codes */
#define USB_AUDIO_SUBCLASS_UNDEFINED 0x00
#define USB_AUDIO_SUBCLASS_CONTROL 0x01
#define USB_AUDIO_SUBCLASS_AUDIOSTREAMING 0x02
#define USB_AUDIO_SUBCLASS_MIDISTREAMING 0x03
/* Table A-4: Audio Class-specific Descriptor Types */
#define USB_AUDIO_DT_CS_UNDEFINED 0x20
#define USB_AUDIO_DT_CS_DEVICE 0x21
#define USB_AUDIO_DT_CS_CONFIGURATION 0x22
#define USB_AUDIO_DT_CS_STRING 0x23
#define USB_AUDIO_DT_CS_INTERFACE 0x24
#define USB_AUDIO_DT_CS_ENDPOINT 0x25
/* Table A-5: Audio Class-Specific AC Interface Descriptor Subtypes */
#define USB_AUDIO_TYPE_AC_DESCRIPTOR_UNDEFINED 0x00
#define USB_AUDIO_TYPE_HEADER 0x01
#define USB_AUDIO_TYPE_INPUT_TERMINAL 0x02
#define USB_AUDIO_TYPE_OUTPUT_TERMINAL 0x03
#define USB_AUDIO_TYPE_MIXER_UNIT 0x04
#define USB_AUDIO_TYPE_SELECTOR_UNIT 0x05
#define USB_AUDIO_TYPE_FEATURE_UNIT 0x06
#define USB_AUDIO_TYPE_PROCESSING_UNIT 0x07
#define USB_AUDIO_TYPE_EXTENSION_UNIT 0x08
/* Table 4-2: Class-Specific AC Interface Header Descriptor (head) */
struct usb_audio_header_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdADC;
uint16_t wTotalLength;
uint8_t bInCollection;
/* ... */
} __attribute__((packed));
/* Table 4-2: Class-Specific AC Interface Header Descriptor (body) */
struct usb_audio_header_descriptor_body {
/* ... */
uint8_t baInterfaceNr;
} __attribute__((packed));
/* Table 4-3: Input Terminal Descriptor */
struct usb_audio_input_terminal_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bTerminalID;
uint16_t wTerminalType;
uint8_t bAssocTerminal;
uint8_t bNrChannels;
uint16_t wChannelConfig;
uint8_t iChannelNames;
uint8_t iTerminal;
} __attribute__((packed));
/* Table 4-3: Output Terminal Descriptor */
struct usb_audio_output_terminal_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bTerminalID;
uint16_t wTerminalType;
uint8_t bAssocTerminal;
uint8_t bSourceID;
uint8_t iTerminal;
} __attribute__((packed));
/* Table 4-7: Feature Unit Descriptor (head) */
struct usb_audio_feature_unit_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bUnitID;
uint8_t bSourceID;
uint8_t bControlSize;
uint16_t bmaControlMaster; /* device can assume 16-bit, given highest
* defined bit in spec is bit #9.
* (it is thus required bControlSize=2) */
/* ... */
} __attribute__((packed));
/* Table 4-7: Feature Unit Descriptor (body) */
struct usb_audio_feature_unit_descriptor_body {
/* ... */
uint16_t bmaControl;
/* ... */
} __attribute__((packed));
/* Table 4-7: Feature Unit Descriptor (tail) */
struct usb_audio_feature_unit_descriptor_tail {
/* ... */
uint8_t iFeature;
} __attribute__((packed));
/* Table 4-7: Feature Unit Descriptor (2-channel)
*
* This structure is a convenience covering the (common) case where
* there are 2 channels associated with the feature unit
*/
struct usb_audio_feature_unit_descriptor_2ch {
struct usb_audio_feature_unit_descriptor_head head;
struct usb_audio_feature_unit_descriptor_body channel_control[2];
struct usb_audio_feature_unit_descriptor_tail tail;
} __attribute__((packed));
/* Table 4-19: Class-Specific AS Interface Descriptor */
struct usb_audio_stream_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bTerminalLink;
uint8_t bDelay;
uint16_t wFormatTag;
} __attribute__((packed));
/* Table 4-20: Standard AS Isochronous Audio Data Endpoint Descriptor */
struct usb_audio_stream_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
uint8_t bRefresh;
uint8_t bSynchAddress;
} __attribute__((packed));
/* Table 4-21: Class-Specific AS Isochronous Audio Data Endpoint Descriptor */
struct usb_audio_stream_audio_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmAttributes;
uint8_t bLockDelayUnits;
uint16_t wLockDelay;
} __attribute__((packed));
/*
* Definitions from the USB_AUDIO_FORMAT_ or usb_audio_format_ namespace come from:
* "Universal Serial Bus Device Class Definition for Audio Data Formats, Revision 1.0"
*/
/* Table 2-1: Type I Format Type Descriptor (head) */
struct usb_audio_format_type1_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bFormatType;
uint8_t bNrChannels;
uint8_t bSubFrameSize;
uint8_t bBitResolution;
uint8_t bSamFreqType;
/* ... */
} __attribute__((packed));
/* Table 2-2: Continuous Sampling Frequency */
struct usb_audio_format_continuous_sampling_frequency {
/* ... */
uint32_t tLowerSamFreq : 24;
uint32_t tUpperSamFreq : 24;
} __attribute__((packed));
/* Table 2-3: Discrete Number of Sampling Frequencies */
struct usb_audio_format_discrete_sampling_frequency {
/* ... */
uint32_t tSamFreq : 24;
} __attribute__((packed));
/* Table 2-1: Type I Format Type Descriptor (1 sampling frequency)
*
* This structure is a convenience covering the (common) case where
* only 1 discrete sampling frequency is used
*/
struct usb_audio_format_type1_descriptor_1freq {
struct usb_audio_format_type1_descriptor_head head;
struct usb_audio_format_discrete_sampling_frequency freqs[1];
} __attribute__((packed));
#endif
/**@}*/
@@ -0,0 +1,190 @@
/** @defgroup usb_audio_defines USB MIDI Type Definitions
@brief <b>Defined Constants and Types for the USB MIDI Type Definitions</b>
@ingroup USB_defines
@version 1.0.0
@author @htmlonly &copy; @endhtmlonly 2014
Daniel Thompson <daniel@redfelineninja.org.uk>
@date 19 April 2014
LGPL License Terms @ref lgpl_license
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2014 Daniel Thompson <daniel@redfelineninja.org.uk>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#ifndef LIBOPENCM3_USB_MIDI_H
#define LIBOPENCM3_USB_MIDI_H
#include <stdint.h>
/*
* Definitions from the USB_MIDI_ or usb_midi_ namespace come from:
* "Universal Serial Bus Class Definitions for MIDI Devices, Revision 1.0"
*/
/* Appendix A.1: MS Class-Specific Interface Descriptor Subtypes */
#define USB_MIDI_SUBTYPE_MS_DESCRIPTOR_UNDEFINED 0x00
#define USB_MIDI_SUBTYPE_MS_HEADER 0x01
#define USB_MIDI_SUBTYPE_MIDI_IN_JACK 0x02
#define USB_MIDI_SUBTYPE_MIDI_OUT_JACK 0x03
#define USB_MIDI_SUBTYPE_MIDI_ELEMENT 0x04
/* Appendix A.2: MS Class-Specific Endpoint Descriptor Subtypes */
#define USB_MIDI_SUBTYPE_DESCRIPTOR_UNDEFINED 0x00
#define USB_MIDI_SUBTYPE_MS_GENERAL 0x01
/* Appendix A.3: MS MIDI IN and OUT Jack types */
#define USB_MIDI_JACK_TYPE_UNDEFINED 0x00
#define USB_MIDI_JACK_TYPE_EMBEDDED 0x01
#define USB_MIDI_JACK_TYPE_EXTERNAL 0x02
/* Appendix A.5.1 Endpoint Control Selectors */
#define USB_MIDI_EP_CONTROL_UNDEFINED 0x00
#define USB_MIDI_ASSOCIATION_CONTROL 0x01
/* Table 6-2: Class-Specific MS Interface Header Descriptor */
struct usb_midi_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdMSC;
uint16_t wTotalLength;
} __attribute__((packed));
/* Table 6-3: MIDI IN Jack Descriptor */
struct usb_midi_in_jack_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bJackType;
uint8_t bJackID;
uint8_t iJack;
} __attribute__((packed));
/* Table 6-4: MIDI OUT Jack Descriptor (head) */
struct usb_midi_out_jack_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bJackType;
uint8_t bJackID;
uint8_t bNrInputPins;
/* ... */
} __attribute__((packed));
/* Table 6.4: MIDI OUT Jack Descriptor (body) */
struct usb_midi_out_jack_descriptor_body {
/* ... */
uint8_t baSourceID;
uint8_t baSourcePin;
/* ... */
} __attribute__((packed));
/* Table 6.4: MIDI OUT Jack Descriptor (tail) */
struct usb_midi_out_jack_descriptor_tail {
/* ... */
uint8_t iJack;
} __attribute__((packed));
/* Table 6.4: MIDI OUT Jack Descriptor (single)
*
* This structure is a convenience covering the (normal) case where
* there is only one input pin.
*/
struct usb_midi_out_jack_descriptor {
struct usb_midi_out_jack_descriptor_head head;
struct usb_midi_out_jack_descriptor_body source[1];
struct usb_midi_out_jack_descriptor_tail tail;
} __attribute__((packed));
/* Table 6-5: MIDI Element Descriptor (head) */
struct usb_midi_element_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bElementID;
uint8_t bNrInputPins;
/* ... */
} __attribute__((packed));
/* Table 6-5: MIDI Element Descriptor (body) */
struct usb_midi_element_descriptor_body {
/* ... */
uint8_t baSourceID;
uint8_t baSourcePin;
/* ... */
} __attribute__((packed));
/* Table 6-5: MIDI Element Descriptor (tail) */
struct usb_midi_element_descriptor_tail {
/* ... */
uint8_t bNrOutputPins;
uint8_t bInTerminalLink;
uint8_t bOutTerminalLink;
uint8_t bElCapsSize;
uint16_t bmElementCaps; /* host cannot assume this is 16-bit but device
can (since highest defined bitmap value in
v1.0 is bit 11) */
uint8_t iElement;
} __attribute__((packed));
/* Table 6-5: MIDI Element Descriptor (single)
*
* This structure is a convenience covering the (common) case where
* there is only one input pin.
*/
struct usb_midi_element_descriptor {
struct usb_midi_element_descriptor_head head;
struct usb_midi_element_descriptor_body source[1];
struct usb_midi_element_descriptor_tail tail;
} __attribute__((packed));
/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (head) */
struct usb_midi_endpoint_descriptor_head {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bNumEmbMIDIJack;
} __attribute__((packed));
/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (body) */
struct usb_midi_endpoint_descriptor_body {
uint8_t baAssocJackID;
} __attribute__((packed));
/* Table 6.7: Class-specific MS Bulk Data Endpoint Descriptor (single)
*
* This structure is a convenience covering the (normal) case where
* there is only one input pin.
*/
struct usb_midi_endpoint_descriptor {
struct usb_midi_endpoint_descriptor_head head;
struct usb_midi_endpoint_descriptor_body jack[1];
} __attribute__((packed));
#endif
/**@}*/
@@ -0,0 +1,428 @@
#include <furi.h>
#include <furi_hal_console.h>
#include <usb.h>
#include <usb_std.h>
#include "usb_midi_driver.h"
#include "cm3_usb_audio.h"
#include "cm3_usb_midi.h"
// Appendix B. "Example: Simple MIDI Adapter" from "Universal Serial Bus Device Class Definition for MIDI Devices", Revision 1.0
#define USB_VID 0x6666
#define USB_PID 0x5119
#define USB_EP0_SIZE 8
#define USB_MIDI_EP_SIZE 64
#define USB_MIDI_EP_IN 0x81
#define USB_MIDI_EP_OUT 0x01
#define EP_CFG_DECONFIGURE 0
#define EP_CFG_CONFIGURE 1
enum {
USB_STR_ZERO,
USB_STR_MANUFACTURER,
USB_STR_PRODUCT,
USB_STR_SERIAL_NUMBER,
};
/*
B.1 Device Descriptor
*/
static const struct usb_device_descriptor device_descriptor = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DTYPE_DEVICE,
.bcdUSB = VERSION_BCD(2, 0, 0), // was 0x0110, 1.10 - current revision of USBspecification.
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = USB_SUBCLASS_NONE,
.bDeviceProtocol = USB_PROTO_NONE,
.bMaxPacketSize0 = USB_EP0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = VERSION_BCD(1, 0, 0),
.iManufacturer = USB_STR_MANUFACTURER,
.iProduct = USB_STR_PRODUCT,
.iSerialNumber = USB_STR_SERIAL_NUMBER,
.bNumConfigurations = 1,
};
struct usb_audio_header_descriptor {
struct usb_audio_header_descriptor_head head;
struct usb_audio_header_descriptor_body body;
} __attribute__((packed));
struct usb_midi_jacks_descriptor {
struct usb_midi_header_descriptor header;
struct usb_midi_in_jack_descriptor in_embedded;
struct usb_midi_in_jack_descriptor in_external;
struct usb_midi_out_jack_descriptor out_embedded;
struct usb_midi_out_jack_descriptor out_external;
} __attribute__((packed));
struct MidiConfigDescriptor {
/*
B.2 Configuration Descriptor
*/
struct usb_config_descriptor config;
/*
B.3 AudioControl Interface Descriptors
The AudioControl interface describes the device structure (audio function topology)
and is used to manipulate the Audio Controls. This device has no audio function incorporated.
However, the AudioControl interface is mandatory and therefore both the standard AC interface
descriptor and the classspecific AC interface descriptor must be present.
The class-specific AC interface descriptor only contains the header descriptor.
*/
// B.3.1 Standard AC Interface Descriptor
struct usb_interface_descriptor audio_control_iface;
// B.3.2 Class-specific AC Interface Descriptor
struct usb_audio_header_descriptor audio_control_header;
/*
B.4 MIDIStreaming Interface Descriptors
*/
// B.4.1 Standard MS Interface Descriptor
struct usb_interface_descriptor midi_streaming_iface;
// B.4.2 Class-specific MS Interface Descriptor
// B.4.3 MIDI IN Jack Descriptor
// B.4.4 MIDI OUT Jack Descriptor
struct usb_midi_jacks_descriptor midi_jacks;
/*
B.5 Bulk OUT Endpoint Descriptors
*/
// B.5.1 Standard Bulk OUT Endpoint Descriptor
struct usb_endpoint_descriptor bulk_out;
// B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor
struct usb_midi_endpoint_descriptor midi_bulk_out;
/*
B.6 Bulk IN Endpoint Descriptors
*/
// B.6.1 Standard Bulk IN Endpoint Descriptor
struct usb_endpoint_descriptor bulk_in;
// B.6.2 Class-specific MS Bulk IN Endpoint Descriptor
struct usb_midi_endpoint_descriptor midi_bulk_in;
} __attribute__((packed));
static const struct MidiConfigDescriptor config_descriptor = {
.config =
{
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DTYPE_CONFIGURATION,
.wTotalLength = sizeof(struct MidiConfigDescriptor),
.bNumInterfaces = 2, /* control and data */
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CFG_ATTR_RESERVED,
.bMaxPower = USB_CFG_POWER_MA(100),
},
.audio_control_iface =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_AUDIO_SUBCLASS_CONTROL,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = 0,
},
.audio_control_header =
{
.head =
{
.bLength = sizeof(struct usb_audio_header_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_AUDIO_TYPE_HEADER,
.bcdADC = VERSION_BCD(1, 0, 0),
.wTotalLength = sizeof(struct usb_audio_header_descriptor),
.bInCollection = 1,
},
.body =
{
.baInterfaceNr = 1,
},
},
.midi_streaming_iface =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_AUDIO_SUBCLASS_MIDISTREAMING,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = 0,
},
.midi_jacks =
{
.header =
{
.bLength = sizeof(struct usb_midi_header_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MS_HEADER,
.bcdMSC = VERSION_BCD(1, 0, 0),
.wTotalLength = sizeof(struct usb_midi_jacks_descriptor),
},
.in_embedded =
{
.bLength = sizeof(struct usb_midi_in_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EMBEDDED,
.bJackID = 0x01,
.iJack = 0x00,
},
.in_external =
{
.bLength = sizeof(struct usb_midi_in_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EXTERNAL,
.bJackID = 0x02,
.iJack = 0x00,
},
.out_embedded =
{
.head =
{
.bLength = sizeof(struct usb_midi_out_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EMBEDDED,
.bJackID = 0x03,
.bNrInputPins = 1,
},
.source[0] =
{
.baSourceID = 0x02,
.baSourcePin = 0x01,
},
.tail =
{
.iJack = 0x00,
},
},
.out_external =
{
.head =
{
.bLength = sizeof(struct usb_midi_out_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EXTERNAL,
.bJackID = 0x04,
.bNrInputPins = 1,
},
.source[0] =
{
.baSourceID = 0x01,
.baSourcePin = 0x01,
},
.tail =
{
.iJack = 0x00,
},
},
},
.bulk_out =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = USB_MIDI_EP_OUT,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = USB_MIDI_EP_SIZE,
.bInterval = 0,
},
.midi_bulk_out =
{
.head =
{
.bLength = sizeof(struct usb_midi_endpoint_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT,
.bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL,
.bNumEmbMIDIJack = 1,
},
.jack[0] =
{
.baAssocJackID = 0x01,
},
},
.bulk_in =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = USB_MIDI_EP_IN,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = USB_MIDI_EP_SIZE,
.bInterval = 0,
},
.midi_bulk_in =
{
.head =
{
.bLength = sizeof(struct usb_midi_endpoint_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT,
.bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL,
.bNumEmbMIDIJack = 1,
},
.jack[0] =
{
.baAssocJackID = 0x03,
},
},
};
static const struct usb_string_descriptor dev_manufacturer_string =
USB_STRING_DESC("Flipper Devices Inc.");
static const struct usb_string_descriptor dev_product_string =
USB_STRING_DESC("Flipper MIDI Device");
static const struct usb_string_descriptor dev_serial_number_string =
USB_STRING_DESC("Serial Number");
static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx);
static void midi_deinit(usbd_device* dev);
static void midi_on_wakeup(usbd_device* dev);
static void midi_on_suspend(usbd_device* dev);
static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg);
static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
FuriHalUsbInterface midi_usb_interface = {
.init = midi_init,
.deinit = midi_deinit,
.wakeup = midi_on_wakeup,
.suspend = midi_on_suspend,
.dev_descr = (struct usb_device_descriptor*)&device_descriptor,
.cfg_descr = (void*)&config_descriptor,
};
typedef struct {
usbd_device* dev;
MidiRxCallback rx_callback;
void* context;
FuriSemaphore* semaphore_tx;
bool connected;
} MidiUsb;
static MidiUsb midi_usb;
void midi_usb_set_context(void* context) {
midi_usb.context = context;
}
void midi_usb_set_rx_callback(MidiRxCallback callback) {
midi_usb.rx_callback = callback;
}
size_t midi_usb_rx(uint8_t* buffer, size_t size) {
size_t len = usbd_ep_read(midi_usb.dev, USB_MIDI_EP_OUT, buffer, size);
return len;
}
size_t midi_usb_tx(uint8_t* buffer, uint8_t size) {
if((midi_usb.semaphore_tx == NULL) || (midi_usb.connected == false)) return 0;
furi_check(furi_semaphore_acquire(midi_usb.semaphore_tx, FuriWaitForever) == FuriStatusOk);
if(midi_usb.connected) {
int32_t len = usbd_ep_write(midi_usb.dev, USB_MIDI_EP_IN, buffer, size);
return len;
} else {
return 0;
}
}
static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
UNUSED(intf);
UNUSED(ctx);
midi_usb_interface.str_manuf_descr = (void*)&dev_manufacturer_string;
midi_usb_interface.str_prod_descr = (void*)&dev_product_string;
midi_usb_interface.str_serial_descr = (void*)&dev_serial_number_string;
midi_usb_interface.dev_descr->idVendor = USB_VID;
midi_usb_interface.dev_descr->idProduct = USB_PID;
midi_usb.dev = dev;
if(midi_usb.semaphore_tx == NULL) midi_usb.semaphore_tx = furi_semaphore_alloc(1, 1);
usbd_reg_config(dev, midi_ep_config);
usbd_reg_control(dev, midi_control);
usbd_connect(dev, true);
}
static void midi_deinit(usbd_device* dev) {
midi_usb.connected = false;
midi_usb.dev = NULL;
furi_semaphore_free(midi_usb.semaphore_tx);
usbd_reg_config(dev, NULL);
usbd_reg_control(dev, NULL);
}
static void midi_on_wakeup(usbd_device* dev) {
UNUSED(dev);
if(!midi_usb.connected) {
midi_usb.connected = true;
}
}
static void midi_on_suspend(usbd_device* dev) {
UNUSED(dev);
if(midi_usb.connected) {
midi_usb.connected = false;
}
}
static void midi_tx_rx(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev);
UNUSED(ep);
switch(event) {
case usbd_evt_eptx:
furi_semaphore_release(midi_usb.semaphore_tx);
break;
case usbd_evt_eprx:
if(midi_usb.rx_callback != NULL) {
midi_usb.rx_callback(midi_usb.context);
}
break;
default:
break;
}
}
static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg) {
switch(cfg) {
case EP_CFG_DECONFIGURE:
usbd_ep_deconfig(dev, USB_MIDI_EP_OUT);
usbd_ep_deconfig(dev, USB_MIDI_EP_IN);
usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, NULL);
usbd_reg_endpoint(dev, USB_MIDI_EP_IN, NULL);
return usbd_ack;
case EP_CFG_CONFIGURE:
usbd_ep_config(dev, USB_MIDI_EP_OUT, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE);
usbd_ep_config(dev, USB_MIDI_EP_IN, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE);
usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, midi_tx_rx);
usbd_reg_endpoint(dev, USB_MIDI_EP_IN, midi_tx_rx);
return usbd_ack;
default:
return usbd_fail;
}
}
static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
UNUSED(dev);
UNUSED(req);
UNUSED(callback);
return usbd_fail;
}
@@ -0,0 +1,14 @@
#pragma once
#include <furi_hal_usb.h>
extern FuriHalUsbInterface midi_usb_interface;
typedef void (*MidiRxCallback)(void* context);
void midi_usb_set_context(void* context);
void midi_usb_set_rx_callback(MidiRxCallback callback);
size_t midi_usb_rx(uint8_t* buffer, size_t size);
size_t midi_usb_tx(uint8_t* buffer, uint8_t size);
+80
View File
@@ -0,0 +1,80 @@
#include <furi.h>
#include <furi_hal.h>
#include "usb/usb_midi_driver.h"
#include "midi/parser.h"
#include "midi/usb_message.h"
#include <math.h>
float note_to_frequency(int note) {
float a = 440;
return (a / 32) * powf(2, ((note - 9) / 12.0));
}
typedef enum {
MidiThreadEventStop = (1 << 0),
MidiThreadEventRx = (1 << 1),
MidiThreadEventAll = MidiThreadEventStop | MidiThreadEventRx,
} MidiThreadEvent;
static void midi_rx_callback(void* context) {
furi_assert(context);
FuriThreadId thread_id = (FuriThreadId)context;
furi_thread_flags_set(thread_id, MidiThreadEventRx);
}
int32_t usb_midi_app(void* p) {
UNUSED(p);
FuriHalUsbInterface* usb_config_prev;
usb_config_prev = furi_hal_usb_get_config();
midi_usb_set_context(furi_thread_get_id(furi_thread_get_current()));
midi_usb_set_rx_callback(midi_rx_callback);
furi_hal_usb_set_config(&midi_usb_interface, NULL);
MidiParser* parser = midi_parser_alloc();
uint32_t events;
uint8_t current_note = 255;
while(1) {
events = furi_thread_flags_wait(MidiThreadEventAll, FuriFlagWaitAny, FuriWaitForever);
if(!(events & FuriFlagError)) {
if(events & MidiThreadEventRx) {
uint8_t buffer[64];
size_t size = midi_usb_rx(buffer, sizeof(buffer));
// loopback
// midi_usb_tx(buffer, size);
size_t start = 0;
while(start < size) {
CodeIndex code_index = code_index_from_data(buffer[start]);
uint8_t data_size = usb_message_data_size(code_index);
if(data_size == 0) break;
start += 1;
for(size_t j = 0; j < data_size; j++) {
if(midi_parser_parse(parser, buffer[start + j])) {
MidiEvent* event = midi_parser_get_message(parser);
if(event->type == NoteOn) {
NoteOnEvent note_on = AsNoteOn(event);
current_note = note_on.note;
furi_hal_speaker_start(
note_to_frequency(note_on.note), note_on.velocity / 127.0f);
} else if(event->type == NoteOff) {
NoteOffEvent note_off = AsNoteOff(event);
if(note_off.note == current_note) {
furi_hal_speaker_stop();
}
}
}
}
start += data_size;
}
}
}
}
midi_parser_free(parser);
furi_hal_usb_set_config(usb_config_prev, NULL);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B