summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GenBkBasB.h4
-rwxr-xr-xGenBkBasB.ttfbin0 -> 267024 bytes
-rw-r--r--Makefile11
-rw-r--r--config.c309
-rw-r--r--config.h75
-rw-r--r--config_midi112
-rw-r--r--display.c123
-rw-r--r--display.h17
-rw-r--r--engine.c220
-rw-r--r--engine.h6
-rw-r--r--main-sdl.c268
-rw-r--r--main.h10
-rw-r--r--midi.c39
-rw-r--r--midi.h9
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
3extern unsigned char GenBkBasB_ttf[];
4extern 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 @@
1all: main
2
3GenBkBasB.c: GenBkBasB.ttf
4 xxd -i $< $@
5
6main: 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
10clean:
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
10int g_midi_main_control = -1;
11int g_midi_main_channel = 0;
12int g_midi_two_octave_split = 256 / 2;
13int g_midi_three_octave_split_1 = 256 / 3;
14int g_midi_three_octave_split_2 = 512 / 3;
15int g_midi_three_octave_split_inverse = 0;
16int g_settled_dist = 5;
17int g_timetosilence = 30;
18
19int g_min_y = 0, g_max_y;
20
21static 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
35static uint8_t
36config_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
46char *config_midi_note_to_string(int string) {
47 return midi_note[string];
48}
49
50#define LINEBUFFER 512
51static char *config_file;
52void
53config_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
305void
306config_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
8extern int g_min_y, g_max_y;
9
10extern int g_midi_two_octave_split;
11extern int g_midi_three_octave_split_1;
12extern int g_midi_three_octave_split_2;
13extern int g_midi_three_octave_split_inverse;
14extern int g_midi_main_control;
15extern int g_midi_main_channel;
16extern int g_settled_dist;
17extern int g_timetosilence;
18
19typedef 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
27typedef 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
35typedef enum {
36 silent = 0,
37 in_attack = 1,
38 playing = 2
39} StringPlaying;
40
41typedef struct {
42 int x0; int y0;
43 int x1; int y1;
44} LLine;
45
46typedef struct {
47 int x; int y;
48} LPoint;
49
50typedef 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
70extern StringConfig g_string_conf[MAX_LINECOUNT];
71extern int g_string_count;
72void config_parse( char *config_file );
73char *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#
5Strings 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
18midi_two_octave_split 50
19
20# If split_1 > split_2 then
21# higher notes are on the bottom
22#
23
24midi_three_octave_split_1 33
25midi_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
42String 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
51String 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
60String 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
69String 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
78String 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
87String 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
96String 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
105String 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
10static SDL_Window *screen;
11static SDL_Renderer *renderer;
12static int g_width, g_height;
13static TTF_Font *font = NULL;
14
15void
16display_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
41void
42display_clear()
43{
44 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
45 SDL_RenderClear(renderer);
46}
47
48void
49display_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
55void
56display_circle_color(int x, int y, int w, int color)
57{
58 filledCircleColor(renderer, x, y, w, color);
59}
60
61void
62display_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
68void
69display_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
75void
76display_redraw()
77{
78 SDL_RenderPresent(renderer);
79}
80
81void
82display_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
94void
95display_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
109int
110display_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
3void display_init(int width, int height);
4void display_redraw();
5
6void display_clear();
7void display_line(int x0, int y0, int x1, int y1);
8void display_line_color(int x0, int y0, int x1, int y1, int color);
9void display_circle(int x, int y, int w);
10void display_circle_color(int x, int y, int w, int color);
11void display_rectangle(int x, int y, int w, int h);
12
13void display_text(char *text, int x, int y, int color);
14void display_textbox(int min_x, int pos, int max_x, int max_pos, char *text, int color);
15int display_test_menu_click(int y, int max_pos);
16
17void 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
10static int g_selected_string = -1;
11
12#ifndef NO_DISPLAY
13static LPoint g_render_points[1024];
14static int g_render_point_count;
15static const int g_harfe_width = 1024;
16static const int g_harfe_height = 768;
17static int g_factor = 1<<16;
18
19static inline int scale(int coord) {
20 return (int)(((((int64_t)coord) << 32) / g_factor ) >> 16);
21}
22
23void
24engine_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
66void
67engine_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
75static int
76dist_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)
84static int
85dist_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
95static int
96dist_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
101void
102engine_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
204void
205engine_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
3void engine_init();
4void engine_redraw();
5void engine_handle_point(LPoint * p, uint32_t monotime);
6void 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;
25const int g_width = 800, g_height = 600;
26
27StringConfig
28g_string_conf[MAX_LINECOUNT];
29int g_string_count;
30int g_harfe_connected = 0;
31int g_harfe_fd = -1;
32
33static char *
34find_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
67int
68set_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
99void
100set_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
116uint32_t
117now()
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
125static uint32_t g_last_avg;
126static uint32_t g_starttime, g_last_mouse_event, g_lastredraw;
127static int g_events;
128static void
129harfe_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}
199static void *
200worker(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
210int
211main(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}
diff --git a/main.h b/main.h
new file mode 100644
index 0000000..2ae09fc
--- /dev/null
+++ b/main.h
@@ -0,0 +1,10 @@
1#pragma once
2#include <stdint.h>
3
4uint32_t now(); // get monotonic time in ms
5
6extern int g_width;
7extern int g_height;
8extern int g_harfe_connected;
9
10extern 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 @@
1#include <stdlib.h>
2#include <unistd.h>
3#include <stdio.h>
4
5#include "midi.h"
6#include "main.h"
7
8int midi_init() {
9 return 0;
10}
11
12void 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
19void 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
26void 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
diff --git a/midi.h b/midi.h
new file mode 100644
index 0000000..41a29fe
--- /dev/null
+++ b/midi.h
@@ -0,0 +1,9 @@
1#pragma once
2
3int midi_init();
4
5void midi_playnote( int channel, int note, int octave_offset );
6void midi_silencenote( int channel, int note, int octave_offset );
7void midi_pitchbend( int channel, int pitch );
8//void midi_controller_event( int saite, int value );
9