ref: 0032aed28e86dfe49d808af12c9debdec0720bca
parent: 17d2d6f75df1eac72b70b6e9f0d4ebf215f015fb
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Sun Mar 23 08:30:34 EDT 2025
use common header for audio and m8 comms implementations
--- a/Makefile
+++ b/Makefile
@@ -9,13 +9,13 @@
src/input.o \
src/backends/queue.o \
src/render.o \
-src/backends/audio.o \
-src/backends/rtmidi.o \
+src/backends/sdl_audio.o \
src/backends/ringbuffer.o \
-src/backends/serialport.o \
src/backends/slip.o \
-src/backends/usb.o \
-src/backends/usb_audio.o \
+src/backends/m8_libserialport.o \
+src/backends/m8_libusb.o \
+src/backends/m8_rtmidi.o \
+src/backends/libusb_audio.o \
#Set any dependant header files so that if they are edited they cause a complete re-compile (e.g. main.h some_subfunctions.h some_definitions_file.h ), or leave blank
DEPS = \
@@ -26,11 +26,12 @@
src/ini.h \
src/input.h \
src/render.h \
-src/backends/audio.h \
+src/backends/sdl_audio.h \
src/backends/ringbuffer.h \
-src/backends/rtmidi.h \
+src/backends/m8_rtmidi.h \
src/backends/queue.h \
-src/backends/serialport.h \
+src/backends/m8_libserialport.h \
+src/backends/m8_libusb.h \
src/backends/slip.h \
src/fonts/inline_font.h
--- a/src/backends/audio.c
+++ /dev/null
@@ -1,147 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifndef USE_LIBUSB
-#include "audio.h"
-#include <SDL3/SDL.h>
-#include <stdint.h>
-
-SDL_AudioStream *audio_stream_in, *audio_stream_out;
-
-static unsigned int audio_paused = 0;
-static unsigned int audio_initialized = 0;
-static SDL_AudioSpec audio_spec_in = {SDL_AUDIO_S16LE, 2, 44100};-static SDL_AudioSpec audio_spec_out = {SDL_AUDIO_S16LE, 2, 44100};-
-static void SDLCALL audio_cb_in(void *userdata, SDL_AudioStream *stream, int length, int unused) {- // suppress compiler warnings
- (void)userdata;
- (void)unused;
-
- Uint8 *src_audio_data = SDL_malloc(length);
- const int bytes_read = SDL_GetAudioStreamData(stream, src_audio_data, length);
- if (bytes_read == length) {- SDL_PutAudioStreamData(audio_stream_out, src_audio_data, bytes_read);
- } else {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error getting audio stream data: %s, destroying audio",
- SDL_GetError());
- audio_close();
- }
- SDL_free(src_audio_data);
-}
-
-void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size) {- if (!audio_initialized) {- audio_initialize(output_device_name, audio_buffer_size);
- return;
- }
- if (audio_paused) {- SDL_ResumeAudioStreamDevice(audio_stream_out);
- SDL_ResumeAudioStreamDevice(audio_stream_in);
- } else {- SDL_PauseAudioStreamDevice(audio_stream_in);
- SDL_PauseAudioStreamDevice(audio_stream_out);
- }
- audio_paused = !audio_paused;
- SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");
-}
-
-int audio_initialize(const char *output_device_name, const unsigned int audio_buffer_size) {-
- // wait for system to initialize possible new audio devices
- SDL_Delay(500);
-
- int num_devices_in, num_devices_out;
- SDL_AudioDeviceID m8_device_id = 0;
- SDL_AudioDeviceID output_device_id = 0;
-
- SDL_AudioDeviceID *devices_in = SDL_GetAudioRecordingDevices(&num_devices_in);
- if (!devices_in) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio capture devices, SDL Error: %s", SDL_GetError());
- return 0;
- }
-
- SDL_AudioDeviceID *devices_out = SDL_GetAudioPlaybackDevices(&num_devices_out);
- if (!devices_out) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio playback devices, SDL Error: %s",
- SDL_GetError());
- return 0;
- }
-
- for (int i = 0; i < num_devices_in; i++) {- const SDL_AudioDeviceID instance_id = devices_in[i];
- const char *device_name = SDL_GetAudioDeviceName(instance_id);
- SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
- if (SDL_strstr(device_name, "M8") != NULL) {- SDL_Log("M8 Audio Input device found: %s", device_name);- m8_device_id = instance_id;
- }
- }
-
- if (output_device_name != NULL) {- for (int i = 0; i < num_devices_out; i++) {- const SDL_AudioDeviceID instance_id = devices_out[i];
- const char *device_name = SDL_GetAudioDeviceName(instance_id);
- SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
- if (SDL_strcasestr(device_name, output_device_name) != NULL) {- SDL_Log("Requested output device found: %s", device_name);- output_device_id = instance_id;
- }
- }
- }
-
- SDL_free(devices_in);
- SDL_free(devices_out);
-
- if (!output_device_id) {- output_device_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
- }
-
- if (!m8_device_id) {- // forget about it
- SDL_Log("Cannot find M8 audio input device");- return 0;
- }
-
- char audio_buffer_size_str[256];
- SDL_snprintf(audio_buffer_size_str, sizeof(audio_buffer_size_str), "%d", audio_buffer_size);
- SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, audio_buffer_size_str);
-
- audio_stream_out = SDL_OpenAudioDeviceStream(output_device_id, &audio_spec_out, NULL, NULL);
-
- if (!audio_stream_out) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio output device: %s", SDL_GetError());
- return 0;
- }
- SDL_Log("Audiospec Out: format %d, channels %d, rate %d", audio_spec_out.format,- audio_spec_out.channels, audio_spec_out.freq);
-
- audio_stream_in = SDL_OpenAudioDeviceStream(m8_device_id, &audio_spec_in, audio_cb_in, NULL);
- if (!audio_stream_in) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio input device: %s", SDL_GetError());
- SDL_DestroyAudioStream(audio_stream_out);
- return 0;
- }
- SDL_Log("Audiospec In: format %d, channels %d, rate %d", audio_spec_in.format,- audio_spec_in.channels, audio_spec_in.freq);
-
- SDL_SetAudioStreamFormat(audio_stream_in, &audio_spec_in, &audio_spec_out);
-
- SDL_ResumeAudioStreamDevice(audio_stream_out);
- SDL_ResumeAudioStreamDevice(audio_stream_in);
-
- audio_paused = 0;
- audio_initialized = 1;
-
- return 1;
-}
-
-void audio_close() {- if (!audio_initialized)
- return;
- SDL_Log("Closing audio devices");- SDL_DestroyAudioStream(audio_stream_in);
- SDL_DestroyAudioStream(audio_stream_out);
- audio_initialized = 0;
-}
-
-#endif
--- /dev/null
+++ b/src/backends/audio_libusb.c
@@ -1,0 +1,206 @@
+#ifdef USE_LIBUSB
+
+#include "m8.h"
+#include "ringbuffer.h"
+#include <SDL3/SDL.h>
+#include <errno.h>
+#include <libusb.h>
+
+#define EP_ISO_IN 0x85
+#define IFACE_NUM 4
+
+#define NUM_TRANSFERS 64
+#define PACKET_SIZE 180
+#define NUM_PACKETS 2
+
+extern libusb_device_handle *devh;
+
+SDL_AudioDeviceID sdl_audio_device_id = 0;
+int audio_initialized = 0;
+RingBuffer *audio_buffer = NULL;
+
+static void audio_callback(void *userdata, Uint8 *stream, int len) {+ uint32_t read_len = ring_buffer_pop(audio_buffer, stream, len);
+
+ if (read_len == -1) {+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow!");
+ }
+
+ // If we didn't read the full len bytes, fill the rest with zeros
+ if (read_len < len) {+ SDL_memset(&stream[read_len], 0, len - read_len);
+ }
+}
+
+static void cb_xfr(struct libusb_transfer *xfr) {+ unsigned int i;
+
+ for (i = 0; i < xfr->num_iso_packets; i++) {+ struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
+
+ if (pack->status != LIBUSB_TRANSFER_COMPLETED) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
+ libusb_error_name(pack->status));
+ /* This doesn't happen, so bail out if it does. */
+ return;
+ }
+
+ const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
+ if (sdl_audio_device_id != 0) {+ uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
+ if (actual == -1) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
+ }
+ }
+ }
+
+ if (libusb_submit_transfer(xfr) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB\n");
+ SDL_free(xfr->buffer);
+ }
+}
+
+static struct libusb_transfer *xfr[NUM_TRANSFERS];
+
+static int benchmark_in() {+ int i;
+
+ for (i = 0; i < NUM_TRANSFERS; i++) {+ xfr[i] = libusb_alloc_transfer(NUM_PACKETS);
+ if (!xfr[i]) {+ SDL_Log("Could not allocate transfer");+ return -ENOMEM;
+ }
+
+ Uint8 *buffer = SDL_malloc(PACKET_SIZE * NUM_PACKETS);
+
+ libusb_fill_iso_transfer(xfr[i], devh, EP_ISO_IN, buffer, PACKET_SIZE * NUM_PACKETS,
+ NUM_PACKETS, cb_xfr, NULL, 0);
+ libusb_set_iso_packet_lengths(xfr[i], PACKET_SIZE);
+
+ libusb_submit_transfer(xfr[i]);
+ }
+
+ return 1;
+}
+
+int audio_initialize(int audio_buffer_size, const char *output_device_name) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,"LIBUSB audio not implemented yet");
+ return -1;
+ /*
+ SDL_Log("USB audio setup");+
+ int rc;
+
+ rc = libusb_kernel_driver_active(devh, IFACE_NUM);
+ if (rc == 1) {+ SDL_Log("Detaching kernel driver");+ rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_Log("Could not detach kernel driver: %s\n", libusb_error_name(rc));+ return rc;
+ }
+ }
+
+ rc = libusb_claim_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error claiming interface: %s\n", libusb_error_name(rc));
+ return rc;
+ }
+
+ rc = libusb_set_interface_alt_setting(devh, IFACE_NUM, 1);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error setting alt setting: %s\n", libusb_error_name(rc));
+ return rc;
+ }
+
+ if (!SDL_WasInit(SDL_INIT_AUDIO)) {+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Init audio failed %s", SDL_GetError());
+ return -1;
+ }
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Audio was already initialised");
+ }
+
+ static SDL_AudioSpec audio_spec;
+ audio_spec.format = SDL_AUDIO_S16;
+ audio_spec.channels = 2;
+ audio_spec.freq = 44100;
+ audio_spec.samples = audio_buffer_size;
+ audio_spec.callback = audio_callback;
+
+ SDL_AudioSpec _obtained;
+ SDL_zero(_obtained);
+
+ SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),+ output_device_name);
+
+ if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {+ SDL_Log("Using default audio device");+ sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, &_obtained, 0);
+ } else {+ sdl_audio_device_id = SDL_OpenAudioDevice(output_device_name, 0, &audio_spec, &_obtained, 0);
+ }
+
+ audio_buffer = ring_buffer_create(4 * _obtained.size);
+
+ SDL_Log("Obtained audio spec. Sample rate: %d, channels: %d, samples: %d, size: %d",+ _obtained.freq, _obtained.channels, _obtained.samples, +_obtained.size);
+
+ SDL_PauseAudioDevice(sdl_audio_device_id, 0);
+
+ // Good to go
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Starting capture");
+ if ((rc = benchmark_in()) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Capture failed to start: %d", rc);
+ return rc;
+ }
+
+ SDL_Log("Successful init");+ return 1;
+ */
+}
+
+void audio_close() {+ if (devh == NULL || !audio_initialized) {+ return;
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Closing audio");
+
+ int rc;
+
+ for (int i = 0; i < NUM_TRANSFERS; i++) {+ rc = libusb_cancel_transfer(xfr[i]);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error cancelling transfer: %s\n",
+ libusb_error_name(rc));
+ }
+ }
+
+ SDL_Log("Freeing interface %d", IFACE_NUM);+
+ rc = libusb_release_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error releasing interface: %s\n", libusb_error_name(rc));
+ return;
+ }
+
+ if (sdl_audio_device_id != 0) {+ SDL_Log("Closing audio device %d", sdl_audio_device_id);+ const SDL_AudioDeviceID device = sdl_audio_device_id;
+ sdl_audio_device_id = 0;
+ SDL_CloseAudioDevice(device);
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Audio closed");
+
+ ring_buffer_free(audio_buffer);
+}
+
+void audio_toggle(unsigned int audio_buffer_size, const char *output_device_name) {+ SDL_Log("Libusb audio toggling not implemented yet");+}
+
+#endif
--- /dev/null
+++ b/src/backends/audio_sdl.c
@@ -1,0 +1,163 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef USE_LIBUSB
+#include "audio.h"
+#include <SDL3/SDL.h>
+#include <stdint.h>
+
+SDL_AudioStream *audio_stream_in, *audio_stream_out;
+
+static unsigned int audio_paused = 0;
+static unsigned int audio_initialized = 0;
+static SDL_AudioSpec audio_spec_in = {SDL_AUDIO_S16LE, 2, 44100};+static SDL_AudioSpec audio_spec_out = {SDL_AUDIO_S16LE, 2, 44100};+
+static void SDLCALL audio_cb_out(void *userdata, SDL_AudioStream *stream, int length, int unused) {+ // suppress compiler warnings
+ (void)userdata;
+ (void)length;
+ (void)unused;
+
+ const int bytes_available = SDL_GetAudioStreamAvailable(audio_stream_in);
+ if (bytes_available == -1) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+ "Error getting available audio stream bytes: %s, destroying audio",
+ SDL_GetError());
+ audio_close();
+ return;
+ }
+
+ Uint8 *src_audio_data = SDL_malloc(bytes_available);
+ const int bytes_read = SDL_GetAudioStreamData(audio_stream_in, src_audio_data, bytes_available);
+ if (bytes_read == bytes_available) {+ SDL_PutAudioStreamData(stream, src_audio_data, bytes_read);
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+ "Error getting available audio stream bytes: %s, destroying audio",
+ SDL_GetError());
+ audio_close();
+ }
+ SDL_free(src_audio_data);
+}
+
+void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size) {+ if (!audio_initialized) {+ audio_initialize(output_device_name, audio_buffer_size);
+ return;
+ }
+ if (audio_paused) {+ SDL_ResumeAudioStreamDevice(audio_stream_out);
+ SDL_ResumeAudioStreamDevice(audio_stream_in);
+ } else {+ SDL_PauseAudioStreamDevice(audio_stream_in);
+ SDL_PauseAudioStreamDevice(audio_stream_out);
+ }
+ audio_paused = !audio_paused;
+ SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");
+}
+
+int audio_initialize(const char *output_device_name, const unsigned int audio_buffer_size) {+
+ // wait for system to initialize possible new audio devices
+ SDL_Delay(500);
+
+ int num_devices_in, num_devices_out;
+ SDL_AudioDeviceID m8_device_id = 0;
+ SDL_AudioDeviceID output_device_id = 0;
+
+ SDL_AudioDeviceID *devices_in = SDL_GetAudioRecordingDevices(&num_devices_in);
+ if (!devices_in) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio capture devices, SDL Error: %s", SDL_GetError());
+ return 0;
+ }
+
+ SDL_AudioDeviceID *devices_out = SDL_GetAudioPlaybackDevices(&num_devices_out);
+ if (!devices_out) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio playback devices, SDL Error: %s",
+ SDL_GetError());
+ return 0;
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audio input devices:");
+ for (int i = 0; i < num_devices_in; i++) {+ const SDL_AudioDeviceID instance_id = devices_in[i];
+ const char *device_name = SDL_GetAudioDeviceName(instance_id);
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
+ if (SDL_strstr(device_name, "M8") != NULL) {+ SDL_Log("M8 Audio Input device found: %s", device_name);+ m8_device_id = instance_id;
+ }
+ }
+
+ if (output_device_name != NULL) {+ for (int i = 0; i < num_devices_out; i++) {+ const SDL_AudioDeviceID instance_id = devices_out[i];
+ const char *device_name = SDL_GetAudioDeviceName(instance_id);
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
+ if (SDL_strcasestr(device_name, output_device_name) != NULL) {+ SDL_Log("Requested output device found: %s", device_name);+ output_device_id = instance_id;
+ }
+ }
+ }
+
+ SDL_free(devices_in);
+ SDL_free(devices_out);
+
+ if (!output_device_id) {+ output_device_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+ }
+
+ if (!m8_device_id) {+ // forget about it
+ SDL_Log("Cannot find M8 audio input device");+ return 0;
+ }
+
+ char audio_buffer_size_str[256];
+ SDL_snprintf(audio_buffer_size_str, sizeof(audio_buffer_size_str), "%d", audio_buffer_size);
+ if (audio_buffer_size > 0) {+ SDL_Log("Setting requested audio device sample frames to %d", audio_buffer_size);+ SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, audio_buffer_size_str);
+ }
+
+ audio_stream_out =
+ SDL_OpenAudioDeviceStream(output_device_id, &audio_spec_out, audio_cb_out, NULL);
+
+ if (!audio_stream_out) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio output device: %s", SDL_GetError());
+ return 0;
+ }
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audiospec Out: format %d, channels %d, rate %d",
+ audio_spec_out.format, audio_spec_out.channels, audio_spec_out.freq);
+
+ audio_stream_in = SDL_OpenAudioDeviceStream(m8_device_id, &audio_spec_in, NULL, NULL);
+ if (!audio_stream_in) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio input device: %s", SDL_GetError());
+ SDL_DestroyAudioStream(audio_stream_out);
+ return 0;
+ }
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audiospec In: format %d, channels %d, rate %d",
+ audio_spec_in.format, audio_spec_in.channels, audio_spec_in.freq);
+
+ SDL_SetAudioStreamFormat(audio_stream_in, &audio_spec_in, &audio_spec_out);
+
+ SDL_ResumeAudioStreamDevice(audio_stream_out);
+ SDL_ResumeAudioStreamDevice(audio_stream_in);
+
+ audio_paused = 0;
+ audio_initialized = 1;
+
+ return 1;
+}
+
+void audio_close() {+ if (!audio_initialized)
+ return;
+ SDL_Log("Closing audio devices");+ SDL_DestroyAudioStream(audio_stream_in);
+ SDL_DestroyAudioStream(audio_stream_out);
+ audio_initialized = 0;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/m8.h
@@ -1,0 +1,17 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef _M8_H_
+#define _M8_H_
+
+#include "../config.h"
+
+int m8_initialize(int verbose, const char *preferred_device);
+int m8_list_devices(void);
+int m8_reset_display(void);
+int m8_enable_and_reset_display(void);
+int m8_send_msg_controller(const unsigned char input);
+int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity);
+int m8_process_data(config_params_s conf);
+int m8_close(void);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/src/backends/m8_libserialport.c
@@ -1,0 +1,334 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+// Contains portions of code from libserialport's examples released to the
+// public domain
+
+#ifdef USE_LIBSERIALPORT
+#include <SDL3/SDL.h>
+#include <libserialport.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../command.h"
+#include "../config.h"
+#include "m8.h"
+#include "slip.h"
+
+// maximum amount of bytes to read from the serial in one read()
+#define serial_read_size 1024
+
+struct sp_port *m8_port = NULL;
+// allocate memory for serial buffers
+static uint8_t serial_buffer[serial_read_size] = {0};+static uint8_t slip_buffer[serial_read_size] = {0};+static slip_handler_s slip;
+static uint16_t zero_byte_packets = 0; // used to detect device disconnection
+
+SDL_Thread *serial_thread = NULL;
+
+// Helper function for error handling
+static int check(enum sp_return result);
+
+static int detect_m8_serial_device(const struct sp_port *m8_port) {+ // Check the connection method - we want USB serial devices
+ const enum sp_transport transport = sp_get_port_transport(m8_port);
+
+ if (transport == SP_TRANSPORT_USB) {+ // Get the USB vendor and product IDs.
+ int usb_vid, usb_pid;
+ sp_get_port_usb_vid_pid(m8_port, &usb_vid, &usb_pid);
+
+ if (usb_vid == 0x16C0 && usb_pid == 0x048A)
+ return 1;
+ }
+
+ return 0;
+}
+
+int m8_list_devices() {+ struct sp_port **port_list;
+ const enum sp_return result = sp_list_ports(&port_list);
+
+ if (result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ abort();
+ }
+
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ SDL_Log("Found M8 device: %s", sp_get_port_name(port));+ }
+ }
+
+ sp_free_port_list(port_list);
+ return 0;
+}
+
+// Checks for connected devices and whether the specified device still exists
+int check_serial_port() {+ int device_found = 0;
+
+ /* A pointer to a null-terminated array of pointers to
+ * struct sp_port, which will contain the ports found.*/
+ struct sp_port **port_list;
+
+ /* Call sp_list_ports() to get the ports. The port_list
+ * pointer will be updated to refer to the array created. */
+ const enum sp_return result = sp_list_ports(&port_list);
+
+ if (result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ abort();
+ }
+
+ /* Iterate through the ports. When port_list[i] is NULL
+ * this indicates the end of the list. */
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ if (strcmp(sp_get_port_name(port), sp_get_port_name(m8_port)) == 0)
+ device_found = 1;
+ }
+ }
+
+ sp_free_port_list(port_list);
+ return device_found;
+}
+
+int m8_initialize(const int verbose, const char *preferred_device) {+ if (m8_port != NULL) {+ // Port is already initialized
+ return 1;
+ }
+
+ // settings for the slip packet handler
+ static const slip_descriptor_s slip_descriptor = {+ .buf = slip_buffer,
+ .buf_size = sizeof(slip_buffer),
+ .recv_message = process_command, // the function where complete slip
+ // packets are processed further
+ };
+
+ slip_init(&slip, &slip_descriptor);
+
+ /* A pointer to a null-terminated array of pointers to
+ * struct sp_port, which will contain the ports found.*/
+ struct sp_port **port_list;
+
+ if (verbose)
+ SDL_Log("Looking for USB serial devices.\n");+
+ /* Call sp_list_ports() to get the ports. The port_list
+ * pointer will be updated to refer to the array created. */
+ enum sp_return result = sp_list_ports(&port_list);
+
+ if (result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ abort();
+ }
+
+ /* Iterate through the ports. When port_list[i] is NULL
+ * this indicates the end of the list. */
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ char *port_name = sp_get_port_name(port);
+ SDL_Log("Found M8 in %s.\n", port_name);+ sp_copy_port(port, &m8_port);
+ if (preferred_device != NULL && strcmp(preferred_device, port_name) == 0) {+ SDL_Log("Found preferred device, breaking");+ break;
+ }
+ }
+ }
+
+ sp_free_port_list(port_list);
+
+ if (m8_port != NULL) {+ // Open the serial port and configure it
+ SDL_Log("Opening port.");+
+ result = sp_open(m8_port, SP_MODE_READ_WRITE);
+ if (check(result) != SP_OK)
+ return 0;
+
+ result = sp_set_baudrate(m8_port, 115200);
+ if (check(result) != SP_OK)
+ return 0;
+
+ result = sp_set_bits(m8_port, 8);
+ if (check(result) != SP_OK)
+ return 0;
+
+ result = sp_set_parity(m8_port, SP_PARITY_NONE);
+ if (check(result) != SP_OK)
+ return 0;
+
+ result = sp_set_stopbits(m8_port, 1);
+ if (check(result) != SP_OK)
+ return 0;
+
+ result = sp_set_flowcontrol(m8_port, SP_FLOWCONTROL_NONE);
+ if (check(result) != SP_OK)
+ return 0;
+ } else {+ if (verbose) {+ SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8.\n");
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+// Helper function for error handling.
+static int check(const enum sp_return result) {+ char *error_message;
+
+ switch (result) {+ case SP_ERR_ARG:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid argument.\n");
+ break;
+ case SP_ERR_FAIL:
+ error_message = sp_last_error_message();
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Failed: %s\n", error_message);
+ sp_free_error_message(error_message);
+ break;
+ case SP_ERR_SUPP:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Not supported.\n");
+ break;
+ case SP_ERR_MEM:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Couldn't allocate memory.\n");
+ break;
+ case SP_OK:
+ default:
+ break;
+ }
+ return result;
+}
+
+int m8_reset_display() {+ SDL_Log("Reset display\n");+
+ const char buf[1] = {'R'};+ const int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_and_reset_display() {+ SDL_Log("Enabling and resetting M8 display\n");+
+ const char buf[1] = {'E'};+ int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
+ return 0;
+ }
+
+ result = m8_reset_display();
+
+ return result;
+}
+
+int disconnect() {+ SDL_Log("Disconnecting M8\n");+
+ const char buf[1] = {'D'};+
+ int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
+ result = 0;
+ }
+ sp_close(m8_port);
+ sp_free_port(m8_port);
+ m8_port = NULL;
+ return result;
+}
+
+int serial_read(uint8_t *serial_buf, const int count) {+ return sp_nonblocking_read(m8_port, serial_buf, count);
+}
+
+int m8_send_msg_controller(const uint8_t input) {+ const char buf[2] = {'C', input};+ const size_t nbytes = 2;
+ const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
+ return -1;
+ }
+ return 1;
+}
+
+int m8_send_msg_keyjazz(const uint8_t note, uint8_t velocity) {+ if (velocity > 0x7F)
+ velocity = 0x7F;
+ const char buf[3] = {'K', note, velocity};+ const size_t nbytes = 3;
+ const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
+ return -1;
+ }
+
+ return 1;
+}
+
+int m8_process_data(const config_params_s conf) {+ while (1) {+ // read serial port
+ const int bytes_read = serial_read(serial_buffer, serial_read_size);
+ if (bytes_read < 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial.", bytes_read);
+ disconnect();
+ return -1;
+ }
+ if (bytes_read > 0) {+ // input from device: reset the zero byte counter and create a
+ // pointer to the serial buffer
+ zero_byte_packets = 0;
+ const uint8_t *cur = serial_buffer;
+ const uint8_t *end = serial_buffer + bytes_read;
+ while (cur < end) {+ // process the incoming bytes into commands and draw them
+ const int n = slip_read_byte(&slip, *cur++);
+ if (n != SLIP_NO_ERROR) {+ if (n == SLIP_ERROR_INVALID_PACKET) {+ m8_reset_display();
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
+ }
+ }
+ }
+ } else {+ // zero byte packet, increment counter
+ zero_byte_packets++;
+ if (zero_byte_packets > conf.wait_packets) {+ zero_byte_packets = 0;
+
+ // try opening the serial port to check if it's alive
+ if (check_serial_port()) {+ // the device is still there, carry on
+ break;
+ }
+ disconnect();
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+int m8_close() { return disconnect(); }+#endif
--- /dev/null
+++ b/src/backends/m8_libusb.c
@@ -1,0 +1,367 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+// Contains portions of code from libserialport's examples released to the
+// public domain
+#ifdef USE_LIBUSB
+
+#include <SDL3/SDL.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int ep_out_addr = 0x03;
+static int ep_in_addr = 0x83;
+
+#define ACM_CTRL_DTR 0x01
+#define ACM_CTRL_RTS 0x02
+
+#define M8_VID 0x16c0
+#define M8_PID 0x048a
+
+libusb_context *ctx = NULL;
+libusb_device_handle *devh = NULL;
+
+static int do_exit = 0;
+
+int m8_list_devices() {+ int r;
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+
+ libusb_device **device_list = NULL;
+ int count = libusb_get_device_list(ctx, &device_list);
+ for (size_t idx = 0; idx < count; ++idx) {+ libusb_device *device = device_list[idx];
+ struct libusb_device_descriptor desc;
+ int rc = libusb_get_device_descriptor(device, &desc);
+ if (rc < 0) {+ SDL_Log("Error");+ libusb_free_device_list(device_list, 1);
+ return rc;
+ }
+
+ if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {+ SDL_Log("Found M8 device: %d:%d\n", libusb_get_port_number(device),+ libusb_get_bus_number(device));
+ }
+ }
+ libusb_free_device_list(device_list, 1);
+ return 0;
+}
+
+int usb_loop(void *data) {+ SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
+ while (!do_exit) {+ int rc = libusb_handle_events(ctx);
+ if (rc != LIBUSB_SUCCESS) {+ SDL_Log("Audio loop error: %s\n", libusb_error_name(rc));+ break;
+ }
+ }
+ return 0;
+}
+
+static SDL_Thread *usb_thread;
+
+static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer) {+ int *completed = transfer->user_data;
+ *completed = 1;
+}
+
+int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {+ if (devh == NULL) {+ return -1;
+ }
+
+ int completed = 0;
+
+ struct libusb_transfer *transfer;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, devh, endpoint, serial_buf, count, xfr_cb_in, &completed,
+ timeout_ms);
+ int r = libusb_submit_transfer(transfer);
+
+ if (r < 0) {+ SDL_Log("Error");+ libusb_free_transfer(transfer);
+ return r;
+ }
+
+retry:
+ libusb_lock_event_waiters(ctx);
+ while (!completed) {+ if (!libusb_event_handler_active(ctx)) {+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+ libusb_wait_for_event(ctx, NULL);
+ }
+ libusb_unlock_event_waiters(ctx);
+
+ int actual_length = transfer->actual_length;
+
+ libusb_free_transfer(transfer);
+
+ return actual_length;
+}
+
+int blocking_write(void *buf, int count, unsigned int timeout_ms) {+ return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
+}
+
+int m8_process_data(uint8_t *serial_buf, int count) {+ return bulk_transfer(ep_in_addr, serial_buf, count, 1);
+}
+
+int check_serial_port() {+ // Reading will fail anyway when the device is not present anymore
+ return 1;
+}
+
+int init_interface() {+
+ if (devh == NULL) {+ SDL_Log("Device not initialised!");+ return 0;
+ }
+
+ int rc;
+
+ for (int if_num = 0; if_num < 2; if_num++) {+ if (libusb_kernel_driver_active(devh, if_num)) {+ SDL_Log("Detaching kernel driver for interface %d", if_num);+ libusb_detach_kernel_driver(devh, if_num);
+ }
+ rc = libusb_claim_interface(devh, if_num);
+ if (rc < 0) {+ SDL_Log("Error claiming interface: %s", libusb_error_name(rc));+ libusb_close(devh);
+ devh = NULL;
+ return 0;
+ }
+ }
+
+ /* Start configuring the device:
+ * - set line state
+ */
+ SDL_Log("Setting line state");+ rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, 0);
+ if (rc < 0) {+ SDL_Log("Error during control transfer: %s", libusb_error_name(rc));+ return 0;
+ }
+
+ /* - set line encoding: here 115200 8N1
+ * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
+ */
+ SDL_Log("Set line encoding");+ unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};+ rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding, sizeof(encoding), 0);
+ if (rc < 0) {+ SDL_Log("Error during control transfer: %s", libusb_error_name(rc));+ return 0;
+ }
+
+ usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
+
+ return 1;
+}
+
+int init_serial_with_file_descriptor(int file_descriptor) {+ SDL_Log("Initialising serial with file descriptor");+
+ if (file_descriptor <= 0) {+ SDL_Log("Invalid file descriptor: %d", file_descriptor);+ return 0;
+ }
+
+ int r;
+ r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
+ if (r != LIBUSB_SUCCESS) {+ SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));+ return 0;
+ }
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+ r = libusb_wrap_sys_device(ctx, (intptr_t)file_descriptor, &devh);
+ if (r < 0) {+ SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));+ return 0;
+ } else if (devh == NULL) {+ SDL_Log("libusb_wrap_sys_device returned invalid handle");+ return 0;
+ }
+ SDL_Log("USB device init success");+
+ return init_interface();
+}
+
+int m8_initialize(int verbose, char *preferred_device) {+
+ if (devh != NULL) {+ return 1;
+ }
+
+ int r;
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+ if (preferred_device == NULL) {+ devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
+ } else {+ char *port;
+ char *saveptr = NULL;
+ char *bus;
+ port = SDL_strtok_r(preferred_device, ":", &saveptr);
+ bus = SDL_strtok_r(NULL, ":", &saveptr);
+ libusb_device **device_list = NULL;
+ int count = libusb_get_device_list(ctx, &device_list);
+ for (size_t idx = 0; idx < count; ++idx) {+ libusb_device *device = device_list[idx];
+ struct libusb_device_descriptor desc;
+ r = libusb_get_device_descriptor(device, &desc);
+ if (r < 0) {+ SDL_Log("libusb_get_device_descriptor failed: %s", libusb_error_name(r));+ libusb_free_device_list(device_list, 1);
+ return 0;
+ }
+
+ if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {+ SDL_Log("Searching for port %s and bus %s", port, bus);+ if (libusb_get_port_number(device) == SDL_atoi(port) &&
+ libusb_get_bus_number(device) == SDL_atoi(bus)) {+ SDL_Log("Preferred device found, connecting");+ r = libusb_open(device, &devh);
+ if (r < 0) {+ SDL_Log("libusb_open failed: %s", libusb_error_name(r));+ return 0;
+ }
+ }
+ }
+ }
+ libusb_free_device_list(device_list, 1);
+ if (devh == NULL) {+ SDL_Log("Preferred device %s not found, using first available", preferred_device);+ devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
+ }
+ }
+ if (devh == NULL) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,
+ "libusb_open_device_with_vid_pid returned invalid handle");
+ return 0;
+ }
+ SDL_Log("USB device init success");+
+ return init_interface();
+}
+
+int m8_reset_display() {+ int result;
+
+ SDL_Log("Reset display\n");+
+ char buf[1] = {'R'};+
+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_and_reset_display() {+ if (devh == NULL) {+ return 0;
+ }
+
+ int result;
+
+ SDL_Log("Enabling and resetting M8 display\n");+
+ char buf[1] = {'E'};+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
+ return 0;
+ }
+
+ SDL_Delay(5);
+ result = m8_reset_display();
+ return result;
+}
+
+int m8_close() {+
+ char buf[1] = {'D'};+ int result;
+
+ SDL_Log("Disconnecting M8\n");+
+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
+ return -1;
+ }
+
+ int rc;
+
+ if (devh != NULL) {+
+ for (int if_num = 0; if_num < 2; if_num++) {+ rc = libusb_release_interface(devh, if_num);
+ if (rc < 0) {+ SDL_Log("Error releasing interface: %s", libusb_error_name(rc));+ return 0;
+ }
+ }
+
+ do_exit = 1;
+
+ libusb_close(devh);
+ }
+
+ SDL_WaitThread(usb_thread, NULL);
+
+ libusb_exit(ctx);
+
+ return 1;
+}
+
+int m8_send_msg_controller(uint8_t input) {+ char buf[2] = {'C', input};+ int nbytes = 2;
+ int result;
+ result = blocking_write(buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
+ return -1;
+ }
+ return 1;
+}
+
+int m8_send_msg_keyjazz(uint8_t note, uint8_t velocity) {+ if (velocity > 0x7F)
+ velocity = 0x7F;
+ char buf[3] = {'K', note, velocity};+ int nbytes = 3;
+ int result;
+ result = blocking_write(buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
+ return -1;
+ }
+
+ return 1;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/m8_rtmidi.c
@@ -1,0 +1,271 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifdef USE_RTMIDI
+
+#ifndef NDEBUG
+#define RTMIDI_DEBUG
+#endif
+
+#include "../command.h"
+#include "../config.h"
+#include "m8.h"
+#include "queue.h"
+#include <SDL3/SDL.h>
+#include <rtmidi_c.h>
+#include <stdbool.h>
+#include <string.h>
+
+RtMidiInPtr midi_in;
+RtMidiOutPtr midi_out;
+message_queue_s queue;
+
+const unsigned char m8_sysex_header[5] = {0xF0, 0x00, 0x02, 0x61, 0x00};+const unsigned int m8_sysex_header_size = sizeof(m8_sysex_header);
+const unsigned char sysex_message_end = 0xF7;
+
+bool message_is_m8_sysex(const unsigned char *message) {+ if (memcmp(m8_sysex_header, message, m8_sysex_header_size) == 0) {+ return true;
+ }
+ return false;
+}
+
+void midi_decode(const uint8_t *encoded_data, size_t length, uint8_t **decoded_data,
+ size_t *decoded_length) {+ if (length < m8_sysex_header_size) {+ // Invalid data
+ *decoded_data = NULL;
+ *decoded_length = 0;
+ return;
+ }
+
+ // Skip header "F0 00 02 61" and the first MSB byte
+ size_t pos = m8_sysex_header_size + 1;
+
+ // Calculate expected output size (ignoring EOT if present)
+ const size_t expected_output_size =
+ (length - m8_sysex_header_size) - ((length - m8_sysex_header_size) / 8);
+ if (encoded_data[length - 1] == sysex_message_end) {+ length--; // Ignore the EOT byte
+ }
+
+ // Allocate memory for decoded output
+ *decoded_data = (uint8_t *)SDL_malloc(expected_output_size);
+ if (*decoded_data == NULL) {+ *decoded_length = 0;
+ return;
+ }
+
+ uint8_t bit_counter = 0;
+ uint8_t bit_byte_counter = 0;
+ uint8_t *out = *decoded_data;
+ *decoded_length = 0;
+
+ while (pos < length) {+ // Extract MSB from the "bit field" position
+ uint8_t msb = (encoded_data[bit_byte_counter * 8 + m8_sysex_header_size] >> bit_counter) & 0x01;
+
+ // Extract LSB from data byte
+ uint8_t lsb = encoded_data[pos] & 0x7F;
+
+ // Reconstruct original byte, skipping the MSB bytes in output
+ *out = (msb << 7) | lsb;
+ out++;
+ (*decoded_length)++;
+
+ bit_counter++;
+ pos++;
+
+ if (bit_counter == 7) {+ bit_counter = 0;
+ bit_byte_counter++;
+ pos++; // Skip the MSB byte
+ }
+ }
+}
+
+static void midi_callback(double delta_time, const unsigned char *message, size_t message_size,
+ void *user_data) {+ // Unused variables
+ (void)delta_time;
+ (void)user_data;
+
+ if (message_size < 5 || !message_is_m8_sysex(message))
+ return;
+
+ unsigned char *decoded_data;
+ size_t decoded_length;
+ midi_decode(message, message_size, &decoded_data, &decoded_length);
+
+ // If you need to debug incoming MIDI packets, you can uncomment the lines below:
+
+ /* printf("Original data: ");+ for (size_t i = 0; i < message_size; i++) {+ printf("%02X ", message[i]);+ } */
+
+ if (decoded_data) {+ /* printf("\nDecoded MIDI Data: ");+ for (size_t i = 0; i < decoded_length; i++) {+ printf("%02X ", decoded_data[i]);+ }
+ printf("\n"); */+ push_message(&queue, decoded_data, decoded_length);
+ SDL_free(decoded_data);
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Decoding failed.\n");
+ }
+}
+
+int initialize_rtmidi() {+ SDL_Log("Initializing rtmidi");+ midi_in = rtmidi_in_create(RTMIDI_API_UNSPECIFIED, "m8c_in", 1024);
+ midi_out = rtmidi_out_create(RTMIDI_API_UNSPECIFIED, "m8c_out");
+ if (midi_in == NULL || midi_out == NULL) {+ return 0;
+ }
+ return 1;
+}
+
+int m8_initialize(const int verbose, const char *preferred_device) {+
+ int midi_in_initialized = 0;
+ int midi_out_initialized = 0;
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "init_serial called");
+ if (midi_in == NULL || midi_out == NULL) {+ initialize_rtmidi();
+ };
+ const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
+ if (verbose)
+ SDL_Log("Number of MIDI in ports: %d", ports_total_in);+ for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {+ int port_name_length_in;
+ rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
+ char port_name[port_name_length_in];
+ rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
+ if (verbose)
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "MIDI IN port %d, name: %s", port_number, port_name);
+
+ if (SDL_strncmp("M8", port_name, 2) == 0) {+ if (verbose)
+ SDL_Log("Found M8 Input in MIDI port %d", port_number);+ rtmidi_in_ignore_types(midi_in, false, true, true);
+ rtmidi_open_port(midi_in, port_number, "M8");
+ midi_in_initialized = 1;
+ rtmidi_open_port(midi_out, port_number, "M8");
+ midi_out_initialized = 1;
+ init_queue(&queue);
+ if (preferred_device != NULL && SDL_strcmp(preferred_device, port_name) == 0) {+ SDL_Log("Found preferred device, breaking");+ break;
+ }
+ }
+ }
+ return midi_in_initialized && midi_out_initialized;
+}
+
+int check_serial_port(void) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "check_serial_port called");
+ return 0;
+}
+
+int m8_reset_display(void) {+ SDL_Log("Reset display");+ const unsigned char reset_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'R', 0xF7};+ const int result = rtmidi_out_send_message(midi_out, &reset_sysex[0], sizeof(reset_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_and_reset_display(void) {+ rtmidi_in_set_callback(midi_in, midi_callback, NULL);
+ const unsigned char enable_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'E', 0xF7};+ int result = rtmidi_out_send_message(midi_out, &enable_sysex[0], sizeof(enable_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to enable display");
+ return 0;
+ }
+ result = m8_reset_display();
+ return result;
+}
+
+int disconnect(void) {+ const unsigned char disconnect_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'D', 0xF7};+ const int result =
+ rtmidi_out_send_message(midi_out, &disconnect_sysex[0], sizeof(disconnect_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send disconnect");
+ }
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Freeing MIDI ports");
+ rtmidi_in_free(midi_in);
+ rtmidi_out_free(midi_out);
+ return !result;
+}
+
+int m8_send_msg_controller(const unsigned char input) {+ const unsigned char input_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', input, 0xF7};+ const int result = rtmidi_out_send_message(midi_out, &input_sysex[0], sizeof(input_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send key input message");
+ return 0;
+ }
+ return 1;
+}
+
+int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity) {+ if (velocity > 0x7F) {+ velocity = 0x7F;
+ }
+ const unsigned char keyjazz_sysex[10] = {0xF0, 0x00, 0x02, 0x61, 0x00,+ 0x00, 'K', note, velocity, 0xF7};
+ const int result = rtmidi_out_send_message(midi_out, &keyjazz_sysex[0], sizeof(keyjazz_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send keyjazz input message");
+ return 0;
+ }
+ return 1;
+}
+
+int m8_process_data(config_params_s conf) {+ (void)conf; // unused parameter
+
+ unsigned char *command;
+ size_t length = 0;
+ while ((command = pop_message(&queue, &length)) != NULL) {+ process_command(command, length);
+ }
+ return 1;
+}
+
+int m8_close() {+ if (queue.mutex != NULL) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Destroying command queue");
+ SDL_DestroyMutex(queue.mutex);
+ SDL_DestroyCondition(queue.cond);
+ }
+ return disconnect();
+}
+
+int m8_list_devices() {+ if (midi_in == NULL || midi_out == NULL) {+ initialize_rtmidi();
+ };
+ const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
+ SDL_Log("Number of MIDI in ports: %d", ports_total_in);+ for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {+ int port_name_length_in;
+ rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
+ char port_name[port_name_length_in];
+ rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
+ SDL_Log("MIDI IN port %d, name: %s", port_number, port_name);+ }
+ rtmidi_in_free(midi_in);
+ rtmidi_out_free(midi_out);
+ return 1;
+}
+
+#endif
--- a/src/backends/rtmidi.c
+++ /dev/null
@@ -1,271 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifdef USE_RTMIDI
-
-#ifndef NDEBUG
-#define RTMIDI_DEBUG
-#endif
-
-#include "rtmidi.h"
-#include "../command.h"
-#include "../config.h"
-#include "queue.h"
-#include <SDL3/SDL.h>
-#include <rtmidi_c.h>
-#include <stdbool.h>
-#include <string.h>
-
-RtMidiInPtr midi_in;
-RtMidiOutPtr midi_out;
-message_queue_s queue;
-
-const unsigned char m8_sysex_header[5] = {0xF0, 0x00, 0x02, 0x61, 0x00};-const unsigned int m8_sysex_header_size = sizeof(m8_sysex_header);
-const unsigned char sysex_message_end = 0xF7;
-
-bool message_is_m8_sysex(const unsigned char *message) {- if (memcmp(m8_sysex_header, message, m8_sysex_header_size) == 0) {- return true;
- }
- return false;
-}
-
-void midi_decode(const uint8_t *encoded_data, size_t length, uint8_t **decoded_data,
- size_t *decoded_length) {- if (length < m8_sysex_header_size) {- // Invalid data
- *decoded_data = NULL;
- *decoded_length = 0;
- return;
- }
-
- // Skip header "F0 00 02 61" and the first MSB byte
- size_t pos = m8_sysex_header_size + 1;
-
- // Calculate expected output size (ignoring EOT if present)
- const size_t expected_output_size =
- (length - m8_sysex_header_size) - ((length - m8_sysex_header_size) / 8);
- if (encoded_data[length - 1] == sysex_message_end) {- length--; // Ignore the EOT byte
- }
-
- // Allocate memory for decoded output
- *decoded_data = (uint8_t *)SDL_malloc(expected_output_size);
- if (*decoded_data == NULL) {- *decoded_length = 0;
- return;
- }
-
- uint8_t bit_counter = 0;
- uint8_t bit_byte_counter = 0;
- uint8_t *out = *decoded_data;
- *decoded_length = 0;
-
- while (pos < length) {- // Extract MSB from the "bit field" position
- uint8_t msb = (encoded_data[bit_byte_counter * 8 + m8_sysex_header_size] >> bit_counter) & 0x01;
-
- // Extract LSB from data byte
- uint8_t lsb = encoded_data[pos] & 0x7F;
-
- // Reconstruct original byte, skipping the MSB bytes in output
- *out = (msb << 7) | lsb;
- out++;
- (*decoded_length)++;
-
- bit_counter++;
- pos++;
-
- if (bit_counter == 7) {- bit_counter = 0;
- bit_byte_counter++;
- pos++; // Skip the MSB byte
- }
- }
-}
-
-static void midi_callback(double delta_time, const unsigned char *message, size_t message_size,
- void *user_data) {- // Unused variables
- (void)delta_time;
- (void)user_data;
-
- if (message_size < 5 || !message_is_m8_sysex(message))
- return;
-
- unsigned char *decoded_data;
- size_t decoded_length;
- midi_decode(message, message_size, &decoded_data, &decoded_length);
-
- // If you need to debug incoming MIDI packets, you can uncomment the lines below:
-
- /* printf("Original data: ");- for (size_t i = 0; i < message_size; i++) {- printf("%02X ", message[i]);- } */
-
- if (decoded_data) {- /* printf("\nDecoded MIDI Data: ");- for (size_t i = 0; i < decoded_length; i++) {- printf("%02X ", decoded_data[i]);- }
- printf("\n"); */- push_message(&queue, decoded_data, decoded_length);
- SDL_free(decoded_data);
- } else {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Decoding failed.\n");
- }
-}
-
-int initialize_rtmidi() {- SDL_Log("Initializing rtmidi");- midi_in = rtmidi_in_create(RTMIDI_API_UNSPECIFIED, "m8c_in", 1024);
- midi_out = rtmidi_out_create(RTMIDI_API_UNSPECIFIED, "m8c_out");
- if (midi_in == NULL || midi_out == NULL) {- return 0;
- }
- return 1;
-}
-
-int m8_initialize(const int verbose, const char *preferred_device) {-
- int midi_in_initialized = 0;
- int midi_out_initialized = 0;
-
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "init_serial called");
- if (midi_in == NULL || midi_out == NULL) {- initialize_rtmidi();
- };
- const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
- if (verbose)
- SDL_Log("Number of MIDI in ports: %d", ports_total_in);- for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {- int port_name_length_in;
- rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
- char port_name[port_name_length_in];
- rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
- if (verbose)
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "MIDI IN port %d, name: %s", port_number, port_name);
-
- if (SDL_strncmp("M8", port_name, 2) == 0) {- if (verbose)
- SDL_Log("Found M8 Input in MIDI port %d", port_number);- rtmidi_in_ignore_types(midi_in, false, true, true);
- rtmidi_open_port(midi_in, port_number, "M8");
- midi_in_initialized = 1;
- rtmidi_open_port(midi_out, port_number, "M8");
- midi_out_initialized = 1;
- init_queue(&queue);
- if (preferred_device != NULL && SDL_strcmp(preferred_device, port_name) == 0) {- SDL_Log("Found preferred device, breaking");- break;
- }
- }
- }
- return midi_in_initialized && midi_out_initialized;
-}
-
-int check_serial_port(void) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "check_serial_port called");
- return 0;
-}
-
-int m8_reset_display(void) {- SDL_Log("Reset display");- const unsigned char reset_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'R', 0xF7};- const int result = rtmidi_out_send_message(midi_out, &reset_sysex[0], sizeof(reset_sysex));
- if (result != 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
- return 0;
- }
- return 1;
-}
-
-int m8_enable_and_reset_display(void) {- rtmidi_in_set_callback(midi_in, midi_callback, NULL);
- const unsigned char enable_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'E', 0xF7};- int result = rtmidi_out_send_message(midi_out, &enable_sysex[0], sizeof(enable_sysex));
- if (result != 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to enable display");
- return 0;
- }
- result = m8_reset_display();
- return result;
-}
-
-int disconnect(void) {- const unsigned char disconnect_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'D', 0xF7};- const int result =
- rtmidi_out_send_message(midi_out, &disconnect_sysex[0], sizeof(disconnect_sysex));
- if (result != 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send disconnect");
- }
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Freeing MIDI ports");
- rtmidi_in_free(midi_in);
- rtmidi_out_free(midi_out);
- return !result;
-}
-
-int m8_send_msg_controller(const unsigned char input) {- const unsigned char input_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', input, 0xF7};- const int result = rtmidi_out_send_message(midi_out, &input_sysex[0], sizeof(input_sysex));
- if (result != 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send key input message");
- return 0;
- }
- return 1;
-}
-
-int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity) {- if (velocity > 0x7F) {- velocity = 0x7F;
- }
- const unsigned char keyjazz_sysex[10] = {0xF0, 0x00, 0x02, 0x61, 0x00,- 0x00, 'K', note, velocity, 0xF7};
- const int result = rtmidi_out_send_message(midi_out, &keyjazz_sysex[0], sizeof(keyjazz_sysex));
- if (result != 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send keyjazz input message");
- return 0;
- }
- return 1;
-}
-
-int m8_process_data(config_params_s conf) {- (void)conf; // unused parameter
-
- unsigned char *command;
- size_t length = 0;
- while ((command = pop_message(&queue, &length)) != NULL) {- process_command(command, length);
- }
- return 1;
-}
-
-int m8_close() {- if (queue.mutex != NULL) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Destroying command queue");
- SDL_DestroyMutex(queue.mutex);
- SDL_DestroyCondition(queue.cond);
- }
- return disconnect();
-}
-
-int m8_list_devices() {- if (midi_in == NULL || midi_out == NULL) {- initialize_rtmidi();
- };
- const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
- SDL_Log("Number of MIDI in ports: %d", ports_total_in);- for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {- int port_name_length_in;
- rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
- char port_name[port_name_length_in];
- rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
- SDL_Log("MIDI IN port %d, name: %s", port_number, port_name);- }
- rtmidi_in_free(midi_in);
- rtmidi_out_free(midi_out);
- return 1;
-}
-
-#endif
--- a/src/backends/rtmidi.h
+++ /dev/null
@@ -1,19 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifndef _MIDI_H_
-#define _MIDI_H_
-#ifdef USE_RTMIDI
-
-#include "../config.h"
-
-int m8_initialize(int verbose, const char *preferred_device);
-int m8_list_devices(void);
-int m8_reset_display(void);
-int m8_enable_and_reset_display(void);
-int m8_send_msg_controller(const unsigned char input);
-int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity);
-int m8_process_data(config_params_s conf);
-int m8_close(void);
-
-#endif
-#endif
\ No newline at end of file
--- a/src/backends/serialport.c
+++ /dev/null
@@ -1,331 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-// Contains portions of code from libserialport's examples released to the
-// public domain
-
-#ifdef USE_LIBSERIALPORT
-#include <SDL3/SDL.h>
-#include <libserialport.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../command.h"
-#include "../config.h"
-#include "serialport.h"
-#include "slip.h"
-
-struct sp_port *m8_port = NULL;
-// allocate memory for serial buffers
-static uint8_t serial_buffer[serial_read_size] = {0};-static uint8_t slip_buffer[serial_read_size] = {0};-static slip_handler_s slip;
-static uint16_t zero_byte_packets = 0; // used to detect device disconnection
-
-SDL_Thread *serial_thread = NULL;
-
-// Helper function for error handling
-static int check(enum sp_return result);
-
-static int detect_m8_serial_device(const struct sp_port *m8_port) {- // Check the connection method - we want USB serial devices
- const enum sp_transport transport = sp_get_port_transport(m8_port);
-
- if (transport == SP_TRANSPORT_USB) {- // Get the USB vendor and product IDs.
- int usb_vid, usb_pid;
- sp_get_port_usb_vid_pid(m8_port, &usb_vid, &usb_pid);
-
- if (usb_vid == 0x16C0 && usb_pid == 0x048A)
- return 1;
- }
-
- return 0;
-}
-
-int m8_list_devices() {- struct sp_port **port_list;
- const enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- SDL_Log("Found M8 device: %s", sp_get_port_name(port));- }
- }
-
- sp_free_port_list(port_list);
- return 0;
-}
-
-// Checks for connected devices and whether the specified device still exists
-int check_serial_port() {- int device_found = 0;
-
- /* A pointer to a null-terminated array of pointers to
- * struct sp_port, which will contain the ports found.*/
- struct sp_port **port_list;
-
- /* Call sp_list_ports() to get the ports. The port_list
- * pointer will be updated to refer to the array created. */
- const enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- /* Iterate through the ports. When port_list[i] is NULL
- * this indicates the end of the list. */
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- if (strcmp(sp_get_port_name(port), sp_get_port_name(m8_port)) == 0)
- device_found = 1;
- }
- }
-
- sp_free_port_list(port_list);
- return device_found;
-}
-
-int m8_initialize(const int verbose, const char *preferred_device) {- if (m8_port != NULL) {- // Port is already initialized
- return 1;
- }
-
- // settings for the slip packet handler
- static const slip_descriptor_s slip_descriptor = {- .buf = slip_buffer,
- .buf_size = sizeof(slip_buffer),
- .recv_message = process_command, // the function where complete slip
- // packets are processed further
- };
-
- slip_init(&slip, &slip_descriptor);
-
- /* A pointer to a null-terminated array of pointers to
- * struct sp_port, which will contain the ports found.*/
- struct sp_port **port_list;
-
- if (verbose)
- SDL_Log("Looking for USB serial devices.\n");-
- /* Call sp_list_ports() to get the ports. The port_list
- * pointer will be updated to refer to the array created. */
- enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- /* Iterate through the ports. When port_list[i] is NULL
- * this indicates the end of the list. */
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- char *port_name = sp_get_port_name(port);
- SDL_Log("Found M8 in %s.\n", port_name);- sp_copy_port(port, &m8_port);
- if (preferred_device != NULL && strcmp(preferred_device, port_name) == 0) {- SDL_Log("Found preferred device, breaking");- break;
- }
- }
- }
-
- sp_free_port_list(port_list);
-
- if (m8_port != NULL) {- // Open the serial port and configure it
- SDL_Log("Opening port.");-
- result = sp_open(m8_port, SP_MODE_READ_WRITE);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_baudrate(m8_port, 115200);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_bits(m8_port, 8);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_parity(m8_port, SP_PARITY_NONE);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_stopbits(m8_port, 1);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_flowcontrol(m8_port, SP_FLOWCONTROL_NONE);
- if (check(result) != SP_OK)
- return 0;
- } else {- if (verbose) {- SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8.\n");
- }
- return 0;
- }
-
- return 1;
-}
-
-// Helper function for error handling.
-static int check(const enum sp_return result) {- char *error_message;
-
- switch (result) {- case SP_ERR_ARG:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid argument.\n");
- break;
- case SP_ERR_FAIL:
- error_message = sp_last_error_message();
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Failed: %s\n", error_message);
- sp_free_error_message(error_message);
- break;
- case SP_ERR_SUPP:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Not supported.\n");
- break;
- case SP_ERR_MEM:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Couldn't allocate memory.\n");
- break;
- case SP_OK:
- default:
- break;
- }
- return result;
-}
-
-int m8_reset_display() {- SDL_Log("Reset display\n");-
- const char buf[1] = {'R'};- const int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
- return 0;
- }
- return 1;
-}
-
-int m8_enable_and_reset_display() {- SDL_Log("Enabling and resetting M8 display\n");-
- const char buf[1] = {'E'};- int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
- return 0;
- }
-
- result = m8_reset_display();
-
- return result;
-}
-
-int disconnect() {- SDL_Log("Disconnecting M8\n");-
- const char buf[1] = {'D'};-
- int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
- result = 0;
- }
- sp_close(m8_port);
- sp_free_port(m8_port);
- m8_port = NULL;
- return result;
-}
-
-int serial_read(uint8_t *serial_buf, const int count) {- return sp_nonblocking_read(m8_port, serial_buf, count);
-}
-
-int m8_send_msg_controller(const uint8_t input) {- const char buf[2] = {'C', input};- const size_t nbytes = 2;
- const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
- return -1;
- }
- return 1;
-}
-
-int m8_send_msg_keyjazz(const uint8_t note, uint8_t velocity) {- if (velocity > 0x7F)
- velocity = 0x7F;
- const char buf[3] = {'K', note, velocity};- const size_t nbytes = 3;
- const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
- return -1;
- }
-
- return 1;
-}
-
-int m8_process_data(const config_params_s conf) {- while (1) {- // read serial port
- const int bytes_read = serial_read(serial_buffer, serial_read_size);
- if (bytes_read < 0) {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial.", bytes_read);
- disconnect();
- return -1;
- }
- if (bytes_read > 0) {- // input from device: reset the zero byte counter and create a
- // pointer to the serial buffer
- zero_byte_packets = 0;
- const uint8_t *cur = serial_buffer;
- const uint8_t *end = serial_buffer + bytes_read;
- while (cur < end) {- // process the incoming bytes into commands and draw them
- const int n = slip_read_byte(&slip, *cur++);
- if (n != SLIP_NO_ERROR) {- if (n == SLIP_ERROR_INVALID_PACKET) {- m8_reset_display();
- } else {- SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
- }
- }
- }
- } else {- // zero byte packet, increment counter
- zero_byte_packets++;
- if (zero_byte_packets > conf.wait_packets) {- zero_byte_packets = 0;
-
- // try opening the serial port to check if it's alive
- if (check_serial_port()) {- // the device is still there, carry on
- break;
- }
- disconnect();
- return 0;
- }
- break;
- }
- }
- return 1;
-}
-
-int m8_close() { return disconnect(); }-#endif
--- a/src/backends/serialport.h
+++ /dev/null
@@ -1,24 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-#ifndef _SERIAL_H_
-#define _SERIAL_H_
-#ifdef USE_LIBSERIALPORT
-
-#include "../config.h"
-#include <stdint.h>
-
-// maximum amount of bytes to read from the serial in one read()
-#define serial_read_size 1024
-
-int m8_initialize(int verbose, const char *preferred_device);
-int m8_list_devices(void);
-int m8_reset_display(void);
-int m8_enable_and_reset_display(void);
-int m8_send_msg_controller(uint8_t input);
-int m8_send_msg_keyjazz(uint8_t note, uint8_t velocity);
-int m8_process_data(config_params_s conf);
-int m8_close(void);
-
-#endif
-#endif
--- a/src/backends/usb.c
+++ /dev/null
@@ -1,361 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-// Contains portions of code from libserialport's examples released to the
-// public domain
-#ifdef USE_LIBUSB
-
-#include <SDL3/SDL.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-
-static int ep_out_addr = 0x03;
-static int ep_in_addr = 0x83;
-
-#define ACM_CTRL_DTR 0x01
-#define ACM_CTRL_RTS 0x02
-
-#define M8_VID 0x16c0
-#define M8_PID 0x048a
-
-libusb_context *ctx = NULL;
-libusb_device_handle *devh = NULL;
-
-static int do_exit = 0;
-
-int m8_list_devices() {- int r;
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
-
- libusb_device **device_list = NULL;
- int count = libusb_get_device_list(ctx, &device_list);
- for (size_t idx = 0; idx < count; ++idx) {- libusb_device *device = device_list[idx];
- struct libusb_device_descriptor desc;
- int rc = libusb_get_device_descriptor(device, &desc);
- if (rc < 0) {- SDL_Log("Error");- libusb_free_device_list(device_list, 1);
- return rc;
- }
-
- if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {- SDL_Log("Found M8 device: %d:%d\n", libusb_get_port_number(device),- libusb_get_bus_number(device));
- }
- }
- libusb_free_device_list(device_list, 1);
- return 0;
-}
-
-int usb_loop(void *data) {- SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
- while (!do_exit) {- int rc = libusb_handle_events(ctx);
- if (rc != LIBUSB_SUCCESS) {- SDL_Log("Audio loop error: %s\n", libusb_error_name(rc));- break;
- }
- }
- return 0;
-}
-
-static SDL_Thread *usb_thread;
-
-static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer) {- int *completed = transfer->user_data;
- *completed = 1;
-}
-
-int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {- if (devh == NULL) {- return -1;
- }
-
- int completed = 0;
-
- struct libusb_transfer *transfer;
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, devh, endpoint, serial_buf, count, xfr_cb_in, &completed,
- timeout_ms);
- int r = libusb_submit_transfer(transfer);
-
- if (r < 0) {- SDL_Log("Error");- libusb_free_transfer(transfer);
- return r;
- }
-
-retry:
- libusb_lock_event_waiters(ctx);
- while (!completed) {- if (!libusb_event_handler_active(ctx)) {- libusb_unlock_event_waiters(ctx);
- goto retry;
- }
- libusb_wait_for_event(ctx, NULL);
- }
- libusb_unlock_event_waiters(ctx);
-
- int actual_length = transfer->actual_length;
-
- libusb_free_transfer(transfer);
-
- return actual_length;
-}
-
-int blocking_write(void *buf, int count, unsigned int timeout_ms) {- return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
-}
-
-int m8_process_data(uint8_t *serial_buf, int count) {- return bulk_transfer(ep_in_addr, serial_buf, count, 1);
-}
-
-int check_serial_port() {- // Reading will fail anyway when the device is not present anymore
- return 1;
-}
-
-int init_interface() {-
- if (devh == NULL) {- SDL_Log("Device not initialised!");- return 0;
- }
-
- int rc;
-
- for (int if_num = 0; if_num < 2; if_num++) {- if (libusb_kernel_driver_active(devh, if_num)) {- SDL_Log("Detaching kernel driver for interface %d", if_num);- libusb_detach_kernel_driver(devh, if_num);
- }
- rc = libusb_claim_interface(devh, if_num);
- if (rc < 0) {- SDL_Log("Error claiming interface: %s", libusb_error_name(rc));- return 0;
- }
- }
-
- /* Start configuring the device:
- * - set line state
- */
- SDL_Log("Setting line state");- rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, 0);
- if (rc < 0) {- SDL_Log("Error during control transfer: %s", libusb_error_name(rc));- return 0;
- }
-
- /* - set line encoding: here 115200 8N1
- * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
- */
- SDL_Log("Set line encoding");- unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};- rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding, sizeof(encoding), 0);
- if (rc < 0) {- SDL_Log("Error during control transfer: %s", libusb_error_name(rc));- return 0;
- }
-
- usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
-
- return 1;
-}
-
-int init_serial_with_file_descriptor(int file_descriptor) {- SDL_Log("Initialising serial with file descriptor");-
- if (file_descriptor <= 0) {- SDL_Log("Invalid file descriptor: %d", file_descriptor);- return 0;
- }
-
- int r;
- r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
- if (r != LIBUSB_SUCCESS) {- SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));- return 0;
- }
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
- r = libusb_wrap_sys_device(ctx, (intptr_t)file_descriptor, &devh);
- if (r < 0) {- SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));- return 0;
- } else if (devh == NULL) {- SDL_Log("libusb_wrap_sys_device returned invalid handle");- return 0;
- }
- SDL_Log("USB device init success");-
- return init_interface();
-}
-
-int m8_initialize(int verbose, char *preferred_device) {-
- if (devh != NULL) {- return 1;
- }
-
- int r;
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
- if (preferred_device == NULL) {- devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
- } else {- char *port;
- char *saveptr = NULL;
- char *bus;
- port = SDL_strtok_r(preferred_device, ":", &saveptr);
- bus = SDL_strtok_r(NULL, ":", &saveptr);
- libusb_device **device_list = NULL;
- int count = libusb_get_device_list(ctx, &device_list);
- for (size_t idx = 0; idx < count; ++idx) {- libusb_device *device = device_list[idx];
- struct libusb_device_descriptor desc;
- r = libusb_get_device_descriptor(device, &desc);
- if (r < 0) {- SDL_Log("libusb_get_device_descriptor failed: %s", libusb_error_name(r));- libusb_free_device_list(device_list, 1);
- return 0;
- }
-
- if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {- SDL_Log("Searching for port %s and bus %s", port, bus);- if (libusb_get_port_number(device) == SDL_atoi(port) &&
- libusb_get_bus_number(device) == SDL_atoi(bus)) {- SDL_Log("Preferred device found, connecting");- r = libusb_open(device, &devh);
- if (r < 0) {- SDL_Log("libusb_open failed: %s", libusb_error_name(r));- return 0;
- }
- }
- }
- }
- libusb_free_device_list(device_list, 1);
- if (devh == NULL) {- SDL_Log("Preferred device %s not found, using first available", preferred_device);- devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
- }
- }
- if (devh == NULL) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,
- "libusb_open_device_with_vid_pid returned invalid handle");
- return 0;
- }
- SDL_Log("USB device init success");-
- return init_interface();
-}
-
-int m8_reset_display() {- int result;
-
- SDL_Log("Reset display\n");-
- char buf[1] = {'R'};-
- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
- return 0;
- }
- return 1;
-}
-
-int m8_enable_and_reset_display() {- int result;
-
- SDL_Log("Enabling and resetting M8 display\n");-
- char buf[1] = {'E'};- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
- return 0;
- }
-
- SDL_Delay(5);
- result = m8_reset_display();
- return result;
-}
-
-int m8_close() {-
- char buf[1] = {'D'};- int result;
-
- SDL_Log("Disconnecting M8\n");-
- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
- return -1;
- }
-
- int rc;
-
- if (devh != NULL) {-
- for (int if_num = 0; if_num < 2; if_num++) {- rc = libusb_release_interface(devh, if_num);
- if (rc < 0) {- SDL_Log("Error releasing interface: %s", libusb_error_name(rc));- return 0;
- }
- }
-
- do_exit = 1;
-
- libusb_close(devh);
- }
-
- SDL_WaitThread(usb_thread, NULL);
-
- libusb_exit(ctx);
-
- return 1;
-}
-
-int m8_send_msg_controller(uint8_t input) {- char buf[2] = {'C', input};- int nbytes = 2;
- int result;
- result = blocking_write(buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
- return -1;
- }
- return 1;
-}
-
-int m8_send_msg_keyjazz(uint8_t note, uint8_t velocity) {- if (velocity > 0x7F)
- velocity = 0x7F;
- char buf[3] = {'K', note, velocity};- int nbytes = 3;
- int result;
- result = blocking_write(buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
- return -1;
- }
-
- return 1;
-}
-
-#endif
--- a/src/backends/usb.h
+++ /dev/null
@@ -1,19 +1,0 @@
-#ifndef M8C_USB_H_
-#define M8C_USB_H_
-#ifdef USE_LIBUSB
-
-#include <libusb.h>
-#include "../config.h"
-
-extern libusb_device_handle *devh;
-int init_serial_with_file_descriptor(int file_descriptor);
-int m8_initialize(int verbose, const char *preferred_device);
-int m8_list_devices(void);
-int m8_reset_display(void);
-int m8_enable_and_reset_display(void);
-int m8_send_msg_controller(uint8_t input);
-int m8_send_msg_keyjazz(uint8_t note, uint8_t velocity);
-int m8_process_data(config_params_s conf);
-int m8_close(void);
-#endif // USE_LIBUSB
-#endif // M8C_USB_H_
--- a/src/backends/usb_audio.c
+++ /dev/null
@@ -1,204 +1,0 @@
-#ifdef USE_LIBUSB
-
-#include "ringbuffer.h"
-#include "usb.h"
-#include <SDL3/SDL.h>
-#include <errno.h>
-#include <libusb.h>
-
-#define EP_ISO_IN 0x85
-#define IFACE_NUM 4
-
-#define NUM_TRANSFERS 64
-#define PACKET_SIZE 180
-#define NUM_PACKETS 2
-
-SDL_AudioDeviceID sdl_audio_device_id = 0;
-RingBuffer *audio_buffer = NULL;
-
-static void audio_callback(void *userdata, Uint8 *stream, int len) {- uint32_t read_len = ring_buffer_pop(audio_buffer, stream, len);
-
- if (read_len == -1) {- SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow!");
- }
-
- // If we didn't read the full len bytes, fill the rest with zeros
- if (read_len < len) {- SDL_memset(&stream[read_len], 0, len - read_len);
- }
-}
-
-static void cb_xfr(struct libusb_transfer *xfr) {- unsigned int i;
-
- for (i = 0; i < xfr->num_iso_packets; i++) {- struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
-
- if (pack->status != LIBUSB_TRANSFER_COMPLETED) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
- libusb_error_name(pack->status));
- /* This doesn't happen, so bail out if it does. */
- return;
- }
-
- const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
- if (sdl_audio_device_id != 0) {- uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
- if (actual == -1) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
- }
- }
- }
-
- if (libusb_submit_transfer(xfr) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB\n");
- SDL_free(xfr->buffer);
- }
-}
-
-static struct libusb_transfer *xfr[NUM_TRANSFERS];
-
-static int benchmark_in() {- int i;
-
- for (i = 0; i < NUM_TRANSFERS; i++) {- xfr[i] = libusb_alloc_transfer(NUM_PACKETS);
- if (!xfr[i]) {- SDL_Log("Could not allocate transfer");- return -ENOMEM;
- }
-
- Uint8 *buffer = SDL_malloc(PACKET_SIZE * NUM_PACKETS);
-
- libusb_fill_iso_transfer(xfr[i], devh, EP_ISO_IN, buffer, PACKET_SIZE * NUM_PACKETS,
- NUM_PACKETS, cb_xfr, NULL, 0);
- libusb_set_iso_packet_lengths(xfr[i], PACKET_SIZE);
-
- libusb_submit_transfer(xfr[i]);
- }
-
- return 1;
-}
-
-int audio_initialize(int audio_buffer_size, const char *output_device_name) {- SDL_LogError(SDL_LOG_CATEGORY_AUDIO,"LIBUSB audio not implemented yet");
- return -1;
- /*
- SDL_Log("USB audio setup");-
- int rc;
-
- rc = libusb_kernel_driver_active(devh, IFACE_NUM);
- if (rc == 1) {- SDL_Log("Detaching kernel driver");- rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
- if (rc < 0) {- SDL_Log("Could not detach kernel driver: %s\n", libusb_error_name(rc));- return rc;
- }
- }
-
- rc = libusb_claim_interface(devh, IFACE_NUM);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error claiming interface: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- rc = libusb_set_interface_alt_setting(devh, IFACE_NUM, 1);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error setting alt setting: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- if (!SDL_WasInit(SDL_INIT_AUDIO)) {- if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Init audio failed %s", SDL_GetError());
- return -1;
- }
- } else {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Audio was already initialised");
- }
-
- static SDL_AudioSpec audio_spec;
- audio_spec.format = SDL_AUDIO_S16;
- audio_spec.channels = 2;
- audio_spec.freq = 44100;
- audio_spec.samples = audio_buffer_size;
- audio_spec.callback = audio_callback;
-
- SDL_AudioSpec _obtained;
- SDL_zero(_obtained);
-
- SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),- output_device_name);
-
- if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {- SDL_Log("Using default audio device");- sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, &_obtained, 0);
- } else {- sdl_audio_device_id = SDL_OpenAudioDevice(output_device_name, 0, &audio_spec, &_obtained, 0);
- }
-
- audio_buffer = ring_buffer_create(4 * _obtained.size);
-
- SDL_Log("Obtained audio spec. Sample rate: %d, channels: %d, samples: %d, size: %d",- _obtained.freq, _obtained.channels, _obtained.samples, +_obtained.size);
-
- SDL_PauseAudioDevice(sdl_audio_device_id, 0);
-
- // Good to go
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Starting capture");
- if ((rc = benchmark_in()) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Capture failed to start: %d", rc);
- return rc;
- }
-
- SDL_Log("Successful init");- return 1;
- */
-}
-
-int audio_close() {- if (devh == NULL) {- return -1;
- }
-
- SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Closing audio");
-
- int i, rc;
-
- for (i = 0; i < NUM_TRANSFERS; i++) {- rc = libusb_cancel_transfer(xfr[i]);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error cancelling transfer: %s\n",
- libusb_error_name(rc));
- }
- }
-
- SDL_Log("Freeing interface %d", IFACE_NUM);-
- rc = libusb_release_interface(devh, IFACE_NUM);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error releasing interface: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- if (sdl_audio_device_id != 0) {- SDL_Log("Closing audio device %d", sdl_audio_device_id);- SDL_AudioDeviceID device = sdl_audio_device_id;
- sdl_audio_device_id = 0;
- SDL_CloseAudioDevice(device);
- }
-
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Audio closed");
-
- ring_buffer_free(audio_buffer);
- return 1;
-}
-
-void audio_toggle(unsigned int audio_buffer_size, const char *output_device_name) {- SDL_Log("Libusb audio toggling not implemented yet");-}
-
-#endif
--- a/src/config.c
+++ b/src/config.c
@@ -34,7 +34,7 @@
c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
// sec for default idle_ms)
c.audio_enabled = 0; // route M8 audio to default output
- c.audio_buffer_size = 1024; // requested audio buffer size in samples
+ c.audio_buffer_size = 0; // requested audio buffer size in samples: 0 = let SDL decide
c.audio_device_name = NULL; // Use this device, leave NULL to use the default output device
c.key_up = SDL_SCANCODE_UP;
--- a/src/input.c
+++ b/src/input.c
@@ -1,8 +1,6 @@
#include "input.h"
#include "backends/audio.h"
-#include "backends/rtmidi.h"
-#include "backends/serialport.h"
-#include "backends/usb.h"
+#include "backends/m8.h"
#include "config.h"
#include "gamecontrollers.h"
#include "render.h"
--- a/src/main.c
+++ b/src/main.c
@@ -10,9 +10,7 @@
#include "SDL2_inprint.h"
#include "backends/audio.h"
-#include "backends/rtmidi.h"
-#include "backends/serialport.h"
-#include "backends/usb.h"
+#include "backends/m8.h"
#include "command.h"
#include "config.h"
#include "gamecontrollers.h"
--
⑨