|
| 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 | + |
| 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 | + |
| 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 | + .dev = session, |
| 81 | + .send = (void (*)(void *, const struct midi_ump)) netmidi2_send, |
| 82 | + .ep_spec = &ump_ep_dt, |
| 83 | + }; |
| 84 | + ump_stream_respond(&responder_cfg, ump); |
| 85 | +} |
| 86 | + |
| 87 | +static void netmidi2_callback(struct netmidi2_session *session, |
| 88 | + const struct midi_ump ump) |
| 89 | +{ |
| 90 | + switch (UMP_MT(ump)) { |
| 91 | + case UMP_MT_MIDI1_CHANNEL_VOICE: |
| 92 | + send_external_midi1(ump); |
| 93 | + break; |
| 94 | + case UMP_MT_UMP_STREAM: |
| 95 | + handle_ump_stream(session, ump); |
| 96 | + break; |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +#if defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_NONE) |
| 101 | +/* Simple Network MIDI 2.0 endpoint without authentication */ |
| 102 | +NETMIDI2_EP_DEFINE(midi_server, ump_ep_dt.name, NULL, 0); |
| 103 | + |
| 104 | +#elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_SHARED_SECRET) |
| 105 | +/* Network MIDI 2.0 endpoint with shared secret authentication */ |
| 106 | +BUILD_ASSERT( |
| 107 | + sizeof(CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET) > 1, |
| 108 | + "CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET must be not empty" |
| 109 | +); |
| 110 | + |
| 111 | +NETMIDI2_EP_DEFINE_WITH_AUTH(midi_server, ump_ep_dt.name, NULL, 0, |
| 112 | + CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET); |
| 113 | + |
| 114 | +#elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_USER_PASSWORD) |
| 115 | +/* Network MIDI 2.0 endpoint with a single user/password*/ |
| 116 | +BUILD_ASSERT( |
| 117 | + sizeof(CONFIG_NET_SAMPLE_MIDI2_USERNAME) > 1, |
| 118 | + "CONFIG_NET_SAMPLE_MIDI2_USERNAME must be not empty" |
| 119 | +); |
| 120 | +BUILD_ASSERT( |
| 121 | + sizeof(CONFIG_NET_SAMPLE_MIDI2_PASSWORD) > 1, |
| 122 | + "CONFIG_NET_SAMPLE_MIDI2_PASSWORD must be not empty" |
| 123 | +); |
| 124 | + |
| 125 | +NETMIDI2_EP_DEFINE_WITH_USERS(midi_server, ump_ep_dt.name, NULL, 0, |
| 126 | + {.name = CONFIG_NET_SAMPLE_MIDI2_USERNAME, |
| 127 | + .password = CONFIG_NET_SAMPLE_MIDI2_PASSWORD}); |
| 128 | + |
| 129 | +#endif |
| 130 | + |
| 131 | +DNS_SD_REGISTER_SERVICE(midi_dns, CONFIG_NET_HOSTNAME "-" CONFIG_BOARD, |
| 132 | + "_midi2", "_udp", "local", DNS_SD_EMPTY_TXT, |
| 133 | + &midi_server.addr4.sin_port); |
| 134 | + |
| 135 | +int main(void) |
| 136 | +{ |
| 137 | + CONFIGURE_LED(); |
| 138 | + |
| 139 | + midi_server.rx_packet_cb = netmidi2_callback; |
| 140 | + netmidi2_host_ep_start(&midi_server); |
| 141 | + |
| 142 | + return 0; |
| 143 | +} |
0 commit comments