Skip to content

Commit

Permalink
simple piano roll view
Browse files Browse the repository at this point in the history
laamaa committed Sep 9, 2024
1 parent 47e041d commit d8879a5
Showing 11 changed files with 305 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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/fx_piano.o src/usb.o src/audio.o src/usb_audio.o src/ringbuffer.o src/inprint2.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/fx_piano.h src/audio.h src/ringbuffer.h src/inline_font.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 sdl2 libserialport | sed 's/-mwindows//')
4 changes: 2 additions & 2 deletions src/command.c
Original file line number Diff line number Diff line change
@@ -152,12 +152,12 @@ int process_command(uint8_t *data, uint32_t size) {
break;
}

char *hwtype[4] = {"Headless", "Beta M8", "Production M8", "Production M8 Model:02"};
const char *hw_type[4] = {"Headless", "Beta M8", "Production M8", "Production M8 Model:02"};

static int system_info_printed = 0;

if (system_info_printed == 0) {
SDL_Log("** Hardware info ** Device type: %s, Firmware ver %d.%d.%d", hwtype[recv_buf[1]],
SDL_Log("** Hardware info ** Device type: %s, Firmware ver %d.%d.%d", hw_type[recv_buf[1]],
recv_buf[2], recv_buf[3], recv_buf[4]);
system_info_printed = 1;
}
7 changes: 6 additions & 1 deletion src/config.c
Original file line number Diff line number Diff line change
@@ -52,6 +52,7 @@ config_params_s init_config() {
c.key_jazz_inc_velocity = SDL_SCANCODE_KP_MINUS;
c.key_jazz_dec_velocity = SDL_SCANCODE_KP_PLUS;
c.key_toggle_audio = SDL_SCANCODE_F12;
c.key_toggle_overlay_fx = SDL_SCANCODE_F11;

c.gamepad_up = SDL_CONTROLLER_BUTTON_DPAD_UP;
c.gamepad_left = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
@@ -86,7 +87,7 @@ void write_config(const config_params_s *conf) {

SDL_Log("Writing config file to %s", config_path);

const unsigned int INI_LINE_COUNT = 50;
const unsigned int INI_LINE_COUNT = 51;
const unsigned int LINELEN = 50;

// Entries for the config file
@@ -132,6 +133,7 @@ void write_config(const config_params_s *conf) {
snprintf(ini_values[initPointer++], LINELEN, "key_jazz_dec_velocity=%d\n",
conf->key_jazz_dec_velocity);
snprintf(ini_values[initPointer++], LINELEN, "key_toggle_audio=%d\n", conf->key_toggle_audio);
snprintf(ini_values[initPointer++], LINELEN, "key_toggle_overlay_fx=%d\n", conf->key_toggle_overlay_fx);
snprintf(ini_values[initPointer++], LINELEN, "[gamepad]\n");
snprintf(ini_values[initPointer++], LINELEN, "gamepad_up=%d\n", conf->gamepad_up);
snprintf(ini_values[initPointer++], LINELEN, "gamepad_left=%d\n", conf->gamepad_left);
@@ -281,6 +283,7 @@ void read_key_config(const ini_t *ini, config_params_s *conf) {
const char *key_jazz_inc_velocity = ini_get(ini, "keyboard", "key_jazz_inc_velocity");
const char *key_jazz_dec_velocity = ini_get(ini, "keyboard", "key_jazz_dec_velocity");
const char *key_toggle_audio = ini_get(ini, "keyboard", "key_toggle_audio");
const char *key_toggle_overlay_fx = ini_get(ini, "keyboard", "key_toggle_overlay_fx");

if (key_up)
conf->key_up = SDL_atoi(key_up);
@@ -320,6 +323,8 @@ void read_key_config(const ini_t *ini, config_params_s *conf) {
conf->key_jazz_dec_velocity = SDL_atoi(key_jazz_dec_velocity);
if (key_toggle_audio)
conf->key_jazz_dec_velocity = SDL_atoi(key_toggle_audio);
if (key_toggle_overlay_fx)
conf->key_jazz_dec_velocity = SDL_atoi(key_toggle_overlay_fx);
}

void read_gamepad_config(const ini_t *ini, config_params_s *conf) {
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ typedef struct config_params_s {
unsigned int key_jazz_inc_velocity;
unsigned int key_jazz_dec_velocity;
unsigned int key_toggle_audio;
unsigned int key_toggle_overlay_fx;

int gamepad_up;
int gamepad_left;
169 changes: 169 additions & 0 deletions src/fx_piano.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//
// Created by jonne on 9/8/24.
//

#include "fx_piano.h"
#include "render.h"
#include "inline_font.h"
#include <SDL2/SDL.h>

struct active_notes {
uint8_t note;
uint8_t sharp;
uint8_t octave;
};

static int width = 320;
static int height = 240;
static SDL_Texture *fx_texture;
static uint32_t *framebuffer;
struct active_notes active_notes[8] = {0};
static int is_initialized = 0;

static const uint32_t bgcolor = 0xA0000000;

// Pastel Rainbow from https://colorkit.co/palettes/8-colors/
uint32_t palette[8] = {0xEFFFADAD, 0xEFFFD6A5, 0xEFFDFFB6, 0xEFCAFFBF,
0xEF9BF6FF, 0xEFA0C4FF, 0xEFBDB2FF, 0xEFFFC6FF};

static int get_active_note_from_channel(int ch) {
if (ch >= 0 && ch < 8)
return active_notes[ch].note + active_notes[ch].sharp + active_notes[ch].octave;
else
return 0;
}

void fx_piano_init(SDL_Texture *output_texture) {
fx_texture = output_texture;
SDL_QueryTexture(fx_texture, NULL, NULL, &width, &height);
framebuffer = SDL_malloc(sizeof(uint32_t) * width * height);
SDL_memset4(framebuffer, bgcolor, width * height);
is_initialized = 1;
}

void fx_piano_update() {
//Shift everything up by one row and clear bottom row
for (int y = 1; y < height; y++) {
for (int x = 0; x < width; x++) {
int buffer_pos = x + (y * width);
// Top rows have a fading alpha value
if (y<60 && (framebuffer[buffer_pos] >> 24) > 0x04){
framebuffer[buffer_pos-width] = framebuffer[buffer_pos]-((0x05 << 24));
} else {
framebuffer[buffer_pos-width] = framebuffer[buffer_pos];
}
}
}

for (int x = 0; x < width; x++) {
framebuffer[width*(height-1)+x] = bgcolor;
framebuffer[x] = bgcolor;
}

int notes[8];
for (int i = 0; i < 8; i++) {
notes[i] = get_active_note_from_channel(i);

int last_row = (height-1)*width;
int spacing = width / 128;
if (notes[i]>0) {
int pos = last_row + (notes[i] * spacing);
uint32_t color = (framebuffer[pos] + palette[i]) | (0xCF <<24);
for (int bar_width = 0; bar_width < spacing; bar_width++) {
framebuffer[pos++] = color;
}
framebuffer[pos] = color | (0x20 << 24);
}
}

SDL_UpdateTexture(fx_texture, NULL, framebuffer,
width * sizeof(framebuffer[0]));
};

void fx_piano_destroy() {
if (is_initialized) {
SDL_free(framebuffer);
is_initialized = 0;
}
}

/* Converts the note characters displayed on M8 screen to hex values for use in
* visualizers */
int note_to_hex(int note_char) {
switch (note_char) {
case 45: // - = note off
return 0x00;
case 67: // C
return 0x01;
case 68: // D
return 0x03;
case 69: // E
return 0x05;
case 70: // F
return 0x06;
case 71: // G
return 0x08;
case 65: // A
return 0x0A;
case 66: // B
return 0x0C;
default:
return 0x00;
}
}

// Updates the active notes information array with the current command's data
void update_active_notes_data(struct draw_character_command *command, int font_mode) {

int line_spacing;
int character_spacing;
int playback_info_offset_x;

switch (font_mode) {
case 1:
case 4:
// Note name sidebar not visible all the time on MK1 / MK2
return;
case 2:
case 3:
line_spacing = 14;
character_spacing = 12;
playback_info_offset_x = 432;
break;
default:
line_spacing = 10;
character_spacing = 8;
playback_info_offset_x = 288;
break;
}

// Channels are 10 pixels apart, starting from y=70
int channel_number = command->pos.y / line_spacing - 7;


// Note playback information starts from X=288
if (command->pos.x == playback_info_offset_x) {
active_notes[channel_number].note = note_to_hex(command->c);
}

// Sharp notes live at x = 296
if (command->pos.x == playback_info_offset_x + character_spacing && !strcmp((char *)&command->c, "#"))
active_notes[channel_number].sharp = 1;
else
active_notes[channel_number].sharp = 0;

// Note octave, x = 304
if (command->pos.x == playback_info_offset_x + 2*character_spacing) {
int8_t octave = command->c - 48;

// Octaves A-B
if (octave == 17 || octave == 18)
octave -= 7;

// If octave hasn't been applied to the note yet, do it
if (octave > 0)
active_notes[channel_number].octave = octave * 0x0C;
else
active_notes[channel_number].octave = 0;
}
}
17 changes: 17 additions & 0 deletions src/fx_piano.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Created by jonne on 9/8/24.
//

#ifndef FX_PIANO_H
#define FX_PIANO_H

#include "SDL_render.h"
#include "command.h"

void fx_piano_init(SDL_Texture *output_texture);
void fx_piano_update();
void fx_piano_destroy();

void update_active_notes_data(struct draw_character_command *command, int font_mode);

#endif //FX_PIANO_H
2 changes: 2 additions & 0 deletions src/input.c
Original file line number Diff line number Diff line change
@@ -177,6 +177,8 @@ static input_msg_s handle_normal_keys(const SDL_Event *event, const config_param
key = (input_msg_s){special, msg_reset_display, 0, 0};
} else if (event->key.keysym.scancode == conf->key_toggle_audio) {
key = (input_msg_s){special, msg_toggle_audio, 0, 0};
} else if (event->key.keysym.scancode == conf->key_toggle_overlay_fx) {
key = (input_msg_s){special, msg_toggle_overlay_fx, 0, 0};
} else {
key.value = 0;
}
3 changes: 2 additions & 1 deletion src/input.h
Original file line number Diff line number Diff line change
@@ -36,7 +36,8 @@ typedef enum input_type_t { normal, keyjazz, special } input_type_t;
typedef enum special_messages_t {
msg_quit = 1,
msg_reset_display = 2,
msg_toggle_audio = 3
msg_toggle_audio = 3,
msg_toggle_overlay_fx = 4
} special_messages_t;

typedef struct input_msg_s {
9 changes: 5 additions & 4 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -12,8 +12,8 @@
#include "audio.h"
#include "command.h"
#include "config.h"
#include "input.h"
#include "gamecontrollers.h"
#include "input.h"
#include "render.h"
#include "serial.h"
#include "slip.h"
@@ -220,6 +220,9 @@ int main(const int argc, char *argv[]) {
case msg_toggle_audio:
toggle_audio(conf.audio_buffer_size, conf.audio_device_name);
break;
case msg_toggle_overlay_fx:
toggle_special_fx();
break;
default:
break;
}
@@ -284,9 +287,7 @@ int main(const int argc, char *argv[]) {

// exit, clean up
SDL_Log("Shutting down\n");
if (conf.audio_enabled == 1) {
audio_destroy();
}
audio_destroy();
gamecontrollers_close();
close_renderer();
close_serial_port();
121 changes: 98 additions & 23 deletions src/render.c
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
#include "SDL2_inprint.h"
#include "command.h"
#include "fx_cube.h"
#include "fx_piano.h"

#include "font1.h"
#include "font2.h"
@@ -19,7 +20,7 @@

SDL_Window *win;
SDL_Renderer *rend;
SDL_Texture *maintexture;
SDL_Texture *main_texture, *m8_texture, *fx_texture;
SDL_Color background_color = (SDL_Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0x00};

static uint32_t ticks_fps;
@@ -29,6 +30,9 @@ static int m8_hardware_model = 0;
static int screen_offset_y = 0;
static int text_offset_y = 0;
static int waveform_max_height = 24;
static int note_column_y_min = 70;
static int note_column_y_max = 140;
static unsigned int special_fx = 0;

static int texture_width = 320;
static int texture_height = 240;
@@ -60,16 +64,28 @@ int initialize_sdl(const int init_fullscreen, const int init_use_gpu) {

SDL_RenderSetLogicalSize(rend, texture_width, texture_height);

maintexture = NULL;
fx_texture = NULL;
fx_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
texture_width, texture_height);

maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);
m8_texture = NULL;
m8_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);

SDL_SetRenderTarget(rend, maintexture);
SDL_SetTextureBlendMode(fx_texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(m8_texture, SDL_BLENDMODE_BLEND);

main_texture = NULL;
main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);

SDL_SetRenderTarget(rend, fx_texture);
SDL_SetRenderDrawColor(rend, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);

SDL_SetRenderTarget(rend, m8_texture);
SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
background_color.a);

SDL_RenderClear(rend);

set_font_mode(0);
@@ -101,14 +117,23 @@ static void check_and_adjust_window_and_texture_size(const unsigned int new_widt
SDL_SetWindowSize(win, texture_width * 2, texture_height * 2);
}

SDL_DestroyTexture(maintexture);
SDL_DestroyTexture(main_texture);
SDL_DestroyTexture(m8_texture);
SDL_DestroyTexture(fx_texture);

SDL_RenderSetLogicalSize(rend, texture_width, texture_height);

maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);
main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);
m8_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_width, texture_height);
fx_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
texture_width, texture_height);

SDL_SetRenderTarget(rend, maintexture);
SDL_SetTextureBlendMode(fx_texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(m8_texture, SDL_BLENDMODE_BLEND);

SDL_SetRenderTarget(rend, m8_texture);
}

// Set M8 hardware model in use. 0 = MK1, 1 = MK2
@@ -127,6 +152,10 @@ void set_m8_model(const unsigned int model) {
}

void set_font_mode(int mode) {

fx_piano_destroy();
special_fx = 0;

if (mode < 0 || mode > 2) {
// bad font mode
return;
@@ -141,14 +170,21 @@ void set_font_mode(int mode) {
screen_offset_y = fonts[mode]->screen_offset_y;
text_offset_y = fonts[mode]->text_offset_y;
waveform_max_height = fonts[mode]->waveform_max_height;
if (font_mode > 1) {
note_column_y_min = 96;
note_column_y_max = 196;
}

change_font(fonts[mode]);
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Font mode %i, Screen offset %i", mode, screen_offset_y);
}

void close_renderer() {
kill_inline_font();
SDL_DestroyTexture(maintexture);
fx_piano_destroy();
SDL_DestroyTexture(m8_texture);
SDL_DestroyTexture(fx_texture);
SDL_DestroyTexture(main_texture);
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
}
@@ -165,20 +201,25 @@ void toggle_fullscreen() {

int draw_character(struct draw_character_command *command) {

const uint32_t fgcolor =
const uint32_t fg_color =
command->foreground.r << 16 | command->foreground.g << 8 | command->foreground.b;
const uint32_t bgcolor =
const uint32_t bg_color =
command->background.r << 16 | command->background.g << 8 | command->background.b;

/* Note characters appear between y=70 and y=140, update the active notes
array used by visual effects */
if (command->pos.y >= note_column_y_min && command->pos.y <= note_column_y_max) {
update_active_notes_data(command, font_mode);
}

/* Notes:
If large font is enabled, offset the screen elements by a fixed amount.
If background and foreground colors are the same, draw transparent
background. Due to the font bitmaps, a different pixel offset is needed for
both*/

inprint(rend, (char *)&command->c, command->pos.x,
command->pos.y + text_offset_y + screen_offset_y, fgcolor,
bgcolor);
command->pos.y + text_offset_y + screen_offset_y, fg_color, bg_color);

dirty = 1;

@@ -223,7 +264,7 @@ void draw_waveform(struct draw_oscilloscope_waveform_command *command) {
static uint8_t wfm_cleared = 0;
static int prev_waveform_size = 0;

// If the waveform is not being displayed and it's already been cleared, skip
// If the waveform is not being displayed, and it's already been cleared, skip
// rendering it
if (!(wfm_cleared && command->waveform_size == 0)) {

@@ -278,34 +319,52 @@ void display_keyjazz_overlay(const uint8_t show, const uint8_t base_octave,

const Uint16 overlay_offset_x = texture_width - (fonts[font_mode]->glyph_x * 7 + 1);
const Uint16 overlay_offset_y = texture_height - (fonts[font_mode]->glyph_y + 1);
const Uint32 bgcolor =
background_color.r << 16 | background_color.g << 8 | background_color.b;
const Uint32 bg_color = background_color.r << 16 | background_color.g << 8 | background_color.b;

if (show) {
char overlay_text[7];
snprintf(overlay_text, sizeof(overlay_text), "%02X %u", velocity, base_octave);
inprint(rend, overlay_text, overlay_offset_x, overlay_offset_y, 0xC8C8C8, bgcolor);
inprint(rend, overlay_text, overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
inprint(rend, "*", overlay_offset_x + (fonts[font_mode]->glyph_x * 5 + 5), overlay_offset_y,
0xFF0000, bgcolor);
0xFF0000, bg_color);
} else {
inprint(rend, " ", overlay_offset_x, overlay_offset_y, 0xC8C8C8, bgcolor);
inprint(rend, " ", overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
}

dirty = 1;
}

static void render_special_fx() {
switch (special_fx) {
case 1:
fx_piano_update();
break;
default:
break;
}
}

void render_screen() {
if (dirty) {
dirty = 0;

SDL_SetRenderTarget(rend, main_texture);
SDL_RenderCopy(rend, m8_texture, NULL, NULL);

if (special_fx) {
render_special_fx();
SDL_RenderCopy(rend, fx_texture, NULL, NULL);
}

SDL_SetRenderTarget(rend, NULL);

SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
background_color.a);

SDL_RenderClear(rend);
SDL_RenderCopy(rend, maintexture, NULL, NULL);
SDL_RenderCopy(rend, main_texture, NULL, NULL);
SDL_RenderPresent(rend);
SDL_SetRenderTarget(rend, maintexture);
SDL_SetRenderTarget(rend, m8_texture);

fps++;

@@ -334,3 +393,19 @@ void screensaver_destroy() {
set_font_mode(0);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver destroyed");
}

int toggle_special_fx() {
special_fx++;
switch (special_fx) {
case 1:
fx_piano_init(fx_texture);
break;
case 2:
fx_piano_destroy();
special_fx = 0;
break;
default:
break;
}
return special_fx;
}
1 change: 1 addition & 0 deletions src/render.h
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ void view_changed(int view);
void render_screen();
void toggle_fullscreen();
void display_keyjazz_overlay(uint8_t show, uint8_t base_octave, uint8_t velocity);
int toggle_special_fx();

void screensaver_init();
void screensaver_draw();

0 comments on commit d8879a5

Please sign in to comment.