#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" /*** 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; int g_importing_config = 0; ConfigSource g_config_source = source_none; int g_calibration_running = 0; 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; } static void calib_fetch() { int default_notes[] = { 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83 }; int i, result_count; LLine *result = calib_get_results(&result_count); config_reset(); g_string_count = result_count; g_min_y = 0; g_max_y = 1024; for (i=0; ip0.y > g_min_y) g_min_y = l->p0.y; if (l->p1.y < g_max_y) g_max_y = l->p1.y; g_string_conf[i].line = *l; g_string_conf[i].mode = midi_three_octaves; g_string_conf[i].channel = i; g_string_conf[i].note = default_notes[i]; g_string_conf[i].modifier = pitch_bend_up; } g_config_source = source_edit; } 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; static int g_up_pressed = 0, g_down_pressed = 0; display_init(SCREEN_WIDTH, SCREEN_HEIGHT, HARFE_WIDTH, HARFE_HEIGHT); engine_init(); // config_parse("config_midi"); 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: if ( ev.key.keysym.scancode == SDL_SCANCODE_0) engine_select_config(sel_none); if ( ev.key.keysym.scancode == SDL_SCANCODE_1) engine_select_config(sel_min_y); if ( ev.key.keysym.scancode == SDL_SCANCODE_2) engine_select_config(sel_max_y); if ( ev.key.keysym.scancode == SDL_SCANCODE_3) engine_select_config(sel_2_oct); if ( ev.key.keysym.scancode == SDL_SCANCODE_4) engine_select_config(sel_3_oct_bottom); if ( ev.key.keysym.scancode == SDL_SCANCODE_5) engine_select_config(sel_3_oct_top); if ( ev.key.keysym.scancode == SDL_SCANCODE_UP) if ( ev.key.repeat || ! g_up_pressed++) engine_change_selected(1); if ( ev.key.keysym.scancode == SDL_SCANCODE_DOWN) if ( ev.key.repeat || ! g_down_pressed++) engine_change_selected(-1); if ( ev.key.keysym.scancode == SDL_SCANCODE_S) { /* export locally */ const char *homeDir = getenv("HOME"); char savefile[512], date[32], confdump[512]; time_t t = time(NULL); struct tm *tmp = localtime(&t); int 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; i 30 && !g_importing_config) { 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; }