diff options
-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 | |||