-
Notifications
You must be signed in to change notification settings - Fork 7.4k
/
Copy pathtusb_midi_main.c
185 lines (156 loc) · 6 KB
/
tusb_midi_main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org)
*
* SPDX-License-Identifier: MIT
*
* SPDX-FileContributor: 2022-2024 Espressif Systems (Shanghai) CO LTD
*/
#include <stdlib.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tinyusb.h"
#include "esp_timer.h"
static const char *TAG = "example";
/** Helper defines **/
// Interface counter
enum interface_count {
#if CFG_TUD_MIDI
ITF_NUM_MIDI = 0,
ITF_NUM_MIDI_STREAMING,
#endif
ITF_COUNT
};
// USB Endpoint numbers
enum usb_endpoints {
// Available USB Endpoints: 5 IN/OUT EPs and 1 IN EP
EP_EMPTY = 0,
#if CFG_TUD_MIDI
EPNUM_MIDI,
#endif
};
/** TinyUSB descriptors **/
#define TUSB_DESCRIPTOR_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_MIDI * TUD_MIDI_DESC_LEN)
/**
* @brief String descriptor
*/
static const char* s_str_desc[5] = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
"Example MIDI device", // 4: MIDI
};
/**
* @brief Configuration descriptor
*
* This is a simple configuration descriptor that defines 1 configuration and a MIDI interface
*/
static const uint8_t s_midi_cfg_desc[] = {
// Configuration number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, TUSB_DESCRIPTOR_TOTAL_LEN, 0, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 4, EPNUM_MIDI, (0x80 | EPNUM_MIDI), 64),
};
#if (TUD_OPT_HIGH_SPEED)
/**
* @brief High Speed configuration descriptor
*
* This is a simple configuration descriptor that defines 1 configuration and a MIDI interface
*/
static const uint8_t s_midi_hs_cfg_desc[] = {
// Configuration number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, TUSB_DESCRIPTOR_TOTAL_LEN, 0, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 4, EPNUM_MIDI, (0x80 | EPNUM_MIDI), 512),
};
#endif // TUD_OPT_HIGH_SPEED
static void midi_task_read_example(void *arg)
{
// The MIDI interface always creates input and output port/jack descriptors
// regardless of these being used or not. Therefore incoming traffic should be read
// (possibly just discarded) to avoid the sender blocking in IO
uint8_t packet[4];
bool read = false;
for (;;) {
vTaskDelay(1);
while (tud_midi_available()) {
read = tud_midi_packet_read(packet);
if (read) {
ESP_LOGI(TAG, "Read - Time (ms since boot): %lld, Data: %02hhX %02hhX %02hhX %02hhX",
esp_timer_get_time(), packet[0], packet[1], packet[2], packet[3]);
}
}
}
}
// Basic MIDI Messages
#define NOTE_OFF 0x80
#define NOTE_ON 0x90
static void periodic_midi_write_example_cb(void *arg)
{
// Example melody stored as an array of note values
uint8_t const note_sequence[] = {
74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85, 88, 92, 97, 100, 97, 92, 88, 85, 81, 78,
74, 69, 66, 62, 57, 62, 66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78, 73, 68, 64, 61,
56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102
};
static uint8_t const cable_num = 0; // MIDI jack associated with USB endpoint
static uint8_t const channel = 0; // 0 for channel 1
static uint32_t note_pos = 0;
// Previous positions in the note sequence.
int previous = note_pos - 1;
// If we currently are at position 0, set the
// previous position to the last note in the sequence.
if (previous < 0) {
previous = sizeof(note_sequence) - 1;
}
// Send Note On for current position at full velocity (127) on channel 1.
ESP_LOGI(TAG, "Writing MIDI data %d", note_sequence[note_pos]);
if (tud_midi_mounted()) {
uint8_t note_on[3] = {NOTE_ON | channel, note_sequence[note_pos], 127};
tud_midi_stream_write(cable_num, note_on, 3);
// Send Note Off for previous note.
uint8_t note_off[3] = {NOTE_OFF | channel, note_sequence[previous], 0};
tud_midi_stream_write(cable_num, note_off, 3);
}
// Increment position
note_pos++;
// If we are at the end of the sequence, start over.
if (note_pos >= sizeof(note_sequence)) {
note_pos = 0;
}
}
void app_main(void)
{
ESP_LOGI(TAG, "USB initialization");
tinyusb_config_t const tusb_cfg = {
.device_descriptor = NULL, // If device_descriptor is NULL, tinyusb_driver_install() will use Kconfig
.string_descriptor = s_str_desc,
.string_descriptor_count = sizeof(s_str_desc) / sizeof(s_str_desc[0]),
.external_phy = false,
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = s_midi_cfg_desc, // HID configuration descriptor for full-speed and high-speed are the same
.hs_configuration_descriptor = s_midi_hs_cfg_desc,
.qualifier_descriptor = NULL,
#else
.configuration_descriptor = s_midi_cfg_desc,
#endif // TUD_OPT_HIGH_SPEED
};
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB initialization DONE");
// Periodically send MIDI packets
int const tempo = 286;
const esp_timer_create_args_t periodic_midi_args = {
.callback = &periodic_midi_write_example_cb,
/* name is optional, but may help identify the timer when debugging */
.name = "periodic_midi"
};
ESP_LOGI(TAG, "MIDI write task init");
esp_timer_handle_t periodic_midi_timer;
ESP_ERROR_CHECK(esp_timer_create(&periodic_midi_args, &periodic_midi_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_midi_timer, tempo * 1000));
// Read received MIDI packets
ESP_LOGI(TAG, "MIDI read task init");
xTaskCreate(midi_task_read_example, "midi_task_read_example", 4 * 1024, NULL, 5, NULL);
}