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 /engine.c | |
commit as a backup
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 | } | ||
