#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "display.h" #include "config.h" #include "engine.h" #include "calib.h" #include "menu.h" /*** Global config and status values ***/ /* Window width and height */ enum { HARFE_WIDTH = 1024, HARFE_HEIGHT = 768, SCREEN_WIDTH = 800, SCREEN_HEIGHT = 600 }; int g_harfe_connected = 0; int g_harfe_fd = -1; static int g_up_pressed, g_down_pressed, g_mouse_down; static LPoint g_last_mouse; static char * find_harfe() { // Device name should be a cu.device // followed by the letter HAR somewhere // e.g. /dev /cu.usbmodemHAR1 DIR * dev = opendir("/dev/"); struct dirent *dp; char *harfe = 0; if (!dev) return 0; while ((dp = readdir(dev)) != NULL) { size_t len = dp->d_namlen; char *name = dp->d_name; int i; if (len < 6 || name[0] != 'c' || name[1] != 'u' || name[2] != '.') continue; for (i = 0; i < len - 3; ++i) if (name[i] == 'H' && name[i + 1] == 'A' && name[i + 2] == 'R' ) { if ((harfe = calloc(1, 5 + len + 1))) { sprintf(harfe, "/dev/"); memcpy(harfe + 5, name, len); } break; } } closedir(dev); return harfe; } int set_interface_attribs(int fd, int speed, int parity) { struct termios tty; memset(&tty, 0, sizeof tty); if (tcgetattr(fd, &tty) != 0) return -1; cfsetospeed(&tty, speed); cfsetispeed(&tty, speed); tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; tty.c_iflag &= ~IGNBRK; tty.c_lflag = 0; tty.c_oflag = 0; tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 5; tty.c_iflag &= ~(IXON | IXOFF | IXANY); tty.c_cflag |= (CLOCAL | CREAD); tty.c_cflag &= ~(PARENB | PARODD); tty.c_cflag |= parity; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CRTSCTS; if (tcsetattr(fd, TCSANOW, &tty) != 0) return -1; return 0; } void set_blocking(int fd, int should_block) { struct termios tty; memset(&tty, 0, sizeof tty); if (tcgetattr(fd, &tty) != 0) return; tty.c_cc[VMIN] = should_block ? 1 : 0; tty.c_cc[VTIME] = 5; if (tcsetattr(fd, TCSANOW, &tty) != 0) return; } uint32_t now() { struct timeval now; gettimeofday(&now, (struct timezone *)NULL); return now.tv_sec * 1000 + now.tv_usec / 1000; /* in ms */ } static uint32_t g_last_avg; static uint32_t g_starttime, g_last_mouse_event, g_lastredraw; static int g_events; static void harfe_worker(void) { LPoint p[4]; char text[256], *lineend; int fd, i, text_fill = 0, consumed, running = 1; uint32_t ptime; char *portname = find_harfe(); if (!portname) { printf("Can't find harfe serial device..."); return; } g_harfe_fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); if (g_harfe_fd < 0) { free(portname); printf("Can't connect to harfe..."); return; } set_interface_attribs(g_harfe_fd, B115200, 0); set_blocking(g_harfe_fd, 0); printf("Connection to harfe established at %s.\n", portname); free(portname); g_harfe_connected = 1; /* Get remote config (if any) */ config_reset(); write(g_harfe_fd, "S2\n", 3); usleep(50); g_importing_config = 1; write(g_harfe_fd, "I\n", 2); while (running) { while (text_fill < sizeof(text) && !memchr(text, '\n', text_fill)) { ssize_t b = read(g_harfe_fd, text + text_fill, sizeof(text) - text_fill); if ((b < 0) && (errno != EAGAIN)) { printf("Connection to harfe lost..."); running = 0; close(g_harfe_fd); g_harfe_fd = -1; break; } text_fill += b; } //Overlong line, drop it and try to resync if (text_fill == sizeof(text)) { text_fill = 0; continue; } // find and remove newline and cf if (!(lineend = memchr(text, '\n', text_fill))) continue; *lineend = 0; if (text_fill && lineend[-1] == '\r') lineend[-1] = 0; printf( "%s\n", text ); if (g_importing_config) { if (!strcmp(text, "-- DONE")) { g_importing_config = 0; g_config_source = source_harfe; } else config_handle_line(text); } else { 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); ptime = now(); for (i = 0; i < num_points / 2; ++i) { // printf("%04d:%04d\n", p[i].x, p[i].y); if (!g_calibration_running) engine_handle_point(p + i, ptime); else calib_handle_point(p + i, ptime); } if (num_points > 1 || *text == '-') ++g_events; } consumed = lineend - text + 1; memmove(text, lineend + 1, text_fill - consumed); text_fill -= consumed; } return; } static void * worker(void *args) { while (1) { harfe_worker(); g_harfe_connected = 0; printf(" retrying in 5 seconds.\n"); sleep(5); } } static void config_parse(char *config_file) { FILE *fh = fopen(config_file, "r"); char line_in[512]; if (!fh) { fprintf(stderr, "Couldn't open config file %s, exiting.\n", config_file); exit(1); } config_reset(); while (!feof(fh)) { char *line = fgets(line_in, sizeof(line_in), fh); if (!line) continue; if (config_handle_line(line)) exit(1); } fclose(fh); g_config_source = source_file; } void config_save() { const char *homeDir = getenv("HOME"); char savefile[512], date[32], confdump[512]; time_t t = time(NULL); struct tm *tmp = localtime(&t); int i, fd; size_t len; if (!homeDir) { struct passwd* pwd = getpwuid(getuid()); if (pwd) homeDir = (const char*)pwd->pw_dir; } strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", tmp); snprintf( savefile, sizeof(savefile), "%s/Laserharfe-%s.cfg", homeDir, date); fd = open(savefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); len = config_dumpglobals( confdump, sizeof(confdump)); write(fd, confdump, len ); for (i=0; ikey.keysym.scancode) { case SDL_SCANCODE_RETURN: menu_setmode(1, 1); break; case SDL_SCANCODE_ESCAPE: if (g_importing_config) { g_importing_config = 0; config_reset(); } calib_stop(); menu_setmode(6, 0); break; case SDL_SCANCODE_UP: if (ev->key.repeat || !g_up_pressed++) engine_change_selected(1); break; case SDL_SCANCODE_DOWN: if (ev->key.repeat || !g_down_pressed++) engine_change_selected(-1); break; case SDL_SCANCODE_0: engine_select_config(sel_none); break; case SDL_SCANCODE_1: engine_select_config(sel_min_y); break; case SDL_SCANCODE_2: engine_select_config(sel_max_y); break; case SDL_SCANCODE_3: engine_select_config(sel_2_oct); break; case SDL_SCANCODE_4: engine_select_config(sel_3_oct_bottom); break; case SDL_SCANCODE_5: engine_select_config(sel_3_oct_top); break; case SDL_SCANCODE_C: if (g_calibration_running) { calib_fetch(); menu_setmode(1<<3, 0); } else { calib_init(); menu_setmode(0, 3); } break; case SDL_SCANCODE_E: config_export(); break; case SDL_SCANCODE_I: config_import(); break; case SDL_SCANCODE_L: config_parse("config_midi"); break; case SDL_SCANCODE_M: if (g_harfe_connected && (g_harfe_fd != -1)) { fprintf( stderr, "MIDIing on %d\n", g_harfe_fd ); write(g_harfe_fd, "ME20020\nM824C00\n", 16); } break; case SDL_SCANCODE_N: engine_toggle_normalize_mode(); break; case SDL_SCANCODE_P: config_flip_pitch(); break; case SDL_SCANCODE_Q: config_reset(); break; case SDL_SCANCODE_R: if (g_calibration_running) calib_init(); break; case SDL_SCANCODE_S: config_save(); break; case SDL_SCANCODE_W: fprintf( stderr, "write remote config\n" ); write(g_harfe_fd, "W\n", 2); break; case SDL_SCANCODE_X: config_reverse_strings(); break; case SDL_SCANCODE_Y: config_flip_y(); break; case SDL_SCANCODE_NONUSBACKSLASH: menu_setmode(2,2); break; #ifdef CALIB_DEBUG case SDL_SCANCODE_SPACE: if (g_calibration_running) calib_next(10); break; #endif default: break; } /* endswitch */ /* Handle the ugly German keyboard <> key mapping */ if (ev->key.keysym.sym == SDLK_GREATER) menu_setmode(2,2); } int main(int argc, char **argv) { SDL_Event ev; int i, counter = 0; pthread_t thread_id; uint32_t runtime; static int last_click_x, last_click_y, last_mouse_event; display_init(SCREEN_WIDTH, SCREEN_HEIGHT, HARFE_WIDTH, HARFE_HEIGHT); engine_init(); pthread_create(&thread_id, NULL, worker, NULL); /* E - Export config to Harfe (with implicite write to SD) I - Import config from Harfe W - Write config on Harfe to SD (no implicite Transfer) S - Save config to local file L - Load config from local default (config_midi) Y - Invert String order X - Invert Octave order Q - Reset local Config C - Start calibration M - Play MIDI C through Harfe */ /* Spin and let call back do all the work */ while (1) { SDL_WaitEventTimeout(&ev, 10); switch (ev.type) { case SDL_QUIT: exit(0); case SDL_KEYUP: if (ev.key.keysym.scancode == SDL_SCANCODE_UP) g_up_pressed = 0; if (ev.key.keysym.scancode == SDL_SCANCODE_DOWN) g_down_pressed = 0; break; case SDL_KEYDOWN: handle_keydown(&ev); break; case SDL_MOUSEMOTION: if (ev.motion.state & SDL_BUTTON_LMASK) { LPoint p = { display_scale_screen_to_harfe(ev.motion.x), 768 - display_scale_screen_to_harfe(ev.motion.y) }; engine_handle_point(&p, now()); g_last_mouse = p; engine_mouse_y((65336 * (SCREEN_HEIGHT-ev.motion.y))/ SCREEN_HEIGHT); } break; case SDL_MOUSEBUTTONDOWN: { LPoint p = { display_scale_screen_to_harfe(ev.button.x), 768 - display_scale_screen_to_harfe(ev.button.y) }; if (menu_test_mouse_down(ev.button.x, ev.button.y)) engine_handle_point(&p, now()); g_last_mouse = p; g_mouse_down = 1; } break; case SDL_MOUSEBUTTONUP: { LPoint p = { display_scale_screen_to_harfe(ev.button.x), 768 - display_scale_screen_to_harfe(ev.button.y) }; menu_handle_button_up(ev.button.x, ev.button.y); g_mouse_down = 0; } break; case SDL_DROPFILE: { char t[512]; int ret; snprintf( t, sizeof(t), "Do you want to import config file %s?\n", ev.drop.file); if ((ret= display_messagebox_yesno("Import Laserharfe config", t))) config_parse(ev.drop.file); printf( "Dropped: %s (%d)\n", ev.drop.file, ret ); break; } default: break; } if (g_mouse_down) engine_handle_point(&g_last_mouse, now()); if (!g_calibration_running || !g_importing_config) engine_checksilence(now()); runtime = now(); if (runtime - g_lastredraw > 30) { g_lastredraw = runtime; if (!g_calibration_running) engine_redraw(); else calib_redraw(); } if (runtime / 1000 - g_last_avg > 10) { if (g_events) printf("avg: %i\n", g_events / 10); g_events = 0; g_last_avg = runtime / 1000; } } return 0; }