/***************************************************************************** * init_midi_processor() * * initialize keylist nodes and round-robin voice selectors *****************************************************************************/ void init_midi_processor() { PART *part; unsigned int part_num; int j; /* initialize keylist nodes and round-robin voice selectors */ for (part_num = 0; part_num < MAX_PARTS; part_num++) { part = get_part(part_num); /* initialize keylist nodes */ for (j = 0; j < 128; j++) { part->keylist[j].midi_key = j & 0x7F; part->keylist[j].next = NULL; } /* initialize round robin voice indices */ vnum[part_num] = 0; /* initialize per-part midi event buffer */ init_midi_event_queue(part_num); } }
/***************************************************************************** * main() * * Parse command line, load patch, start midi_tx, midi_rx, and jack threads. *****************************************************************************/ int main(int argc, char **argv) { char thread_name[16]; char opts[NUM_OPTS * 2 + 1]; struct option *op; char *cp; char *p; char *term; char *tokbuf; int c; int j = 0; int ret = 0; int saved_errno; int argcount = 0; char **argvals = argv; char **envp = environ; char *argvend = (char *)argv; size_t argsize; unsigned char rx_channel; setlocale(LC_ALL, "C"); jamrouter_instance = get_instance_num(); fprintf(stderr, "Starting jamrouter instance %d.\n", jamrouter_instance); /* Start debug thread. debug_class is not set until arguemnts are parsed, so use fprintf() until then. */ if ((ret = pthread_create(&debug_thread_p, NULL, &jamrouter_debug_thread, NULL)) != 0) { fprintf(stderr, "***** ERROR: Unable to start debug thread.\n"); } /* lock down memory (rt hates page faults) */ if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) { saved_errno = errno; fprintf(stderr, "Unable to unlock memory: errno=%d (%s)\n", saved_errno, strerror(saved_errno)); } /* init lash client */ #ifndef WITHOUT_LASH for (j = 0; j < argc; j++) { if ((strcmp(argv[j], "-L") == 0) || (strcmp(argv[j], "--disable-lash") == 0) || (strcmp(argv[j], "-h") == 0) || (strcmp(argv[j], "--help") == 0) || (strcmp(argv[j], "-l") == 0) || (strcmp(argv[j], "--list") == 0) || (strcmp(argv[j], "-v") == 0) || (strcmp(argv[j], "--version") == 0) || (strcmp(argv[j], "-D") == 0) || (strcmp(argv[j], "--session-dir") == 0) || (strcmp(argv[j], "-u") == 0) || (strcmp(argv[j], "--uuid") == 0)) { lash_disabled = 1; break; } } if (!lash_disabled) { snprintf(thread_name, 16, "jamrouter%c-lash", ('0' + jamrouter_instance)); pthread_setname_np(pthread_self(), thread_name); if (lash_client_init(&argc, &argv) == 0) { lash_poll_event(); } snprintf(thread_name, 16, "jamrouter%c-main", ('0' + jamrouter_instance)); pthread_setname_np(pthread_self(), thread_name); } #endif /* startup initializations */ init_midi_event_queue(); init_jack_audio_driver(); select_midi_driver(NULL, DEFAULT_MIDI_DRIVER); /* save original command line for session handling */ if (jamrouter_cmdline[0] == '\0') { jamrouter_cmdline[0] = '\0'; for (j = 0; j < argc; j++) { strcat(&(jamrouter_cmdline[0]), argv[j]); strcat(&(jamrouter_cmdline[0]), " "); } jamrouter_cmdline[strlen(jamrouter_cmdline) - 1] = '\0'; term = get_color_terminal(); if (term == NULL) { strcpy(jamrouter_full_cmdline, jamrouter_cmdline); } else { snprintf(jamrouter_full_cmdline, 512, "%s -e \"%s \"", term, jamrouter_cmdline); } argcount = argc; argvals = argv; } /* command line args supplied by session manager */ else { argcount = 0; cp = strdup(jamrouter_cmdline); p = cp; while ((p = index(p, ' ')) != NULL) { p++; argcount++; } if ((argvals = malloc(((size_t)(argcount) + 1UL) * (size_t)sizeof(char *))) == NULL) { fprintf(stderr, "Out of Memory!\n"); return -1; } if ((tokbuf = alloca(strlen(jamrouter_cmdline) * 4)) == NULL) { fprintf(stderr, "Out of Memory!\n"); return -1; } while ((p = strtok_r(cp, " ", &tokbuf)) != NULL) { cp = NULL; argvals[j++] = p; } argvals[argcount] = NULL; } /* build the short option string */ cp = opts; for (op = long_opts; op < &long_opts[NUM_OPTS]; op++) { *cp++ = (char) op->val; if (op->has_arg) { *cp++ = ':'; } } /* handle options */ for (;;) { c = getopt_long(argcount, argvals, opts, long_opts, NULL); if (c == -1) { break; } switch (c) { case 'M': /* MIDI driver */ select_midi_driver(optarg, -1); break; case 'D': /* MIDI Rx/Tx port/device */ midi_rx_port_name = strdup(optarg); midi_tx_port_name = strdup(optarg); break; case 'r': /* MIDI Rx port/device */ midi_rx_port_name = strdup(optarg); break; case 't': /* MIDI Tx port/device */ midi_tx_port_name = strdup(optarg); break; case 'x': /* MIDI Rx latency periods */ rx_latency_periods = atoi(optarg); break; case 'X': /* MIDI Tx latency periods */ tx_latency_periods = atoi(optarg); break; case 'g': /* Tx byte guard time in usec */ byte_guard_time_usec = atoi(optarg); break; case 'G': /* Tx event guard time in usec */ event_guard_time_usec = atoi(optarg); break; case 'i': /* JACK MIDI input port */ jack_input_port_name = strdup(optarg); break; case 'o': /* JACK MIDI output port */ jack_output_port_name = strdup(optarg); break; case 'j': /* Jitter correction mode */ jitter_correct_mode = 1; break; case 'z': /* JACK wake phase within MIDI Rx/Tx period */ setting_midi_phase_lock = (timecalc_t)(atof(optarg)); if (setting_midi_phase_lock < (timecalc_t)(0.0625)) { setting_midi_phase_lock = (timecalc_t)(0.0625); } else if (setting_midi_phase_lock > (timecalc_t)(0.9375)) { setting_midi_phase_lock = (timecalc_t)(0.9375); } break; #ifndef WITHOUT_JACK_DLL case '4': /* JACK DLL timing level 4 */ jack_dll_level = 4; break; case '3': /* JACK DLL timing level 3 */ jack_dll_level = 3; break; case '2': /* JACK DLL timing level 2 */ jack_dll_level = 2; break; case '1': /* JACK DLL timing level 1 */ jack_dll_level = 1; break; #endif case 'k': /* key to controller mapping */ if (optarg != NULL) { if ((tokbuf = alloca(strlen((const char *)optarg) * 4)) == NULL) { jamrouter_shutdown("Out of memory!\n"); } if ((p = strtok_r(optarg, ",", &tokbuf)) != NULL) { rx_channel = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { keymap_tx_channel[rx_channel] = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { keymap_tx_controller[rx_channel] = atoi(p) & 0x7F; } } JAMROUTER_DEBUG(DEBUG_CLASS_INIT, "Key --> Controller Map: " "rx_channel=%0d tx_channel=%d tx_cc=%d\n", rx_channel + 1, keymap_tx_channel[rx_channel] + 1, keymap_tx_controller[rx_channel]); } } break; case 'p': /* key to pitchbend translation */ if (optarg != NULL) { if ((tokbuf = alloca(strlen((const char *)optarg) * 4)) == NULL) { jamrouter_shutdown("Out of memory!\n"); } if ((p = strtok_r(optarg, ",", &tokbuf)) != NULL) { rx_channel = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { pitchmap_tx_channel[rx_channel] = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { pitchmap_center_note[rx_channel] = atoi(p) & 0x7F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { pitchmap_bend_range[rx_channel] = atoi(p) & 0x7F; } } } JAMROUTER_DEBUG(DEBUG_CLASS_INIT, "Key --> Pitchbend Map: " "rx_chan=%0d tx_chan=%d center=%d range=%d\n", rx_channel + 1, pitchmap_tx_channel[rx_channel] + 1, pitchmap_center_note[rx_channel], pitchmap_bend_range[rx_channel]); } } break; case 'q': /* pitchbend to controller translation */ if (optarg != NULL) { if ((tokbuf = alloca(strlen((const char *)optarg) * 4)) == NULL) { jamrouter_shutdown("Out of memory!\n"); } if ((p = strtok_r(optarg, ",", &tokbuf)) != NULL) { rx_channel = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { pitchcontrol_tx_channel[rx_channel] = (atoi(p) - 1) & 0x0F; if ((p = strtok_r(NULL, ",", &tokbuf)) != NULL) { pitchcontrol_controller[rx_channel] = atoi(p) & 0x7F; } } JAMROUTER_DEBUG(DEBUG_CLASS_INIT, "Pitchbend --> Controller Map: " "rx_chan=%0d tx_chan=%d controller=%d\n", rx_channel + 1, pitchcontrol_tx_channel[rx_channel] + 1, pitchcontrol_controller[rx_channel]); } } break; #ifndef WITHOUT_JUNO case 'J': /* Juno-106 sysex controller translation */ translate_juno_sysex = 1; break; case 's': /* echo sysex translations back to originator */ echosysex = 1; break; #endif case 'e': /* echo pitchbend and controller translations back to originator */ echotrans = 1; break; case 'T': /* alternate sysex terminator byte */ sysex_terminator = hex_to_byte(optarg); break; case 'U': /* alternate sysex terminator byte */ sysex_extra_terminator = hex_to_byte(optarg); break; case 'A': /* Active-Sensing mode */ if (strcmp(optarg, "on") == 0) { active_sensing_mode = ACTIVE_SENSING_MODE_ON; } else if (strcmp(optarg, "thru") == 0) { active_sensing_mode = ACTIVE_SENSING_MODE_THRU; } else if (strcmp(optarg, "drop") == 0) { active_sensing_mode = ACTIVE_SENSING_MODE_DROP; } break; case 'R': /* Omit running status byte on MIDI Tx */ use_running_status = 1; break; case 'n': /* Note-On Velocity */ note_on_velocity = hex_to_byte(optarg); break; case 'N': /* Note-Off Velocity */ note_off_velocity = hex_to_byte(optarg); break; case 'f': /* Send multiple Note-Off messages as All-Notes-Off */ tx_prefer_all_notes_off = 1; break; case 'F': /* Tx send real Note-Off instead of Velocity-0-Note-On */ tx_prefer_real_note_off = 1; break; case '0': /* Rx queue real Note-Off instead of Velocity-0-Note-On */ rx_queue_real_note_off = 1; break; case 'y': /* MIDI Rx thread priority */ if ((midi_rx_thread_priority = atoi(optarg)) <= 0) { midi_rx_thread_priority = MIDI_RX_THREAD_PRIORITY; } break; case 'Y': /* MIDI Tx thread priority */ if ((midi_tx_thread_priority = atoi(optarg)) <= 0) { midi_tx_thread_priority = MIDI_TX_THREAD_PRIORITY; } break; case 'd': /* debug */ debug = 1; for (j = 0; debug_class_list[j].name != NULL; j++) { if (strcmp(debug_class_list[j].name, optarg) == 0) { debug_class |= debug_class_list[j].id; } } break; case 'v': /* version */ printf("jamrouter-%s\n", PACKAGE_VERSION); return 0; case 'L': /* disable lash */ lash_disabled = 1; break; case 'l': /* list midi devices */ scan_midi(); return 0; case 'u': /* jack session uuid */ jack_session_uuid = strdup(optarg); break; case '?': case 'h': /* help */ default: showusage(argv[0]); return -1; } } /* Rewrite process title */ argcount = argc; argvals = argv; for (j = 0; j <= argcount; j++) { if ((j == 0) || ((argvend + 1) == argvals[j])) { argvend = argvals[j] + strlen(argvals[j]); } else { continue; } } /* steal space from first environment entry */ if (envp[0] != NULL) { argvend = envp[0] + strlen (envp[0]); } /* calculate size we have for process title */ argsize = (size_t)((char *)argvend - (char *)*argvals - 1); memset (*argvals, 0, argsize); /* rewrite process title */ argc = 0; snprintf((char *)*argvals, argsize, "jamrouter%d", jamrouter_instance); /* signal handlers for clean shutdown */ init_signal_handlers(); /* init MIDI system based on selected driver */ JAMROUTER_DEBUG(DEBUG_CLASS_INIT, "Initializing MIDI: driver=%s.\n", midi_driver_names[midi_driver]); init_sync_info(0, 0); init_midi(); /* initialize JACK audio system based on selected driver */ snprintf(thread_name, 16, "jamrouter%c-clnt", ('0' + jamrouter_instance)); pthread_setname_np(pthread_self(), thread_name); init_jack_audio(); while (sample_rate == 0) { JAMROUTER_DEBUG(DEBUG_CLASS_INIT, "JACK did not set sample rate. Re-initializing...\n"); init_jack_audio(); usleep(125000); } /* start the JACK audio system */ start_jack_audio(); /* wait for JACK audio to start before starting midi. */ wait_jack_audio_start(); snprintf(thread_name, 16, "jamrouter%c-main", ('0' + jamrouter_instance)); pthread_setname_np(pthread_self(), thread_name); /* start MIDI Rx/Tx threads. */ start_midi_rx(); wait_midi_rx_start(); start_midi_tx(); wait_midi_tx_start(); /* debug thread not needed once watchdog is running */ debug_done = 1; pthread_join(debug_thread_p, NULL); /* Jamrouter watchdog handles restarting threads on config changes, runs driver supplied watchdog loop iterations, and handles debug output. */ jamrouter_watchdog(); stop_midi_tx(); stop_midi_rx(); stop_jack_audio(); output_pending_debug(); /* Wait for threads created directly by JAMROUTER to terminate. */ if (midi_rx_thread_p != 0) { pthread_join(midi_rx_thread_p, NULL); } if (midi_tx_thread_p != 0) { pthread_join(midi_tx_thread_p, NULL); } output_pending_debug(); return 0; }