From f2683a4b707cd714b7f540ebf6482563df83d51e Mon Sep 17 00:00:00 2001 From: erdgeist Date: Sat, 8 Apr 2017 14:21:36 +0200 Subject: Near complete rewrite. --- Makefile | 4 +- arduino/Laserharfe/Laserharfe.ino | 105 ++++++++++++---- config.c | 69 ++++++----- config.h | 27 +++-- display.c | 88 ++++++++++++-- display.h | 9 +- engine.c | 172 +++++++++++++++----------- engine.h | 6 + geometry.c | 31 +++++ geometry.h | 18 +++ main-sdl.c | 245 +++++++++++++++++++++++++++++++++----- main.h | 8 ++ 12 files changed, 604 insertions(+), 178 deletions(-) create mode 100644 geometry.c create mode 100644 geometry.h diff --git a/Makefile b/Makefile index b41215a..622150b 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ all: main GenBkBasB.c: GenBkBasB.ttf xxd -i $< $@ -main: main-sdl.c display.c config.c display.h config.h engine.c engine.h midi-sdl.c midi.h GenBkBasB.c GenBkBasB.h - cc -O2 -o Laserharfe main-sdl.c config.c display.c engine.c midi-sdl.c GenBkBasB.c -I /usr/local/include -lm -L/usr/local/lib -lSDL2 -framework Cocoa -lSDL2main -lSDL2_ttf -lSDL2_gfx +main: main-sdl.c calib.c calib.h config.c config.h display.c display.h engine.c engine.h geometry.c geometry.h midi-noop.c midi.h GenBkBasB.c GenBkBasB.h + cc -g -o Laserharfe main-sdl.c calib.c config.c display.c engine.c geometry.c midi-noop.c GenBkBasB.c -I /usr/local/include -lm -L/usr/local/lib -lSDL2 -framework Cocoa -lSDL2main -lSDL2_ttf -lSDL2_gfx .PHONY: clean clean: diff --git a/arduino/Laserharfe/Laserharfe.ino b/arduino/Laserharfe/Laserharfe.ino index a0e8d07..107dffb 100644 --- a/arduino/Laserharfe/Laserharfe.ino +++ b/arduino/Laserharfe/Laserharfe.ino @@ -1,11 +1,3 @@ -/* - * MIDIUSB_test.ino - * - * Created: 4/6/2015 10:47:08 AM - * Author: gurbrinder grewal - * Modified by Arduino LLC (2015) - */ - #include "MIDIUSB.h" #include #include @@ -15,12 +7,16 @@ #include "engine.h" int led = 13; +int g_reading_config = 0; +int g_writing_config = 0; +ConfigSource g_config_source = source_none; +//Sd2Card card; enum { MODE_STANDALONE = 0, MODE_REPORTPOINTS = 1, - MODE_REPORTPOINTS_PLAY = 2, -} g_mode = MODE_REPORTPOINTS_PLAY; + MODE_REPORTPOINTS_PLAY = 2 +} g_mode = MODE_STANDALONE; uint32_t now() { return millis(); @@ -85,6 +81,7 @@ static void set_cam_register(uint8_t r, uint8_t v) { } static void setup_cam() { + digitalWrite(19, HIGH); // turn the LED on (HIGH is the voltage level) set_cam_register(0x30, 0x01); set_cam_register(0x06, 0x90); set_cam_register(0x08, 0xc0); @@ -99,7 +96,6 @@ void read_config() { Serial.println("initialization failed!"); return; } - File configfile = SD.open("laserhar.cfg", FILE_READ); if (!configfile) { Serial.println("opening config failed"); @@ -107,14 +103,15 @@ void read_config() { } char command[256]; - int command_len; - int skip_command; + int command_len = 0; + int skip_command = 0; while (configfile.available()) { char c = command[command_len++] = configfile.read(); if (c=='\n') { if (!skip_command) { command[command_len] = 0; + // Serial.write(command); config_handle_line(command); } skip_command = 0; @@ -126,9 +123,38 @@ void read_config() { command_len = 0; } } + + configfile.close(); +} + +void write_config() { + SD.remove("laserhar.cfg"); + File configfile = SD.open("laserhar.cfg", FILE_WRITE); + if (!configfile) { + Serial.println("opening config failed"); + return; + } + + char text[256]; + size_t len = config_dumpglobals(text, sizeof(text)); + configfile.write(text, len); + for (int i=0; i< g_string_count; ++i) { + len = config_dumpstring(i, text, sizeof(text)); + configfile.write(text, len); + } + Serial.println("-- DONE"); configfile.close(); } +void flashy_death() { + while(1) { + digitalWrite(led, LOW); // turn the LED on (HIGH is the voltage level) + delay(500); + digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) + delay(500); + } +} + void setup() { pinMode(led, OUTPUT); @@ -139,8 +165,8 @@ void setup() { // Let PWM settle for a bit delay(100); setup_cam(); - Serial.begin(115200); + delay(5000); read_config(); digitalWrite(led, LOW); // turn the LED on (HIGH is the voltage level) } @@ -220,10 +246,10 @@ void handle_midi(char *command) { return; if (command[6] != '\n') return; - midiEventPacket_t p = { m[0], (m[0]<<4) | m[1], (m[2]<<4) | m[3], (m[4]<<4) | m[5] }; MidiUSB.sendMIDI(p); - // MidiUSB.flush(); + MidiUSB.flush(); + Serial.println("- MIDI SENT"); } void midi_playnote(int channel, int note, int octave_offset ) { @@ -246,11 +272,20 @@ void midi_pitchbend( int channel, int pitch ) { MidiUSB.sendMIDI(p); } -void handle_configure(char *command) { +void handle_command(char *command) { + char text[512]; + size_t len; +// Serial.write( text, sprintf(text, "- %s\n", command)); -} + if (g_reading_config ) { + if ( !strcmp(command, "-- DONE")) { + g_reading_config = 0; + return; + } + config_handle_line(command); + return; + } -void handle_command(char *command) { switch (*command) { case 'M': /* Getting MIDI instruction */ handle_midi(command+1); @@ -266,12 +301,32 @@ void handle_command(char *command) { case '3': g_mode = MODE_REPORTPOINTS; break; - default: - break; } break; - case 'C': /* Getting configure command */ - handle_configure(command+1); + case 'C': /* Import a single config line from host */ + config_handle_line(command+1); + break; + case 'E': /* Export config from host, import here */ + config_reset(); + g_reading_config = true; + break; + case 'I': /* Import config at host */ + delay(1000); + len = config_dumpglobals(text, sizeof(text)); + Serial.write(text, len); + for (int i=0; i< g_string_count; ++i) { + len = config_dumpstring(i, text, sizeof(text)); + delay(100); + Serial.write(text, len); + } + delay(100); + Serial.println("-- DONE"); + break; + case 'R': /* Re-read config */ + read_config(); + break; + case 'W': /* Write local config */ + write_config(); break; default: break; @@ -279,7 +334,7 @@ void handle_command(char *command) { } void handle_serial() { - static char command[128]; + static char command[512]; static int command_len; static int skip_command; @@ -323,6 +378,6 @@ void loop() { */ engine_checksilence(millis()); - digitalWrite(led, ++led_state & 1 ? HIGH : LOW); // turn the LED on (HIGH is the voltage level) + digitalWrite(led, ++led_state & 0xff < 0x7f ? HIGH : LOW); // turn the LED on (HIGH is the voltage level) } diff --git a/config.c b/config.c index fb8a6fa..e373727 100644 --- a/config.c +++ b/config.c @@ -8,9 +8,9 @@ int g_midi_main_control = -1; int g_midi_main_channel = 0; -int g_midi_two_octave_split = 256 / 2; -int g_midi_three_octave_split_1 = 256 / 3; -int g_midi_three_octave_split_2 = 512 / 3; +int g_midi_two_octave_split = 50; +int g_midi_three_octave_split_1 = 33; +int g_midi_three_octave_split_2 = 66; int g_midi_three_octave_split_inverse = 0; int g_settled_dist = 5; int g_timetosilence = 30; @@ -33,12 +33,14 @@ config_reset() g_midi_main_control = -1; g_midi_main_channel = 0; - g_midi_two_octave_split = 256 / 2; - g_midi_three_octave_split_1 = 256 / 3; - g_midi_three_octave_split_2 = 512 / 3; + g_midi_two_octave_split = 50; + g_midi_three_octave_split_1 = 33; + g_midi_three_octave_split_2 = 66; g_midi_three_octave_split_inverse = 0; g_settled_dist = 5; g_timetosilence = 30; + + g_config_source = source_none; } @@ -206,23 +208,19 @@ config_handle_line(char *line) { LLine *l = &sc->line; - if (sscanf(line, "%d %d %d %d", &l->x0, &l->y0, &l->x1, &l->y1) != 4) { + if (sscanf(line, "%d %d %d %d", &l->p0.x, &l->p0.y, &l->p1.x, &l->p1.y) != 4) { fprintf(stderr, "Incorrect Line statement for string\n"); return -1; } - if (l->y0 > l->y1) { - l->y0 ^= l->y1; - l->y1 ^= l->y0; - l->y0 ^= l->y1; - l->x0 ^= l->x1; - l->x1 ^= l->x0; - l->x0 ^= l->x1; - + if (l->p0.y > l->p1.y) { + LPoint tmp = l->p0; + l->p0 = l->p1; + l->p1 = tmp; } - if (l->y0 > g_min_y) - g_min_y = l->y0; - if (l->y1 < g_max_y) - g_max_y = l->y1; + if (l->p0.y > g_min_y) + g_min_y = l->p0.y; + if (l->p0.y < g_max_y) + g_max_y = l->p1.y; break; } case KEYWORD_MODE: @@ -259,7 +257,7 @@ config_handle_line(char *line) fprintf(stderr, "Incorrect channel specified: %s.\n", _line); return -1; } - printf("String %d is on channel %d\n", g_current_string, sc->channel); + printf("String %d is on channel %d\n", 1 + g_current_string, sc->channel); break; case KEYWORD_NOTE: sc->note = config_midi_note_from_string(line); @@ -267,7 +265,7 @@ config_handle_line(char *line) fprintf(stderr, "Unknown midi note specified: %s.\n", _line); return -1; } - printf("String %d is midi note %d\n", g_current_string, sc->note); + printf("String %d is midi note %d\n", 1 + g_current_string, sc->note); break; case KEYWORD_AFTERTOUCH: switch (config_findkeyword(&line)) { @@ -315,7 +313,6 @@ config_handle_line(char *line) fprintf(stderr, "Invalid percentage in line: %s\n", _line); exit(1); } - g_midi_two_octave_split = (256 * g_midi_two_octave_split) / 100; break; case KEYWORD_THREEOCTAVESPLIT_1: g_midi_three_octave_split_1 = atol(line); @@ -324,7 +321,6 @@ config_handle_line(char *line) fprintf(stderr, "Invalid percentage in line: %s\n", _line); exit(1); } - g_midi_three_octave_split_1 = (256 * g_midi_three_octave_split_1) / 100; split_done = 1; break; case KEYWORD_THREEOCTAVESPLIT_2: @@ -334,7 +330,6 @@ config_handle_line(char *line) fprintf(stderr, "Invalid percentage in line: %s\n", _line); return -1; } - g_midi_three_octave_split_2 = (256 * g_midi_three_octave_split_2) / 100; split_done = 1; break; case KEYWORD_MIDI_MAIN_CONTROL: @@ -377,34 +372,43 @@ config_handle_line(char *line) return 0; } -void +size_t config_dumpglobals(char *out, size_t outsize) { int tos1 = g_midi_three_octave_split_inverse ? g_midi_three_octave_split_2 : g_midi_three_octave_split_1; int tos2 = g_midi_three_octave_split_inverse ? g_midi_three_octave_split_1 : g_midi_three_octave_split_2; size_t off = snprintf(out, outsize, - "Strings %d\n\nmidi_two_octave_split %d\nmidi_three_octave_split_1 %d\nmidi_three_octave_split_2 %d\nmidi_main_control %d\nmidi_main_channel %d\n", - g_string_count, g_midi_two_octave_split, tos1, tos2, g_midi_main_control, g_midi_main_channel ); + "Strings %d\n\nmidi_two_octave_split %d\nmidi_three_octave_split_1 %d\nmidi_three_octave_split_2 %d\n", + g_string_count, g_midi_two_octave_split, tos1, tos2 ); + + if (g_midi_main_control >= 0) + off += snprintf(out + off, outsize - off, + "midi_main_control %d\n\n", g_midi_main_control); + + if (g_midi_main_channel > 0) + off += snprintf(out + off, outsize - off, + "midi_main_channel %d\n\n", g_midi_main_channel); if (g_settled_dist != 5) off += snprintf(out + off, outsize - off, "SettledDist %d\n\n", g_settled_dist); - snprintf(out + off, outsize - off, "\n" ); + off += snprintf(out + off, outsize - off, "\n" ); + return off; } -void +size_t config_dumpstring(int string, char *out, size_t outsize) { StringConfig *s = g_string_conf + string; if (string < 0 || string > g_string_count || !out) { if (out && outsize) *out = 0; - return; + return 0; } size_t off = snprintf(out, outsize, "String %d\n Line %d %d %d %d\n Mode %s\n Channel %d\n Note %s\n AfterTouch %s\n Controller %d\n", - string + 1, s->line.x0, s->line.y0, s->line.x1, s->line.y1, + string + 1, s->line.p0.x, s->line.p0.y, s->line.p1.x, s->line.p1.y, mode_names[s->mode], s->channel, midi_note[s->note], modifier_names[s->modifier], s->controller ); if (s->timetosilence) @@ -413,5 +417,6 @@ config_dumpstring(int string, char *out, size_t outsize) if (s->pitch_factor) off += snprintf(out + off, outsize - off, " PitchFactor %d\n", s->pitch_factor); - snprintf(out + off, outsize - off, "\n" ); + off += snprintf(out + off, outsize - off, "\n" ); + return off; } diff --git a/config.h b/config.h index d7399fa..64f7689 100644 --- a/config.h +++ b/config.h @@ -1,8 +1,10 @@ #pragma once #include +#include "geometry.h" #define MAX_LINECOUNT 32 +#define CALIB_DEBUG extern int g_min_y, g_max_y; @@ -37,14 +39,21 @@ typedef enum { playing = 2 } StringPlaying; -typedef struct { - int x0; int y0; - int x1; int y1; -} LLine; +typedef enum { + sel_none = 0, + sel_min_y = 1, + sel_max_y = 2, + sel_2_oct = 3, + sel_3_oct_top = 4, + sel_3_oct_bottom = 5 +} ConfigSelect; -typedef struct { - int x; int y; -} LPoint; +typedef enum { + source_none = 0, + source_harfe = 1, + source_file = 2, + source_edit = 3 +} ConfigSource; typedef struct { StringMode mode; @@ -70,6 +79,6 @@ extern StringConfig g_string_conf[MAX_LINECOUNT]; extern int g_string_count; void config_reset(); int config_handle_line( char *line); -void config_dumpglobals(char *out, size_t outsize); -void config_dumpstring(int string, char *out, size_t outsize); +size_t config_dumpglobals(char *out, size_t outsize); +size_t config_dumpstring(int string, char *out, size_t outsize); char *config_midi_note_to_string(int string); diff --git a/display.c b/display.c index 42235a2..4263e52 100644 --- a/display.c +++ b/display.c @@ -6,25 +6,30 @@ #include "display.h" #define display_measure_text(text,w,h) TTF_SizeText(font,(text),(w),(h)) +enum { FONT_SIZE = 28 }; static SDL_Window *screen; static SDL_Renderer *renderer; -static int g_width, g_height; +static int g_screen_width, g_screen_height; +static int g_harfe_width, g_harfe_height; +double g_scale_factor; static TTF_Font *font = NULL; void -display_init(int width, int height) +display_init(int screen_width, int screen_height, int harfe_width, int harfe_height) { SDL_RWops *font_file; - g_width = width; - g_height = height; + g_screen_width = screen_width; + g_screen_height = screen_height; + g_harfe_width = harfe_width; + g_harfe_height = harfe_height; if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { fprintf(stderr, "Can't initialize SDL.\n"); exit(1); } - screen = SDL_CreateWindow("Laserharfe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL); + screen = SDL_CreateWindow("Laserharfe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, SDL_WINDOW_OPENGL); if (!screen) { fprintf(stderr, "Can't set video mode.\n"); exit(1); @@ -35,7 +40,32 @@ display_init(int width, int height) TTF_Init(); font_file = SDL_RWFromConstMem(GenBkBasB_ttf, GenBkBasB_ttf_len); - font = TTF_OpenFontRW(font_file, 1, 28); + font = TTF_OpenFontRW(font_file, 1, FONT_SIZE); + + g_scale_factor = (double)harfe_height / (double)screen_height; + if( (double)harfe_width / (double)screen_width < g_scale_factor ) + g_scale_factor = (double)harfe_width / (double)screen_width; + printf( "Using scale factor of %lf\n", g_scale_factor ); +} + +void +display_getdimensions(int *width, int *height, int *fontsize) +{ + *width = g_screen_width; + *height = g_screen_height; + *fontsize = FONT_SIZE; +} + +int +display_scale_harfe_to_screen(int coord) +{ + return (int)((double)coord / g_scale_factor); +} + +int +display_scale_screen_to_harfe(int coord) +{ + return (int)((double)coord * g_scale_factor); } void @@ -48,19 +78,23 @@ display_clear() void display_circle(int x, int y, int w) { - if (x >= 0 && x < g_width && y >= 0 && y < g_height) - display_circle_color(x, y, w, 0xffffffff); + y = g_screen_height - y; + if (x >= 0 && x <= g_screen_width && y >= 0 && y <= g_screen_height) + filledCircleColor(renderer, x, y, w, 0xffffffff); } void display_circle_color(int x, int y, int w, int color) { + y = g_screen_height - y; filledCircleColor(renderer, x, y, w, color); } void display_line(int x0, int y0, int x1, int y1) { + y1 = g_screen_height - y1; + y0 = g_screen_height - y0; SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderDrawLine(renderer, x0, y0, x1, y1); } @@ -68,10 +102,24 @@ display_line(int x0, int y0, int x1, int y1) void display_line_color(int x0, int y0, int x1, int y1, int color) { + y1 = g_screen_height - y1; + y0 = g_screen_height - y0; SDL_SetRenderDrawColor(renderer, (color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, 255); SDL_RenderDrawLine(renderer, x0, y0, x1, y1); } +void +display_rect_color(int x, int y, int width, int height, int color) +{ + SDL_Rect r; + r.x = x; + r.y = g_screen_height - y; + r.w = width; + r.h = height; + SDL_SetRenderDrawColor(renderer, (color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, 255); + SDL_RenderFillRect(renderer, &r); +} + void display_redraw() { @@ -81,6 +129,7 @@ display_redraw() void display_text(char *text, int x, int y, int color) { + y = g_screen_height - y; SDL_Color s_color = { 255 & (color>>24), 255 & (color>>16), 255 & (color>>8) }; SDL_Surface *sText = TTF_RenderText_Solid(font, text, s_color); SDL_Rect rect = {x, y, sText->w, sText->h}; @@ -98,7 +147,7 @@ display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int colo display_measure_text("#", &w, &h); item_height = 3 * h / 2; - min_y = (g_height - item_height * max_pos) / 2 + pos * item_height; + min_y = (g_screen_height - item_height * max_pos) / 2 + pos * item_height; //boxColor(screen, min_x, min_y, max_x, min_y + item_height, color); //rectangleColor(screen, min_x, min_y, max_x, min_y + item_height, 0xffffffff); @@ -106,6 +155,25 @@ display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int colo display_text(text, min_x + (max_x - min_x - w) / 2, min_y + (item_height - h) / 2, 0xffffffff); } +void +display_messagebox(char *title, char *info) { + SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_INFORMATION, title, info, screen); +} + +int +display_messagebox_yesno(char *title, char *info) { + const SDL_MessageBoxButtonData buttons[] = { + { /* .flags, .buttonid, .text */ 0, 0, "Cancel" }, + { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "OK" }, + }; + const SDL_MessageBoxData messageboxdata = { + SDL_MESSAGEBOX_INFORMATION, screen, title, info, SDL_arraysize(buttons), buttons, NULL + }; + int buttonid; + SDL_ShowMessageBox(&messageboxdata, &buttonid); + return buttonid == 1; +} + int display_test_menu_click(int y, int max_pos) { @@ -114,7 +182,7 @@ display_test_menu_click(int y, int max_pos) /* display_measure_text( "#", &w, &h ); item_height = 3 * h / 2; - min_y = ( g_height - item_height * max_pos ) / 2; + min_y = ( g_screen_height - item_height * max_pos ) / 2; if( y < min_y ) return -1; if( y > min_y + item_height * max_pos ) return -1; return ( y - min_y ) / item_height; diff --git a/display.h b/display.h index adc08b7..c7003fd 100644 --- a/display.h +++ b/display.h @@ -1,6 +1,9 @@ #pragma once -void display_init(int width, int height); +void display_init(int screen_width, int screen_height, int harfe_width, int harfe_height); +void display_getdimensions(int *width, int *height, int *fontsize); +int display_scale_harfe_to_screen(int coord); +int display_scale_screen_to_harfe(int coord); void display_redraw(); void display_clear(); @@ -8,10 +11,12 @@ void display_line(int x0, int y0, int x1, int y1); void display_line_color(int x0, int y0, int x1, int y1, int color); void display_circle(int x, int y, int w); void display_circle_color(int x, int y, int w, int color); -void display_rectangle(int x, int y, int w, int h); +void display_rect_color(int x, int y, int width, int height, int color); void display_text(char *text, int x, int y, int color); void display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int color); int display_test_menu_click(int y, int max_pos); +void display_messagebox(char *title, char *info); +int display_messagebox_yesno(char *title, char *info); void display_report(char *message, int line); diff --git a/engine.c b/engine.c index 0d92b50..43892fb 100644 --- a/engine.c +++ b/engine.c @@ -2,6 +2,7 @@ #include #include "config.h" +#include "geometry.h" #include "engine.h" #include "main.h" #include "midi.h" @@ -13,91 +14,131 @@ static int g_selected_string = -1; static LPoint g_render_points[1024]; static int g_render_point_count; -static const int g_harfe_width = 1024; -static const int g_harfe_height = 768; -static int g_factor = 1<<16; +static ConfigSelect g_selected_config; +static void highlight_line(int value, int y, int max_x, uint32_t color); -static inline int scale(int coord) { - return (int)(((((int64_t)coord) << 32) / g_factor ) >> 16); -} +#define scale(X) display_scale_harfe_to_screen(X) void engine_redraw() { - int i; + char text[32]; + char *config_hints[] = { "No", "Harfe", "File", "Edit" }; + int i, MAX_X, MAX_Y, FONT_HEIGHT; + int height = g_max_y - g_min_y; display_redraw(); display_clear(); - display_text(g_harfe_connected ? "online" : "offline", 4, 4, 0xffffffff); + display_getdimensions(&MAX_X, &MAX_Y, &FONT_HEIGHT); - for (i = 0; i < g_string_count; ++i) { - LLine *l = &g_string_conf[i].line; - uint32_t color = i == g_selected_string ? 0xff00ffff : 0x00ffffffff; + snprintf( text, sizeof(text), g_harfe_connected ? "online (%s)" : "offline (%s)", config_hints[(int)g_config_source]); + display_text(text, 8, MAX_Y - 4, g_harfe_connected ? 0x00ff3fff : 0xff003fff ); - display_line_color(scale(l->x0), scale(l->y0), scale(l->x1), scale(l->y1), color); - display_circle(scale(l->x0), scale(l->y0), 4); - display_circle(scale(l->x1), scale(l->y1), 4); + if (height) { + int b = g_midi_three_octave_split_inverse; + int tos1 = g_midi_three_octave_split_1, tos2 = g_midi_three_octave_split_2; + display_text( b ? "+1" : "-1", 4, scale(g_min_y + tos1 * height / 200) + FONT_HEIGHT / 2, 0x007f7f7fff); + display_text( " 0", 4, scale(g_min_y + (tos1 + tos2) * height / 200) + FONT_HEIGHT / 2, 0x007f7f7fff); + display_text( b ? "-1" : "+1", 4, scale(g_min_y + (tos2 / 2 + 50 ) * height / 100) + FONT_HEIGHT / 2, 0x007f7f7fff); - if (g_string_conf[i].playing) - display_text(config_midi_note_to_string(g_string_conf[i].note+12*g_string_conf[i].octave), scale(l->x1) - 20, g_height - 40, color ); - else - display_text(config_midi_note_to_string(g_string_conf[i].note), scale(l->x1) - 20, g_height - 40, color ); + display_line_color(0, scale(g_min_y), MAX_X, scale(g_min_y), 0xff00ffff); + display_line_color(0, scale(g_max_y), MAX_X, scale(g_max_y), 0xff00ffff); + + display_line_color(0, scale(g_min_y + g_midi_two_octave_split * height / 100), MAX_X, scale(g_min_y + g_midi_two_octave_split * height / 100), 0xffff00ff); + + display_line_color(0, scale(g_min_y + tos1 * height / 100), MAX_X, scale(g_min_y + tos1 * height / 100), 0x00ff00ff); + display_line_color(0, scale(g_min_y + tos2 * height / 100), MAX_X, scale(g_min_y + tos2 * height / 100), 0x00ff00ff); + + + switch (g_selected_config) { + case sel_min_y: highlight_line(g_min_y, g_min_y, MAX_X, 0xff00ffff); break; + case sel_max_y: highlight_line(g_max_y, g_max_y, MAX_X, 0xff00ffff); break; + case sel_2_oct: highlight_line(g_midi_two_octave_split, g_min_y + g_midi_two_octave_split * height / 100, MAX_X, 0xffff00ff); break; + case sel_3_oct_top: highlight_line(tos2, g_min_y + tos2 * height / 100, MAX_X, 0x00ff00ff); break; + case sel_3_oct_bottom: highlight_line(tos1, g_min_y + tos1 * height / 100, MAX_X, 0x00ff00ff); break; + case sel_none: break; + } + + for (i = 0; i < g_string_count; ++i) { + LLine *l = &g_string_conf[i].line; + uint32_t color = ( ( i == g_selected_string ) ? 0xff00ffff : 0x00ffffffff ); + uint32_t text_color = ( ( i == g_selected_string ) ? 0xff00ffff : 0x007f7f7fff ); + + int center_y = g_min_y+height/2; + int x2 = l->p1.x; + + if (l->p1.x!=l->p0.x) { + double m = (double)(l->p1.y-l->p0.y) / (double)(l->p1.x-l->p0.x); + double _x2 = ((double)center_y) - (double)l->p0.y + m * (double)l->p0.x; + x2 = (int)(_x2/m); + } + + if (g_string_conf[i].playing) + display_text(config_midi_note_to_string(g_string_conf[i].note+12*g_string_conf[i].octave), scale(x2-20), scale(center_y) + FONT_HEIGHT, text_color ); + else + display_text(config_midi_note_to_string(g_string_conf[i].note), scale(x2-20), scale(center_y) + FONT_HEIGHT, text_color ); + + display_line_color(scale(l->p0.x), scale(l->p0.y), scale(l->p1.x), scale(l->p1.y), color); + display_circle(scale(l->p0.x), scale(l->p0.y), 4); + display_circle(scale(l->p1.x), scale(l->p1.y), 4); + + } } g_selected_string = -1; for (i = 0; i < g_render_point_count; ++i) display_circle_color(scale(g_render_points[i].x), scale(g_render_points[i].y), 4, 0xff0000ff); g_render_point_count = 0; +} - display_line_color(0, scale(g_min_y), g_width, scale(g_min_y), 0xff00ffff); - display_line_color(0, scale(g_max_y), g_width, scale(g_max_y), 0xff00ffff); - - if (g_min_y != g_max_y) { - int height = scale(g_max_y - g_min_y); - - display_line_color(0, scale(g_max_y) - g_midi_two_octave_split * height / 256, g_width, scale(g_max_y) - g_midi_two_octave_split * height / 256, 0xffff00ff); +static void +highlight_line(int value, int y, int max_x, uint32_t color) { + char text[32]; + display_line_color(0, scale(y-10), 10, scale(y), color); + display_line_color(0, scale(y+10), 10, scale(y), color); + display_line_color(max_x, scale(y-10), max_x-10, scale(y), color); + display_line_color(max_x, scale(y+10), max_x-10, scale(y), color); - display_line_color(0, scale(g_max_y) - g_midi_three_octave_split_1 * height / 256, g_width, scale(g_max_y) - g_midi_three_octave_split_1 * height / 256, 0x00ff00ff); - display_line_color(0, scale(g_max_y) - g_midi_three_octave_split_2 * height / 256, g_width, scale(g_max_y) - g_midi_three_octave_split_2 * height / 256, 0x00ff00ff); - } + display_rect_color(max_x/2-40, scale(y+20), 80, 40, 0xffffffff); + display_text(text, max_x/2-20, scale(y+20), 0); } -#endif - void -engine_init() { -#ifndef NO_DISPLAY - g_factor = (g_harfe_width << 16) / g_width; - if ((g_harfe_height << 16) / g_height < g_factor) - g_factor = (g_harfe_height << 16) / g_height; -#endif +engine_select_config(ConfigSelect sel) { + g_selected_config = sel; } -static int -dist_pp(int x0, int y0, int x1, int y1) +ConfigSelect +engine_change_selected(int off) { - return (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1); + switch(g_selected_config) { + case sel_3_oct_top: + g_midi_three_octave_split_2 += off; + if (g_midi_three_octave_split_2<0) g_midi_three_octave_split_2 = 0; + if (g_midi_three_octave_split_2>100) g_midi_three_octave_split_2 = 100; + break; + case sel_3_oct_bottom: + g_midi_three_octave_split_1 += off; + if (g_midi_three_octave_split_1<0) g_midi_three_octave_split_1 = 0; + if (g_midi_three_octave_split_1>100) g_midi_three_octave_split_1 = 100; + break; + case sel_2_oct: + g_midi_two_octave_split += off; + if (g_midi_two_octave_split<0) g_midi_two_octave_split = 0; + if (g_midi_two_octave_split>100) g_midi_two_octave_split = 100; + break; + default: + break; + } + return g_selected_config; } -// dist is a fixed point with precission of 8 bits -// offs is where on the line segment xy0-xy1 the point's normale hits, -// range 0..65536 (but can extend, if normale hits line outside line segment) -static int -dist_lp(int x0, int y0, int x1, int y1, int xp, int yp, int *offs) -{ - int64_t r = (y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0); - int64_t q1 = (xp - x0) * (y1 - y0) - (yp - y0) * (x1 - x0); - int64_t q2 = (x1 - x0) * (xp - x0) + (y1 - y0) * (yp - y0); +#endif - *offs = (int)((q2 << 16) / r); - return (int)( q1 * q1 * ((y0 - y1) * (y0 - y1) + (x1 - x0) * (x1 - x0)) * 256 / (r * r)); +void +engine_init() { } -static int -dist_pl(LPoint * p, LLine * l, int *offs) -{ - return dist_lp(l->x0, l->y0, l->x1, l->y1, p->x, p->y, offs); -} void engine_handle_point(LPoint * p, uint32_t monotime) @@ -108,9 +149,6 @@ engine_handle_point(LPoint * p, uint32_t monotime) int y_viewfield, pitch_factor = 12; int dv, dt, speed, new_pitch; - // XXX should not be inverted here - p->x = 1024 - p->x; - #ifndef NO_DISPLAY /* Pass to "render thread" */ g_render_points[g_render_point_count] = *p; @@ -120,27 +158,25 @@ engine_handle_point(LPoint * p, uint32_t monotime) /* See which line is closest */ for (i = 0; i < g_string_count; ++i) { int dist = dist_pl(p, &g_string_conf[i].line, &offs); - if ((dist < 256 * 10 * 10 ) && (dist < dist_max)) { + + /* Avoid miss-fires, check if offset is in range -5% - +105% */ + if ((dist < 512 * 10 * 10 ) && (dist < dist_max) && (offs<68812) && (offs>-3276)) { dist_max = dist; saite = i; } } - /* Avoid miss-fires, check if offset is in range -5% - +105% */ - if (offs>68812 || offs<-3276) - return; - if (saite == -1) return; s = g_string_conf + saite; g_selected_string = saite; - y_viewfield = 256 * (g_max_y - p->y) / (g_max_y - g_min_y); + y_viewfield = (100 * (p->y - g_min_y)) / (g_max_y - g_min_y); if (y_viewfield < 0) y_viewfield = 0; - if (y_viewfield >= 256) - y_viewfield = 255; + if (y_viewfield >= 100) + y_viewfield = 100; // Determine octave, if configured switch (s->mode) { @@ -183,7 +219,7 @@ engine_handle_point(LPoint * p, uint32_t monotime) dv = abs(s->start_off - offs); dt = monotime - s->first_time_seen; if (!dt) ++dt; - speed = 1000 * dv / dt; // in offs_prec per second + speed = 1000 * dv / dt; // in offs_prec per second } s->last_off = offs; break; @@ -193,7 +229,7 @@ engine_handle_point(LPoint * p, uint32_t monotime) if (s->modifier == pitch_bend_up) new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; else if (s->modifier == pitch_bend_down) - new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; + new_pitch = (pitch_factor * (offs - s->start_off)) >> 16; else break; // avoid reporting same pitch bend over and over diff --git a/engine.h b/engine.h index 3b59e86..2d47dc7 100644 --- a/engine.h +++ b/engine.h @@ -1,6 +1,12 @@ #pragma once +#include "config.h" + void engine_init(); void engine_redraw(); void engine_handle_point(LPoint * p, uint32_t monotime); void engine_checksilence(uint32_t monotime); +void engine_select_config(ConfigSelect sel); +ConfigSelect engine_change_selected(int off); + +extern int g_harfe_width, g_harfe_height; diff --git a/geometry.c b/geometry.c new file mode 100644 index 0000000..f2961c7 --- /dev/null +++ b/geometry.c @@ -0,0 +1,31 @@ +#include "geometry.h" + +// dist is a fixed point with precission of 8 bits +// offs is where on the line segment xy0-xy1 the point's normale hits, +// range 0..65536 (but can extend, if normale hits line outside line segment) +static inline int +impl_dist_pl(int xp, int yp, int x0, int y0, int x1, int y1, int *offs) +{ + double r = (y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0); + double q1 = (xp - x0) * (y1 - y0) - (yp - y0) * (x1 - x0); + double q2 = (x1 - x0) * (xp - x0) + (y1 - y0) * (yp - y0); + + *offs = (int)((q2 *65336.0f) / r); + return (int)( q1 * q1 * ((double)(y0 - y1) * (double)(y0 - y1) + (double)(x1 - x0) * (double)(x1 - x0)) * 256.0f / (r * r)); +} + +int +dist_pl(LPoint const * p, LLine const * l, int *offs) +{ + return impl_dist_pl(p->x, p->y, l->p0.x, l->p0.y, l->p1.x, l->p1.y, offs); +} + +static inline int +impl_dist_pp(int x0, int y0, int x1, int y1 ) { + return (y0-y1)*(y0-y1)+(x0-x1)*(x0-x1); +} + +int +dist_pp(LPoint const * p0, LPoint const * p1) { + return impl_dist_pp(p0->x,p0->y,p1->x,p1->y); +} diff --git a/geometry.h b/geometry.h new file mode 100644 index 0000000..509ec7e --- /dev/null +++ b/geometry.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct { + int x; + int y; +} LPoint; + +typedef struct { + LPoint p0; + LPoint p1; +} LLine; + +// dist is a fixed point with precission of 8 bits +// offs is where on the line segment xy0-xy1 the point's normale hits, +// range 0..65536 (but can extend, if normale hits line outside line segment) +int dist_pl(LPoint const * p, LLine const * l, int * offs); +int dist_pp(LPoint const * p0, LPoint const * p1); + diff --git a/main-sdl.c b/main-sdl.c index 687e5c7..6bc3970 100644 --- a/main-sdl.c +++ b/main-sdl.c @@ -10,22 +10,31 @@ #include #include #include +#include #include #include "display.h" #include "config.h" #include "engine.h" +#include "calib.h" /*** Global config and status values ***/ /* Window width and height */ -// const int g_width = 1024, g_height = 768; -const int g_width = 800, g_height = 600; +enum { + HARFE_WIDTH = 1024, + HARFE_HEIGHT = 768, + SCREEN_WIDTH = 800, + SCREEN_HEIGHT = 600 +}; int g_harfe_connected = 0; int g_harfe_fd = -1; +int g_importing_config = 0; +ConfigSource g_config_source = source_none; +int g_calibration_running = 0; static char * find_harfe() @@ -150,6 +159,13 @@ harfe_worker(void) free(portname); g_harfe_connected = 1; + /* Get remote config (if any) */ + config_reset(); + write(g_harfe_fd, "S2\n", 3); + usleep(50); + g_importing_config = 1; + write(g_harfe_fd, "I\n", 2); + while (running) { while (text_fill < sizeof(text) && !memchr(text, '\n', text_fill)) { ssize_t b = read(g_harfe_fd, text + text_fill, sizeof(text) - text_fill); @@ -178,15 +194,29 @@ harfe_worker(void) if (text_fill && lineend[-1] == '\r') lineend[-1] = 0; printf( "%s\n", text ); - int num_points = sscanf(text, "%04d:%04d %04d:%04d %04d:%04d %04d:%04d", &p[0].x, &p[0].y, &p[1].x, &p[1].y, &p[2].x, &p[2].y, &p[3].x, &p[3].y); - ptime = now(); - for (i = 0; i < num_points / 2; ++i) { + if (g_importing_config) { + if (!strcmp(text, "-- DONE")) { + g_importing_config = 0; + g_config_source = source_harfe; + } else + config_handle_line(text); + } else { + + int num_points = sscanf(text, "%04d:%04d %04d:%04d %04d:%04d %04d:%04d", &p[0].x, &p[0].y, &p[1].x, &p[1].y, &p[2].x, &p[2].y, &p[3].x, &p[3].y); + + ptime = now(); + for (i = 0; i < num_points / 2; ++i) { // printf("%04d:%04d\n", p[i].x, p[i].y); - engine_handle_point(p + i, ptime); - } - if (num_points > 1 || *text == '-') + + if (!g_calibration_running) + engine_handle_point(p + i, ptime); + else + calib_handle_point(p + i, ptime); + } + if (num_points > 1 || *text == '-') ++g_events; + } consumed = lineend - text + 1; memmove(text, lineend + 1, text_fill - consumed); @@ -226,6 +256,36 @@ config_parse(char *config_file) } fclose(fh); + g_config_source = source_file; +} + +static void +calib_fetch() { + int default_notes[] = { 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83 }; + int i, result_count; + LLine *result = calib_get_results(&result_count); + + config_reset(); + g_string_count = result_count; + + g_min_y = 0; g_max_y = 1024; + + for (i=0; ip0.y > g_min_y) + g_min_y = l->p0.y; + if (l->p1.y < g_max_y) + g_max_y = l->p1.y; + + g_string_conf[i].line = *l; + g_string_conf[i].mode = midi_three_octaves; + g_string_conf[i].channel = i; + g_string_conf[i].note = default_notes[i]; + g_string_conf[i].modifier = pitch_bend_up; + } + + g_config_source = source_edit; } int @@ -236,48 +296,153 @@ main(int argc, char **argv) pthread_t thread_id; uint32_t runtime; static int last_click_x, last_click_y, last_mouse_event; + static int g_up_pressed = 0, g_down_pressed = 0; - display_init(g_width, g_height); + display_init(SCREEN_WIDTH, SCREEN_HEIGHT, HARFE_WIDTH, HARFE_HEIGHT); engine_init(); - config_parse("config_midi"); +// config_parse("config_midi"); pthread_create(&thread_id, NULL, worker, NULL); +/* + E - Export config to Harfe (with implicite write to SD) + I - Import config from Harfe + W - Write config on Harfe to SD (no implicite Transfer) + + S - Save config to local file + L - Load config from local default (config_midi) + + Y - Invert String order + + Q - Reset local Config + C - Start calibration + M - Play MIDI C through Harfe +*/ + /* Spin and let call back do all the work */ while (1) { SDL_WaitEventTimeout(&ev, 10); switch (ev.type) { case SDL_QUIT: exit(0); - case SDL_KEYDOWN: + case SDL_KEYUP: + if (ev.key.keysym.scancode == SDL_SCANCODE_UP) g_up_pressed = 0; + if (ev.key.keysym.scancode == SDL_SCANCODE_DOWN) g_down_pressed = 0; + break; + case SDL_KEYDOWN: /* - if( ev.key.keysym.sym >= SDLK_1 && ev.key.keysym.sym <= SDLK_9 ) + if ( ev.key.keysym.sym >= SDLK_1 && ev.key.keysym.sym <= SDLK_9 ) engine_select_string( ev.key.keysym.sym - SDLK_1 ); - if( ev.key.keysym.sym == SDLK_BACKSPACE || ev.key.keysym.sym == SDLK_DELETE ) + if ( ev.key.keysym.sym == SDLK_BACKSPACE || ev.key.keysym.sym == SDLK_DELETE ) engine_delete_selected_string( ); - if( ev.key.keysym.sym == SDLK_d ) { + if ( ev.key.keysym.sym == SDLK_d ) { g_stringsdescending = 1 - g_stringsdescending; printf( "String order (left to right) is now %sscending.\n", g_stringsdescending ? "de" : "a" ); } */ - if( ev.key.keysym.scancode == SDL_SCANCODE_R) { - fprintf( stderr, "re-read config\n" ); - write(g_harfe_fd, "R\n", 2); + if ( ev.key.keysym.scancode == SDL_SCANCODE_0) + engine_select_config(sel_none); + if ( ev.key.keysym.scancode == SDL_SCANCODE_1) + engine_select_config(sel_min_y); + if ( ev.key.keysym.scancode == SDL_SCANCODE_2) + engine_select_config(sel_max_y); + if ( ev.key.keysym.scancode == SDL_SCANCODE_3) + engine_select_config(sel_2_oct); + if ( ev.key.keysym.scancode == SDL_SCANCODE_4) + engine_select_config(sel_3_oct_top); + if ( ev.key.keysym.scancode == SDL_SCANCODE_5) + engine_select_config(sel_3_oct_bottom); + + if ( ev.key.keysym.scancode == SDL_SCANCODE_UP && ! g_up_pressed++) + engine_change_selected(1); + if ( ev.key.keysym.scancode == SDL_SCANCODE_DOWN && ! g_down_pressed++) + engine_change_selected(-1); + + if ( ev.key.keysym.scancode == SDL_SCANCODE_S) { /* export locally */ + const char *homeDir = getenv("HOME"); + char savefile[512], date[32], confdump[512]; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + int fd; + size_t len; + + if (!homeDir) { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) + homeDir = (const char*)pwd->pw_dir; + } + strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", tmp); + snprintf( savefile, sizeof(savefile), "%s/Laserharfe-%s.cfg", homeDir, date); + fd = open(savefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + len = config_dumpglobals( confdump, sizeof(confdump)); + write(fd, confdump, len ); + for (i=0; i 30) { + if (runtime - g_lastredraw > 30 && !g_importing_config) { g_lastredraw = runtime; - engine_redraw(); + + if (!g_calibration_running) + engine_redraw(); + else + calib_redraw(); + } if (runtime / 1000 - g_last_avg > 10) { printf("avg: %i\n", g_events / 10); diff --git a/main.h b/main.h index 7d87f99..38fe4ff 100644 --- a/main.h +++ b/main.h @@ -1,5 +1,6 @@ #pragma once #include +#include "config.h" #ifdef ARDUINO #define NO_DISPLAY @@ -11,4 +12,11 @@ extern int g_width; extern int g_height; extern int g_harfe_connected; +/* 0 unconfigured + 1 import from Harfe + 2 import from File + 3 modified locally +*/ +extern ConfigSource g_config_source; + extern int g_harfe_fd; -- cgit v1.2.3