/* Copyright (C) 2002 Anthony Van Groningen 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 #ifndef WIN32 #include #endif #include #include #include #include #include #include typedef jack_default_audio_sample_t sample_t; const double PI = 3.14; jack_client_t *client; jack_port_t *output_port; unsigned long sr; int freq = 880; int bpm; jack_nframes_t tone_length, wave_length; sample_t *wave; long offset = 0; int transport_aware = 0; jack_transport_state_t transport_state; static void signal_handler(int sig) { jack_client_close(client); fprintf(stderr, "signal received, exiting ...\n"); exit(0); } static void usage () { fprintf (stderr, "\n" "usage: jack_metro \n" " [ --frequency OR -f frequency (in Hz) ]\n" " [ --amplitude OR -A maximum amplitude (between 0 and 1) ]\n" " [ --duration OR -D duration (in ms) ]\n" " [ --attack OR -a attack (in percent of duration) ]\n" " [ --decay OR -d decay (in percent of duration) ]\n" " [ --name OR -n jack name for metronome client ]\n" " [ --transport OR -t transport aware ]\n" " --bpm OR -b beats per minute\n" ); } static void process_silence (jack_nframes_t nframes) { sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes); memset (buffer, 0, sizeof (jack_default_audio_sample_t) * nframes); } jack_nframes_t last_time; jack_time_t last_micro_time; static void process_audio (jack_nframes_t nframes) { sample_t *buffer = (sample_t *) jack_port_get_buffer (output_port, nframes); jack_nframes_t frames_left = nframes; while (wave_length - offset < frames_left) { memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * (wave_length - offset)); frames_left -= wave_length - offset; offset = 0; } if (frames_left > 0) { memcpy (buffer + (nframes - frames_left), wave + offset, sizeof (sample_t) * frames_left); offset += frames_left; } /* jack_nframes_t cur_time = jack_frame_time(client); jack_time_t cur_micro_time = jack_get_time(); printf("jack_frame_time %lld micro %lld delta %d\n", cur_time, (cur_micro_time - last_micro_time), cur_time - last_time); last_time = cur_time; last_micro_time = cur_micro_time; */ } static int process (jack_nframes_t nframes, void *arg) { if (transport_aware) { jack_position_t pos; if (jack_transport_query (client, &pos) != JackTransportRolling) { process_silence (nframes); return 0; } offset = pos.frame % wave_length; } process_audio (nframes); return 0; } int main (int argc, char *argv[]) { sample_t scale; int i, attack_length, decay_length; double *amp; double max_amp = 0.5; int option_index; int opt; int got_bpm = 0; int attack_percent = 1, decay_percent = 10, dur_arg = 100; char *client_name = 0; char *bpm_string = "bpm"; jack_status_t status; const char *options = "f:A:D:a:d:b:n:thv"; struct option long_options[] = { {"frequency", 1, 0, 'f'}, {"amplitude", 1, 0, 'A'}, {"duration", 1, 0, 'D'}, {"attack", 1, 0, 'a'}, {"decay", 1, 0, 'd'}, {"bpm", 1, 0, 'b'}, {"name", 1, 0, 'n'}, {"transport", 0, 0, 't'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != -1) { switch (opt) { case 'f': if ((freq = atoi (optarg)) <= 0) { fprintf (stderr, "invalid frequency\n"); return -1; } break; case 'A': if (((max_amp = atof (optarg)) <= 0)|| (max_amp > 1)) { fprintf (stderr, "invalid amplitude\n"); return -1; } break; case 'D': dur_arg = atoi (optarg); fprintf (stderr, "durarg = %u\n", dur_arg); break; case 'a': if (((attack_percent = atoi (optarg)) < 0) || (attack_percent > 100)) { fprintf (stderr, "invalid attack percent\n"); return -1; } break; case 'd': if (((decay_percent = atoi (optarg)) < 0) || (decay_percent > 100)) { fprintf (stderr, "invalid decay percent\n"); return -1; } break; case 'b': got_bpm = 1; if ((bpm = atoi (optarg)) < 0) { fprintf (stderr, "invalid bpm\n"); return -1; } bpm_string = (char *) malloc ((strlen (optarg) + 5) * sizeof (char)); strcpy (bpm_string, optarg); strcat (bpm_string, "_bpm"); break; case 'n': client_name = (char *) malloc ((strlen (optarg) + 1) * sizeof (char)); strcpy (client_name, optarg); break; case 't': transport_aware = 1; break; default: fprintf (stderr, "unknown option %c\n", opt); case 'h': usage (); return -1; } } if (!got_bpm) { fprintf (stderr, "bpm not specified\n"); usage (); return -1; } /* Initial Jack setup, get sample rate */ if (!client_name) { client_name = (char *) malloc (9 * sizeof (char)); strcpy (client_name, "metro"); } if ((client = jack_client_open (client_name, JackNoStartServer, &status)) == 0) { fprintf (stderr, "JACK server not running?\n"); return 1; } jack_set_process_callback (client, process, 0); output_port = jack_port_register (client, bpm_string, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); sr = jack_get_sample_rate (client); /* setup wave table parameters */ wave_length = 60 * sr / bpm; tone_length = sr * dur_arg / 1000; attack_length = tone_length * attack_percent / 100; decay_length = tone_length * decay_percent / 100; scale = 2 * PI * freq / sr; if (tone_length >= wave_length) { fprintf (stderr, "invalid duration (tone length = %u, wave length = %u\n", tone_length, wave_length); return -1; } if (attack_length + decay_length > (int)tone_length) { fprintf (stderr, "invalid attack/decay\n"); return -1; } /* Build the wave table */ wave = (sample_t *) malloc (wave_length * sizeof(sample_t)); amp = (double *) malloc (tone_length * sizeof(double)); for (i = 0; i < attack_length; i++) { amp[i] = max_amp * i / ((double) attack_length); } for (i = attack_length; i < (int)tone_length - decay_length; i++) { amp[i] = max_amp; } for (i = (int)tone_length - decay_length; i < (int)tone_length; i++) { amp[i] = - max_amp * (i - (double) tone_length) / ((double) decay_length); } for (i = 0; i < (int)tone_length; i++) { wave[i] = amp[i] * sin (scale * i); } for (i = tone_length; i < (int)wave_length; i++) { wave[i] = 0; } if (jack_activate (client)) { fprintf (stderr, "cannot activate client\n"); goto error; } /* install a signal handler to properly quits jack client */ #ifdef WIN32 signal(SIGINT, signal_handler); signal(SIGABRT, signal_handler); signal(SIGTERM, signal_handler); #else signal(SIGQUIT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGINT, signal_handler); #endif /* run until interrupted */ while (1) { #ifdef WIN32 Sleep(1000); #else sleep(1); #endif }; jack_client_close(client); error: free(amp); free(wave); exit (0); }