From a8be0d3d20f07d4561826b01f566ca307eb23526 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 12 Aug 2016 14:46:51 +0200 Subject: commit as a backup --- GenBkBasB.h | 4 + GenBkBasB.ttf | Bin 0 -> 267024 bytes Makefile | 11 +++ config.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 75 ++++++++++++++ config_midi | 112 +++++++++++++++++++++ display.c | 123 +++++++++++++++++++++++ display.h | 17 ++++ engine.c | 220 +++++++++++++++++++++++++++++++++++++++++ engine.h | 6 ++ main-sdl.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.h | 10 ++ midi.c | 39 ++++++++ midi.h | 9 ++ 14 files changed, 1203 insertions(+) create mode 100644 GenBkBasB.h create mode 100755 GenBkBasB.ttf create mode 100644 Makefile create mode 100644 config.c create mode 100644 config.h create mode 100644 config_midi create mode 100644 display.c create mode 100644 display.h create mode 100644 engine.c create mode 100644 engine.h create mode 100644 main-sdl.c create mode 100644 main.h create mode 100644 midi.c create mode 100644 midi.h diff --git a/GenBkBasB.h b/GenBkBasB.h new file mode 100644 index 0000000..762176f --- /dev/null +++ b/GenBkBasB.h @@ -0,0 +1,4 @@ +#pragma once + +extern unsigned char GenBkBasB_ttf[]; +extern unsigned int GenBkBasB_ttf_len; diff --git a/GenBkBasB.ttf b/GenBkBasB.ttf new file mode 100755 index 0000000..5a852af Binary files /dev/null and b/GenBkBasB.ttf differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f5ed44 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +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.c midi.h GenBkBasB.c GenBkBasB.h + cc -O2 -o Laserharfe main-sdl.c config.c display.c engine.c midi.c GenBkBasB.c -I /usr/local/include -lm -L/usr/local/lib -lSDL2 -framework Cocoa -lSDL2main -lSDL2_ttf -lSDL2_gfx + +.PHONY: clean +clean: + rm -f Laserharfe GenBkBasB.c diff --git a/config.c b/config.c new file mode 100644 index 0000000..8465dc7 --- /dev/null +++ b/config.c @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include + +#include "main.h" +#include "config.h" + +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_three_octave_split_inverse = 0; +int g_settled_dist = 5; +int g_timetosilence = 30; + +int g_min_y = 0, g_max_y; + +static char *midi_note[] = { + "C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1", "G#-1", "A-1", "A#-1", "B-1", + "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0", + "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1", + "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", + "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", + "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", + "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", + "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", + "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", + "C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8", + "C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9" +}; + +static uint8_t +config_midi_note_from_string(char *string) +{ + int i; + + for (i = 0; i < sizeof(midi_note) / sizeof(*midi_note); ++i) + if (!strncasecmp(string, midi_note[i], strlen(midi_note[i]))) + return i; + return 0xff; +} + +char *config_midi_note_to_string(int string) { + return midi_note[string]; +} + +#define LINEBUFFER 512 +static char *config_file; +void +config_parse(char *config_file_in) +{ + FILE *fh; + char line_in[LINEBUFFER]; + int current_string = -1; + + if (!config_file_in && !config_file) + return; + if (config_file_in) + config_file = config_file_in; + fh = fopen(config_file, "r"); + + if (!fh) { + fprintf(stderr, "Couldn't open config file %s, exiting.\n", config_file); + exit(1); + } + + //Reinitialise string config array + memset(g_string_conf, 0, sizeof(g_string_conf)); + g_max_y = 1024; + + while (!feof(fh)) { + char *line = fgets(line_in, LINEBUFFER, fh); + + if (!line) + break; + + //Skip leading spaces + while (isspace(*line)) + ++line; + if (*line == 0 || *line == '#' || *line == '\n') + continue; + + if (!strncasecmp(line, "Strings", 7) && isspace(line[7])) { + line += 7; + while (isspace(*line)) + ++line; + g_string_count = atol(line); + if (!g_string_count || g_string_count > MAX_LINECOUNT) { + fprintf(stderr, "Incorrect number of strings: %s\n", line); + exit(1); + } + printf("GLOBAL: Configuring expected lines %d\n", g_string_count); + } else if (!strncasecmp(line, "String", 6) && isspace(line[6])) { + line += 6; + while (isspace(*line)) + ++line; + current_string = atol(line); + printf("Switching to string: %d\n", current_string); + if (current_string < 0 || current_string > g_string_count) { + fprintf(stderr, "Incorrect string selected: %s\n", line); + exit(1); + } + } else if (!strncasecmp(line, "Line", 4) && isspace(line[4])) { + LLine *l = &g_string_conf[current_string - 1].line; + + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 4; + while (isspace(*line)) + ++line; + if (sscanf(line, "%d %d %d %d", &l->x0, &l->y0, &l->x1, &l->y1) != 4) { + fprintf(stderr, "Incorrect Line statement for string\n"); + exit(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->y0 > g_min_y) + g_min_y = l->y0; + if (l->y1 < g_max_y) + g_max_y = l->y1; + } else if (!strncasecmp(line, "Mode", 4) && isspace(line[4])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 4; + while (isspace(*line)) + ++line; + if (!strncasecmp(line, "midi_one_octave", 15) && (!line[15] || isspace(line[15]))) { + g_string_conf[current_string - 1].mode = midi_one_octave; + printf("String %d is midi_one_octave\n", current_string); + } else if (!strncasecmp(line, "midi_two_octaves", 16) && (!line[16] || isspace(line[16]))) { + g_string_conf[current_string - 1].mode = midi_two_octaves; + printf("String %d is midi_two_octaves\n", current_string); + } else if (!strncasecmp(line, "midi_three_octaves", 18) && (!line[18] || isspace(line[18]))) { + g_string_conf[current_string - 1].mode = midi_three_octaves; + printf("String %d is midi_three_octaves\n", current_string); + } else if (!strncasecmp(line, "midi_control", 12) && (!line[12] || isspace(line[12]))) { + g_string_conf[current_string - 1].mode = midi_control; + printf("String %d is midi_control\n", current_string); + } else if (!strncasecmp(line, "midi_control_inverse", 20) && (!line[20] || isspace(line[20]))) { + g_string_conf[current_string - 1].mode = midi_control_inv; + printf("String %d is midi_control_inverse\n", current_string); + } else { + fprintf(stderr, "Illegal Mode for string: %s\n", line); + exit(1); + } + } else if (!strncasecmp(line, "Channel", 7) && isspace(line[7])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 7; + while (isspace(*line)) + ++line; + g_string_conf[current_string - 1].channel = atol(line); + if (g_string_conf[current_string - 1].channel > 16) { + fprintf(stderr, "Incorrect channel specified: %s.\n", line); + exit(1); + } + printf("String %d is on channel %d\n", current_string, g_string_conf[current_string - 1].channel); + } else if (!strncasecmp(line, "Note", 4) && isspace(line[4])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 4; + while (isspace(*line)) + ++line; + g_string_conf[current_string - 1].note = config_midi_note_from_string(line); + if (g_string_conf[current_string - 1].note == 0xff) { + fprintf(stderr, "Unknown midi note specified: %s.\n", line); + exit(1); + } + printf("String %d is midi note %d\n", current_string, g_string_conf[current_string - 1].note); + } else if (!strncasecmp(line, "AfterTouch", 10) && isspace(line[10])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 10; + while (isspace(*line)) + ++line; + if (!strncasecmp(line, "none", 4) && (!line[4] || isspace(line[4]))) { + g_string_conf[current_string - 1].modifier = none; + printf("String %d does not act aftertouch\n", current_string); + } else if (!strncasecmp(line, "pitch_bend_up", 13) && (!line[13] || isspace(line[13]))) { + g_string_conf[current_string - 1].modifier = pitch_bend_up; + printf("String %d acts aftertouch as pitch_bend_up\n", current_string); + } else if (!strncasecmp(line, "pitch_bend_down", 15) && (!line[15] || isspace(line[15]))) { + g_string_conf[current_string - 1].modifier = pitch_bend_down; + printf("String %d acts aftertouch as pitch_bend_down\n", current_string); + } else if (!strncasecmp(line, "midi_control", 12) && (!line[12] || isspace(line[12]))) { + g_string_conf[current_string - 1].modifier = midi_controller; + printf("String %d acts aftertouch as midi_controller\n", current_string); + } else if (!strncasecmp(line, "midi_control_inverse", 20) && (!line[20] || isspace(line[20]))) { + g_string_conf[current_string - 1].modifier = midi_controller_inv; + printf("String %d acts aftertouch as midi_controller_inverse\n", current_string); + } else { + fprintf(stderr, "Illegal Modifier for string: %s\n", line); + exit(1); + } + } else if (!strncasecmp(line, "Controller", 10) && isspace(line[10])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 10; + while (isspace(*line)) + ++line; + g_string_conf[current_string - 1].controller = atol(line); + printf("String %d is on midi_controller line %d\n", current_string, g_string_conf[current_string - 1].controller); + } else if (!strncasecmp(line, "TimeToSilence", 13) && isspace(line[13])) { + if (current_string == -1) { + fprintf(stderr, "No string selected yet.\n"); + exit(1); + } + line += 13; + while (isspace(*line)) + ++line; + g_string_conf[current_string - 1].timetosilence = atol(line); + printf("String %d has a timetosilence of %d\n", current_string, g_string_conf[current_string - 1].timetosilence); + } else if (!strncasecmp(line, "midi_two_octave_split", 21) && isspace(line[21])) { + line += 21; + while (isspace(*line)) + ++line; + g_midi_two_octave_split = atol(line); + printf("Splitting TWO octaves at %d%%\n", g_midi_two_octave_split); + if (g_midi_two_octave_split < 0 || g_midi_two_octave_split > 100) { + fprintf(stderr, "Invalid percentage in line: %s\n", line); + exit(1); + } + g_midi_two_octave_split = (256 * g_midi_two_octave_split) / 100; + } else if (!strncasecmp(line, "midi_three_octave_split_1", 25) && isspace(line[25])) { + line += 25; + while (isspace(*line)) + ++line; + g_midi_three_octave_split_1 = atol(line); + printf("Splitting THREE octaves top above %d%%\n", g_midi_three_octave_split_1); + if (g_midi_three_octave_split_1 < 0 || g_midi_three_octave_split_1 > 100) { + 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; + } else if (!strncasecmp(line, "midi_three_octave_split_2", 25) && isspace(line[25])) { + line += 25; + while (isspace(*line)) + ++line; + g_midi_three_octave_split_2 = atol(line); + printf("Splitting THREE octaves bottom below %d%%\n", g_midi_three_octave_split_2); + if (g_midi_three_octave_split_2 < 0 || g_midi_three_octave_split_2 > 100) { + fprintf(stderr, "Invalid percentage in line: %s\n", line); + exit(1); + } + g_midi_three_octave_split_2 = (256 * g_midi_three_octave_split_2) / 100; + } else if (!strncasecmp(line, "midi_main_control", 17) && isspace(line[17])) { + line += 17; + while (isspace(*line)) + ++line; + g_midi_main_control = atol(line); + printf("All Strings modify controller %d\n", g_midi_main_control); + if (g_midi_main_control > 127) { + fprintf(stderr, "Invalid controller number %d in line: %s\n", g_midi_main_control, line); + exit(1); + } + } else if (!strncasecmp(line, "midi_main_channel", 17) && isspace(line[17])) { + line += 17; + while (isspace(*line)) + ++line; + g_midi_main_channel = atol(line); + printf("All Strings modify controller %d on channel %d\n", g_midi_main_control, g_midi_main_channel); + if (g_midi_main_channel < 1 || g_midi_main_channel > 16) { + fprintf(stderr, "Invalid channel number %d in line: %s\n", g_midi_main_channel, line); + exit(1); + } + } else { + fprintf(stderr, "Unhandled config line: %s\n", line); + exit(1); + } + + // split also true for two octaves + if (g_midi_three_octave_split_2 < g_midi_three_octave_split_1) { + g_midi_three_octave_split_inverse = g_midi_three_octave_split_1; + g_midi_three_octave_split_1 = g_midi_three_octave_split_2; + g_midi_three_octave_split_2 = g_midi_three_octave_split_inverse; + g_midi_three_octave_split_inverse = 1; + } else + g_midi_three_octave_split_inverse = 0; + } + fclose(fh); +} + +void +config_write() +{ + +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..2a9c09d --- /dev/null +++ b/config.h @@ -0,0 +1,75 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include + +#define MAX_LINECOUNT 32 + +extern int g_min_y, g_max_y; + +extern int g_midi_two_octave_split; +extern int g_midi_three_octave_split_1; +extern int g_midi_three_octave_split_2; +extern int g_midi_three_octave_split_inverse; +extern int g_midi_main_control; +extern int g_midi_main_channel; +extern int g_settled_dist; +extern int g_timetosilence; + +typedef enum { + midi_one_octave = 0, + midi_two_octaves = 1, + midi_three_octaves = 2, + midi_control = 3, + midi_control_inv = 4 +} StringMode; + +typedef enum { + none = 0, + pitch_bend_up = 1, + pitch_bend_down = 2, + midi_controller = 3, + midi_controller_inv = 4 +} StringModifier; + +typedef enum { + silent = 0, + in_attack = 1, + playing = 2 +} StringPlaying; + +typedef struct { + int x0; int y0; + int x1; int y1; +} LLine; + +typedef struct { + int x; int y; +} LPoint; + +typedef struct { + StringMode mode; + LLine line; + uint8_t channel; + uint8_t note; + uint8_t controller; + int timetosilence; + StringModifier modifier; + int pitch_factor; + +/* runtime values */ + uint32_t first_time_seen; + uint32_t last_time_seen; + StringPlaying playing; + int octave; + int start_off; + int last_off; + int current_pitch; +} StringConfig; + +extern StringConfig g_string_conf[MAX_LINECOUNT]; +extern int g_string_count; +void config_parse( char *config_file ); +char *config_midi_note_to_string(int string); + +#endif diff --git a/config_midi b/config_midi new file mode 100644 index 0000000..641002a --- /dev/null +++ b/config_midi @@ -0,0 +1,112 @@ +# This line tells LaserHarfe, how many Strings +# it will expect on screen. Be sure to add one +# config section for each String below +# +Strings 8 + +# Enable next line to invert string order +# +# StringsDescending + +# Use these lines to configure where your +# midi_two_octaves and midi_three_octaves +# Strings should be split on screen +# +# Value is in percent +# + +midi_two_octave_split 50 + +# If split_1 > split_2 then +# higher notes are on the bottom +# + +midi_three_octave_split_1 33 +midi_three_octave_split_2 66 + +# Demo String section +# +# String x +# Mode midi_one_octave +# Mode midi_two_octaves +# Mode midi_three_octaves +# Mode midi_control +# Mode midi_control_inverse +# Channel 0..15 +# Controller 0..15 (use 0 unless really sure) +# AfterTouch pitch_bend_up +# AfterTouch pitch_bend_down +# AfterTouch midi_control +# TimeToSilence 15 + +String 1 + Line 860 450 768 684 + Mode midi_three_octaves + Note C4 + Channel 0 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 2 + Line 738 448 672 683 + Mode midi_three_octaves + Note D4 + Channel 1 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 3 + Line 640 450 602 679 + Mode midi_three_octaves + Note E4 + Channel 2 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 4 + Line 535 442 519 681 + Mode midi_three_octaves + Note F4 + Channel 3 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 5 + Line 425 420 437 676 + Mode midi_three_octaves + Note G4 + Channel 4 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 6 + Line 319 428 355 673 + Mode midi_three_octaves + Note A4 + Channel 5 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 7 + Line 208 432 272 667 + Mode midi_three_octaves + Note B4 + Channel 6 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 + +String 8 + Line 92 422 187 668 + Mode midi_three_octaves + Note C5 + Channel 7 + Controller 0 + AfterTouch pitch_bend_up + TimeToSilence 15 diff --git a/display.c b/display.c new file mode 100644 index 0000000..42235a2 --- /dev/null +++ b/display.c @@ -0,0 +1,123 @@ +#include "SDL2/SDL.h" +#include "SDL2/SDL2_gfxPrimitives.h" +#include + +#include "GenBkBasB.h" +#include "display.h" + +#define display_measure_text(text,w,h) TTF_SizeText(font,(text),(w),(h)) + +static SDL_Window *screen; +static SDL_Renderer *renderer; +static int g_width, g_height; +static TTF_Font *font = NULL; + +void +display_init(int width, int height) +{ + SDL_RWops *font_file; + + g_width = width; + g_height = 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); + if (!screen) { + fprintf(stderr, "Can't set video mode.\n"); + exit(1); + } + renderer = SDL_CreateRenderer(screen, -1, 0); + + SDL_RenderClear(renderer); + + TTF_Init(); + font_file = SDL_RWFromConstMem(GenBkBasB_ttf, GenBkBasB_ttf_len); + font = TTF_OpenFontRW(font_file, 1, 28); +} + +void +display_clear() +{ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); +} + +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); +} + +void +display_circle_color(int x, int y, int w, int color) +{ + filledCircleColor(renderer, x, y, w, color); +} + +void +display_line(int x0, int y0, int x1, int y1) +{ + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_RenderDrawLine(renderer, x0, y0, x1, y1); +} + +void +display_line_color(int x0, int y0, int x1, int y1, int color) +{ + SDL_SetRenderDrawColor(renderer, (color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, 255); + SDL_RenderDrawLine(renderer, x0, y0, x1, y1); +} + +void +display_redraw() +{ + SDL_RenderPresent(renderer); +} + +void +display_text(char *text, int x, int y, int color) +{ + 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}; + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, sText); + + SDL_RenderCopy(renderer, texture, NULL, &rect); + SDL_DestroyTexture(texture); + SDL_FreeSurface(sText); +} + +void +display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int color) +{ + int w, h, min_y, item_height; + + display_measure_text("#", &w, &h); + item_height = 3 * h / 2; + min_y = (g_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); + display_measure_text(text, &w, &h); + display_text(text, min_x + (max_x - min_x - w) / 2, min_y + (item_height - h) / 2, 0xffffffff); +} + +int +display_test_menu_click(int y, int max_pos) +{ + int w, h, min_y, item_height; + +/* + display_measure_text( "#", &w, &h ); + item_height = 3 * h / 2; + min_y = ( g_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; +*/ + return 0; +} diff --git a/display.h b/display.h new file mode 100644 index 0000000..adc08b7 --- /dev/null +++ b/display.h @@ -0,0 +1,17 @@ +#pragma once + +void display_init(int width, int height); +void display_redraw(); + +void display_clear(); +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_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_report(char *message, int line); diff --git a/engine.c b/engine.c new file mode 100644 index 0000000..ae62cb5 --- /dev/null +++ b/engine.c @@ -0,0 +1,220 @@ +#include +#include + +#include "config.h" +#include "engine.h" +#include "display.h" +#include "main.h" +#include "midi.h" + +static int g_selected_string = -1; + +#ifndef NO_DISPLAY +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 inline int scale(int coord) { + return (int)(((((int64_t)coord) << 32) / g_factor ) >> 16); +} + +void +engine_redraw() +{ + int i; + + display_redraw(); + display_clear(); + display_text(g_harfe_connected ? "online" : "offline", 4, 4, 0xffffffff); + + for (i = 0; i < g_string_count; ++i) { + LLine *l = &g_string_conf[i].line; + uint32_t color = i == g_selected_string ? 0xff00ffff : 0x00ffffffff; + + 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 (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 ); + } + 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); + + 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); + } +} + +#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 +} + +static int +dist_pp(int x0, int y0, int x1, int y1) +{ + return (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1); +} + +// 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); + + *offs = (int)((q2 << 16) / r); + return (int)( q1 * q1 * ((y0 - y1) * (y0 - y1) + (x1 - x0) * (x1 - x0)) * 256 / (r * r)); +} + +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) +{ + StringConfig *s;; + int dist_max = 1024 * 1024 * 8; + int offs, saite = -1, i, oct = 0; + 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; + ++g_render_point_count; +#endif + + /* 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)) { + dist_max = dist; + saite = i; + } + } + + 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); + if (y_viewfield < 0) + y_viewfield = 0; + if (y_viewfield >= 256) + y_viewfield = 255; + + // Determine octave, if configured + switch (s->mode) { + case midi_controller: + case midi_controller_inv: + return; // not implemented + case midi_one_octave: + break; + case midi_two_octaves: + if (y_viewfield < g_midi_two_octave_split) + oct = -1; + break; + case midi_three_octaves: + if (y_viewfield < g_midi_three_octave_split_1) + oct = -1; + if (y_viewfield > g_midi_three_octave_split_2) + oct = 1; + break; + } + // handle inverted octave configuration + if (g_midi_three_octave_split_inverse) + oct = -oct; + + switch (s->playing) { + case silent: + midi_playnote(s->channel, s->note, oct); + s->playing = in_attack; + s->octave = oct; + s->start_off = s->last_off = offs; + s->first_time_seen = monotime; + break; + case in_attack: + // test if difference is less than g_settled_dist percent of + // line segment length + if (100 * abs(s->last_off - offs) < g_settled_dist << 16) { + s->playing = playing; + s->current_pitch = 0; + + // estimated energy of hand is dv/dt from first seen to settled + dv = abs(s->start_off - offs); + dt = monotime - s->first_time_seen; + if (!dt) ++dt; + speed = 1000 * dv / dt; // in offs_prec per second + } + s->last_off = offs; + break; + case playing: + if (s->pitch_factor) + pitch_factor = s->pitch_factor; + 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; + else + break; + // avoid reporting same pitch bend over and over + if (s->current_pitch == new_pitch) + break; + midi_pitchbend(s->channel, new_pitch); + s->current_pitch = new_pitch; + break; + } + s->last_time_seen = monotime; +} + +void +engine_checksilence(uint32_t monotime) +{ + int i; + + for (i = 0; i < g_string_count; ++i) { + StringConfig *s = g_string_conf + i; + int tts = s->timetosilence ? s->timetosilence : g_timetosilence; + + if (s->mode == midi_controller) + continue; + if (s->playing && (monotime - s->last_time_seen > tts)) { + midi_silencenote(s->channel, s->note, s->octave); + s->playing = 0; + } + } +} diff --git a/engine.h b/engine.h new file mode 100644 index 0000000..3b59e86 --- /dev/null +++ b/engine.h @@ -0,0 +1,6 @@ +#pragma once + +void engine_init(); +void engine_redraw(); +void engine_handle_point(LPoint * p, uint32_t monotime); +void engine_checksilence(uint32_t monotime); diff --git a/main-sdl.c b/main-sdl.c new file mode 100644 index 0000000..1f6d92d --- /dev/null +++ b/main-sdl.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "display.h" +#include "config.h" +#include "engine.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; + +StringConfig +g_string_conf[MAX_LINECOUNT]; +int g_string_count; +int g_harfe_connected = 0; +int g_harfe_fd = -1; + +static char * +find_harfe() +{ + // Device name should be a cu.device + // followed by the letter HAR somewhere + // e.g. /dev /cu.usbmodemHAR1 + DIR * dev = opendir("/dev/"); + struct dirent *dp; + char *harfe = 0; + + if (!dev) + return 0; + + while ((dp = readdir(dev)) != NULL) { + size_t len = dp->d_namlen; + char *name = dp->d_name, *H, *A, *R; + int i; + + if (len < 6 || name[0] != 'c' || name[1] != 'u' || name[2] != '.') + continue; + + for (i = 0; i < len - 3; ++i) + if (name[i] == 'H' && name[i + 1] == 'A' && name[i + 2] == 'R') { + if ((harfe = calloc(1, 5 + len + 1))) { + sprintf(harfe, "/dev/"); + memcpy(harfe + 5, name, len); + } + break; + } + } + closedir(dev); + return harfe; +} + +int +set_interface_attribs(int fd, int speed, int parity) +{ + struct termios tty; + + memset(&tty, 0, sizeof tty); + if (tcgetattr(fd, &tty) != 0) + return -1; + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); + + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + tty.c_iflag &= ~IGNBRK; + tty.c_lflag = 0; + tty.c_oflag = 0; + tty.c_cc[VMIN] = 0; + tty.c_cc[VTIME] = 5; + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); + + tty.c_cflag |= (CLOCAL | CREAD); + + tty.c_cflag &= ~(PARENB | PARODD); + tty.c_cflag |= parity; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CRTSCTS; + + if (tcsetattr(fd, TCSANOW, &tty) != 0) + return -1; + return 0; +} + +void +set_blocking(int fd, int should_block) +{ + struct termios tty; + + memset(&tty, 0, sizeof tty); + if (tcgetattr(fd, &tty) != 0) + return; + + tty.c_cc[VMIN] = should_block ? 1 : 0; + tty.c_cc[VTIME] = 5; + + if (tcsetattr(fd, TCSANOW, &tty) != 0) + return; +} + + +uint32_t +now() +{ + struct timeval now; + + gettimeofday(&now, (struct timezone *)NULL); + return now.tv_sec * 1000 + now.tv_usec / 1000; /* in ms */ +} + +static uint32_t g_last_avg; +static uint32_t g_starttime, g_last_mouse_event, g_lastredraw; +static int g_events; +static void +harfe_worker(void) +{ + LPoint p[4]; + char text[256], *lineend; + int fd, i, text_fill = 0, consumed, running = 1; + uint32_t ptime; + + char *portname = find_harfe(); + + if (!portname) { + printf("Can't find harfe serial device.\n"); + return; + } + g_harfe_fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); + if (g_harfe_fd < 0) { + free(portname); + printf("Can't connect to harfe..."); + return; + } + set_interface_attribs(g_harfe_fd, B115200, 0); + set_blocking(g_harfe_fd, 0); + + printf("Connection to harfe established at %s.\n", portname); + free(portname); + g_harfe_connected = 1; + + 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); + + if ((b < 0) && (errno != EAGAIN)) { + printf("Connection to harfe lost..."); + running = 0; + close(g_harfe_fd); + g_harfe_fd = -1; + break; + } + text_fill += b; + } + + //Overlong line, drop it and try to resync + if (text_fill == sizeof(text)) { + text_fill = 0; + continue; + } + + // find and remove newline and cf + if (!(lineend = memchr(text, '\n', text_fill))) + continue; + + *lineend = 0; + if (text_fill && lineend[-1] == '\r') + lineend[-1] = 0; + + 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 == '-') + ++g_events; + + consumed = lineend - text + 1; + memmove(text, lineend + 1, text_fill - consumed); + text_fill -= consumed; + } + return; +} +static void * +worker(void *args) +{ + while (1) { + harfe_worker(); + g_harfe_connected = 0; + printf("retrying in 5 seconds.\n"); + sleep(5); + } +} + +int +main(int argc, char **argv) +{ + SDL_Event ev; + int i, counter = 0; + pthread_t thread_id; + uint32_t runtime; + static int last_click_x, last_click_y, last_mouse_event; + + display_init(g_width, g_height); + engine_init(); + config_parse("config_midi"); + + pthread_create(&thread_id, NULL, worker, NULL); + + /* 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: +/* + 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 ) + engine_delete_selected_string( ); + 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" ); + } +*/ + break; + case SDL_MOUSEBUTTONDOWN: +/* + if( ( g_last_mouse_event / 1000 ) != ( engine_now( ) / 1000 ) || ev.button.x != last_click_x || ev.button.y != last_click_y ) + engine_process_mouse( ev.button.x, ev.button.y ); + last_click_x = ev.button.x; + last_click_y = ev.button.y; + last_mouse_event = engine_now( ); +*/ + break; + } + + engine_checksilence(now()); + runtime = now(); + if (runtime - g_lastredraw > 30) { + g_lastredraw = runtime; + engine_redraw(); + } + if (runtime / 1000 - g_last_avg > 10) { + printf("avg: %i\n", g_events / 10); + g_events = 0; + g_last_avg = runtime / 1000; + } + } + + return 0; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..2ae09fc --- /dev/null +++ b/main.h @@ -0,0 +1,10 @@ +#pragma once +#include + +uint32_t now(); // get monotonic time in ms + +extern int g_width; +extern int g_height; +extern int g_harfe_connected; + +extern int g_harfe_fd; diff --git a/midi.c b/midi.c new file mode 100644 index 0000000..7509859 --- /dev/null +++ b/midi.c @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "midi.h" +#include "main.h" + +int midi_init() { + return 0; +} + +void midi_playnote( int channel, int note, int octave_offset ) { + char out[32]; + int b = sprintf(out,"M%02X0020\nM%02X%02X%02X\n", 0xe0 | channel, 0x90 | channel, note + 12 * octave_offset, 0x7f); + if (g_harfe_connected && (g_harfe_fd != -1)) + write(g_harfe_fd, out, b); +} + +void midi_silencenote( int channel, int note, int octave_offset ) { + char out[10]; + int b = sprintf(out,"M%02X%02X%02X\n", 0x80 | channel, note + 12 * octave_offset, 0); + if (g_harfe_connected && (g_harfe_fd != -1)) + write(g_harfe_fd, out, b); +} + +void midi_pitchbend( int channel, int pitch ) { + char out[10]; + pitch += 8192; + if (pitch < 0) + pitch = 0; + if (pitch > 16383) + pitch = 16383; + int b = sprintf(out,"M%02X%02X%02X\n", 0xe0 | channel, 0x7f & pitch, 0x7f & (pitch>>7)); + if (g_harfe_connected && (g_harfe_fd != -1)) + write(g_harfe_fd, out, b); +} + +//void midi_controller_event( int saite, int value ); + diff --git a/midi.h b/midi.h new file mode 100644 index 0000000..41a29fe --- /dev/null +++ b/midi.h @@ -0,0 +1,9 @@ +#pragma once + +int midi_init(); + +void midi_playnote( int channel, int note, int octave_offset ); +void midi_silencenote( int channel, int note, int octave_offset ); +void midi_pitchbend( int channel, int pitch ); +//void midi_controller_event( int saite, int value ); + -- cgit v1.2.3