Signed-off-by: falkTX <falktx@falktx.com>pull/876/head
@@ -1,353 +0,0 @@ | |||
/* | |||
Copyright (C) 2001 Paul Davis | |||
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. | |||
* 2002/08/23 - modify for libsndfile 1.0.0 <andy@alsaplayer.org> | |||
* 2003/05/26 - use ringbuffers - joq | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <sndfile.h> | |||
#include <pthread.h> | |||
#include <signal.h> | |||
#include <getopt.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/ringbuffer.h> | |||
typedef struct _thread_info { | |||
pthread_t thread_id; | |||
SNDFILE *sf; | |||
jack_nframes_t duration; | |||
jack_nframes_t rb_size; | |||
jack_client_t *client; | |||
unsigned int channels; | |||
int bitdepth; | |||
char *path; | |||
volatile int can_capture; | |||
volatile int can_process; | |||
volatile int status; | |||
} jack_thread_info_t; | |||
/* JACK data */ | |||
unsigned int nports; | |||
jack_port_t **ports; | |||
jack_default_audio_sample_t **in; | |||
jack_nframes_t nframes; | |||
const size_t sample_size = sizeof(jack_default_audio_sample_t); | |||
/* Synchronization between process thread and disk thread. */ | |||
#define DEFAULT_RB_SIZE 16384 /* ringbuffer size in frames */ | |||
jack_ringbuffer_t *rb; | |||
pthread_mutex_t disk_thread_lock = PTHREAD_MUTEX_INITIALIZER; | |||
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER; | |||
long overruns = 0; | |||
jack_client_t *client; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void * | |||
disk_thread (void *arg) | |||
{ | |||
jack_thread_info_t *info = (jack_thread_info_t *) arg; | |||
static jack_nframes_t total_captured = 0; | |||
jack_nframes_t samples_per_frame = info->channels; | |||
size_t bytes_per_frame = samples_per_frame * sample_size; | |||
void *framebuf = malloc (bytes_per_frame); | |||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | |||
pthread_mutex_lock (&disk_thread_lock); | |||
info->status = 0; | |||
while (1) { | |||
/* Write the data one frame at a time. This is | |||
* inefficient, but makes things simpler. */ | |||
while (info->can_capture && | |||
(jack_ringbuffer_read_space (rb) >= bytes_per_frame)) { | |||
jack_ringbuffer_read (rb, framebuf, bytes_per_frame); | |||
if (sf_writef_float (info->sf, framebuf, 1) != 1) { | |||
char errstr[256]; | |||
sf_error_str (0, errstr, sizeof (errstr) - 1); | |||
fprintf (stderr, | |||
"cannot write sndfile (%s)\n", | |||
errstr); | |||
info->status = EIO; /* write failed */ | |||
goto done; | |||
} | |||
if (++total_captured >= info->duration) { | |||
printf ("disk thread finished\n"); | |||
goto done; | |||
} | |||
} | |||
/* wait until process() signals more data */ | |||
pthread_cond_wait (&data_ready, &disk_thread_lock); | |||
} | |||
done: | |||
pthread_mutex_unlock (&disk_thread_lock); | |||
free (framebuf); | |||
return 0; | |||
} | |||
static int | |||
process (jack_nframes_t nframes, void *arg) | |||
{ | |||
int chn; | |||
size_t i; | |||
jack_thread_info_t *info = (jack_thread_info_t *) arg; | |||
/* Do nothing until we're ready to begin. */ | |||
if ((!info->can_process) || (!info->can_capture)) | |||
return 0; | |||
for (chn = 0; chn < nports; chn++) | |||
in[chn] = jack_port_get_buffer (ports[chn], nframes); | |||
/* Sndfile requires interleaved data. It is simpler here to | |||
* just queue interleaved samples to a single ringbuffer. */ | |||
for (i = 0; i < nframes; i++) { | |||
for (chn = 0; chn < nports; chn++) { | |||
if (jack_ringbuffer_write (rb, (void *) (in[chn]+i), | |||
sample_size) | |||
< sample_size) | |||
overruns++; | |||
} | |||
} | |||
/* Tell the disk thread there is work to do. If it is already | |||
* running, the lock will not be available. We can't wait | |||
* here in the process() thread, but we don't need to signal | |||
* in that case, because the disk thread will read all the | |||
* data queued before waiting again. */ | |||
if (pthread_mutex_trylock (&disk_thread_lock) == 0) { | |||
pthread_cond_signal (&data_ready); | |||
pthread_mutex_unlock (&disk_thread_lock); | |||
} | |||
return 0; | |||
} | |||
static void | |||
jack_shutdown (void *arg) | |||
{ | |||
fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
exit(1); | |||
} | |||
static void | |||
setup_disk_thread (jack_thread_info_t *info) | |||
{ | |||
SF_INFO sf_info; | |||
int short_mask; | |||
sf_info.samplerate = jack_get_sample_rate (info->client); | |||
sf_info.channels = info->channels; | |||
switch (info->bitdepth) { | |||
case 8: short_mask = SF_FORMAT_PCM_U8; | |||
break; | |||
case 16: short_mask = SF_FORMAT_PCM_16; | |||
break; | |||
case 24: short_mask = SF_FORMAT_PCM_24; | |||
break; | |||
case 32: short_mask = SF_FORMAT_PCM_32; | |||
break; | |||
default: short_mask = SF_FORMAT_PCM_16; | |||
break; | |||
} | |||
sf_info.format = SF_FORMAT_WAV|short_mask; | |||
if ((info->sf = sf_open (info->path, SFM_WRITE, &sf_info)) == NULL) { | |||
char errstr[256]; | |||
sf_error_str (0, errstr, sizeof (errstr) - 1); | |||
fprintf (stderr, "cannot open sndfile \"%s\" for output (%s)\n", info->path, errstr); | |||
jack_client_close (info->client); | |||
exit (1); | |||
} | |||
info->duration *= sf_info.samplerate; | |||
info->can_capture = 0; | |||
pthread_create (&info->thread_id, NULL, disk_thread, info); | |||
} | |||
static void | |||
run_disk_thread (jack_thread_info_t *info) | |||
{ | |||
info->can_capture = 1; | |||
pthread_join (info->thread_id, NULL); | |||
sf_close (info->sf); | |||
if (overruns > 0) { | |||
fprintf (stderr, | |||
"jackrec failed with %ld overruns.\n", overruns); | |||
fprintf (stderr, " try a bigger buffer than -B %" | |||
PRIu32 ".\n", info->rb_size); | |||
info->status = EPIPE; | |||
} | |||
} | |||
static void | |||
setup_ports (int sources, char *source_names[], jack_thread_info_t *info) | |||
{ | |||
unsigned int i; | |||
size_t in_size; | |||
/* Allocate data structures that depend on the number of ports. */ | |||
nports = sources; | |||
ports = (jack_port_t **) malloc (sizeof (jack_port_t *) * nports); | |||
in_size = nports * sizeof (jack_default_audio_sample_t *); | |||
in = (jack_default_audio_sample_t **) malloc (in_size); | |||
rb = jack_ringbuffer_create (nports * sample_size * info->rb_size); | |||
/* When JACK is running realtime, jack_activate() will have | |||
* called mlockall() to lock our pages into memory. But, we | |||
* still need to touch any newly allocated pages before | |||
* process() starts using them. Otherwise, a page fault could | |||
* create a delay that would force JACK to shut us down. */ | |||
memset(in, 0, in_size); | |||
memset(rb->buf, 0, rb->size); | |||
for (i = 0; i < nports; i++) { | |||
char name[64]; | |||
sprintf (name, "input%d", i+1); | |||
if ((ports[i] = jack_port_register (info->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)) == 0) { | |||
fprintf (stderr, "cannot register input port \"%s\"!\n", name); | |||
jack_client_close (info->client); | |||
exit (1); | |||
} | |||
} | |||
for (i = 0; i < nports; i++) { | |||
if (jack_connect (info->client, source_names[i], jack_port_name (ports[i]))) { | |||
fprintf (stderr, "cannot connect input port %s to %s\n", jack_port_name (ports[i]), source_names[i]); | |||
jack_client_close (info->client); | |||
exit (1); | |||
} | |||
} | |||
info->can_process = 1; /* process() can start, now */ | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_thread_info_t thread_info; | |||
int c; | |||
int longopt_index = 0; | |||
extern int optind, opterr; | |||
int show_usage = 0; | |||
char *optstring = "d:f:b:B:h"; | |||
struct option long_options[] = { | |||
{ "help", 0, 0, 'h' }, | |||
{ "duration", 1, 0, 'd' }, | |||
{ "file", 1, 0, 'f' }, | |||
{ "bitdepth", 1, 0, 'b' }, | |||
{ "bufsize", 1, 0, 'B' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
memset (&thread_info, 0, sizeof (thread_info)); | |||
thread_info.rb_size = DEFAULT_RB_SIZE; | |||
opterr = 0; | |||
while ((c = getopt_long (argc, argv, optstring, long_options, &longopt_index)) != -1) { | |||
switch (c) { | |||
case 1: | |||
/* getopt signals end of '-' options */ | |||
break; | |||
case 'h': | |||
show_usage++; | |||
break; | |||
case 'd': | |||
thread_info.duration = atoi (optarg); | |||
break; | |||
case 'f': | |||
thread_info.path = optarg; | |||
break; | |||
case 'b': | |||
thread_info.bitdepth = atoi (optarg); | |||
break; | |||
case 'B': | |||
thread_info.rb_size = atoi (optarg); | |||
break; | |||
default: | |||
fprintf (stderr, "error\n"); | |||
show_usage++; | |||
break; | |||
} | |||
} | |||
if (show_usage || thread_info.path == NULL || optind == argc) { | |||
fprintf (stderr, "usage: jackrec -f filename [ -d second ] [ -b bitdepth ] [ -B bufsize ] port1 [ port2 ... ]\n"); | |||
exit (1); | |||
} | |||
if ((client = jack_client_open ("jackrec", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
exit (1); | |||
} | |||
thread_info.client = client; | |||
thread_info.channels = argc - optind; | |||
thread_info.can_process = 0; | |||
setup_disk_thread (&thread_info); | |||
jack_set_process_callback (client, process, &thread_info); | |||
jack_on_shutdown (client, jack_shutdown, &thread_info); | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
} | |||
setup_ports (argc - optind, &argv[optind], &thread_info); | |||
/* install a signal handler to properly quits jack client */ | |||
#ifndef WIN32 | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
run_disk_thread (&thread_info); | |||
jack_client_close (client); | |||
jack_ringbuffer_free (rb); | |||
exit (0); | |||
} |
@@ -1,75 +0,0 @@ | |||
/** @file control.c | |||
* | |||
* @brief This simple client demonstrates the basic features of JACK | |||
* as they would be used by many applications. | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#include <math.h> | |||
#include <jack/jack.h> | |||
jack_client_t *client; | |||
static int reorder = 0; | |||
static int Jack_Graph_Order_Callback(void *arg) | |||
{ | |||
const char **ports; | |||
int i; | |||
printf("Jack_Graph_Order_Callback count = %d\n", reorder++); | |||
ports = jack_get_ports(client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); | |||
if (ports) { | |||
for (i = 0; ports[i]; ++i) { | |||
printf("name: %s\n", ports[i]); | |||
} | |||
jack_free(ports); | |||
} | |||
ports = jack_get_ports(client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); | |||
if (ports) { | |||
for (i = 0; ports[i]; ++i) { | |||
printf("name: %s\n", ports[i]); | |||
} | |||
jack_free(ports); | |||
} | |||
return 0; | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
/* open a client connection to the JACK server */ | |||
client = jack_client_open("control_client", options, &status); | |||
if (client == NULL) { | |||
printf("jack_client_open() failed \n"); | |||
exit(1); | |||
} | |||
if (jack_set_graph_order_callback(client, Jack_Graph_Order_Callback, 0) != 0) { | |||
printf("Error when calling jack_set_graph_order_callback() !\n"); | |||
} | |||
/* Tell the JACK server that we are ready to roll. Our | |||
* process() callback will start running now. */ | |||
if (jack_activate(client)) { | |||
printf("cannot activate client"); | |||
exit(1); | |||
} | |||
printf("Type 'q' to quit\n"); | |||
while ((getchar() != 'q')) {} | |||
jack_client_close(client); | |||
exit (0); | |||
} |
@@ -1,88 +0,0 @@ | |||
/** @file cpu_load.c | |||
* | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#include <signal.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <jack/jack.h> | |||
jack_client_t *client; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
/** | |||
* JACK calls this shutdown_callback if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
void | |||
jack_shutdown(void *arg) | |||
{ | |||
exit(1); | |||
} | |||
int | |||
main(int argc, char *argv[]) | |||
{ | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
/* open a client connection to the JACK server */ | |||
client = jack_client_open ("jack_cpu_load", options, &status); | |||
if (client == NULL) { | |||
fprintf(stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
if (status & JackServerFailed) { | |||
fprintf(stderr, "Unable to connect to JACK server\n"); | |||
} | |||
exit(1); | |||
} | |||
jack_on_shutdown(client, jack_shutdown, 0); | |||
/* Tell the JACK server that we are ready to roll. Our | |||
* process() callback will start running now. */ | |||
if (jack_activate(client)) { | |||
fprintf(stderr, "cannot activate client"); | |||
exit(1); | |||
} | |||
/* 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 | |||
while (1) { | |||
printf("jack DSP load %f\n", jack_cpu_load(client)); | |||
#ifdef WIN32 | |||
Sleep(1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
} | |||
jack_client_close(client); | |||
exit(0 ); | |||
} |
@@ -1,247 +0,0 @@ | |||
/* | |||
* Copyright (C) 2001 Steve Harris | |||
* | |||
* 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 <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <signal.h> | |||
#include <math.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
unsigned int impulse_sent = 0; | |||
float *response; | |||
unsigned long response_duration; | |||
unsigned long response_pos; | |||
int grab_finished = 0; | |||
jack_client_t *client; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static int | |||
process (jack_nframes_t nframes, void *arg) | |||
{ | |||
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); | |||
jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); | |||
unsigned int i; | |||
if (grab_finished) { | |||
return 0; | |||
} else if (impulse_sent) { | |||
for(i=0; i<nframes && response_pos < response_duration; i++) { | |||
response[response_pos++] = in[i]; | |||
} | |||
if (response_pos >= response_duration) { | |||
grab_finished = 1; | |||
} | |||
for (i=0; i<nframes; i++) { | |||
out[i] = 0.0f;; | |||
} | |||
} else { | |||
out[0] = 1.0f; | |||
for (i=1; i<nframes; i++) { | |||
out[i] = 0.0f; | |||
} | |||
impulse_sent = 1; | |||
} | |||
return 0; | |||
} | |||
static void | |||
jack_shutdown (void *arg) | |||
{ | |||
fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
exit (1); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
const char **ports; | |||
float fs; // The sample rate | |||
float peak; | |||
unsigned long peak_sample; | |||
unsigned int i; | |||
float duration = 0.0f; | |||
unsigned int c_format = 0; | |||
int longopt_index = 0; | |||
int c; | |||
extern int optind, opterr; | |||
int show_usage = 0; | |||
char *optstring = "d:f:h"; | |||
struct option long_options[] = { | |||
{ "help", 1, 0, 'h' }, | |||
{ "duration", 1, 0, 'd' }, | |||
{ "format", 1, 0, 'f' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
while ((c = getopt_long (argc, argv, optstring, long_options, &longopt_index)) != -1) { | |||
switch (c) { | |||
case 1: | |||
// end of opts, but don't care | |||
break; | |||
case 'h': | |||
show_usage++; | |||
break; | |||
case 'd': | |||
duration = (float)atof(optarg); | |||
break; | |||
case 'f': | |||
if (*optarg == 'c' || *optarg == 'C') { | |||
c_format = 1; | |||
} | |||
break; | |||
default: | |||
show_usage++; | |||
break; | |||
} | |||
} | |||
if (show_usage || duration <= 0.0f) { | |||
fprintf(stderr, "usage: jack_impulse_grab -d duration [-f (C|gnuplot)]\n"); | |||
exit(1); | |||
} | |||
/* try to become a client of the JACK server */ | |||
if ((client = jack_client_open("impulse_grabber", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
/* tell the JACK server to call `process()' whenever | |||
there is work to be done. | |||
*/ | |||
jack_set_process_callback (client, process, 0); | |||
/* tell the JACK server to call `jack_shutdown()' if | |||
it ever shuts down, either entirely, or if it | |||
just decides to stop calling us. | |||
*/ | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
/* display the current sample rate. once the client is activated | |||
(see below), you should rely on your own sample rate | |||
callback (see above) for this value. | |||
*/ | |||
fs = jack_get_sample_rate(client); | |||
response_duration = (unsigned long) (fs * duration); | |||
response = malloc(response_duration * sizeof(float)); | |||
fprintf(stderr, | |||
"Grabbing %f seconds (%lu samples) of impulse response\n", | |||
duration, response_duration); | |||
/* create two ports */ | |||
input_port = jack_port_register (client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||
output_port = jack_port_register (client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
/* connect the ports. Note: you can't do this before | |||
the client is activated (this may change in the future). | |||
*/ | |||
if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) { | |||
fprintf(stderr, "Cannot find any physical capture ports"); | |||
exit(1); | |||
} | |||
if (jack_connect (client, ports[0], jack_port_name (input_port))) { | |||
fprintf (stderr, "cannot connect input ports\n"); | |||
} | |||
free (ports); | |||
if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) { | |||
fprintf(stderr, "Cannot find any physical playback ports"); | |||
exit(1); | |||
} | |||
if (jack_connect (client, jack_port_name (output_port), ports[0])) { | |||
fprintf (stderr, "cannot connect output ports\n"); | |||
} | |||
free (ports); | |||
/* install a signal handler to properly quits jack client */ | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
/* Wait for grab to finish */ | |||
while (!grab_finished) { | |||
sleep (1); | |||
} | |||
jack_client_close (client); | |||
peak = response[0]; | |||
peak_sample = 0; | |||
if (c_format) { | |||
printf("impulse[%lu] = {", response_duration); | |||
for (i=0; i<response_duration; i++) { | |||
if (i % 4 != 0) { | |||
printf(" "); | |||
} else { | |||
printf("\n\t"); | |||
} | |||
printf("\"%+1.10f\"", response[i]); | |||
if (i < response_duration - 1) { | |||
printf(","); | |||
} | |||
if (fabs(response[i]) > peak) { | |||
peak = fabs(response[i]); | |||
peak_sample = i; | |||
} | |||
} | |||
printf("\n};\n"); | |||
} else { | |||
for (i=0; i<response_duration; i++) { | |||
printf("%1.12f\n", response[i]); | |||
if (fabs(response[i]) > peak) { | |||
peak = fabs(response[i]); | |||
peak_sample = i; | |||
} | |||
} | |||
} | |||
fprintf(stderr, "Peak value was %f at sample %lu\n", peak, peak_sample); | |||
exit (0); | |||
} |
@@ -1,125 +0,0 @@ | |||
/** @file inprocess.c | |||
* | |||
* @brief This demonstrates the basic concepts for writing a client | |||
* that runs within the JACK server process. | |||
* | |||
* For the sake of example, a port_pair_t is allocated in | |||
* jack_initialize(), passed to inprocess() as an argument, then freed | |||
* in jack_finish(). | |||
*/ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <memory.h> | |||
#include <jack/jack.h> | |||
/** | |||
* For the sake of example, an instance of this struct is allocated in | |||
* jack_initialize(), passed to inprocess() as an argument, then freed | |||
* in jack_finish(). | |||
*/ | |||
typedef struct { | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
} port_pair_t; | |||
/** | |||
* Called in the realtime thread on every process cycle. The entry | |||
* point name was passed to jack_set_process_callback() from | |||
* jack_initialize(). Although this is an internal client, its | |||
* process() interface is identical to @ref simple_client.c. | |||
* | |||
* @return 0 if successful; otherwise jack_finish() will be called and | |||
* the client terminated immediately. | |||
*/ | |||
int | |||
inprocess (jack_nframes_t nframes, void *arg) | |||
{ | |||
port_pair_t *pp = arg; | |||
jack_default_audio_sample_t *out = | |||
jack_port_get_buffer (pp->output_port, nframes); | |||
jack_default_audio_sample_t *in = | |||
jack_port_get_buffer (pp->input_port, nframes); | |||
memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); | |||
return 0; /* continue */ | |||
} | |||
/** | |||
* This required entry point is called after the client is loaded by | |||
* jack_internal_client_load(). | |||
* | |||
* @param client pointer to JACK client structure. | |||
* @param load_init character string passed to the load operation. | |||
* | |||
* @return 0 if successful; otherwise jack_finish() will be called and | |||
* the client terminated immediately. | |||
*/ | |||
JACK_LIB_EXPORT | |||
int | |||
jack_initialize (jack_client_t *client, const char *load_init) | |||
{ | |||
port_pair_t *pp = malloc (sizeof (port_pair_t)); | |||
const char **ports; | |||
if (pp == NULL) | |||
return 1; /* heap exhausted */ | |||
jack_set_process_callback (client, inprocess, pp); | |||
/* create a pair of ports */ | |||
pp->input_port = jack_port_register (client, "input", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsInput, 0); | |||
pp->output_port = jack_port_register (client, "output", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsOutput, 0); | |||
/* join the process() cycle */ | |||
jack_activate (client); | |||
ports = jack_get_ports (client, NULL, NULL, | |||
JackPortIsPhysical|JackPortIsOutput); | |||
if (ports == NULL) { | |||
fprintf(stderr, "no physical capture ports\n"); | |||
return 1; /* terminate client */ | |||
} | |||
if (jack_connect (client, ports[0], jack_port_name (pp->input_port))) { | |||
fprintf (stderr, "cannot connect input ports\n"); | |||
} | |||
jack_free (ports); | |||
ports = jack_get_ports (client, NULL, NULL, | |||
JackPortIsPhysical|JackPortIsInput); | |||
if (ports == NULL) { | |||
fprintf(stderr, "no physical playback ports\n"); | |||
return 1; /* terminate client */ | |||
} | |||
if (jack_connect (client, jack_port_name (pp->output_port), ports[0])) { | |||
fprintf (stderr, "cannot connect output ports\n"); | |||
} | |||
jack_free (ports); | |||
return 0; /* success */ | |||
} | |||
/** | |||
* This required entry point is called immediately before the client | |||
* is unloaded, which could happen due to a call to | |||
* jack_internal_client_unload(), or a nonzero return from either | |||
* jack_initialize() or inprocess(). | |||
* | |||
* @param arg the same parameter provided to inprocess(). | |||
*/ | |||
JACK_LIB_EXPORT | |||
void | |||
jack_finish (void *arg) | |||
{ | |||
if (arg) | |||
free ((port_pair_t *) arg); | |||
} |
@@ -1,123 +0,0 @@ | |||
/* | |||
Copyright (C) 2002 Anthony Van Groningen | |||
Copyright (C) 2005 Grame | |||
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 "internal_metro.h" | |||
typedef jack_default_audio_sample_t sample_t; | |||
const double PI = 3.14; | |||
static int process_audio (jack_nframes_t nframes, void* arg) | |||
{ | |||
InternalMetro* metro = (InternalMetro*)arg; | |||
sample_t *buffer = (sample_t *) jack_port_get_buffer (metro->output_port, nframes); | |||
jack_nframes_t frames_left = nframes; | |||
while (metro->wave_length - metro->offset < frames_left) { | |||
memcpy (buffer + (nframes - frames_left), metro->wave + metro->offset, sizeof (sample_t) * (metro->wave_length - metro->offset)); | |||
frames_left -= metro->wave_length - metro->offset; | |||
metro->offset = 0; | |||
} | |||
if (frames_left > 0) { | |||
memcpy (buffer + (nframes - frames_left), metro->wave + metro->offset, sizeof (sample_t) * frames_left); | |||
metro->offset += frames_left; | |||
} | |||
return 0; | |||
} | |||
InternalMetro::InternalMetro(int freq, double max_amp, int dur_arg, int bpm, char* client_name) | |||
{ | |||
sample_t scale; | |||
int i, attack_length, decay_length; | |||
int attack_percent = 1, decay_percent = 10; | |||
const char *bpm_string = "bpm"; | |||
offset = 0; | |||
/* 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, JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "jack server not running?\n"); | |||
return; | |||
} | |||
jack_set_process_callback (client, process_audio, this); | |||
output_port = jack_port_register (client, bpm_string, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
input_port = jack_port_register (client, "metro_in", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 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 = %" PRIu32 | |||
", wave length = %" PRIu32 "\n", tone_length, | |||
wave_length); | |||
*/ | |||
return; | |||
} | |||
if (attack_length + decay_length > (int)tone_length) { | |||
fprintf (stderr, "invalid attack/decay\n"); | |||
return; | |||
} | |||
/* 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"); | |||
} | |||
} | |||
InternalMetro::~InternalMetro() | |||
{ | |||
jack_deactivate(client); | |||
jack_port_unregister(client, input_port); | |||
jack_port_unregister(client, output_port); | |||
jack_client_close(client); | |||
free(amp); | |||
free(wave); | |||
} |
@@ -1,68 +0,0 @@ | |||
/* | |||
Copyright (C) 2001 Paul Davis | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#ifndef __internal_metro__ | |||
#define __internal_metro__ | |||
#ifdef __cplusplus | |||
extern "C" | |||
{ | |||
#endif | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <math.h> | |||
#include <getopt.h> | |||
#include <string.h> | |||
#include "jack.h" | |||
#include "transport.h" | |||
typedef jack_default_audio_sample_t sample_t; | |||
/*! | |||
\brief A class to test internal clients | |||
*/ | |||
struct InternalMetro { | |||
jack_client_t *client; | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
unsigned long sr; | |||
int freq; | |||
int bpm; | |||
jack_nframes_t tone_length, wave_length; | |||
sample_t *wave; | |||
double *amp; | |||
long offset ; | |||
InternalMetro(int freq, double max_amp, int dur_arg, int bpm, char* client_name); | |||
virtual ~InternalMetro(); | |||
}; | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -1,217 +0,0 @@ | |||
/** @file latent_client.c | |||
* | |||
* @brief This simple client demonstrates the most basic features of JACK | |||
* as they would be used by many applications. | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
jack_client_t *client; | |||
jack_default_audio_sample_t *delay_line; | |||
jack_nframes_t delay_index; | |||
jack_nframes_t latency = 1024; | |||
#ifdef WIN32 | |||
#define jack_sleep(val) Sleep((val)) | |||
#else | |||
#define jack_sleep(val) usleep((val) * 1000) | |||
#endif | |||
/** | |||
* The process callback for this JACK application is called in a | |||
* special realtime thread once for each audio cycle. | |||
* | |||
* This client does nothing more than copy data from its input | |||
* port to its output port. It will exit when stopped by | |||
* the user (e.g. using Ctrl-C on a unix-ish operating system) | |||
*/ | |||
int | |||
process (jack_nframes_t nframes, void *arg) | |||
{ | |||
jack_default_audio_sample_t *in, *out; | |||
int k; | |||
in = jack_port_get_buffer (input_port, nframes); | |||
out = jack_port_get_buffer (output_port, nframes); | |||
for (k=0; k<nframes; k++) { | |||
out[k] = delay_line[delay_index]; | |||
delay_line[delay_index] = in[k]; | |||
delay_index = (delay_index + 1) % latency; | |||
} | |||
return 0; | |||
} | |||
void | |||
latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
{ | |||
jack_latency_range_t range; | |||
if (mode == JackCaptureLatency) { | |||
jack_port_get_latency_range (input_port, mode, &range); | |||
range.min += latency; | |||
range.max += latency; | |||
jack_port_set_latency_range (output_port, mode, &range); | |||
} else { | |||
jack_port_get_latency_range (output_port, mode, &range); | |||
range.min += latency; | |||
range.max += latency; | |||
jack_port_set_latency_range (input_port, mode, &range); | |||
} | |||
} | |||
/** | |||
* JACK calls this shutdown_callback if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
void | |||
jack_shutdown (void *arg) | |||
{ | |||
fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
exit (1); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
const char **ports; | |||
const char *client_name = "latent"; | |||
const char *server_name = NULL; | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
if (argc == 2) | |||
latency = atoi(argv[1]); | |||
delay_line = malloc( latency * sizeof(jack_default_audio_sample_t)); | |||
if (delay_line == NULL) { | |||
fprintf (stderr, "no memory"); | |||
exit(1); | |||
} | |||
memset (delay_line, 0, latency * sizeof(jack_default_audio_sample_t)); | |||
/* open a client connection to the JACK server */ | |||
client = jack_client_open (client_name, options, &status, server_name); | |||
if (client == NULL) { | |||
fprintf (stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
if (status & JackServerFailed) { | |||
fprintf (stderr, "Unable to connect to JACK server\n"); | |||
} | |||
exit (1); | |||
} | |||
if (status & JackServerStarted) { | |||
fprintf (stderr, "JACK server started\n"); | |||
} | |||
if (status & JackNameNotUnique) { | |||
client_name = jack_get_client_name(client); | |||
fprintf (stderr, "unique name `%s' assigned\n", client_name); | |||
} | |||
/* tell the JACK server to call `process()' whenever | |||
there is work to be done. | |||
*/ | |||
jack_set_process_callback (client, process, 0); | |||
/* tell the JACK server to call `latency()' whenever | |||
the latency needs to be recalculated. | |||
*/ | |||
if (jack_set_latency_callback) | |||
jack_set_latency_callback (client, latency_cb, 0); | |||
/* tell the JACK server to call `jack_shutdown()' if | |||
it ever shuts down, either entirely, or if it | |||
just decides to stop calling us. | |||
*/ | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
/* display the current sample rate. | |||
*/ | |||
printf ("engine sample rate: %" PRIu32 "\n", | |||
jack_get_sample_rate (client)); | |||
/* create two ports */ | |||
input_port = jack_port_register (client, "input", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsInput, 0); | |||
output_port = jack_port_register (client, "output", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsOutput, 0); | |||
if ((input_port == NULL) || (output_port == NULL)) { | |||
fprintf(stderr, "no more JACK ports available\n"); | |||
exit (1); | |||
} | |||
/* Tell the JACK server that we are ready to roll. Our | |||
* process() callback will start running now. */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
exit (1); | |||
} | |||
/* Connect the ports. You can't do this before the client is | |||
* activated, because we can't make connections to clients | |||
* that aren't running. Note the confusing (but necessary) | |||
* orientation of the driver backend ports: playback ports are | |||
* "input" to the backend, and capture ports are "output" from | |||
* it. | |||
*/ | |||
ports = jack_get_ports (client, NULL, NULL, | |||
JackPortIsPhysical|JackPortIsOutput); | |||
if (ports == NULL) { | |||
fprintf(stderr, "no physical capture ports\n"); | |||
exit (1); | |||
} | |||
if (jack_connect (client, ports[0], jack_port_name (input_port))) { | |||
fprintf (stderr, "cannot connect input ports\n"); | |||
} | |||
free (ports); | |||
ports = jack_get_ports (client, NULL, NULL, | |||
JackPortIsPhysical|JackPortIsInput); | |||
if (ports == NULL) { | |||
fprintf(stderr, "no physical playback ports\n"); | |||
exit (1); | |||
} | |||
if (jack_connect (client, jack_port_name (output_port), ports[0])) { | |||
fprintf (stderr, "cannot connect output ports\n"); | |||
} | |||
free (ports); | |||
/* keep running until stopped by the user */ | |||
jack_sleep (-1); | |||
/* this is never reached but if the program | |||
had some other way to exit besides being killed, | |||
they would be important to call. | |||
*/ | |||
jack_client_close (client); | |||
exit (0); | |||
} | |||
@@ -1,302 +0,0 @@ | |||
/* | |||
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 <stdlib.h> | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <math.h> | |||
#include <signal.h> | |||
#include <getopt.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
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"; | |||
int verbose = 0; | |||
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'}, | |||
{"verbose", 0, 0, 'v'}, | |||
{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 'v': | |||
verbose = 1; | |||
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); | |||
} |
@@ -1,969 +0,0 @@ | |||
/* | |||
Copyright (C) 2010 Devin Anderson | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
/* | |||
* This program is used to measure MIDI latency and jitter. It writes MIDI | |||
* messages to one port and calculates how long it takes before it reads the | |||
* same MIDI message over another port. It was written to calculate the | |||
* latency and jitter of hardware and JACK hardware drivers, but might have | |||
* other practical applications. | |||
* | |||
* The latency results of the program include the latency introduced by the | |||
* JACK system. Because JACK has sample accurate MIDI, the same latency | |||
* imposed on audio is also imposed on MIDI going through the system. Make | |||
* sure you take this into account before complaining to me or (*especially*) | |||
* other JACK developers about reported MIDI latency. | |||
* | |||
* The jitter results are a little more interesting. The program attempts to | |||
* calculate 'average jitter' and 'peak jitter', as defined here: | |||
* | |||
* http://openmuse.org/transport/fidelity.html | |||
* | |||
* It also outputs a jitter plot, which gives you a more specific idea about | |||
* the MIDI jitter for the ports you're testing. This is useful for catching | |||
* extreme jitter values, and for analyzing the amount of truth in the | |||
* technical specifications for your MIDI interface(s). :) | |||
* | |||
* This program is loosely based on 'alsa-midi-latency-test' in the ALSA test | |||
* suite. | |||
* | |||
* To port this program to non-POSIX platforms, you'll have to include | |||
* implementations for semaphores and command-line argument handling. | |||
*/ | |||
#include <assert.h> | |||
#include <errno.h> | |||
#include <math.h> | |||
#include <signal.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#ifdef WIN32 | |||
#include <windows.h> | |||
#include <unistd.h> | |||
#else | |||
#include <semaphore.h> | |||
#endif | |||
#define ABS(x) (((x) >= 0) ? (x) : (-(x))) | |||
#ifdef WIN32 | |||
typedef HANDLE semaphore_t; | |||
#else | |||
typedef sem_t *semaphore_t; | |||
#endif | |||
const char *ERROR_MSG_TIMEOUT = "timed out while waiting for MIDI message"; | |||
const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; | |||
const char *ERROR_SHUTDOWN = "the JACK server has been shutdown"; | |||
const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve"; | |||
const char *SOURCE_PROCESS = "handle_process"; | |||
const char *SOURCE_SHUTDOWN = "handle_shutdown"; | |||
const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore"; | |||
const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore"; | |||
char *alias1; | |||
char *alias2; | |||
jack_client_t *client; | |||
semaphore_t connect_semaphore; | |||
volatile int connections_established; | |||
const char *error_message; | |||
const char *error_source; | |||
jack_nframes_t highest_latency; | |||
jack_time_t highest_latency_time; | |||
jack_latency_range_t in_latency_range; | |||
jack_port_t *in_port; | |||
semaphore_t init_semaphore; | |||
jack_nframes_t last_activity; | |||
jack_time_t last_activity_time; | |||
jack_time_t *latency_time_values; | |||
jack_nframes_t *latency_values; | |||
jack_nframes_t lowest_latency; | |||
jack_time_t lowest_latency_time; | |||
jack_midi_data_t *message_1; | |||
jack_midi_data_t *message_2; | |||
int messages_received; | |||
int messages_sent; | |||
size_t message_size; | |||
jack_latency_range_t out_latency_range; | |||
jack_port_t *out_port; | |||
semaphore_t process_semaphore; | |||
volatile sig_atomic_t process_state; | |||
char *program_name; | |||
jack_port_t *remote_in_port; | |||
jack_port_t *remote_out_port; | |||
size_t samples; | |||
const char *target_in_port_name; | |||
const char *target_out_port_name; | |||
int timeout; | |||
jack_nframes_t total_latency; | |||
jack_time_t total_latency_time; | |||
int unexpected_messages; | |||
int xrun_count; | |||
#ifdef WIN32 | |||
char semaphore_error_msg[1024]; | |||
#endif | |||
static void | |||
output_error(const char *source, const char *message); | |||
static void | |||
output_usage(void); | |||
static void | |||
set_process_error(const char *source, const char *message); | |||
static int | |||
signal_semaphore(semaphore_t semaphore); | |||
static jack_port_t * | |||
update_connection(jack_port_t *remote_port, int connected, | |||
jack_port_t *local_port, jack_port_t *current_port, | |||
const char *target_name); | |||
static int | |||
wait_semaphore(semaphore_t semaphore, int block); | |||
static semaphore_t | |||
create_semaphore(int id) | |||
{ | |||
semaphore_t semaphore; | |||
#ifdef WIN32 | |||
semaphore = CreateSemaphore(NULL, 0, 2, NULL); | |||
#elif defined (__APPLE__) | |||
char name[128]; | |||
sprintf(name, "midi_sem_%d", id); | |||
semaphore = sem_open(name, O_CREAT, 0777, 0); | |||
if (semaphore == (sem_t *) SEM_FAILED) { | |||
semaphore = NULL; | |||
} | |||
#else | |||
semaphore = malloc(sizeof(sem_t)); | |||
if (semaphore != NULL) { | |||
if (sem_init(semaphore, 0, 0)) { | |||
free(semaphore); | |||
semaphore = NULL; | |||
} | |||
} | |||
#endif | |||
return semaphore; | |||
} | |||
static void | |||
destroy_semaphore(semaphore_t semaphore, int id) | |||
{ | |||
#ifdef WIN32 | |||
CloseHandle(semaphore); | |||
#else | |||
sem_destroy(semaphore); | |||
#ifdef __APPLE__ | |||
{ | |||
char name[128]; | |||
sprintf(name, "midi_sem_%d", id); | |||
sem_close(semaphore); | |||
sem_unlink(name); | |||
} | |||
#else | |||
free(semaphore); | |||
#endif | |||
#endif | |||
} | |||
static void | |||
die(const char *source, const char *error_message) | |||
{ | |||
output_error(source, error_message); | |||
output_usage(); | |||
exit(EXIT_FAILURE); | |||
} | |||
static const char * | |||
get_semaphore_error(void) | |||
{ | |||
#ifdef WIN32 | |||
DWORD error = GetLastError(); | |||
if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, | |||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |||
semaphore_error_msg, 1024, NULL)) { | |||
snprintf(semaphore_error_msg, 1023, "Unknown OS error code '%ld'", | |||
error); | |||
} | |||
return semaphore_error_msg; | |||
#else | |||
return strerror(errno); | |||
#endif | |||
} | |||
static void | |||
handle_info(const char *message) | |||
{ | |||
/* Suppress info */ | |||
} | |||
static void | |||
handle_port_connection_change(jack_port_id_t port_id_1, | |||
jack_port_id_t port_id_2, int connected, | |||
void *arg) | |||
{ | |||
jack_port_t *port_1; | |||
jack_port_t *port_2; | |||
if ((remote_in_port != NULL) && (remote_out_port != NULL)) { | |||
return; | |||
} | |||
port_1 = jack_port_by_id(client, port_id_1); | |||
port_2 = jack_port_by_id(client, port_id_2); | |||
/* The 'update_connection' call is not RT-safe. It calls | |||
'jack_port_get_connections' and 'jack_free'. This might be a problem | |||
with JACK 1, as this callback runs in the process thread in JACK 1. */ | |||
if (port_1 == in_port) { | |||
remote_in_port = update_connection(port_2, connected, in_port, | |||
remote_in_port, | |||
target_in_port_name); | |||
} else if (port_2 == in_port) { | |||
remote_in_port = update_connection(port_1, connected, in_port, | |||
remote_in_port, | |||
target_in_port_name); | |||
} else if (port_1 == out_port) { | |||
remote_out_port = update_connection(port_2, connected, out_port, | |||
remote_out_port, | |||
target_out_port_name); | |||
} else if (port_2 == out_port) { | |||
remote_out_port = update_connection(port_1, connected, out_port, | |||
remote_out_port, | |||
target_out_port_name); | |||
} | |||
if ((remote_in_port != NULL) && (remote_out_port != NULL)) { | |||
connections_established = 1; | |||
if (! signal_semaphore(connect_semaphore)) { | |||
/* Sigh ... */ | |||
die("post_semaphore", get_semaphore_error()); | |||
} | |||
if (! signal_semaphore(init_semaphore)) { | |||
/* Sigh ... */ | |||
die("post_semaphore", get_semaphore_error()); | |||
} | |||
} | |||
} | |||
static int | |||
handle_process(jack_nframes_t frames, void *arg) | |||
{ | |||
jack_midi_data_t *buffer; | |||
jack_midi_event_t event; | |||
jack_nframes_t event_count; | |||
jack_nframes_t event_time; | |||
jack_nframes_t frame; | |||
size_t i; | |||
jack_nframes_t last_frame_time; | |||
jack_midi_data_t *message; | |||
jack_time_t microseconds; | |||
void *port_buffer; | |||
jack_time_t time; | |||
jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames)); | |||
switch (process_state) { | |||
case 0: | |||
/* State: initializing */ | |||
switch (wait_semaphore(init_semaphore, 0)) { | |||
case -1: | |||
set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error()); | |||
/* Fallthrough on purpose */ | |||
case 0: | |||
return 0; | |||
} | |||
highest_latency = 0; | |||
lowest_latency = 0; | |||
messages_received = 0; | |||
messages_sent = 0; | |||
process_state = 1; | |||
total_latency = 0; | |||
total_latency_time = 0; | |||
unexpected_messages = 0; | |||
xrun_count = 0; | |||
jack_port_get_latency_range(remote_in_port, JackCaptureLatency, | |||
&in_latency_range); | |||
jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, | |||
&out_latency_range); | |||
goto send_message; | |||
case 1: | |||
/* State: processing */ | |||
port_buffer = jack_port_get_buffer(in_port, frames); | |||
event_count = jack_midi_get_event_count(port_buffer); | |||
last_frame_time = jack_last_frame_time(client); | |||
for (i = 0; i < event_count; i++) { | |||
jack_midi_event_get(&event, port_buffer, i); | |||
message = (messages_received % 2) ? message_2 : message_1; | |||
if ((event.size == message_size) && | |||
(! memcmp(message, event.buffer, | |||
message_size * sizeof(jack_midi_data_t)))) { | |||
goto found_message; | |||
} | |||
unexpected_messages++; | |||
} | |||
microseconds = jack_frames_to_time(client, last_frame_time) - | |||
last_activity_time; | |||
if ((microseconds / 1000000) >= timeout) { | |||
set_process_error(SOURCE_PROCESS, ERROR_MSG_TIMEOUT); | |||
} | |||
break; | |||
found_message: | |||
event_time = last_frame_time + event.time; | |||
frame = event_time - last_activity; | |||
time = jack_frames_to_time(client, event_time) - last_activity_time; | |||
if ((! highest_latency) || (frame > highest_latency)) { | |||
highest_latency = frame; | |||
highest_latency_time = time; | |||
} | |||
if ((! lowest_latency) || (frame < lowest_latency)) { | |||
lowest_latency = frame; | |||
lowest_latency_time = time; | |||
} | |||
latency_time_values[messages_received] = time; | |||
latency_values[messages_received] = frame; | |||
total_latency += frame; | |||
total_latency_time += time; | |||
messages_received++; | |||
if (messages_received == samples) { | |||
process_state = 2; | |||
if (! signal_semaphore(process_semaphore)) { | |||
/* Sigh ... */ | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
break; | |||
} | |||
send_message: | |||
frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames); | |||
if (frame >= frames) { | |||
frame = frames - 1; | |||
} | |||
port_buffer = jack_port_get_buffer(out_port, frames); | |||
buffer = jack_midi_event_reserve(port_buffer, frame, message_size); | |||
if (buffer == NULL) { | |||
set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE); | |||
break; | |||
} | |||
message = (messages_sent % 2) ? message_2 : message_1; | |||
memcpy(buffer, message, message_size * sizeof(jack_midi_data_t)); | |||
last_activity = jack_last_frame_time(client) + frame; | |||
last_activity_time = jack_frames_to_time(client, last_activity); | |||
messages_sent++; | |||
case 2: | |||
/* State: finished - do nothing */ | |||
case -1: | |||
/* State: error - do nothing */ | |||
case -2: | |||
/* State: signalled - do nothing */ | |||
; | |||
} | |||
return 0; | |||
} | |||
static void | |||
handle_shutdown(void *arg) | |||
{ | |||
set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN); | |||
} | |||
static void | |||
handle_signal(int sig) | |||
{ | |||
process_state = -2; | |||
if (! signal_semaphore(connect_semaphore)) { | |||
/* Sigh ... */ | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
if (! signal_semaphore(process_semaphore)) { | |||
/* Sigh ... */ | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
} | |||
static int | |||
handle_xrun(void *arg) | |||
{ | |||
xrun_count++; | |||
return 0; | |||
} | |||
static void | |||
output_error(const char *source, const char *message) | |||
{ | |||
fprintf(stderr, "%s: %s: %s\n", program_name, source, message); | |||
} | |||
static void | |||
output_usage(void) | |||
{ | |||
fprintf(stderr, "Usage: %s [options] [out-port-name in-port-name]\n\n" | |||
"\t-h, --help print program usage\n" | |||
"\t-m, --message-size=size set size of MIDI messages to send " | |||
"(default: 3)\n" | |||
"\t-s, --samples=n number of MIDI messages to send " | |||
"(default: 1024)\n" | |||
"\t-t, --timeout=seconds message timeout (default: 5)\n\n", | |||
program_name); | |||
} | |||
static unsigned long | |||
parse_positive_number_arg(char *s, char *name) | |||
{ | |||
char *end_ptr; | |||
unsigned long result; | |||
errno = 0; | |||
result = strtoul(s, &end_ptr, 10); | |||
if (errno) { | |||
die(name, strerror(errno)); | |||
} | |||
if (*s == '\0') { | |||
die(name, "argument value cannot be empty"); | |||
} | |||
if (*end_ptr != '\0') { | |||
die(name, "invalid value"); | |||
} | |||
if (! result) { | |||
die(name, "must be a positive number"); | |||
} | |||
return result; | |||
} | |||
static int | |||
register_signal_handler(void (*func)(int)) | |||
{ | |||
#ifdef WIN32 | |||
if (signal(SIGABRT, func) == SIG_ERR) { | |||
return 0; | |||
} | |||
#else | |||
if (signal(SIGQUIT, func) == SIG_ERR) { | |||
return 0; | |||
} | |||
if (signal(SIGHUP, func) == SIG_ERR) { | |||
return 0; | |||
} | |||
#endif | |||
if (signal(SIGINT, func) == SIG_ERR) { | |||
return 0; | |||
} | |||
if (signal(SIGTERM, func) == SIG_ERR) { | |||
return 0; | |||
} | |||
return 1; | |||
} | |||
static void | |||
set_process_error(const char *source, const char *message) | |||