shithub: m8c

Download patch

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"
--