#include #include #include "config.h" #include "geometry.h" #include "engine.h" #include "main.h" #include "midi.h" static int g_selected_string = -1; static int g_normalize_mode = 0; #ifndef NO_DISPLAY #include "display.h" #include "menu.h" static LPoint g_render_points[1024]; static int g_render_point_count; static ConfigSelect g_selected_config; static void highlight_line(int value, int y, int max_x, uint32_t color); #define scale(X) display_scale_harfe_to_screen(X) static int64_t engine_normalize(int height, int y) { /* double xf = ((double)x) / 100.0f; double yf = g_normalize_factor * (xf*xf-xf) + xf; y = ax^2 + x - ax = a * (x^2 - x) + x int y = scale(g_min_y + (int)(((double)height) * yf)); */ int64_t yf = (y << 16) / height; yf = (((yf * yf - (yf << 16)) * ((int64_t)g_normalize_factor))) + (yf << 32); return (((int64_t)height) * yf) >> 32; } void engine_redraw() { const char *config_hints[] = { "No", "Harfe", "File", "Edit" }; char text[32]; const int tos1 = g_midi_three_octave_split_1, tos2 = g_midi_three_octave_split_2; const int height = g_max_y - g_min_y; int i, x2, MAX_X, MAX_Y, FONT_HEIGHT; int t1 = 0, t2 = 0, t3 = 0; display_redraw(); display_clear(); display_getdimensions(&MAX_X, &MAX_Y, &FONT_HEIGHT); if (!g_importing_config) snprintf( text, sizeof(text), g_harfe_connected ? "online (%s)" : "offline (%s)", config_hints[(int)g_config_source]); else snprintf( text, sizeof(text), "importing config (%s)", config_hints[(int)g_config_source]); display_text(text, 8, MAX_Y - 4, g_harfe_connected ? 0x00ff3fff : 0xff003fff ); menu_redraw(); if (height) { int b = g_midi_three_octave_split_inverse; display_text( b ? "+1" : "-1", 4, scale(g_min_y + tos1 * height / 200) + FONT_HEIGHT / 2, 0x007f7f7fff); display_text( " 0", 4, scale(g_min_y + (tos1 + tos2) * height / 200) + FONT_HEIGHT / 2, 0x007f7f7fff); display_text( b ? "-1" : "+1", 4, scale(g_min_y + (tos2 / 2 + 50 ) * height / 100) + FONT_HEIGHT / 2, 0x007f7f7fff); for (int x=0; x>16)), MAX_X, scale(g_min_y + (y>>16)), 0x1f1f1f1f); if (y >> 16 > (tos2 * height) / 100 ) t1++; else if (y >> 16 > (tos1 * height) / 100 ) t2++; else t3++; } if (g_normalize_mode) { sprintf(text, "%lf", g_normalize_factor / 65336.0f); display_text(text, 8, MAX_Y - 32, 0xff003fff ); sprintf(text, "%d %d %d", t1, t2, t3); display_text(text, 8, MAX_Y - 64, 0xff003fff ); } display_line_color(0, scale(g_min_y), MAX_X, scale(g_min_y), 0xff00ffff); display_line_color(0, scale(g_max_y), MAX_X, scale(g_max_y), 0xff00ffff); #ifdef HAVE_TWO_OCTAVE_MODE display_line_color(0, scale(g_min_y + g_midi_two_octave_split * height / 100), MAX_X, scale(g_min_y + g_midi_two_octave_split * height / 100), 0xffff00ff); #endif display_line_color(0, scale(g_min_y + tos1 * height / 100), MAX_X, scale(g_min_y + tos1 * height / 100), 0x00ff00ff); display_line_color(0, scale(g_min_y + tos2 * height / 100), MAX_X, scale(g_min_y + tos2 * height / 100), 0x00ff00ff); switch (g_selected_config) { case sel_min_y: highlight_line(g_min_y, g_min_y, MAX_X, 0xff00ffff); break; case sel_max_y: highlight_line(g_max_y, g_max_y, MAX_X, 0xff00ffff); break; case sel_2_oct: highlight_line(g_midi_two_octave_split, g_min_y + g_midi_two_octave_split * height / 100, MAX_X, 0xffff00ff); break; case sel_3_oct_top: highlight_line(tos2, g_min_y + tos2 * height / 100, MAX_X, 0x00ff00ff); break; case sel_3_oct_bottom: highlight_line(tos1, g_min_y + tos1 * height / 100, MAX_X, 0x00ff00ff); break; case sel_none: break; } for (i = 0; i < g_string_count; ++i) { LLine *l = &g_string_conf[i].line; LLine _l = g_string_conf[i].line; // Copy for extension uint32_t color = ( ( i == g_selected_string ) ? 0xff00ffff : 0x00ffffffff ); uint32_t text_color = ( ( i == g_selected_string ) ? 0xff00ffff : 0x007f7f7fff ); int extended = 0, center_y = g_min_y+height/2; if (_l.p0.y > g_min_y) { _l.p0.x = get_x_for_y(&_l.p0, &_l.p1, g_min_y); _l.p0.y = g_min_y; extended = 1; } if (_l.p1.y < g_max_y) { _l.p1.x = get_x_for_y(&l->p0 /* Using unmodified original! */, &_l.p1, g_max_y); _l.p1.y = g_max_y; extended = 1; } if (extended) { display_line_color(scale(_l.p0.x), scale(_l.p0.y), scale(_l.p1.x), scale(_l.p1.y), 0x007f3f00); display_circle_color(scale(_l.p0.x), scale(_l.p0.y), 4, 0x007f3f00); display_circle_color(scale(_l.p1.x), scale(_l.p1.y), 4, 0x007f3f00); } x2 = get_x_for_y(&l->p0,&l->p1,center_y); 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(x2-20), scale(center_y) + FONT_HEIGHT, text_color ); else display_text(config_midi_note_to_string(g_string_conf[i].note), scale(x2-20), scale(center_y) + FONT_HEIGHT, text_color ); display_line_color(scale(l->p0.x), scale(l->p0.y), scale(l->p1.x), scale(l->p1.y), color); display_circle(scale(l->p0.x), scale(l->p0.y), 4); display_circle(scale(l->p1.x), scale(l->p1.y), 4); } } 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; } static void highlight_line(int value, int y, int max_x, uint32_t color) { char text[32]; display_line_color(0, scale(y-10), 10, scale(y), color); display_line_color(0, scale(y+10), 10, scale(y), color); display_line_color(max_x, scale(y-10), max_x-10, scale(y), color); display_line_color(max_x, scale(y+10), max_x-10, scale(y), color); display_rect_color(max_x/2-40, scale(y+20), 80, 40, 0xffffffff); snprintf(text, sizeof(text), "%d", value); display_text(text, max_x/2-20, scale(y+20), 0); } void engine_select_config(ConfigSelect sel) { // test if we have moved g_min_y or g_max_y and resize lines accordingly if (sel == sel_none) { int i; for (i=0; ip0.y > g_min_y) { l->p0.x = get_x_for_y(&l->p0, &l->p1, g_min_y); l->p0.y = g_min_y; } if (l->p1.y < g_max_y) { l->p1.x = get_x_for_y(&_l.p0 /* Using unmodified original! */, &l->p1, g_max_y); l->p1.y = g_max_y; } } menu_setmode(0x17, 0); } else if (!g_string_count || menu_setmode(0x17, 4)) return; g_selected_config = sel; } void engine_toggle_normalize_mode() { if (g_normalize_mode) g_config_source = source_edit; g_normalize_mode = 1 - g_normalize_mode; } void engine_mouse_y(int y) { if (g_normalize_mode) g_normalize_factor = y; } ConfigSelect engine_change_selected(int off) { /* Assume source has changed unless no config is selected, in default branch */ ConfigSource backup = g_config_source; g_config_source = source_edit; switch(g_selected_config) { case sel_min_y: g_min_y += off; break; case sel_max_y: g_max_y += off; break; case sel_3_oct_top: g_midi_three_octave_split_2 += off; if (g_midi_three_octave_split_2<0) g_midi_three_octave_split_2 = 0; if (g_midi_three_octave_split_2>100) g_midi_three_octave_split_2 = 100; break; case sel_3_oct_bottom: g_midi_three_octave_split_1 += off; if (g_midi_three_octave_split_1<0) g_midi_three_octave_split_1 = 0; if (g_midi_three_octave_split_1>100) g_midi_three_octave_split_1 = 100; break; case sel_2_oct: g_midi_two_octave_split += off; if (g_midi_two_octave_split<0) g_midi_two_octave_split = 0; if (g_midi_two_octave_split>100) g_midi_two_octave_split = 100; break; default: g_config_source = backup; break; } return g_selected_config; } #endif void engine_init() { } 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 = g_pitch_factor; int dv, dt, speed, new_pitch; #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); /* Avoid miss-fires, check if offset is in range -5% - +105% */ if ((dist < 512 * 10 * 10 ) && (dist < dist_max) && (offs<68812) && (offs>-3276)) { dist_max = dist; saite = i; } } if (saite == -1) return; s = g_string_conf + saite; g_selected_string = saite; y_viewfield = (100 * (p->y - g_min_y)) / (g_max_y - g_min_y); if (y_viewfield < 0) y_viewfield = 0; if (y_viewfield >= 100) y_viewfield = 100; // 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 string_is_silent: s->playing = string_is_in_attack; s->octave = oct; s->start_off = s->last_off = offs; s->first_time_seen = monotime; break; case string_is_in_attack: // test if difference is less than g_settled_dist percent of // line segment length dt = monotime - s->first_time_seen; if (dt > g_settled_timedelta && abs(s->last_off - offs) < g_settled_dist) { s->playing = string_is_playing; // estimated energy of hand is dv/dt from first seen to settled dv = abs(s->start_off - offs); if (!dt) ++dt; speed = 1000 * dv / dt; // in offs_prec per second // printf("SPEED: %d (%d) for dt %d\n", speed, 64 + speed / 4000, dt); speed = 64 + speed / 4000; if (speed > 127) speed = 127; midi_playnote(s->channel, s->note, s->octave, speed); } s->last_off = offs; break; case string_is_playing: if (monotime - s->first_time_seen < g_pitchbend_delay) break; s->start_off = offs; s->current_pitch = 0; s->playing = string_is_bending; break; case string_is_bending: 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 * (offs - s->start_off)) >> 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; s->seen_but_unchecked = 1; } 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; // Play notes in attack that are not visible anymore if (s->playing == string_is_in_attack && !s->seen_but_unchecked ) { int speed, dv, dt = monotime - s->first_time_seen; if (!dt) ++dt; s->playing = string_is_playing; // estimated energy of hand is dv/dt from first seen to settled dv = abs(s->start_off - s->last_off); speed = 1000 * dv / dt; // in offs_prec per second // printf("SPEED: %d (%d) for dt %d\n", speed, 64 + speed / 4000, dt); speed = 64 + speed / 4000; if (speed > 127) speed = 127; midi_playnote(s->channel, s->note, s->octave, speed); s->start_off = s->last_off; } if (s->playing && (monotime - s->last_time_seen > tts)) { if (s->playing >= string_is_playing) midi_silencenote(s->channel, s->note, s->octave); s->playing = string_is_silent; } s->seen_but_unchecked = 0; } }