ref: 6e40acac61543546ff0bab95dc7389fcf5b2f3b1
parent: 77d211a56fd2689b5927e1aaa9f316d2d7848f4c
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Mon Mar 17 10:11:47 EDT 2025
add initial rtmidi things
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,23 +2,33 @@
project(m8c LANGUAGES C)
-set(CMAKE_C_FLAGS "-O2 -Wall -Wextra")
+set(CMAKE_C_FLAGS "-g -Wall -Wextra")
set(APP_NAME m8c)
+set(USE_RTMIDI 1)
+
find_package(PkgConfig REQUIRED)
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
pkg_check_modules(SDL3 REQUIRED sdl3)
+if (USE_LIBSERIALPORT)
+ pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
+ link_directories(${SDL3_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})+ add_compile_definitions(USE_LIBSERIALPORT)
+endif (USE_LIBSERIALPORT)
if (USE_LIBUSB)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
link_directories(${SDL3_LIBRARY_DIRS} ${LIBUSB_LIBRARY_DIRS})add_compile_definitions(USE_LIBUSB)
-else ()
- pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
- link_directories(${SDL3_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})endif (USE_LIBUSB)
+if (USE_RTMIDI)
+ pkg_check_modules(RTMIDI REQUIRED rtmidi)
+ link_directories(${SDL3_LIBRARY_DIRS} ${RTMIDI_LIBRARY_DIRS})+ add_compile_definitions(USE_RTMIDI)
+endif (USE_RTMIDI)
+
file(GLOB m8c_SRC "src/*.h" "src/*.c")
set(MACOS_CONTENTS "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/m8c.app/Contents")@@ -29,22 +39,30 @@
add_executable(${APP_NAME} WIN32 MACOSX_BUNDLE ${APP_ICON} ${m8c_SRC})+if (USE_LIBSERIALPORT)
+ target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})+ target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})+ target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})+endif ()
+
if (USE_LIBUSB)
target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBUSB_LIBRARIES}) target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBUSB_INCLUDE_DIRS}) target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBUSB_CFLAGS_OTHER})-else ()
- target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})- target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})- target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})endif ()
+if (USE_RTMIDI)
+ target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${RTMIDI_LIBRARIES})+ target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${RTMIDI_INCLUDE_DIRS})+ target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${RTMIDI_CFLAGS_OTHER})+endif ()
+
if (APPLE)
# Destination paths below are relative to ${CMAKE_INSTALL_PREFIX} install(TARGETS ${APP_NAME}BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
- )
+ )
set_target_properties(${APP_NAME} PROPERTIESMACOSX_BUNDLE TRUE
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
#Set all your object files (the object files of all the .c files in your project, e.g. main.o my_sub_functions.o )
-OBJ = src/main.o src/serial.o src/slip.o src/command.o src/render.o src/ini.o src/config.o src/input.o src/gamecontrollers.o src/fx_cube.o src/usb.o src/audio.o src/usb_audio.o src/ringbuffer.o src/inprint2.o
+OBJ = src/main.o src/serial.o src/slip.o src/command.o src/render.o src/ini.o src/config.o src/input.o src/gamecontrollers.o src/fx_cube.o src/usb.o src/audio.o src/usb_audio.o src/ringbuffer.o src/inprint2.o src/midi.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 = src/serial.h src/slip.h src/command.h src/render.h src/ini.h src/config.h src/input.h src/gamecontrollers.h src/fx_cube.h src/audio.h src/ringbuffer.h src/inline_font.h
+DEPS = src/serial.h src/slip.h src/command.h src/render.h src/ini.h src/config.h src/input.h src/gamecontrollers.h src/fx_cube.h src/audio.h src/ringbuffer.h src/inline_font.h src/midi.h
#Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
INCLUDES = $(shell pkg-config --libs sdl3 libserialport | sed 's/-mwindows//')
@@ -9,7 +9,6 @@
#Set any compiler flags you want to use (e.g. -I/usr/include/somefolder `pkg-config --cflags gtk+-3.0` ), or leave blank
local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libserialport) -DUSE_LIBSERIALPORT -Wall -Wextra -O2 -pipe -I.
-# local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libserialport) -DUSE_RTMIDI -Wall -Wextra -O2 -pipe -I.
#Set the compiler you are using ( gcc for C or g++ for C++ )
@@ -30,8 +29,12 @@
$(CC) -o $@ $^ $(local_CFLAGS) $(INCLUDES)
libusb: INCLUDES = $(shell pkg-config --libs sdl3 libusb-1.0)
-libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
+libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libusb-1.0) -Wall -Wextra -O2 -pipe -I. -DUSE_LIBUSB=1
libusb: m8c
+
+rtmidi: INCLUDES = $(shell pkg-config --libs sdl3 rtmidi)
+rtmidi: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 rtmidi) -Wall -Wextra -O2 -pipe -I. -DUSE_RTMIDI
+rtmidi: m8c
#Cleanup
.PHONY: clean
--- a/src/main.c
+++ b/src/main.c
@@ -14,9 +14,9 @@
#include "config.h"
#include "gamecontrollers.h"
#include "input.h"
+#include "midi.h"
#include "render.h"
#include "serial.h"
-#include "slip.h"
enum state { QUIT, WAIT_FOR_DEVICE, RUN };@@ -77,12 +77,13 @@
int main(const int argc, char *argv[]) {+ char *preferred_device = NULL;
+
+#ifdef USE_LIBSERIALPORT // Device selection should be only available with libserialport
if (argc == 2 && SDL_strcmp(argv[1], "--list") == 0) {return list_devices();
}
- char *preferred_device = NULL;
-#ifdef USE_LIBSERIALPORT // Device selection should be only available with libserialport
if (argc == 3 && SDL_strcmp(argv[1], "--dev") == 0) {preferred_device = argv[2];
SDL_Log("Using preferred device %s.\n", preferred_device);@@ -220,7 +221,6 @@
// main loop
while (run == RUN) {process_inputs(conf);
-
const int result = process_serial(conf);
if (result == 0) {port_inited = 0;
@@ -230,7 +230,6 @@
run = QUIT;
}
render_screen();
- SDL_Delay(conf.idle_ms);
}
} while (run > QUIT);
// main loop end
--- /dev/null
+++ b/src/midi.c
@@ -1,0 +1,249 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifdef USE_RTMIDI
+
+#include "midi.h"
+#include "command.h"
+#include "config.h"
+#include <SDL3/SDL.h>
+#include <rtmidi_c.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+RtMidiInPtr midi_in;
+RtMidiOutPtr midi_out;
+
+const unsigned char m8_sysex_header[4] = {0xF0, 0x00, 0x02, 0x61};+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)
+ 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 bitCounter = 0;
+ uint8_t bitByteCounter = 0;
+ uint8_t *out = *decoded_data;
+ *decoded_length = 0;
+
+ while (pos < length) {+ // Extract MSB from the "bit field" position
+ uint8_t msb = (encoded_data[bitByteCounter * 8 + m8_sysex_header_size] >> bitCounter) & 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)++;
+
+ bitCounter++;
+ pos++;
+
+ if (bitCounter == 7) {+ bitCounter = 0;
+ bitByteCounter++;
+ pos++; // Skip the MSB byte
+ }
+ }
+}
+
+static void midi_callback(double delta_time, const unsigned char *message, size_t message_size,
+ void *user_data) {+ if (message_size < 5 || !message_is_m8_sysex(message))
+ return;
+
+ uint8_t *decoded_data;
+ size_t decoded_length;
+ midi_decode(message, message_size, &decoded_data, &decoded_length);
+
+ // 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");+ process_command(decoded_data, decoded_length);
+ SDL_free(decoded_data);
+ } else {+ printf("Decoding failed.\n");+ }
+}
+
+int initialize_rtmidi(void) {+ SDL_Log("init 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 init_serial(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();
+ };
+ 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 (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;
+ }
+ }
+ 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 reset_display(void) {+ SDL_Log("Reset display\n");+ const unsigned char reset_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'R', 0xF7};+ 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 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 = reset_display();
+ return result;
+}
+
+int disconnect(void) {+ const unsigned char disconnect_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'D', 0xF7};+ 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_Log("Freeing MIDI ports");+ rtmidi_in_free(midi_in);
+ rtmidi_out_free(midi_out);
+ return !result;
+}
+
+int send_msg_controller(const unsigned char input) {+ const unsigned char input_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', input, 0xF7};+ 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 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};
+ 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 process_serial(config_params_s conf) {+ unsigned char midi_message[1024];
+ size_t midi_message_size = 1024;
+ while (rtmidi_in_get_message(midi_in,midi_message,&midi_message_size)) {+ if (midi_message_size < 5 || !message_is_m8_sysex(midi_message))
+ return 1;
+
+ uint8_t *decoded_data;
+ size_t decoded_length;
+ midi_decode(midi_message, midi_message_size, &decoded_data, &decoded_length);
+
+ // printf("Original data: ");+ for (size_t i = 0; i < midi_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");+ process_command(decoded_data, decoded_length);
+ SDL_free(decoded_data);
+ }
+ printf("Decoding failed.\n");+ return 0;
+ }
+ return 1;
+}
+
+int destroy_serial() { return disconnect(); }+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/src/midi.h
@@ -1,0 +1,23 @@
+// 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"
+#include <stdint.h>
+
+int initialize_rtmidi(void);
+int init_serial(int verbose, const char *preferred_device);
+int list_devices(void);
+int check_serial_port(void);
+int reset_display(void);
+int enable_and_reset_display(void);
+int disconnect(void);
+int send_msg_controller(const unsigned char input);
+int send_msg_keyjazz(const unsigned char note, unsigned char velocity);
+int process_serial(config_params_s conf);
+int destroy_serial();
+
+#endif
+#endif
\ No newline at end of file
--- a/src/render.c
+++ b/src/render.c
@@ -76,7 +76,7 @@
set_font_mode(0);
- SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
+ SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
dirty = 1;
--
⑨