/* * transport.c -- JACK transport master example client. * * Copyright (C) 2003 Jack O'Quin. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include char *package; /* program name */ int done = 0; jack_client_t *client; /* Time and tempo variables. These are global to the entire, * transport timeline. There is no attempt to keep a true tempo map. * The default time signature is: "march time", 4/4, 120bpm */ float time_beats_per_bar = 4.0; float time_beat_type = 4.0; double time_ticks_per_beat = 1920.0; double time_beats_per_minute = 120.0; volatile int time_reset = 1; /* true when time values change */ volatile int avr_set = 0; float audio_frames_per_video_frame; /* JACK timebase callback. * * Runs in the process thread. Realtime, must not wait. */ void timebase(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg) { double min; /* minutes since frame 0 */ long abs_tick; /* ticks since frame 0 */ long abs_beat; /* beats since frame 0 */ if (new_pos || time_reset) { pos->valid = JackPositionBBT; pos->beats_per_bar = time_beats_per_bar; pos->beat_type = time_beat_type; pos->ticks_per_beat = time_ticks_per_beat; pos->beats_per_minute = time_beats_per_minute; time_reset = 0; /* time change complete */ /* Compute BBT info from frame number. This is relatively * simple here, but would become complex if we supported tempo * or time signature changes at specific locations in the * transport timeline. */ min = pos->frame / ((double) pos->frame_rate * 60.0); abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat; abs_beat = abs_tick / pos->ticks_per_beat; pos->bar = abs_beat / pos->beats_per_bar; pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1; pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat); pos->bar_start_tick = pos->bar * pos->beats_per_bar * pos->ticks_per_beat; pos->bar++; /* adjust start to bar 1 */ #if 0 /* some debug code... */ fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3" PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n", pos->frame, pos->bar, pos->beat, pos->tick); #endif } else { /* Compute BBT info based on previous period. */ pos->tick += nframes * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60); while (pos->tick >= pos->ticks_per_beat) { pos->tick -= pos->ticks_per_beat; if (++pos->beat > pos->beats_per_bar) { pos->beat = 1; ++pos->bar; pos->bar_start_tick += pos->beats_per_bar * pos->ticks_per_beat; } } } if (avr_set) { pos->valid |= JackAudioVideoRatio; pos->audio_frames_per_video_frame = audio_frames_per_video_frame; } } void jack_shutdown(void *arg) { #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400 rl_cleanup_after_signal(); #endif fprintf(stderr, "JACK shut down, exiting ...\n"); exit(1); } void signal_handler(int sig) { jack_client_close(client); fprintf(stderr, "signal received, exiting ...\n"); exit(0); } /* Command functions: see commands[] table following. */ void com_activate(char *arg) { if (jack_activate(client)) { fprintf(stderr, "cannot activate client"); } } void com_deactivate(char *arg) { if (jack_deactivate(client)) { fprintf(stderr, "cannot deactivate client"); } } void com_exit(char *arg) { done = 1; } void com_help(char *); /* forward declaration */ void com_locate(char *arg) { jack_nframes_t frame = 0; if (*arg != '\0') frame = atoi(arg); jack_transport_locate(client, frame); } void com_master(char *arg) { int cond = (*arg != '\0'); if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0) fprintf(stderr, "Unable to take over timebase.\n"); } void com_play(char *arg) { jack_transport_start(client); } void com_release(char *arg) { jack_release_timebase(client); } void com_stop(char *arg) { jack_transport_stop(client); } /* Change the tempo for the entire timeline, not just from the current * location. */ void com_tempo(char *arg) { float tempo = 120.0; if (*arg != '\0') tempo = atof(arg); time_beats_per_minute = tempo; time_reset = 1; } /* Set sync timeout in seconds. */ void com_timeout(char *arg) { double timeout = 2.0; if (*arg != '\0') timeout = atof(arg); jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000)); } /* Change the tempo for the entire timeline, not just from the current * location. */ void com_av_ratio(char *arg) { float avr = 0; if (*arg != '\0') avr = atof(arg); audio_frames_per_video_frame = avr; avr_set = 1; } /* Command parsing based on GNU readline info examples. */ typedef void cmd_function_t(char *); /* command function type */ /* Transport command table. */ typedef struct { char *name; /* user printable name */ cmd_function_t *func; /* function to call */ char *doc; /* documentation */ } command_t; /* command table must be in alphabetical order */ command_t commands[] = { {"activate", com_activate, "Call jack_activate()"}, {"avr", com_av_ratio, "Set audio/video frame ratio