diff options
| author | erdgeist <erdgeist@bauklotz.fritz.box> | 2016-08-12 14:46:51 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@bauklotz.fritz.box> | 2016-08-12 14:46:51 +0200 |
| commit | a8be0d3d20f07d4561826b01f566ca307eb23526 (patch) | |
| tree | b2c5c6d513ae3a84aba8e4eea94ec32e46d352fa | |
commit as a backup
| -rw-r--r-- | GenBkBasB.h | 4 | ||||
| -rwxr-xr-x | GenBkBasB.ttf | bin | 0 -> 267024 bytes | |||
| -rw-r--r-- | Makefile | 11 | ||||
| -rw-r--r-- | config.c | 309 | ||||
| -rw-r--r-- | config.h | 75 | ||||
| -rw-r--r-- | config_midi | 112 | ||||
| -rw-r--r-- | display.c | 123 | ||||
| -rw-r--r-- | display.h | 17 | ||||
| -rw-r--r-- | engine.c | 220 | ||||
| -rw-r--r-- | engine.h | 6 | ||||
| -rw-r--r-- | main-sdl.c | 268 | ||||
| -rw-r--r-- | main.h | 10 | ||||
| -rw-r--r-- | midi.c | 39 | ||||
| -rw-r--r-- | midi.h | 9 |
14 files changed, 1203 insertions, 0 deletions
diff --git a/GenBkBasB.h b/GenBkBasB.h new file mode 100644 index 0000000..762176f --- /dev/null +++ b/GenBkBasB.h | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | extern unsigned char GenBkBasB_ttf[]; | ||
| 4 | extern unsigned int GenBkBasB_ttf_len; | ||
diff --git a/GenBkBasB.ttf b/GenBkBasB.ttf new file mode 100755 index 0000000..5a852af --- /dev/null +++ b/GenBkBasB.ttf | |||
| Binary files differ | |||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f5ed44 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | all: main | ||
| 2 | |||
| 3 | GenBkBasB.c: GenBkBasB.ttf | ||
| 4 | xxd -i $< $@ | ||
| 5 | |||
| 6 | main: main-sdl.c display.c config.c display.h config.h engine.c engine.h midi.c midi.h GenBkBasB.c GenBkBasB.h | ||
| 7 | 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 | ||
| 8 | |||
| 9 | .PHONY: clean | ||
| 10 | clean: | ||
| 11 | 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 @@ | |||
| 1 | #include <stdlib.h> | ||
| 2 | #include <fcntl.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <ctype.h> | ||
| 6 | |||
| 7 | #include "main.h" | ||
| 8 | #include "config.h" | ||
| 9 | |||
| 10 | int g_midi_main_control = -1; | ||
| 11 | int g_midi_main_channel = 0; | ||
| 12 | int g_midi_two_octave_split = 256 / 2; | ||
| 13 | int g_midi_three_octave_split_1 = 256 / 3; | ||
| 14 | int g_midi_three_octave_split_2 = 512 / 3; | ||
| 15 | int g_midi_three_octave_split_inverse = 0; | ||
| 16 | int g_settled_dist = 5; | ||
| 17 | int g_timetosilence = 30; | ||
| 18 | |||
| 19 | int g_min_y = 0, g_max_y; | ||
| 20 | |||
| 21 | static char *midi_note[] = { | ||
| 22 | "C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1", "G#-1", "A-1", "A#-1", "B-1", | ||
| 23 | "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0", | ||
| 24 | "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1", | ||
| 25 | "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", | ||
| 26 | "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", | ||
| 27 | "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", | ||
| 28 | "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", | ||
| 29 | "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", | ||
| 30 | "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", | ||
| 31 | "C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8", | ||
| 32 | "C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9" | ||
| 33 | }; | ||
| 34 | |||
| 35 | static uint8_t | ||
| 36 | config_midi_note_from_string(char *string) | ||
| 37 | { | ||
| 38 | int i; | ||
| 39 | |||
| 40 | for (i = 0; i < sizeof(midi_note) / sizeof(*midi_note); ++i) | ||
| 41 | if (!strncasecmp(string, midi_note[i], strlen(midi_note[i]))) | ||
| 42 | return i; | ||
| 43 | return 0xff; | ||
| 44 | } | ||
| 45 | |||
| 46 | char *config_midi_note_to_string(int string) { | ||
| 47 | return midi_note[string]; | ||
| 48 | } | ||
| 49 | |||
| 50 | #define LINEBUFFER 512 | ||
| 51 | static char *config_file; | ||
| 52 | void | ||
| 53 | config_parse(char *config_file_in) | ||
| 54 | { | ||
| 55 | FILE *fh; | ||
| 56 | char line_in[LINEBUFFER]; | ||
| 57 | int current_string = -1; | ||
| 58 | |||
| 59 | if (!config_file_in && !config_file) | ||
| 60 | return; | ||
| 61 | if (config_file_in) | ||
| 62 | config_file = config_file_in; | ||
| 63 | fh = fopen(config_file, "r"); | ||
| 64 | |||
| 65 | if (!fh) { | ||
| 66 | fprintf(stderr, "Couldn't open config file %s, exiting.\n", config_file); | ||
| 67 | exit(1); | ||
| 68 | } | ||
| 69 | |||
| 70 | //Reinitialise string config array | ||
| 71 | memset(g_string_conf, 0, sizeof(g_string_conf)); | ||
| 72 | g_max_y = 1024; | ||
| 73 | |||
| 74 | while (!feof(fh)) { | ||
| 75 | char *line = fgets(line_in, LINEBUFFER, fh); | ||
| 76 | |||
| 77 | if (!line) | ||
| 78 | break; | ||
| 79 | |||
| 80 | //Skip leading spaces | ||
| 81 | while (isspace(*line)) | ||
| 82 | ++line; | ||
| 83 | if (*line == 0 || *line == '#' || *line == '\n') | ||
| 84 | continue; | ||
| 85 | |||
| 86 | if (!strncasecmp(line, "Strings", 7) && isspace(line[7])) { | ||
| 87 | line += 7; | ||
| 88 | while (isspace(*line)) | ||
| 89 | ++line; | ||
| 90 | g_string_count = atol(line); | ||
| 91 | if (!g_string_count || g_string_count > MAX_LINECOUNT) { | ||
| 92 | fprintf(stderr, "Incorrect number of strings: %s\n", line); | ||
| 93 | exit(1); | ||
| 94 | } | ||
| 95 | printf("GLOBAL: Configuring expected lines %d\n", g_string_count); | ||
| 96 | } else if (!strncasecmp(line, "String", 6) && isspace(line[6])) { | ||
| 97 | line += 6; | ||
| 98 | while (isspace(*line)) | ||
| 99 | ++line; | ||
| 100 | current_string = atol(line); | ||
| 101 | printf("Switching to string: %d\n", current_string); | ||
| 102 | if (current_string < 0 || current_string > g_string_count) { | ||
| 103 | fprintf(stderr, "Incorrect string selected: %s\n", line); | ||
| 104 | exit(1); | ||
| 105 | } | ||
| 106 | } else if (!strncasecmp(line, "Line", 4) && isspace(line[4])) { | ||
| 107 | LLine *l = &g_string_conf[current_string - 1].line; | ||
| 108 | |||
| 109 | if (current_string == -1) { | ||
| 110 | fprintf(stderr, "No string selected yet.\n"); | ||
| 111 | exit(1); | ||
| 112 | } | ||
| 113 | line += 4; | ||
| 114 | while (isspace(*line)) | ||
| 115 | ++line; | ||
| 116 | if (sscanf(line, "%d %d %d %d", &l->x0, &l->y0, &l->x1, &l->y1) != 4) { | ||
| 117 | fprintf(stderr, "Incorrect Line statement for string\n"); | ||
| 118 | exit(1); | ||
| 119 | } | ||
| 120 | if (l->y0 > l->y1) { | ||
| 121 | l->y0 ^= l->y1; | ||
| 122 | l->y1 ^= l->y0; | ||
| 123 | l->y0 ^= l->y1; | ||
| 124 | l->x0 ^= l->x1; | ||
| 125 | l->x1 ^= l->x0; | ||
| 126 | l->x0 ^= l->x1; | ||
| 127 | |||
| 128 | } | ||
| 129 | if (l->y0 > g_min_y) | ||
| 130 | g_min_y = l->y0; | ||
| 131 | if (l->y1 < g_max_y) | ||
| 132 | g_max_y = l->y1; | ||
| 133 | } else if (!strncasecmp(line, "Mode", 4) && isspace(line[4])) { | ||
| 134 | if (current_string == -1) { | ||
| 135 | fprintf(stderr, "No string selected yet.\n"); | ||
| 136 | exit(1); | ||
| 137 | } | ||
| 138 | line += 4; | ||
| 139 | while (isspace(*line)) | ||
| 140 | ++line; | ||
| 141 | if (!strncasecmp(line, "midi_one_octave", 15) && (!line[15] || isspace(line[15]))) { | ||
| 142 | g_string_conf[current_string - 1].mode = midi_one_octave; | ||
| 143 | printf("String %d is midi_one_octave\n", current_string); | ||
| 144 | } else if (!strncasecmp(line, "midi_two_octaves", 16) && (!line[16] || isspace(line[16]))) { | ||
| 145 | g_string_conf[current_string - 1].mode = midi_two_octaves; | ||
| 146 | printf("String %d is midi_two_octaves\n", current_string); | ||
| 147 | } else if (!strncasecmp(line, "midi_three_octaves", 18) && (!line[18] || isspace(line[18]))) { | ||
| 148 | g_string_conf[current_string - 1].mode = midi_three_octaves; | ||
| 149 | printf("String %d is midi_three_octaves\n", current_string); | ||
| 150 | } else if (!strncasecmp(line, "midi_control", 12) && (!line[12] || isspace(line[12]))) { | ||
| 151 | g_string_conf[current_string - 1].mode = midi_control; | ||
| 152 | printf("String %d is midi_control\n", current_string); | ||
| 153 | } else if (!strncasecmp(line, "midi_control_inverse", 20) && (!line[20] || isspace(line[20]))) { | ||
| 154 | g_string_conf[current_string - 1].mode = midi_control_inv; | ||
| 155 | printf("String %d is midi_control_inverse\n", current_string); | ||
| 156 | } else { | ||
| 157 | fprintf(stderr, "Illegal Mode for string: %s\n", line); | ||
| 158 | exit(1); | ||
| 159 | } | ||
| 160 | } else if (!strncasecmp(line, "Channel", 7) && isspace(line[7])) { | ||
| 161 | if (current_string == -1) { | ||
| 162 | fprintf(stderr, "No string selected yet.\n"); | ||
| 163 | exit(1); | ||
| 164 | } | ||
| 165 | line += 7; | ||
| 166 | while (isspace(*line)) | ||
| 167 | ++line; | ||
| 168 | g_string_conf[current_string - 1].channel = atol(line); | ||
| 169 | if (g_string_conf[current_string - 1].channel > 16) { | ||
| 170 | fprintf(stderr, "Incorrect channel specified: %s.\n", line); | ||
| 171 | exit(1); | ||
| 172 | } | ||
| 173 | printf("String %d is on channel %d\n", current_string, g_string_conf[current_string - 1].channel); | ||
| 174 | } else if (!strncasecmp(line, "Note", 4) && isspace(line[4])) { | ||
| 175 | if (current_string == -1) { | ||
| 176 | fprintf(stderr, "No string selected yet.\n"); | ||
| 177 | exit(1); | ||
| 178 | } | ||
| 179 | line += 4; | ||
| 180 | while (isspace(*line)) | ||
| 181 | ++line; | ||
| 182 | g_string_conf[current_string - 1].note = config_midi_note_from_string(line); | ||
| 183 | if (g_string_conf[current_string - 1].note == 0xff) { | ||
| 184 | fprintf(stderr, "Unknown midi note specified: %s.\n", line); | ||
| 185 | exit(1); | ||
| 186 | } | ||
| 187 | printf("String %d is midi note %d\n", current_string, g_string_conf[current_string - 1].note); | ||
| 188 | } else if (!strncasecmp(line, "AfterTouch", 10) && isspace(line[10])) { | ||
| 189 | if (current_string == -1) { | ||
| 190 | fprintf(stderr, "No string selected yet.\n"); | ||
| 191 | exit(1); | ||
| 192 | } | ||
| 193 | line += 10; | ||
| 194 | while (isspace(*line)) | ||
| 195 | ++line; | ||
| 196 | if (!strncasecmp(line, "none", 4) && (!line[4] || isspace(line[4]))) { | ||
| 197 | g_string_conf[current_string - 1].modifier = none; | ||
| 198 | printf("String %d does not act aftertouch\n", current_string); | ||
| 199 | } else if (!strncasecmp(line, "pitch_bend_up", 13) && (!line[13] || isspace(line[13]))) { | ||
| 200 | g_string_conf[current_string - 1].modifier = pitch_bend_up; | ||
| 201 | printf("String %d acts aftertouch as pitch_bend_up\n", current_string); | ||
| 202 | } else if (!strncasecmp(line, "pitch_bend_down", 15) && (!line[15] || isspace(line[15]))) { | ||
| 203 | g_string_conf[current_string - 1].modifier = pitch_bend_down; | ||
| 204 | printf("String %d acts aftertouch as pitch_bend_down\n", current_string); | ||
| 205 | } else if (!strncasecmp(line, "midi_control", 12) && (!line[12] || isspace(line[12]))) { | ||
| 206 | g_string_conf[current_string - 1].modifier = midi_controller; | ||
| 207 | printf("String %d acts aftertouch as midi_controller\n", current_string); | ||
| 208 | } else if (!strncasecmp(line, "midi_control_inverse", 20) && (!line[20] || isspace(line[20]))) { | ||
| 209 | g_string_conf[current_string - 1].modifier = midi_controller_inv; | ||
| 210 | printf("String %d acts aftertouch as midi_controller_inverse\n", current_string); | ||
| 211 | } else { | ||
| 212 | fprintf(stderr, "Illegal Modifier for string: %s\n", line); | ||
| 213 | exit(1); | ||
| 214 | } | ||
| 215 | } else if (!strncasecmp(line, "Controller", 10) && isspace(line[10])) { | ||
| 216 | if (current_string == -1) { | ||
| 217 | fprintf(stderr, "No string selected yet.\n"); | ||
| 218 | exit(1); | ||
| 219 | } | ||
| 220 | line += 10; | ||
| 221 | while (isspace(*line)) | ||
| 222 | ++line; | ||
| 223 | g_string_conf[current_string - 1].controller = atol(line); | ||
| 224 | printf("String %d is on midi_controller line %d\n", current_string, g_string_conf[current_string - 1].controller); | ||
| 225 | } else if (!strncasecmp(line, "TimeToSilence", 13) && isspace(line[13])) { | ||
| 226 | if (current_string == -1) { | ||
| 227 | fprintf(stderr, "No string selected yet.\n"); | ||
| 228 | exit(1); | ||
| 229 | } | ||
| 230 | line += 13; | ||
| 231 | while (isspace(*line)) | ||
| 232 | ++line; | ||
| 233 | g_string_conf[current_string - 1].timetosilence = atol(line); | ||
| 234 | printf("String %d has a timetosilence of %d\n", current_string, g_string_conf[current_string - 1].timetosilence); | ||
| 235 | } else if (!strncasecmp(line, "midi_two_octave_split", 21) && isspace(line[21])) { | ||
| 236 | line += 21; | ||
| 237 | while (isspace(*line)) | ||
| 238 | ++line; | ||
| 239 | g_midi_two_octave_split = atol(line); | ||
| 240 | printf("Splitting TWO octaves at %d%%\n", g_midi_two_octave_split); | ||
| 241 | if (g_midi_two_octave_split < 0 || g_midi_two_octave_split > 100) { | ||
| 242 | fprintf(stderr, "Invalid percentage in line: %s\n", line); | ||
| 243 | exit(1); | ||
| 244 | } | ||
| 245 | g_midi_two_octave_split = (256 * g_midi_two_octave_split) / 100; | ||
| 246 | } else if (!strncasecmp(line, "midi_three_octave_split_1", 25) && isspace(line[25])) { | ||
| 247 | line += 25; | ||
| 248 | while (isspace(*line)) | ||
| 249 | ++line; | ||
| 250 | g_midi_three_octave_split_1 = atol(line); | ||
| 251 | printf("Splitting THREE octaves top above %d%%\n", g_midi_three_octave_split_1); | ||
| 252 | if (g_midi_three_octave_split_1 < 0 || g_midi_three_octave_split_1 > 100) { | ||
| 253 | fprintf(stderr, "Invalid percentage in line: %s\n", line); | ||
| 254 | exit(1); | ||
| 255 | } | ||
| 256 | g_midi_three_octave_split_1 = (256 * g_midi_three_octave_split_1) / 100; | ||
| 257 | } else if (!strncasecmp(line, "midi_three_octave_split_2", 25) && isspace(line[25])) { | ||
| 258 | line += 25; | ||
| 259 | while (isspace(*line)) | ||
| 260 | ++line; | ||
| 261 | g_midi_three_octave_split_2 = atol(line); | ||
| 262 | printf("Splitting THREE octaves bottom below %d%%\n", g_midi_three_octave_split_2); | ||
| 263 | if (g_midi_three_octave_split_2 < 0 || g_midi_three_octave_split_2 > 100) { | ||
| 264 | fprintf(stderr, "Invalid percentage in line: %s\n", line); | ||
| 265 | exit(1); | ||
| 266 | } | ||
| 267 | g_midi_three_octave_split_2 = (256 * g_midi_three_octave_split_2) / 100; | ||
| 268 | } else if (!strncasecmp(line, "midi_main_control", 17) && isspace(line[17])) { | ||
| 269 | line += 17; | ||
| 270 | while (isspace(*line)) | ||
| 271 | ++line; | ||
| 272 | g_midi_main_control = atol(line); | ||
| 273 | printf("All Strings modify controller %d\n", g_midi_main_control); | ||
| 274 | if (g_midi_main_control > 127) { | ||
| 275 | fprintf(stderr, "Invalid controller number %d in line: %s\n", g_midi_main_control, line); | ||
| 276 | exit(1); | ||
| 277 | } | ||
| 278 | } else if (!strncasecmp(line, "midi_main_channel", 17) && isspace(line[17])) { | ||
| 279 | line += 17; | ||
| 280 | while (isspace(*line)) | ||
| 281 | ++line; | ||
| 282 | g_midi_main_channel = atol(line); | ||
| 283 | printf("All Strings modify controller %d on channel %d\n", g_midi_main_control, g_midi_main_channel); | ||
| 284 | if (g_midi_main_channel < 1 || g_midi_main_channel > 16) { | ||
| 285 | fprintf(stderr, "Invalid channel number %d in line: %s\n", g_midi_main_channel, line); | ||
| 286 | exit(1); | ||
| 287 | } | ||
| 288 | } else { | ||
| 289 | fprintf(stderr, "Unhandled config line: %s\n", line); | ||
| 290 | exit(1); | ||
| 291 | } | ||
| 292 | |||
| 293 | // split also true for two octaves | ||
| 294 | if (g_midi_three_octave_split_2 < g_midi_three_octave_split_1) { | ||
| 295 | g_midi_three_octave_split_inverse = g_midi_three_octave_split_1; | ||
| 296 | g_midi_three_octave_split_1 = g_midi_three_octave_split_2; | ||
| 297 | g_midi_three_octave_split_2 = g_midi_three_octave_split_inverse; | ||
| 298 | g_midi_three_octave_split_inverse = 1; | ||
| 299 | } else | ||
| 300 | g_midi_three_octave_split_inverse = 0; | ||
| 301 | } | ||
| 302 | fclose(fh); | ||
| 303 | } | ||
| 304 | |||
| 305 | void | ||
| 306 | config_write() | ||
| 307 | { | ||
| 308 | |||
| 309 | } | ||
diff --git a/config.h b/config.h new file mode 100644 index 0000000..2a9c09d --- /dev/null +++ b/config.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | #ifndef __CONFIG_H__ | ||
| 2 | #define __CONFIG_H__ | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | #define MAX_LINECOUNT 32 | ||
| 7 | |||
| 8 | extern int g_min_y, g_max_y; | ||
| 9 | |||
| 10 | extern int g_midi_two_octave_split; | ||
| 11 | extern int g_midi_three_octave_split_1; | ||
| 12 | extern int g_midi_three_octave_split_2; | ||
| 13 | extern int g_midi_three_octave_split_inverse; | ||
| 14 | extern int g_midi_main_control; | ||
| 15 | extern int g_midi_main_channel; | ||
| 16 | extern int g_settled_dist; | ||
| 17 | extern int g_timetosilence; | ||
| 18 | |||
| 19 | typedef enum { | ||
| 20 | midi_one_octave = 0, | ||
| 21 | midi_two_octaves = 1, | ||
| 22 | midi_three_octaves = 2, | ||
| 23 | midi_control = 3, | ||
| 24 | midi_control_inv = 4 | ||
| 25 | } StringMode; | ||
| 26 | |||
| 27 | typedef enum { | ||
| 28 | none = 0, | ||
| 29 | pitch_bend_up = 1, | ||
| 30 | pitch_bend_down = 2, | ||
| 31 | midi_controller = 3, | ||
| 32 | midi_controller_inv = 4 | ||
| 33 | } StringModifier; | ||
| 34 | |||
| 35 | typedef enum { | ||
| 36 | silent = 0, | ||
| 37 | in_attack = 1, | ||
| 38 | playing = 2 | ||
| 39 | } StringPlaying; | ||
| 40 | |||
| 41 | typedef struct { | ||
| 42 | int x0; int y0; | ||
| 43 | int x1; int y1; | ||
| 44 | } LLine; | ||
| 45 | |||
| 46 | typedef struct { | ||
| 47 | int x; int y; | ||
| 48 | } LPoint; | ||
| 49 | |||
| 50 | typedef struct { | ||
| 51 | StringMode mode; | ||
| 52 | LLine line; | ||
| 53 | uint8_t channel; | ||
| 54 | uint8_t note; | ||
| 55 | uint8_t controller; | ||
| 56 | int timetosilence; | ||
| 57 | StringModifier modifier; | ||
| 58 | int pitch_factor; | ||
| 59 | |||
| 60 | /* runtime values */ | ||
| 61 | uint32_t first_time_seen; | ||
| 62 | uint32_t last_time_seen; | ||
| 63 | StringPlaying playing; | ||
| 64 | int octave; | ||
| 65 | int start_off; | ||
| 66 | int last_off; | ||
| 67 | int current_pitch; | ||
| 68 | } StringConfig; | ||
| 69 | |||
| 70 | extern StringConfig g_string_conf[MAX_LINECOUNT]; | ||
| 71 | extern int g_string_count; | ||
| 72 | void config_parse( char *config_file ); | ||
| 73 | char *config_midi_note_to_string(int string); | ||
| 74 | |||
| 75 | #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 @@ | |||
| 1 | # This line tells LaserHarfe, how many Strings | ||
| 2 | # it will expect on screen. Be sure to add one | ||
| 3 | # config section for each String below | ||
| 4 | # | ||
| 5 | Strings 8 | ||
| 6 | |||
| 7 | # Enable next line to invert string order | ||
| 8 | # | ||
| 9 | # StringsDescending | ||
| 10 | |||
| 11 | # Use these lines to configure where your | ||
| 12 | # midi_two_octaves and midi_three_octaves | ||
| 13 | # Strings should be split on screen | ||
| 14 | # | ||
| 15 | # Value is in percent | ||
| 16 | # | ||
| 17 | |||
| 18 | midi_two_octave_split 50 | ||
| 19 | |||
| 20 | # If split_1 > split_2 then | ||
| 21 | # higher notes are on the bottom | ||
| 22 | # | ||
| 23 | |||
| 24 | midi_three_octave_split_1 33 | ||
| 25 | midi_three_octave_split_2 66 | ||
| 26 | |||
| 27 | # Demo String section | ||
| 28 | # | ||
| 29 | # String x | ||
| 30 | # Mode midi_one_octave | ||
| 31 | # Mode midi_two_octaves | ||
| 32 | # Mode midi_three_octaves | ||
| 33 | # Mode midi_control | ||
| 34 | # Mode midi_control_inverse | ||
| 35 | # Channel 0..15 | ||
| 36 | # Controller 0..15 (use 0 unless really sure) | ||
| 37 | # AfterTouch pitch_bend_up | ||
| 38 | # AfterTouch pitch_bend_down | ||
| 39 | # AfterTouch midi_control | ||
| 40 | # TimeToSilence 15 | ||
| 41 | |||
| 42 | String 1 | ||
| 43 | Line 860 450 768 684 | ||
| 44 | Mode midi_three_octaves | ||
| 45 | Note C4 | ||
| 46 | Channel 0 | ||
| 47 | Controller 0 | ||
| 48 | AfterTouch pitch_bend_up | ||
| 49 | TimeToSilence 15 | ||
| 50 | |||
| 51 | String 2 | ||
| 52 | Line 738 448 672 683 | ||
| 53 | Mode midi_three_octaves | ||
| 54 | Note D4 | ||
| 55 | Channel 1 | ||
| 56 | Controller 0 | ||
| 57 | AfterTouch pitch_bend_up | ||
| 58 | TimeToSilence 15 | ||
| 59 | |||
| 60 | String 3 | ||
| 61 | Line 640 450 602 679 | ||
| 62 | Mode midi_three_octaves | ||
| 63 | Note E4 | ||
| 64 | Channel 2 | ||
| 65 | Controller 0 | ||
| 66 | AfterTouch pitch_bend_up | ||
| 67 | TimeToSilence 15 | ||
| 68 | |||
| 69 | String 4 | ||
| 70 | Line 535 442 519 681 | ||
| 71 | Mode midi_three_octaves | ||
| 72 | Note F4 | ||
| 73 | Channel 3 | ||
| 74 | Controller 0 | ||
| 75 | AfterTouch pitch_bend_up | ||
| 76 | TimeToSilence 15 | ||
| 77 | |||
| 78 | String 5 | ||
| 79 | Line 425 420 437 676 | ||
| 80 | Mode midi_three_octaves | ||
| 81 | Note G4 | ||
| 82 | Channel 4 | ||
| 83 | Controller 0 | ||
| 84 | AfterTouch pitch_bend_up | ||
| 85 | TimeToSilence 15 | ||
| 86 | |||
| 87 | String 6 | ||
| 88 | Line 319 428 355 673 | ||
| 89 | Mode midi_three_octaves | ||
| 90 | Note A4 | ||
| 91 | Channel 5 | ||
| 92 | Controller 0 | ||
| 93 | AfterTouch pitch_bend_up | ||
| 94 | TimeToSilence 15 | ||
| 95 | |||
| 96 | String 7 | ||
| 97 | Line 208 432 272 667 | ||
| 98 | Mode midi_three_octaves | ||
| 99 | Note B4 | ||
| 100 | Channel 6 | ||
| 101 | Controller 0 | ||
| 102 | AfterTouch pitch_bend_up | ||
| 103 | TimeToSilence 15 | ||
| 104 | |||
| 105 | String 8 | ||
| 106 | Line 92 422 187 668 | ||
| 107 | Mode midi_three_octaves | ||
| 108 | Note C5 | ||
| 109 | Channel 7 | ||
| 110 | Controller 0 | ||
| 111 | AfterTouch pitch_bend_up | ||
| 112 | 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 @@ | |||
| 1 | #include "SDL2/SDL.h" | ||
| 2 | #include "SDL2/SDL2_gfxPrimitives.h" | ||
| 3 | #include <SDL2/SDL_ttf.h> | ||
| 4 | |||
| 5 | #include "GenBkBasB.h" | ||
| 6 | #include "display.h" | ||
| 7 | |||
| 8 | #define display_measure_text(text,w,h) TTF_SizeText(font,(text),(w),(h)) | ||
| 9 | |||
| 10 | static SDL_Window *screen; | ||
| 11 | static SDL_Renderer *renderer; | ||
| 12 | static int g_width, g_height; | ||
| 13 | static TTF_Font *font = NULL; | ||
| 14 | |||
| 15 | void | ||
| 16 | display_init(int width, int height) | ||
| 17 | { | ||
| 18 | SDL_RWops *font_file; | ||
| 19 | |||
| 20 | g_width = width; | ||
| 21 | g_height = height; | ||
| 22 | |||
| 23 | if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { | ||
| 24 | fprintf(stderr, "Can't initialize SDL.\n"); | ||
| 25 | exit(1); | ||
| 26 | } | ||
| 27 | screen = SDL_CreateWindow("Laserharfe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL); | ||
| 28 | if (!screen) { | ||
| 29 | fprintf(stderr, "Can't set video mode.\n"); | ||
| 30 | exit(1); | ||
| 31 | } | ||
| 32 | renderer = SDL_CreateRenderer(screen, -1, 0); | ||
| 33 | |||
| 34 | SDL_RenderClear(renderer); | ||
| 35 | |||
| 36 | TTF_Init(); | ||
| 37 | font_file = SDL_RWFromConstMem(GenBkBasB_ttf, GenBkBasB_ttf_len); | ||
| 38 | font = TTF_OpenFontRW(font_file, 1, 28); | ||
| 39 | } | ||
| 40 | |||
| 41 | void | ||
| 42 | display_clear() | ||
| 43 | { | ||
| 44 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); | ||
| 45 | SDL_RenderClear(renderer); | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | display_circle(int x, int y, int w) | ||
| 50 | { | ||
| 51 | if (x >= 0 && x < g_width && y >= 0 && y < g_height) | ||
| 52 | display_circle_color(x, y, w, 0xffffffff); | ||
| 53 | } | ||
| 54 | |||
| 55 | void | ||
| 56 | display_circle_color(int x, int y, int w, int color) | ||
| 57 | { | ||
| 58 | filledCircleColor(renderer, x, y, w, color); | ||
| 59 | } | ||
| 60 | |||
| 61 | void | ||
| 62 | display_line(int x0, int y0, int x1, int y1) | ||
| 63 | { | ||
| 64 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 65 | SDL_RenderDrawLine(renderer, x0, y0, x1, y1); | ||
| 66 | } | ||
| 67 | |||
| 68 | void | ||
| 69 | display_line_color(int x0, int y0, int x1, int y1, int color) | ||
| 70 | { | ||
| 71 | SDL_SetRenderDrawColor(renderer, (color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, 255); | ||
| 72 | SDL_RenderDrawLine(renderer, x0, y0, x1, y1); | ||
| 73 | } | ||
| 74 | |||
| 75 | void | ||
| 76 | display_redraw() | ||
| 77 | { | ||
| 78 | SDL_RenderPresent(renderer); | ||
| 79 | } | ||
| 80 | |||
| 81 | void | ||
| 82 | display_text(char *text, int x, int y, int color) | ||
| 83 | { | ||
| 84 | SDL_Color s_color = { 255 & (color>>24), 255 & (color>>16), 255 & (color>>8) }; | ||
| 85 | SDL_Surface *sText = TTF_RenderText_Solid(font, text, s_color); | ||
| 86 | SDL_Rect rect = {x, y, sText->w, sText->h}; | ||
| 87 | SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, sText); | ||
| 88 | |||
| 89 | SDL_RenderCopy(renderer, texture, NULL, &rect); | ||
| 90 | SDL_DestroyTexture(texture); | ||
| 91 | SDL_FreeSurface(sText); | ||
| 92 | } | ||
| 93 | |||
| 94 | void | ||
| 95 | display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int color) | ||
| 96 | { | ||
| 97 | int w, h, min_y, item_height; | ||
| 98 | |||
| 99 | display_measure_text("#", &w, &h); | ||
| 100 | item_height = 3 * h / 2; | ||
| 101 | min_y = (g_height - item_height * max_pos) / 2 + pos * item_height; | ||
| 102 | |||
| 103 | //boxColor(screen, min_x, min_y, max_x, min_y + item_height, color); | ||
| 104 | //rectangleColor(screen, min_x, min_y, max_x, min_y + item_height, 0xffffffff); | ||
| 105 | display_measure_text(text, &w, &h); | ||
| 106 | display_text(text, min_x + (max_x - min_x - w) / 2, min_y + (item_height - h) / 2, 0xffffffff); | ||
| 107 | } | ||
| 108 | |||
| 109 | int | ||
| 110 | display_test_menu_click(int y, int max_pos) | ||
| 111 | { | ||
| 112 | int w, h, min_y, item_height; | ||
| 113 | |||
| 114 | /* | ||
| 115 | display_measure_text( "#", &w, &h ); | ||
| 116 | item_height = 3 * h / 2; | ||
| 117 | min_y = ( g_height - item_height * max_pos ) / 2; | ||
| 118 | if( y < min_y ) return -1; | ||
| 119 | if( y > min_y + item_height * max_pos ) return -1; | ||
| 120 | return ( y - min_y ) / item_height; | ||
| 121 | */ | ||
| 122 | return 0; | ||
| 123 | } | ||
diff --git a/display.h b/display.h new file mode 100644 index 0000000..adc08b7 --- /dev/null +++ b/display.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | void display_init(int width, int height); | ||
| 4 | void display_redraw(); | ||
| 5 | |||
| 6 | void display_clear(); | ||
| 7 | void display_line(int x0, int y0, int x1, int y1); | ||
| 8 | void display_line_color(int x0, int y0, int x1, int y1, int color); | ||
| 9 | void display_circle(int x, int y, int w); | ||
| 10 | void display_circle_color(int x, int y, int w, int color); | ||
| 11 | void display_rectangle(int x, int y, int w, int h); | ||
| 12 | |||
| 13 | void display_text(char *text, int x, int y, int color); | ||
| 14 | void display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int color); | ||
| 15 | int display_test_menu_click(int y, int max_pos); | ||
| 16 | |||
| 17 | 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 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | |||
| 4 | #include "config.h" | ||
| 5 | #include "engine.h" | ||
| 6 | #include "display.h" | ||
| 7 | #include "main.h" | ||
| 8 | #include "midi.h" | ||
| 9 | |||
| 10 | static int g_selected_string = -1; | ||
| 11 | |||
| 12 | #ifndef NO_DISPLAY | ||
| 13 | static LPoint g_render_points[1024]; | ||
| 14 | static int g_render_point_count; | ||
| 15 | static const int g_harfe_width = 1024; | ||
| 16 | static const int g_harfe_height = 768; | ||
| 17 | static int g_factor = 1<<16; | ||
| 18 | |||
| 19 | static inline int scale(int coord) { | ||
| 20 | return (int)(((((int64_t)coord) << 32) / g_factor ) >> 16); | ||
| 21 | } | ||
| 22 | |||
| 23 | void | ||
| 24 | engine_redraw() | ||
| 25 | { | ||
| 26 | int i; | ||
| 27 | |||
| 28 | display_redraw(); | ||
| 29 | display_clear(); | ||
| 30 | display_text(g_harfe_connected ? "online" : "offline", 4, 4, 0xffffffff); | ||
| 31 | |||
| 32 | for (i = 0; i < g_string_count; ++i) { | ||
| 33 | LLine *l = &g_string_conf[i].line; | ||
| 34 | uint32_t color = i == g_selected_string ? 0xff00ffff : 0x00ffffffff; | ||
| 35 | |||
| 36 | display_line_color(scale(l->x0), scale(l->y0), scale(l->x1), scale(l->y1), color); | ||
| 37 | display_circle(scale(l->x0), scale(l->y0), 4); | ||
| 38 | display_circle(scale(l->x1), scale(l->y1), 4); | ||
| 39 | |||
| 40 | if (g_string_conf[i].playing) | ||
| 41 | 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 ); | ||
| 42 | else | ||
| 43 | display_text(config_midi_note_to_string(g_string_conf[i].note), scale(l->x1) - 20, g_height - 40, color ); | ||
| 44 | } | ||
| 45 | g_selected_string = -1; | ||
| 46 | |||
| 47 | for (i = 0; i < g_render_point_count; ++i) | ||
| 48 | display_circle_color(scale(g_render_points[i].x), scale(g_render_points[i].y), 4, 0xff0000ff); | ||
| 49 | g_render_point_count = 0; | ||
| 50 | |||
| 51 | display_line_color(0, scale(g_min_y), g_width, scale(g_min_y), 0xff00ffff); | ||
| 52 | display_line_color(0, scale(g_max_y), g_width, scale(g_max_y), 0xff00ffff); | ||
| 53 | |||
| 54 | if (g_min_y != g_max_y) { | ||
| 55 | int height = scale(g_max_y - g_min_y); | ||
| 56 | |||
| 57 | 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); | ||
| 58 | |||
| 59 | 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); | ||
| 60 | 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); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #endif | ||
| 65 | |||
| 66 | void | ||
| 67 | engine_init() { | ||
| 68 | #ifndef NO_DISPLAY | ||
| 69 | g_factor = (g_harfe_width << 16) / g_width; | ||
| 70 | if ((g_harfe_height << 16) / g_height < g_factor) | ||
| 71 | g_factor = (g_harfe_height << 16) / g_height; | ||
| 72 | #endif | ||
| 73 | } | ||
| 74 | |||
| 75 | static int | ||
| 76 | dist_pp(int x0, int y0, int x1, int y1) | ||
| 77 | { | ||
| 78 | return (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1); | ||
| 79 | } | ||
| 80 | |||
| 81 | // dist is a fixed point with precission of 8 bits | ||
| 82 | // offs is where on the line segment xy0-xy1 the point's normale hits, | ||
| 83 | // range 0..65536 (but can extend, if normale hits line outside line segment) | ||
| 84 | static int | ||
| 85 | dist_lp(int x0, int y0, int x1, int y1, int xp, int yp, int *offs) | ||
| 86 | { | ||
| 87 | int64_t r = (y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0); | ||
| 88 | int64_t q1 = (xp - x0) * (y1 - y0) - (yp - y0) * (x1 - x0); | ||
| 89 | int64_t q2 = (x1 - x0) * (xp - x0) + (y1 - y0) * (yp - y0); | ||
| 90 | |||
| 91 | *offs = (int)((q2 << 16) / r); | ||
| 92 | return (int)( q1 * q1 * ((y0 - y1) * (y0 - y1) + (x1 - x0) * (x1 - x0)) * 256 / (r * r)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static int | ||
| 96 | dist_pl(LPoint * p, LLine * l, int *offs) | ||
| 97 | { | ||
| 98 | return dist_lp(l->x0, l->y0, l->x1, l->y1, p->x, p->y, offs); | ||
| 99 | } | ||
| 100 | |||
| 101 | void | ||
| 102 | engine_handle_point(LPoint * p, uint32_t monotime) | ||
| 103 | { | ||
| 104 | StringConfig *s;; | ||
| 105 | int dist_max = 1024 * 1024 * 8; | ||
| 106 | int offs, saite = -1, i, oct = 0; | ||
| 107 | int y_viewfield, pitch_factor = 12; | ||
| 108 | int dv, dt, speed, new_pitch; | ||
| 109 | |||
| 110 | // XXX should not be inverted here | ||
| 111 | p->x = 1024 - p->x; | ||
| 112 | |||
| 113 | #ifndef NO_DISPLAY | ||
| 114 | /* Pass to "render thread" */ | ||
| 115 | g_render_points[g_render_point_count] = *p; | ||
| 116 | ++g_render_point_count; | ||
| 117 | #endif | ||
| 118 | |||
| 119 | /* See which line is closest */ | ||
| 120 | for (i = 0; i < g_string_count; ++i) { | ||
| 121 | int dist = dist_pl(p, &g_string_conf[i].line, &offs); | ||
| 122 | if ((dist < 256 * 10 * 10 ) && (dist < dist_max)) { | ||
| 123 | dist_max = dist; | ||
| 124 | saite = i; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | if (saite == -1) | ||
| 129 | return; | ||
| 130 | |||
| 131 | s = g_string_conf + saite; | ||
| 132 | g_selected_string = saite; | ||
| 133 | |||
| 134 | y_viewfield = 256 * (g_max_y - p->y) / (g_max_y - g_min_y); | ||
| 135 | if (y_viewfield < 0) | ||
| 136 | y_viewfield = 0; | ||
| 137 | if (y_viewfield >= 256) | ||
| 138 | y_viewfield = 255; | ||
| 139 | |||
| 140 | // Determine octave, if configured | ||
| 141 | switch (s->mode) { | ||
| 142 | case midi_controller: | ||
| 143 | case midi_controller_inv: | ||
| 144 | return; // not implemented | ||
| 145 | case midi_one_octave: | ||
| 146 | break; | ||
| 147 | case midi_two_octaves: | ||
| 148 | if (y_viewfield < g_midi_two_octave_split) | ||
| 149 | oct = -1; | ||
| 150 | break; | ||
| 151 | case midi_three_octaves: | ||
| 152 | if (y_viewfield < g_midi_three_octave_split_1) | ||
| 153 | oct = -1; | ||
| 154 | if (y_viewfield > g_midi_three_octave_split_2) | ||
| 155 | oct = 1; | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | // handle inverted octave configuration | ||
| 159 | if (g_midi_three_octave_split_inverse) | ||
| 160 | oct = -oct; | ||
| 161 | |||
| 162 | switch (s->playing) { | ||
| 163 | case silent: | ||
| 164 | midi_playnote(s->channel, s->note, oct); | ||
| 165 | s->playing = in_attack; | ||
| 166 | s->octave = oct; | ||
| 167 | s->start_off = s->last_off = offs; | ||
| 168 | s->first_time_seen = monotime; | ||
| 169 | break; | ||
| 170 | case in_attack: | ||
| 171 | // test if difference is less than g_settled_dist percent of | ||
| 172 | // line segment length | ||
| 173 | if (100 * abs(s->last_off - offs) < g_settled_dist << 16) { | ||
| 174 | s->playing = playing; | ||
| 175 | s->current_pitch = 0; | ||
| 176 | |||
| 177 | // estimated energy of hand is dv/dt from first seen to settled | ||
| 178 | dv = abs(s->start_off - offs); | ||
| 179 | dt = monotime - s->first_time_seen; | ||
| 180 | if (!dt) ++dt; | ||
| 181 | speed = 1000 * dv / dt; // in offs_prec per second | ||
| 182 | } | ||
| 183 | s->last_off = offs; | ||
| 184 | break; | ||
| 185 | case playing: | ||
| 186 | if (s->pitch_factor) | ||
| 187 | pitch_factor = s->pitch_factor; | ||
| 188 | if (s->modifier == pitch_bend_up) | ||
| 189 | new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; | ||
| 190 | else if (s->modifier == pitch_bend_down) | ||
| 191 | new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; | ||
| 192 | else | ||
| 193 | break; | ||
| 194 | // avoid reporting same pitch bend over and over | ||
| 195 | if (s->current_pitch == new_pitch) | ||
| 196 | break; | ||
| 197 | midi_pitchbend(s->channel, new_pitch); | ||
| 198 | s->current_pitch = new_pitch; | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | s->last_time_seen = monotime; | ||
| 202 | } | ||
| 203 | |||
| 204 | void | ||
| 205 | engine_checksilence(uint32_t monotime) | ||
| 206 | { | ||
| 207 | int i; | ||
| 208 | |||
| 209 | for (i = 0; i < g_string_count; ++i) { | ||
| 210 | StringConfig *s = g_string_conf + i; | ||
| 211 | int tts = s->timetosilence ? s->timetosilence : g_timetosilence; | ||
| 212 | |||
| 213 | if (s->mode == midi_controller) | ||
| 214 | continue; | ||
| 215 | if (s->playing && (monotime - s->last_time_seen > tts)) { | ||
| 216 | midi_silencenote(s->channel, s->note, s->octave); | ||
| 217 | s->playing = 0; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
diff --git a/engine.h b/engine.h new file mode 100644 index 0000000..3b59e86 --- /dev/null +++ b/engine.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | void engine_init(); | ||
| 4 | void engine_redraw(); | ||
| 5 | void engine_handle_point(LPoint * p, uint32_t monotime); | ||
| 6 | 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 @@ | |||
| 1 | #include <stdarg.h> | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <signal.h> | ||
| 5 | #include <sys/time.h> | ||
| 6 | #include <pthread.h> | ||
| 7 | #include <errno.h> | ||
| 8 | #include <fcntl.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <termios.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | #include <dirent.h> | ||
| 13 | |||
| 14 | #include <SDL2/SDL.h> | ||
| 15 | #include "display.h" | ||
| 16 | #include "config.h" | ||
| 17 | #include "engine.h" | ||
| 18 | |||
| 19 | /*** | ||
| 20 | Global config and status values | ||
| 21 | ***/ | ||
| 22 | |||
| 23 | /* Window width and height */ | ||
| 24 | // const int g_width = 1024, g_height = 768; | ||
| 25 | const int g_width = 800, g_height = 600; | ||
| 26 | |||
| 27 | StringConfig | ||
| 28 | g_string_conf[MAX_LINECOUNT]; | ||
| 29 | int g_string_count; | ||
| 30 | int g_harfe_connected = 0; | ||
| 31 | int g_harfe_fd = -1; | ||
| 32 | |||
| 33 | static char * | ||
| 34 | find_harfe() | ||
| 35 | { | ||
| 36 | // Device name should be a cu.device | ||
| 37 | // followed by the letter HAR somewhere | ||
| 38 | // e.g. /dev /cu.usbmodemHAR1 | ||
| 39 | DIR * dev = opendir("/dev/"); | ||
| 40 | struct dirent *dp; | ||
| 41 | char *harfe = 0; | ||
| 42 | |||
| 43 | if (!dev) | ||
| 44 | return 0; | ||
| 45 | |||
| 46 | while ((dp = readdir(dev)) != NULL) { | ||
| 47 | size_t len = dp->d_namlen; | ||
| 48 | char *name = dp->d_name, *H, *A, *R; | ||
| 49 | int i; | ||
| 50 | |||
| 51 | if (len < 6 || name[0] != 'c' || name[1] != 'u' || name[2] != '.') | ||
| 52 | continue; | ||
| 53 | |||
| 54 | for (i = 0; i < len - 3; ++i) | ||
| 55 | if (name[i] == 'H' && name[i + 1] == 'A' && name[i + 2] == 'R') { | ||
| 56 | if ((harfe = calloc(1, 5 + len + 1))) { | ||
| 57 | sprintf(harfe, "/dev/"); | ||
| 58 | memcpy(harfe + 5, name, len); | ||
| 59 | } | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | closedir(dev); | ||
| 64 | return harfe; | ||
| 65 | } | ||
| 66 | |||
| 67 | int | ||
| 68 | set_interface_attribs(int fd, int speed, int parity) | ||
| 69 | { | ||
| 70 | struct termios tty; | ||
| 71 | |||
| 72 | memset(&tty, 0, sizeof tty); | ||
| 73 | if (tcgetattr(fd, &tty) != 0) | ||
| 74 | return -1; | ||
| 75 | cfsetospeed(&tty, speed); | ||
| 76 | cfsetispeed(&tty, speed); | ||
| 77 | |||
| 78 | tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; | ||
| 79 | tty.c_iflag &= ~IGNBRK; | ||
| 80 | tty.c_lflag = 0; | ||
| 81 | tty.c_oflag = 0; | ||
| 82 | tty.c_cc[VMIN] = 0; | ||
| 83 | tty.c_cc[VTIME] = 5; | ||
| 84 | |||
| 85 | tty.c_iflag &= ~(IXON | IXOFF | IXANY); | ||
| 86 | |||
| 87 | tty.c_cflag |= (CLOCAL | CREAD); | ||
| 88 | |||
| 89 | tty.c_cflag &= ~(PARENB | PARODD); | ||
| 90 | tty.c_cflag |= parity; | ||
| 91 | tty.c_cflag &= ~CSTOPB; | ||
| 92 | tty.c_cflag &= ~CRTSCTS; | ||
| 93 | |||
| 94 | if (tcsetattr(fd, TCSANOW, &tty) != 0) | ||
| 95 | return -1; | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | void | ||
| 100 | set_blocking(int fd, int should_block) | ||
| 101 | { | ||
| 102 | struct termios tty; | ||
| 103 | |||
| 104 | memset(&tty, 0, sizeof tty); | ||
| 105 | if (tcgetattr(fd, &tty) != 0) | ||
| 106 | return; | ||
| 107 | |||
| 108 | tty.c_cc[VMIN] = should_block ? 1 : 0; | ||
| 109 | tty.c_cc[VTIME] = 5; | ||
| 110 | |||
| 111 | if (tcsetattr(fd, TCSANOW, &tty) != 0) | ||
| 112 | return; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | uint32_t | ||
| 117 | now() | ||
| 118 | { | ||
| 119 | struct timeval now; | ||
| 120 | |||
| 121 | gettimeofday(&now, (struct timezone *)NULL); | ||
| 122 | return now.tv_sec * 1000 + now.tv_usec / 1000; /* in ms */ | ||
| 123 | } | ||
| 124 | |||
| 125 | static uint32_t g_last_avg; | ||
| 126 | static uint32_t g_starttime, g_last_mouse_event, g_lastredraw; | ||
| 127 | static int g_events; | ||
| 128 | static void | ||
| 129 | harfe_worker(void) | ||
| 130 | { | ||
| 131 | LPoint p[4]; | ||
| 132 | char text[256], *lineend; | ||
| 133 | int fd, i, text_fill = 0, consumed, running = 1; | ||
| 134 | uint32_t ptime; | ||
| 135 | |||
| 136 | char *portname = find_harfe(); | ||
| 137 | |||
| 138 | if (!portname) { | ||
| 139 | printf("Can't find harfe serial device.\n"); | ||
| 140 | return; | ||
| 141 | } | ||
| 142 | g_harfe_fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); | ||
| 143 | if (g_harfe_fd < 0) { | ||
| 144 | free(portname); | ||
| 145 | printf("Can't connect to harfe..."); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | set_interface_attribs(g_harfe_fd, B115200, 0); | ||
| 149 | set_blocking(g_harfe_fd, 0); | ||
| 150 | |||
| 151 | printf("Connection to harfe established at %s.\n", portname); | ||
| 152 | free(portname); | ||
| 153 | g_harfe_connected = 1; | ||
| 154 | |||
| 155 | while (running) { | ||
| 156 | while (text_fill < sizeof(text) && !memchr(text, '\n', text_fill)) { | ||
| 157 | ssize_t b = read(g_harfe_fd, text + text_fill, sizeof(text) - text_fill); | ||
| 158 | |||
| 159 | if ((b < 0) && (errno != EAGAIN)) { | ||
| 160 | printf("Connection to harfe lost..."); | ||
| 161 | running = 0; | ||
| 162 | close(g_harfe_fd); | ||
| 163 | g_harfe_fd = -1; | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | text_fill += b; | ||
| 167 | } | ||
| 168 | |||
| 169 | //Overlong line, drop it and try to resync | ||
| 170 | if (text_fill == sizeof(text)) { | ||
| 171 | text_fill = 0; | ||
| 172 | continue; | ||
| 173 | } | ||
| 174 | |||
| 175 | // find and remove newline and cf | ||
| 176 | if (!(lineend = memchr(text, '\n', text_fill))) | ||
| 177 | continue; | ||
| 178 | |||
| 179 | *lineend = 0; | ||
| 180 | if (text_fill && lineend[-1] == '\r') | ||
| 181 | lineend[-1] = 0; | ||
| 182 | |||
| 183 | 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); | ||
| 184 | |||
| 185 | ptime = now(); | ||
| 186 | for (i = 0; i < num_points / 2; ++i) { | ||
| 187 | // printf("%04d:%04d\n", p[i].x, p[i].y); | ||
| 188 | engine_handle_point(p + i, ptime); | ||
| 189 | } | ||
| 190 | if (num_points > 1 || *text == '-') | ||
| 191 | ++g_events; | ||
| 192 | |||
| 193 | consumed = lineend - text + 1; | ||
| 194 | memmove(text, lineend + 1, text_fill - consumed); | ||
| 195 | text_fill -= consumed; | ||
| 196 | } | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | static void * | ||
| 200 | worker(void *args) | ||
| 201 | { | ||
| 202 | while (1) { | ||
| 203 | harfe_worker(); | ||
| 204 | g_harfe_connected = 0; | ||
| 205 | printf("retrying in 5 seconds.\n"); | ||
| 206 | sleep(5); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | int | ||
| 211 | main(int argc, char **argv) | ||
| 212 | { | ||
| 213 | SDL_Event ev; | ||
| 214 | int i, counter = 0; | ||
| 215 | pthread_t thread_id; | ||
| 216 | uint32_t runtime; | ||
| 217 | static int last_click_x, last_click_y, last_mouse_event; | ||
| 218 | |||
| 219 | display_init(g_width, g_height); | ||
| 220 | engine_init(); | ||
| 221 | config_parse("config_midi"); | ||
| 222 | |||
| 223 | pthread_create(&thread_id, NULL, worker, NULL); | ||
| 224 | |||
| 225 | /* Spin and let call back do all the work */ | ||
| 226 | while (1) { | ||
| 227 | SDL_WaitEventTimeout(&ev, 10); | ||
| 228 | switch (ev.type) { | ||
| 229 | case SDL_QUIT: | ||
| 230 | exit(0); | ||
| 231 | case SDL_KEYDOWN: | ||
| 232 | /* | ||
| 233 | if( ev.key.keysym.sym >= SDLK_1 && ev.key.keysym.sym <= SDLK_9 ) | ||
| 234 | engine_select_string( ev.key.keysym.sym - SDLK_1 ); | ||
| 235 | if( ev.key.keysym.sym == SDLK_BACKSPACE || ev.key.keysym.sym == SDLK_DELETE ) | ||
| 236 | engine_delete_selected_string( ); | ||
| 237 | if( ev.key.keysym.sym == SDLK_d ) { | ||
| 238 | g_stringsdescending = 1 - g_stringsdescending; | ||
| 239 | printf( "String order (left to right) is now %sscending.\n", g_stringsdescending ? "de" : "a" ); | ||
| 240 | } | ||
| 241 | */ | ||
| 242 | break; | ||
| 243 | case SDL_MOUSEBUTTONDOWN: | ||
| 244 | /* | ||
| 245 | if( ( g_last_mouse_event / 1000 ) != ( engine_now( ) / 1000 ) || ev.button.x != last_click_x || ev.button.y != last_click_y ) | ||
| 246 | engine_process_mouse( ev.button.x, ev.button.y ); | ||
| 247 | last_click_x = ev.button.x; | ||
| 248 | last_click_y = ev.button.y; | ||
| 249 | last_mouse_event = engine_now( ); | ||
| 250 | */ | ||
| 251 | break; | ||
| 252 | } | ||
| 253 | |||
| 254 | engine_checksilence(now()); | ||
| 255 | runtime = now(); | ||
| 256 | if (runtime - g_lastredraw > 30) { | ||
| 257 | g_lastredraw = runtime; | ||
| 258 | engine_redraw(); | ||
| 259 | } | ||
| 260 | if (runtime / 1000 - g_last_avg > 10) { | ||
| 261 | printf("avg: %i\n", g_events / 10); | ||
| 262 | g_events = 0; | ||
| 263 | g_last_avg = runtime / 1000; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | } | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | #pragma once | ||
| 2 | #include <stdint.h> | ||
| 3 | |||
| 4 | uint32_t now(); // get monotonic time in ms | ||
| 5 | |||
| 6 | extern int g_width; | ||
| 7 | extern int g_height; | ||
| 8 | extern int g_harfe_connected; | ||
| 9 | |||
| 10 | extern int g_harfe_fd; | ||
| @@ -0,0 +1,39 @@ | |||
| 1 | #include <stdlib.h> | ||
| 2 | #include <unistd.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | |||
| 5 | #include "midi.h" | ||
| 6 | #include "main.h" | ||
| 7 | |||
| 8 | int midi_init() { | ||
| 9 | return 0; | ||
| 10 | } | ||
| 11 | |||
| 12 | void midi_playnote( int channel, int note, int octave_offset ) { | ||
| 13 | char out[32]; | ||
| 14 | int b = sprintf(out,"M%02X0020\nM%02X%02X%02X\n", 0xe0 | channel, 0x90 | channel, note + 12 * octave_offset, 0x7f); | ||
| 15 | if (g_harfe_connected && (g_harfe_fd != -1)) | ||
| 16 | write(g_harfe_fd, out, b); | ||
| 17 | } | ||
| 18 | |||
| 19 | void midi_silencenote( int channel, int note, int octave_offset ) { | ||
| 20 | char out[10]; | ||
| 21 | int b = sprintf(out,"M%02X%02X%02X\n", 0x80 | channel, note + 12 * octave_offset, 0); | ||
| 22 | if (g_harfe_connected && (g_harfe_fd != -1)) | ||
| 23 | write(g_harfe_fd, out, b); | ||
| 24 | } | ||
| 25 | |||
| 26 | void midi_pitchbend( int channel, int pitch ) { | ||
| 27 | char out[10]; | ||
| 28 | pitch += 8192; | ||
| 29 | if (pitch < 0) | ||
| 30 | pitch = 0; | ||
| 31 | if (pitch > 16383) | ||
| 32 | pitch = 16383; | ||
| 33 | int b = sprintf(out,"M%02X%02X%02X\n", 0xe0 | channel, 0x7f & pitch, 0x7f & (pitch>>7)); | ||
| 34 | if (g_harfe_connected && (g_harfe_fd != -1)) | ||
| 35 | write(g_harfe_fd, out, b); | ||
| 36 | } | ||
| 37 | |||
| 38 | //void midi_controller_event( int saite, int value ); | ||
| 39 | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | int midi_init(); | ||
| 4 | |||
| 5 | void midi_playnote( int channel, int note, int octave_offset ); | ||
| 6 | void midi_silencenote( int channel, int note, int octave_offset ); | ||
| 7 | void midi_pitchbend( int channel, int pitch ); | ||
| 8 | //void midi_controller_event( int saite, int value ); | ||
| 9 | |||
