diff options
Diffstat (limited to 'engine.c')
-rw-r--r-- | engine.c | 220 |
1 files changed, 220 insertions, 0 deletions
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 | } | ||