ref: 8e0bad662f99eebde515dbc63c5670b0eb0e87e9
parent: a71d4cd80beec7769ea9e63c41bdf2a5e42f3ab1
parent: 15d162059b0a92c4f94201018be5226cd58b7e14
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Tue Apr 20 13:19:47 EDT 2021
Merge pull request #5 from laamaa/feature/keyjazz initial keyjazz support, reorganize input.c/.h
--- a/README.md
+++ b/README.md
@@ -66,18 +66,29 @@
If the stars are aligned correctly, you should see the M8 screen.
+-----------
+
+## Keyboard mappings
+
Keys for controlling the progam:
-* Up arrow / i = up
-* Down arrow / k = down
-* Left arrow / j = left
-* Right arrow / l = right
-* a = select
-* s = start
-* z = opt
-* x = edit
+* Up arrow = up
+* Down arrow = down
+* Left arrow = left
+* Right arrow = right
+* a / left shift = select
+* s / space = start
+* z / left alt = opt
+* x / left ctrl = edit
-You can toggle full screen mode with ALT+Enter and quit the program with CTRL+q.
+Additional controls:
+* Alt + enter = toggle full screen / windowed
+* Alt + F4 = quit program
+* Delete = opt+edit (deletes a row)
+* Esc = toggle keyjazz on/off
+
+Keyjazz allows to enter notes with keyboard, oldschool tracker-style. The layout is two octaves, starting from keys Z and Q.
+When keyjazz is active, regular a/s/z/x keys are disabled.
There is also a so-called gamepad support, currently it's working only if there's only one pad connected and it's a Retrobit NES-style controller, since the settings are hardcoded.
--- a/input.c
+++ b/input.c
@@ -6,6 +6,7 @@
#include "input.h"
#include "render.h"
+#include "write.h"
SDL_Joystick *js;
@@ -20,6 +21,14 @@
key_edit = 1
};
+uint8_t keyjazz_enabled = 0;
+uint8_t keyjazz_base_octave = 2;
+
+uint8_t toggle_input_keyjazz() {+ keyjazz_enabled = !keyjazz_enabled;
+ return keyjazz_enabled;
+}
+
int initialize_input() {int num_joysticks = SDL_NumJoysticks();
@@ -27,7 +36,6 @@
if (num_joysticks > 0) {js = SDL_JoystickOpen(0);
SDL_JoystickEventState(SDL_ENABLE);
- fprintf(stderr, "Opened Joystick 0");
}
return num_joysticks;
@@ -35,19 +43,210 @@
void close_input() { SDL_JoystickClose(js); }-uint8_t process_input() {+static input_msg_s handle_keyjazz(SDL_Event *event, uint8_t keyvalue) {+ input_msg_s key = {normal, keyvalue};+ switch (event->key.keysym.scancode) {+ case SDL_SCANCODE_Z:
+ key.type = keyjazz;
+ key.value = keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_S:
+ key.type = keyjazz;
+ key.value = 1 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_X:
+ key.type = keyjazz;
+ key.value = 2 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_D:
+ key.type = keyjazz;
+ key.value = 3 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_C:
+ key.type = keyjazz;
+ key.value = 4 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_V:
+ key.type = keyjazz;
+ key.value = 5 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_G:
+ key.type = keyjazz;
+ key.value = 6 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_B:
+ key.type = keyjazz;
+ key.value = 7 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_H:
+ key.type = keyjazz;
+ key.value = 8 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_N:
+ key.type = keyjazz;
+ key.value = 9 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_J:
+ key.type = keyjazz;
+ key.value = 10 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_M:
+ key.type = keyjazz;
+ key.value = 11 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_Q:
+ key.type = keyjazz;
+ key.value = 12 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_2:
+ key.type = keyjazz;
+ key.value = 13 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_W:
+ key.type = keyjazz;
+ key.value = 14 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_3:
+ key.type = keyjazz;
+ key.value = 15 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_E:
+ key.type = keyjazz;
+ key.value = 16 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_R:
+ key.type = keyjazz;
+ key.value = 17 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_5:
+ key.type = keyjazz;
+ key.value = 18 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_T:
+ key.type = keyjazz;
+ key.value = 19 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_6:
+ key.type = keyjazz;
+ key.value = 20 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_Y:
+ key.type = keyjazz;
+ key.value = 21 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_7:
+ key.type = keyjazz;
+ key.value = 22 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_U:
+ key.type = keyjazz;
+ key.value = 23 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_I:
+ key.type = keyjazz;
+ key.value = 24 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_9:
+ key.type = keyjazz;
+ key.value = 25 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_O:
+ key.type = keyjazz;
+ key.value = 26 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_0:
+ key.type = keyjazz;
+ key.value = 27 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_P:
+ key.type = keyjazz;
+ key.value = 28 + keyjazz_base_octave * 12;
+ break;
+ case SDL_SCANCODE_KP_DIVIDE:
+ if (event->type == SDL_KEYDOWN && keyjazz_base_octave > 0) {+ keyjazz_base_octave--;
+ display_keyjazz_overlay(1, keyjazz_base_octave);
+ }
+ break;
+ case SDL_SCANCODE_KP_MULTIPLY:
+ if (event->type == SDL_KEYDOWN && keyjazz_base_octave < 8) {+ keyjazz_base_octave++;
+ display_keyjazz_overlay(1, keyjazz_base_octave);
+ }
+ break;
+ default:
+ break;
+ }
+ return key;
+}
+
+static input_msg_s handle_normal_keys(SDL_Event *event, uint8_t keyvalue) {+ input_msg_s key = {normal, keyvalue};+ switch (event->key.keysym.scancode) {+
+ case SDL_SCANCODE_UP:
+ key.value = key_up;
+ break;
+
+ case SDL_SCANCODE_LEFT:
+ key.value = key_left;
+ break;
+
+ case SDL_SCANCODE_DOWN:
+ key.value = key_down;
+ break;
+
+ case SDL_SCANCODE_RIGHT:
+ key.value = key_right;
+ break;
+
+ case SDL_SCANCODE_LSHIFT:
+ case SDL_SCANCODE_A:
+ key.value = key_select;
+ break;
+
+ case SDL_SCANCODE_SPACE:
+ case SDL_SCANCODE_S:
+ key.value = key_start;
+ break;
+
+ case SDL_SCANCODE_LALT:
+ case SDL_SCANCODE_Z:
+ key.value = key_opt;
+ break;
+
+ case SDL_SCANCODE_LCTRL:
+ case SDL_SCANCODE_X:
+ key.value = key_edit;
+ break;
+
+ case SDL_SCANCODE_DELETE:
+ key.value = key_opt | key_edit;
+ break;
+
+ default:
+ key.value = 0;
+ break;
+ }
+ return key;
+}
+
+input_msg_s get_input_msg() {+
SDL_Event event;
- uint8_t key = 0;
- static uint8_t input = 0;
+ static uint8_t keycode = 0;
+ input_msg_s key = {normal, 0};SDL_PollEvent(&event);
switch (event.type) {+ // Handle SDL quit events (for example, X11 window close)
case SDL_QUIT:
- return 21;
+ return (input_msg_s){special, msg_quit};+ // Joystick axis movement events (directional buttons)
case SDL_JOYAXISMOTION:
switch (event.jaxis.axis) {@@ -55,16 +254,16 @@
case 0:
if (event.jaxis.value < 0) {- input |= key_left;
+ keycode |= key_left;
break;
}
if (event.jaxis.value > 0) {- input |= key_right;
+ keycode |= key_right;
break;
}
if (event.jaxis.value == 0) {- input &= ~key_left;
- input &= ~key_right;
+ keycode &= ~key_left;
+ keycode &= ~key_right;
break;
}
@@ -71,18 +270,18 @@
case 1:
if (event.jaxis.value < 0) {- input |= key_up;
+ keycode |= key_up;
break;
}
if (event.jaxis.value > 0) {- input |= key_down;
+ keycode |= key_down;
break;
}
if (event.jaxis.value == 0) {- input &= ~key_up;
- input &= ~key_down;
+ keycode &= ~key_up;
+ keycode &= ~key_down;
break;
}
}
@@ -95,28 +294,33 @@
switch (event.jbutton.button) {case 1:
- key = key_edit;
+ key.type = normal;
+ key.value = key_edit;
break;
case 2:
- key = key_opt;
+ key.type = normal;
+ key.value = key_opt;
break;
case 8:
- key = key_select;
+ key.type = normal;
+ key.value = key_select;
break;
case 9:
- key = key_start;
+ key.type = normal;
+ key.value = key_start;
break;
}
if (event.type == SDL_JOYBUTTONDOWN)
- input |= key;
+ keycode |= key.value;
else
- input &= ~key;
+ keycode &= ~key.value;
- return input;
+ return (input_msg_s){key.type, keycode};break;
+ // Special keyboard events
case SDL_KEYDOWN:
// ALT+ENTER toggles fullscreen
@@ -123,59 +327,26 @@
if (event.key.keysym.sym == SDLK_RETURN &&
(event.key.keysym.mod & KMOD_ALT) > 0) {toggle_fullscreen();
+ break;
}
- // CTRL+q quits program
- if (event.key.keysym.sym == SDLK_q &&
- (event.key.keysym.mod & KMOD_CTRL) > 0) {- return 21; // arbitary quit code for main loop
+ // ALT+F4 quits program
+ if (event.key.keysym.sym == SDLK_F4 &&
+ (event.key.keysym.mod & KMOD_ALT) > 0) {+ return (input_msg_s){special, msg_quit};}
- case SDL_KEYUP:
-
- switch (event.key.keysym.scancode) {-
- case SDL_SCANCODE_I:
- case SDL_SCANCODE_UP:
- key = key_up;
- break;
-
- case SDL_SCANCODE_J:
- case SDL_SCANCODE_LEFT:
- key = key_left;
- break;
-
- case SDL_SCANCODE_K:
- case SDL_SCANCODE_DOWN:
- key = key_down;
- break;
-
- case SDL_SCANCODE_L:
- case SDL_SCANCODE_RIGHT:
- key = key_right;
- break;
-
- case SDL_SCANCODE_A:
- key = key_select;
- break;
-
- case SDL_SCANCODE_S:
- key = key_start;
- break;
-
- case SDL_SCANCODE_Z:
- key = key_opt;
- break;
-
- case SDL_SCANCODE_X:
- key = key_edit;
- break;
-
- default:
- key = 0;
- break;
+ // ESC = toggle keyjazz
+ if (event.key.keysym.sym == SDLK_ESCAPE) {+ display_keyjazz_overlay(toggle_input_keyjazz(), keyjazz_base_octave);
}
+ // Normal keyboard inputs
+ case SDL_KEYUP:
+ key = handle_normal_keys(&event, 0);
+
+ if (keyjazz_enabled)
+ key = handle_keyjazz(&event, key.value);
break;
default:
@@ -182,10 +353,18 @@
break;
}
- if (event.type == SDL_KEYDOWN)
- input |= key;
- else
- input &= ~key;
+ // do not allow pressing multiple keys with keyjazz
+ if (key.type == normal) {+ if (event.type == SDL_KEYDOWN)
+ keycode |= key.value;
+ else
+ keycode &= ~key.value;
+ } else {+ if (event.type == SDL_KEYDOWN)
+ keycode = key.value;
+ else
+ keycode = 0;
+ }
- return input;
+ return (input_msg_s){key.type, keycode};}
\ No newline at end of file
--- a/input.h
+++ b/input.h
@@ -1,10 +1,25 @@
#ifndef INPUT_H_
#define INPUT_H_
-#include <SDL2/SDL_joystick.h>
+#include <stdint.h>
+typedef enum input_type_t {+ normal,
+ keyjazz,
+ special
+} input_type_t;
+
+typedef enum special_messages_t {+ msg_quit
+} special_messages_t;
+
+typedef struct input_msg_s {+ input_type_t type;
+ uint8_t value;
+} input_msg_s;
+
int initialize_input();
void close_input();
-uint8_t process_input();
+input_msg_s get_input_msg();
#endif
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -33,7 +33,8 @@
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
+ .recv_message = process_command, // the function where complete slip
+ // packets are processed further
};
static slip_handler_s slip;
@@ -47,7 +48,7 @@
if (argc > 1) {portname = argv[1];
} else {- portname = "/dev/ttyACM1";
+ portname = "/dev/ttyACM0";
}
int port = init_serial(portname);
if (port == -1)
@@ -62,7 +63,7 @@
// initialize joystick etc.
initialize_input();
- static uint8_t prev_input = 0;
+ uint8_t prev_input = 0;
// main loop
while (run) {@@ -84,23 +85,36 @@
}
}
- uint8_t input = process_input();
+ input_msg_s input = get_input_msg();
- if (input != prev_input) {- prev_input = input;
- switch (input) {- case 21: // arbitary quit code
+ switch (input.type) {+ case normal:
+ if (input.value != prev_input) {+ prev_input = input.value;
+ send_msg_controller(port, input.value);
+ }
+ break;
+ case keyjazz:
+ if (input.value != prev_input) {+ prev_input = input.value;
+ if (input.value != 0) {+ send_msg_keyjazz(port, input.value, 64);
+ } else {+ send_msg_keyjazz(port, 0, 0);
+ }
+ }
+ break;
+ case special:
+ switch (input.value) {+ case msg_quit:
run = 0;
break;
- default:
- send_input(port, input);
- break;
}
- } else {+ break;
}
render_screen();
-
+
usleep(10);
}
--- a/render.c
+++ b/render.c
@@ -47,7 +47,8 @@
rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
- font = TTF_OpenFontRW(SDL_RWFromMem(stealth57_ttf, stealth57_ttf_len), 1, font_size);
+ font = TTF_OpenFontRW(SDL_RWFromMem(stealth57_ttf, stealth57_ttf_len), 1,
+ font_size);
surface =
SDL_CreateRGBSurfaceWithFormat(0, 320, 240, 8, SDL_PIXELFORMAT_ARGB8888);
@@ -153,6 +154,38 @@
command->waveform[i] = 20;
// draw the pixels directly to the surface
set_pixel(surface, i, command->waveform[i], color);
+ }
+}
+
+void display_keyjazz_overlay(uint8_t show, uint8_t base_octave) {+ if (show) {+ struct draw_rectangle_command drc;
+ drc.color = (struct color){255, 0, 0};+ drc.pos.x = 310;
+ drc.pos.y = 230;
+ drc.size.width = 5;
+ drc.size.height = 5;
+
+ draw_rectangle(&drc);
+
+ struct draw_character_command dcc;
+ dcc.background = (struct color){0, 0, 0};+ dcc.foreground = (struct color){200, 200, 200};+ dcc.c = base_octave+48;
+ dcc.pos.x = 300;
+ dcc.pos.y = 226;
+
+ draw_character(&dcc);
+
+ } else {+ struct draw_rectangle_command drc;
+ drc.color = (struct color){0, 0, 0};+ drc.pos.x = 300;
+ drc.pos.y = 226;
+ drc.size.width = 20;
+ drc.size.height = 14;
+
+ draw_rectangle(&drc);
}
}
--- a/render.h
+++ b/render.h
@@ -13,5 +13,6 @@
void render_screen();
void toggle_fullscreen();
+void display_keyjazz_overlay(uint8_t show, uint8_t base_octave);
#endif
\ No newline at end of file
--- a/write.c
+++ b/write.c
@@ -30,14 +30,12 @@
}
int disconnect(int port) {- char buf[20];
- size_t nbytes;
+ char buf[1] = {'D'};+ size_t nbytes = 1;
ssize_t bytes_written;
fprintf(stderr, "Disconnecting M8\n");
- strcpy(buf, "D");
- nbytes = strlen(buf);
bytes_written = write(port, buf, nbytes);
if (bytes_written != nbytes) {fprintf(stderr,
@@ -52,7 +50,7 @@
return 1;
}
-int send_input(int port, uint8_t input) {+int send_msg_controller(int port, uint8_t input) { char buf[2] = {'C',input};size_t nbytes = 2;
ssize_t bytes_written;
@@ -59,7 +57,28 @@
bytes_written = write(port, buf, nbytes);
if (bytes_written != nbytes) {fprintf(stderr,
- "Error sending input, expected to write %zu bytes, %zd written\n",
+ "Error sending controller message, expected to write %zu bytes, %zd written\n",
+ nbytes, bytes_written);
+
+ if (bytes_written == -1) {+ fprintf(stderr, "Error code %d: %s\n", errno, strerror(errno));
+ }
+ return -1;
+ }
+ return 1;
+
+}
+
+int send_msg_keyjazz(int port, uint8_t note, uint8_t velocity) {+ if (velocity > 64)
+ velocity = 64;
+ char buf[3] = {'K',note,velocity};+ size_t nbytes = 3;
+ ssize_t bytes_written;
+ bytes_written = write(port, buf, nbytes);
+ if (bytes_written != nbytes) {+ fprintf(stderr,
+ "Error sending keyjazz message, expected to write %zu bytes, %zd written\n",
nbytes, bytes_written);
if (bytes_written == -1) {--- a/write.h
+++ b/write.h
@@ -4,9 +4,8 @@
#include <stdint.h>
int enable_and_reset_display(int port);
int disconnect(int port);
-int send_input(int port, uint8_t input);
-
-void draw_rectangle(struct draw_rectangle_command *command);
+int send_msg_controller(int port, uint8_t input);
+int send_msg_keyjazz(int port, uint8_t note, uint8_t velocity);
#endif
\ No newline at end of file
--
⑨