Signed-off-by: falkTX <falktx@falktx.com>tags/v1.9.22
@@ -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) | |||
{ | |||
error_source = source; | |||
error_message = message; | |||
process_state = -1; | |||
if (! signal_semaphore(process_semaphore)) { | |||
/* Sigh ... */ | |||
output_error(source, message); | |||
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); | |||
} | |||
} | |||
static int | |||
signal_semaphore(semaphore_t semaphore) | |||
{ | |||
#ifdef WIN32 | |||
return ReleaseSemaphore(semaphore, 1, NULL); | |||
#else | |||
return ! sem_post(semaphore); | |||
#endif | |||
} | |||
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) | |||
{ | |||
if (connected) { | |||
if (current_port) { | |||
return current_port; | |||
} | |||
if (target_name) { | |||
char *aliases[2]; | |||
if (! strcmp(target_name, jack_port_name(remote_port))) { | |||
return remote_port; | |||
} | |||
aliases[0] = alias1; | |||
aliases[1] = alias2; | |||
switch (jack_port_get_aliases(remote_port, aliases)) { | |||
case -1: | |||
/* Sigh ... */ | |||
die("jack_port_get_aliases", "Failed to get port aliases"); | |||
case 2: | |||
if (! strcmp(target_name, alias2)) { | |||
return remote_port; | |||
} | |||
/* Fallthrough on purpose */ | |||
case 1: | |||
if (! strcmp(target_name, alias1)) { | |||
return remote_port; | |||
} | |||
/* Fallthrough on purpose */ | |||
case 0: | |||
return NULL; | |||
} | |||
/* This shouldn't happen. */ | |||
assert(0); | |||
} | |||
return remote_port; | |||
} | |||
if (! strcmp(jack_port_name(remote_port), jack_port_name(current_port))) { | |||
const char **port_names; | |||
if (target_name) { | |||
return NULL; | |||
} | |||
port_names = jack_port_get_connections(local_port); | |||
if (port_names == NULL) { | |||
return NULL; | |||
} | |||
/* If a connected port is disconnected and other ports are still | |||
connected, then we take the first port name in the array and use it | |||
as our remote port. It's a dumb implementation. */ | |||
current_port = jack_port_by_name(client, port_names[0]); | |||
jack_free(port_names); | |||
if (current_port == NULL) { | |||
/* Sigh */ | |||
die("jack_port_by_name", "failed to get port by name"); | |||
} | |||
} | |||
return current_port; | |||
} | |||
static int | |||
wait_semaphore(semaphore_t semaphore, int block) | |||
{ | |||
#ifdef WIN32 | |||
DWORD result = WaitForSingleObject(semaphore, block ? INFINITE : 0); | |||
switch (result) { | |||
case WAIT_OBJECT_0: | |||
return 1; | |||
case WAIT_TIMEOUT: | |||
return 0; | |||
} | |||
return -1; | |||
#else | |||
if (block) { | |||
while (sem_wait(semaphore)) { | |||
if (errno != EINTR) { | |||
return -1; | |||
} | |||
} | |||
} else { | |||
while (sem_trywait(semaphore)) { | |||
switch (errno) { | |||
case EAGAIN: | |||
return 0; | |||
case EINTR: | |||
continue; | |||
default: | |||
return -1; | |||
} | |||
} | |||
} | |||
return 1; | |||
#endif | |||
} | |||
int | |||
main(int argc, char **argv) | |||
{ | |||
int jitter_plot[101]; | |||
int latency_plot[101]; | |||
int long_index = 0; | |||
struct option long_options[] = { | |||
{"help", 0, NULL, 'h'}, | |||
{"message-size", 1, NULL, 'm'}, | |||
{"samples", 1, NULL, 's'}, | |||
{"timeout", 1, NULL, 't'} | |||
}; | |||
size_t name_arg_count; | |||
size_t name_size; | |||
char *option_string = "hm:s:t:"; | |||
int show_usage = 0; | |||
connections_established = 0; | |||
error_message = NULL; | |||
message_size = 3; | |||
program_name = argv[0]; | |||
remote_in_port = 0; | |||
remote_out_port = 0; | |||
samples = 1024; | |||
timeout = 5; | |||
for (;;) { | |||
signed char c = getopt_long(argc, argv, option_string, long_options, | |||
&long_index); | |||
switch (c) { | |||
case 'h': | |||
show_usage = 1; | |||
break; | |||
case 'm': | |||
message_size = parse_positive_number_arg(optarg, "message-size"); | |||
break; | |||
case 's': | |||
samples = parse_positive_number_arg(optarg, "samples"); | |||
break; | |||
case 't': | |||
timeout = parse_positive_number_arg(optarg, "timeout"); | |||
break; | |||
default: | |||
{ | |||
char *s = "'- '"; | |||
s[2] = c; | |||
die(s, "invalid switch"); | |||
} | |||
case -1: | |||
if (show_usage) { | |||
output_usage(); | |||
exit(EXIT_SUCCESS); | |||
} | |||
goto parse_port_names; | |||
case 1: | |||
/* end of switch :) */ | |||
; | |||
} | |||
} | |||
parse_port_names: | |||
name_arg_count = argc - optind; | |||
switch (name_arg_count) { | |||
case 2: | |||
target_in_port_name = argv[optind + 1]; | |||
target_out_port_name = argv[optind]; | |||
break; | |||
case 0: | |||
target_in_port_name = 0; | |||
target_out_port_name = 0; | |||
break; | |||
default: | |||
output_usage(); | |||
return EXIT_FAILURE; | |||
} | |||
name_size = jack_port_name_size(); | |||
alias1 = malloc(name_size * sizeof(char)); | |||
if (alias1 == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto show_error; | |||
} | |||
alias2 = malloc(name_size * sizeof(char)); | |||
if (alias2 == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto free_alias1; | |||
} | |||
latency_values = malloc(sizeof(jack_nframes_t) * samples); | |||
if (latency_values == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto free_alias2; | |||
} | |||
latency_time_values = malloc(sizeof(jack_time_t) * samples); | |||
if (latency_time_values == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto free_latency_values; | |||
} | |||
message_1 = malloc(message_size * sizeof(jack_midi_data_t)); | |||
if (message_1 == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto free_latency_time_values; | |||
} | |||
message_2 = malloc(message_size * sizeof(jack_midi_data_t)); | |||
if (message_2 == NULL) { | |||
error_message = strerror(errno); | |||
error_source = "malloc"; | |||
goto free_message_1; | |||
} | |||
switch (message_size) { | |||
case 1: | |||
message_1[0] = 0xf6; | |||
message_2[0] = 0xfe; | |||
break; | |||
case 2: | |||
message_1[0] = 0xc0; | |||
message_1[1] = 0x00; | |||
message_2[0] = 0xd0; | |||
message_2[1] = 0x7f; | |||
break; | |||
case 3: | |||
message_1[0] = 0x80; | |||
message_1[1] = 0x00; | |||
message_1[2] = 0x00; | |||
message_2[0] = 0x90; | |||
message_2[1] = 0x7f; | |||
message_2[2] = 0x7f; | |||
break; | |||
default: | |||
message_1[0] = 0xf0; | |||
memset(message_1 + 1, 0, | |||
(message_size - 2) * sizeof(jack_midi_data_t)); | |||
message_1[message_size - 1] = 0xf7; | |||
message_2[0] = 0xf0; | |||
memset(message_2 + 1, 0x7f, | |||
(message_size - 2) * sizeof(jack_midi_data_t)); | |||
message_2[message_size - 1] = 0xf7; | |||
} | |||
client = jack_client_open(program_name, JackNullOption, NULL); | |||
if (client == NULL) { | |||
error_message = "failed to open JACK client"; | |||
error_source = "jack_client_open"; | |||
goto free_message_2; | |||
} | |||
in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE, | |||
JackPortIsInput, 0); | |||
if (in_port == NULL) { | |||
error_message = "failed to register MIDI-in port"; | |||
error_source = "jack_port_register"; | |||
goto close_client; | |||
} | |||
out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE, | |||
JackPortIsOutput, 0); | |||
if (out_port == NULL) { | |||
error_message = "failed to register MIDI-out port"; | |||
error_source = "jack_port_register"; | |||
goto unregister_in_port; | |||
} | |||
if (jack_set_process_callback(client, handle_process, NULL)) { | |||
error_message = "failed to set process callback"; | |||
error_source = "jack_set_process_callback"; | |||
goto unregister_out_port; | |||
} | |||
if (jack_set_xrun_callback(client, handle_xrun, NULL)) { | |||
error_message = "failed to set xrun callback"; | |||
error_source = "jack_set_xrun_callback"; | |||
goto unregister_out_port; | |||
} | |||
if (jack_set_port_connect_callback(client, handle_port_connection_change, | |||
NULL)) { | |||
error_message = "failed to set port connection callback"; | |||
error_source = "jack_set_port_connect_callback"; | |||
goto unregister_out_port; | |||
} | |||
jack_on_shutdown(client, handle_shutdown, NULL); | |||
jack_set_info_function(handle_info); | |||
process_state = 0; | |||
connect_semaphore = create_semaphore(0); | |||
if (connect_semaphore == NULL) { | |||
error_message = get_semaphore_error(); | |||
error_source = "create_semaphore"; | |||
goto unregister_out_port; | |||
} | |||
init_semaphore = create_semaphore(1); | |||
if (init_semaphore == NULL) { | |||
error_message = get_semaphore_error(); | |||
error_source = "create_semaphore"; | |||
goto destroy_connect_semaphore;; | |||
} | |||
process_semaphore = create_semaphore(2); | |||
if (process_semaphore == NULL) { | |||
error_message = get_semaphore_error(); | |||
error_source = "create_semaphore"; | |||
goto destroy_init_semaphore; | |||
} | |||
if (jack_activate(client)) { | |||
error_message = "could not activate client"; | |||
error_source = "jack_activate"; | |||
goto destroy_process_semaphore; | |||
} | |||
if (name_arg_count) { | |||
if (jack_connect(client, jack_port_name(out_port), | |||
target_out_port_name)) { | |||
error_message = "could not connect MIDI out port"; | |||
error_source = "jack_connect"; | |||
goto deactivate_client; | |||
} | |||
if (jack_connect(client, target_in_port_name, | |||
jack_port_name(in_port))) { | |||
error_message = "could not connect MIDI in port"; | |||
error_source = "jack_connect"; | |||
goto deactivate_client; | |||
} | |||
} | |||
if (! register_signal_handler(handle_signal)) { | |||
error_message = strerror(errno); | |||
error_source = "register_signal_handler"; | |||
goto deactivate_client; | |||
} | |||
printf("Waiting for connections ...\n"); | |||
if (wait_semaphore(connect_semaphore, 1) == -1) { | |||
error_message = get_semaphore_error(); | |||
error_source = "wait_semaphore"; | |||
goto deactivate_client; | |||
} | |||
if (connections_established) { | |||
printf("Waiting for test completion ...\n\n"); | |||
if (wait_semaphore(process_semaphore, 1) == -1) { | |||
error_message = get_semaphore_error(); | |||
error_source = "wait_semaphore"; | |||
goto deactivate_client; | |||
} | |||
} | |||
if (! register_signal_handler(SIG_DFL)) { | |||
error_message = strerror(errno); | |||
error_source = "register_signal_handler"; | |||
goto deactivate_client; | |||
} | |||
if (process_state == 2) { | |||
double average_latency = ((double) total_latency) / samples; | |||
double average_latency_time = total_latency_time / samples; | |||
size_t i; | |||
double latency_plot_offset = | |||
floor(((double) lowest_latency_time) / 100.0) / 10.0; | |||
double sample_rate = (double) jack_get_sample_rate(client); | |||
jack_nframes_t total_jitter = 0; | |||
jack_time_t total_jitter_time = 0; | |||
for (i = 0; i <= 100; i++) { | |||
jitter_plot[i] = 0; | |||
latency_plot[i] = 0; | |||
} | |||
for (i = 0; i < samples; i++) { | |||
double latency_time_value = (double) latency_time_values[i]; | |||
double latency_plot_time = | |||
(latency_time_value / 1000.0) - latency_plot_offset; | |||
double jitter_time = ABS(average_latency_time - | |||
latency_time_value); | |||
if (latency_plot_time >= 10.0) { | |||
(latency_plot[100])++; | |||
} else { | |||
(latency_plot[(int) (latency_plot_time * 10.0)])++; | |||
} | |||
if (jitter_time >= 10000.0) { | |||
(jitter_plot[100])++; | |||
} else { | |||
(jitter_plot[(int) (jitter_time / 100.0)])++; | |||
} | |||
total_jitter += ABS(average_latency - | |||
((double) latency_values[i])); | |||
total_jitter_time += jitter_time; | |||
} | |||
printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n" | |||
"Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n" | |||
"Average latency: %.2f ms (%.2f frames)\n" | |||
"Lowest latency: %.2f ms (%u frames)\n" | |||
"Highest latency: %.2f ms (%u frames)\n" | |||
"Peak MIDI jitter: %.2f ms (%u frames)\n" | |||
"Average MIDI jitter: %.2f ms (%.2f frames)\n", | |||
(out_latency_range.min / sample_rate) * 1000.0, | |||
(out_latency_range.max / sample_rate) * 1000.0, | |||
out_latency_range.min, out_latency_range.max, | |||
(in_latency_range.min / sample_rate) * 1000.0, | |||
(in_latency_range.max / sample_rate) * 1000.0, | |||
in_latency_range.min, in_latency_range.max, | |||
average_latency_time / 1000.0, average_latency, | |||
lowest_latency_time / 1000.0, lowest_latency, | |||
highest_latency_time / 1000.0, highest_latency, | |||
(highest_latency_time - lowest_latency_time) / 1000.0, | |||
highest_latency - lowest_latency, | |||
(total_jitter_time / 1000.0) / samples, | |||
((double) total_jitter) / samples); | |||
printf("\nJitter Plot:\n"); | |||
for (i = 0; i < 100; i++) { | |||
if (jitter_plot[i]) { | |||
printf("%.1f - %.1f ms: %d\n", ((float) i) / 10.0, | |||
((float) (i + 1)) / 10.0, jitter_plot[i]); | |||
} | |||
} | |||
if (jitter_plot[100]) { | |||
printf(" > 10 ms: %d\n", jitter_plot[100]); | |||
} | |||
printf("\nLatency Plot:\n"); | |||
for (i = 0; i < 100; i++) { | |||
if (latency_plot[i]) { | |||
printf("%.1f - %.1f ms: %d\n", | |||
latency_plot_offset + (((float) i) / 10.0), | |||
latency_plot_offset + (((float) (i + 1)) / 10.0), | |||
latency_plot[i]); | |||
} | |||
} | |||
if (latency_plot[100]) { | |||
printf(" > %.1f ms: %d\n", latency_plot_offset + 10.0, | |||
latency_plot[100]); | |||
} | |||
} | |||
deactivate_client: | |||
jack_deactivate(client); | |||
printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent, | |||
messages_received); | |||
if (unexpected_messages) { | |||
printf("Unexpected messages received: %d\n", unexpected_messages); | |||
} | |||
if (xrun_count) { | |||
printf("Xruns: %d\n", xrun_count); | |||
} | |||
destroy_process_semaphore: | |||
destroy_semaphore(process_semaphore, 2); | |||
destroy_init_semaphore: | |||
destroy_semaphore(init_semaphore, 1); | |||
destroy_connect_semaphore: | |||
destroy_semaphore(connect_semaphore, 0); | |||
unregister_out_port: | |||
jack_port_unregister(client, out_port); | |||
unregister_in_port: | |||
jack_port_unregister(client, in_port); | |||
close_client: | |||
jack_client_close(client); | |||
free_message_2: | |||
free(message_2); | |||
free_message_1: | |||
free(message_1); | |||
free_latency_time_values: | |||
free(latency_time_values); | |||
free_latency_values: | |||
free(latency_values); | |||
free_alias2: | |||
free(alias2); | |||
free_alias1: | |||
free(alias1); | |||
if (error_message != NULL) { | |||
show_error: | |||
output_error(error_source, error_message); | |||
exit(EXIT_FAILURE); | |||
} | |||
return EXIT_SUCCESS; | |||
} |
@@ -1,133 +0,0 @@ | |||
/* | |||
Copyright (C) 2004 Ian Esten | |||
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 <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#include <stdio.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
jack_client_t *client; | |||
jack_port_t *output_port; | |||
unsigned char* note_frqs; | |||
jack_nframes_t* note_starts; | |||
jack_nframes_t* note_lengths; | |||
jack_nframes_t num_notes; | |||
jack_nframes_t loop_nsamp; | |||
jack_nframes_t loop_index; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void usage() | |||
{ | |||
fprintf(stderr, "usage: jack_midiseq name nsamp [startindex note nsamp] ...... [startindex note nsamp]\n"); | |||
fprintf(stderr, "eg: jack_midiseq Sequencer 24000 0 60 8000 12000 63 8000\n"); | |||
fprintf(stderr, "will play a 1/2 sec loop (if srate is 48khz) with a c4 note at the start of the loop\n"); | |||
fprintf(stderr, "that lasts for 12000 samples, then a d4# that starts at 1/4 sec that lasts for 800 samples\n"); | |||
} | |||
static int process(jack_nframes_t nframes, void *arg) | |||
{ | |||
int i,j; | |||
void* port_buf = jack_port_get_buffer(output_port, nframes); | |||
unsigned char* buffer; | |||
jack_midi_clear_buffer(port_buf); | |||
/*memset(buffer, 0, nframes*sizeof(jack_default_audio_sample_t));*/ | |||
for (i = 0; i < nframes; i++) { | |||
for (j = 0; j < num_notes; j++) { | |||
if (note_starts[j] == loop_index) { | |||
if ((buffer = jack_midi_event_reserve(port_buf, i, 3))) { | |||
/* printf("wrote a note on, port buffer = 0x%x, event buffer = 0x%x\n", port_buf, buffer); */ | |||
buffer[2] = 64; /* velocity */ | |||
buffer[1] = note_frqs[j]; | |||
buffer[0] = 0x90; /* note on */ | |||
} | |||
} else if (note_starts[j] + note_lengths[j] == loop_index) { | |||
if ((buffer = jack_midi_event_reserve(port_buf, i, 3))) { | |||
/* printf("wrote a note off, port buffer = 0x%x, event buffer = 0x%x\n", port_buf, buffer); */ | |||
buffer[2] = 64; /* velocity */ | |||
buffer[1] = note_frqs[j]; | |||
buffer[0] = 0x80; /* note off */ | |||
} | |||
} | |||
} | |||
loop_index = loop_index+1 >= loop_nsamp ? 0 : loop_index+1; | |||
} | |||
return 0; | |||
} | |||
int main(int narg, char **args) | |||
{ | |||
int i; | |||
jack_nframes_t nframes; | |||
if ((narg<6) || ((narg-3)%3 != 0)) { | |||
usage(); | |||
exit(1); | |||
} | |||
if ((client = jack_client_open (args[1], JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
jack_set_process_callback (client, process, 0); | |||
output_port = jack_port_register (client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); | |||
nframes = jack_get_buffer_size(client); | |||
loop_index = 0; | |||
num_notes = (narg - 3)/3; | |||
note_frqs = malloc(num_notes*sizeof(unsigned char)); | |||
note_starts = malloc(num_notes*sizeof(jack_nframes_t)); | |||
note_lengths = malloc(num_notes*sizeof(jack_nframes_t)); | |||
loop_nsamp = atoi(args[2]); | |||
for (i = 0; i < num_notes; i++) { | |||
note_starts[i] = atoi(args[3 + 3*i]); | |||
note_frqs[i] = atoi(args[4 + 3*i]); | |||
note_lengths[i] = atoi(args[5 + 3*i]); | |||
} | |||
if (jack_activate(client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
/* 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 until interrupted */ | |||
while (1) { | |||
#ifdef WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
}; | |||
jack_client_close(client); | |||
exit (0); | |||
} |
@@ -1,163 +0,0 @@ | |||
/* | |||
Copyright (C) 2004 Ian Esten | |||
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 <string.h> | |||
#include <signal.h> | |||
#include <math.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
jack_default_audio_sample_t ramp=0.0; | |||
jack_default_audio_sample_t note_on; | |||
unsigned char note = 0; | |||
jack_default_audio_sample_t note_frqs[128]; | |||
jack_client_t *client; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void calc_note_frqs(jack_default_audio_sample_t srate) | |||
{ | |||
int i; | |||
for(i=0; i<128; i++) | |||
{ | |||
note_frqs[i] = (2.0 * 440.0 / 32.0) * pow(2, (((jack_default_audio_sample_t)i - 9.0) / 12.0)) / srate; | |||
} | |||
} | |||
static int process(jack_nframes_t nframes, void *arg) | |||
{ | |||
int i; | |||
void* port_buf = jack_port_get_buffer(input_port, nframes); | |||
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); | |||
jack_midi_event_t in_event; | |||
jack_nframes_t event_index = 0; | |||
jack_nframes_t event_count = jack_midi_get_event_count(port_buf); | |||
if(event_count > 1) | |||
{ | |||
printf(" midisine: have %d events\n", event_count); | |||
for(i=0; i<event_count; i++) | |||
{ | |||
jack_midi_event_get(&in_event, port_buf, i); | |||
printf(" event %d time is %d. 1st byte is 0x%x\n", i, in_event.time, *(in_event.buffer)); | |||
} | |||
/* printf("1st byte of 1st event addr is %p\n", in_events[0].buffer);*/ | |||
} | |||
jack_midi_event_get(&in_event, port_buf, 0); | |||
for(i = 0; i < nframes; i++) | |||
{ | |||
if ((in_event.time == i) && (event_index < event_count)) | |||
{ | |||
if (((*(in_event.buffer) & 0xf0)) == 0x90) | |||
{ | |||
/* note on */ | |||
note = *(in_event.buffer + 1); | |||
if (*(in_event.buffer + 2) == 0) { | |||
note_on = 0.0; | |||
} else { | |||
note_on = (float)(*(in_event.buffer + 2)) / 127.f; | |||
} | |||
} | |||
else if (((*(in_event.buffer)) & 0xf0) == 0x80) | |||
{ | |||
/* note off */ | |||
note = *(in_event.buffer + 1); | |||
note_on = 0.0; | |||
} | |||
event_index++; | |||
if(event_index < event_count) | |||
jack_midi_event_get(&in_event, port_buf, event_index); | |||
} | |||
ramp += note_frqs[note]; | |||
ramp = (ramp > 1.0) ? ramp - 2.0 : ramp; | |||
out[i] = note_on*sin(2*M_PI*ramp); | |||
} | |||
return 0; | |||
} | |||
static int srate(jack_nframes_t nframes, void *arg) | |||
{ | |||
printf("the sample rate is now %" PRIu32 "/sec\n", nframes); | |||
calc_note_frqs((jack_default_audio_sample_t)nframes); | |||
return 0; | |||
} | |||
static void jack_shutdown(void *arg) | |||
{ | |||
fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
exit(1); | |||
} | |||
int main(int narg, char **args) | |||
{ | |||
if ((client = jack_client_open("midisine", JackNullOption, NULL)) == 0) | |||
{ | |||
fprintf(stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
calc_note_frqs(jack_get_sample_rate (client)); | |||
jack_set_process_callback (client, process, 0); | |||
jack_set_sample_rate_callback (client, srate, 0); | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
input_port = jack_port_register (client, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
output_port = jack_port_register (client, "audio_out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
if (jack_activate (client)) | |||
{ | |||
fprintf(stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
/* 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 until interrupted */ | |||
while(1) { | |||
#ifdef WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
} | |||
jack_client_close(client); | |||
exit (0); | |||
} | |||
@@ -1,208 +0,0 @@ | |||
/* | |||
Copyright (C) 2009 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 <stdlib.h> | |||
#include <stdbool.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 <assert.h> | |||
#include <jack/net.h> | |||
jack_net_master_t* net; | |||
#define BUFFER_SIZE 512 | |||
#define SAMPLE_RATE 44100 | |||
static void signal_handler(int sig) | |||
{ | |||
jack_net_master_close(net); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void | |||
usage () | |||
{ | |||
fprintf (stderr, "\n" | |||
"usage: jack_net_master \n" | |||
" [ -b buffer size (default = %d) ]\n" | |||
" [ -r sample rate (default = %d) ]\n" | |||
" [ -a hostname (default = %s) ]\n" | |||
" [ -p port (default = %d) ]\n", BUFFER_SIZE, SAMPLE_RATE, DEFAULT_MULTICAST_IP, DEFAULT_PORT); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
int buffer_size = BUFFER_SIZE; | |||
int sample_rate = SAMPLE_RATE; | |||
int udp_port = DEFAULT_PORT; | |||
const char* multicast_ip = DEFAULT_MULTICAST_IP; | |||
const char *options = "b:r:a:p:h"; | |||
int option_index; | |||
int opt; | |||
struct option long_options[] = | |||
{ | |||
{"buffer size", 1, 0, 'b'}, | |||
{"sample rate", 1, 0, 'r'}, | |||
{"hostname", 1, 0, 'a'}, | |||
{"port", 1, 0, 'p'}, | |||
{0, 0, 0, 0} | |||
}; | |||
while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != -1) { | |||
switch (opt) { | |||
case 'b': | |||
buffer_size = atoi(optarg); | |||
break; | |||
case 'r': | |||
sample_rate = atoi(optarg); | |||
break; | |||
case 'a': | |||
multicast_ip = strdup(optarg); | |||
break; | |||
case 'p': | |||
udp_port = atoi(optarg); | |||
break; | |||
case 'h': | |||
usage(); | |||
return -1; | |||
} | |||
} | |||
int i; | |||
//jack_master_t request = { 4, 4, -1, -1, buffer_size, sample_rate, "master", -1 }; | |||
jack_master_t request = { -1, -1, -1, -1, buffer_size, sample_rate, "net_master", 6, true }; | |||
jack_slave_t result; | |||
float** audio_input_buffer; | |||
float** audio_output_buffer; | |||
int wait_usec = (int) ((((float)buffer_size) * 1000000) / ((float)sample_rate)); | |||
printf("Waiting for a slave...\n"); | |||
if ((net = jack_net_master_open(multicast_ip, udp_port, &request, &result)) == 0) { | |||
fprintf(stderr, "NetJack master can not be opened\n"); | |||
return 1; | |||
} | |||
printf("Slave is running...\n"); | |||
/* 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 | |||
// Allocate buffers | |||
audio_input_buffer = (float**)calloc(result.audio_input, sizeof(float*)); | |||
for (i = 0; i < result.audio_input; i++) { | |||
audio_input_buffer[i] = (float*)calloc(buffer_size, sizeof(float)); | |||
} | |||
audio_output_buffer = (float**)calloc(result.audio_output, sizeof(float*)); | |||
for (i = 0; i < result.audio_output; i++) { | |||
audio_output_buffer[i] = (float*)calloc(buffer_size, sizeof(float)); | |||
} | |||
/* | |||
Run until interrupted. | |||
WARNING !! : this code is given for demonstration purpose. For proper timing bevahiour | |||
it has to be called in a real-time context (which is *not* the case here...) | |||
*/ | |||
//usleep(5*1000000); | |||
printf("Wait...\n"); | |||
//sleep(10); | |||
usleep(1000000); | |||
printf("Wait...OK\n"); | |||
while (1) { | |||
// Copy input to output | |||
assert(result.audio_input == result.audio_output); | |||
for (i = 0; i < result.audio_input; i++) { | |||
memcpy(audio_output_buffer[i], audio_input_buffer[i], buffer_size * sizeof(float)); | |||
} | |||
/* | |||
if (jack_net_master_send(net, result.audio_output, audio_output_buffer, 0, NULL) < 0) { | |||
printf("jack_net_master_send failure, exiting\n"); | |||
break; | |||
} | |||
usleep(10000); | |||
if (jack_net_master_recv(net, result.audio_input, audio_input_buffer, 0, NULL) < 0) { | |||
printf("jack_net_master_recv failure, exiting\n"); | |||
break; | |||
} | |||
*/ | |||
if (jack_net_master_send_slice(net, result.audio_output, audio_output_buffer, 0, NULL, BUFFER_SIZE/2) < 0) { | |||
printf("jack_net_master_send failure, exiting\n"); | |||
break; | |||
} | |||
usleep(10000); | |||
if (jack_net_master_recv_slice(net, result.audio_input, audio_input_buffer, 0, NULL, BUFFER_SIZE/2) < 0) { | |||
printf("jack_net_master_recv failure, exiting\n"); | |||
break; | |||
} | |||
usleep(wait_usec); | |||
}; | |||
// Wait for application end | |||
jack_net_master_close(net); | |||
for (i = 0; i < result.audio_input; i++) { | |||
free(audio_input_buffer[i]); | |||
} | |||
free(audio_input_buffer); | |||
for (i = 0; i < result.audio_output; i++) { | |||
free(audio_output_buffer[i]); | |||
} | |||
free(audio_output_buffer); | |||
exit (0); | |||
} |
@@ -1,168 +0,0 @@ | |||
/* | |||
Copyright (C) 2009 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 <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/net.h> | |||
jack_net_slave_t* net; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_net_slave_close(net); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void | |||
usage () | |||
{ | |||
fprintf (stderr, "\n" | |||
"usage: jack_net_slave \n" | |||
" [ -C capture channels (default = 2)]\n" | |||
" [ -P playback channels (default = 2) ]\n" | |||
" [ -a hostname (default = %s) ]\n" | |||
" [ -p port (default = %d)]\n", DEFAULT_MULTICAST_IP, DEFAULT_PORT); | |||
} | |||
static void net_shutdown(void* data) | |||
{ | |||
printf("Restarting...\n"); | |||
} | |||
static int net_process(jack_nframes_t buffer_size, | |||
int audio_input, | |||
float** audio_input_buffer, | |||
int midi_input, | |||
void** midi_input_buffer, | |||
int audio_output, | |||
float** audio_output_buffer, | |||
int midi_output, | |||
void** midi_output_buffer, | |||
void* data) | |||
{ | |||
int i; | |||
// Copy input to output | |||
for (i = 0; i < audio_input; i++) { | |||
memcpy(audio_output_buffer[i], audio_input_buffer[i], buffer_size * sizeof(float)); | |||
} | |||
return 0; | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
int audio_input = 2; | |||
int audio_output = 2; | |||
int udp_port = DEFAULT_PORT; | |||
const char* multicast_ip = DEFAULT_MULTICAST_IP; | |||
const char *options = "C:P:a:p:h"; | |||
int option_index; | |||
int opt; | |||
struct option long_options[] = | |||
{ | |||
{"audio input", 1, 0, 'C'}, | |||
{"audio output", 1, 0, 'P'}, | |||
{"hostname", 1, 0, 'a'}, | |||
{"port", 1, 0, 'p'}, | |||
{0, 0, 0, 0} | |||
}; | |||
while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) { | |||
switch (opt) { | |||
case 'C': | |||
audio_input = atoi(optarg); | |||
break; | |||
case 'P': | |||
audio_output = atoi(optarg); | |||
break; | |||
case 'a': | |||
multicast_ip = strdup(optarg); | |||
break; | |||
case 'p': | |||
udp_port = atoi(optarg); | |||
break; | |||
case 'h': | |||
usage(); | |||
return -1; | |||
} | |||
} | |||
jack_slave_t request = { audio_input, audio_output, 0, 0, DEFAULT_MTU, -1, JackFloatEncoder, 0, 2 }; | |||
jack_master_t result; | |||
printf("Waiting for a master...\n"); | |||
if ((net = jack_net_slave_open(multicast_ip, udp_port, "net_slave", &request, &result)) == 0) { | |||
fprintf(stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
printf("Master is found and running...\n"); | |||
jack_set_net_slave_process_callback(net, net_process, NULL); | |||
jack_set_net_slave_shutdown_callback(net, net_shutdown, NULL); | |||
if (jack_net_slave_activate(net) != 0) { | |||
fprintf(stderr, "Cannot activate slave client\n"); | |||
return 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 | |||
/* run until interrupted */ | |||
while (1) { | |||
#ifdef WIN32 | |||
Sleep(1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
}; | |||
// Wait for application end | |||
jack_net_slave_deactivate(net); | |||
jack_net_slave_close(net); | |||
exit(0); | |||
} |
@@ -1,244 +0,0 @@ | |||
/* | |||
Copyright (C) 2008 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 <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/control.h> | |||
static jackctl_driver_t * jackctl_server_get_driver(jackctl_server_t *server, const char *driver_name) | |||
{ | |||
const JSList * node_ptr = jackctl_server_get_drivers_list(server); | |||
while (node_ptr) { | |||
if (strcmp(jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data), driver_name) == 0) { | |||
return (jackctl_driver_t *)node_ptr->data; | |||
} | |||
node_ptr = jack_slist_next(node_ptr); | |||
} | |||
return NULL; | |||
} | |||
static jackctl_internal_t * jackctl_server_get_internal(jackctl_server_t *server, const char *internal_name) | |||
{ | |||
const JSList * node_ptr = jackctl_server_get_internals_list(server); | |||
while (node_ptr) { | |||
if (strcmp(jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data), internal_name) == 0) { | |||
return (jackctl_internal_t *)node_ptr->data; | |||
} | |||
node_ptr = jack_slist_next(node_ptr); | |||
} | |||
return NULL; | |||
} | |||
#if 0 | |||
static jackctl_parameter_t * | |||
jackctl_get_parameter( | |||
const JSList * parameters_list, | |||
const char * parameter_name) | |||
{ | |||
while (parameters_list) | |||
{ | |||
if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) | |||
{ | |||
return (jackctl_parameter_t *)parameters_list->data; | |||
} | |||
parameters_list = jack_slist_next(parameters_list); | |||
} | |||
return NULL; | |||
} | |||
#endif | |||
static void print_value(union jackctl_parameter_value value, jackctl_param_type_t type) | |||
{ | |||
switch (type) { | |||
case JackParamInt: | |||
printf("parameter value = %d\n", value.i); | |||
break; | |||
case JackParamUInt: | |||
printf("parameter value = %u\n", value.ui); | |||
break; | |||
case JackParamChar: | |||
printf("parameter value = %c\n", value.c); | |||
break; | |||
case JackParamString: | |||
printf("parameter value = %s\n", value.str); | |||
break; | |||
case JackParamBool: | |||
printf("parameter value = %d\n", value.b); | |||
break; | |||
} | |||
} | |||
static void print_parameters(const JSList * node_ptr) | |||
{ | |||
while (node_ptr != NULL) { | |||
jackctl_parameter_t * parameter = (jackctl_parameter_t *)node_ptr->data; | |||
printf("\nparameter name = %s\n", jackctl_parameter_get_name(parameter)); | |||
printf("parameter id = %c\n", jackctl_parameter_get_id(parameter)); | |||
printf("parameter short decs = %s\n", jackctl_parameter_get_short_description(parameter)); | |||
printf("parameter long decs = %s\n", jackctl_parameter_get_long_description(parameter)); | |||
print_value(jackctl_parameter_get_default_value(parameter), jackctl_parameter_get_type(parameter)); | |||
node_ptr = jack_slist_next(node_ptr); | |||
} | |||
} | |||
static void print_driver(jackctl_driver_t * driver) | |||
{ | |||
printf("\n--------------------------\n"); | |||
printf("driver = %s\n", jackctl_driver_get_name(driver)); | |||
printf("-------------------------- \n"); | |||
print_parameters(jackctl_driver_get_parameters(driver)); | |||
} | |||
static void print_internal(jackctl_internal_t * internal) | |||
{ | |||
printf("\n-------------------------- \n"); | |||
printf("internal = %s\n", jackctl_internal_get_name(internal)); | |||
printf("-------------------------- \n"); | |||
print_parameters(jackctl_internal_get_parameters(internal)); | |||
} | |||
static void usage() | |||
{ | |||
fprintf (stderr, "\n" | |||
"usage: jack_server_control \n" | |||
" [ --driver OR -d driver_name ]\n" | |||
" [ --client OR -c client_name ]\n" | |||
); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
jackctl_server_t * server; | |||
const JSList * parameters; | |||
const JSList * drivers; | |||
const JSList * internals; | |||
const JSList * node_ptr; | |||
jackctl_sigmask_t * sigmask; | |||
int opt, option_index; | |||
const char* driver_name = "dummy"; | |||
const char* client_name = "audioadapter"; | |||
const char *options = "d:c:"; | |||
struct option long_options[] = { | |||
{"driver", 1, 0, 'd'}, | |||
{"client", 1, 0, 'c'}, | |||
{0, 0, 0, 0} | |||
}; | |||
while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != -1) { | |||
switch (opt) { | |||
case 'd': | |||
driver_name = optarg; | |||
break; | |||
case 'c': | |||
client_name = optarg; | |||
break; | |||
default: | |||
usage(); | |||
exit(0); | |||
} | |||
} | |||
server = jackctl_server_create2(NULL, NULL, NULL); | |||
parameters = jackctl_server_get_parameters(server); | |||
/* | |||
jackctl_parameter_t* param; | |||
union jackctl_parameter_value value; | |||
param = jackctl_get_parameter(parameters, "verbose"); | |||
if (param != NULL) { | |||
value.b = true; | |||
jackctl_parameter_set_value(param, &value); | |||
} | |||
*/ | |||
printf("\n========================== \n"); | |||
printf("List of server parameters \n"); | |||
printf("========================== \n"); | |||
print_parameters(parameters); | |||
printf("\n========================== \n"); | |||
printf("List of drivers \n"); | |||
printf("========================== \n"); | |||
drivers = jackctl_server_get_drivers_list(server); | |||
node_ptr = drivers; | |||
while (node_ptr != NULL) { | |||
print_driver((jackctl_driver_t *)node_ptr->data); | |||
node_ptr = jack_slist_next(node_ptr); | |||
} | |||
printf("\n========================== \n"); | |||
printf("List of internal clients \n"); | |||
printf("========================== \n"); | |||
internals = jackctl_server_get_internals_list(server); | |||
node_ptr = internals; | |||
while (node_ptr != NULL) { | |||
print_internal((jackctl_internal_t *)node_ptr->data); | |||
node_ptr = jack_slist_next(node_ptr); | |||
} | |||
// No error checking in this simple example... | |||
jackctl_server_open(server, jackctl_server_get_driver(server, driver_name)); | |||
jackctl_server_start(server); | |||
jackctl_server_load_internal(server, jackctl_server_get_internal(server, client_name)); | |||
/* | |||
// Switch master test | |||
jackctl_driver_t* master; | |||
usleep(5000000); | |||
printf("jackctl_server_load_master\n"); | |||
master = jackctl_server_get_driver(server, "coreaudio"); | |||
jackctl_server_switch_master(server, master); | |||
usleep(5000000); | |||
printf("jackctl_server_load_master\n"); | |||
master = jackctl_server_get_driver(server, "dummy"); | |||
jackctl_server_switch_master(server, master); | |||
*/ | |||
sigmask = jackctl_setup_signals(0); | |||
jackctl_wait_signals(sigmask); | |||
jackctl_server_stop(server); | |||
jackctl_server_close(server); | |||
jackctl_server_destroy(server); | |||
return 0; | |||
} |
@@ -1,119 +0,0 @@ | |||
/* | |||
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 <signal.h> | |||
#include <stdlib.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
jack_client_t *client; | |||
static void | |||
showtime () | |||
{ | |||
jack_position_t current; | |||
jack_transport_state_t transport_state; | |||
jack_nframes_t frame_time; | |||
transport_state = jack_transport_query (client, ¤t); | |||
frame_time = jack_frame_time (client); | |||
printf ("frame = %u frame_time = %u usecs = %" PRIu64 "\t", current.frame, frame_time, current.usecs); | |||
switch (transport_state) { | |||
case JackTransportStopped: | |||
printf ("state: Stopped"); | |||
break; | |||
case JackTransportRolling: | |||
printf ("state: Rolling"); | |||
break; | |||
case JackTransportStarting: | |||
printf ("state: Starting"); | |||
break; | |||
default: | |||
printf ("state: [unknown]"); | |||
} | |||
if (current.valid & JackPositionBBT) | |||
printf ("\tBBT: %3" PRIi32 "|%" PRIi32 "|%04" | |||
PRIi32, current.bar, current.beat, current.tick); | |||
if (current.valid & JackPositionTimecode) | |||
printf ("\tTC: (%.6f, %.6f)", | |||
current.frame_time, current.next_time); | |||
printf ("\n"); | |||
} | |||
static void | |||
jack_shutdown (void *arg) | |||
{ | |||
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); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
/* try to become a client of the JACK server */ | |||
if ((client = jack_client_open ("showtime", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
#ifndef WIN32 | |||
signal (SIGQUIT, signal_handler); | |||
signal (SIGHUP, signal_handler); | |||
#endif | |||
signal (SIGTERM, signal_handler); | |||
signal (SIGINT, signal_handler); | |||
/* 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); | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
while (1) { | |||
usleep (20); | |||
showtime (); | |||
} | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,415 +0,0 @@ | |||
/* | |||
* simdtests.c -- test accuracy and performance of simd optimizations | |||
* | |||
* Copyright (C) 2017 Andreas Mueller. | |||
* | |||
* 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. | |||
*/ | |||
/* We must include all headers memops.c includes to avoid trouble with | |||
* out namespace game below. | |||
*/ | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#include <memory.h> | |||
#include <stdlib.h> | |||
#include <stdint.h> | |||
#include <limits.h> | |||
#ifdef __linux__ | |||
#include <endian.h> | |||
#endif | |||
#include "memops.h" | |||
#if defined (__SSE2__) && !defined (__sun__) | |||
#include <emmintrin.h> | |||
#ifdef __SSE4_1__ | |||
#include <smmintrin.h> | |||
#endif | |||
#endif | |||
#if defined (__ARM_NEON__) || defined (__ARM_NEON) | |||
#include <arm_neon.h> | |||
#endif | |||
// our additional headers | |||
#include <time.h> | |||
/* Dirty: include mempos.c twice the second time with SIMD disabled | |||
* so we can compare aceelerated non accelerated | |||
*/ | |||
namespace accelerated { | |||
#include "../common/memops.c" | |||
} | |||
namespace origerated { | |||
#ifdef __SSE2__ | |||
#undef __SSE2__ | |||
#endif | |||
#ifdef __ARM_NEON__ | |||
#undef __ARM_NEON__ | |||
#endif | |||
#ifdef __ARM_NEON | |||
#undef __ARM_NEON | |||
#endif | |||
#include "../common/memops.c" | |||
} | |||
// define conversion function types | |||
typedef void (*t_jack_to_integer)( | |||
char *dst, | |||
jack_default_audio_sample_t *src, | |||
unsigned long nsamples, | |||
unsigned long dst_skip, | |||
dither_state_t *state); | |||
typedef void (*t_integer_to_jack)( | |||
jack_default_audio_sample_t *dst, | |||
char *src, | |||
unsigned long nsamples, | |||
unsigned long src_skip); | |||
// define/setup test case data | |||
typedef struct test_case_data { | |||
uint32_t frame_size; | |||
uint32_t sample_size; | |||
bool reverse; | |||
t_jack_to_integer jack_to_integer_accel; | |||
t_jack_to_integer jack_to_integer_orig; | |||
t_integer_to_jack integer_to_jack_accel; | |||
t_integer_to_jack integer_to_jack_orig; | |||
dither_state_t *ditherstate; | |||
const char *name; | |||
} test_case_data_t; | |||
test_case_data_t test_cases[] = { | |||
{ | |||
4, | |||
3, | |||
true, | |||
accelerated::sample_move_d32u24_sSs, | |||
origerated::sample_move_d32u24_sSs, | |||
accelerated::sample_move_dS_s32u24s, | |||
origerated::sample_move_dS_s32u24s, | |||
NULL, | |||
"32u24s" }, | |||
{ | |||
4, | |||
3, | |||
false, | |||
accelerated::sample_move_d32u24_sS, | |||
origerated::sample_move_d32u24_sS, | |||
accelerated::sample_move_dS_s32u24, | |||
origerated::sample_move_dS_s32u24, | |||
NULL, | |||
"32u24" }, | |||
{ | |||
4, | |||
3, | |||
true, | |||
accelerated::sample_move_d32l24_sSs, | |||
origerated::sample_move_d32l24_sSs, | |||
accelerated::sample_move_dS_s32l24s, | |||
origerated::sample_move_dS_s32l24s, | |||
NULL, | |||
"32l24s" }, | |||
{ | |||
4, | |||
3, | |||
false, | |||
accelerated::sample_move_d32l24_sS, | |||
origerated::sample_move_d32l24_sS, | |||
accelerated::sample_move_dS_s32l24, | |||
origerated::sample_move_dS_s32l24, | |||
NULL, | |||
"32l24" }, | |||
{ | |||
3, | |||
3, | |||
true, | |||
accelerated::sample_move_d24_sSs, | |||
origerated::sample_move_d24_sSs, | |||
accelerated::sample_move_dS_s24s, | |||
origerated::sample_move_dS_s24s, | |||
NULL, | |||
"24s" }, | |||
{ | |||
3, | |||
3, | |||
false, | |||
accelerated::sample_move_d24_sS, | |||
origerated::sample_move_d24_sS, | |||
accelerated::sample_move_dS_s24, | |||
origerated::sample_move_dS_s24, | |||
NULL, | |||
"24" }, | |||
{ | |||
2, | |||
2, | |||
true, | |||
accelerated::sample_move_d16_sSs, | |||
origerated::sample_move_d16_sSs, | |||
accelerated::sample_move_dS_s16s, | |||
origerated::sample_move_dS_s16s, | |||
NULL, | |||
"16s" }, | |||
{ | |||
2, | |||
2, | |||
false, | |||
accelerated::sample_move_d16_sS, | |||
origerated::sample_move_d16_sS, | |||
accelerated::sample_move_dS_s16, | |||
origerated::sample_move_dS_s16, | |||
NULL, | |||
"16" }, | |||
}; | |||
// we need to repeat for better accuracy at time measurement | |||
const uint32_t retry_per_case = 1000; | |||
// setup test buffers | |||
#define TESTBUFF_SIZE 1024 | |||
jack_default_audio_sample_t jackbuffer_source[TESTBUFF_SIZE]; | |||
// integer buffers: max 4 bytes per value / * 2 for stereo | |||
char integerbuffer_accel[TESTBUFF_SIZE*4*2]; | |||
char integerbuffer_orig[TESTBUFF_SIZE*4*2]; | |||
// float buffers | |||
jack_default_audio_sample_t jackfloatbuffer_accel[TESTBUFF_SIZE]; | |||
jack_default_audio_sample_t jackfloatbuffer_orig[TESTBUFF_SIZE]; | |||
// comparing unsigned makes life easier | |||
uint32_t extract_integer( | |||
char* buff, | |||
uint32_t offset, | |||
uint32_t frame_size, | |||
uint32_t sample_size, | |||
bool big_endian) | |||
{ | |||
uint32_t retval = 0; | |||
unsigned char* curr; | |||
uint32_t mult = 1; | |||
if(big_endian) { | |||
curr = (unsigned char*)buff + offset + sample_size-1; | |||
for(uint32_t i=0; i<sample_size; i++) { | |||
retval += *(curr--) * mult; | |||
mult*=256; | |||
} | |||
} | |||
else { | |||
curr = (unsigned char*)buff + offset + frame_size-sample_size; | |||
for(uint32_t i=0; i<sample_size; i++) { | |||
retval += *(curr++) * mult; | |||
mult*=256; | |||
} | |||
} | |||
return retval; | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
// parse_arguments(argc, argv); | |||
uint32_t maxerr_displayed = 10; | |||
// fill jackbuffer | |||
for(int i=0; i<TESTBUFF_SIZE; i++) { | |||
// ramp | |||
jack_default_audio_sample_t value = | |||
((jack_default_audio_sample_t)((i % TESTBUFF_SIZE) - TESTBUFF_SIZE/2)) / (TESTBUFF_SIZE/2); | |||
// force clipping | |||
value *= 1.02; | |||
jackbuffer_source[i] = value; | |||
} | |||
for(uint32_t testcase=0; testcase<sizeof(test_cases)/sizeof(test_case_data_t); testcase++) { | |||
// test mono/stereo | |||
for(uint32_t channels=1; channels<=2; channels++) { | |||
////////////////////////////////////////////////////////////////////////////// | |||
// jackfloat -> integer | |||
// clean target buffers | |||
memset(integerbuffer_accel, 0, sizeof(integerbuffer_accel)); | |||
memset(integerbuffer_orig, 0, sizeof(integerbuffer_orig)); | |||
// accel | |||
clock_t time_to_integer_accel = clock(); | |||
for(uint32_t repetition=0; repetition<retry_per_case; repetition++) | |||
{ | |||
test_cases[testcase].jack_to_integer_accel( | |||
integerbuffer_accel, | |||
jackbuffer_source, | |||
TESTBUFF_SIZE, | |||
test_cases[testcase].frame_size*channels, | |||
test_cases[testcase].ditherstate); | |||
} | |||
float timediff_to_integer_accel = ((float)(clock() - time_to_integer_accel)) / CLOCKS_PER_SEC; | |||
// orig | |||
clock_t time_to_integer_orig = clock(); | |||
for(uint32_t repetition=0; repetition<retry_per_case; repetition++) | |||
{ | |||
test_cases[testcase].jack_to_integer_orig( | |||
integerbuffer_orig, | |||
jackbuffer_source, | |||
TESTBUFF_SIZE, | |||
test_cases[testcase].frame_size*channels, | |||
test_cases[testcase].ditherstate); | |||
} | |||
float timediff_to_integer_orig = ((float)(clock() - time_to_integer_orig)) / CLOCKS_PER_SEC; | |||
// output performance results | |||
printf( | |||
"JackFloat->Integer @%7.7s/%u: Orig %7.6f sec / Accel %7.6f sec -> Win: %5.2f %%\n", | |||
test_cases[testcase].name, | |||
channels, | |||
timediff_to_integer_orig, | |||
timediff_to_integer_accel, | |||
(timediff_to_integer_orig/timediff_to_integer_accel-1)*100.0); | |||
uint32_t int_deviation_max = 0; | |||
uint32_t int_error_count = 0; | |||
// output error (avoid spam -> limit error lines per test case) | |||
for(uint32_t sample=0; sample<TESTBUFF_SIZE; sample++) { | |||
uint32_t sample_offset = sample*test_cases[testcase].frame_size*channels; | |||
// compare both results | |||
uint32_t intval_accel=extract_integer( | |||
integerbuffer_accel, | |||
sample_offset, | |||
test_cases[testcase].frame_size, | |||
test_cases[testcase].sample_size, | |||
#if __BYTE_ORDER == __BIG_ENDIAN | |||
!test_cases[testcase].reverse); | |||
#else | |||
test_cases[testcase].reverse); | |||
#endif | |||
uint32_t intval_orig=extract_integer( | |||
integerbuffer_orig, | |||
sample_offset, | |||
test_cases[testcase].frame_size, | |||
test_cases[testcase].sample_size, | |||
#if __BYTE_ORDER == __BIG_ENDIAN | |||
!test_cases[testcase].reverse); | |||
#else | |||
test_cases[testcase].reverse); | |||
#endif | |||
// allow a deviation of 1 | |||
if(intval_accel>intval_orig+1 || intval_orig>intval_accel+1) { | |||
if(int_error_count<maxerr_displayed) { | |||
printf("Value error sample %u:", sample); | |||
printf(" Orig 0x"); | |||
char formatstr[10]; | |||
sprintf(formatstr, "%%0%uX", test_cases[testcase].sample_size*2); | |||
printf(formatstr, intval_orig); | |||
printf(" Accel 0x"); | |||
printf(formatstr, intval_accel); | |||
printf("\n"); | |||
} | |||
int_error_count++; | |||
uint32_t int_deviation; | |||
if(intval_accel > intval_orig) | |||
int_deviation = intval_accel-intval_orig; | |||
else | |||
int_deviation = intval_orig-intval_accel; | |||
if(int_deviation > int_deviation_max) | |||
int_deviation_max = int_deviation; | |||
} | |||
} | |||
printf( | |||
"JackFloat->Integer @%7.7s/%u: Errors: %u Max deviation %u\n", | |||
test_cases[testcase].name, | |||
channels, | |||
int_error_count, | |||
int_deviation_max); | |||
////////////////////////////////////////////////////////////////////////////// | |||
// integer -> jackfloat | |||
// clean target buffers | |||
memset(jackfloatbuffer_accel, 0, sizeof(jackfloatbuffer_accel)); | |||
memset(jackfloatbuffer_orig, 0, sizeof(jackfloatbuffer_orig)); | |||
// accel | |||
clock_t time_to_float_accel = clock(); | |||
for(uint32_t repetition=0; repetition<retry_per_case; repetition++) | |||
{ | |||
test_cases[testcase].integer_to_jack_accel( | |||
jackfloatbuffer_accel, | |||
integerbuffer_orig, | |||
TESTBUFF_SIZE, | |||
test_cases[testcase].frame_size*channels); | |||
} | |||
float timediff_to_float_accel = ((float)(clock() - time_to_float_accel)) / CLOCKS_PER_SEC; | |||
// orig | |||
clock_t time_to_float_orig = clock(); | |||
for(uint32_t repetition=0; repetition<retry_per_case; repetition++) | |||
{ | |||
test_cases[testcase].integer_to_jack_orig( | |||
jackfloatbuffer_orig, | |||
integerbuffer_orig, | |||
TESTBUFF_SIZE, | |||
test_cases[testcase].frame_size*channels); | |||
} | |||
float timediff_to_float_orig = ((float)(clock() - time_to_float_orig)) / CLOCKS_PER_SEC; | |||
// output performance results | |||
printf( | |||
"Integer->JackFloat @%7.7s/%u: Orig %7.6f sec / Accel %7.6f sec -> Win: %5.2f %%\n", | |||
test_cases[testcase].name, | |||
channels, | |||
timediff_to_float_orig, | |||
timediff_to_float_accel, | |||
(timediff_to_float_orig/timediff_to_float_accel-1)*100.0); | |||
jack_default_audio_sample_t float_deviation_max = 0.0; | |||
uint32_t float_error_count = 0; | |||
// output error (avoid spam -> limit error lines per test case) | |||
for(uint32_t sample=0; sample<TESTBUFF_SIZE; sample++) { | |||
// For easier estimation/readability we scale floats back to integer | |||
jack_default_audio_sample_t sample_scaling; | |||
switch(test_cases[testcase].sample_size) { | |||
case 2: | |||
sample_scaling = SAMPLE_16BIT_SCALING; | |||
break; | |||
default: | |||
sample_scaling = SAMPLE_24BIT_SCALING; | |||
break; | |||
} | |||
jack_default_audio_sample_t floatval_accel = jackfloatbuffer_accel[sample] * sample_scaling; | |||
jack_default_audio_sample_t floatval_orig = jackfloatbuffer_orig[sample] * sample_scaling; | |||
// compare both results | |||
jack_default_audio_sample_t float_deviation; | |||
if(floatval_accel > floatval_orig) | |||
float_deviation = floatval_accel-floatval_orig; | |||
else | |||
float_deviation = floatval_orig-floatval_accel; | |||
if(float_deviation > float_deviation_max) | |||
float_deviation_max = float_deviation; | |||
// deviation > half bit => error | |||
if(float_deviation > 0.5) { | |||
if(float_error_count<maxerr_displayed) { | |||
printf("Value error sample %u:", sample); | |||
printf(" Orig %8.1f Accel %8.1f\n", floatval_orig, floatval_accel); | |||
} | |||
float_error_count++; | |||
} | |||
} | |||
printf( | |||
"Integer->JackFloat @%7.7s/%u: Errors: %u Max deviation %f\n", | |||
test_cases[testcase].name, | |||
channels, | |||
float_error_count, | |||
float_deviation_max); | |||
printf("\n"); | |||
} | |||
} | |||
return 0; | |||
} |
@@ -1,220 +0,0 @@ | |||
/** @file simple_client.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 <math.h> | |||
#include <signal.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <jack/jack.h> | |||
jack_port_t *output_port1, *output_port2; | |||
jack_client_t *client; | |||
#ifndef M_PI | |||
#define M_PI (3.14159265) | |||
#endif | |||
#define TABLE_SIZE (200) | |||
typedef struct | |||
{ | |||
float sine[TABLE_SIZE]; | |||
int left_phase; | |||
int right_phase; | |||
} | |||
paTestData; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
/** | |||
* The process callback for this JACK application is called in a | |||
* special realtime thread once for each audio cycle. | |||
* | |||
* This client follows a simple rule: when the JACK transport is | |||
* running, copy the input port to the output. When it stops, exit. | |||
*/ | |||
int | |||
process (jack_nframes_t nframes, void *arg) | |||
{ | |||
jack_default_audio_sample_t *out1, *out2; | |||
paTestData *data = (paTestData*)arg; | |||
int i; | |||
out1 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port1, nframes); | |||
out2 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port2, nframes); | |||
for( i=0; i<nframes; i++ ) | |||
{ | |||
out1[i] = data->sine[data->left_phase]; /* left */ | |||
out2[i] = data->sine[data->right_phase]; /* right */ | |||
data->left_phase += 1; | |||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; | |||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ | |||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; | |||
} | |||
return 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[]) | |||
{ | |||
const char **ports; | |||
const char *client_name; | |||
const char *server_name = NULL; | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
paTestData data; | |||
int i; | |||
if (argc >= 2) { /* client name specified? */ | |||
client_name = argv[1]; | |||
if (argc >= 3) { /* server name specified? */ | |||
server_name = argv[2]; | |||
int my_option = JackNullOption | JackServerName; | |||
options = (jack_options_t)my_option; | |||
} | |||
} else { /* use basename of argv[0] */ | |||
client_name = strrchr(argv[0], '/'); | |||
if (client_name == 0) { | |||
client_name = argv[0]; | |||
} else { | |||
client_name++; | |||
} | |||
} | |||
for( i=0; i<TABLE_SIZE; i++ ) | |||
{ | |||
data.sine[i] = 0.2 * (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); | |||
} | |||
data.left_phase = data.right_phase = 0; | |||
/* 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, &data); | |||
/* 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); | |||
/* create two ports */ | |||
output_port1 = jack_port_register (client, "output1", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsOutput, 0); | |||
output_port2 = jack_port_register (client, "output2", | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
JackPortIsOutput, 0); | |||
if ((output_port1 == NULL) || (output_port2 == 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|JackPortIsInput); | |||
if (ports == NULL) { | |||
fprintf(stderr, "no physical playback ports\n"); | |||
exit (1); | |||
} | |||
if (jack_connect (client, jack_port_name (output_port1), ports[0])) { | |||
fprintf (stderr, "cannot connect output ports\n"); | |||
} | |||
if (jack_connect (client, jack_port_name (output_port2), ports[1])) { | |||
fprintf (stderr, "cannot connect output ports\n"); | |||
} | |||
jack_free (ports); | |||
/* 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 | |||
/* keep running until the Ctrl+C */ | |||
while (1) { | |||
#ifdef WIN32 | |||
Sleep(1000); | |||
#else | |||
sleep (1); | |||
#endif | |||
} | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,206 +0,0 @@ | |||
/** @file simple_session_client.c | |||
* | |||
* @brief This simple client demonstrates the most basic features of JACK | |||
* as they would be used by many applications. | |||
* this version also adds session manager functionality. | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/types.h> | |||
#include <jack/session.h> | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
jack_client_t *client; | |||
int simple_quit = 0; | |||
/** | |||
* 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; | |||
in = jack_port_get_buffer (input_port, nframes); | |||
out = jack_port_get_buffer (output_port, nframes); | |||
memcpy (out, in, | |||
sizeof (jack_default_audio_sample_t) * nframes); | |||
return 0; | |||
} | |||
void | |||
session_callback (jack_session_event_t *event, void *arg) | |||
{ | |||
char retval[100]; | |||
printf ("session notification\n"); | |||
printf ("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit"); | |||
snprintf (retval, 100, "jack_simple_session_client %s", event->client_uuid); | |||
event->command_line = strdup (retval); | |||
jack_session_reply( client, event ); | |||
if (event->type == JackSessionSaveAndQuit) { | |||
simple_quit = 1; | |||
} | |||
jack_session_event_free (event); | |||
} | |||
/** | |||
* 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[]) | |||
{ | |||
const char **ports; | |||
const char *client_name = "simple"; | |||
jack_status_t status; | |||
/* open a client connection to the JACK server */ | |||
if( argc == 1 ) | |||
client = jack_client_open (client_name, JackNullOption, &status ); | |||
else if( argc == 2 ) | |||
client = jack_client_open (client_name, JackSessionID, &status, argv[1] ); | |||
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 `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); | |||
/* tell the JACK server to call `session_callback()' if | |||
the session is saved. | |||
*/ | |||
jack_set_session_callback (client, session_callback, NULL); | |||
/* 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. | |||
*/ | |||
/* only do the autoconnect when not reloading from a session. | |||
* in case of a session reload, the SM will restore our connections | |||
*/ | |||
if (argc==1) { | |||
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 until we get a quit event */ | |||
while (!simple_quit) | |||
#ifdef WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,217 +0,0 @@ | |||
/** @file thru_client.c | |||
* | |||
* @brief This simple through 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 <math.h> | |||
#include <signal.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <jack/jack.h> | |||
jack_port_t **input_ports; | |||
jack_port_t **output_ports; | |||
jack_client_t *client; | |||
static void signal_handler ( int sig ) | |||
{ | |||
jack_client_close ( client ); | |||
fprintf ( stderr, "signal received, exiting ...\n" ); | |||
exit ( 0 ); | |||
} | |||
/** | |||
* The process callback for this JACK application is called in a | |||
* special realtime thread once for each audio cycle. | |||
* | |||
* This client follows a simple rule: when the JACK transport is | |||
* running, copy the input port to the output. When it stops, exit. | |||
*/ | |||
int | |||
process ( jack_nframes_t nframes, void *arg ) | |||
{ | |||
int i; | |||
jack_default_audio_sample_t *in, *out; | |||
for ( i = 0; i < 2; i++ ) | |||
{ | |||
in = jack_port_get_buffer ( input_ports[i], nframes ); | |||
out = jack_port_get_buffer ( output_ports[i], nframes ); | |||
memcpy ( out, in, nframes * sizeof ( jack_default_audio_sample_t ) ); | |||
} | |||
return 0; | |||
} | |||
/** | |||
* JACK calls this shutdown_callback if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
void | |||
jack_shutdown ( void *arg ) | |||
{ | |||
free ( input_ports ); | |||
free ( output_ports ); | |||
exit ( 1 ); | |||
} | |||
int | |||
main ( int argc, char *argv[] ) | |||
{ | |||
int i; | |||
const char **ports; | |||
const char *client_name; | |||
const char *server_name = NULL; | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
if ( argc >= 2 ) /* client name specified? */ | |||
{ | |||
client_name = argv[1]; | |||
if ( argc >= 3 ) /* server name specified? */ | |||
{ | |||
server_name = argv[2]; | |||
options |= JackServerName; | |||
} | |||
} | |||
else /* use basename of argv[0] */ | |||
{ | |||
client_name = strrchr ( argv[0], '/' ); | |||
if ( client_name == 0 ) | |||
{ | |||
client_name = argv[0]; | |||
} | |||
else | |||
{ | |||
client_name++; | |||
} | |||
} | |||
/* 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 `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 ); | |||
/* create two ports pairs*/ | |||
input_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) ); | |||
output_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) ); | |||
char port_name[16]; | |||
for ( i = 0; i < 2; i++ ) | |||
{ | |||
sprintf ( port_name, "input_%d", i + 1 ); | |||
input_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); | |||
sprintf ( port_name, "output_%d", i + 1 ); | |||
output_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); | |||
if ( ( input_ports[i] == NULL ) || ( output_ports[i] == 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 ); | |||
} | |||
for ( i = 0; i < 2; i++ ) | |||
if ( jack_connect ( client, ports[i], jack_port_name ( input_ports[i] ) ) ) | |||
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 ); | |||
} | |||
for ( i = 0; i < 2; i++ ) | |||
if ( jack_connect ( client, jack_port_name ( output_ports[i] ), ports[i] ) ) | |||
fprintf ( stderr, "cannot connect input ports\n" ); | |||
free ( ports ); | |||
/* 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 | |||
/* keep running until the transport stops */ | |||
while (1) | |||
{ | |||
#ifdef WIN32 | |||
Sleep ( 1000 ); | |||
#else | |||
sleep ( 1 ); | |||
#endif | |||
} | |||
jack_client_close ( client ); | |||
exit ( 0 ); | |||
} |
@@ -1,110 +0,0 @@ | |||
#! /usr/bin/python3 | |||
# encoding: utf-8 | |||
example_programs = { | |||
'jack_cpu_load': 'cpu_load.c', | |||
'jack_latent_client': 'latent_client.c', | |||
'jack_metro': 'metro.c', | |||
'jack_midi_latency_test': 'midi_latency_test.c', | |||
'jack_midiseq': 'midiseq.c', | |||
'jack_midisine': 'midisine.c', | |||
'jack_net_master': 'netmaster.c', | |||
'jack_net_slave': 'netslave.c', | |||
'jack_server_control': 'server_control.cpp', | |||
'jack_showtime': 'showtime.c', | |||
'jack_simdtests': 'simdtests.cpp', | |||
'jack_simple_client': 'simple_client.c', | |||
'jack_simple_session_client': 'simple_session_client.c', | |||
'jack_thru': 'thru_client.c', | |||
'jack_zombie': 'zombie.c', | |||
} | |||
example_libs = { | |||
'inprocess': 'inprocess.c', | |||
} | |||
def configure(conf): | |||
conf.env['BUILD_EXAMPLE_CLIENT_REC'] = conf.env['SNDFILE'] | |||
def build(bld): | |||
if bld.env['IS_LINUX']: | |||
os_incdir = ['../linux', '../posix'] | |||
if bld.env['IS_MACOSX']: | |||
os_incdir = ['../macosx', '../posix'] | |||
if bld.env['IS_FREEBSD']: | |||
os_incdir = ['../freebsd', '../posix'] | |||
if bld.env['IS_SUN']: | |||
os_incdir = ['../solaris', '../posix'] | |||
if bld.env['IS_WINDOWS']: | |||
os_incdir = ['../windows'] | |||
for example_program, example_program_source in list(example_programs.items()): | |||
if example_program == 'jack_server_control': | |||
use = ['serverlib', 'STDC++'] | |||
elif example_program == 'jack_net_slave': | |||
if not bld.env['BUILD_NETLIB']: | |||
continue | |||
use = ['netlib'] | |||
elif example_program == 'jack_net_master': | |||
if not bld.env['BUILD_NETLIB']: | |||
continue | |||
use = ['netlib'] | |||
else: | |||
use = ['clientlib'] | |||
if example_program == 'jack_simdtests': | |||
ftrs = 'cxx cxxprogram' | |||
else: | |||
ftrs = 'c cprogram' | |||
if bld.env['IS_MACOSX']: | |||
prog = bld(features=ftrs, framework=['Foundation']) | |||
else: | |||
prog = bld(features=ftrs) | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = example_program_source | |||
prog.use = use | |||
if bld.env['IS_LINUX']: | |||
prog.use += ['RT', 'M'] | |||
if bld.env['IS_SUN']: | |||
prog.use += ['M'] | |||
if bld.env['IS_FREEBSD']: | |||
prog.use += ['M'] | |||
if bld.env['IS_WINDOWS'] and bld.env['BUILD_STATIC']: | |||
prog.env['LIB_PTHREAD'] = [':libwinpthread.a'] | |||
prog.target = example_program | |||
if bld.env['BUILD_EXAMPLE_CLIENT_REC']: | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = 'capture_client.c' | |||
prog.use = ['clientlib'] | |||
if bld.env['IS_MACOSX']: | |||
prog.use += ['SNDFILE'] | |||
if bld.env['IS_LINUX']: | |||
prog.use += ['RT', 'SNDFILE'] | |||
if bld.env['IS_FREEBSD']: | |||
prog.use += ['SNDFILE'] | |||
if bld.env['IS_SUN']: | |||
prog.use += ['RT', 'SNDFILE'] | |||
if bld.env['IS_WINDOWS']: | |||
prog.uselib = ['SNDFILE'] | |||
if bld.env['BUILD_STATIC']: | |||
prog.env['LIB_PTHREAD'] = [':libwinpthread.a'] | |||
prog.target = 'jack_rec' | |||
for example_lib, example_lib_source in list(example_libs.items()): | |||
lib = bld(features='c cshlib') | |||
if not bld.env['IS_WINDOWS']: | |||
lib.env['cshlib_PATTERN'] = '%s.so' | |||
lib.includes = os_incdir + ['../common/jack', '../common'] | |||
lib.target = example_lib | |||
lib.source = example_lib_source | |||
if bld.env['IS_SUN']: | |||
lib.env.append_value('LINKFLAGS', '-lm') | |||
if bld.env['IS_WINDOWS'] and bld.env['BUILD_STATIC']: | |||
prog.env['LIB_PTHREAD'] = [':libwinpthread.a'] | |||
lib.use = 'serverlib' | |||
lib.install_path = '${ADDON_DIR}/' |
@@ -1,95 +0,0 @@ | |||
/* | |||
Copyright (C) 2002 Jeremy Hall | |||
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. | |||
$Id: zombie.c,v 1.1 2005/08/18 11:42:08 letz Exp $ | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <jack/jack.h> | |||
int running = 1; | |||
int count = 0; | |||
jack_port_t* output_port; | |||
static int | |||
process(jack_nframes_t nframes, void* arg) | |||
{ | |||
if (count++ == 1000) { | |||
printf("process block\n"); | |||
//while (1) {} | |||
#if WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
} | |||
return 0; | |||
} | |||
static void | |||
shutdown_handler (void *arg) | |||
{ | |||
printf("shutdown \n"); | |||
running = 0; | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_client_t* client = NULL; | |||
/* try to become a client of the JACK server */ | |||
if ((client = jack_client_open ("zombie", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
goto error; | |||
} | |||
jack_set_process_callback (client, process, NULL); | |||
jack_on_shutdown(client, shutdown_handler, NULL); | |||
output_port = jack_port_register (client, "port1", 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"); | |||
goto error; | |||
} | |||
jack_connect(client, jack_port_name(output_port), "coreaudio:Built-in Audio:in2"); | |||
while (running) { | |||
#if WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
printf ("run\n"); | |||
} | |||
jack_deactivate (client); | |||
jack_client_close (client); | |||
return 0; | |||
error: | |||
if (client) | |||
jack_client_close (client); | |||
return 1; | |||
} | |||
@@ -1,133 +0,0 @@ | |||
/* | |||
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 <stdlib.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
char * my_name; | |||
void | |||
show_version (void) | |||
{ | |||
//fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", my_name); | |||
} | |||
void | |||
show_usage (void) | |||
{ | |||
show_version (); | |||
fprintf (stderr, "\nUsage: %s [options] portname alias\n", my_name); | |||
fprintf (stderr, "List active Jack ports, and optionally display extra information.\n\n"); | |||
fprintf (stderr, "Display options:\n"); | |||
fprintf (stderr, " -u, --unalias remove `alias' as an alias for `port'\n"); | |||
fprintf (stderr, " -h, --help Display this help message\n"); | |||
fprintf (stderr, " --version Output version information and exit\n\n"); | |||
fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_client_t *client; | |||
jack_status_t status; | |||
char* portname; | |||
char* alias; | |||
int unset = 0; | |||
int ret; | |||
int c; | |||
int option_index; | |||
extern int optind; | |||
jack_port_t* port; | |||
struct option long_options[] = { | |||
{ "unalias", 0, 0, 'u' }, | |||
{ "help", 0, 0, 'h' }, | |||
{ "version", 0, 0, 'v' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
if (argc < 3) { | |||
show_usage (); | |||
return 1; | |||
} | |||
my_name = strrchr(argv[0], '/'); | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name ++; | |||
} | |||
while ((c = getopt_long (argc, argv, "uhv", long_options, &option_index)) >= 0) { | |||
switch (c) { | |||
case 'u': | |||
unset = 1; | |||
break; | |||
case 'h': | |||
show_usage (); | |||
return 1; | |||
break; | |||
case 'v': | |||
show_version (); | |||
return 1; | |||
break; | |||
default: | |||
show_usage (); | |||
return 1; | |||
break; | |||
} | |||
} | |||
portname = argv[optind++]; | |||
alias = argv[optind]; | |||
/* Open a client connection to the JACK server. Starting a | |||
* new server only to list its ports seems pointless, so we | |||
* specify JackNoStartServer. */ | |||
//JOQ: need a new server name option | |||
client = jack_client_open ("lsp", JackNoStartServer, &status); | |||
if (client == NULL) { | |||
if (status & JackServerFailed) { | |||
fprintf (stderr, "JACK server not running\n"); | |||
} else { | |||
fprintf (stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
} | |||
return 1; | |||
} | |||
if ((port = jack_port_by_name (client, portname)) == 0) { | |||
fprintf (stderr, "No port named \"%s\"\n", portname); | |||
return 1; | |||
} | |||
if (!unset) { | |||
ret = jack_port_set_alias (port, alias); | |||
} else { | |||
ret = jack_port_unset_alias (port, alias); | |||
} | |||
jack_client_close (client); | |||
return ret; | |||
} |
@@ -1,846 +0,0 @@ | |||
/** @file simple_client.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 <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#include <math.h> | |||
#include <jack/jack.h> | |||
#include <jack/jslist.h> | |||
#include "memops.h" | |||
#include "alsa/asoundlib.h" | |||
#include <samplerate.h> | |||
// Here are the lists of the jack ports... | |||
JSList *capture_ports = NULL; | |||
JSList *capture_srcs = NULL; | |||
JSList *playback_ports = NULL; | |||
JSList *playback_srcs = NULL; | |||
jack_client_t *client; | |||
snd_pcm_t *alsa_handle; | |||
int jack_sample_rate; | |||
int jack_buffer_size; | |||
int quit = 0; | |||
double resample_mean = 1.0; | |||
double static_resample_factor = 1.0; | |||
double resample_lower_limit = 0.25; | |||
double resample_upper_limit = 4.0; | |||
double *offset_array; | |||
double *window_array; | |||
int offset_differential_index = 0; | |||
double offset_integral = 0; | |||
// ------------------------------------------------------ commandline parameters | |||
int sample_rate = 0; /* stream rate */ | |||
int num_channels = 2; /* count of channels */ | |||
int period_size = 1024; | |||
int num_periods = 2; | |||
int target_delay = 0; /* the delay which the program should try to approach. */ | |||
int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ | |||
int catch_factor = 100000; | |||
int catch_factor2 = 10000; | |||
double pclamp = 15.0; | |||
double controlquant = 10000.0; | |||
int smooth_size = 256; | |||
int good_window=0; | |||
int verbose = 0; | |||
int instrument = 0; | |||
int samplerate_quality = 2; | |||
// Debug stuff: | |||
volatile float output_resampling_factor = 1.0; | |||
volatile int output_new_delay = 0; | |||
volatile float output_offset = 0.0; | |||
volatile float output_integral = 0.0; | |||
volatile float output_diff = 0.0; | |||
volatile int running_freewheel = 0; | |||
snd_pcm_uframes_t real_buffer_size; | |||
snd_pcm_uframes_t real_period_size; | |||
// buffers | |||
char *tmpbuf; | |||
char *outbuf; | |||
float *resampbuf; | |||
// format selection, and corresponding functions from memops in a nice set of structs. | |||
typedef struct alsa_format { | |||
snd_pcm_format_t format_id; | |||
size_t sample_size; | |||
void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
const char *name; | |||
} alsa_format_t; | |||
alsa_format_t formats[] = { | |||
{ SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, | |||
{ SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, | |||
{ SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" }, | |||
{ SND_PCM_FORMAT_S24, 4, sample_move_d32l24_sS, sample_move_dS_s32l24, "24bit" }, | |||
{ SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } | |||
#ifdef __ANDROID__ | |||
,{ SND_PCM_FORMAT_S16_LE, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit little-endian" } | |||
#endif | |||
}; | |||
#define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) | |||
int format=0; | |||
// Alsa stuff... i don't want to touch this bullshit in the next years.... please... | |||
static int xrun_recovery(snd_pcm_t *handle, int err) { | |||
// printf( "xrun !!!.... %d\n", err ); | |||
if (err == -EPIPE) { /* under-run */ | |||
err = snd_pcm_prepare(handle); | |||
if (err < 0) | |||
printf("Can't recover from underrun, prepare failed: %s\n", snd_strerror(err)); | |||
return 0; | |||
} else if (err == -ESTRPIPE) { | |||
while ((err = snd_pcm_resume(handle)) == -EAGAIN) | |||
usleep(100); /* wait until the suspend flag is released */ | |||
if (err < 0) { | |||
err = snd_pcm_prepare(handle); | |||
if (err < 0) | |||
printf("Can't recover from suspend, prepare failed: %s\n", snd_strerror(err)); | |||
} | |||
return 0; | |||
} | |||
return err; | |||
} | |||
static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) | |||
{ | |||
#ifdef __ANDROID__ | |||
format = 5; | |||
snd_pcm_hw_params_set_format(handle, params, formats[format].format_id); | |||
return 0; | |||
#else | |||
int i; | |||
int err; | |||
for( i=0; i<NUMFORMATS; i++ ) { | |||
/* set the sample format */ | |||
err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id); | |||
if (err == 0) { | |||
format = i; | |||
return 0; | |||
} | |||
} | |||
return err; | |||
#endif | |||
} | |||
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) { | |||
int err, dir=0; | |||
unsigned int buffer_time; | |||
unsigned int period_time; | |||
unsigned int rrate; | |||
unsigned int rchannels; | |||
/* choose all parameters */ | |||
err = snd_pcm_hw_params_any(handle, params); | |||
if (err < 0) { | |||
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the interleaved read/write format */ | |||
err = snd_pcm_hw_params_set_access(handle, params, access); | |||
if (err < 0) { | |||
printf("Access type not available for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the sample format */ | |||
err = set_hwformat(handle, params); | |||
if (err < 0) { | |||
printf("Sample format not available for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the count of channels */ | |||
rchannels = channels; | |||
err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels); | |||
if (err < 0) { | |||
printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); | |||
return err; | |||
} | |||
if (rchannels != channels) { | |||
printf("WARNING: channel count does not match (requested %d got %d)\n", channels, rchannels); | |||
num_channels = rchannels; | |||
} | |||
/* set the stream rate */ | |||
rrate = rate; | |||
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); | |||
if (err < 0) { | |||
printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); | |||
return err; | |||
} | |||
if (rrate != rate) { | |||
printf("WARNING: Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate); | |||
sample_rate = rrate; | |||
} | |||
/* set the buffer time */ | |||
buffer_time = 1000000*(uint64_t)period*nperiods/rate; | |||
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); | |||
if (err < 0) { | |||
printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size ); | |||
if (err < 0) { | |||
printf("Unable to get buffer size back: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
if( real_buffer_size != nperiods * period ) { | |||
printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size ); | |||
} | |||
/* set the period time */ | |||
period_time = 1000000*(uint64_t)period/rate; | |||
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); | |||
if (err < 0) { | |||
printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL ); | |||
if (err < 0) { | |||
printf("Unable to get period size back: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
if( real_period_size != period ) { | |||
printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size ); | |||
} | |||
/* write the parameters to device */ | |||
err = snd_pcm_hw_params(handle, params); | |||
if (err < 0) { | |||
printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
return 0; | |||
} | |||
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period) { | |||
int err; | |||
/* get the current swparams */ | |||
err = snd_pcm_sw_params_current(handle, swparams); | |||
if (err < 0) { | |||
printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* start the transfer when the buffer is full */ | |||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period ); | |||
if (err < 0) { | |||
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 ); | |||
if (err < 0) { | |||
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* allow the transfer when at least period_size samples can be processed */ | |||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, 2*period ); | |||
if (err < 0) { | |||
printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* align all transfers to 1 sample */ | |||
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); | |||
if (err < 0) { | |||
printf("Unable to set transfer align for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* write the parameters to the playback device */ | |||
err = snd_pcm_sw_params(handle, swparams); | |||
if (err < 0) { | |||
printf("Unable to set sw params for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
return 0; | |||
} | |||
// ok... i only need this function to communicate with the alsa bloat api... | |||
static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) { | |||
int err; | |||
snd_pcm_t *handle; | |||
snd_pcm_hw_params_t *hwparams; | |||
snd_pcm_sw_params_t *swparams; | |||
snd_pcm_hw_params_alloca(&hwparams); | |||
snd_pcm_sw_params_alloca(&swparams); | |||
if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) { | |||
printf("Capture open error: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) { | |||
printf("Setting of hwparams failed: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
if ((err = set_swparams(handle, swparams, period)) < 0) { | |||
printf("Setting of swparams failed: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
snd_pcm_start( handle ); | |||
snd_pcm_wait( handle, 200 ); | |||
return handle; | |||
} | |||
double hann( double x ) | |||
{ | |||
return 0.5 * (1.0 - cos( 2*M_PI * x ) ); | |||
} | |||
/** | |||
* The freewheel callback. | |||
*/ | |||
void freewheel (int starting, void* arg) { | |||
running_freewheel = starting; | |||
} | |||
/** | |||
* The process callback for this JACK application. | |||
* It is called by JACK at the appropriate times. | |||
*/ | |||
int process (jack_nframes_t nframes, void *arg) { | |||
if (running_freewheel) { | |||
JSList *node = capture_ports; | |||
while ( node != NULL) | |||
{ | |||
jack_port_t *port = (jack_port_t *) node->data; | |||
float *buf = jack_port_get_buffer (port, nframes); | |||
memset(buf, 0, sizeof(float)*nframes); | |||
node = jack_slist_next (node); | |||
} | |||
return 0; | |||
} | |||
int rlen; | |||
int err; | |||
snd_pcm_sframes_t delay = target_delay; | |||
int put_back_samples=0; | |||
int i; | |||
delay = snd_pcm_avail( alsa_handle ); | |||
delay -= round( jack_frames_since_cycle_start( client ) / static_resample_factor ); | |||
// Do it the hard way. | |||
// this is for compensating xruns etc... | |||
if( delay > (target_delay+max_diff) ) { | |||
output_new_delay = (int) delay; | |||
while ((delay-target_delay) > 0) { | |||
snd_pcm_uframes_t to_read = ((delay-target_delay) > 512) ? 512 : (delay-target_delay); | |||
snd_pcm_readi( alsa_handle, tmpbuf, to_read ); | |||
delay -= to_read; | |||
} | |||
delay = target_delay; | |||
// Set the resample_rate... we need to adjust the offset integral, to do this. | |||
// first look at the PI controller, this code is just a special case, which should never execute once | |||
// everything is swung in. | |||
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
// Also clear the array. we are beginning a new control cycle. | |||
for( i=0; i<smooth_size; i++ ) | |||
offset_array[i] = 0.0; | |||
} | |||
if( delay < (target_delay-max_diff) ) { | |||
snd_pcm_rewind( alsa_handle, target_delay - delay ); | |||
output_new_delay = (int) delay; | |||
delay = target_delay; | |||
// Set the resample_rate... we need to adjust the offset integral, to do this. | |||
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
// Also clear the array. we are beginning a new control cycle. | |||
for( i=0; i<smooth_size; i++ ) | |||
offset_array[i] = 0.0; | |||
} | |||
/* ok... now we should have target_delay +- max_diff on the alsa side. | |||
* | |||
* calculate the number of frames, we want to get. | |||
*/ | |||
double offset = delay - target_delay; | |||
// Save offset. | |||
offset_array[(offset_differential_index++)% smooth_size ] = offset; | |||
// Build the mean of the windowed offset array | |||
// basically fir lowpassing. | |||
double smooth_offset = 0.0; | |||
for( i=0; i<smooth_size; i++ ) | |||
smooth_offset += | |||
offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i]; | |||
smooth_offset /= (double) smooth_size; | |||
// this is the integral of the smoothed_offset | |||
offset_integral += smooth_offset; | |||
// Clamp offset. | |||
// the smooth offset still contains unwanted noise | |||
// which would go straight onto the resample coeff. | |||
// it only used in the P component and the I component is used for the fine tuning anyways. | |||
if( fabs( smooth_offset ) < pclamp ) | |||
smooth_offset = 0.0; | |||
// ok. now this is the PI controller. | |||
// u(t) = K * ( e(t) + 1/T \int e(t') dt' ) | |||
// K = 1/catch_factor and T = catch_factor2 | |||
double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2; | |||
// now quantize this value around resample_mean, so that the noise which is in the integral component doesn't hurt. | |||
current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean; | |||
// Output "instrumentatio" gonna change that to real instrumentation in a few. | |||
output_resampling_factor = (float) current_resample_factor; | |||
output_diff = (float) smooth_offset; | |||
output_integral = (float) offset_integral; | |||
output_offset = (float) offset; | |||
// Clamp a bit. | |||
if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit; | |||
if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit; | |||
// Now Calculate how many samples we need. | |||
rlen = ceil( ((double)nframes) / current_resample_factor )+2; | |||
assert( rlen > 2 ); | |||
// Calculate resample_mean so we can init ourselves to saner values. | |||
resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; | |||
// get the data... | |||
again: | |||
err = snd_pcm_readi(alsa_handle, outbuf, rlen); | |||
if( err < 0 ) { | |||
printf( "err = %d\n", err ); | |||
if (xrun_recovery(alsa_handle, err) < 0) { | |||
//printf("Write error: %s\n", snd_strerror(err)); | |||
//exit(EXIT_FAILURE); | |||
} | |||
goto again; | |||
} | |||
if( err != rlen ) { | |||
//printf( "read = %d\n", rlen ); | |||
} | |||
/* | |||
* render jack ports to the outbuf... | |||
*/ | |||
int chn = 0; | |||
JSList *node = capture_ports; | |||
JSList *src_node = capture_srcs; | |||
SRC_DATA src; | |||
while ( node != NULL) | |||
{ | |||
jack_port_t *port = (jack_port_t *) node->data; | |||
float *buf = jack_port_get_buffer (port, nframes); | |||
SRC_STATE *src_state = src_node->data; | |||
formats[format].soundcard_to_jack( resampbuf, outbuf + format[formats].sample_size * chn, rlen, num_channels*format[formats].sample_size ); | |||
src.data_in = resampbuf; | |||
src.input_frames = rlen; | |||
src.data_out = buf; | |||
src.output_frames = nframes; | |||
src.end_of_input = 0; | |||
src.src_ratio = current_resample_factor; | |||
src_process( src_state, &src ); | |||
put_back_samples = rlen-src.input_frames_used; | |||
src_node = jack_slist_next (src_node); | |||
node = jack_slist_next (node); | |||
chn++; | |||
} | |||
// Put back the samples libsamplerate did not consume. | |||
//printf( "putback = %d\n", put_back_samples ); | |||
snd_pcm_rewind( alsa_handle, put_back_samples ); | |||
return 0; | |||
} | |||
/** | |||
* the latency callback. | |||
* sets up the latencies on the ports. | |||
*/ | |||
void | |||
latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
{ | |||
jack_latency_range_t range; | |||
JSList *node; | |||
range.min = range.max = round(target_delay * static_resample_factor); | |||
if (mode == JackCaptureLatency) { | |||
for (node = capture_ports; node; node = jack_slist_next (node)) { | |||
jack_port_t *port = node->data; | |||
jack_port_set_latency_range (port, mode, &range); | |||
} | |||
} else { | |||
for (node = playback_ports; node; node = jack_slist_next (node)) { | |||
jack_port_t *port = node->data; | |||
jack_port_set_latency_range (port, mode, &range); | |||
} | |||
} | |||
} | |||
/** | |||
* Allocate the necessary jack ports... | |||
*/ | |||
void alloc_ports( int n_capture, int n_playback ) { | |||
int port_flags = JackPortIsOutput; | |||
int chn; | |||
jack_port_t *port; | |||
char buf[32]; | |||
capture_ports = NULL; | |||
for (chn = 0; chn < n_capture; chn++) | |||
{ | |||
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); | |||
port = jack_port_register (client, buf, | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
port_flags, 0); | |||
if (!port) | |||
{ | |||
printf( "jacknet_client: cannot register port for %s", buf); | |||
break; | |||
} | |||
capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
capture_ports = jack_slist_append (capture_ports, port); | |||
} | |||
port_flags = JackPortIsInput; | |||
playback_ports = NULL; | |||
for (chn = 0; chn < n_playback; chn++) | |||
{ | |||
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); | |||
port = jack_port_register (client, buf, | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
port_flags, 0); | |||
if (!port) | |||
{ | |||
printf( "jacknet_client: cannot register port for %s", buf); | |||
break; | |||
} | |||
playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
playback_ports = jack_slist_append (playback_ports, port); | |||
} | |||
} | |||
/** | |||
* This is the shutdown callback for this JACK application. | |||
* It is called by JACK if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
void jack_shutdown (void *arg) { | |||
exit (1); | |||
} | |||
/** | |||
* be user friendly. | |||
* be user friendly. | |||
* be user friendly. | |||
*/ | |||
void printUsage() { | |||
fprintf(stderr, "usage: alsa_out [options]\n" | |||
"\n" | |||
" -j <jack name> - client name\n" | |||
" -S <server name> - server to connect\n" | |||
" -d <alsa_device> \n" | |||
" -c <channels> \n" | |||
" -p <period_size> \n" | |||
" -n <num_period> \n" | |||
" -r <sample_rate> \n" | |||
" -q <sample_rate quality [0..4]\n" | |||
" -m <max_diff> \n" | |||
" -t <target_delay> \n" | |||
" -i turns on instrumentation\n" | |||
" -v turns on printouts\n" | |||
"\n"); | |||
} | |||
/** | |||
* the main function.... | |||
*/ | |||
void | |||
sigterm_handler( int signal ) | |||
{ | |||
quit = 1; | |||
} | |||
int main (int argc, char *argv[]) { | |||
char jack_name[30] = "alsa_in"; | |||
char alsa_device[30] = "hw:0"; | |||
char *server_name = NULL; | |||
int jack_opts = 0; | |||
extern char *optarg; | |||
extern int optind, optopt; | |||
int errflg=0; | |||
int c; | |||
while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:S:")) != -1) { | |||
switch(c) { | |||
case 'j': | |||
strcpy(jack_name,optarg); | |||
break; | |||
case 'r': | |||
sample_rate = atoi(optarg); | |||
break; | |||
case 'c': | |||
num_channels = atoi(optarg); | |||
break; | |||
case 'p': | |||
period_size = atoi(optarg); | |||
break; | |||
case 'n': | |||
num_periods = atoi(optarg); | |||
break; | |||
case 'd': | |||
strcpy(alsa_device,optarg); | |||
break; | |||
case 't': | |||
target_delay = atoi(optarg); | |||
break; | |||
case 'q': | |||
samplerate_quality = atoi(optarg); | |||
break; | |||
case 'm': | |||
max_diff = atoi(optarg); | |||
break; | |||
case 'f': | |||
catch_factor = atoi(optarg); | |||
break; | |||
case 'F': | |||
catch_factor2 = atoi(optarg); | |||
break; | |||
case 'C': | |||
pclamp = (double) atoi(optarg); | |||
break; | |||
case 'Q': | |||
controlquant = (double) atoi(optarg); | |||
break; | |||
case 'v': | |||
verbose = 1; | |||
break; | |||
case 'i': | |||
instrument = 1; | |||
break; | |||
case 's': | |||
smooth_size = atoi(optarg); | |||
break; | |||
case 'S': | |||
server_name = optarg; | |||
jack_opts |= JackServerName; | |||
break; | |||
case ':': | |||
fprintf(stderr, | |||
"Option -%c requires an operand\n", optopt); | |||
errflg++; | |||
break; | |||
case '?': | |||
fprintf(stderr, | |||
"Unrecognized option: -%c\n", optopt); | |||
errflg++; | |||
} | |||
} | |||
if (errflg) { | |||
printUsage(); | |||
exit(2); | |||
} | |||
if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { | |||
fprintf (stderr, "invalid samplerate quality\n"); | |||
return 1; | |||
} | |||
if ((client = jack_client_open (jack_name, jack_opts, NULL, server_name)) == 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 `freewheel()' whenever | |||
freewheel mode changes. | |||
*/ | |||
jack_set_freewheel_callback (client, freewheel, 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); | |||
if (jack_set_latency_callback) | |||
jack_set_latency_callback (client, latency_cb, 0); | |||
// get jack sample_rate | |||
jack_sample_rate = jack_get_sample_rate( client ); | |||
if( !sample_rate ) | |||
sample_rate = jack_sample_rate; | |||
// now open the alsa fd... | |||
alsa_handle = open_audiofd( alsa_device, 1, sample_rate, num_channels, period_size, num_periods); | |||
if( alsa_handle == 0 ) | |||
exit(20); | |||
printf( "selected sample format: %s\n", formats[format].name ); | |||
static_resample_factor = (double) jack_sample_rate / (double) sample_rate; | |||
resample_lower_limit = static_resample_factor * 0.25; | |||
resample_upper_limit = static_resample_factor * 4.0; | |||
resample_mean = static_resample_factor; | |||
offset_array = malloc( sizeof(double) * smooth_size ); | |||
if( offset_array == NULL ) { | |||
fprintf( stderr, "no memory for offset_array !!!\n" ); | |||
exit(20); | |||
} | |||
window_array = malloc( sizeof(double) * smooth_size ); | |||
if( window_array == NULL ) { | |||
fprintf( stderr, "no memory for window_array !!!\n" ); | |||
exit(20); | |||
} | |||
int i; | |||
for( i=0; i<smooth_size; i++ ) { | |||
offset_array[i] = 0.0; | |||
window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) ); | |||
} | |||
jack_buffer_size = jack_get_buffer_size( client ); | |||
// Setup target delay and max_diff for the normal user, who does not play with them... | |||
if( !target_delay ) | |||
target_delay = (num_periods*period_size / 2) + jack_buffer_size/2; | |||
if( !max_diff ) | |||
max_diff = num_periods*period_size - target_delay ; | |||
if( max_diff > target_delay ) { | |||
fprintf( stderr, "target_delay (%d) can not be smaller than max_diff(%d)\n", target_delay, max_diff ); | |||
exit(20); | |||
} | |||
if( (target_delay+max_diff) > (num_periods*period_size) ) { | |||
fprintf( stderr, "target_delay+max_diff (%d) can not be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); | |||
exit(20); | |||
} | |||
// alloc input ports, which are blasted out to alsa... | |||
alloc_ports( num_channels, 0 ); | |||
outbuf = malloc( num_periods * period_size * formats[format].sample_size * num_channels ); | |||
resampbuf = malloc( num_periods * period_size * sizeof( float ) ); | |||
tmpbuf = malloc( 512 * formats[format].sample_size * num_channels ); | |||
if ((outbuf == NULL) || (resampbuf == NULL) || (tmpbuf == NULL)) | |||
{ | |||
fprintf( stderr, "no memory for buffers.\n" ); | |||
exit(20); | |||
} | |||
memset( tmpbuf, 0, 512 * formats[format].sample_size * num_channels); | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
signal( SIGTERM, sigterm_handler ); | |||
signal( SIGINT, sigterm_handler ); | |||
if( verbose ) { | |||
while(!quit) { | |||
usleep(500000); | |||
if( output_new_delay ) { | |||
printf( "delay = %d\n", output_new_delay ); | |||
output_new_delay = 0; | |||
} | |||
printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); | |||
} | |||
} else if( instrument ) { | |||
printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); | |||
int n=0; | |||
while(!quit) { | |||
usleep(1000); | |||
printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); | |||
} | |||
} else { | |||
while(!quit) | |||
{ | |||
usleep(500000); | |||
if( output_new_delay ) { | |||
printf( "delay = %d\n", output_new_delay ); | |||
output_new_delay = 0; | |||
} | |||
} | |||
} | |||
jack_deactivate( client ); | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,848 +0,0 @@ | |||
/** @file simple_client.c | |||
* | |||
* @brief This simple client demonstrates the basic features of JACK | |||
* as they would be used by many applications. | |||
*/ | |||
#include <alloca.h> | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#include <math.h> | |||
#include <jack/jack.h> | |||
#include <jack/jslist.h> | |||
#include "memops.h" | |||
#include "alsa/asoundlib.h" | |||
#include <samplerate.h> | |||
// Here are the lists of the jack ports... | |||
JSList *capture_ports = NULL; | |||
JSList *capture_srcs = NULL; | |||
JSList *playback_ports = NULL; | |||
JSList *playback_srcs = NULL; | |||
jack_client_t *client; | |||
snd_pcm_t *alsa_handle; | |||
int jack_sample_rate; | |||
int jack_buffer_size; | |||
int quit = 0; | |||
double resample_mean = 1.0; | |||
double static_resample_factor = 1.0; | |||
double resample_lower_limit = 0.25; | |||
double resample_upper_limit = 4.0; | |||
double *offset_array; | |||
double *window_array; | |||
int offset_differential_index = 0; | |||
double offset_integral = 0; | |||
// ------------------------------------------------------ commandline parameters | |||
int sample_rate = 0; /* stream rate */ | |||
int num_channels = 2; /* count of channels */ | |||
int period_size = 1024; | |||
int num_periods = 2; | |||
int target_delay = 0; /* the delay which the program should try to approach. */ | |||
int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ | |||
int catch_factor = 100000; | |||
int catch_factor2 = 10000; | |||
double pclamp = 15.0; | |||
double controlquant = 10000.0; | |||
int smooth_size = 256; | |||
int good_window=0; | |||
int verbose = 0; | |||
int instrument = 0; | |||
int samplerate_quality = 2; | |||
// Debug stuff: | |||
volatile float output_resampling_factor = 1.0; | |||
volatile int output_new_delay = 0; | |||
volatile float output_offset = 0.0; | |||
volatile float output_integral = 0.0; | |||
volatile float output_diff = 0.0; | |||
volatile int running_freewheel = 0; | |||
snd_pcm_uframes_t real_buffer_size; | |||
snd_pcm_uframes_t real_period_size; | |||
// buffers | |||
char *tmpbuf; | |||
char *outbuf; | |||
float *resampbuf; | |||
// format selection, and corresponding functions from memops in a nice set of structs. | |||
typedef struct alsa_format { | |||
snd_pcm_format_t format_id; | |||
size_t sample_size; | |||
void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
const char *name; | |||
} alsa_format_t; | |||
alsa_format_t formats[] = { | |||
{ SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, | |||
{ SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, | |||
{ SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" }, | |||
{ SND_PCM_FORMAT_S24, 4, sample_move_d32l24_sS, sample_move_dS_s32l24, "24bit" }, | |||
{ SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } | |||
#ifdef __ANDROID__ | |||
,{ SND_PCM_FORMAT_S16_LE, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit little-endian" } | |||
#endif | |||
}; | |||
#define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) | |||
int format=0; | |||
// Alsa stuff... i don't want to touch this bullshit in the next years.... please... | |||
static int xrun_recovery(snd_pcm_t *handle, int err) { | |||
// printf( "xrun !!!.... %d\n", err ); | |||
if (err == -EPIPE) { /* under-run */ | |||
err = snd_pcm_prepare(handle); | |||
if (err < 0) | |||
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); | |||
return 0; | |||
} else if (err == -ESTRPIPE) { | |||
while ((err = snd_pcm_resume(handle)) == -EAGAIN) | |||
usleep(100); /* wait until the suspend flag is released */ | |||
if (err < 0) { | |||
err = snd_pcm_prepare(handle); | |||
if (err < 0) | |||
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); | |||
} | |||
return 0; | |||
} | |||
return err; | |||
} | |||
static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) | |||
{ | |||
#ifdef __ANDROID__ | |||
format = 5; | |||
snd_pcm_hw_params_set_format(handle, params, formats[format].format_id); | |||
return 0; | |||
#else | |||
int i; | |||
int err; | |||
for( i=0; i<NUMFORMATS; i++ ) { | |||
/* set the sample format */ | |||
err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id); | |||
if (err == 0) { | |||
format = i; | |||
return 0; | |||
} | |||
} | |||
return err; | |||
#endif | |||
} | |||
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) { | |||
int err, dir=0; | |||
unsigned int buffer_time; | |||
unsigned int period_time; | |||
unsigned int rrate; | |||
unsigned int rchannels; | |||
/* choose all parameters */ | |||
err = snd_pcm_hw_params_any(handle, params); | |||
if (err < 0) { | |||
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the interleaved read/write format */ | |||
err = snd_pcm_hw_params_set_access(handle, params, access); | |||
if (err < 0) { | |||
printf("Access type not available for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the sample format */ | |||
err = set_hwformat(handle, params); | |||
if (err < 0) { | |||
printf("Sample format not available for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* set the count of channels */ | |||
rchannels = channels; | |||
err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels); | |||
if (err < 0) { | |||
printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); | |||
return err; | |||
} | |||
if (rchannels != channels) { | |||
printf("WARNING: channel count does not match (requested %d got %d)\n", channels, rchannels); | |||
num_channels = rchannels; | |||
} | |||
/* set the stream rate */ | |||
rrate = rate; | |||
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); | |||
if (err < 0) { | |||
printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); | |||
return err; | |||
} | |||
if (rrate != rate) { | |||
printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate); | |||
return -EINVAL; | |||
} | |||
/* set the buffer time */ | |||
buffer_time = 1000000*(uint64_t)period*nperiods/rate; | |||
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); | |||
if (err < 0) { | |||
printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size ); | |||
if (err < 0) { | |||
printf("Unable to get buffer size back: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
if( real_buffer_size != nperiods * period ) { | |||
printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size ); | |||
} | |||
/* set the period time */ | |||
period_time = 1000000*(uint64_t)period/rate; | |||
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); | |||
if (err < 0) { | |||
printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL ); | |||
if (err < 0) { | |||
printf("Unable to get period size back: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
if( real_period_size != period ) { | |||
printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size ); | |||
} | |||
/* write the parameters to device */ | |||
err = snd_pcm_hw_params(handle, params); | |||
if (err < 0) { | |||
printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
return 0; | |||
} | |||
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period, int nperiods) { | |||
int err; | |||
/* get the current swparams */ | |||
err = snd_pcm_sw_params_current(handle, swparams); | |||
if (err < 0) { | |||
printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* start the transfer when the buffer is full */ | |||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period ); | |||
if (err < 0) { | |||
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 ); | |||
if (err < 0) { | |||
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* allow the transfer when at least period_size samples can be processed */ | |||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, 1 ); | |||
if (err < 0) { | |||
printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* align all transfers to 1 sample */ | |||
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); | |||
if (err < 0) { | |||
printf("Unable to set transfer align for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
/* write the parameters to the playback device */ | |||
err = snd_pcm_sw_params(handle, swparams); | |||
if (err < 0) { | |||
printf("Unable to set sw params for capture: %s\n", snd_strerror(err)); | |||
return err; | |||
} | |||
return 0; | |||
} | |||
// ok... i only need this function to communicate with the alsa bloat api... | |||
static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) { | |||
int err; | |||
snd_pcm_t *handle; | |||
snd_pcm_hw_params_t *hwparams; | |||
snd_pcm_sw_params_t *swparams; | |||
snd_pcm_hw_params_alloca(&hwparams); | |||
snd_pcm_sw_params_alloca(&swparams); | |||
if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) { | |||
printf("Capture open error: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) { | |||
printf("Setting of hwparams failed: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
if ((err = set_swparams(handle, swparams, period, nperiods)) < 0) { | |||
printf("Setting of swparams failed: %s\n", snd_strerror(err)); | |||
return NULL; | |||
} | |||
//snd_pcm_start( handle ); | |||
//snd_pcm_wait( handle, 200 ); | |||
int num_null_samples = nperiods * period * channels; | |||
char *tmp = alloca( num_null_samples * formats[format].sample_size ); | |||
memset( tmp, 0, num_null_samples * formats[format].sample_size ); | |||
snd_pcm_writei( handle, tmp, num_null_samples ); | |||
return handle; | |||
} | |||
double hann( double x ) | |||
{ | |||
return 0.5 * (1.0 - cos( 2*M_PI * x ) ); | |||
} | |||
/** | |||
* The freewheel callback. | |||
*/ | |||
void freewheel (int starting, void* arg) { | |||
running_freewheel = starting; | |||
} | |||
/** | |||
* The process callback for this JACK application. | |||
* It is called by JACK at the appropriate times. | |||
*/ | |||
int process (jack_nframes_t nframes, void *arg) { | |||
if (running_freewheel) { | |||
JSList *node = playback_ports; | |||
while ( node != NULL) | |||
{ | |||
jack_port_t *port = (jack_port_t *) node->data; | |||
float *buf = jack_port_get_buffer (port, nframes); | |||
memset(buf, 0, sizeof(float)*nframes); | |||
node = jack_slist_next (node); | |||
} | |||
return 0; | |||
} | |||
int rlen; | |||
int err; | |||
snd_pcm_sframes_t delay = target_delay; | |||
int i; | |||
delay = (num_periods*period_size)-snd_pcm_avail( alsa_handle ) ; | |||
delay -= round( jack_frames_since_cycle_start( client ) * static_resample_factor ); | |||
// Do it the hard way. | |||
// this is for compensating xruns etc... | |||
if( delay > (target_delay+max_diff) ) { | |||
snd_pcm_rewind( alsa_handle, delay - target_delay ); | |||
output_new_delay = (int) delay; | |||
delay = target_delay; | |||
// Set the resample_rate... we need to adjust the offset integral, to do this. | |||
// first look at the PI controller, this code is just a special case, which should never execute once | |||
// everything is swung in. | |||
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
// Also clear the array. we are beginning a new control cycle. | |||
for( i=0; i<smooth_size; i++ ) | |||
offset_array[i] = 0.0; | |||
} | |||
if( delay < (target_delay-max_diff) ) { | |||
output_new_delay = (int) delay; | |||
while ((target_delay-delay) > 0) { | |||
snd_pcm_uframes_t to_write = ((target_delay-delay) > 512) ? 512 : (target_delay-delay); | |||
snd_pcm_writei( alsa_handle, tmpbuf, to_write ); | |||
delay += to_write; | |||
} | |||
delay = target_delay; | |||
// Set the resample_rate... we need to adjust the offset integral, to do this. | |||
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
// Also clear the array. we are beginning a new control cycle. | |||
for( i=0; i<smooth_size; i++ ) | |||
offset_array[i] = 0.0; | |||
} | |||
/* ok... now we should have target_delay +- max_diff on the alsa side. | |||
* | |||
* calculate the number of frames, we want to get. | |||
*/ | |||
double offset = delay - target_delay; | |||
// Save offset. | |||
offset_array[(offset_differential_index++)% smooth_size ] = offset; | |||
// Build the mean of the windowed offset array | |||
// basically fir lowpassing. | |||
double smooth_offset = 0.0; | |||
for( i=0; i<smooth_size; i++ ) | |||
smooth_offset += | |||
offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i]; | |||
smooth_offset /= (double) smooth_size; | |||
// this is the integral of the smoothed_offset | |||
offset_integral += smooth_offset; | |||
// Clamp offset. | |||
// the smooth offset still contains unwanted noise | |||
// which would go straight onto the resample coeff. | |||
// it only used in the P component and the I component is used for the fine tuning anyways. | |||
if( fabs( smooth_offset ) < pclamp ) | |||
smooth_offset = 0.0; | |||
// ok. now this is the PI controller. | |||
// u(t) = K * ( e(t) + 1/T \int e(t') dt' ) | |||
// K = 1/catch_factor and T = catch_factor2 | |||
double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2; | |||
// now quantize this value around resample_mean, so that the noise which is in the integral component doesn't hurt. | |||
current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean; | |||
// Output "instrumentatio" gonna change that to real instrumentation in a few. | |||
output_resampling_factor = (float) current_resample_factor; | |||
output_diff = (float) smooth_offset; | |||
output_integral = (float) offset_integral; | |||
output_offset = (float) offset; | |||
// Clamp a bit. | |||
if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit; | |||
if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit; | |||
// Now Calculate how many samples we need. | |||
rlen = ceil( ((double)nframes) * current_resample_factor )+2; | |||
assert( rlen > 2 ); | |||
// Calculate resample_mean so we can init ourselves to saner values. | |||
resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; | |||
/* | |||
* now this should do it... | |||
*/ | |||
outbuf = alloca( rlen * formats[format].sample_size * num_channels ); | |||
resampbuf = alloca( rlen * sizeof( float ) ); | |||
/* | |||
* render jack ports to the outbuf... | |||
*/ | |||
int chn = 0; | |||
JSList *node = playback_ports; | |||
JSList *src_node = playback_srcs; | |||
SRC_DATA src; | |||
while ( node != NULL) | |||
{ | |||
jack_port_t *port = (jack_port_t *) node->data; | |||
float *buf = jack_port_get_buffer (port, nframes); | |||
SRC_STATE *src_state = src_node->data; | |||
src.data_in = buf; | |||
src.input_frames = nframes; | |||
src.data_out = resampbuf; | |||
src.output_frames = rlen; | |||
src.end_of_input = 0; | |||
src.src_ratio = current_resample_factor; | |||
src_process( src_state, &src ); | |||
formats[format].jack_to_soundcard( outbuf + format[formats].sample_size * chn, resampbuf, src.output_frames_gen, num_channels*format[formats].sample_size, NULL); | |||
src_node = jack_slist_next (src_node); | |||
node = jack_slist_next (node); | |||
chn++; | |||
} | |||
// now write the output... | |||
again: | |||
err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); | |||
//err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); | |||
if( err < 0 ) { | |||
printf( "err = %d\n", err ); | |||
if (xrun_recovery(alsa_handle, err) < 0) { | |||
printf("Write error: %s\n", snd_strerror(err)); | |||
exit(EXIT_FAILURE); | |||
} | |||
goto again; | |||
} | |||
return 0; | |||
} | |||
/** | |||
* the latency callback. | |||
* sets up the latencies on the ports. | |||
*/ | |||
void | |||
latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
{ | |||
jack_latency_range_t range; | |||
JSList *node; | |||
range.min = range.max = round(target_delay / static_resample_factor); | |||
if (mode == JackCaptureLatency) { | |||
for (node = capture_ports; node; node = jack_slist_next (node)) { | |||
jack_port_t *port = node->data; | |||
jack_port_set_latency_range (port, mode, &range); | |||
} | |||
} else { | |||
for (node = playback_ports; node; node = jack_slist_next (node)) { | |||
jack_port_t *port = node->data; | |||
jack_port_set_latency_range (port, mode, &range); | |||
} | |||
} | |||
} | |||
/** | |||
* Allocate the necessary jack ports... | |||
*/ | |||
void alloc_ports( int n_capture, int n_playback ) { | |||
int port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; | |||
int chn; | |||
jack_port_t *port; | |||
char buf[32]; | |||
capture_ports = NULL; | |||
for (chn = 0; chn < n_capture; chn++) | |||
{ | |||
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); | |||
port = jack_port_register (client, buf, | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
port_flags, 0); | |||
if (!port) | |||
{ | |||
printf( "jacknet_client: cannot register port for %s", buf); | |||
break; | |||
} | |||
capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
capture_ports = jack_slist_append (capture_ports, port); | |||
} | |||
port_flags = JackPortIsInput; | |||
playback_ports = NULL; | |||
for (chn = 0; chn < n_playback; chn++) | |||
{ | |||
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); | |||
port = jack_port_register (client, buf, | |||
JACK_DEFAULT_AUDIO_TYPE, | |||
port_flags, 0); | |||
if (!port) | |||
{ | |||
printf( "jacknet_client: cannot register port for %s", buf); | |||
break; | |||
} | |||
playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
playback_ports = jack_slist_append (playback_ports, port); | |||
} | |||
} | |||
/** | |||
* This is the shutdown callback for this JACK application. | |||
* It is called by JACK if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
void jack_shutdown (void *arg) { | |||
exit (1); | |||
} | |||
/** | |||
* be user friendly. | |||
* be user friendly. | |||
* be user friendly. | |||
*/ | |||
void printUsage() { | |||
fprintf(stderr, "usage: alsa_out [options]\n" | |||
"\n" | |||
" -j <jack name> - client name\n" | |||
" -d <alsa_device> \n" | |||
" -c <channels> \n" | |||
" -p <period_size> \n" | |||
" -n <num_period> \n" | |||
" -r <sample_rate> \n" | |||
" -q <sample_rate quality [0..4]\n" | |||
" -m <max_diff> \n" | |||
" -t <target_delay> \n" | |||
" -i turns on instrumentation\n" | |||
" -v turns on printouts\n" | |||
"\n"); | |||
} | |||
/** | |||
* the main function.... | |||
*/ | |||
void | |||
sigterm_handler( int signal ) | |||
{ | |||
quit = 1; | |||
} | |||
int main (int argc, char *argv[]) { | |||
char jack_name[30] = "alsa_out"; | |||
char alsa_device[30] = "hw:0"; | |||
int jack_opts = 0; | |||
char *server_name = NULL; | |||
extern char *optarg; | |||
extern int optind, optopt; | |||
int errflg=0; | |||
int c; | |||
while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:S:")) != -1) { | |||
switch(c) { | |||
case 'j': | |||
strcpy(jack_name,optarg); | |||
break; | |||
case 'r': | |||
sample_rate = atoi(optarg); | |||
break; | |||
case 'c': | |||
num_channels = atoi(optarg); | |||
break; | |||
case 'p': | |||
period_size = atoi(optarg); | |||
break; | |||
case 'n': | |||
num_periods = atoi(optarg); | |||
break; | |||
case 'd': | |||
strcpy(alsa_device,optarg); | |||
break; | |||
case 't': | |||
target_delay = atoi(optarg); | |||
break; | |||
case 'q': | |||
samplerate_quality = atoi(optarg); | |||
break; | |||
case 'm': | |||
max_diff = atoi(optarg); | |||
break; | |||
case 'f': | |||
catch_factor = atoi(optarg); | |||
break; | |||
case 'F': | |||
catch_factor2 = atoi(optarg); | |||
break; | |||
case 'C': | |||
pclamp = (double) atoi(optarg); | |||
break; | |||
case 'Q': | |||
controlquant = (double) atoi(optarg); | |||
break; | |||
case 'v': | |||
verbose = 1; | |||
break; | |||
case 'i': | |||
instrument = 1; | |||
break; | |||
case 's': | |||
smooth_size = atoi(optarg); | |||
break; | |||
case 'S': | |||
server_name = optarg; | |||
jack_opts |= JackServerName; | |||
break; | |||
case ':': | |||
fprintf(stderr, | |||
"Option -%c requires an operand\n", optopt); | |||
errflg++; | |||
break; | |||
case '?': | |||
fprintf(stderr, | |||
"Unrecognized option: -%c\n", optopt); | |||
errflg++; | |||
} | |||
} | |||
if (errflg) { | |||
printUsage(); | |||
exit(2); | |||
} | |||
if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { | |||
fprintf (stderr, "invalid samplerate quality\n"); | |||
return 1; | |||
} | |||
if ((client = jack_client_open (jack_name, jack_opts, NULL, server_name)) == 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 `freewheel()' whenever | |||
freewheel mode changes. | |||
*/ | |||
jack_set_freewheel_callback (client, freewheel, 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); | |||
if (jack_set_latency_callback) | |||
jack_set_latency_callback (client, latency_cb, 0); | |||
// get jack sample_rate | |||
jack_sample_rate = jack_get_sample_rate( client ); | |||
if( !sample_rate ) | |||
sample_rate = jack_sample_rate; | |||
static_resample_factor = (double) sample_rate / (double) jack_sample_rate; | |||
resample_lower_limit = static_resample_factor * 0.25; | |||
resample_upper_limit = static_resample_factor * 4.0; | |||
resample_mean = static_resample_factor; | |||
offset_array = malloc( sizeof(double) * smooth_size ); | |||
if( offset_array == NULL ) { | |||
fprintf( stderr, "no memory for offset_array !!!\n" ); | |||
exit(20); | |||
} | |||
window_array = malloc( sizeof(double) * smooth_size ); | |||
if( window_array == NULL ) { | |||
fprintf( stderr, "no memory for window_array !!!\n" ); | |||
exit(20); | |||
} | |||
int i; | |||
for( i=0; i<smooth_size; i++ ) { | |||
offset_array[i] = 0.0; | |||
window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) ); | |||
} | |||
jack_buffer_size = jack_get_buffer_size( client ); | |||
// Setup target delay and max_diff for the normal user, who does not play with them... | |||
if( !target_delay ) | |||
target_delay = (num_periods*period_size / 2) - jack_buffer_size/2; | |||
if( !max_diff ) | |||
max_diff = target_delay; | |||
if( max_diff > target_delay ) { | |||
fprintf( stderr, "target_delay (%d) can not be smaller than max_diff(%d)\n", target_delay, max_diff ); | |||
exit(20); | |||
} | |||
if( (target_delay+max_diff) > (num_periods*period_size) ) { | |||
fprintf( stderr, "target_delay+max_diff (%d) can not be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); | |||
exit(20); | |||
} | |||
// now open the alsa fd... | |||
alsa_handle = open_audiofd( alsa_device, 0, sample_rate, num_channels, period_size, num_periods); | |||
if( alsa_handle == 0 ) | |||
exit(20); | |||
printf( "selected sample format: %s\n", formats[format].name ); | |||
// alloc input ports, which are blasted out to alsa... | |||
alloc_ports( 0, num_channels ); | |||
outbuf = malloc( num_periods * period_size * formats[format].sample_size * num_channels ); | |||
resampbuf = malloc( num_periods * period_size * sizeof( float ) ); | |||
tmpbuf = malloc( 512 * formats[format].sample_size * num_channels ); | |||
if ((outbuf == NULL) || (resampbuf == NULL) || (tmpbuf == NULL)) | |||
{ | |||
fprintf( stderr, "no memory for buffers.\n" ); | |||
exit(20); | |||
} | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
signal( SIGTERM, sigterm_handler ); | |||
signal( SIGINT, sigterm_handler ); | |||
if( verbose ) { | |||
while(!quit) { | |||
usleep(500000); | |||
if( output_new_delay ) { | |||
printf( "delay = %d\n", output_new_delay ); | |||
output_new_delay = 0; | |||
} | |||
printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); | |||
} | |||
} else if( instrument ) { | |||
printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); | |||
int n=0; | |||
while(!quit) { | |||
usleep(1000); | |||
printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); | |||
} | |||
} else { | |||
while(!quit) | |||
{ | |||
usleep(500000); | |||
if( output_new_delay ) { | |||
printf( "delay = %d\n", output_new_delay ); | |||
output_new_delay = 0; | |||
} | |||
} | |||
} | |||
jack_deactivate( client ); | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,124 +0,0 @@ | |||
/* | |||
* bufsize.c -- change JACK buffer size. | |||
* | |||
* 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 <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
char *package; /* program name */ | |||
jack_client_t *client; | |||
jack_nframes_t nframes; | |||
int just_print_bufsize=0; | |||
void jack_shutdown(void *arg) | |||
{ | |||
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); | |||
} | |||
void parse_arguments(int argc, char *argv[]) | |||
{ | |||
/* basename $0 */ | |||
package = strrchr(argv[0], '/'); | |||
if (package == 0) | |||
package = argv[0]; | |||
else | |||
package++; | |||
if (argc==1) { | |||
just_print_bufsize = 1; | |||
return; | |||
} | |||
if (argc < 2) { | |||
fprintf(stderr, "usage: %s <bufsize>\n", package); | |||
exit(9); | |||
} | |||
if (strspn (argv[1], "0123456789") != strlen (argv[1])) { | |||
fprintf(stderr, "usage: %s <bufsize>\n", package); | |||
exit(8); | |||
} | |||
nframes = strtoul(argv[1], NULL, 0); | |||
if (errno == ERANGE) { | |||
fprintf(stderr, "%s: invalid buffer size: %s (range is 1-16384)\n", | |||
package, argv[1]); | |||
exit(2); | |||
} | |||
if (nframes < 1 || nframes > 16384) { | |||
fprintf(stderr, "%s: invalid buffer size: %s (range is 1-16384)\n", | |||
package, argv[1]); | |||
exit(3); | |||
} | |||
} | |||
void silent_function( const char *ignore ) | |||
{ | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
int rc; | |||
parse_arguments(argc, argv); | |||
if (just_print_bufsize) | |||
jack_set_info_function( silent_function ); | |||
/* become a JACK client */ | |||
if ((client = jack_client_open(package, JackNoStartServer, NULL)) == 0) { | |||
fprintf(stderr, "JACK server not running?\n"); | |||
exit(1); | |||
} | |||
#ifndef WIN32 | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
jack_on_shutdown(client, jack_shutdown, 0); | |||
if (just_print_bufsize) { | |||
fprintf(stdout, "%d\n", jack_get_buffer_size( client ) ); | |||
rc = 0; | |||
} | |||
else | |||
{ | |||
rc = jack_set_buffer_size(client, nframes); | |||
if (rc) | |||
fprintf(stderr, "jack_set_buffer_size(): %s\n", strerror(rc)); | |||
} | |||
jack_client_close(client); | |||
return rc; | |||
} |
@@ -1,248 +0,0 @@ | |||
/* | |||
Copyright (C) 2002 Jeremy Hall | |||
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> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/session.h> | |||
#define TRUE 1 | |||
#define FALSE 0 | |||
volatile int done = 0; | |||
void port_connect_callback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg) | |||
{ | |||
done = 1; | |||
} | |||
void | |||
show_version (char *my_name) | |||
{ | |||
//fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", my_name); | |||
} | |||
void | |||
show_usage (char *my_name) | |||
{ | |||
show_version (my_name); | |||
fprintf (stderr, "\nusage: %s [options] port1 port2\n", my_name); | |||
fprintf (stderr, "Connects two JACK ports together.\n\n"); | |||
fprintf (stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
fprintf (stderr, " -v, --version Output version information and exit\n"); | |||
fprintf (stderr, " -h, --help Display this help message\n\n"); | |||
fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_client_t *client; | |||
jack_status_t status; | |||
char *server_name = NULL; | |||
int c; | |||
int option_index; | |||
jack_options_t options = JackNoStartServer; | |||
char *my_name = strrchr(argv[0], '/'); | |||
jack_port_t *src_port = 0; | |||
jack_port_t *dst_port = 0; | |||
jack_port_t *port1 = 0; | |||
jack_port_t *port2 = 0; | |||
char portA[300]; | |||
char portB[300]; | |||
int use_uuid=0; | |||
int connecting, disconnecting; | |||
int port1_flags, port2_flags; | |||
int rc = 1; | |||
struct option long_options[] = { | |||
{ "server", 1, 0, 's' }, | |||
{ "help", 0, 0, 'h' }, | |||
{ "version", 0, 0, 'v' }, | |||
{ "uuid", 0, 0, 'u' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
while ((c = getopt_long (argc, argv, "s:hvu", long_options, &option_index)) >= 0) { | |||
switch (c) { | |||
case 's': | |||
server_name = (char *) malloc (sizeof (char) * (strlen(optarg) + 1)); | |||
strcpy (server_name, optarg); | |||
options |= JackServerName; | |||
break; | |||
case 'u': | |||
use_uuid = 1; | |||
break; | |||
case 'h': | |||
show_usage (my_name); | |||
return 1; | |||
break; | |||
case 'v': | |||
show_version (my_name); | |||
return 1; | |||
break; | |||
default: | |||
show_usage (my_name); | |||
return 1; | |||
break; | |||
} | |||
} | |||
connecting = disconnecting = FALSE; | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name ++; | |||
} | |||
if (strstr(my_name, "disconnect")) { | |||
disconnecting = 1; | |||
} else if (strstr(my_name, "connect")) { | |||
connecting = 1; | |||
} else { | |||
fprintf(stderr, "ERROR! client should be called jack_connect or jack_disconnect. client is called %s\n", my_name); | |||
return 1; | |||
} | |||
if (argc < 3) { | |||
show_usage(my_name); | |||
return 1; | |||
} | |||
/* try to become a client of the JACK server */ | |||
if ((client = jack_client_open (my_name, options, &status, server_name)) == 0) { | |||
fprintf (stderr, "jack server not running?\n"); | |||
return 1; | |||
} | |||
jack_set_port_connect_callback(client, port_connect_callback, NULL); | |||
/* find the two ports */ | |||
if( use_uuid ) { | |||
char *tmpname; | |||
char *clientname; | |||
char *portname; | |||
tmpname = strdup( argv[argc-1] ); | |||
portname = strchr( tmpname, ':' ); | |||
portname[0] = '\0'; | |||
portname+=1; | |||
clientname = jack_get_client_name_by_uuid( client, tmpname ); | |||
if( clientname ) { | |||
snprintf( portA, sizeof(portA), "%s:%s", clientname, portname ); | |||
jack_free( clientname ); | |||
} else { | |||
snprintf( portA, sizeof(portA), "%s", argv[argc-1] ); | |||
} | |||
free( tmpname ); | |||
tmpname = strdup( argv[argc-2] ); | |||
portname = strchr( tmpname, ':' ); | |||
portname[0] = '\0'; | |||
portname+=1; | |||
clientname = jack_get_client_name_by_uuid( client, tmpname ); | |||
if( clientname ) { | |||
snprintf( portB, sizeof(portB), "%s:%s", clientname, portname ); | |||
jack_free( clientname ); | |||
} else { | |||
snprintf( portB, sizeof(portB), "%s", argv[argc-2] ); | |||
} | |||
free( tmpname ); | |||
} else { | |||
snprintf( portA, sizeof(portA), "%s", argv[argc-1] ); | |||
snprintf( portB, sizeof(portB), "%s", argv[argc-2] ); | |||
} | |||
if ((port1 = jack_port_by_name(client, portA)) == 0) { | |||
fprintf (stderr, "ERROR %s not a valid port\n", portA); | |||
goto exit; | |||
} | |||
if ((port2 = jack_port_by_name(client, portB)) == 0) { | |||
fprintf (stderr, "ERROR %s not a valid port\n", portB); | |||
goto exit; | |||
} | |||
port1_flags = jack_port_flags (port1); | |||
port2_flags = jack_port_flags (port2); | |||
if (port1_flags & JackPortIsInput) { | |||
if (port2_flags & JackPortIsOutput) { | |||
src_port = port2; | |||
dst_port = port1; | |||
} | |||
} else { | |||
if (port2_flags & JackPortIsInput) { | |||
src_port = port1; | |||
dst_port = port2; | |||
} | |||
} | |||
if (!src_port || !dst_port) { | |||
fprintf (stderr, "arguments must include 1 input port and 1 output port\n"); | |||
goto exit; | |||
} | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
goto exit; | |||
} | |||
/* connect the ports. Note: you can't do this before | |||
the client is activated (this may change in the future). | |||
*/ | |||
if (connecting) { | |||
if (jack_connect(client, jack_port_name(src_port), jack_port_name(dst_port))) { | |||
fprintf (stderr, "cannot connect client, already connected?\n"); | |||
goto exit; | |||
} | |||
} | |||
if (disconnecting) { | |||
if (jack_disconnect(client, jack_port_name(src_port), jack_port_name(dst_port))) { | |||
fprintf (stderr, "cannot disconnect client, already disconnected?\n"); | |||
goto exit; | |||
} | |||
} | |||
// Wait for connection/disconnection to be effective | |||
while(!done) { | |||
#ifdef WIN32 | |||
Sleep(10); | |||
#else | |||
usleep(10000); | |||
#endif | |||
} | |||
/* everything was ok, so setting exitcode to 0 */ | |||
rc = 0; | |||
exit: | |||
jack_client_close (client); | |||
exit (rc); | |||
} |
@@ -1,164 +0,0 @@ | |||
/* | |||
Copyright (C) 2007 Paul Davis | |||
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> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <string.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <jack/jack.h> | |||
#include <jack/metadata.h> | |||
#include <jack/uuid.h> | |||
jack_client_t *client; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void | |||
port_rename_callback (jack_port_id_t port, const char* old_name, const char* new_name, void* arg) | |||
{ | |||
printf ("Port %d renamed from %s to %s\n", port, old_name, new_name); | |||
} | |||
static void | |||
port_callback (jack_port_id_t port, int yn, void* arg) | |||
{ | |||
printf ("Port %d %s\n", port, (yn ? "registered" : "unregistered")); | |||
} | |||
static void | |||
connect_callback (jack_port_id_t a, jack_port_id_t b, int yn, void* arg) | |||
{ | |||
printf ("Ports %d and %d %s\n", a, b, (yn ? "connected" : "disconnected")); | |||
} | |||
static void | |||
client_callback (const char* client, int yn, void* arg) | |||
{ | |||
printf ("Client %s %s\n", client, (yn ? "registered" : "unregistered")); | |||
} | |||
static int | |||
graph_callback (void* arg) | |||
{ | |||
printf ("Graph reordered\n"); | |||
return 0; | |||
} | |||
static void | |||
propchange (jack_uuid_t subject, const char* key, jack_property_change_t change, void* arg) | |||
{ | |||
char buf[JACK_UUID_STRING_SIZE]; | |||
const char* action = ""; | |||
switch (change) { | |||
case PropertyCreated: | |||
action = "created"; | |||
break; | |||
case PropertyChanged: | |||
action = "changed"; | |||
break; | |||
case PropertyDeleted: | |||
action = "deleted"; | |||
break; | |||
} | |||
if (jack_uuid_empty (subject)) { | |||
printf ("All properties changed!\n"); | |||
} else { | |||
jack_uuid_unparse (subject, buf); | |||
if (key) { | |||
printf ("key [%s] for %s %s\n", key, buf, action); | |||
} else { | |||
printf ("all keys for %s %s\n", buf, action); | |||
} | |||
} | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
if ((client = jack_client_open ("event-monitor", options, &status, NULL)) == 0) { | |||
fprintf (stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
if (status & JackServerFailed) { | |||
fprintf (stderr, "Unable to connect to JACK server\n"); | |||
} | |||
return 1; | |||
} | |||
if (jack_set_port_registration_callback (client, port_callback, NULL)) { | |||
fprintf (stderr, "cannot set port registration callback\n"); | |||
return 1; | |||
} | |||
if (jack_set_port_rename_callback (client, port_rename_callback, NULL)) { | |||
fprintf (stderr, "cannot set port registration callback\n"); | |||
return 1; | |||
} | |||
if (jack_set_port_connect_callback (client, connect_callback, NULL)) { | |||
fprintf (stderr, "cannot set port connect callback\n"); | |||
return 1; | |||
} | |||
if (jack_set_client_registration_callback (client, client_callback, NULL)) { | |||
fprintf (stderr, "cannot set client registration callback\n"); | |||
return 1; | |||
} | |||
if (jack_set_graph_order_callback (client, graph_callback, NULL)) { | |||
fprintf (stderr, "cannot set graph order registration callback\n"); | |||
return 1; | |||
} | |||
if (jack_set_property_change_callback (client, propchange, NULL)) { | |||
fprintf (stderr, "cannot set property change callback\n"); | |||
return 1; | |||
} | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
#ifndef WIN32 | |||
signal(SIGINT, signal_handler); | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGABRT, signal_handler); | |||
signal(SIGTERM, signal_handler); | |||
#ifdef WIN32 | |||
Sleep(INFINITE); | |||
#else | |||
sleep (-1); | |||
#endif | |||
exit (0); | |||
} | |||
@@ -1,87 +0,0 @@ | |||
/* | |||
* freewheel - start/stop JACK "freewheeling" mode | |||
* | |||
* Copyright (C) 2003 Paul Davis. | |||
* | |||
* 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 <signal.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
char *package; /* program name */ | |||
jack_client_t *client; | |||
int onoff; | |||
static void jack_shutdown(void *arg) | |||
{ | |||
fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
exit(1); | |||
} | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
static void parse_arguments(int argc, char *argv[]) | |||
{ | |||
if (argc < 2) { | |||
fprintf(stderr, "usage: %s y|n\n", package); | |||
exit(9); | |||
} | |||
if (argv[1][0] == 'y' || argv[1][0] == 'Y' || argv[1][0] == '1') { | |||
onoff = 1; | |||
} else { | |||
onoff = 0; | |||
} | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
parse_arguments (argc, argv); | |||
/* become a JACK client */ | |||
if ((client = jack_client_open ("freewheel", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
exit(1); | |||
} | |||
#ifndef WIN32 | |||
signal (SIGQUIT, signal_handler); | |||
signal (SIGHUP, signal_handler); | |||
#endif | |||
signal (SIGTERM, signal_handler); | |||
signal (SIGINT, signal_handler); | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
if (jack_set_freewheel (client, onoff)) { | |||
fprintf (stderr, "failed to reset freewheel mode\n"); | |||
} | |||
jack_client_close(client); | |||
return 0; | |||
} |
@@ -1,246 +0,0 @@ | |||
/* | |||
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 <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/intclient.h> | |||
jack_client_t *client; | |||
jack_intclient_t intclient; | |||
char *client_name; | |||
char *intclient_name; | |||
char *load_name; | |||
char *load_init = ""; | |||
char *server_name = NULL; | |||
int autoclose_opt = 0; | |||
int wait_opt = 0; | |||
volatile int idling = 1; | |||
static void | |||
signal_handler (int sig) | |||
{ | |||
/* do nothing if internal client closed itself */ | |||
if (idling == 0) | |||
return; | |||
jack_status_t status; | |||
fprintf (stderr, "signal received, unloading..."); | |||
status = jack_internal_client_unload (client, intclient); | |||
if (status & JackFailure) | |||
fprintf (stderr, "(failed), status = 0x%2.0x\n", status); | |||
else | |||
fprintf (stderr, "(succeeded)\n"); | |||
if (autoclose_opt) | |||
jack_deactivate(client); | |||
jack_client_close (client); | |||
exit (0); | |||
} | |||
static void | |||
registration_callback (const char *name, int reg, void *arg) | |||
{ | |||
if (reg || strcmp(intclient_name, name)) | |||
return; | |||
/* this will stop the wait loop and thus close this application. */ | |||
idling = 0; | |||
return; | |||
/* unused */ | |||
(void)arg; | |||
} | |||
static void | |||
show_usage () | |||
{ | |||
fprintf (stderr, "usage: %s [ options ] client-name [ load-name " | |||
"[ init-string]]\n\noptions:\n", client_name); | |||
fprintf (stderr, | |||
"\t-h, --help \t\t print help message\n" | |||
"\t-a, --autoclose\t automatically close when intclient is unloaded\n" | |||
"\t-i, --init string\t initialize string\n" | |||
"\t-s, --server name\t select JACK server\n" | |||
"\t-w, --wait \t\t wait for signal, then unload\n" | |||
"\n" | |||
); | |||
} | |||
static int | |||
parse_args (int argc, char *argv[]) | |||
{ | |||
int c; | |||
int option_index = 0; | |||
char *short_options = "hai:s:w"; | |||
struct option long_options[] = { | |||
{ "help", 0, 0, 'h' }, | |||
{ "autoclose", 0, 0, 'a' }, | |||
{ "init", required_argument, 0, 'i' }, | |||
{ "server", required_argument, 0, 's' }, | |||
{ "wait", 0, 0, 'w' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
client_name = strrchr(argv[0], '/'); | |||
if (client_name == NULL) { | |||
client_name = argv[0]; | |||
} else { | |||
client_name++; | |||
} | |||
while ((c = getopt_long (argc, argv, short_options, long_options, | |||
&option_index)) >= 0) { | |||
switch (c) { | |||
case 'a': | |||
autoclose_opt = 1; | |||
break; | |||
case 'i': | |||
load_init = optarg; | |||
break; | |||
case 's': | |||
server_name = optarg; | |||
break; | |||
case 'w': | |||
wait_opt = 1; | |||
break; | |||
case 'h': | |||
default: | |||
show_usage (); | |||
return 1; | |||
} | |||
} | |||
/* autoclose makes no sense without wait */ | |||
if (autoclose_opt && ! wait_opt) | |||
autoclose_opt = 0; | |||
if (optind == argc) { /* no positional args? */ | |||
show_usage (); | |||
return 1; | |||
} | |||
if (optind < argc) | |||
load_name = intclient_name = argv[optind++]; | |||
if (optind < argc) | |||
load_name = argv[optind++]; | |||
if (optind < argc) | |||
load_init = argv[optind++]; | |||
//fprintf (stderr, "client-name = `%s', load-name = `%s', " | |||
// "load-init = `%s', wait = %d\n", | |||
// intclient_name, load_name, load_init, wait_opt); | |||
return 0; /* args OK */ | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_status_t status; | |||
char* name; | |||
/* parse and validate command arguments */ | |||
if (parse_args (argc, argv)) | |||
exit (1); /* invalid command line */ | |||
/* first, become a JACK client */ | |||
client = jack_client_open (client_name, JackServerName, | |||
&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); | |||
} | |||
/* then, load the internal client */ | |||
intclient = jack_internal_client_load (client, intclient_name, | |||
(JackLoadName|JackLoadInit), | |||
&status, load_name, load_init); | |||
if (status & JackFailure) { | |||
fprintf (stderr, "could not load %s, intclient = %d status = 0x%2.0x\n", | |||
load_name, (int)intclient, status); | |||
return 2; | |||
} | |||
if (status & JackNameNotUnique) { | |||
intclient_name = | |||
jack_get_internal_client_name (client, intclient); | |||
fprintf (stderr, "unique internal client name `%s' assigned\n", | |||
intclient_name); | |||
} | |||
fprintf (stdout, "%s is running.\n", load_name); | |||
name = jack_get_internal_client_name(client, intclient); | |||
if (name) { | |||
printf("client name = %s\n", name); | |||
free(name); | |||
} | |||
fflush(stdout); | |||
if (autoclose_opt) { | |||
jack_set_client_registration_callback(client, registration_callback, NULL); | |||
jack_activate(client); | |||
} | |||
if (wait_opt) { | |||
/* define a signal handler to unload the client, then | |||
* wait for it to exit */ | |||
#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 (idling) { | |||
#ifdef WIN32 | |||
Sleep(1000); | |||
#else | |||
sleep (1); | |||
#endif | |||
} | |||
} | |||
if (autoclose_opt) { | |||
jack_deactivate(client); | |||
} | |||
jack_client_close(client); | |||
return 0; | |||
} | |||
@@ -1,93 +0,0 @@ | |||
/* | |||
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 <string.h> | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <jack/jack.h> | |||
#include <jack/intclient.h> | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
char *my_name; | |||
char *client_name; | |||
jack_client_t *client; | |||
jack_status_t status; | |||
jack_intclient_t intclient; | |||
/* validate args */ | |||
if ((argc < 2) || (argc > 3)) { | |||
fprintf (stderr, "usage: %s client-name [ server-name ]]\n", | |||
argv[0]); | |||
return 1; | |||
} | |||
/* use `basename $0` for my own client name */ | |||
my_name = strrchr(argv[0], '/'); | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name++; | |||
} | |||
/* first, become a JACK client */ | |||
if (argc > 2) { | |||
client = jack_client_open (my_name, | |||
(JackServerName|JackNoStartServer), | |||
&status, argv[2]); | |||
} else { | |||
client = jack_client_open (my_name, JackNoStartServer, &status); | |||
} | |||
if (client == NULL) { | |||
if (status & JackServerFailed) { | |||
fprintf (stderr, "JACK server not running.\n"); | |||
} else { | |||
fprintf (stderr, "JACK open failed, " | |||
"status = 0x%2.0x\n", status); | |||
} | |||
exit (1); | |||
} | |||
/* then, get the internal client handle */ | |||
client_name = argv[1]; | |||
intclient = jack_internal_client_handle (client, client_name, &status); | |||
if (status & JackFailure) { | |||
fprintf (stderr, "client %s not found.\n", client_name); | |||
exit (2); | |||
} | |||
/* now, unload the internal client */ | |||
status = jack_internal_client_unload (client, intclient); | |||
if (status & JackFailure) { | |||
if (status & JackNoSuchClient) { | |||
fprintf (stderr, "client %s is gone.\n", | |||
client_name); | |||
} else { | |||
fprintf (stderr, "could not unload %s, " | |||
"returns 0x%2.0x\n", client_name, status); | |||
} | |||
exit (3); | |||
} else { | |||
fprintf (stdout, "%s unloaded.\n", client_name); | |||
} | |||
jack_client_close(client); | |||
return 0; | |||
} | |||
@@ -1,291 +0,0 @@ | |||
/* | |||
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 <stdlib.h> | |||
#ifndef WIN32 | |||
#include <unistd.h> | |||
#endif | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/session.h> | |||
#include <jack/uuid.h> | |||
char * my_name; | |||
static void | |||
show_version (void) | |||
{ | |||
//fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", my_name); | |||
} | |||
static void | |||
printf_name2uuid (jack_client_t* client, const char* pname) | |||
{ | |||
char *port_component = strchr( pname, ':' ); | |||
size_t csize = port_component - pname + 1; | |||
char client_component[csize]; | |||
snprintf(client_component, csize, "%s", pname); | |||
char *uuid = jack_get_uuid_for_client_name(client, client_component); | |||
if (uuid) { | |||
printf("%s%s\n", uuid, port_component ); | |||
} else { | |||
printf("%s\n",pname); | |||
} | |||
jack_free(uuid); | |||
} | |||
static void | |||
show_usage (void) | |||
{ | |||
show_version (); | |||
fprintf (stderr, "\nUsage: %s [options] [filter string]\n", my_name); | |||
fprintf (stderr, "List active Jack ports, and optionally display extra information.\n"); | |||
fprintf (stderr, "Optionally filter ports which match ALL strings provided after any options.\n\n"); | |||
fprintf (stderr, "Display options:\n"); | |||
fprintf (stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
fprintf (stderr, " -A, --aliases List aliases for each port\n"); | |||
fprintf (stderr, " -c, --connections List connections to/from each port\n"); | |||
fprintf (stderr, " -l, --port-latency Display per-port latency in frames at each port\n"); | |||
fprintf (stderr, " -L, --total-latency Display total latency in frames at each port\n"); | |||
fprintf (stderr, " -p, --properties Display port properties. Output may include:\n" | |||
" input|output, can-monitor, physical, terminal\n\n"); | |||
fprintf (stderr, " -t, --type Display port type\n"); | |||
fprintf (stderr, " -u, --uuid Display uuid instead of client name (if available)\n"); | |||
fprintf (stderr, " -U, --port-uuid Display port uuid\n"); | |||
fprintf (stderr, " -h, --help Display this help message\n"); | |||
fprintf (stderr, " --version Output version information and exit\n\n"); | |||
fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_client_t *client; | |||
jack_status_t status; | |||
jack_options_t options = JackNoStartServer; | |||
const char **ports, **connections; | |||
unsigned int i, j, k; | |||
int skip_port; | |||
int show_aliases = 0; | |||
int show_con = 0; | |||
int show_port_latency = 0; | |||
int show_total_latency = 0; | |||
int show_properties = 0; | |||
int show_type = 0; | |||
int show_uuid = 0; | |||
int show_port_uuid = 0; | |||
int c; | |||
int option_index; | |||
char* aliases[2]; | |||
char *server_name = NULL; | |||
struct option long_options[] = { | |||
{ "server", 1, 0, 's' }, | |||
{ "aliases", 0, 0, 'A' }, | |||
{ "connections", 0, 0, 'c' }, | |||
{ "port-latency", 0, 0, 'l' }, | |||
{ "total-latency", 0, 0, 'L' }, | |||
{ "properties", 0, 0, 'p' }, | |||
{ "type", 0, 0, 't' }, | |||
{ "uuid", 0, 0, 'u' }, | |||
{ "port-uuid", 0, 0, 'U' }, | |||
{ "help", 0, 0, 'h' }, | |||
{ "version", 0, 0, 'v' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
my_name = strrchr(argv[0], '/'); | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name ++; | |||
} | |||
while ((c = getopt_long (argc, argv, "s:AclLphvtuU", long_options, &option_index)) >= 0) { | |||
switch (c) { | |||
case 's': | |||
server_name = (char *) malloc (sizeof (char) * strlen(optarg)); | |||
strcpy (server_name, optarg); | |||
options |= JackServerName; | |||
break; | |||
case 'A': | |||
aliases[0] = (char *) malloc (jack_port_name_size()); | |||
aliases[1] = (char *) malloc (jack_port_name_size()); | |||
show_aliases = 1; | |||
break; | |||
case 'c': | |||
show_con = 1; | |||
break; | |||
case 'l': | |||
show_port_latency = 1; | |||
break; | |||
case 'L': | |||
show_total_latency = 1; | |||
break; | |||
case 'p': | |||
show_properties = 1; | |||
break; | |||
case 't': | |||
show_type = 1; | |||
break; | |||
case 'u': | |||
show_uuid = 1; | |||
break; | |||
case 'U': | |||
show_port_uuid = 1; | |||
break; | |||
case 'h': | |||
show_usage (); | |||
return 1; | |||
break; | |||
case 'v': | |||
show_version (); | |||
return 1; | |||
break; | |||
default: | |||
show_usage (); | |||
return 1; | |||
break; | |||
} | |||
} | |||
/* Open a client connection to the JACK server. Starting a | |||
* new server only to list its ports seems pointless, so we | |||
* specify JackNoStartServer. */ | |||
if ((client = jack_client_open ("lsp", options, &status, server_name)) == 0) { | |||
fprintf (stderr, "Error: cannot connect to JACK, "); | |||
if (status & JackServerFailed) { | |||
fprintf (stderr, "server is not running.\n"); | |||
} else { | |||
fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n", status); | |||
} | |||
return 1; | |||
} | |||
ports = jack_get_ports (client, NULL, NULL, 0); | |||
for (i = 0; ports && ports[i]; ++i) { | |||
// skip over any that don't match ALL of the strings presented at command line | |||
skip_port = 0; | |||
for (k = optind; k < argc; k++){ | |||
if (strstr(ports[i], argv[k]) == NULL ){ | |||
skip_port = 1; | |||
} | |||
} | |||
if (skip_port) continue; | |||
if (show_uuid) { | |||
printf_name2uuid(client, ports[i]); | |||
} else { | |||
printf ("%s\n", ports[i]); | |||
} | |||
jack_port_t *port = jack_port_by_name (client, ports[i]); | |||
if (show_port_uuid) { | |||
char buf[JACK_UUID_STRING_SIZE]; | |||
jack_uuid_t uuid = jack_port_uuid (port); | |||
jack_uuid_unparse (uuid, buf); | |||
printf (" uuid: %s\n", buf); | |||
} | |||
if (show_aliases) { | |||
int cnt; | |||
int i; | |||
cnt = jack_port_get_aliases (port, aliases); | |||
for (i = 0; i < cnt; ++i) { | |||
printf (" %s\n", aliases[i]); | |||
} | |||
} | |||
if (show_con) { | |||
if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { | |||
for (j = 0; connections[j]; j++) { | |||
printf(" "); | |||
if (show_uuid) { | |||
printf_name2uuid(client, connections[j]); | |||
} else { | |||
printf("%s\n", connections[j]); | |||
} | |||
} | |||
jack_free (connections); | |||
} | |||
} | |||
if (show_port_latency) { | |||
if (port) { | |||
jack_latency_range_t range; | |||
jack_port_get_latency_range (port, JackPlaybackLatency, &range); | |||
printf (" port playback latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", | |||
range.min, range.max); | |||
jack_port_get_latency_range (port, JackCaptureLatency, &range); | |||
printf (" port capture latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", | |||
range.min, range.max); | |||
} | |||
} | |||
if (show_total_latency) { | |||
if (port) { | |||
printf (" total latency = %d frames\n", | |||
jack_port_get_total_latency (client, port)); | |||
} | |||
} | |||
if (show_properties) { | |||
if (port) { | |||
int flags = jack_port_flags (port); | |||
printf (" properties: "); | |||
if (flags & JackPortIsInput) { | |||
fputs ("input,", stdout); | |||
} | |||
if (flags & JackPortIsOutput) { | |||
fputs ("output,", stdout); | |||
} | |||
if (flags & JackPortCanMonitor) { | |||
fputs ("can-monitor,", stdout); | |||
} | |||
if (flags & JackPortIsPhysical) { | |||
fputs ("physical,", stdout); | |||
} | |||
if (flags & JackPortIsTerminal) { | |||
fputs ("terminal,", stdout); | |||
} | |||
putc ('\n', stdout); | |||
} | |||
} | |||
if (show_type) { | |||
if (port) { | |||
putc ('\t', stdout); | |||
fputs (jack_port_type (port), stdout); | |||
putc ('\n', stdout); | |||
} | |||
} | |||
} | |||
if (show_aliases) { | |||
free(aliases[0]); | |||
free(aliases[1]); | |||
} | |||
if (ports) | |||
jack_free (ports); | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,238 +0,0 @@ | |||
// gcc -o jack_midi_dump -Wall midi_dump.c -ljack -pthread | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <assert.h> | |||
#include <inttypes.h> | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#include <jack/ringbuffer.h> | |||
#ifdef __MINGW32__ | |||
#include <pthread.h> | |||
#endif | |||
#ifndef WIN32 | |||
#include <signal.h> | |||
#include <pthread.h> | |||
#include <sys/mman.h> | |||
#endif | |||
static jack_port_t* port; | |||
static jack_ringbuffer_t *rb = NULL; | |||
static pthread_mutex_t msg_thread_lock = PTHREAD_MUTEX_INITIALIZER; | |||
static pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER; | |||
static int keeprunning = 1; | |||
static uint64_t monotonic_cnt = 0; | |||
#define RBSIZE 100 | |||
#define MSG_BUFFER_SIZE 4096 | |||
typedef struct { | |||
uint8_t buffer[MSG_BUFFER_SIZE]; | |||
uint32_t size; | |||
uint32_t tme_rel; | |||
uint64_t tme_mon; | |||
} midimsg; | |||
static void | |||
describe (midimsg* event) | |||
{ | |||
if (event->size == 0) { | |||
return; | |||
} | |||
uint8_t type = event->buffer[0] & 0xf0; | |||
uint8_t channel = event->buffer[0] & 0xf; | |||
switch (type) { | |||
case 0x90: | |||
assert (event->size == 3); | |||
printf (" note on (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]); | |||
break; | |||
case 0x80: | |||
assert (event->size == 3); | |||
printf (" note off (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]); | |||
break; | |||
case 0xb0: | |||
assert (event->size == 3); | |||
printf (" control change (channel %2d): controller %3d, value %3d", channel, event->buffer[1], event->buffer[2]); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
int | |||
process (jack_nframes_t frames, void* arg) | |||
{ | |||
void* buffer; | |||
jack_nframes_t N; | |||
jack_nframes_t i; | |||
buffer = jack_port_get_buffer (port, frames); | |||
assert (buffer); | |||
N = jack_midi_get_event_count (buffer); | |||
for (i = 0; i < N; ++i) { | |||
jack_midi_event_t event; | |||
int r; | |||
r = jack_midi_event_get (&event, buffer, i); | |||
if (r != 0) {continue;} | |||
if (event.size > MSG_BUFFER_SIZE) { | |||
fprintf(stderr, "Error: MIDI message was too large, skipping event. Max. allowed size: %d bytes\n", MSG_BUFFER_SIZE); | |||
} | |||
else if (jack_ringbuffer_write_space (rb) >= sizeof(midimsg)) { | |||
midimsg m; | |||
m.tme_mon = monotonic_cnt; | |||
m.tme_rel = event.time; | |||
m.size = event.size; | |||
memcpy (m.buffer, event.buffer, event.size); | |||
jack_ringbuffer_write (rb, (void *) &m, sizeof(midimsg)); | |||
} | |||
else { | |||
fprintf (stderr, "Error: ringbuffer was full, skipping event.\n"); | |||
} | |||
} | |||
monotonic_cnt += frames; | |||
if (pthread_mutex_trylock (&msg_thread_lock) == 0) { | |||
pthread_cond_signal (&data_ready); | |||
pthread_mutex_unlock (&msg_thread_lock); | |||
} | |||
return 0; | |||
} | |||
static void wearedone(int sig) { | |||
fprintf(stderr, "Shutting down\n"); | |||
keeprunning = 0; | |||
/* main loop might be blocked by data_ready when jack server dies. */ | |||
if (pthread_mutex_trylock (&msg_thread_lock) == 0) { | |||
pthread_cond_signal (&data_ready); | |||
pthread_mutex_unlock (&msg_thread_lock); | |||
} | |||
} | |||
static void usage (int status) { | |||
printf ("jack_midi_dump - JACK MIDI Monitor.\n\n"); | |||
printf ("Usage: jack_midi_dump [ OPTIONS ] [CLIENT-NAME]\n\n"); | |||
printf ("Options:\n\ | |||
-a use absolute timestamps relative to application start\n\ | |||
-h display this help and exit\n\ | |||
-r use relative timestamps to previous MIDI event\n\ | |||
\n"); | |||
printf ("\n\ | |||
This tool listens for MIDI events on a JACK MIDI port and prints\n\ | |||
the message to stdout.\n\ | |||
\n\ | |||
If no client name is given it defaults to 'midi-monitor'.\n\ | |||
\n\ | |||
See also: jackd(1)\n\ | |||
\n"); | |||
exit (status); | |||
} | |||
int | |||
main (int argc, char* argv[]) | |||
{ | |||
jack_client_t* client; | |||
char const default_name[] = "midi-monitor"; | |||
char const * client_name; | |||
int time_format = 0; | |||
int r; | |||
int cn = 1; | |||
if (argc > 1) { | |||
if (!strcmp (argv[1], "-a")) { time_format = 1; cn = 2; } | |||
else if (!strcmp (argv[1], "-r")) { time_format = 2; cn = 2; } | |||
else if (!strcmp (argv[1], "-h")) { usage (EXIT_SUCCESS); } | |||
else if (argv[1][0] == '-') { usage (EXIT_FAILURE); } | |||
} | |||
if (argc > cn) { | |||
client_name = argv[cn]; | |||
} else { | |||
client_name = default_name; | |||
} | |||
client = jack_client_open (client_name, JackNullOption, NULL); | |||
if (client == NULL) { | |||
fprintf (stderr, "Could not create JACK client.\n"); | |||
exit (EXIT_FAILURE); | |||
} | |||
rb = jack_ringbuffer_create (RBSIZE * sizeof(midimsg)); | |||
jack_set_process_callback (client, process, 0); | |||
port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
if (port == NULL) { | |||
fprintf (stderr, "Could not register port.\n"); | |||
exit (EXIT_FAILURE); | |||
} | |||
#ifndef WIN32 | |||
if (mlockall (MCL_CURRENT | MCL_FUTURE)) { | |||
fprintf (stderr, "Warning: Can not lock memory.\n"); | |||
} | |||
#endif | |||
r = jack_activate (client); | |||
if (r != 0) { | |||
fprintf (stderr, "Could not activate client.\n"); | |||
exit (EXIT_FAILURE); | |||
} | |||
#ifndef WIN32 | |||
signal(SIGHUP, wearedone); | |||
signal(SIGINT, wearedone); | |||
#endif | |||
pthread_mutex_lock (&msg_thread_lock); | |||
uint64_t prev_event = 0; | |||
while (keeprunning) { | |||
const int mqlen = jack_ringbuffer_read_space (rb) / sizeof(midimsg); | |||
int i; | |||
for (i=0; i < mqlen; ++i) { | |||
size_t j; | |||
midimsg m; | |||
jack_ringbuffer_read(rb, (char*) &m, sizeof(midimsg)); | |||
switch(time_format) { | |||
case 1: | |||
printf ("%7"PRId64":", m.tme_rel + m.tme_mon); | |||
break; | |||
case 2: | |||
printf ("%+6"PRId64":", m.tme_rel + m.tme_mon - prev_event); | |||
break; | |||
default: | |||
printf ("%4d:", m.tme_rel); | |||
break; | |||
} | |||
for (j = 0; j < m.size && j < sizeof(m.buffer); ++j) { | |||
printf (" %02x", m.buffer[j]); | |||
} | |||
describe (&m); | |||
printf("\n"); | |||
prev_event = m.tme_rel + m.tme_mon; | |||
} | |||
fflush (stdout); | |||
pthread_cond_wait (&data_ready, &msg_thread_lock); | |||
} | |||
pthread_mutex_unlock (&msg_thread_lock); | |||
jack_deactivate (client); | |||
jack_client_close (client); | |||
jack_ringbuffer_free (rb); | |||
return 0; | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
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 <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#define TRUE 1 | |||
#define FALSE 0 | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
jack_client_t *client; | |||
char *my_name = strrchr(argv[0], '/'); | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name ++; | |||
} | |||
if (argc != 2) { | |||
fprintf (stderr, "Usage: %s client\n", my_name); | |||
return 1; | |||
} | |||
if ((client = jack_client_open ("input monitoring", JackNullOption, NULL)) == 0) { | |||
fprintf (stderr, "JACK server not running?\n"); | |||
return 1; | |||
} | |||
if (jack_port_request_monitor_by_name (client, argv[1], TRUE)) { | |||
fprintf (stderr, "could not enable monitoring for %s\n", argv[1]); | |||
jack_client_close (client); | |||
return 1; | |||
} | |||
#ifdef WIN32 | |||
Sleep (30*1000); | |||
#else | |||
sleep (30); | |||
#endif | |||
if (jack_port_request_monitor_by_name (client, argv[1], FALSE)) { | |||
fprintf (stderr, "could not disable monitoring for %s\n", argv[1]); | |||
} | |||
jack_client_close (client); | |||
exit (0); | |||
} | |||
@@ -1,802 +0,0 @@ | |||
/* | |||
NetJack Client | |||
Copyright (C) 2008 Marc-Olivier Barre <marco@marcochapeau.org> | |||
Copyright (C) 2008 Pieter Palmers <pieterpalmers@users.sourceforge.net> | |||
Copyright (C) 2006 Torben Hohn <torbenh@gmx.de> | |||
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. | |||
*/ | |||
/** @file netsource.c | |||
* | |||
* @brief This client connects a remote slave JACK to a local JACK server assumed to be the master | |||
*/ | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#if defined(HAVE_CONFIG_H) | |||
#include "config.h" | |||
#endif | |||
#ifdef WIN32 | |||
#include <winsock2.h> | |||
#define socklen_t int | |||
#include <malloc.h> | |||
#else | |||
#include <netinet/in.h> | |||
#include <netdb.h> | |||
#include <sys/socket.h> | |||
#endif | |||
/* These two required by FreeBSD. */ | |||
#include <sys/types.h> | |||
#include <jack/jack.h> | |||
#include <netjack_packet.h> | |||
#if HAVE_SAMPLERATE | |||
#include <samplerate.h> | |||
#endif | |||
#if HAVE_CELT | |||
#include <celt/celt.h> | |||
#endif | |||
#ifndef CUSTOM_MODES | |||
#define CUSTOM_MODES // for opus_custom_decoder_init | |||
#endif | |||
#if HAVE_OPUS | |||
#include <opus/opus.h> | |||
#include <opus/opus_custom.h> | |||
#endif | |||
#include <math.h> | |||
JSList *capture_ports = NULL; | |||
JSList *capture_srcs = NULL; | |||
int capture_channels = 0; | |||
int capture_channels_audio = 2; | |||
int capture_channels_midi = 1; | |||
JSList *playback_ports = NULL; | |||
JSList *playback_srcs = NULL; | |||
int playback_channels = 0; | |||
int playback_channels_audio = 2; | |||
int playback_channels_midi = 1; | |||
int dont_htonl_floats = 0; | |||
int latency = 5; | |||
jack_nframes_t factor = 1; | |||
int bitdepth = 0; | |||
int mtu = 1400; | |||
int reply_port = 0; | |||
int bind_port = 0; | |||
int redundancy = 1; | |||
jack_client_t *client; | |||
packet_cache * packcache = 0; | |||
int state_connected = 0; | |||
int state_latency = 0; | |||
int state_netxruns = 0; | |||
int state_currentframe = 0; | |||
int state_recv_packet_queue_time = 0; | |||
int quit = 0; | |||
int outsockfd; | |||
int insockfd; | |||
#ifdef WIN32 | |||
struct sockaddr_in destaddr; | |||
struct sockaddr_in bindaddr; | |||
#else | |||
struct sockaddr destaddr; | |||
struct sockaddr bindaddr; | |||
#endif | |||
int sync_state; | |||
jack_transport_state_t last_transport_state; | |||
int framecnt = 0; | |||
int cont_miss = 0; | |||
int freewheeling = 0; | |||
/** | |||
* This Function allocates all the I/O Ports which are added the lists. | |||
*/ | |||
void | |||
alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int n_playback_midi) | |||
{ | |||
int port_flags = JackPortIsOutput; | |||
int chn; | |||
jack_port_t *port; | |||
char buf[32]; | |||
capture_ports = NULL; | |||
/* Allocate audio capture channels */ | |||
for (chn = 0; chn < n_capture_audio; chn++) { | |||
snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); | |||
port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); | |||
if (!port) { | |||
printf( "jack_netsource: cannot register %s port\n", buf); | |||
break; | |||
} | |||
if (bitdepth == 1000) { | |||
#if HAVE_CELT | |||
#if HAVE_CELT_API_0_11 | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), jack_get_buffer_size(client), NULL ); | |||
capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create_custom( celt_mode, 1, NULL ) ); | |||
#elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), jack_get_buffer_size(client), NULL ); | |||
capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); | |||
#else | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); | |||
capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); | |||
#endif | |||
#endif | |||
} else if (bitdepth == 999) { | |||
#if HAVE_OPUS | |||
int err; | |||
OpusCustomMode *opus_mode = opus_custom_mode_create(jack_get_sample_rate( client ), jack_get_buffer_size(client), &err); | |||
if (err != OPUS_OK) { printf("OPUS MODE FAILED\n"); } | |||
OpusCustomDecoder *decoder = opus_custom_decoder_create(opus_mode, 1, &err); | |||
if (err != OPUS_OK) { printf("OPUS DECODER FAILED\n"); } | |||
opus_custom_decoder_init(decoder, opus_mode, 1); | |||
capture_srcs = jack_slist_append(capture_srcs, decoder); | |||
#endif | |||
} else { | |||
#if HAVE_SAMPLERATE | |||
capture_srcs = jack_slist_append (capture_srcs, src_new (SRC_LINEAR, 1, NULL)); | |||
#endif | |||
} | |||
capture_ports = jack_slist_append (capture_ports, port); | |||
} | |||
/* Allocate midi capture channels */ | |||
for (chn = n_capture_audio; chn < n_capture_midi + n_capture_audio; chn++) { | |||
snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); | |||
port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); | |||
if (!port) { | |||
printf ("jack_netsource: cannot register %s port\n", buf); | |||
break; | |||
} | |||
capture_ports = jack_slist_append(capture_ports, port); | |||
} | |||
/* Allocate audio playback channels */ | |||
port_flags = JackPortIsInput; | |||
playback_ports = NULL; | |||
for (chn = 0; chn < n_playback_audio; chn++) { | |||
snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); | |||
port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); | |||
if (!port) { | |||
printf ("jack_netsource: cannot register %s port\n", buf); | |||
break; | |||
} | |||
if( bitdepth == 1000 ) { | |||
#if HAVE_CELT | |||
#if HAVE_CELT_API_0_11 | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), jack_get_buffer_size(client), NULL ); | |||
playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create_custom( celt_mode, 1, NULL ) ); | |||
#elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), jack_get_buffer_size(client), NULL ); | |||
playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); | |||
#else | |||
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); | |||
playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); | |||
#endif | |||
#endif | |||
} else if( bitdepth == 999 ) { | |||
#if HAVE_OPUS | |||
const int kbps = factor; | |||
printf("new opus encoder %d kbps\n", kbps); | |||
int err; | |||
OpusCustomMode *opus_mode = opus_custom_mode_create(jack_get_sample_rate (client), jack_get_buffer_size(client), &err ); // XXX free me | |||
if (err != OPUS_OK) { printf("OPUS MODE FAILED\n"); } | |||
OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, &err ); | |||
if (err != OPUS_OK) { printf("OPUS ENCODER FAILED\n"); } | |||
opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second | |||
opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); | |||
opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); | |||
opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); | |||
opus_custom_encoder_init(oe, opus_mode, 1); | |||
playback_srcs = jack_slist_append(playback_srcs, oe); | |||
#endif | |||
} else { | |||
#if HAVE_SAMPLERATE | |||
playback_srcs = jack_slist_append (playback_srcs, src_new (SRC_LINEAR, 1, NULL)); | |||
#endif | |||
} | |||
playback_ports = jack_slist_append (playback_ports, port); | |||
} | |||
/* Allocate midi playback channels */ | |||
for (chn = n_playback_audio; chn < n_playback_midi + n_playback_audio; chn++) { | |||
snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); | |||
port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); | |||
if (!port) { | |||
printf ("jack_netsource: cannot register %s port\n", buf); | |||
break; | |||
} | |||
playback_ports = jack_slist_append (playback_ports, port); | |||
} | |||
} | |||
/** | |||
* The Sync callback... sync state is set elsewhere... | |||
* we will see if this is working correctly. | |||
* i don't really believe in it yet. | |||
*/ | |||
int | |||
sync_cb (jack_transport_state_t state, jack_position_t *pos, void *arg) | |||
{ | |||
static int latency_count = 0; | |||
int retval = sync_state; | |||
if (! state_connected) { | |||
return 1; | |||
} | |||
if (latency_count) { | |||
latency_count--; | |||
retval = 0; | |||
} | |||
else if (state == JackTransportStarting && last_transport_state != JackTransportStarting) { | |||
retval = 0; | |||
latency_count = latency - 1; | |||
} | |||
last_transport_state = state; | |||
return retval; | |||
} | |||
void | |||
freewheel_cb (int starting, void *arg) | |||
{ | |||
freewheeling = starting; | |||
} | |||
int deadline_goodness = 0; | |||
/** | |||
* The process callback for this JACK application. | |||
* It is called by JACK at the appropriate times. | |||
*/ | |||
int | |||
process (jack_nframes_t nframes, void *arg) | |||
{ | |||
jack_nframes_t net_period; | |||
int rx_bufsize, tx_bufsize; | |||
jack_default_audio_sample_t *buf; | |||
jack_port_t *port; | |||
JSList *node; | |||
int chn; | |||
int size, i; | |||
const char *porttype; | |||
int input_fd; | |||
jack_position_t local_trans_pos; | |||
uint32_t *packet_buf_tx, *packet_bufX; | |||
uint32_t *rx_packet_ptr; | |||
jack_time_t packet_recv_timestamp; | |||
if( bitdepth == 1000 || bitdepth == 999) | |||
net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | |||
else | |||
net_period = (float) nframes / (float) factor; | |||
rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); | |||
tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header); | |||
/* Allocate a buffer where both In and Out Buffer will fit */ | |||
packet_buf_tx = alloca (tx_bufsize); | |||
jacknet_packet_header *pkthdr_tx = (jacknet_packet_header *) packet_buf_tx; | |||
/* | |||
* for latency==0 we need to send out the packet before we wait on the reply. | |||
* but this introduces a cycle of latency, when netsource is connected to itself. | |||
* so we send out before read only in zero latency mode. | |||
* | |||
*/ | |||
if( latency == 0 ) { | |||
/* reset packet_bufX... */ | |||
packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
/* ---------- Send ---------- */ | |||
render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, | |||
packet_bufX, net_period, dont_htonl_floats); | |||
/* fill in packet hdr */ | |||
pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); | |||
pkthdr_tx->transport_frame = local_trans_pos.frame; | |||
pkthdr_tx->framecnt = framecnt; | |||
pkthdr_tx->latency = latency; | |||
pkthdr_tx->reply_port = reply_port; | |||
pkthdr_tx->sample_rate = jack_get_sample_rate (client); | |||
pkthdr_tx->period_size = nframes; | |||
/* playback for us is capture on the other side */ | |||
pkthdr_tx->capture_channels_audio = playback_channels_audio; | |||
pkthdr_tx->playback_channels_audio = capture_channels_audio; | |||
pkthdr_tx->capture_channels_midi = playback_channels_midi; | |||
pkthdr_tx->playback_channels_midi = capture_channels_midi; | |||
pkthdr_tx->mtu = mtu; | |||
if( freewheeling != 0 ) | |||
pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; | |||
else | |||
pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; | |||
//printf("goodness=%d\n", deadline_goodness ); | |||
packet_header_hton (pkthdr_tx); | |||
if (cont_miss < 3 * latency + 5) { | |||
int r; | |||
for( r = 0; r < redundancy; r++ ) | |||
netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); | |||
} else if (cont_miss > 50 + 5 * latency) { | |||
state_connected = 0; | |||
packet_cache_reset_master_address( packcache ); | |||
//printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); | |||
cont_miss = 0; | |||
} | |||
} | |||
/* | |||
* ok... now the RECEIVE code. | |||
* | |||
*/ | |||
if( reply_port ) | |||
input_fd = insockfd; | |||
else | |||
input_fd = outsockfd; | |||
// for latency == 0 we can poll. | |||
if( (latency == 0) || (freewheeling != 0) ) { | |||
jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client) / jack_get_sample_rate(client); | |||
// Now loop until we get the right packet. | |||
while(1) { | |||
jack_nframes_t got_frame; | |||
if ( ! netjack_poll_deadline( input_fd, deadline ) ) | |||
break; | |||
packet_cache_drain_socket(packcache, input_fd); | |||
if (packet_cache_get_next_available_framecnt( packcache, framecnt - latency, &got_frame )) | |||
if( got_frame == (framecnt - latency) ) | |||
break; | |||
} | |||
} else { | |||
// normally: | |||
// only drain socket. | |||
packet_cache_drain_socket(packcache, input_fd); | |||
} | |||
size = packet_cache_retreive_packet_pointer( packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp ); | |||
/* First alternative : we received what we expected. Render the data | |||
* to the JACK ports so it can be played. */ | |||
if (size == rx_bufsize) { | |||
uint32_t *packet_buf_rx = rx_packet_ptr; | |||
jacknet_packet_header *pkthdr_rx = (jacknet_packet_header *) packet_buf_rx; | |||
packet_bufX = packet_buf_rx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
// calculate how much time there would have been, if this packet was sent at the deadline. | |||
int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp); | |||
packet_header_ntoh (pkthdr_rx); | |||
deadline_goodness = recv_time_offset - (int)pkthdr_rx->latency; | |||
//printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset ); | |||
if (cont_miss) { | |||
//printf("Frame %d \tRecovered from dropouts\n", framecnt); | |||
cont_miss = 0; | |||
} | |||
render_payload_to_jack_ports (bitdepth, packet_bufX, net_period, | |||
capture_ports, capture_srcs, nframes, dont_htonl_floats); | |||
state_currentframe = framecnt; | |||
state_recv_packet_queue_time = recv_time_offset; | |||
state_connected = 1; | |||
sync_state = pkthdr_rx->sync_state; | |||
packet_cache_release_packet( packcache, framecnt - latency ); | |||
} | |||
/* Second alternative : we've received something that's not | |||
* as big as expected or we missed a packet. We render silence | |||
* to the output ports */ | |||
else { | |||
jack_nframes_t latency_estimate; | |||
if( packet_cache_find_latency( packcache, framecnt, &latency_estimate ) ) | |||
//if( (state_latency == 0) || (latency_estimate < state_latency) ) | |||
state_latency = latency_estimate; | |||
// Set the counters up. | |||
state_currentframe = framecnt; | |||
//state_latency = framecnt - pkthdr->framecnt; | |||
state_netxruns += 1; | |||
//printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); | |||
//printf ("Frame %d \tPacket missed or incomplete\n", framecnt); | |||
cont_miss += 1; | |||
chn = 0; | |||
node = capture_ports; | |||
while (node != NULL) { | |||
port = (jack_port_t *) node->data; | |||
buf = jack_port_get_buffer (port, nframes); | |||
porttype = jack_port_type (port); | |||
if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0) | |||
for (i = 0; i < nframes; i++) | |||
buf[i] = 0.0; | |||
else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0) | |||
jack_midi_clear_buffer (buf); | |||
node = jack_slist_next (node); | |||
chn++; | |||
} | |||
} | |||
if (latency != 0) { | |||
/* reset packet_bufX... */ | |||
packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
/* ---------- Send ---------- */ | |||
render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, | |||
packet_bufX, net_period, dont_htonl_floats); | |||
/* fill in packet hdr */ | |||
pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); | |||
pkthdr_tx->transport_frame = local_trans_pos.frame; | |||
pkthdr_tx->framecnt = framecnt; | |||
pkthdr_tx->latency = latency; | |||
pkthdr_tx->reply_port = reply_port; | |||
pkthdr_tx->sample_rate = jack_get_sample_rate (client); | |||
pkthdr_tx->period_size = nframes; | |||
/* playback for us is capture on the other side */ | |||
pkthdr_tx->capture_channels_audio = playback_channels_audio; | |||
pkthdr_tx->playback_channels_audio = capture_channels_audio; | |||
pkthdr_tx->capture_channels_midi = playback_channels_midi; | |||
pkthdr_tx->playback_channels_midi = capture_channels_midi; | |||
pkthdr_tx->mtu = mtu; | |||
if( freewheeling != 0 ) | |||
pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; | |||
else | |||
pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; | |||
//printf("goodness=%d\n", deadline_goodness ); | |||
packet_header_hton (pkthdr_tx); | |||
if (cont_miss < 3 * latency + 5) { | |||
int r; | |||
for( r = 0; r < redundancy; r++ ) | |||
netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); | |||
} else if (cont_miss > 50 + 5 * latency) { | |||
state_connected = 0; | |||
packet_cache_reset_master_address( packcache ); | |||
//printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); | |||
cont_miss = 0; | |||
} | |||
} | |||
framecnt++; | |||
return 0; | |||
} | |||
/** | |||
* This is the shutdown callback for this JACK application. | |||
* It is called by JACK 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); | |||
} | |||
void | |||
init_sockaddr_in (struct sockaddr_in *name , const char *hostname , uint16_t port) | |||
{ | |||
name->sin_family = AF_INET ; | |||
name->sin_port = htons (port); | |||
if (hostname) { | |||
struct hostent *hostinfo = gethostbyname (hostname); | |||
if (hostinfo == NULL) { | |||
fprintf (stderr, "init_sockaddr_in: unknown host: %s.\n", hostname); | |||
fflush( stderr ); | |||
} | |||
#ifdef WIN32 | |||
name->sin_addr.s_addr = inet_addr( hostname ); | |||
#else | |||
name->sin_addr = *(struct in_addr *) hostinfo->h_addr ; | |||
#endif | |||
} else | |||
name->sin_addr.s_addr = htonl (INADDR_ANY) ; | |||
} | |||
void | |||
printUsage () | |||
{ | |||
fprintf (stderr, "usage: jack_netsource [options]\n" | |||
"\n" | |||
" -h this help text\n" | |||
" -H <slave host> - Host name of the slave JACK\n" | |||
" -o <num channels> - Number of audio playback channels\n" | |||
" -i <num channels> - Number of audio capture channels\n" | |||
" -O <num channels> - Number of midi playback channels\n" | |||
" -I <num channels> - Number of midi capture channels\n" | |||
" -n <periods> - Network latency in JACK periods\n" | |||
" -p <port> - UDP port that the slave is listening on\n" | |||
" -r <reply port> - UDP port that we are listening on\n" | |||
" -B <bind port> - reply port, for use in NAT environments\n" | |||
" -b <bitdepth> - Set transport to use 16bit or 8bit\n" | |||
" -c <kbits> - Use CELT encoding with <kbits> kbits per channel\n" | |||
" -P <kbits> - Use Opus encoding with <kbits> kbits per channel\n" | |||
" -m <mtu> - Assume this mtu for the link\n" | |||
" -R <N> - Redundancy: send out packets N times.\n" | |||
" -e - skip host-to-network endianness conversion\n" | |||
" -N <jack name> - Reports a different name to jack\n" | |||
" -s <server name> - The name of the local jack server\n" | |||
"\n"); | |||
} | |||
void | |||
sigterm_handler( int signal ) | |||
{ | |||
quit = 1; | |||
} | |||
int | |||
main (int argc, char *argv[]) | |||
{ | |||
/* Some startup related basics */ | |||
char *client_name, *server_name = NULL, *peer_ip; | |||
int peer_port = 3000; | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
#ifdef WIN32 | |||
WSADATA wsa; | |||
int rc = WSAStartup(MAKEWORD(2, 0), &wsa); | |||
#endif | |||
/* Torben's famous state variables, aka "the reporting API" ! */ | |||
/* heh ? these are only the copies of them ;) */ | |||
int statecopy_connected, statecopy_latency, statecopy_netxruns; | |||
jack_nframes_t net_period; | |||
/* Argument parsing stuff */ | |||
extern char *optarg; | |||
extern int optind, optopt; | |||
int errflg = 0, c; | |||
if (argc < 3) { | |||
printUsage (); | |||
return 1; | |||
} | |||
client_name = (char *) malloc (sizeof (char) * 10); | |||
peer_ip = (char *) malloc (sizeof (char) * 10); | |||
sprintf(client_name, "netjack"); | |||
sprintf(peer_ip, "localhost"); | |||
while ((c = getopt (argc, argv, ":h:H:o:i:O:I:n:p:r:B:b:c:m:R:e:N:s:P:")) != -1) { | |||
switch (c) { | |||
case 'h': | |||
printUsage(); | |||
exit (0); | |||
break; | |||
case 'H': | |||
free(peer_ip); | |||
peer_ip = (char *) malloc (sizeof (char) * strlen (optarg) + 1); | |||
strcpy (peer_ip, optarg); | |||
break; | |||
case 'o': | |||
playback_channels_audio = atoi (optarg); | |||
break; | |||
case 'i': | |||
capture_channels_audio = atoi (optarg); | |||
break; | |||
case 'O': | |||
playback_channels_midi = atoi (optarg); | |||
break; | |||
case 'I': | |||
capture_channels_midi = atoi (optarg); | |||
break; | |||
case 'n': | |||
latency = atoi (optarg); | |||
break; | |||
case 'p': | |||
peer_port = atoi (optarg); | |||
break; | |||
case 'r': | |||
reply_port = atoi (optarg); | |||
break; | |||
case 'B': | |||
bind_port = atoi (optarg); | |||
break; | |||
case 'f': | |||
factor = atoi (optarg); | |||
printf("This feature is deprecated and will be removed in future netjack versions. CELT offers a superiour way to conserve bandwidth"); | |||
break; | |||
case 'b': | |||
bitdepth = atoi (optarg); | |||
break; | |||
case 'c': | |||
#if HAVE_CELT | |||
bitdepth = 1000; | |||
factor = atoi (optarg); | |||
#else | |||
printf( "not built with celt support\n" ); | |||
exit(10); | |||
#endif | |||
break; | |||
case 'P': | |||
#if HAVE_OPUS | |||
bitdepth = 999; | |||
factor = atoi (optarg); | |||
#else | |||
printf( "not built with opus support\n" ); | |||
exit(10); | |||
#endif | |||
break; | |||
case 'm': | |||
mtu = atoi (optarg); | |||
break; | |||
case 'R': | |||
redundancy = atoi (optarg); | |||
break; | |||
case 'e': | |||
dont_htonl_floats = 1; | |||
break; | |||
case 'N': | |||
free(client_name); | |||
client_name = (char *) malloc (sizeof (char) * strlen (optarg) + 1); | |||
strcpy (client_name, optarg); | |||
break; | |||
case 's': | |||
server_name = (char *) malloc (sizeof (char) * strlen (optarg) + 1); | |||
strcpy (server_name, optarg); | |||
options |= JackServerName; | |||
break; | |||
case ':': | |||
fprintf (stderr, "Option -%c requires an operand\n", optopt); | |||
errflg++; | |||
break; | |||
case '?': | |||
fprintf (stderr, "Unrecognized option: -%c\n", optopt); | |||
errflg++; | |||
} | |||
} | |||
if (errflg) { | |||
printUsage (); | |||
exit (2); | |||
} | |||
capture_channels = capture_channels_audio + capture_channels_midi; | |||
playback_channels = playback_channels_audio + playback_channels_midi; | |||
outsockfd = socket (AF_INET, SOCK_DGRAM, 0); | |||
insockfd = socket (AF_INET, SOCK_DGRAM, 0); | |||
if ((outsockfd == -1) || (insockfd == -1)) { | |||
fprintf (stderr, "can not open sockets\n" ); | |||
return 1; | |||
} | |||
init_sockaddr_in ((struct sockaddr_in *) &destaddr, peer_ip, peer_port); | |||
if (bind_port) { | |||
init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, bind_port); | |||
if( bind (outsockfd, &bindaddr, sizeof (bindaddr)) ) { | |||
fprintf (stderr, "bind failure\n" ); | |||
} | |||
} | |||
if (reply_port) { | |||
init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, reply_port); | |||
if( bind (insockfd, &bindaddr, sizeof (bindaddr)) ) { | |||
fprintf (stderr, "bind failure\n" ); | |||
} | |||
} | |||
/* try to become a client of 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" | |||
"Is the JACK server running ?\n", status); | |||
return 1; | |||
} | |||
/* Set up jack callbacks */ | |||
jack_set_process_callback (client, process, 0); | |||
jack_set_sync_callback (client, sync_cb, 0); | |||
jack_set_freewheel_callback (client, freewheel_cb, 0); | |||
jack_on_shutdown (client, jack_shutdown, 0); | |||
alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); | |||
if( bitdepth == 1000 || bitdepth == 999) | |||
net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | |||
else | |||
net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); | |||
int rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); | |||
packcache = packet_cache_new (latency + 50, rx_bufsize, mtu); | |||
/* tell the JACK server that we are ready to roll */ | |||
if (jack_activate (client)) { | |||
fprintf (stderr, "Cannot activate client"); | |||
return 1; | |||
} | |||
/* Now sleep forever... and evaluate the state_ vars */ | |||
signal( SIGTERM, sigterm_handler ); | |||
signal( SIGINT, sigterm_handler ); | |||
statecopy_connected = 2; // make it report unconnected on start. | |||
statecopy_latency = state_latency; | |||
statecopy_netxruns = state_netxruns; | |||
while ( !quit ) { | |||
#ifdef WIN32 | |||
Sleep (1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
if (statecopy_connected != state_connected) { | |||
statecopy_connected = state_connected; | |||
if (statecopy_connected) { | |||
state_netxruns = 1; // We want to reset the netxrun count on each new connection | |||
printf ("Connected :-)\n"); | |||
} else | |||
printf ("Not Connected\n"); | |||
fflush(stdout); | |||
} | |||
if (statecopy_connected) { | |||
if (statecopy_netxruns != state_netxruns) { | |||
statecopy_netxruns = state_netxruns; | |||
printf ("%s: at frame %06d -> total netxruns %d (%d%%) queue time= %d\n", | |||
client_name, | |||
state_currentframe, | |||
statecopy_netxruns, | |||
100 * statecopy_netxruns / state_currentframe, | |||
state_recv_packet_queue_time); | |||
fflush(stdout); | |||
} | |||
} else { | |||
if (statecopy_latency != state_latency) { | |||
statecopy_latency = state_latency; | |||
if (statecopy_latency > 1) | |||
printf ("current latency %d\n", statecopy_latency); | |||
fflush(stdout); | |||
} | |||
} | |||
} | |||
jack_client_close (client); | |||
packet_cache_free (packcache); | |||
exit (0); | |||
} |
@@ -1,326 +0,0 @@ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <jack/jack.h> | |||
#include <jack/metadata.h> | |||
#include <jack/uuid.h> | |||
#include <jack/session.h> | |||
static int subject_is_client = 0; | |||
static int subject_is_port = 0; | |||
static jack_uuid_t uuid = JACK_UUID_EMPTY_INITIALIZER; | |||
static char* subject = NULL; | |||
static void | |||
show_usage (void) | |||
{ | |||
fprintf (stderr, "\nUsage: jack_property [options] UUID [ key [ value [ type ] ] ]\n"); | |||
fprintf (stderr, "Set/Display JACK properties (metadata).\n\n"); | |||
fprintf (stderr, "Set options:\n"); | |||
fprintf (stderr, " -s, --set Set property \"key\" to \"value\" for \"UUID\" with optional MIME type \"type\"\n"); | |||
fprintf (stderr, " -d, --delete Remove/delete property \"key\" for \"UUID\"\n"); | |||
fprintf (stderr, " -d, --delete UUID Remove/delete all properties for \"UUID\"\n"); | |||
fprintf (stderr, " -D, --delete-all Remove/delete all properties\n"); | |||
fprintf (stderr, " --client Interpret UUID as a client name, not a UUID\n"); | |||
fprintf (stderr, " --port \tInterpret UUID as a port name, not a UUID\n"); | |||
fprintf (stderr, "\nDisplay options:\n"); | |||
fprintf (stderr, " -l Show all properties\n"); | |||
fprintf (stderr, " -l, --list UUID \tShow value for all properties of UUID\n"); | |||
fprintf (stderr, " -l, --list UUID key Show value for key of UUID\n"); | |||
fprintf (stderr, "\nFor more information see https://jackaudio.org/\n"); | |||
} | |||
static int | |||
get_subject (jack_client_t* client, char* argv[], int* optind) | |||
{ | |||
if (subject_is_client) { | |||
char* cstr = argv[(*optind)++]; | |||
char* ustr; | |||
if ((ustr = jack_get_uuid_for_client_name (client, cstr)) == NULL) { | |||
fprintf (stderr, "cannot get UUID for client named %s\n", cstr); | |||
return -1; | |||
} | |||
if (jack_uuid_parse (ustr, &uuid)) { | |||
fprintf (stderr, "cannot parse client UUID as UUID '%s' '%s'\n", cstr, ustr); | |||
return -1; | |||
} | |||
subject = cstr; | |||
} else if (subject_is_port) { | |||
char* pstr = argv[(*optind)++]; | |||
jack_port_t* port; | |||
if ((port = jack_port_by_name (client, pstr)) == NULL) { | |||
fprintf (stderr, "cannot find port name %s\n", pstr); | |||
return -1; | |||
} | |||
uuid = jack_port_uuid (port); | |||
subject = pstr; | |||
} else { | |||
char* str = argv[(*optind)++]; | |||
if (jack_uuid_parse (str, &uuid)) { | |||
fprintf (stderr, "cannot parse subject as UUID\n"); | |||
return -1; | |||
} | |||
subject = str; | |||
} | |||
return 0; | |||
} | |||
int main (int argc, char* argv[]) | |||
{ | |||
jack_client_t* client = NULL; | |||
jack_options_t options = JackNoStartServer; | |||
char* key = NULL; | |||
char* value = NULL; | |||
char* type = NULL; | |||
int set = 1; | |||
int delete = 0; | |||
int delete_all = 0; | |||
int c; | |||
int option_index; | |||
extern int optind; | |||
struct option long_options[] = { | |||
{ "set", 0, 0, 's' }, | |||
{ "delete", 0, 0, 'd' }, | |||
{ "delete-all", 0, 0, 'D' }, | |||
{ "list", 0, 0, 'l' }, | |||
{ "client", 0, 0, 'c' }, | |||
{ "port", 0, 0, 'p' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
if (argc < 2) { | |||
show_usage (); | |||
exit (1); | |||
} | |||
while ((c = getopt_long (argc, argv, "sdDlaApc", long_options, &option_index)) >= 0) { | |||
switch (c) { | |||
case 's': | |||
if (argc < 5) { | |||
show_usage (); | |||
exit (1); | |||
} | |||
set = 1; | |||
break; | |||
case 'd': | |||
if (argc < 3) { | |||
show_usage (); | |||
return 1; | |||
} | |||
set = 0; | |||
delete = 1; | |||
break; | |||
case 'D': | |||
delete = 0; | |||
set = 0; | |||
delete_all = 1; | |||
break; | |||
case 'l': | |||
set = 0; | |||
delete = 0; | |||
delete_all = 0; | |||
break; | |||
case 'p': | |||
subject_is_port = 1; | |||
break; | |||
case 'c': | |||
subject_is_client = 1; | |||
break; | |||
case '?': | |||
default: | |||
show_usage (); | |||
exit (1); | |||
} | |||
} | |||
if ((client = jack_client_open ("jack-property", options, NULL)) == 0) { | |||
fprintf (stderr, "Cannot connect to JACK server\n"); | |||
exit (1); | |||
} | |||
if (delete_all) { | |||
if (jack_remove_all_properties (client) == 0) { | |||
printf ("JACK metadata successfully delete\n"); | |||
exit (0); | |||
} | |||
exit (1); | |||
} | |||
if (delete) { | |||
int args_left = argc - optind; | |||
if (args_left < 1) { | |||
show_usage (); | |||
exit (1); | |||
} | |||
/* argc == 3: delete all properties for a subject | |||
argc == 4: delete value of key for subject | |||
*/ | |||
if (args_left >= 2) { | |||
if (get_subject (client, argv, &optind)) { | |||
return 1; | |||
} | |||
key = argv[optind++]; | |||
if (jack_remove_property (client, uuid, key)) { | |||
fprintf (stderr, "\"%s\" property not removed for %s\n", key, subject); | |||
exit (1); | |||
} | |||
} else { | |||
if (get_subject (client, argv, &optind)) { | |||
return 1; | |||
} | |||
if (jack_remove_properties (client, uuid) < 0) { | |||
fprintf (stderr, "cannot remove properties for UUID %s\n", subject); | |||
exit (1); | |||
} | |||
} | |||
} else if (set) { | |||
int args_left = argc - optind; | |||
if (get_subject (client, argv, &optind)) { | |||
return -1; | |||
} | |||
key = argv[optind++]; | |||
value = argv[optind++]; | |||
if (args_left >= 3) { | |||
type = argv[optind++]; | |||
} else { | |||
type = ""; | |||
} | |||
if (jack_set_property (client, uuid, key, value, type)) { | |||
fprintf (stderr, "cannot set value for key %s of %s\n", key, subject); | |||
exit (1); | |||
} | |||
} else { | |||
/* list properties */ | |||
int args_left = argc - optind; | |||
if (args_left >= 2) { | |||
/* list properties for a UUID/key pair */ | |||
if (get_subject (client, argv, &optind)) { | |||
return -1; | |||
} | |||
key = argv[optind++]; | |||
if (jack_get_property (uuid, key, &value, &type) == 0) { | |||
printf ("%s\n", value); | |||
free (value); | |||
if (type) { | |||
free (type); | |||
} | |||
} else { | |||
fprintf (stderr, "Value not found for %s of %s\n", key, subject); | |||
exit (1); | |||
} | |||
} else if (args_left == 1) { | |||
/* list all properties for a given UUID */ | |||
jack_description_t description; | |||
int cnt, n; | |||
if (get_subject (client, argv, &optind)) { | |||
return -1; | |||
} | |||
if ((cnt = jack_get_properties (uuid, &description)) < 0) { | |||
fprintf (stderr, "could not retrieve properties for %s\n", subject); | |||
exit (1); | |||
} | |||
for (n = 0; n < cnt; ++n) { | |||
if (description.properties[n].type) { | |||
printf ("key: %s value: %s type: %s\n", | |||
description.properties[n].key, | |||
description.properties[n].data, | |||
description.properties[n].type); | |||
} else { | |||
printf ("key: %s value: %s\n", | |||
description.properties[n].key, | |||
description.properties[n].data); | |||
} | |||
} | |||
jack_free_description (&description, 0); | |||
} else { | |||
/* list all properties */ | |||
jack_description_t* description; | |||
int cnt, n; | |||
size_t p; | |||
char buf[JACK_UUID_STRING_SIZE]; | |||
if ((cnt = jack_get_all_properties (&description)) < 0) { | |||
fprintf (stderr, "could not retrieve all properties\n"); | |||
exit (1); | |||
} | |||
for (n = 0; n < cnt; ++n) { | |||
jack_uuid_unparse (description[n].subject, buf); | |||
printf ("%s\n", buf); | |||
for (p = 0; p < description[n].property_cnt; ++p) { | |||
if (description[n].properties[p].type) { | |||
printf ("key: %s value: %s type: %s\n", | |||
description[n].properties[p].key, | |||
description[n].properties[p].data, | |||
description[n].properties[p].type); | |||
} else { | |||
printf ("key: %s value: %s\n", | |||
description[n].properties[p].key, | |||
description[n].properties[p].data); | |||
} | |||
} | |||
jack_free_description (&description[n], 0); | |||
} | |||
free (description); | |||
} | |||
} | |||
(void) jack_client_close (client); | |||
return 0; | |||
} |
@@ -1,87 +0,0 @@ | |||
/* | |||
* smaplerate.c -- get current samplerate | |||
* | |||
* 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 <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
char *package; /* program name */ | |||
jack_client_t *client; | |||
void jack_shutdown(void *arg) | |||
{ | |||
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); | |||
} | |||
void parse_arguments(int argc, char *argv[]) | |||
{ | |||
/* basename $0 */ | |||
package = strrchr(argv[0], '/'); | |||
if (package == 0) | |||
package = argv[0]; | |||
else | |||
package++; | |||
if (argc==1) { | |||
return; | |||
} | |||
fprintf(stderr, "usage: %s\n", package); | |||
exit(9); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
parse_arguments(argc, argv); | |||
/* become a JACK client */ | |||
if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) { | |||
fprintf(stderr, "JACK server not running?\n"); | |||
exit(1); | |||
} | |||
#ifndef WIN32 | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
jack_on_shutdown(client, jack_shutdown, 0); | |||
fprintf(stdout, "%d\n", jack_get_sample_rate( client ) ); | |||
jack_client_close(client); | |||
return 0; | |||
} |
@@ -1,186 +0,0 @@ | |||
/* | |||
* session_notify.c -- ultra minimal session manager | |||
* | |||
* Copyright (C) 2018 Karl Linden <karl.j.linden@gmail.com> | |||
* Copyright (C) 2010 Torben Hohn. | |||
* | |||
* 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 <alloca.h> | |||
#include <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/jslist.h> | |||
#include <jack/transport.h> | |||
#include <jack/session.h> | |||
char *package; /* program name */ | |||
jack_client_t *client; | |||
jack_session_event_type_t notify_type; | |||
char *save_path = NULL; | |||
void jack_shutdown(void *arg) | |||
{ | |||
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); | |||
} | |||
void parse_arguments(int argc, char *argv[]) | |||
{ | |||
/* basename $0 */ | |||
package = strrchr(argv[0], '/'); | |||
if (package == 0) | |||
package = argv[0]; | |||
else | |||
package++; | |||
if (argc==2) { | |||
if( !strcmp( argv[1], "quit" ) ) { | |||
notify_type = JackSessionSaveAndQuit; | |||
return; | |||
} | |||
} | |||
if (argc==3) { | |||
if( !strcmp( argv[1], "save" ) ) { | |||
notify_type = JackSessionSave; | |||
save_path = argv[2]; | |||
return; | |||
} | |||
} | |||
fprintf(stderr, "usage: %s quit|save [path]\n", package); | |||
exit(9); | |||
} | |||
typedef struct { | |||
char name[32]; | |||
char uuid[16]; | |||
} uuid_map_t; | |||
JSList *uuid_map = NULL; | |||
void add_uuid_mapping( const char *uuid ) { | |||
char *clientname = jack_get_client_name_by_uuid( client, uuid ); | |||
if( !clientname ) { | |||
printf( "error... can not find client for uuid" ); | |||
return; | |||
} | |||
uuid_map_t *mapping = malloc( sizeof(uuid_map_t) ); | |||
snprintf( mapping->uuid, sizeof(mapping->uuid), "%s", uuid ); | |||
snprintf( mapping->name, sizeof(mapping->name), "%s", clientname ); | |||
uuid_map = jack_slist_append( uuid_map, mapping ); | |||
} | |||
char *map_port_name_to_uuid_port( const char *port_name ) | |||
{ | |||
JSList *node; | |||
char retval[300]; | |||
char *port_component = strchr( port_name,':' ); | |||
char *client_component = strdup( port_name ); | |||
strchr( client_component, ':' )[0] = '\0'; | |||
sprintf( retval, "%s", port_name ); | |||
for( node=uuid_map; node; node=jack_slist_next(node) ) { | |||
uuid_map_t *mapping = node->data; | |||
if( !strcmp( mapping->name, client_component ) ) { | |||
sprintf( retval, "%s%s", mapping->uuid, port_component ); | |||
break; | |||
} | |||
} | |||
return strdup(retval); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
parse_arguments(argc, argv); | |||
jack_session_command_t *retval; | |||
int k,i,j; | |||
/* become a JACK client */ | |||
if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) { | |||
fprintf(stderr, "JACK server not running?\n"); | |||
exit(1); | |||
} | |||
#ifndef WIN32 | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
jack_on_shutdown(client, jack_shutdown, 0); | |||
jack_activate(client); | |||
retval = jack_session_notify( client, NULL, notify_type, save_path ); | |||
for (i = 0; retval[i].uuid; i++) { | |||
printf( "export SESSION_DIR=\"%s%s/\"\n", save_path, retval[i].client_name ); | |||
printf( "%s &\n", retval[i].command ); | |||
add_uuid_mapping(retval[i].uuid); | |||
} | |||
printf( "sleep 10\n" ); | |||
for (k = 0; retval[k].uuid; k++) { | |||
char* port_regexp = alloca( jack_client_name_size()+3 ); | |||
char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid ); | |||
snprintf( port_regexp, jack_client_name_size()+3, "%s:.*", client_name ); | |||
jack_free(client_name); | |||
const char **ports = jack_get_ports( client, port_regexp, NULL, 0 ); | |||
if( !ports ) { | |||
continue; | |||
} | |||
for (i = 0; ports[i]; ++i) { | |||
const char **connections; | |||
if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { | |||
for (j = 0; connections[j]; j++) { | |||
char *src = map_port_name_to_uuid_port( ports[i] ); | |||
char *dst = map_port_name_to_uuid_port( connections[j] ); | |||
printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst ); | |||
} | |||
jack_free (connections); | |||
} | |||
} | |||
jack_free(ports); | |||
} | |||
jack_session_commands_free(retval); | |||
jack_client_close(client); | |||
return 0; | |||
} |
@@ -1,508 +0,0 @@ | |||
/* | |||
* 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 <stdio.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <signal.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#if HAVE_READLINE | |||
#include <readline/readline.h> | |||
#include <readline/history.h> | |||
#endif | |||
#include <jack/jack.h> | |||
#include <jack/transport.h> | |||
/* Use a copy of the readline macro whitespace if it does not exist. | |||
* Not all readline compatible libraries supply the whitespace macro | |||
* (libedit for example), so pull in the copy in those cases too. */ | |||
#if !HAVE_READLINE || !defined(whitespace) | |||
#define whitespace(c) (((c) == ' ') || ((c) == '\t')) | |||
#endif | |||
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 */ | |||
/* JACK timebase callback. | |||
* | |||
* Runs in the process thread. Realtime, must not wait. | |||
*/ | |||
static 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; | |||
} | |||
} | |||
} | |||
} | |||
static 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); | |||
} | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
/* Command functions: see commands[] table following. */ | |||
static void com_activate(char *arg) | |||
{ | |||
if (jack_activate(client)) { | |||
fprintf(stderr, "cannot activate client"); | |||
} | |||
} | |||
static void com_deactivate(char *arg) | |||
{ | |||
if (jack_deactivate(client)) { | |||
fprintf(stderr, "cannot deactivate client"); | |||
} | |||
} | |||
static void com_exit(char *arg) | |||
{ | |||
done = 1; | |||
} | |||
static void com_help(char *); /* forward declaration */ | |||
static void com_locate(char *arg) | |||
{ | |||
jack_nframes_t frame = 0; | |||
if (*arg != '\0') | |||
frame = atoi(arg); | |||
jack_transport_locate(client, frame); | |||
} | |||
static 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"); | |||
} | |||
static void com_play(char *arg) | |||
{ | |||
jack_transport_start(client); | |||
} | |||
static void com_release(char *arg) | |||
{ | |||
jack_release_timebase(client); | |||
} | |||
static void com_stop(char *arg) | |||
{ | |||
jack_transport_stop(client); | |||
} | |||
/* Change the tempo for the entire timeline, not just from the current | |||
* location. */ | |||
static 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. */ | |||
static 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)); | |||
} | |||
/* Toggle between play and stop state */ | |||
static void com_toggle(char *arg) | |||
{ | |||
jack_position_t current; | |||
jack_transport_state_t transport_state; | |||
transport_state = jack_transport_query (client, ¤t); | |||
switch (transport_state) { | |||
case JackTransportStopped: | |||
com_play( arg ); | |||
break; | |||
case JackTransportRolling: | |||
com_stop( arg ); | |||
break; | |||
case JackTransportStarting: | |||
fprintf(stderr, "state: Starting - no transport toggling"); | |||
break; | |||
default: | |||
fprintf(stderr, "unexpected state: no transport toggling"); | |||
} | |||
} | |||
/* 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()"}, | |||
{"exit", com_exit, "Exit transport program"}, | |||
{"deactivate", com_deactivate, "Call jack_deactivate()"}, | |||
{"help", com_help, "Display help text [<command>]"}, | |||
{"locate", com_locate, "Locate to frame <position>"}, | |||
{"master", com_master, "Become timebase master " | |||
"[<conditionally>]"}, | |||
{"play", com_play, "Start transport rolling"}, | |||
{"quit", com_exit, "Synonym for `exit'"}, | |||
{"release", com_release, "Release timebase"}, | |||
{"stop", com_stop, "Stop transport"}, | |||
{"tempo", com_tempo, "Set beat tempo <beats_per_min>"}, | |||
{"timeout", com_timeout, "Set sync timeout in <seconds>"}, | |||
{"toggle", com_toggle, "Toggle transport rolling"}, | |||
{"?", com_help, "Synonym for `help'" }, | |||
{(char *)NULL, (cmd_function_t *)NULL, (char *)NULL } | |||
}; | |||
static command_t *find_command(char *name) | |||
{ | |||
register int i; | |||
size_t namelen; | |||
if ((name == NULL) || (*name == '\0')) | |||
return ((command_t *)NULL); | |||
namelen = strlen(name); | |||
for (i = 0; commands[i].name; i++) | |||
if (strncmp(name, commands[i].name, namelen) == 0) { | |||
/* make sure the match is unique */ | |||
if ((commands[i+1].name) && | |||
(strncmp(name, commands[i+1].name, namelen) == 0)) | |||
return ((command_t *)NULL); | |||
else | |||
return (&commands[i]); | |||
} | |||
return ((command_t *)NULL); | |||
} | |||
static void com_help(char *arg) | |||
{ | |||
register int i; | |||
command_t *cmd; | |||
if (!*arg) { | |||
/* print help for all commands */ | |||
for (i = 0; commands[i].name; i++) { | |||
printf("%s\t\t%s.\n", commands[i].name, | |||
commands[i].doc); | |||
} | |||
} else if ((cmd = find_command(arg))) { | |||
printf("%s\t\t%s.\n", cmd->name, cmd->doc); | |||
} else { | |||
int printed = 0; | |||
printf("No `%s' command. Valid command names are:\n", arg); | |||
for (i = 0; commands[i].name; i++) { | |||
/* Print in six columns. */ | |||
if (printed == 6) { | |||
printed = 0; | |||
printf ("\n"); | |||
} | |||
printf ("%s\t", commands[i].name); | |||
printed++; | |||
} | |||
printf("\n\nTry `help [command]\' for more information.\n"); | |||
} | |||
} | |||
static void execute_command(char *line) | |||
{ | |||
register int i; | |||
command_t *command; | |||
char *word; | |||
/* Isolate the command word. */ | |||
i = 0; | |||
while (line[i] && whitespace(line[i])) | |||
i++; | |||
word = line + i; | |||
while (line[i] && !whitespace(line[i])) | |||
i++; | |||
if (line[i]) | |||
line[i++] = '\0'; | |||
command = find_command(word); | |||
if (!command) { | |||
fprintf(stderr, "%s: No such command. There is `help\'.\n", | |||
word); | |||
return; | |||
} | |||
/* Get argument to command, if any. */ | |||
while (whitespace(line[i])) | |||
i++; | |||
word = line + i; | |||
/* invoke the command function. */ | |||
(*command->func)(word); | |||
} | |||
/* Strip whitespace from the start and end of string. */ | |||
static char *stripwhite(char *string) | |||
{ | |||
register char *s, *t; | |||
s = string; | |||
while (whitespace(*s)) | |||
s++; | |||
if (*s == '\0') | |||
return s; | |||
t = s + strlen (s) - 1; | |||
while (t > s && whitespace(*t)) | |||
t--; | |||
*++t = '\0'; | |||
return s; | |||
} | |||
static char *dupstr(char *s) | |||
{ | |||
char *r = malloc(strlen(s) + 1); | |||
strcpy(r, s); | |||
return r; | |||
} | |||
/* Readline generator function for command completion. */ | |||
static char *command_generator (const char *text, int state) | |||
{ | |||
static int list_index, len; | |||
char *name; | |||
/* If this is a new word to complete, initialize now. This | |||
includes saving the length of TEXT for efficiency, and | |||
initializing the index variable to 0. */ | |||
if (!state) { | |||
list_index = 0; | |||
len = strlen (text); | |||
} | |||
/* Return the next name which partially matches from the | |||
command list. */ | |||
while ((name = commands[list_index].name)) { | |||
list_index++; | |||
if (strncmp(name, text, len) == 0) | |||
return dupstr(name); | |||
} | |||
return (char *) NULL; /* No names matched. */ | |||
} | |||
static void command_loop() | |||
{ | |||
#if HAVE_READLINE | |||
char *line, *cmd; | |||
char prompt[32]; | |||
snprintf(prompt, sizeof(prompt), "%s> ", package); | |||
/* Allow conditional parsing of the ~/.inputrc file. */ | |||
rl_readline_name = package; | |||
/* Define a custom completion function. */ | |||
rl_completion_entry_function = command_generator; | |||
#else | |||
char line[64] = {0,}; | |||
char *cmd = NULL; | |||
#endif | |||
/* Read and execute commands until the user quits. */ | |||
while (!done) { | |||
#if HAVE_READLINE | |||
line = readline(prompt); | |||
if (line == NULL) { /* EOF? */ | |||
printf("\n"); /* close out prompt */ | |||
done = 1; | |||
break; | |||
} | |||
#else | |||
printf("%s> ", package); | |||
fgets(line, sizeof(line), stdin); | |||
line[strlen(line)-1] = '\0'; | |||
#endif | |||
/* Remove leading and trailing whitespace from the line. */ | |||
cmd = stripwhite(line); | |||
/* If anything left, add to history and execute it. */ | |||
if (*cmd) | |||
{ | |||
#if HAVE_READLINE | |||
add_history(cmd); | |||
#endif | |||
execute_command(cmd); | |||
} | |||
#if HAVE_READLINE | |||
free(line); /* realine() called malloc() */ | |||
#endif | |||
} | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
jack_status_t status; | |||
/* basename $0 */ | |||
package = strrchr(argv[0], '/'); | |||
if (package == 0) | |||
package = argv[0]; | |||
else | |||
package++; | |||
/* open a connection to the JACK server */ | |||
client = jack_client_open (package, JackNullOption, &status); | |||
if (client == NULL) { | |||
fprintf (stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
return 1; | |||
} | |||
#if !WIN32 | |||
signal(SIGQUIT, signal_handler); | |||
signal(SIGHUP, signal_handler); | |||
#endif | |||
signal(SIGTERM, signal_handler); | |||
signal(SIGINT, signal_handler); | |||
jack_on_shutdown(client, jack_shutdown, 0); | |||
if (jack_activate(client)) { | |||
fprintf(stderr, "cannot activate client"); | |||
return 1; | |||
} | |||
/* execute commands until done */ | |||
command_loop(); | |||
jack_client_close(client); | |||
exit(0); | |||
} |
@@ -1,264 +0,0 @@ | |||
/** @file tw.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 <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <signal.h> | |||
#include <jack/jack.h> | |||
jack_port_t *input_port; | |||
jack_port_t *output_port; | |||
jack_client_t *client; | |||
/* a simple state machine for this client */ | |||
volatile enum { | |||
Init, | |||
Run, | |||
Exit | |||
} client_state = Init; | |||
static void signal_handler(int sig) | |||
{ | |||
jack_client_close(client); | |||
fprintf(stderr, "signal received, exiting ...\n"); | |||
exit(0); | |||
} | |||
/** | |||
* The process callback for this JACK application is called in a | |||
* special realtime thread once for each audio cycle. | |||
* | |||
* This client follows a simple rule: when the JACK transport is | |||
* running, copy the input port to the output. When it stops, exit. | |||
*/ | |||
static int | |||
_process (jack_nframes_t nframes) | |||
{ | |||
jack_default_audio_sample_t *in, *out; | |||
jack_transport_state_t ts = jack_transport_query(client, NULL); | |||
if (ts == JackTransportRolling) { | |||
if (client_state == Init) | |||
client_state = Run; | |||
in = jack_port_get_buffer (input_port, nframes); | |||
out = jack_port_get_buffer (output_port, nframes); | |||
memcpy (out, in, | |||
sizeof (jack_default_audio_sample_t) * nframes); | |||
} else if (ts == JackTransportStopped) { | |||
if (client_state == Run) { | |||
client_state = Exit; | |||
return -1; // to stop the thread | |||
} | |||
} | |||
return 0; | |||
} | |||
static void* jack_thread(void *arg) | |||
{ | |||
jack_client_t* client = (jack_client_t*) arg; | |||
while (1) { | |||
jack_nframes_t frames = jack_cycle_wait (client); | |||
int status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
/* | |||
Possibly do something else after signaling next clients in the graph | |||
*/ | |||
/* End condition */ | |||
if (status != 0) | |||
return 0; | |||
} | |||
/* not reached*/ | |||
return 0; | |||
} | |||
/* | |||
static void* jack_thread(void *arg) | |||
{ | |||
jack_client_t* client = (jack_client_t*) arg; | |||
while (1) { | |||
jack_nframes_t frames; | |||
int status; | |||
// cycle 1 | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
// cycle 2 | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
// cycle 3 | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
// cycle 4 | |||
frames = jack_cycle_wait (client); | |||
status = _process(frames); | |||
jack_cycle_signal (client, status); | |||
} | |||
return 0; | |||
} | |||
*/ | |||
/** | |||
* JACK calls this shutdown_callback if the server ever shuts down or | |||
* decides to disconnect the client. | |||
*/ | |||
static 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; | |||
const char *server_name = NULL; | |||
jack_options_t options = JackNullOption; | |||
jack_status_t status; | |||
if (argc >= 2) { /* client name specified? */ | |||
client_name = argv[1]; | |||
if (argc >= 3) { /* server name specified? */ | |||
server_name = argv[2]; | |||
options |= JackServerName; | |||
} | |||
} else { /* use basename of argv[0] */ | |||
client_name = strrchr(argv[0], '/'); | |||
if (client_name == 0) { | |||
client_name = argv[0]; | |||
} else { | |||
client_name++; | |||
} | |||
} | |||
/* 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. | |||
*/ | |||
if (jack_set_process_thread(client, jack_thread, client) < 0) | |||
exit(1); | |||
/* 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"); | |||
} | |||
jack_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"); | |||
} | |||
jack_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); | |||
/* keep running until the transport stops */ | |||
while (client_state != Exit) { | |||
sleep (1); | |||
} | |||
jack_client_close (client); | |||
exit (0); | |||
} |
@@ -1,152 +0,0 @@ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <getopt.h> | |||
#include <time.h> | |||
#include <jack/jack.h> | |||
char * my_name; | |||
void | |||
show_usage(void) | |||
{ | |||
fprintf(stderr, "\nUsage: %s [options]\n", my_name); | |||
fprintf(stderr, "Check for jack existence, or wait, until it either quits, or gets started\n"); | |||
fprintf(stderr, "options:\n"); | |||
fprintf(stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
fprintf(stderr, " -n, --name <name> Set client name to <name>\n"); | |||
fprintf(stderr, " -w, --wait Wait for server to become available\n"); | |||
fprintf(stderr, " -q, --quit Wait until server is quit\n"); | |||
fprintf(stderr, " -c, --check Check whether server is running\n"); | |||
fprintf(stderr, " -t, --timeout Wait timeout in seconds\n"); | |||
fprintf(stderr, " -h, --help Display this help message\n"); | |||
fprintf(stderr, "For more information see http://jackaudio.org/\n"); | |||
} | |||
int | |||
main(int argc, char *argv[]) | |||
{ | |||
jack_client_t *client; | |||
jack_status_t status; | |||
jack_options_t options = JackNoStartServer; | |||
int c; | |||
int option_index; | |||
char *server_name = NULL; | |||
char *client_name = NULL; | |||
int wait_for_start = 0; | |||
int wait_for_quit = 0; | |||
int just_check = 0; | |||
int wait_timeout = 0; | |||
time_t start_timestamp; | |||
struct option long_options[] = { | |||
{ "server", 1, 0, 's' }, | |||
{ "wait", 0, 0, 'w' }, | |||
{ "name", 1, 0, 'n'}, | |||
{ "quit", 0, 0, 'q' }, | |||
{ "check", 0, 0, 'c' }, | |||
{ "timeout", 1, 0, 't' }, | |||
{ "help", 0, 0, 'h' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
my_name = strrchr(argv[0], '/'); | |||
if (my_name == 0) { | |||
my_name = argv[0]; | |||
} else { | |||
my_name ++; | |||
} | |||
while ((c = getopt_long (argc, argv, "s:n:wqct:hv", long_options, &option_index)) >= 0) { | |||
switch (c) { | |||
case 's': | |||
server_name = (char *) malloc (sizeof (char) * (strlen(optarg) + 1)); | |||
strcpy (server_name, optarg); | |||
options |= JackServerName; | |||
break; | |||
case 'n': | |||
client_name = (char *) malloc (sizeof (char) * (strlen(optarg) + 1)); | |||
strcpy (client_name, optarg); | |||
break; | |||
case 'w': | |||
wait_for_start = 1; | |||
break; | |||
case 'q': | |||
wait_for_quit = 1; | |||
break; | |||
case 'c': | |||
just_check = 1; | |||
break; | |||
case 't': | |||
wait_timeout = atoi(optarg); | |||
break; | |||
case 'h': | |||
show_usage(); | |||
return 1; | |||
break; | |||
default: | |||
show_usage(); | |||
return 1; | |||
break; | |||
} | |||
} | |||
/* try to open server in a loop. breaking under certein conditions */ | |||
start_timestamp = time(NULL); | |||
while (1) { | |||
if (client_name) { | |||
client = jack_client_open (client_name, options, &status, server_name); | |||
} | |||
else { | |||
client = jack_client_open ("wait", options, &status, server_name); | |||
} | |||
/* check for some real error and bail out */ | |||
if ((client == NULL) && !(status & JackServerFailed)) { | |||
fprintf (stderr, "jack_client_open() failed, " | |||
"status = 0x%2.0x\n", status); | |||
return 1; | |||
} | |||
if (client == NULL) { | |||
if (wait_for_quit) { | |||
fprintf(stdout, "server is gone\n"); | |||
break; | |||
} | |||
if (just_check) { | |||
fprintf(stdout, "not running\n"); | |||
break; | |||
} | |||
} else { | |||
jack_client_close(client); | |||
if (wait_for_start) { | |||
fprintf(stdout, "server is available\n"); | |||
break; | |||
} | |||
if (just_check) { | |||
fprintf(stdout, "running\n"); | |||
break; | |||
} | |||
} | |||
if (wait_timeout) { | |||
if ((time(NULL) - start_timestamp) > wait_timeout) { | |||
fprintf(stdout, "timeout\n"); | |||
exit(EXIT_FAILURE); | |||
} | |||
} | |||
// Wait a second, and repeat | |||
#ifdef WIN32 | |||
Sleep(1*1000); | |||
#else | |||
sleep(1); | |||
#endif | |||
} | |||
exit(0); | |||
} |
@@ -1,130 +0,0 @@ | |||
#! /usr/bin/python3 | |||
# encoding: utf-8 | |||
example_tools = { | |||
'jack_alias': 'alias.c', | |||
'jack_bufsize': 'bufsize.c', | |||
'jack_connect': 'connect.c', | |||
'jack_evmon': 'evmon.c', | |||
'jack_freewheel': 'freewheel.c', | |||
'jack_load': 'ipload.c', | |||
'jack_lsp': 'lsp.c', | |||
'jack_midi_dump': 'midi_dump.c', | |||
'jack_monitor_client': 'monitor_client.c', | |||
'jack_property': 'property.c', | |||
'jack_samplerate': 'samplerate.c', | |||
'jack_session_notify': 'session_notify.c', | |||
'jack_unload': 'ipunload.c', | |||
'jack_wait': 'wait.c', | |||
} | |||
def configure(conf): | |||
conf.env['BUILD_TOOL_ALSA_IO'] = conf.env['SAMPLERATE'] and conf.env['BUILD_DRIVER_ALSA'] | |||
conf.env['BUILD_TOOL_CLIENT_TRANSPORT'] = conf.env['READLINE'] | |||
conf.env['BUILD_TOOL_CLIENT_NETSOURCE'] = conf.env['CELT'] or conf.env['OPUS'] | |||
conf.env['BUILD_TOOL_ZALSA'] = conf.env['ZALSA'] | |||
def build(bld): | |||
if bld.env['IS_LINUX']: | |||
os_incdir = ['../linux', '../posix'] | |||
if bld.env['IS_MACOSX']: | |||
os_incdir = ['../macosx', '../posix'] | |||
if bld.env['IS_FREEBSD']: | |||
os_incdir = ['../freebsd', '../posix'] | |||
if bld.env['IS_SUN']: | |||
os_incdir = ['../solaris', '../posix'] | |||
if bld.env['IS_WINDOWS']: | |||
os_incdir = ['../windows'] | |||
for example_tool, example_tool_source in list(example_tools.items()): | |||
if bld.env['IS_MACOSX']: | |||
prog = bld(features='c cprogram', framework=['Foundation']) | |||
else: | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = example_tool_source | |||
prog.use = ['clientlib'] | |||
if bld.env['IS_LINUX']: | |||
prog.use += ['RT', 'M'] | |||
if bld.env['IS_SUN']: | |||
prog.use += ['M'] | |||
if bld.env['IS_WINDOWS'] and bld.env['BUILD_STATIC']: | |||
prog.env['LIB_PTHREAD'] = [':libwinpthread.a'] | |||
# prog.cflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] | |||
# prog.cxxflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] | |||
prog.target = example_tool | |||
if bld.env['BUILD_TOOL_CLIENT_TRANSPORT']: | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = 'transport.c' | |||
prog.use = ['clientlib'] | |||
if bld.env['IS_LINUX']: | |||
prog.use += ['RT', 'READLINE'] | |||
if bld.env['IS_MACOSX']: | |||
prog.use += ['READLINE'] | |||
if bld.env['IS_WINDOWS']: | |||
prog.use += ['READLINE'] | |||
if bld.env['BUILD_STATIC']: | |||
prog.env['LIB_PTHREAD'] = [':libwinpthread.a'] | |||
prog.target = 'jack_transport' | |||
if bld.env['BUILD_TOOL_CLIENT_NETSOURCE']: | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] | |||
prog.source = ['netsource.c', '../common/netjack_packet.c'] | |||
prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') | |||
prog.use = ['CELT', 'SAMPLERATE', 'OPUS', 'M', 'clientlib'] | |||
prog.target = 'jack_netsource' | |||
prog.defines = ['HAVE_CONFIG_H'] | |||
if bld.env['IS_LINUX'] and bld.env['BUILD_TOOL_ALSA_IO']: | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = ['alsa_in.c', '../common/memops.c'] | |||
prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') | |||
prog.use = ['clientlib', 'ALSA', 'SAMPLERATE', 'M'] | |||
prog.target = 'alsa_in' | |||
prog = bld(features='c cprogram') | |||
prog.includes = os_incdir + ['../common/jack', '../common'] | |||
prog.source = ['alsa_out.c', '../common/memops.c'] | |||
prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') | |||
prog.use = ['clientlib', 'ALSA', 'SAMPLERATE', 'M'] | |||
prog.target = 'alsa_out' | |||
if bld.env['IS_LINUX'] and bld.env['BUILD_TOOL_ZALSA']: | |||
prog = bld(features=['cxx', 'cxxshlib']) | |||
prog.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE', 'APPNAME="zalsa_in"', 'VERSION="0.4.0"'] | |||
prog.install_path = '${ADDON_DIR}/' | |||
prog.includes = os_incdir + ['../common/jack', '../common', 'zalsa'] | |||
prog.source = [ | |||
'zalsa/zita-a2j.cc', | |||
'zalsa/alsathread.cc', | |||
'zalsa/jackclient.cc', | |||
'zalsa/pxthread.cc', | |||
'zalsa/lfqueue.cc', | |||
] | |||
prog.target = 'zalsa_in' | |||
prog.use = ['ZITA-ALSA-PCMI', 'ZITA-RESAMPLER', 'ALSA', 'M', 'RT', 'serverlib'] | |||
prog.env['cxxshlib_PATTERN'] = '%s.so' | |||
prog = bld(features=['cxx', 'cxxshlib']) | |||
prog.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE', 'APPNAME="zalsa_out"', 'VERSION="0.4.0"'] | |||
prog.install_path = '${ADDON_DIR}/' | |||
prog.includes = os_incdir + ['../common/jack', '../common', 'zalsa'] | |||
prog.source = [ | |||
'zalsa/zita-j2a.cc', | |||
'zalsa/alsathread.cc', | |||
'zalsa/jackclient.cc', | |||
'zalsa/pxthread.cc', | |||
'zalsa/lfqueue.cc', | |||
] | |||
prog.target = 'zalsa_out' | |||
prog.use = ['ZITA-ALSA-PCMI', 'ZITA-RESAMPLER', 'ALSA', 'M', 'RT', 'serverlib'] | |||
prog.env['cxxshlib_PATTERN'] = '%s.so' | |||
if not bld.env['IS_WINDOWS']: | |||
bld.symlink_as('${PREFIX}/bin/jack_disconnect', 'jack_connect') |
@@ -1,226 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include <math.h> | |||
#include "alsathread.h" | |||
#include "timers.h" | |||
Alsathread::Alsathread (Alsa_pcmi *alsadev, int mode) : | |||
_alsadev (alsadev ), | |||
_mode (mode), | |||
_state (INIT), | |||
_fsize (alsadev->fsize ()), | |||
_audioq (0), | |||
_commq (0), | |||
_alsaq (0) | |||
{ | |||
// Compute DLL filter coefficients. | |||
_dt = (double) _fsize / _alsadev->fsamp (); | |||
_w1 = 2 * M_PI * 0.1 * _dt; | |||
_w2 = _w1 * _w1; | |||
_w1 *= 1.6; | |||
} | |||
Alsathread::~Alsathread (void) | |||
{ | |||
if (_state != INIT) | |||
{ | |||
_state = TERM; | |||
thr_wait (); | |||
} | |||
else | |||
{ | |||
_alsadev->pcm_stop (); | |||
} | |||
} | |||
int Alsathread::start (Lfq_audio *audioq, Lfq_int32 *commq, Lfq_adata *alsaq, int rtprio) | |||
{ | |||
// Start the ALSA thread. | |||
_audioq = audioq; | |||
_commq = commq; | |||
_alsaq = alsaq; | |||
_state = WAIT; | |||
if (thr_start (SCHED_FIFO, rtprio, 0x10000)) return 1; | |||
return 0; | |||
} | |||
void Alsathread::send (int k, double t) | |||
{ | |||
Adata *D; | |||
// Send (state, frame count, timestamp) to Jack thread. | |||
if (_alsaq->wr_avail ()) | |||
{ | |||
D = _alsaq->wr_datap (); | |||
D->_state = _state; | |||
D->_nsamp = k; | |||
D->_timer = t; | |||
_alsaq->wr_commit (); | |||
} | |||
} | |||
// The following two functions transfer data between the audio queue | |||
// and the ALSA device. Note that we do *not* check the queue's fill | |||
// state, and it may overrun or underrun. It actually will in the first | |||
// few iterations and in error conditions. This is entirely intentional. | |||
// The queue keeps correct read and write counters even in that case, | |||
// and the main control loop and error recovery depend on it working | |||
// and being used in this way. | |||
int Alsathread::capture (void) | |||
{ | |||
int c, n, k; | |||
float *p; | |||
// Start reading from ALSA device. | |||
_alsadev->capt_init (_fsize); | |||
if (_state == PROC) | |||
{ | |||
// Input frames from the ALSA device to the audio queue. | |||
// The outer loop takes care of wraparound. | |||
for (n = _fsize; n; n -= k) | |||
{ | |||
p = _audioq->wr_datap (); // Audio queue write pointer. | |||
k = _audioq->wr_linav (); // Number of frames that can be | |||
if (k > n) k = n; // written without wraparound. | |||
for (c = 0; c < _audioq->nchan (); c++) | |||
{ | |||
// Copy and interleave one channel. | |||
_alsadev->capt_chan (c, p + c, k, _audioq->nchan ()); | |||
} | |||
_audioq->wr_commit (k); // Update audio queue state. | |||
} | |||
} | |||
// Finish reading from ALSA device. | |||
_alsadev->capt_done (_fsize); | |||
return _fsize; | |||
} | |||
int Alsathread::playback (void) | |||
{ | |||
int c, n, k; | |||
float *p; | |||
// Start writing to ALSA device. | |||
_alsadev->play_init (_fsize); | |||
c = 0; | |||
if (_state == PROC) | |||
{ | |||
// Output frames from the audio queue to the ALSA device. | |||
// The outer loop takes care of wraparound. | |||
for (n = _fsize; n; n -= k) | |||
{ | |||
p = _audioq->rd_datap (); // Audio queue read pointer. | |||
k = _audioq->rd_linav (); // Number of frames that can | |||
if (k > n) k = n; // be read without wraparound. | |||
for (c = 0; c < _audioq->nchan (); c++) | |||
{ | |||
// De-interleave and copy one channel. | |||
_alsadev->play_chan (c, p + c, k, _audioq->nchan ()); | |||
} | |||
_audioq->rd_commit (k); // Update audio queue state. | |||
} | |||
} | |||
// Clear all or remaining channels. | |||
while (c < _alsadev->nplay ()) _alsadev->clear_chan (c++, _fsize); | |||
// Finish writing to ALSA device. | |||
_alsadev->play_done (_fsize); | |||
return _fsize; | |||
} | |||
void Alsathread::thr_main (void) | |||
{ | |||
int na, nu; | |||
double tw, er; | |||
_alsadev->pcm_start (); | |||
while (_state != TERM) | |||
{ | |||
// Wait for next cycle, then take timestamp. | |||
na = _alsadev->pcm_wait (); | |||
tw = tjack (jack_get_time ()); | |||
// Check for errors - requires restart. | |||
if (_alsadev->state () && (na == 0)) | |||
{ | |||
_state = WAIT; | |||
send (0, 0); | |||
usleep (10000); | |||
continue; | |||
} | |||
// Check for commands from the Jack thread. | |||
if (_commq->rd_avail ()) | |||
{ | |||
_state = _commq->rd_int32 (); | |||
if (_state == PROC) _first = true; | |||
if (_state == TERM) send (0, 0); | |||
} | |||
// We could have more than one period. | |||
nu = 0; | |||
while (na >= _fsize) | |||
{ | |||
// Transfer frames. | |||
if (_mode == PLAY) nu += playback (); | |||
else nu += capture (); | |||
// Update loop condition. | |||
na -= _fsize; | |||
// Run the DLL if in PROC state. | |||
if (_state == PROC) | |||
{ | |||
if (_first) | |||
{ | |||
// Init DLL in first iteration. | |||
_first = false; | |||
_dt = (double) _fsize / _alsadev->fsamp (); | |||
_t0 = tw; | |||
_t1 = tw + _dt; | |||
} | |||
else | |||
{ | |||
// Update the DLL. | |||
// If we have more than one period, use | |||
// the time error only for the last one. | |||
if (na >= _fsize) er = 0; | |||
else er = tjack_diff (tw, _t1); | |||
_t0 = _t1; | |||
_t1 = tjack_diff (_t1 + _dt + _w1 * er, 0.0); | |||
_dt += _w2 * er; | |||
} | |||
} | |||
} | |||
// Send number of frames used and timestamp to Jack thread. | |||
if (_state == PROC) send (nu, _t1); | |||
} | |||
_alsadev->pcm_stop (); | |||
} |
@@ -1,68 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#ifndef __ALSATHREAD_H | |||
#define __ALSATHREAD_H | |||
#include <zita-alsa-pcmi.h> | |||
#include "jack/jack.h" | |||
#include "pxthread.h" | |||
#include "lfqueue.h" | |||
class Alsathread : public Pxthread | |||
{ | |||
public: | |||
enum { INIT, WAIT, PROC, TERM }; | |||
enum { PLAY, CAPT }; | |||
Alsathread (Alsa_pcmi *alsadev, int mode); | |||
virtual ~Alsathread (void); | |||
virtual void thr_main (void); | |||
int start (Lfq_audio *audioq, Lfq_int32 *commq, Lfq_adata *alsaq, int rtprio); | |||
private: | |||
void send (int k, double t); | |||
int capture (void); | |||
int playback (void); | |||
Alsa_pcmi *_alsadev; | |||
int _mode; | |||
int _state; | |||
int _nfail; | |||
int _fsize; | |||
Lfq_audio *_audioq; | |||
Lfq_int32 *_commq; | |||
Lfq_adata *_alsaq; | |||
bool _first; | |||
// double _jtmod; | |||
double _t0; | |||
double _t1; | |||
double _dt; | |||
double _w1; | |||
double _w2; | |||
}; | |||
#endif |
@@ -1,549 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include <stdio.h> | |||
#include <math.h> | |||
#include "jackclient.h" | |||
#include "alsathread.h" | |||
#include "timers.h" | |||
Jackclient::Jackclient (jack_client_t* cl, const char *jserv, int mode, int nchan, bool sync, void *arg) : | |||
_client (cl), | |||
_arg (arg), | |||
_mode (mode), | |||
_nchan (nchan), | |||
_state (INIT), | |||
_freew (false), | |||
_resamp (0) | |||
{ | |||
init (jserv); | |||
if (!sync) _resamp = new VResampler (); | |||
} | |||
Jackclient::~Jackclient (void) | |||
{ | |||
fini (); | |||
} | |||
bool Jackclient::init (const char *jserv) | |||
{ | |||
int i, spol, flags; | |||
char s [64]; | |||
struct sched_param spar; | |||
if (_client == 0) | |||
{ | |||
fprintf (stderr, "Can't connect to Jack, is the server running ?\n"); | |||
return false; | |||
} | |||
jack_set_process_callback (_client, jack_static_process, (void *) this); | |||
jack_set_latency_callback (_client, jack_static_latency, (void *) this); | |||
jack_set_freewheel_callback (_client, jack_static_freewheel, (void *) this); | |||
jack_set_buffer_size_callback (_client, jack_static_buffsize, (void *) this); | |||
jack_on_shutdown (_client, jack_static_shutdown, (void *) this); | |||
_bsize = 0; | |||
_fsamp = 0; | |||
if (jack_activate (_client)) | |||
{ | |||
fprintf(stderr, "Can't activate Jack"); | |||
return false; | |||
} | |||
_jname = jack_get_client_name (_client); | |||
_bsize = jack_get_buffer_size (_client); | |||
_fsamp = jack_get_sample_rate (_client); | |||
flags = JackPortIsTerminal | JackPortIsPhysical; | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
if (_mode == PLAY) | |||
{ | |||
sprintf (s, "playback_%d", i + 1); | |||
_ports [i] = jack_port_register (_client, s, JACK_DEFAULT_AUDIO_TYPE, | |||
flags | JackPortIsInput, 0); | |||
} | |||
else | |||
{ | |||
sprintf (s, "capture_%d", i + 1); | |||
_ports [i] = jack_port_register (_client, s, JACK_DEFAULT_AUDIO_TYPE, | |||
flags | JackPortIsOutput, 0); | |||
} | |||
} | |||
pthread_getschedparam (jack_client_thread_id (_client), &spol, &spar); | |||
_rprio = spar.sched_priority - sched_get_priority_max (spol); | |||
_buff = new float [_bsize * _nchan]; | |||
return true; | |||
} | |||
void Jackclient::fini (void) | |||
{ | |||
delete[] _buff; | |||
delete _resamp; | |||
} | |||
void Jackclient::jack_static_shutdown (void *arg) | |||
{ | |||
((Jackclient *) arg)->sendinfo (TERM, 0, 0); | |||
} | |||
int Jackclient::jack_static_buffsize (jack_nframes_t nframes, void *arg) | |||
{ | |||
Jackclient *J = (Jackclient *) arg; | |||
if (J->_bsize == 0) J->_bsize = nframes; | |||
else if (J->_bsize != (int) nframes) J->_state = Jackclient::TERM; | |||
return 0; | |||
} | |||
void Jackclient::jack_static_freewheel (int state, void *arg) | |||
{ | |||
((Jackclient *) arg)->jack_freewheel (state); | |||
} | |||
void Jackclient::jack_static_latency (jack_latency_callback_mode_t jlcm, void *arg) | |||
{ | |||
((Jackclient *) arg)->jack_latency (jlcm); | |||
} | |||
int Jackclient::jack_static_process (jack_nframes_t nframes, void *arg) | |||
{ | |||
return ((Jackclient *) arg)->jack_process (nframes); | |||
} | |||
void Jackclient::start (Lfq_audio *audioq, | |||
Lfq_int32 *commq, | |||
Lfq_adata *alsaq, | |||
Lfq_jdata *infoq, | |||
double ratio, | |||
int delay, | |||
int ltcor, | |||
int rqual) | |||
{ | |||
double d; | |||
_audioq = audioq; | |||
_commq = commq; | |||
_alsaq = alsaq; | |||
_infoq = infoq; | |||
_ratio = ratio; | |||
_delay = delay; | |||
_rcorr = 1.0; | |||
if (_resamp) | |||
{ | |||
_resamp->setup (_ratio, _nchan, rqual); | |||
_resamp->set_rrfilt (100); | |||
d = _resamp->inpsize () / 2.0; | |||
if (_mode == PLAY) d *= _ratio; | |||
_delay += d; | |||
} | |||
_ltcor = ltcor; | |||
_ppsec = (_fsamp + _bsize / 2) / _bsize; | |||
initwait (_ppsec / 2); | |||
jack_recompute_total_latencies (_client); | |||
} | |||
void Jackclient::initwait (int nwait) | |||
{ | |||
_count = -nwait; | |||
_commq->wr_int32 (Alsathread::WAIT); | |||
_state = WAIT; | |||
if (nwait > _ppsec) sendinfo (WAIT, 0, 0); | |||
} | |||
void Jackclient::initsync (void) | |||
{ | |||
// Reset all lock-free queues. | |||
_commq->reset (); | |||
_alsaq->reset (); | |||
_audioq->reset (); | |||
if (_resamp) | |||
{ | |||
// Reset and prefill the resampler. | |||
_resamp->reset (); | |||
_resamp->inp_count = _resamp->inpsize () / 2 - 1; | |||
_resamp->out_count = 99999; | |||
_resamp->process (); | |||
} | |||
// Initialise state variables. | |||
_t_a0 = _t_a1 = 0; | |||
_k_a0 = _k_a1 = 0; | |||
// Initialise loop filter state. | |||
_z1 = _z2 = _z3 = 0; | |||
// Activate the ALSA thread, | |||
_commq->wr_int32 (Alsathread::PROC); | |||
_state = SYNC0; | |||
sendinfo (SYNC0, 0, 0); | |||
} | |||
void Jackclient::setloop (double bw) | |||
{ | |||
double w; | |||
// Set the loop bandwidth to bw Hz. | |||
w = 6.28 * bw * _bsize / _fsamp; | |||
_w0 = 1.0 - exp (-20.0 * w); | |||
_w1 = w * 2 / _bsize; | |||
_w2 = w / 2; | |||
if (_mode == PLAY) _w1 /= _ratio; | |||
else _w1 *= _ratio; | |||
} | |||
void Jackclient::playback (int nframes) | |||
{ | |||
int i, j, n; | |||
float *p, *q; | |||
float *inp [MAXCHAN]; | |||
_bstat = _audioq->rd_avail (); | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
inp [i] = (float *)(jack_port_get_buffer (_ports [i], nframes)); | |||
} | |||
if (_resamp) | |||
{ | |||
// Interleave inputs into _buff. | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
p = inp [i]; | |||
q = _buff + i; | |||
for (j = 0; j < _bsize; j++) q [j * _nchan] = p [j]; | |||
} | |||
// Resample _buff and write to audio queue. | |||
// The while loop takes care of wraparound. | |||
_resamp->inp_count = _bsize; | |||
_resamp->inp_data = _buff; | |||
while (_resamp->inp_count) | |||
{ | |||
_resamp->out_count = _audioq->wr_linav (); | |||
_resamp->out_data = _audioq->wr_datap (); | |||
n = _resamp->out_count; | |||
_resamp->process (); | |||
n -= _resamp->out_count; | |||
_audioq->wr_commit (n); | |||
} | |||
} | |||
else | |||
{ | |||
// Interleave inputs into audio queue. | |||
// The while loop takes care of wraparound. | |||
while (nframes) | |||
{ | |||
q = _audioq->wr_datap (); | |||
n = _audioq->wr_linav (); | |||
if (n > nframes) n = nframes; | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
p = inp [i]; | |||
for (j = 0; j < n; j++) q [j * _nchan] = p [j]; | |||
inp [i] += n; | |||
q += 1; | |||
} | |||
_audioq->wr_commit (n); | |||
nframes -= n; | |||
} | |||
} | |||
} | |||
void Jackclient::capture (int nframes) | |||
{ | |||
int i, j, n; | |||
float *p, *q; | |||
float *out [MAXCHAN]; | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
out [i] = (float *)(jack_port_get_buffer (_ports [i], nframes)); | |||
} | |||
if (_resamp) | |||
{ | |||
// Read from audio queue and resample. | |||
// The while loop takes care of wraparound. | |||
_resamp->out_count = _bsize; | |||
_resamp->out_data = _buff; | |||
while (_resamp->out_count) | |||
{ | |||
_resamp->inp_count = _audioq->rd_linav (); | |||
_resamp->inp_data = _audioq->rd_datap (); | |||
n = _resamp->inp_count; | |||
_resamp->process (); | |||
n -= _resamp->inp_count; | |||
_audioq->rd_commit (n); | |||
} | |||
// Deinterleave _buff to outputs. | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
p = _buff + i; | |||
q = out [i]; | |||
for (j = 0; j < _bsize; j++) q [j] = p [j * _nchan]; | |||
} | |||
} | |||
else | |||
{ | |||
// Deinterleave audio queue to outputs. | |||
// The while loop takes care of wraparound. | |||
while (nframes) | |||
{ | |||
p = _audioq->rd_datap (); | |||
n = _audioq->rd_linav (); | |||
if (n > nframes) n = nframes; | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
q = out [i]; | |||
for (j = 0; j < n; j++) q [j] = p [j * _nchan]; | |||
out [i] += n; | |||
p += 1; | |||
} | |||
_audioq->rd_commit (n); | |||
nframes -= n; | |||
} | |||
} | |||
_bstat = _audioq->rd_avail (); | |||
} | |||
void Jackclient::silence (int nframes) | |||
{ | |||
int i; | |||
float *q; | |||
// Write silence to all jack ports. | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
q = (float *)(jack_port_get_buffer (_ports [i], nframes)); | |||
memset (q, 0, nframes * sizeof (float)); | |||
} | |||
} | |||
void Jackclient::sendinfo (int state, double error, double ratio) | |||
{ | |||
Jdata *J; | |||
if (_infoq->wr_avail ()) | |||
{ | |||
J = _infoq->wr_datap (); | |||
J->_state = state; | |||
J->_error = error; | |||
J->_ratio = ratio; | |||
J->_bstat = _bstat; | |||
_infoq->wr_commit (); | |||
} | |||
} | |||
void Jackclient::jack_freewheel (int state) | |||
{ | |||
_freew = state ? true : false; | |||
if (_freew) initwait (_ppsec / 4); | |||
} | |||
void Jackclient::jack_latency (jack_latency_callback_mode_t jlcm) | |||
{ | |||
jack_latency_range_t R; | |||
int i; | |||
if (_state < WAIT) return; | |||
if (_mode == PLAY) | |||
{ | |||
if (jlcm != JackPlaybackLatency) return; | |||
R.min = R.max = (int)(_delay / _ratio) + _ltcor; | |||
} | |||
else | |||
{ | |||
if (jlcm != JackCaptureLatency) return; | |||
R.min = R.max = (int)(_delay * _ratio) + _ltcor; | |||
} | |||
for (i = 0; i < _nchan; i++) | |||
{ | |||
jack_port_set_latency_range (_ports [i], jlcm, &R); | |||
} | |||
} | |||
int Jackclient::jack_process (int nframes) | |||
{ | |||
int dk, n; | |||
Adata *D; | |||
jack_time_t t0, t1; | |||
jack_nframes_t ft; | |||
float us; | |||
double tj, err, d1, d2, rd; | |||
// Buffer size change or other evil. | |||
if (_state == TERM) | |||
{ | |||
sendinfo (TERM, 0, 0); | |||
return 0; | |||
} | |||
// Skip cylce if ports may not yet exist. | |||
if (_state < WAIT) return 0; | |||
// Start synchronisation 1/2 second after entering | |||
// the WAIT state. This delay allows the ALSA thread | |||
// to restart cleanly if necessary. Disabled while | |||
// freewheeling. | |||
if (_state == WAIT) | |||
{ | |||
if (_freew) return 0; | |||
if (_mode == CAPT) silence (nframes); | |||
if (++_count == 0) initsync (); | |||
else return 0; | |||
} | |||
// Get the start time of the current cycle. | |||
jack_get_cycle_times (_client, &ft, &t0, &t1, &us); | |||
tj = tjack (t0); | |||
// Check for any skipped cycles. | |||
if (_state >= SYNC1) | |||
{ | |||
dk = ft - _ft - _bsize; | |||
if (_mode == PLAY) | |||
{ | |||
dk = (int)(dk * _ratio + 0.5); | |||
_audioq->wr_commit (dk); | |||
} | |||
else | |||
{ | |||
dk = (int)(dk / _ratio + 0.5); | |||
_audioq->rd_commit (dk); | |||
} | |||
} | |||
_ft = ft; | |||
// Check if we have timing data from the ALSA thread. | |||
n = _alsaq->rd_avail (); | |||
// If the data queue is full restart synchronisation. | |||
// This can happen e.g. on a jack engine timeout, or | |||
// when too many cycles have been skipped. | |||
if (n == _alsaq->size ()) | |||
{ | |||
initwait (_ppsec / 2); | |||
return 0; | |||
} | |||
if (n) | |||
{ | |||
// Else move interval end to start, and update the | |||
// interval end keeping only the most recent data. | |||
if (_state < SYNC2) _state++; | |||
_t_a0 = _t_a1; | |||
_k_a0 = _k_a1; | |||
while (_alsaq->rd_avail ()) | |||
{ | |||
D = _alsaq->rd_datap (); | |||
// Restart synchronisation in case of | |||
// an error in the ALSA interface. | |||
if (D->_state == Alsathread::WAIT) | |||
{ | |||
initwait (_ppsec / 2); | |||
return 0; | |||
} | |||
_t_a1 = D->_timer; | |||
_k_a1 += D->_nsamp; | |||
_alsaq->rd_commit (); | |||
} | |||
} | |||
err = 0; | |||
if (_state >= SYNC2) | |||
{ | |||
// Compute the delay error. | |||
d1 = tjack_diff (tj, _t_a0); | |||
d2 = tjack_diff (_t_a1, _t_a0); | |||
rd = _resamp ? _resamp->inpdist () : 0.0; | |||
if (_mode == PLAY) | |||
{ | |||
n = _audioq->nwr () - _k_a0; // Must be done as integer as both terms will overflow. | |||
err = n - (_k_a1 - _k_a0) * d1 / d2 + rd * _ratio - _delay; | |||
} | |||
else | |||
{ | |||
n = _k_a0 - _audioq->nrd (); // Must be done as integer as both terms will overflow. | |||
err = n + (_k_a1 - _k_a0) * d1 / d2 + rd - _delay ; | |||
} | |||
n = (int)(floor (err + 0.5)); | |||
if (_state == SYNC2) | |||
{ | |||
// We have the first delay error value. Adjust the audio | |||
// queue to obtain the actually wanted delay, and start | |||
// tracking. | |||
if (_mode == PLAY) _audioq->wr_commit (-n); | |||
else _audioq->rd_commit (n); | |||
err -= n; | |||
setloop (1.0); | |||
_state = PROC1; | |||
} | |||
} | |||
// Switch to lower bandwidth after 4 seconds. | |||
if ((_state == PROC1) && (++_count == 4 * _ppsec)) | |||
{ | |||
_state = PROC2; | |||
setloop (0.05); | |||
} | |||
if (_state >= PROC1) | |||
{ | |||
_z1 += _w0 * (_w1 * err - _z1); | |||
_z2 += _w0 * (_z1 - _z2); | |||
_z3 += _w2 * _z2; | |||
// Check error conditions. | |||
if (fabs (_z3) > 0.05) | |||
{ | |||
// Something is really wrong, wait 10 seconds then restart. | |||
initwait (10 * _ppsec); | |||
return 0; | |||
} | |||
// Run loop filter and set resample ratio. | |||
if (_resamp) | |||
{ | |||
_rcorr = 1 - (_z2 + _z3); | |||
if (_rcorr > 1.05) _rcorr = 1.05; | |||
if (_rcorr < 0.95) _rcorr = 0.95; | |||
_resamp->set_rratio (_rcorr); | |||
} | |||
sendinfo (_state, err, _rcorr); | |||
// Resample and transfer between audio | |||
// queue and jack ports. | |||
if (_mode == PLAY) playback (nframes); | |||
else capture (nframes); | |||
} | |||
else if (_mode == CAPT) silence (nframes); | |||
return 0; | |||
} |
@@ -1,120 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#ifndef __JACKCLIENT_H | |||
#define __JACKCLIENT_H | |||
#include <zita-resampler/vresampler.h> | |||
#include "jack/jack.h" | |||
#include "lfqueue.h" | |||
class Jackclient | |||
{ | |||
public: | |||
Jackclient (jack_client_t*, const char *jserv, int mode, int nchan, bool sync, void *arg); | |||
virtual ~Jackclient (void); | |||
enum { PLAY, CAPT, MAXCHAN = 64 }; | |||
enum { INIT, TERM, WAIT, SYNC0, SYNC1, SYNC2, PROC1, PROC2 }; | |||
void start (Lfq_audio *audioq, | |||
Lfq_int32 *commq, | |||
Lfq_adata *alsaq, | |||
Lfq_jdata *infoq, | |||
double ratio, | |||
int delay, | |||
int ltcor, | |||
int rqual); | |||
const char *jname (void) const { return _jname; } | |||
int fsamp (void) const { return _fsamp; } | |||
int bsize (void) const { return _bsize; } | |||
int rprio (void) const { return _rprio; } | |||
void *getarg(void) const { return _arg; } | |||
private: | |||
bool init (const char *jserv); | |||
void fini (void); | |||
void initwait (int nwait); | |||
void initsync (void); | |||
void setloop (double bw); | |||
void silence (int nframes); | |||
void playback (int nframes); | |||
void capture (int nframes); | |||
void sendinfo (int state, double error, double ratio); | |||
virtual void thr_main (void) {} | |||
void jack_freewheel (int state); | |||
void jack_latency (jack_latency_callback_mode_t jlcm); | |||
int jack_process (int nframes); | |||
jack_client_t *_client; | |||
jack_port_t *_ports [MAXCHAN]; | |||
void *_arg; | |||
const char *_jname; | |||
int _mode; | |||
int _nchan; | |||
int _state; | |||
int _count; | |||
int _fsamp; | |||
int _bsize; | |||
int _rprio; | |||
bool _freew; | |||
float *_buff; | |||
Lfq_audio *_audioq; | |||
Lfq_int32 *_commq; | |||
Lfq_adata *_alsaq; | |||
Lfq_jdata *_infoq; | |||
double _ratio; | |||
int _ppsec; | |||
int _bstat; | |||
jack_nframes_t _ft; | |||
double _t_a0; | |||
double _t_a1; | |||
int _k_a0; | |||
int _k_a1; | |||
double _delay; | |||
int _ltcor; | |||
double _w0; | |||
double _w1; | |||
double _w2; | |||
double _z1; | |||
double _z2; | |||
double _z3; | |||
double _rcorr; | |||
VResampler *_resamp; | |||
static void jack_static_shutdown (void *arg); | |||
static int jack_static_buffsize (jack_nframes_t nframes, void *arg); | |||
static void jack_static_freewheel (int state, void *arg); | |||
static void jack_static_latency (jack_latency_callback_mode_t jlcm, void *arg); | |||
static int jack_static_process (jack_nframes_t nframes, void *arg); | |||
}; | |||
#endif |
@@ -1,89 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include <assert.h> | |||
#include "lfqueue.h" | |||
Lfq_adata::Lfq_adata (int size) : | |||
_size (size), | |||
_mask (size - 1), | |||
_nwr (0), | |||
_nrd (0) | |||
{ | |||
assert (!(_size & _mask)); | |||
_data = new Adata [_size]; | |||
} | |||
Lfq_adata::~Lfq_adata (void) | |||
{ | |||
delete[] _data; | |||
} | |||
Lfq_jdata::Lfq_jdata (int size) : | |||
_size (size), | |||
_mask (size - 1), | |||
_nwr (0), | |||
_nrd (0) | |||
{ | |||
assert (!(_size & _mask)); | |||
_data = new Jdata [_size]; | |||
} | |||
Lfq_jdata::~Lfq_jdata (void) | |||
{ | |||
delete[] _data; | |||
} | |||
Lfq_int32::Lfq_int32 (int size) : | |||
_size (size), | |||
_mask (size - 1), | |||
_nwr (0), | |||
_nrd (0) | |||
{ | |||
assert (!(_size & _mask)); | |||
_data = new int32_t [_size]; | |||
} | |||
Lfq_int32::~Lfq_int32 (void) | |||
{ | |||
delete[] _data; | |||
} | |||
Lfq_audio::Lfq_audio (int nsamp, int nchan) : | |||
_size (nsamp), | |||
_mask (nsamp - 1), | |||
_nch (nchan), | |||
_nwr (0), | |||
_nrd (0) | |||
{ | |||
assert (!(_size & _mask)); | |||
_data = new float [_nch * _size]; | |||
} | |||
Lfq_audio::~Lfq_audio (void) | |||
{ | |||
delete[] _data; | |||
} | |||
@@ -1,182 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#ifndef __LFQUEUE_H | |||
#define __LFQUEUE_H | |||
#include <stdint.h> | |||
#include <string.h> | |||
class Adata | |||
{ | |||
public: | |||
int32_t _state; | |||
int32_t _nsamp; | |||
double _timer; | |||
}; | |||
class Lfq_adata | |||
{ | |||
public: | |||
Lfq_adata (int size); | |||
~Lfq_adata (void); | |||
void reset (void) { _nwr = _nrd = 0; } | |||
int size (void) const { return _size; } | |||
int wr_avail (void) const { return _size - _nwr + _nrd; } | |||
Adata *wr_datap (void) { return _data + (_nwr & _mask); } | |||
void wr_commit (void) { _nwr++; } | |||
int rd_avail (void) const { return _nwr - _nrd; } | |||
Adata *rd_datap (void) { return _data + (_nrd & _mask); } | |||
void rd_commit (void) { _nrd++; } | |||
private: | |||
Adata *_data; | |||
int _size; | |||
int _mask; | |||
int _nwr; | |||
int _nrd; | |||
}; | |||
class Jdata | |||
{ | |||
public: | |||
int32_t _state; | |||
double _error; | |||
double _ratio; | |||
int _bstat; | |||
}; | |||
class Lfq_jdata | |||
{ | |||
public: | |||
Lfq_jdata (int size); | |||
~Lfq_jdata (void); | |||
void reset (void) { _nwr = _nrd = 0; } | |||
int size (void) const { return _size; } | |||
int wr_avail (void) const { return _size - _nwr + _nrd; } | |||
Jdata *wr_datap (void) { return _data + (_nwr & _mask); } | |||
void wr_commit (void) { _nwr++; } | |||
int rd_avail (void) const { return _nwr - _nrd; } | |||
Jdata *rd_datap (void) { return _data + (_nrd & _mask); } | |||
void rd_commit (void) { _nrd++; } | |||
private: | |||
Jdata *_data; | |||
int _size; | |||
int _mask; | |||
int _nwr; | |||
int _nrd; | |||
}; | |||
class Lfq_int32 | |||
{ | |||
public: | |||
Lfq_int32 (int size); | |||
~Lfq_int32 (void); | |||
int size (void) const { return _size; } | |||
void reset (void) { _nwr = _nrd = 0; } | |||
int wr_avail (void) const { return _size - _nwr + _nrd; } | |||
int32_t *wr_datap (void) { return _data + (_nwr & _mask); } | |||
void wr_commit (void) { _nwr++; } | |||
int rd_avail (void) const { return _nwr - _nrd; } | |||
int32_t *rd_datap (void) { return _data + (_nrd & _mask); } | |||
void rd_commit (void) { _nrd++; } | |||
void wr_int32 (int32_t v) { _data [_nwr++ & _mask] = v; } | |||
void wr_uint32 (uint32_t v) { _data [_nwr++ & _mask] = v; } | |||
void wr_float (float v) { *(float *)(_data + (_nwr++ & _mask)) = v; } | |||
int32_t rd_int32 (void) { return _data [_nrd++ & _mask]; } | |||
int32_t rd_uint32 (void) { return _data [_nrd++ & _mask]; } | |||
float rd_float (void) { return *(float *)(_data + (_nrd++ & _mask)); } | |||
private: | |||
int32_t *_data; | |||
int _size; | |||
int _mask; | |||
int _nwr; | |||
int _nrd; | |||
}; | |||
class Lfq_audio | |||
{ | |||
public: | |||
Lfq_audio (int nsamp, int nchan); | |||
~Lfq_audio (void); | |||
int size (void) const { return _size; } | |||
void reset (void) | |||
{ | |||
_nwr = _nrd = 0; | |||
memset (_data, 0, _size * _nch * sizeof (float)); | |||
} | |||
int nchan (void) const { return _nch; } | |||
int nwr (void) const { return _nwr; }; | |||
int nrd (void) const { return _nrd; }; | |||
int wr_avail (void) const { return _size - _nwr + _nrd; } | |||
int wr_linav (void) const { return _size - (_nwr & _mask); } | |||
float *wr_datap (void) { return _data + _nch * (_nwr & _mask); } | |||
void wr_commit (int k) { _nwr += k; } | |||
int rd_avail (void) const { return _nwr - _nrd; } | |||
int rd_linav (void) const { return _size - (_nrd & _mask); } | |||
float *rd_datap (void) { return _data + _nch * (_nrd & _mask); } | |||
void rd_commit (int k) { _nrd += k; } | |||
private: | |||
float *_data; | |||
int _size; | |||
int _mask; | |||
int _nch; | |||
int _nwr; | |||
int _nrd; | |||
}; | |||
#endif | |||
@@ -1,87 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include "pxthread.h" | |||
Pxthread::Pxthread (void) : _thrid (0) | |||
{ | |||
} | |||
Pxthread::~Pxthread (void) | |||
{ | |||
} | |||
extern "C" void *Pxthread_entry_point (void *arg) | |||
{ | |||
Pxthread *T = (Pxthread *) arg; | |||
T->thr_main (); | |||
return NULL; | |||
} | |||
int Pxthread::thr_start (int policy, int priority, size_t stacksize) | |||
{ | |||
int min, max, rc; | |||
pthread_attr_t attr; | |||
struct sched_param parm; | |||
min = sched_get_priority_min (policy); | |||
max = sched_get_priority_max (policy); | |||
priority += max; | |||
if (priority > max) priority = max; | |||
if (priority < min) priority = min; | |||
parm.sched_priority = priority; | |||
pthread_attr_init (&attr); | |||
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); | |||
pthread_attr_setschedpolicy (&attr, policy); | |||
pthread_attr_setschedparam (&attr, &parm); | |||
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); | |||
pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); | |||
pthread_attr_setstacksize (&attr, stacksize); | |||
_thrid = 0; | |||
rc = pthread_create (&_thrid, | |||
&attr, | |||
Pxthread_entry_point, | |||
this); | |||
pthread_attr_destroy (&attr); | |||
return rc; | |||
} | |||
void Pxthread::thr_main (void) | |||
{ | |||
} | |||
void Pxthread::thr_wait (void) | |||
{ | |||
if (_thrid == 0) | |||
return; | |||
pthread_join (_thrid, NULL); | |||
_thrid = 0; | |||
} | |||
@@ -1,53 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#ifndef __PXTHREAD_H | |||
#define __PXTHREAD_H | |||
#include <sys/types.h> | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdarg.h> | |||
#include <assert.h> | |||
#include <errno.h> | |||
#include <pthread.h> | |||
class Pxthread | |||
{ | |||
public: | |||
Pxthread (void); | |||
virtual ~Pxthread (void); | |||
Pxthread (const Pxthread&); | |||
Pxthread& operator=(const Pxthread&); | |||
virtual void thr_main (void) = 0; | |||
virtual int thr_start (int policy, int priority, size_t stacksize = 0); | |||
virtual void thr_wait (void); | |||
private: | |||
pthread_t _thrid; | |||
}; | |||
#endif |
@@ -1,53 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#ifndef __TIMERS_H | |||
#define __TIMERS_H | |||
#include <math.h> | |||
#include <sys/time.h> | |||
#include <jack/jack.h> | |||
#define tjack_mod ldexp (1e-6f, 32) | |||
inline double tjack_diff (double a, double b) | |||
{ | |||
double d, m; | |||
d = a - b; | |||
m = tjack_mod; | |||
while (d < -m / 2) d += m; | |||
while (d >= m / 2) d -= m; | |||
return d; | |||
} | |||
inline double tjack (jack_time_t t, double dt = 0) | |||
{ | |||
int32_t u = (int32_t)(t & 0xFFFFFFFFLL); | |||
return 1e-6 * u; | |||
} | |||
#endif |
@@ -1,409 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include <stdio.h> | |||
#include <signal.h> | |||
#include "alsathread.h" | |||
#include "jackclient.h" | |||
#include "lfqueue.h" | |||
#include "jack/control.h" | |||
static const char *clopt = "hvLSwj:d:r:p:n:c:Q:I:"; | |||
static void help (void) | |||
{ | |||
jack_info ("%s-%s", APPNAME, VERSION); | |||
jack_info ("(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>"); | |||
jack_info ("Use ALSA capture device as a Jack client."); | |||
jack_info ("Options:"); | |||
jack_info (" -h Display this text"); | |||
jack_info (" -j <jackname> Name as Jack client [%s]", APPNAME); | |||
jack_info (" -d <device> ALSA capture device [none]"); | |||
jack_info (" -r <rate> Sample rate [48000]"); | |||
jack_info (" -p <period> Period size [256]"); | |||
jack_info (" -n <nfrags> Number of fragments [2]"); | |||
jack_info (" -c <nchannels> Number of channels [2]"); | |||
jack_info (" -S Word clock sync, no resampling"); | |||
jack_info (" -Q <quality> Resampling quality, 16..96 [auto]"); | |||
jack_info (" -I <samples> Latency adjustment [0]"); | |||
jack_info (" -L Force 16-bit and 2 channels [off]"); | |||
jack_info (" -w Wait until soundcard is available [off]"); | |||
jack_info (" -v Print tracing information [off]"); | |||
} | |||
class zita_a2j | |||
{ | |||
Lfq_int32 *commq; | |||
Lfq_adata *alsaq; | |||
Lfq_jdata *infoq; | |||
Lfq_audio *audioq; | |||
bool stop; | |||
bool v_opt; | |||
bool L_opt; | |||
bool S_opt; | |||
bool w_opt; | |||
char *jname; | |||
char *device; | |||
int fsamp; | |||
int bsize; | |||
int nfrag; | |||
int nchan; | |||
int rqual; | |||
int ltcor; | |||
public: | |||
zita_a2j() | |||
{ | |||
commq = new Lfq_int32(16); | |||
alsaq = new Lfq_adata(256); | |||
infoq = new Lfq_jdata(256); | |||
audioq = 0; | |||
stop = false; | |||
v_opt = false; | |||
L_opt = false; | |||
S_opt = false; | |||
w_opt = false; | |||
jname = strdup(APPNAME); | |||
device = 0; | |||
fsamp = 48000; | |||
bsize = 128; | |||
nfrag = 2; | |||
nchan = 2; | |||
rqual = 0; | |||
ltcor = 0; | |||
A = 0; | |||
C = 0; | |||
J = 0; | |||
t = 0; | |||
} | |||
private: | |||
int procoptions (int ac, const char *av []) | |||
{ | |||
int k; | |||
optind = 1; | |||
opterr = 0; | |||
while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||
{ | |||
if (optarg && (*optarg == '-')) | |||
{ | |||
jack_error (APPNAME ": Missing argument for '-%c' option.", k); | |||
jack_error (APPNAME ": Use '-h' to see all options."); | |||
return 1; | |||
} | |||
switch (k) | |||
{ | |||
case 'h' : help (); return 1; | |||
case 'v' : v_opt = true; break; | |||
case 'L' : L_opt = true; break; | |||
case 'S' : S_opt = true; break; | |||
case 'w' : w_opt = true; break; | |||
case 'j' : jname = optarg; break; | |||
case 'd' : device = optarg; break; | |||
case 'r' : fsamp = atoi (optarg); break; | |||
case 'p' : bsize = atoi (optarg); break; | |||
case 'n' : nfrag = atoi (optarg); break; | |||
case 'c' : nchan = atoi (optarg); break; | |||
case 'Q' : rqual = atoi (optarg); break; | |||
case 'I' : ltcor = atoi (optarg); break; | |||
case '?': | |||
if (optopt != ':' && strchr (clopt, optopt)) | |||
{ | |||
jack_error (APPNAME ": Missing argument for '-%c' option.", optopt); | |||
} | |||
else if (isprint (optopt)) | |||
{ | |||
jack_error (APPNAME ": Unknown option '-%c'.", optopt); | |||
} | |||
else | |||
{ | |||
jack_error (APPNAME ": Unknown option character '0x%02x'.", optopt & 255); | |||
} | |||
jack_error (APPNAME ": Use '-h' to see all options."); | |||
return 1; | |||
default: | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
int parse_options (const char* load_init) | |||
{ | |||
int argsz; | |||
int argc = 0; | |||
const char** argv; | |||
char* args = strdup (load_init); | |||
char* token; | |||
char* ptr = args; | |||
char* savep; | |||
if (!load_init) { | |||
return 0; | |||
} | |||
argsz = 8; /* random guess at "maxargs" */ | |||
argv = (const char **) malloc (sizeof (char *) * argsz); | |||
argv[argc++] = APPNAME; | |||
while (1) { | |||
if ((token = strtok_r (ptr, " ", &savep)) == NULL) { | |||
break; | |||
} | |||
if (argc == argsz) { | |||
argsz *= 2; | |||
argv = (const char **) realloc (argv, sizeof (char *) * argsz); | |||
} | |||
argv[argc++] = token; | |||
ptr = NULL; | |||
} | |||
return procoptions (argc, argv); | |||
} | |||
void printinfo (void) | |||
{ | |||
int n, k; | |||
double e, r; | |||
Jdata *J; | |||
n = 0; | |||
k = 99999; | |||
e = r = 0; | |||
while (infoq->rd_avail ()) | |||
{ | |||
J = infoq->rd_datap (); | |||
if (J->_state == Jackclient::TERM) | |||
{ | |||
jack_error (APPNAME ": Fatal error condition, terminating."); | |||
stop = true; | |||
return; | |||
} | |||
else if (J->_state == Jackclient::WAIT) | |||
{ | |||
jack_info (APPNAME ": Detected excessive timing errors, waiting 10 seconds."); | |||
n = 0; | |||
} | |||
else if (J->_state == Jackclient::SYNC0) | |||
{ | |||
jack_info (APPNAME ": Starting synchronisation."); | |||
} | |||
else if (v_opt) | |||
{ | |||
n++; | |||
e += J->_error; | |||
r += J->_ratio; | |||
if (J->_bstat < k) k = J->_bstat; | |||
} | |||
infoq->rd_commit (); | |||
} | |||
if (n) jack_info (APPNAME ": %8.3lf %10.6lf %5d", e / n, r / n, k); | |||
} | |||
Alsa_pcmi *A; | |||
Alsathread *C; | |||
Jackclient *J; | |||
pthread_t t; | |||
int topts; | |||
static void* _retry_alsa_pcmi (void *arg) | |||
{ | |||
((zita_a2j*)arg)->retry_alsa_pcmi (); | |||
return NULL; | |||
} | |||
void retry_alsa_pcmi () | |||
{ | |||
Alsa_pcmi *a; | |||
while (! stop) | |||
{ | |||
sleep(1); | |||
a = new Alsa_pcmi (0, device, 0, fsamp, bsize, nfrag, topts); | |||
if (a->state ()) | |||
{ | |||
delete a; | |||
continue; | |||
} | |||
A = a; | |||
if (v_opt) A->printinfo (); | |||
C = new Alsathread (A, Alsathread::CAPT); | |||
usleep (100*1000); | |||
jack_initialize_part2 (); | |||
jack_info (APPNAME ": Device is now available and has been activated"); | |||
break; | |||
} | |||
t = 0; | |||
} | |||
public: | |||
int | |||
jack_initialize (jack_client_t* client, const char* load_init) | |||
{ | |||
int opts; | |||
if (parse_options (load_init)) { | |||
jack_error (APPNAME ": parse options failed"); | |||
delete this; | |||
return 1; | |||
} | |||
if (device == 0) | |||
{ | |||
help (); | |||
delete this; | |||
return 1; | |||
} | |||
if (rqual < 16) rqual = 16; | |||
if (rqual > 96) rqual = 96; | |||
if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||
{ | |||
jack_error (APPNAME ": Illegal parameter value(s)."); | |||
delete this; | |||
return 1; | |||
} | |||
opts = 0; | |||
if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL; | |||
if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH; | |||
if (w_opt) | |||
{ | |||
J = new Jackclient (client, 0, Jackclient::CAPT, nchan, S_opt, this); | |||
A = new Alsa_pcmi (0, device, 0, fsamp, bsize, nfrag, opts); | |||
// if device is not available, spawn thread to keep trying | |||
if (A->state ()) | |||
{ | |||
delete A; | |||
A = NULL; | |||
topts = opts; | |||
pthread_create (&t, NULL, _retry_alsa_pcmi, this); | |||
jack_info (APPNAME ": Could not open device, will keep trying in new thread..."); | |||
return 0; | |||
} | |||
// otherwise continue as normal | |||
if (v_opt) A->printinfo (); | |||
C = new Alsathread (A, Alsathread::CAPT); | |||
} | |||
else | |||
{ | |||
A = new Alsa_pcmi (0, device, 0, fsamp, bsize, nfrag, opts); | |||
if (A->state ()) | |||
{ | |||
jack_error (APPNAME ": Can't open ALSA capture device '%s'.", device); | |||
delete this; | |||
return 1; | |||
} | |||
if (v_opt) A->printinfo (); | |||
if (nchan > A->ncapt ()) | |||
{ | |||
nchan = A->ncapt (); | |||
jack_error (APPNAME ": Warning: only %d channels are available.", nchan); | |||
} | |||
C = new Alsathread (A, Alsathread::CAPT); | |||
J = new Jackclient (client, 0, Jackclient::CAPT, nchan, S_opt, this); | |||
} | |||
usleep (100*1000); | |||
jack_initialize_part2 (); | |||
return 0; | |||
} | |||
void jack_initialize_part2 () | |||
{ | |||
int k, k_del; | |||
double t_alsa; | |||
double t_jack; | |||
double t_del; | |||
t_alsa = (double) bsize / fsamp; | |||
if (t_alsa < 1e-3) t_alsa = 1e-3; | |||
t_jack = (double) J->bsize () / J->fsamp (); | |||
t_del = t_alsa + t_jack; | |||
k_del = (int)(t_del * fsamp); | |||
for (k = 256; k < 2 * k_del; k *= 2); | |||
audioq = new Lfq_audio (k, nchan); | |||
if (rqual == 0) | |||
{ | |||
k = (fsamp < J->fsamp ()) ? fsamp : J->fsamp (); | |||
if (k < 44100) k = 44100; | |||
rqual = (int)((6.7 * k) / (k - 38000)); | |||
} | |||
if (rqual < 16) rqual = 16; | |||
if (rqual > 96) rqual = 96; | |||
C->start (audioq, commq, alsaq, J->rprio () + 10); | |||
J->start (audioq, commq, alsaq, infoq, J->fsamp () / (double) fsamp, k_del, ltcor, rqual); | |||
} | |||
void jack_finish (void* arg) | |||
{ | |||
if (t != 0) | |||
{ | |||
stop = true; | |||
pthread_join(t, NULL); | |||
} | |||
commq->wr_int32 (Alsathread::TERM); | |||
usleep (100000); | |||
delete C; | |||
delete A; | |||
delete J; | |||
delete audioq; | |||
} | |||
}; | |||
extern "C" { | |||
int | |||
jack_initialize (jack_client_t* client, const char* load_init) | |||
{ | |||
zita_a2j *c = new zita_a2j(); | |||
return c->jack_initialize(client, load_init); | |||
} | |||
void jack_finish (void* arg) | |||
{ | |||
if (!arg) return; | |||
Jackclient *J = (Jackclient *)arg; | |||
zita_a2j *c = (zita_a2j *)J->getarg(); | |||
c->jack_finish(arg); | |||
delete c; | |||
} | |||
} /* extern "C" */ |
@@ -1,408 +0,0 @@ | |||
// ---------------------------------------------------------------------------- | |||
// | |||
// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org> | |||
// | |||
// 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 3 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, see <http://www.gnu.org/licenses/>. | |||
// | |||
// ---------------------------------------------------------------------------- | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include <stdio.h> | |||
#include <signal.h> | |||
#include "alsathread.h" | |||
#include "jackclient.h" | |||
#include "lfqueue.h" | |||
#include "jack/control.h" | |||
static const char *clopt = "hvLSwj:d:r:p:n:c:Q:O:"; | |||
static void help (void) | |||
{ | |||
jack_info ("%s-%s", APPNAME, VERSION); | |||
jack_info ("(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>"); | |||
jack_info ("Use ALSA playback device as a Jack client."); | |||
jack_info ("Options:"); | |||
jack_info (" -h Display this text"); | |||
jack_info (" -j <jackname> Name as Jack client [%s]", APPNAME); | |||
jack_info (" -d <device> ALSA playback device [none]"); | |||
jack_info (" -r <rate> Sample rate [48000]"); | |||
jack_info (" -p <period> Period size [256]"); | |||
jack_info (" -n <nfrags> Number of fragments [2]"); | |||
jack_info (" -c <nchannels> Number of channels [2]"); | |||
jack_info (" -S Word clock sync, no resampling"); | |||
jack_info (" -Q <quality> Resampling quality, 16..96 [auto]"); | |||
jack_info (" -O <samples> Latency adjustment [0]"); | |||
jack_info (" -L Force 16-bit and 2 channels [off]"); | |||
jack_info (" -w Wait until soundcard is available [off]"); | |||
jack_info (" -v Print tracing information [off]"); | |||
} | |||
class zita_j2a | |||
{ | |||
Lfq_int32 *commq; | |||
Lfq_adata *alsaq; | |||
Lfq_jdata *infoq; | |||
Lfq_audio *audioq; | |||
bool stop; | |||
bool v_opt; | |||
bool L_opt; | |||
bool S_opt; | |||
bool w_opt; | |||
char *jname; | |||
char *device; | |||
int fsamp; | |||
int bsize; | |||
int nfrag; | |||
int nchan; | |||
int rqual; | |||
int ltcor; | |||
public: | |||
zita_j2a() | |||
{ | |||
commq = new Lfq_int32(16); | |||
alsaq = new Lfq_adata(256); | |||
infoq = new Lfq_jdata(256); | |||
audioq = 0; | |||
stop = false; | |||
v_opt = false; | |||
L_opt = false; | |||
S_opt = false; | |||
w_opt = false; | |||
jname = strdup(APPNAME); | |||
device = 0; | |||
fsamp = 48000; | |||
bsize = 128; | |||
nfrag = 2; | |||
nchan = 2; | |||
rqual = 0; | |||
ltcor = 0; | |||
A = 0; | |||
P = 0; | |||
J = 0; | |||
t = 0; | |||
} | |||
private: | |||
int procoptions (int ac, const char *av []) | |||
{ | |||
int k; | |||
optind = 1; | |||
opterr = 0; | |||
while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||
{ | |||
if (optarg && (*optarg == '-')) | |||
{ | |||
jack_error (APPNAME ": Missing argument for '-%c' option.", k); | |||
jack_error (APPNAME ": Use '-h' to see all options."); | |||
return 1; | |||
} | |||
switch (k) | |||
{ | |||
case 'h' : help (); return 1; | |||
case 'v' : v_opt = true; break; | |||
case 'L' : L_opt = true; break; | |||
case 'S' : S_opt = true; break; | |||
case 'w' : w_opt = true; break; | |||
case 'j' : jname = optarg; break; | |||
case 'd' : device = optarg; break; | |||
case 'r' : fsamp = atoi (optarg); break; | |||
case 'p' : bsize = atoi (optarg); break; | |||
case 'n' : nfrag = atoi (optarg); break; | |||
case 'c' : nchan = atoi (optarg); break; | |||
case 'Q' : rqual = atoi (optarg); break; | |||
case 'O' : ltcor = atoi (optarg); break; | |||
case '?': | |||
if (optopt != ':' && strchr (clopt, optopt)) | |||
{ | |||
jack_error (APPNAME ": Missing argument for '-%c' option.", optopt); | |||
} | |||
else if (isprint (optopt)) | |||
{ | |||
jack_error (APPNAME ": Unknown option '-%c'.", optopt); | |||
} | |||
else | |||
{ | |||
jack_error (APPNAME ": Unknown option character '0x%02x'.", optopt & 255); | |||
} | |||
jack_error (APPNAME ": Use '-h' to see all options."); | |||
return 1; | |||
default: | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
int parse_options (const char* load_init) | |||
{ | |||
int argsz; | |||
int argc = 0; | |||
const char** argv; | |||
char* args = strdup (load_init); | |||
char* token; | |||
char* ptr = args; | |||
char* savep; | |||
if (!load_init) { | |||
return 0; | |||
} | |||
argsz = 8; /* random guess at "maxargs" */ | |||
argv = (const char **) malloc (sizeof (char *) * argsz); | |||
argv[argc++] = APPNAME; | |||
while (1) { | |||
if ((token = strtok_r (ptr, " ", &savep)) == NULL) { | |||
break; | |||
} | |||
if (argc == argsz) { | |||
argsz *= 2; | |||
argv = (const char **) realloc (argv, sizeof (char *) * argsz); | |||
} | |||
argv[argc++] = token; | |||
ptr = NULL; | |||
} | |||
return procoptions (argc, argv); | |||
} | |||
void printinfo (void) | |||
{ | |||
int n, k; | |||
double e, r; | |||
Jdata *J; | |||
n = 0; | |||
k = 99999; | |||
e = r = 0; | |||
while (infoq->rd_avail ()) | |||
{ | |||
J = infoq->rd_datap (); | |||
if (J->_state == Jackclient::TERM) | |||
{ | |||
jack_info (APPNAME ": Fatal error condition, terminating."); | |||
stop = true; | |||
return; | |||
} | |||
else if (J->_state == Jackclient::WAIT) | |||
{ | |||
jack_info (APPNAME ": Detected excessive timing errors, waiting 10 seconds."); | |||
n = 0; | |||
} | |||
else if (J->_state == Jackclient::SYNC0) | |||
{ | |||
jack_info (APPNAME ": Starting synchronisation."); | |||
} | |||
else if (v_opt) | |||
{ | |||
n++; | |||
e += J->_error; | |||
r += J->_ratio; | |||
if (J->_bstat < k) k = J->_bstat; | |||
} | |||
infoq->rd_commit (); | |||
} | |||
if (n) jack_info ("%8.3lf %10.6lf %5d", e / n, r / n, k); | |||
} | |||
Alsa_pcmi *A; | |||
Alsathread *P; | |||
Jackclient *J; | |||
pthread_t t; | |||
int topts; | |||
static void* _retry_alsa_pcmi (void *arg) | |||
{ | |||
((zita_j2a*)arg)->retry_alsa_pcmi (); | |||
return NULL; | |||
} | |||
void retry_alsa_pcmi () | |||
{ | |||
Alsa_pcmi *a; | |||
while (! stop) | |||
{ | |||
sleep(1); | |||
a = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, topts); | |||
if (a->state ()) | |||
{ | |||
delete a; | |||
continue; | |||
} | |||
A = a; | |||
if (v_opt) A->printinfo (); | |||
P = new Alsathread (A, Alsathread::PLAY); | |||
usleep (100*1000); | |||
jack_initialize_part2 (); | |||
jack_info (APPNAME ": Device is now available and has been activated"); | |||
break; | |||
} | |||
t = 0; | |||
} | |||
public: | |||
int jack_initialize (jack_client_t* client, const char* load_init) | |||
{ | |||
int opts; | |||
if (parse_options (load_init)) { | |||
delete this; | |||
return 1; | |||
} | |||
if (device == 0) | |||
{ | |||
help (); | |||
delete this; | |||
return 1; | |||
} | |||
if (rqual < 16) rqual = 16; | |||
if (rqual > 96) rqual = 96; | |||
if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||
{ | |||
jack_error (APPNAME ": Illegal parameter value(s)."); | |||
delete this; | |||
return 1; | |||
} | |||
opts = 0; | |||
if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL; | |||
if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH; | |||
if (w_opt) | |||
{ | |||
J = new Jackclient (client, 0, Jackclient::PLAY, nchan, S_opt, this); | |||
// if device is not available, spawn thread to keep trying | |||
A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts); | |||
if (A->state ()) | |||
{ | |||
delete A; | |||
A = NULL; | |||
topts = opts; | |||
pthread_create (&t, NULL, _retry_alsa_pcmi, this); | |||
jack_info (APPNAME ": Could not open device, will keep trying in new thread..."); | |||
return 0; | |||
} | |||
// otherwise continue as normal | |||
if (v_opt) A->printinfo (); | |||
P = new Alsathread (A, Alsathread::PLAY); | |||
} | |||
else | |||
{ | |||
A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts); | |||
if (A->state ()) | |||
{ | |||
jack_error (APPNAME ": Can't open ALSA playback device '%s'.", device); | |||
delete this; | |||
return 1; | |||
} | |||
if (v_opt) A->printinfo (); | |||
if (nchan > A->nplay ()) | |||
{ | |||
nchan = A->nplay (); | |||
jack_error (APPNAME ": Warning: only %d channels are available.", nchan); | |||
} | |||
P = new Alsathread (A, Alsathread::PLAY); | |||
J = new Jackclient (client, 0, Jackclient::PLAY, nchan, S_opt, this); | |||
} | |||
usleep (100*1000); | |||
jack_initialize_part2 (); | |||
return 0; | |||
} | |||
void jack_initialize_part2 () | |||
{ | |||
int k, k_del; | |||
double t_jack; | |||
double t_alsa; | |||
double t_del; | |||
t_alsa = (double) bsize / fsamp; | |||
if (t_alsa < 1e-3) t_alsa = 1e-3; | |||
t_jack = (double) J->bsize () / J->fsamp (); | |||
t_del = t_alsa + t_jack; | |||
k_del = (int)(t_del * fsamp); | |||
for (k = 256; k < 2 * k_del; k *= 2); | |||
audioq = new Lfq_audio (k, nchan); | |||
if (rqual == 0) | |||
{ | |||
k = (fsamp < J->fsamp ()) ? fsamp : J->fsamp (); | |||
if (k < 44100) k = 44100; | |||
rqual = (int)((6.7 * k) / (k - 38000)); | |||
} | |||
if (rqual < 16) rqual = 16; | |||
if (rqual > 96) rqual = 96; | |||
P->start (audioq, commq, alsaq, J->rprio () + 10); | |||
J->start (audioq, commq, alsaq, infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual); | |||
} | |||
void jack_finish (void* arg) | |||
{ | |||
if (t != 0) | |||
{ | |||
stop = true; | |||
pthread_join(t, NULL); | |||
t = 0; | |||
} | |||
commq->wr_int32 (Alsathread::TERM); | |||
usleep (100*1000); | |||
delete P; | |||
delete A; | |||
delete J; | |||
delete audioq; | |||
} | |||
}; | |||
extern "C" { | |||
int | |||
jack_initialize (jack_client_t* client, const char* load_init) | |||
{ | |||
zita_j2a *c = new zita_j2a(); | |||
return c->jack_initialize(client, load_init); | |||
} | |||
void jack_finish (void* arg) | |||
{ | |||
if (!arg) return; | |||
Jackclient *J = (Jackclient *)arg; | |||
zita_j2a *c = (zita_j2a *)J->getarg(); | |||
c->jack_finish(arg); | |||
delete c; | |||
} | |||
} /* extern "C" */ |
@@ -169,9 +169,9 @@ def options(opt): | |||
help='Build with CELT') | |||
celt.add_function(check_for_celt) | |||
opt.add_auto_option( | |||
'example-tools', | |||
help='Build with jack-example-tools', | |||
conf_dest='BUILD_JACK_EXAMPLE_TOOLS', | |||
'tests', | |||
help='Build tests', | |||
conf_dest='BUILD_TESTS', | |||
default=False, | |||
) | |||
@@ -337,10 +337,6 @@ def configure(conf): | |||
else: | |||
conf.env['SYSTEMD_USER_UNIT_DIR'] = None | |||
if conf.env['BUILD_JACK_EXAMPLE_TOOLS']: | |||
conf.recurse('example-clients') | |||
conf.recurse('tools') | |||
# test for the availability of ucontext, and how it should be used | |||
for t in ['gp_regs', 'uc_regs', 'mc_gregs', 'gregs']: | |||
fragment = '#include <ucontext.h>\n' | |||
@@ -853,14 +849,10 @@ def build(bld): | |||
build_drivers(bld) | |||
if bld.env['BUILD_JACK_EXAMPLE_TOOLS']: | |||
bld.recurse('example-clients') | |||
bld.recurse('tools') | |||
if bld.env['IS_LINUX'] or bld.env['IS_FREEBSD']: | |||
bld.recurse('man') | |||
bld.recurse('systemd') | |||
if not bld.env['IS_WINDOWS'] and bld.env['BUILD_JACK_EXAMPLE_TOOLS']: | |||
if not bld.env['IS_WINDOWS'] and bld.env['BUILD_TESTS']: | |||
bld.recurse('tests') | |||
if bld.env['BUILD_JACKDBUS']: | |||
bld.recurse('dbus') | |||