1 /*
2 * Copyright (c) 2025 Titouan Christophe
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <zephyr/kernel.h>
7 #include <errno.h>
8 #include <stdio.h>
9
10 #include <zephyr/audio/midi.h>
11 #include <zephyr/net/dns_sd.h>
12 #include <zephyr/net/midi2.h>
13
14 #include <ump_stream_responder.h>
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(net_midi2_sample, LOG_LEVEL_DBG);
18
19 #define ACT_LED_NODE DT_NODELABEL(midi_green_led)
20 #define SERIAL_NODE DT_NODELABEL(midi_serial)
21
22 #if !DT_NODE_EXISTS(ACT_LED_NODE)
23 #define CONFIGURE_LED()
24 #define SET_LED(_state)
25 #else /* DT_NODE_EXISTS(ACT_LED_NODE) */
26 #include <zephyr/drivers/gpio.h>
27
28 static const struct gpio_dt_spec act_led = GPIO_DT_SPEC_GET(ACT_LED_NODE, gpios);
29
30 #define CONFIGURE_LED() gpio_pin_configure_dt(&act_led, GPIO_OUTPUT_INACTIVE)
31 #define SET_LED(_state) gpio_pin_set_dt(&act_led, (_state))
32 #endif /* DT_NODE_EXISTS(ACT_LED_NODE) */
33
34
35 #if !DT_NODE_EXISTS(SERIAL_NODE)
36 #define send_external_midi1(...)
37 #else /* DT_NODE_EXISTS(SERIAL_NODE) */
38 #include <zephyr/drivers/uart.h>
39
40 static const struct device *const uart_dev = DEVICE_DT_GET(SERIAL_NODE);
41
send_external_midi1(const struct midi_ump ump)42 static inline void send_external_midi1(const struct midi_ump ump)
43 {
44 /* Only send MIDI events aimed at the external output */
45 if (UMP_GROUP(ump) != DT_REG_ADDR(DT_NODELABEL(ext_midi_out))) {
46 return;
47 }
48
49 switch (UMP_MIDI_COMMAND(ump)) {
50 case UMP_MIDI_PROGRAM_CHANGE:
51 SET_LED(1);
52 uart_poll_out(uart_dev, UMP_MIDI_STATUS(ump));
53 uart_poll_out(uart_dev, UMP_MIDI1_P1(ump));
54 SET_LED(0);
55 break;
56
57 case UMP_MIDI_NOTE_OFF:
58 case UMP_MIDI_NOTE_ON:
59 case UMP_MIDI_AFTERTOUCH:
60 case UMP_MIDI_CONTROL_CHANGE:
61 case UMP_MIDI_PITCH_BEND:
62 SET_LED(1);
63 uart_poll_out(uart_dev, UMP_MIDI_STATUS(ump));
64 uart_poll_out(uart_dev, UMP_MIDI1_P1(ump));
65 uart_poll_out(uart_dev, UMP_MIDI1_P2(ump));
66 SET_LED(0);
67 break;
68 }
69 }
70 #endif /* DT_NODE_EXISTS(SERIAL_NODE) */
71
72
73 static const struct ump_endpoint_dt_spec ump_ep_dt =
74 UMP_ENDPOINT_DT_SPEC_GET(DT_NODELABEL(midi2));
75
handle_ump_stream(struct netmidi2_session * session,const struct midi_ump ump)76 static inline void handle_ump_stream(struct netmidi2_session *session,
77 const struct midi_ump ump)
78 {
79 const struct ump_stream_responder_cfg responder_cfg =
80 UMP_STREAM_RESPONDER(session, netmidi2_send, &ump_ep_dt);
81 ump_stream_respond(&responder_cfg, ump);
82 }
83
netmidi2_callback(struct netmidi2_session * session,const struct midi_ump ump)84 static void netmidi2_callback(struct netmidi2_session *session,
85 const struct midi_ump ump)
86 {
87 switch (UMP_MT(ump)) {
88 case UMP_MT_MIDI1_CHANNEL_VOICE:
89 send_external_midi1(ump);
90 break;
91 case UMP_MT_UMP_STREAM:
92 handle_ump_stream(session, ump);
93 break;
94 }
95 }
96
97 #if defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_NONE)
98 /* Simple Network MIDI 2.0 endpoint without authentication */
99 NETMIDI2_EP_DEFINE(midi_server, ump_ep_dt.name, NULL, 0);
100
101 #elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_SHARED_SECRET)
102 /* Network MIDI 2.0 endpoint with shared secret authentication */
103 BUILD_ASSERT(
104 sizeof(CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET) > 1,
105 "CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET must be not empty"
106 );
107
108 NETMIDI2_EP_DEFINE_WITH_AUTH(midi_server, ump_ep_dt.name, NULL, 0,
109 CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET);
110
111 #elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_USER_PASSWORD)
112 /* Network MIDI 2.0 endpoint with a single user/password*/
113 BUILD_ASSERT(
114 sizeof(CONFIG_NET_SAMPLE_MIDI2_USERNAME) > 1,
115 "CONFIG_NET_SAMPLE_MIDI2_USERNAME must be not empty"
116 );
117 BUILD_ASSERT(
118 sizeof(CONFIG_NET_SAMPLE_MIDI2_PASSWORD) > 1,
119 "CONFIG_NET_SAMPLE_MIDI2_PASSWORD must be not empty"
120 );
121
122 NETMIDI2_EP_DEFINE_WITH_USERS(midi_server, ump_ep_dt.name, NULL, 0,
123 {.name = CONFIG_NET_SAMPLE_MIDI2_USERNAME,
124 .password = CONFIG_NET_SAMPLE_MIDI2_PASSWORD});
125
126 #endif
127
128 DNS_SD_REGISTER_SERVICE(midi_dns, CONFIG_NET_HOSTNAME "-" CONFIG_BOARD,
129 "_midi2", "_udp", "local", DNS_SD_EMPTY_TXT,
130 &midi_server.addr4.sin_port);
131
main(void)132 int main(void)
133 {
134 CONFIGURE_LED();
135
136 midi_server.rx_packet_cb = netmidi2_callback;
137 netmidi2_host_ep_start(&midi_server);
138
139 return 0;
140 }
141