#include #include #include "config.h" #include "engine.h" #include "display.h" #include "main.h" #include "midi.h" static int g_selected_string = -1; #ifndef NO_DISPLAY static LPoint g_render_points[1024]; static int g_render_point_count; static const int g_harfe_width = 1024; static const int g_harfe_height = 768; static int g_factor = 1<<16; static inline int scale(int coord) { return (int)(((((int64_t)coord) << 32) / g_factor ) >> 16); } void engine_redraw() { int i; display_redraw(); display_clear(); display_text(g_harfe_connected ? "online" : "offline", 4, 4, 0xffffffff); for (i = 0; i < g_string_count; ++i) { LLine *l = &g_string_conf[i].line; uint32_t color = i == g_selected_string ? 0xff00ffff : 0x00ffffffff; display_line_color(scale(l->x0), scale(l->y0), scale(l->x1), scale(l->y1), color); display_circle(scale(l->x0), scale(l->y0), 4); display_circle(scale(l->x1), scale(l->y1), 4); if (g_string_conf[i].playing) 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 ); else display_text(config_midi_note_to_string(g_string_conf[i].note), scale(l->x1) - 20, g_height - 40, color ); } g_selected_string = -1; for (i = 0; i < g_render_point_count; ++i) display_circle_color(scale(g_render_points[i].x), scale(g_render_points[i].y), 4, 0xff0000ff); g_render_point_count = 0; display_line_color(0, scale(g_min_y), g_width, scale(g_min_y), 0xff00ffff); display_line_color(0, scale(g_max_y), g_width, scale(g_max_y), 0xff00ffff); if (g_min_y != g_max_y) { int height = scale(g_max_y - g_min_y); 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); 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); 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); } } #endif void engine_init() { #ifndef NO_DISPLAY g_factor = (g_harfe_width << 16) / g_width; if ((g_harfe_height << 16) / g_height < g_factor) g_factor = (g_harfe_height << 16) / g_height; #endif } static int dist_pp(int x0, int y0, int x1, int y1) { return (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1); } // dist is a fixed point with precission of 8 bits // offs is where on the line segment xy0-xy1 the point's normale hits, // range 0..65536 (but can extend, if normale hits line outside line segment) static int dist_lp(int x0, int y0, int x1, int y1, int xp, int yp, int *offs) { int64_t r = (y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0); int64_t q1 = (xp - x0) * (y1 - y0) - (yp - y0) * (x1 - x0); int64_t q2 = (x1 - x0) * (xp - x0) + (y1 - y0) * (yp - y0); *offs = (int)((q2 << 16) / r); return (int)( q1 * q1 * ((y0 - y1) * (y0 - y1) + (x1 - x0) * (x1 - x0)) * 256 / (r * r)); } static int dist_pl(LPoint * p, LLine * l, int *offs) { return dist_lp(l->x0, l->y0, l->x1, l->y1, p->x, p->y, offs); } void engine_handle_point(LPoint * p, uint32_t monotime) { StringConfig *s;; int dist_max = 1024 * 1024 * 8; int offs, saite = -1, i, oct = 0; int y_viewfield, pitch_factor = 12; int dv, dt, speed, new_pitch; // XXX should not be inverted here p->x = 1024 - p->x; #ifndef NO_DISPLAY /* Pass to "render thread" */ g_render_points[g_render_point_count] = *p; ++g_render_point_count; #endif /* See which line is closest */ for (i = 0; i < g_string_count; ++i) { int dist = dist_pl(p, &g_string_conf[i].line, &offs); if ((dist < 256 * 10 * 10 ) && (dist < dist_max)) { dist_max = dist; saite = i; } } if (saite == -1) return; s = g_string_conf + saite; g_selected_string = saite; y_viewfield = 256 * (g_max_y - p->y) / (g_max_y - g_min_y); if (y_viewfield < 0) y_viewfield = 0; if (y_viewfield >= 256) y_viewfield = 255; // Determine octave, if configured switch (s->mode) { case midi_controller: case midi_controller_inv: return; // not implemented case midi_one_octave: break; case midi_two_octaves: if (y_viewfield < g_midi_two_octave_split) oct = -1; break; case midi_three_octaves: if (y_viewfield < g_midi_three_octave_split_1) oct = -1; if (y_viewfield > g_midi_three_octave_split_2) oct = 1; break; } // handle inverted octave configuration if (g_midi_three_octave_split_inverse) oct = -oct; switch (s->playing) { case silent: midi_playnote(s->channel, s->note, oct); s->playing = in_attack; s->octave = oct; s->start_off = s->last_off = offs; s->first_time_seen = monotime; break; case in_attack: // test if difference is less than g_settled_dist percent of // line segment length if (100 * abs(s->last_off - offs) < g_settled_dist << 16) { s->playing = playing; s->current_pitch = 0; // estimated energy of hand is dv/dt from first seen to settled dv = abs(s->start_off - offs); dt = monotime - s->first_time_seen; if (!dt) ++dt; speed = 1000 * dv / dt; // in offs_prec per second } s->last_off = offs; break; case playing: if (s->pitch_factor) pitch_factor = s->pitch_factor; if (s->modifier == pitch_bend_up) new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; else if (s->modifier == pitch_bend_down) new_pitch = (pitch_factor * (s->start_off - offs)) >> 16; else break; // avoid reporting same pitch bend over and over if (s->current_pitch == new_pitch) break; midi_pitchbend(s->channel, new_pitch); s->current_pitch = new_pitch; break; } s->last_time_seen = monotime; } void engine_checksilence(uint32_t monotime) { int i; for (i = 0; i < g_string_count; ++i) { StringConfig *s = g_string_conf + i; int tts = s->timetosilence ? s->timetosilence : g_timetosilence; if (s->mode == midi_controller) continue; if (s->playing && (monotime - s->last_time_seen > tts)) { midi_silencenote(s->channel, s->note, s->octave); s->playing = 0; } } }