ref: 7ee19d3d5baa4da0fb7a06f82b5fb2a3f233fd4f
parent: b892a5ab495e3d92a14955d8daf9fece5c651167
parent: bfa999b28198f019b9e9fe9a66363a6a9bb85c76
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Fri Mar 28 07:50:09 EDT 2025
Merge pull request #188 from laamaa/refactoring Refactor and enhance input handling, logging, and initialization
--- a/README.md
+++ b/README.md
@@ -21,11 +21,12 @@
Many thanks to:
-* Trash80 for the great M8 hardware and the original font (stealth57.ttf) that was converted to a bitmap for use in the
+* Trash80: For the great M8 hardware and the original fonts that were converted to a bitmap for use in the
progam.
-* driedfruit for a wonderful little routine to blit inline bitmap fonts, https://github.com/driedfruit/SDL_inprint/
-* marcinbor85 for the slip handling routine, https://github.com/marcinbor85/slip
-* turbolent for the great Golang-based g0m8 application, which I used as reference on how the M8 serial protocol works.
+* driedfruit: For a wonderful little routine to blit inline bitmap
+ fonts, [SDL_inprint](https://github.com/driedfruit/SDL_inprint/)
+* marcinbor85: For the slip handling routine, https://github.com/marcinbor85/slip
+* turbolent: For the great Golang-based g0m8 application, which I used as reference on how the M8 serial protocol works.
* *Everyone who's contributed to m8c!*
Disclaimer: I'm not a coder and hardly understand C, use at your own risk :)
--- a/src/backends/m8.h
+++ b/src/backends/m8.h
@@ -5,13 +5,19 @@
#include "../config.h"
+enum return_codes {+ DEVICE_DISCONNECTED = 0,
+ DEVICE_PROCESSING = 1,
+ DEVICE_FATAL_ERROR = -1
+};
+
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_send_msg_controller(unsigned char input);
+int m8_send_msg_keyjazz(unsigned char note, unsigned char velocity);
+int m8_process_data(const config_params_s *conf);
int m8_close(void);
#endif
\ No newline at end of file
--- a/src/backends/m8_libserialport.c
+++ b/src/backends/m8_libserialport.c
@@ -13,8 +13,8 @@
#include "../command.h"
#include "../config.h"
#include "m8.h"
-#include "slip.h"
#include "queue.h"
+#include "slip.h"
#define SERIAL_READ_SIZE 1024 // maximum amount of bytes to read from the serial in one pass
#define SERIAL_READ_DELAY_MS 4 // delay between serial reads in milliseconds
@@ -24,7 +24,6 @@
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
message_queue_s queue;
SDL_Thread *serial_thread = NULL;
@@ -39,44 +38,20 @@
// Helper function for error handling
static int check(enum sp_return result);
-int send_message_to_queue(uint8_t *data, const uint32_t size) {+static int send_message_to_queue(uint8_t *data, const uint32_t size) {push_message(&queue, data, size);
return 1;
}
-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 disconnect() {+static int disconnect() { SDL_Log("Disconnecting M8");// wait for serial processing thread to finish
thread_params.should_stop = 1;
SDL_WaitThread(serial_thread, NULL);
+ destroy_queue(&queue);
- const char buf[1] = {'D'};+ const unsigned char buf[1] = {'D'};int result = sp_blocking_write(m8_port, buf, 1, 5);
if (result != 1) {@@ -83,6 +58,7 @@
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;
@@ -105,27 +81,6 @@
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");
- return 1;
- }
-
- 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;
-}
-
static void process_received_bytes(const uint8_t *buffer, int bytes_read, slip_handler_s *slip) {const uint8_t *cur = buffer;
const uint8_t *end = buffer + bytes_read;
@@ -137,8 +92,8 @@
}
}
-int thread_process_serial_data(void *data) {- thread_params_s *thread_params = data;
+static int thread_process_serial_data(void *data) {+ const thread_params_s *thread_params = data;
while (!thread_params->should_stop) {// attempt to read from serial port
@@ -175,38 +130,67 @@
return 1;
}
-int m8_initialize(const int verbose, const char *preferred_device) {- if (m8_port != NULL) {- // Port is already initialized
- 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");
+ break;
+ case SP_ERR_FAIL:
+ error_message = sp_last_error_message();
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Failed: %s", error_message);
+ sp_free_error_message(error_message);
+ break;
+ case SP_ERR_SUPP:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Not supported");
+ break;
+ case SP_ERR_MEM:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Couldn't allocate memory");
+ break;
+ case SP_OK:
+ default:
+ break;
}
+ return result;
+}
- // settings for the slip packet handler
- static const slip_descriptor_s slip_descriptor = {- .buf = slip_buffer,
- .buf_size = sizeof(slip_buffer),
- .recv_message = send_message_to_queue, // complete slip packets callback
- };
+// Extracted function for initializing threads and message queue
+static int initialize_serial_thread() {- slip_init(&slip, &slip_descriptor);
+ init_queue(&queue);
+ thread_params.should_stop = 0;
+ serial_thread = SDL_CreateThread(thread_process_serial_data, "SerialThread", &thread_params);
- if (verbose)
- SDL_Log("Looking for USB serial devices.\n");+ if (!serial_thread) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "SDL_CreateThread Error: %s", SDL_GetError());
+ SDL_Quit();
+ return 0;
+ }
+
+ return 1;
+}
+
+// Extracted function for detecting and selecting the M8 device
+static int find_and_select_device(const char *preferred_device) {struct sp_port **port_list;
- enum sp_return port_result = sp_list_ports(&port_list);
+ const enum sp_return port_result = sp_list_ports(&port_list);
+
if (port_result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!");
return 0;
}
- // 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);+ SDL_Log("Found M8 in %s", port_name);sp_copy_port(port, &m8_port);
+
+ // Break if preferred device is found
if (preferred_device != NULL && strcmp(preferred_device, port_name) == 0) { SDL_Log("Found preferred device, breaking");break;
@@ -215,60 +199,113 @@
}
sp_free_port_list(port_list);
+ return (m8_port != NULL);
+}
- if (m8_port == NULL) {- if (verbose)
+int m8_initialize(const int verbose, const char *preferred_device) {+ if (m8_port != NULL) {+ // Port is already initialized
+ return 1;
+ }
+
+ // Initialize slip descriptor
+ static const slip_descriptor_s slip_descriptor = {+ .buf = slip_buffer,
+ .buf_size = sizeof(slip_buffer),
+ .recv_message = send_message_to_queue,
+ };
+ slip_init(&slip, &slip_descriptor);
+
+ if (verbose) {+ SDL_Log("Looking for USB serial devices.\n");+ }
+
+ // Detect and select M8 device
+ if (!find_and_select_device(preferred_device)) {+ if (verbose) {SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8");
+ }
return 0;
}
- SDL_Log("Opening and configuring port");+ // Configure serial port
if (!configure_serial_port(m8_port)) {return 0;
}
- init_queue(&queue);
- thread_params.should_stop = 0;
- serial_thread = SDL_CreateThread(thread_process_serial_data, "SerialThread", &thread_params);
+ // Initialize message queue and threads
+ return initialize_serial_thread();
+}
- if (!serial_thread) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "SDL_CreateThread Error: %s\n", SDL_GetError());
- SDL_Quit();
- return 0;
+int m8_send_msg_controller(const uint8_t input) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending controller input %d", input);
+ const unsigned 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;
}
-// Helper function for error handling.
-static int check(const enum sp_return result) {- char *error_message;
+int m8_send_msg_keyjazz(const uint8_t note, uint8_t velocity) {- 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;
+ // Cap velocity to 7bits
+ if (velocity > 0x7F)
+ velocity = 0x7F;
+
+ // Special case for note off
+ if (note == 0xFF && velocity == 0x00) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending keyjazz note off");
+ const unsigned char buf[2] = {'K', 0xFF};+ 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 keyjazz, code %d", result);
+ return -1;
+ }
+ return 1;
}
- return result;
+
+ // Regular note on message
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending keyjazz note %d, velocity %d", note, velocity);
+ const unsigned 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_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");
+ return 1;
+ }
+
+ 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;
+}
+
int m8_reset_display() { SDL_Log("Reset display\n");- const char buf[1] = {'R'};+ const unsigned 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);
@@ -292,12 +329,12 @@
return result;
}
-int m8_process_data(const config_params_s conf) {+int m8_process_data(const config_params_s *conf) {static unsigned int empty_cycles = 0;
// Device likely has been disconnected
if (m8_port == NULL) {- return 0;
+ return DEVICE_DISCONNECTED;
}
if (queue_size(&queue) > 0) {@@ -305,18 +342,23 @@
empty_cycles = 0;
size_t length = 0;
while ((command = pop_message(&queue, &length)) != NULL) {- process_command(command, length);
+ if (length > 0) {+ process_command(command, length);
+ }
SDL_free(command);
}
} else {empty_cycles++;
- if (empty_cycles >= conf.wait_packets) {- SDL_Log("No messages received for %d cycles, assuming device disconnected", empty_cycles);+ if (empty_cycles >= conf->wait_packets) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM,
+ "No messages received for %d cycles, assuming device disconnected",
+ empty_cycles);
+ empty_cycles = 0;
disconnect();
- return 0;
+ return DEVICE_DISCONNECTED;
}
}
- return 1;
+ return DEVICE_PROCESSING;
}
int m8_close() { return disconnect(); }--- a/src/backends/m8_rtmidi.c
+++ b/src/backends/m8_rtmidi.c
@@ -216,7 +216,13 @@
}
int m8_send_msg_controller(const unsigned char input) {- const unsigned char input_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', input, 0xF7};+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "MIDI key inputs not implemented yet");
+ return 0;
+#if 0
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending controller input 0x%02X", input);
+ // Encode a 8bit byte to two 7-bit bytes
+ //const unsigned char sysex_encoded_input[2] = {input & 0x7F, (input >> 7) & 0x01};+ const unsigned char input_sysex[10] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', sysex_encoded_input[0], sysex_encoded_input[1], 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");
@@ -223,12 +229,29 @@
return 0;
}
return 1;
+#endif
}
int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "MIDI keyjazz not implemented yet");
+ return 0;
+#if 0
if (velocity > 0x7F) {velocity = 0x7F;
}
+
+ // Special case for note off
+ if (note == 0xFF && velocity == 0x00) {+ const unsigned char all_notes_off_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'K', 0xFF, 0xF7};+ const int result =
+ rtmidi_out_send_message(midi_out, &all_notes_off_sysex[0], sizeof(all_notes_off_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send all notes off");
+ return 0;
+ }
+ return 1;
+ }
+
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));
@@ -237,9 +260,10 @@
return 0;
}
return 1;
+#endif
}
-int m8_process_data(config_params_s conf) {+int m8_process_data(const config_params_s *conf) {static unsigned int empty_cycles = 0;
@@ -253,7 +277,7 @@
}
} else {empty_cycles++;
- if (empty_cycles >= conf.wait_packets) {+ if (empty_cycles >= conf->wait_packets) { SDL_Log("No messages received for %d cycles, assuming device disconnected", empty_cycles);close_and_free_midi_ports();
return 0;
--- a/src/backends/queue.h
+++ b/src/backends/queue.h
@@ -26,6 +26,13 @@
void init_queue(message_queue_s *queue);
/**
+ * Destroys the message queue and releases associated resources.
+ *
+ * @param queue A pointer to the message queue structure to be destroyed.
+ */
+void destroy_queue(message_queue_s *queue);
+
+/**
* Retrieves and removes a message from the front of the message queue.
* If the queue is empty, the function returns NULL.
*
--- a/src/input.c
+++ b/src/input.c
@@ -14,228 +14,156 @@
static input_msg_s key = {normal, 0, 0, 0};-int input_process(config_params_s conf, enum app_state *app_state) {- static uint8_t prev_input = 0;
- static uint8_t prev_note = 0;
+static unsigned char toggle_input_keyjazz() {+ keyjazz_enabled = !keyjazz_enabled;
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, keyjazz_enabled ? "Keyjazz enabled" : "Keyjazz disabled");
+ return keyjazz_enabled;
+}
- // get current inputs
- const input_msg_s input = input_get_msg(&conf);
+// Get note value for a scancode, or -1 if not found
+static int get_note_for_scancode(SDL_Scancode scancode) {- switch (input.type) {- case normal:
- if (input.value != prev_input) {- prev_input = input.value;
- m8_send_msg_controller(input.value);
+ // Map from SDL scancodes to note offsets
+ const struct keyjazz_scancodes_t {+ SDL_Scancode scancode;
+ uint8_t note_offset;
+ } NOTE_MAP[] = {+ {SDL_SCANCODE_Z, 0}, {SDL_SCANCODE_S, 1}, {SDL_SCANCODE_X, 2}, {SDL_SCANCODE_D, 3},+ {SDL_SCANCODE_C, 4}, {SDL_SCANCODE_V, 5}, {SDL_SCANCODE_G, 6}, {SDL_SCANCODE_B, 7},+ {SDL_SCANCODE_H, 8}, {SDL_SCANCODE_N, 9}, {SDL_SCANCODE_J, 10}, {SDL_SCANCODE_M, 11},+ {SDL_SCANCODE_Q, 12}, {SDL_SCANCODE_2, 13}, {SDL_SCANCODE_W, 14}, {SDL_SCANCODE_3, 15},+ {SDL_SCANCODE_E, 16}, {SDL_SCANCODE_R, 17}, {SDL_SCANCODE_5, 18}, {SDL_SCANCODE_T, 19},+ {SDL_SCANCODE_6, 20}, {SDL_SCANCODE_Y, 21}, {SDL_SCANCODE_7, 22}, {SDL_SCANCODE_U, 23},+ {SDL_SCANCODE_I, 24}, {SDL_SCANCODE_9, 25}, {SDL_SCANCODE_O, 26}, {SDL_SCANCODE_0, 27},+ {SDL_SCANCODE_P, 28},+ };
+
+ const size_t NOTE_MAP_SIZE = (sizeof(NOTE_MAP) / sizeof(NOTE_MAP[0]));
+
+ for (size_t i = 0; i < NOTE_MAP_SIZE; i++) {+ if (NOTE_MAP[i].scancode == scancode) {+ return NOTE_MAP[i].note_offset + keyjazz_base_octave * 12;
}
- break;
- case keyjazz:
- if (input.value != 0) {- if (input.eventType == SDL_EVENT_KEY_DOWN && input.value != prev_input) {- m8_send_msg_keyjazz(input.value, input.value2);
- prev_note = input.value;
- } else if (input.eventType == SDL_EVENT_KEY_UP && input.value == prev_note) {- m8_send_msg_keyjazz(0xFF, 0);
- }
- }
- prev_input = input.value;
- break;
- case special:
- if (input.value != prev_input) {- prev_input = input.value;
- switch (input.value) {- case msg_quit:
- SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Received msg_quit from input device.");
- *app_state = 0;
- break;
- case msg_reset_display:
- m8_reset_display();
- break;
- case msg_toggle_audio:
- conf.audio_enabled = !conf.audio_enabled;
- audio_toggle(conf.audio_device_name, conf.audio_buffer_size);
- break;
- default:
- break;
- }
- break;
- }
}
- return 1;
+ return -1; // Not a note key
}
-uint8_t toggle_input_keyjazz() {- keyjazz_enabled = !keyjazz_enabled;
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, keyjazz_enabled ? "Keyjazz enabled" : "Keyjazz disabled");
- return keyjazz_enabled;
-}
+// Handle octave and velocity changes
+static void handle_keyjazz_settings(const SDL_Event *event, const config_params_s *conf) {-static input_msg_s handle_keyjazz(SDL_Event *event, uint8_t keyvalue, config_params_s *conf) {- input_msg_s key = {keyjazz, keyvalue, keyjazz_velocity, event->type};- switch (event->key.scancode) {- case SDL_SCANCODE_Z:
- key.value = keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_S:
- key.value = 1 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_X:
- key.value = 2 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_D:
- key.value = 3 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_C:
- key.value = 4 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_V:
- key.value = 5 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_G:
- key.value = 6 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_B:
- key.value = 7 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_H:
- key.value = 8 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_N:
- key.value = 9 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_J:
- key.value = 10 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_M:
- key.value = 11 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_Q:
- key.value = 12 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_2:
- key.value = 13 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_W:
- key.value = 14 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_3:
- key.value = 15 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_E:
- key.value = 16 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_R:
- key.value = 17 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_5:
- key.value = 18 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_T:
- key.value = 19 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_6:
- key.value = 20 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_Y:
- key.value = 21 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_7:
- key.value = 22 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_U:
- key.value = 23 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_I:
- key.value = 24 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_9:
- key.value = 25 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_O:
- key.value = 26 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_0:
- key.value = 27 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_P:
- key.value = 28 + keyjazz_base_octave * 12;
- break;
- default:
- key.type = normal;
- if (event->key.repeat > 0 || event->key.type == SDL_EVENT_KEY_UP) {- break;
+ // Constants for keyjazz limits and adjustments
+ const unsigned char KEYJAZZ_MIN_OCTAVE = 0;
+ const unsigned char KEYJAZZ_MAX_OCTAVE = 8;
+ const unsigned char KEYJAZZ_MIN_VELOCITY = 0;
+ const unsigned char KEYJAZZ_MAX_VELOCITY = 0x7F;
+ const unsigned char KEYJAZZ_FINE_VELOCITY_STEP = 1;
+ const unsigned char KEYJAZZ_COARSE_VELOCITY_STEP = 0x10;
+
+ if (event->key.repeat > 0 || event->key.type == SDL_EVENT_KEY_UP) {+ return;
+ }
+
+ const SDL_Scancode scancode = event->key.scancode;
+ const bool is_fine_adjustment = (event->key.mod & SDL_KMOD_ALT) > 0;
+
+ if (scancode == conf->key_jazz_dec_octave && keyjazz_base_octave > KEYJAZZ_MIN_OCTAVE) {+ keyjazz_base_octave--;
+ display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
+ } else if (scancode == conf->key_jazz_inc_octave && keyjazz_base_octave < KEYJAZZ_MAX_OCTAVE) {+ keyjazz_base_octave++;
+ display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
+ } else if (scancode == conf->key_jazz_dec_velocity) {+ const int step = is_fine_adjustment ? KEYJAZZ_FINE_VELOCITY_STEP : KEYJAZZ_COARSE_VELOCITY_STEP;
+ if (keyjazz_velocity > (is_fine_adjustment ? KEYJAZZ_MIN_VELOCITY + step : step)) {+ keyjazz_velocity -= step;
+ display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
}
- if (event->key.scancode == conf->key_jazz_dec_octave) {- if (keyjazz_base_octave > 0) {- keyjazz_base_octave--;
- display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- }
- } else if (event->key.scancode == conf->key_jazz_inc_octave) {- if (keyjazz_base_octave < 8) {- keyjazz_base_octave++;
- display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- }
- } else if (event->key.scancode == conf->key_jazz_dec_velocity) {- if ((event->key.mod & SDL_KMOD_ALT) > 0) {- if (keyjazz_velocity > 1)
- keyjazz_velocity -= 1;
- } else {- if (keyjazz_velocity > 0x10)
- keyjazz_velocity -= 0x10;
- }
+ } else if (scancode == conf->key_jazz_inc_velocity) {+ const int step = is_fine_adjustment ? KEYJAZZ_FINE_VELOCITY_STEP : KEYJAZZ_COARSE_VELOCITY_STEP;
+ const int max = is_fine_adjustment ? KEYJAZZ_MAX_VELOCITY : (KEYJAZZ_MAX_VELOCITY - step);
+ if (keyjazz_velocity < max) {+ keyjazz_velocity += step;
display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- } else if (event->key.scancode == conf->key_jazz_inc_velocity) {- if ((event->key.mod & SDL_KMOD_ALT) > 0) {- if (keyjazz_velocity < 0x7F)
- keyjazz_velocity += 1;
- } else {- if (keyjazz_velocity < 0x6F)
- keyjazz_velocity += 0x10;
- }
- display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
}
- break;
}
+}
+static input_msg_s handle_keyjazz(SDL_Event *event, uint8_t keyvalue, config_params_s *conf) {+ input_msg_s key = {keyjazz, keyvalue, keyjazz_velocity, event->type};+
+ // Check if this is a note key
+ const int note_value = get_note_for_scancode(event->key.scancode);
+ if (note_value >= 0) {+ key.value = note_value;
+ return key;
+ }
+
+ // Not a note key, handle other settings
+ key.type = normal;
+ handle_keyjazz_settings(event, conf);
+
return key;
}
static input_msg_s handle_normal_keys(const SDL_Event *event, const config_params_s *conf) {+ // Default message with normal type and no value
input_msg_s key = {normal, 0, 0, 0};- if (event->key.scancode == conf->key_up) {- key.value = key_up;
- } else if (event->key.scancode == conf->key_left) {- key.value = key_left;
- } else if (event->key.scancode == conf->key_down) {- key.value = key_down;
- } else if (event->key.scancode == conf->key_right) {- key.value = key_right;
- } else if (event->key.scancode == conf->key_select ||
- event->key.scancode == conf->key_select_alt) {- key.value = key_select;
- } else if (event->key.scancode == conf->key_start ||
- event->key.scancode == conf->key_start_alt) {- key.value = key_start;
- } else if (event->key.scancode == conf->key_opt ||
- event->key.scancode == conf->key_opt_alt) {- key.value = key_opt;
- } else if (event->key.scancode == conf->key_edit ||
- event->key.scancode == conf->key_edit_alt) {- key.value = key_edit;
- } else if (event->key.scancode == conf->key_delete) {- key.value = key_opt | key_edit;
- } else if (event->key.scancode == conf->key_reset) {- key = (input_msg_s){special, msg_reset_display, 0, 0};- } else if (event->key.scancode == conf->key_toggle_audio) {- key = (input_msg_s){special, msg_toggle_audio, 0, 0};- } else {- key.value = 0;
+ // Get the current scancode
+ const SDL_Scancode scancode = event->key.scancode;
+
+ // Handle standard keycodes (single key mapping)
+ const struct {+ SDL_Scancode scancode;
+ uint8_t value;
+ } normal_key_map[] = {+ {conf->key_up, key_up},+ {conf->key_left, key_left},+ {conf->key_down, key_down},+ {conf->key_right, key_right},+ {conf->key_select, key_select},+ {conf->key_select_alt, key_select},+ {conf->key_start, key_start},+ {conf->key_start_alt, key_start},+ {conf->key_opt, key_opt},+ {conf->key_opt_alt, key_opt},+ {conf->key_edit, key_edit},+ {conf->key_edit_alt, key_edit},+ {conf->key_delete, key_opt | key_edit},+ };
+
+ // Handle special messages (different message type)
+ const struct {+ SDL_Scancode scancode;
+ special_messages_t message;
+ } special_key_map[] = {+ {conf->key_reset, msg_reset_display},+ {conf->key_toggle_audio, msg_toggle_audio},+ };
+
+ // Check normal key mappings
+ for (size_t i = 0; i < sizeof(normal_key_map) / sizeof(normal_key_map[0]); i++) {+ if (scancode == normal_key_map[i].scancode) {+ key.value = normal_key_map[i].value;
+ return key;
+ }
}
+
+ // Check special key mappings
+ for (size_t i = 0; i < sizeof(special_key_map) / sizeof(special_key_map[0]); i++) {+ if (scancode == special_key_map[i].scancode) {+ key.type = special;
+ key.value = special_key_map[i].message;
+ return key;
+ }
+ }
+
+ // No matching key found, return default key message
return key;
}
// Handles SDL input events
-void handle_sdl_events(config_params_s *conf) {+static void handle_sdl_events(config_params_s *conf) {static int prev_key_analog = 0;
static unsigned int ticks_window_resized = 0;
@@ -270,11 +198,11 @@
break;
case SDL_EVENT_WINDOW_RESIZED:
- if (SDL_GetTicks() - ticks_window_resized > 500) {- SDL_Log("Resizing window...");- key = (input_msg_s){special, msg_reset_display, 0, 0};- ticks_window_resized = SDL_GetTicks();
- }
+ if (SDL_GetTicks() - ticks_window_resized > 500) {+ SDL_Log("Resizing window...");+ key = (input_msg_s){special, msg_reset_display, 0, 0};+ ticks_window_resized = SDL_GetTicks();
+ }
break;
case SDL_EVENT_KEY_DOWN:
@@ -337,6 +265,55 @@
break;
}
}
+}
+
+int input_process(config_params_s *conf, enum app_state *app_state) {+ static uint8_t prev_input = 0;
+ static uint8_t prev_note = 0;
+
+ // get current inputs
+ const input_msg_s input = input_get_msg(conf);
+
+ switch (input.type) {+ case normal:
+ if (input.value != prev_input) {+ prev_input = input.value;
+ m8_send_msg_controller(input.value);
+ }
+ break;
+ case keyjazz:
+ if (input.value != 0) {+ if (input.eventType == SDL_EVENT_KEY_DOWN && input.value != prev_input) {+ m8_send_msg_keyjazz(input.value, input.value2);
+ prev_note = input.value;
+ } else if (input.eventType == SDL_EVENT_KEY_UP && input.value == prev_note) {+ m8_send_msg_keyjazz(0xFF, 0);
+ }
+ }
+ prev_input = input.value;
+ break;
+ case special:
+ if (input.value != prev_input) {+ prev_input = input.value;
+ switch (input.value) {+ case msg_quit:
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Received msg_quit from input device.");
+ *app_state = 0;
+ break;
+ case msg_reset_display:
+ m8_reset_display();
+ break;
+ case msg_toggle_audio:
+ conf->audio_enabled = !conf->audio_enabled;
+ audio_toggle(conf->audio_device_name, conf->audio_buffer_size);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ return 1;
}
// Returns the currently pressed keys to main
--- a/src/input.h
+++ b/src/input.h
@@ -49,6 +49,6 @@
} input_msg_s;
input_msg_s input_get_msg(config_params_s *conf);
-int input_process(config_params_s conf, enum app_state *app_state);
+int input_process(config_params_s *conf, enum app_state *app_state);
#endif
--- a/src/main.c
+++ b/src/main.c
@@ -17,15 +17,26 @@
#include "input.h"
#include "render.h"
+#include <stdlib.h>
+
enum app_state app_state = WAIT_FOR_DEVICE;
// Handle CTRL+C / SIGINT, SIGKILL etc.
-void signal_handler(int unused) {+static void signal_handler(int unused) {(void)unused;
app_state = QUIT;
}
-void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
+static void initialize_signals() {+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+#ifdef SIGQUIT
+ signal(SIGQUIT, signal_handler);
+#endif
+}
+
+
+static void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
config_params_s *conf) {static Uint64 ticks_poll_device = 0;
static Uint64 ticks_update_screen = 0;
@@ -77,124 +88,114 @@
}
}
-int main(const int argc, char *argv[]) {-
- char *preferred_device = NULL;
- unsigned char m8_connected = 0;
-
- if (argc == 2 && SDL_strcmp(argv[1], "--list") == 0) {- return m8_list_devices();
+static config_params_s initialize_config(int argc, char *argv[], char **preferred_device, char **config_filename) {+ for (int i = 1; i < argc; i++) {+ if (SDL_strcmp(argv[i], "--list") == 0) {+ exit(m8_list_devices());
+ }
+ if (SDL_strcmp(argv[i], "--dev") == 0 && i + 1 < argc) {+ *preferred_device = argv[i + 1];
+ SDL_Log("Using preferred device: %s.\n", *preferred_device);+ i++;
+ } else if (SDL_strcmp(argv[i], "--config") == 0 && i + 1 < argc) {+ *config_filename = argv[i + 1];
+ SDL_Log("Using config file: %s.\n", *config_filename);+ i++;
+ }
}
- if (argc == 3 && SDL_strcmp(argv[1], "--dev") == 0) {- preferred_device = argv[2];
- SDL_Log("Using preferred device %s.\n", preferred_device);- }
-
- char *config_filename = NULL;
- if (argc == 3 && SDL_strcmp(argv[1], "--config") == 0) {- config_filename = argv[2];
- SDL_Log("Using config file %s.\n", config_filename);- }
-
- // Initialize the config to defaults
- config_params_s conf = config_initialize(config_filename);
- // Read in the params from the configfile if present
+ config_params_s conf = config_initialize(*config_filename);
config_read(&conf);
+ return conf;
+}
- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
-#ifdef SIGQUIT
- signal(SIGQUIT, signal_handler);
-#endif
-
- // First device detection to avoid SDL init if it isn't necessary. To be run
- // only if we shouldn't wait for M8 to be connected.
- if (conf.wait_for_device == 0) {- m8_connected = m8_initialize(1, preferred_device);
- if (m8_connected == 0) {- return 1;
- }
+static void cleanup_resources(const unsigned char device_connected, const config_params_s *conf) {+ if (conf->audio_enabled) {+ audio_close();
}
+ gamecontrollers_close();
+ renderer_close();
+ inline_font_close();
+ if (device_connected) {+ m8_close();
+ }
+ SDL_Quit();
+ SDL_Log("Shutting down.");+}
- // initialize all SDL systems
- if (renderer_initialize(conf.init_fullscreen) == false) {- SDL_Quit();
- return 1;
+static unsigned char handle_device_initialization(unsigned char wait_for_device, const char *preferred_device) {+ const unsigned char device_connected = m8_initialize(1, preferred_device);
+ if (!wait_for_device && device_connected == 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected!");
+ exit(EXIT_FAILURE);
}
- app_state = QUIT;
+ return device_connected;
+}
- // initial scan for (existing) gamepads
- gamecontrollers_initialize();
+static void main_loop(config_params_s *conf, const char *preferred_device) {+ unsigned char device_connected = 0;
-#ifndef NDEBUG
- SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
- SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
-#endif
-
- // main loop begin
do {- // try to init connection to M8
- m8_connected = m8_initialize(1, preferred_device);
- if (m8_connected == 1 && m8_enable_and_reset_display() == 1) {- if (conf.audio_enabled == 1) {- audio_initialize(conf.audio_device_name, conf.audio_buffer_size);
- // if audio is enabled, reset the display for second time to avoid glitches
- m8_reset_display();
+ device_connected = m8_initialize(1, preferred_device);
+ if (device_connected && m8_enable_and_reset_display()) {+ if (conf->audio_enabled) {+ audio_initialize(conf->audio_device_name, conf->audio_buffer_size);
+ m8_reset_display(); // Avoid display glitches.
}
app_state = RUN;
} else {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected on initial check.");
- if (conf.wait_for_device == 1) {- app_state = WAIT_FOR_DEVICE;
- } else {- app_state = QUIT;
- }
+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+ app_state = conf->wait_for_device ? WAIT_FOR_DEVICE : QUIT;
}
- // wait until device is connected
- if (conf.wait_for_device == 1) {- do_wait_for_device(preferred_device, &m8_connected, &conf);
- } else {- // classic startup behaviour, exit if device is not found
- if (m8_connected == 0) {- if (conf.audio_enabled == 1) {- audio_close();
- }
- gamecontrollers_close();
- renderer_close();
- inline_font_close();
- SDL_Quit();
- return 1;
- }
+ if (conf->wait_for_device && app_state == WAIT_FOR_DEVICE) {+ do_wait_for_device(preferred_device, &device_connected, conf);
+ } else if (!device_connected && app_state != WAIT_FOR_DEVICE) {+ cleanup_resources(0, conf);
+ exit(EXIT_FAILURE);
}
- // main loop
+ // Handle input, process data, and render screen while running.
while (app_state == RUN) {input_process(conf, &app_state);
const int result = m8_process_data(conf);
- if (result == 0) {- // Device disconnected
- m8_connected = 0;
+ if (result == DEVICE_DISCONNECTED) {+ device_connected = 0;
app_state = WAIT_FOR_DEVICE;
audio_close();
- } else if (result == -1) {- // Fatal error
+ } else if (result == DEVICE_FATAL_ERROR) {app_state = QUIT;
}
render_screen();
- SDL_Delay(conf.idle_ms);
+ SDL_Delay(conf->idle_ms);
}
} while (app_state > QUIT);
- // Main loop end
- // Exit, clean up
- SDL_Log("Shutting down");- audio_close();
- gamecontrollers_close();
- renderer_close();
- if (m8_connected == 1)
- m8_close();
- SDL_Quit();
- return 0;
+ cleanup_resources(device_connected, conf);
+}
+
+
+int main(const int argc, char *argv[]) {+ char *preferred_device = NULL;
+ char *config_filename = NULL;
+
+ config_params_s conf = initialize_config(argc, argv, &preferred_device, &config_filename);
+ initialize_signals();
+
+ const unsigned char initial_device_connected = handle_device_initialization(conf.wait_for_device, preferred_device);
+ if (!renderer_initialize(conf.init_fullscreen)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer.");
+ cleanup_resources(initial_device_connected, &conf);
+ return EXIT_FAILURE;
+ }
+
+ gamecontrollers_initialize();
+
+#ifndef NDEBUG
+ SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
+ SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
+#endif
+
+ main_loop(&conf, preferred_device);
+ return EXIT_SUCCESS;
}
--
⑨