aboutsummaryrefslogtreecommitdiff
path: root/desiredata/src/s_audio_jack.c
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
committerIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
commit4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch)
tree6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src/s_audio_jack.c
parentb334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff)
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src/s_audio_jack.c')
-rw-r--r--desiredata/src/s_audio_jack.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/desiredata/src/s_audio_jack.c b/desiredata/src/s_audio_jack.c
new file mode 100644
index 00000000..ed37829b
--- /dev/null
+++ b/desiredata/src/s_audio_jack.c
@@ -0,0 +1,381 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define PD_PLUSPLUS_FACE
+#include "desire.h"
+#include "m_simd.h"
+#include <jack/jack.h>
+#include <regex.h>
+#include <errno.h>
+#define MAX_CLIENTS 100
+#define NUM_JACK_PORTS 32
+#define BUF_JACK 4096
+static jack_nframes_t jack_out_max;
+#define JACK_OUT_MAX 64
+static jack_nframes_t jack_filled = 0;
+static float jack_outbuf[NUM_JACK_PORTS*BUF_JACK];
+static float jack_inbuf[NUM_JACK_PORTS*BUF_JACK];
+static int jack_started = 0;
+static jack_port_t * input_port[NUM_JACK_PORTS];
+static jack_port_t *output_port[NUM_JACK_PORTS];
+static int outport_count = 0;
+static jack_client_t *jack_client = 0;
+char *jack_client_names[MAX_CLIENTS];
+static int jack_dio_error;
+static int jack_scheduler;
+pthread_mutex_t jack_mutex;
+pthread_cond_t jack_sem;
+t_int jack_save_connection_state(t_int* dummy);
+static void jack_restore_connection_state();
+void run_all_idle_callbacks();
+
+static int process (jack_nframes_t nframes, void *arg) {
+ jack_out_max = max(int(nframes),JACK_OUT_MAX);
+ if (jack_filled >= nframes) {
+ if (jack_filled != nframes) post("Partial read");
+ for (int j = 0; j < sys_outchannels; j++) {
+ float *out = (float *)jack_port_get_buffer(output_port[j], nframes);
+ memcpy(out, jack_outbuf + (j * BUF_JACK), sizeof(float)*nframes);
+ }
+ for (int j = 0; j < sys_inchannels; j++) {
+ float *in = (float *)jack_port_get_buffer( input_port[j], nframes);
+ memcpy(jack_inbuf + (j * BUF_JACK), in, sizeof(float)*nframes);
+ }
+ jack_filled -= nframes;
+ } else { /* PD could not keep up ! */
+ if (jack_started) jack_dio_error = 1;
+ for (int j = 0; j < outport_count; j++) {
+ float *out = (float *)jack_port_get_buffer (output_port[j], nframes);
+ memset(out, 0, sizeof (float) * nframes);
+ }
+ memset(jack_outbuf,0,sizeof(jack_outbuf));
+ jack_filled = 0;
+ }
+ /* tb: wait in the scheduler */
+ /* pthread_cond_broadcast(&jack_sem); */
+ return 0;
+}
+
+void sys_peakmeters();
+extern int sys_meters; /* true if we're metering */
+static int dspticks_per_jacktick;
+static void (*copyblock)(t_sample *dst,t_sample *src,int n);
+static void (*zeroblock)(t_sample *dst,int n);
+extern int canvas_dspstate;
+
+static int cb_process (jack_nframes_t nframes, void *arg) {
+ int timeout = int(nframes * 1e6 / sys_dacsr);
+ if (canvas_dspstate == 0) {
+ /* dsp is switched off, the audio is open ... */
+ for (int j=0; j<sys_outchannels; j++) {
+ t_sample *out = (t_sample *)jack_port_get_buffer (output_port[j], nframes);
+ zeroblock(out, dspticks_per_jacktick * sys_dacblocksize);
+ }
+ return 0;
+ }
+ int status = sys_timedlock(timeout);
+ if (status)
+ if (status == ETIMEDOUT) {
+ /* we're late ... lets hope that jack doesn't kick us out */
+ error("timeout %d", (timeout));
+ sys_log_error(ERR_SYSLOCK);
+ return 0;
+ } else {
+ post("sys_timedlock returned %d", status);
+ return 0;
+ }
+ for (int i = 0; i != dspticks_per_jacktick; ++i) {
+ for (int j=0; j<sys_inchannels; j++) {
+ t_sample *in = (t_sample *)jack_port_get_buffer(input_port[j], nframes);
+ copyblock(sys_soundin + j * sys_dacblocksize, in + i * sys_dacblocksize, sys_dacblocksize);
+ }
+ sched_tick(sys_time + sys_time_per_dsp_tick);
+ for (int j=0; j<sys_outchannels; j++) {
+ t_sample *out = (t_sample *)jack_port_get_buffer (output_port[j], nframes);
+ copyblock(out + i * sys_dacblocksize, sys_soundout + j * sys_dacblocksize, sys_dacblocksize);
+ }
+ if (sys_meters) sys_peakmeters();
+ zeroblock(sys_soundout, sys_outchannels * sys_dacblocksize);
+ }
+ run_all_idle_callbacks();
+ sys_unlock();
+ return 0;
+}
+
+static int jack_srate (jack_nframes_t srate, void *arg) {
+ sys_dacsr = srate;
+ return 0;
+}
+
+void jack_close_audio(void);
+static int jack_ignore_graph_callback = 0;
+static t_int jack_shutdown_handler(t_int* none) {
+ error("jack kicked us out ... trying to reconnect");
+ jack_ignore_graph_callback = 1;
+ /* clean up */
+ jack_close_audio();
+ /* try to reconnect to jack server */
+ jack_open_audio(sys_inchannels, sys_outchannels, int(sys_dacsr), jack_scheduler);
+ /* restore last connection state */
+ jack_restore_connection_state();
+ jack_ignore_graph_callback = 0;
+ return 0;
+}
+
+/* register idle callback in scheduler */
+static void jack_shutdown (void *arg) {sys_callback(jack_shutdown_handler,0,0);}
+static int jack_graph_order_callback(void* arg) {sys_callback(jack_save_connection_state,0,0); return 0;}
+
+static char** jack_get_clients() {
+ int num_clients = 0;
+ regex_t port_regex;
+ const char **jack_ports = jack_get_ports(jack_client, "", "", 0);
+ regcomp(&port_regex, "^[^:]*", REG_EXTENDED);
+ jack_client_names[0] = 0;
+ /* Build a list of clients from the list of ports */
+ for (int i=0; jack_ports[i] != 0; i++) {
+ regmatch_t match_info;
+ char tmp_client_name[100];
+ /* extract the client name from the port name, using a regex that parses the clientname:portname syntax */
+ regexec(&port_regex, jack_ports[i], 1, &match_info, 0);
+ memcpy(tmp_client_name, &jack_ports[i][match_info.rm_so], match_info.rm_eo-match_info.rm_so);
+ tmp_client_name[ match_info.rm_eo - match_info.rm_so ] = '\0';
+ /* do we know about this port's client yet? */
+ int client_seen = 0;
+ for (int j=0; j<num_clients; j++) if (strcmp(tmp_client_name, jack_client_names[j])==0) client_seen = 1;
+ if (!client_seen) {
+ jack_client_names[num_clients] = (char*)getbytes(strlen(tmp_client_name) + 1);
+ /* The alsa_pcm client should go in spot 0. If this is the alsa_pcm client AND we are NOT about to put
+ it in spot 0 put it in spot 0 and move whatever was already in spot 0 to the end. */
+ if (strcmp("alsa_pcm",tmp_client_name)==0 && num_clients>0) {
+ /* alsa_pcm goes in spot 0 */
+ char* tmp = jack_client_names[ num_clients ];
+ jack_client_names[num_clients] = jack_client_names[0];
+ jack_client_names[0] = tmp;
+ strcpy(jack_client_names[0], tmp_client_name);
+ } else {
+ /* put the new client at the end of the client list */
+ strcpy(jack_client_names[num_clients], tmp_client_name);
+ }
+ num_clients++;
+ }
+ }
+ /* for (int i=0; i<num_clients; i++) post("client: %s",jack_client_names[i]); */
+ free(jack_ports);
+ return jack_client_names;
+}
+
+/* Wire up all the ports of one client. */
+static int jack_connect_ports(char *client) {
+ char regex_pattern[100];
+ static int entered = 0;
+ if (entered) return 0;
+ entered = 1;
+ if (strlen(client) > 96) return -1;
+ sprintf(regex_pattern, "%s:.*", client);
+ const char **jack_ports = jack_get_ports(jack_client, regex_pattern, 0, JackPortIsOutput);
+ if (jack_ports)
+ for (int i=0;jack_ports[i] != 0 && i < sys_inchannels;i++)
+ if (jack_connect (jack_client, jack_ports[i], jack_port_name (input_port[i])))
+ error("cannot connect input ports %s -> %s", jack_ports[i],jack_port_name(input_port[i]));
+ free(jack_ports);
+ jack_ports = jack_get_ports(jack_client, regex_pattern, 0, JackPortIsInput);
+ if (jack_ports)
+ for (int i=0;jack_ports[i] != 0 && i < sys_outchannels;i++)
+ if (jack_connect (jack_client, jack_port_name (output_port[i]), jack_ports[i]))
+ error("cannot connect output ports %s -> %s",jack_port_name(output_port[i]),jack_ports[i]);
+ free(jack_ports);
+ return 0;
+}
+
+static void jack_error(const char *desc) {}
+
+int jack_open_audio_2(int inchans, int outchans, int rate, int scheduler);
+int jack_open_audio(int inchans, int outchans, int rate, int scheduler) {
+ jack_dio_error = 0;
+ if (inchans==0 && outchans==0) return 0;
+ int ret = jack_open_audio_2(inchans,outchans,rate,scheduler);
+ if (ret) sys_setscheduler(0);
+ return ret;
+}
+
+int jack_open_audio_2(int inchans, int outchans, int rate, int scheduler) {
+ char port_name[80] = "";
+ int new_jack = 0;
+ if (outchans > NUM_JACK_PORTS) {post("%d output ports not supported, setting to %d",outchans, NUM_JACK_PORTS); outchans = NUM_JACK_PORTS;}
+ if ( inchans > NUM_JACK_PORTS) {post( "%d input ports not supported, setting to %d", inchans, NUM_JACK_PORTS); inchans = NUM_JACK_PORTS;}
+ if (jack_client && scheduler != sys_getscheduler()) {
+ jack_client_close(jack_client);
+ jack_client = 0;
+ }
+ sys_setscheduler(scheduler);
+ jack_scheduler = scheduler;
+ /* set block copy/zero functions */
+ if(SIMD_CHKCNT(sys_dacblocksize) && simd_runtime_check()) {
+ copyblock = (void (*)(t_sample *,t_sample *,int))&copyvec_simd;
+ zeroblock = &zerovec_simd;
+ } else {
+ copyblock = (void (*)(t_sample *,t_sample *,int))&copyvec;
+ zeroblock = &zerovec;
+ }
+ /* try to become a client of the JACK server (we allow two pd's)*/
+ if (!jack_client) {
+ int client_iterator = 0;
+ do {
+ sprintf(port_name,"pure_data_%d",client_iterator);
+ client_iterator++;
+ } while (((jack_client = jack_client_new (port_name)) == 0) && client_iterator < 2);
+ // jack spits out enough messages already, do not warn
+ if (!jack_client) {sys_inchannels = sys_outchannels = 0; return 1;}
+ jack_get_clients();
+ /* tell the JACK server to call `process()' whenever there is work to be done.
+ tb: adapted for callback based scheduling */
+ if (scheduler == 1) {
+ dspticks_per_jacktick = jack_get_buffer_size(jack_client) / sys_schedblocksize;
+ jack_set_process_callback (jack_client, cb_process, 0);
+ } else jack_set_process_callback (jack_client, process, 0);
+ jack_set_error_function (jack_error);
+#ifdef JACK_XRUN
+ jack_set_xrun_callback (jack_client, jack_xrun, 0);
+#endif
+ jack_set_graph_order_callback(jack_client, jack_graph_order_callback, 0);
+ /* tell the JACK server to call `srate()' whenever the sample rate of the system changes. */
+ jack_set_sample_rate_callback (jack_client, jack_srate, 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 (jack_client, jack_shutdown, 0);
+ for (int j=0;j<NUM_JACK_PORTS;j++) {
+ input_port[j]=0;
+ output_port[j]=0;
+ }
+ new_jack = 1;
+ }
+ /* 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. */
+ int srate = jack_get_sample_rate (jack_client);
+ sys_dacsr = srate;
+ /* create the ports */
+ for (int j = 0; j < inchans; j++) {
+ sprintf(port_name, "input%d", j);
+ if (!input_port[j]) input_port[j] = jack_port_register(jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ }
+ for (int j = 0; j < outchans; j++) {
+ sprintf(port_name, "output%d", j);
+ if (!output_port[j]) output_port[j] = jack_port_register(jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ }
+ outport_count = outchans;
+ /* tell the JACK server that we are ready to roll */
+ if (new_jack) {
+ if (jack_activate (jack_client)) {error("cannot activate client"); sys_inchannels = sys_outchannels = 0; return 1;}
+ memset(jack_outbuf,0,sizeof(jack_outbuf));
+ if (jack_client_names[0]) jack_connect_ports(jack_client_names[0]);
+ pthread_mutex_init(&jack_mutex,0);
+ pthread_cond_init(&jack_sem,0);
+ }
+ /* tb: get advance from jack server */
+ sys_schedadvance = int((float)jack_port_get_total_latency(jack_client,output_port[0]) * 1000. / sys_dacsr * 1000);
+ return 0;
+}
+
+void jack_close_audio() {
+ if (!jack_client) return;
+ jack_deactivate(jack_client);
+ jack_started = 0;
+ jack_client_close(jack_client);
+ jack_client = 0;
+ for (int i=0; i<NUM_JACK_PORTS; i++) {
+ input_port[i] = 0;
+ output_port[i] = 0;
+ }
+}
+
+int jack_send_dacs() {
+ int rtnval = SENDDACS_YES;
+ int timeref = int(sys_getrealtime());
+ if (!jack_client) return SENDDACS_NO;
+ if (!sys_inchannels && !sys_outchannels) return SENDDACS_NO;
+ if (jack_dio_error) {
+ sys_log_error(ERR_RESYNC);
+ jack_dio_error = 0;
+ }
+ if (jack_filled >= jack_out_max) return SENDDACS_NO;
+ /* tb: wait in the scheduler */
+/* pthread_cond_wait(&jack_sem,&jack_mutex); */
+ jack_started = 1;
+ float *fp = sys_soundout;
+ for (int j=0; j<sys_outchannels; j++) {
+ memcpy(jack_outbuf + j*BUF_JACK + jack_filled, fp, sys_dacblocksize*sizeof(float));
+ fp += sys_dacblocksize;
+ }
+ fp = sys_soundin;
+ for (int j=0; j<sys_inchannels; j++) {
+ memcpy(fp, jack_inbuf + j*BUF_JACK + jack_filled, sys_dacblocksize*sizeof(float));
+ fp += sys_dacblocksize;
+ }
+ int timenow = int(sys_getrealtime());
+ if (timenow-timeref > sys_sleepgrain*1e-6) rtnval = SENDDACS_SLEPT;
+ memset(sys_soundout,0,sys_dacblocksize*sizeof(float)*sys_outchannels);
+ jack_filled += sys_dacblocksize;
+ return rtnval;
+}
+
+void jack_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) {
+ *canmulti = 0; /* supports multiple devices */
+ int ndev = 1;
+ for (int i=0; i<ndev; i++) {
+ sprintf( indevlist + i * devdescsize, "JACK");
+ sprintf(outdevlist + i * devdescsize, "JACK");
+ }
+ *nindevs = *noutdevs = ndev;
+}
+
+void jack_listdevs () {error("device listing not implemented for jack yet");}
+
+static const char ** jack_in_connections[NUM_JACK_PORTS]; /* ports connected to the inputs */
+static const char **jack_out_connections[NUM_JACK_PORTS]; /* ports connected to the outputs */
+
+/* tb: save the current state of pd's jack connections */
+t_int jack_save_connection_state(t_int* dummy) {
+ if (jack_ignore_graph_callback) return 0;
+ for (int i=0; i<NUM_JACK_PORTS; i++) {
+ /* saving the inputs connections */
+ if ( jack_in_connections[i]) free( jack_in_connections[i]);
+ jack_in_connections[i] = i< sys_inchannels ? jack_port_get_all_connections(jack_client, input_port[i]) : 0;
+ /* saving the outputs connections */
+ if (jack_out_connections[i]) free(jack_out_connections[i]);
+ jack_out_connections[i]= i<sys_outchannels ? jack_port_get_all_connections(jack_client, output_port[i]) : 0;
+ }
+ return 0;
+}
+
+/* todo: don't try to connect twice if we're both input and output host */
+static void jack_restore_connection_state() {
+ post("restoring connections");
+ for (int i=0; i<NUM_JACK_PORTS; i++) {
+ /* restoring the inputs connections */
+ if (jack_in_connections[i]) {
+ for (int j=0;;j++) {
+ const char *port = jack_in_connections[i][j];
+ if (!port) break; /* we've connected all incoming ports */
+ int status = jack_connect(jack_client, port, jack_port_name(input_port[i]));
+ if (status) error("cannot connect input ports %s -> %s", port, jack_port_name (input_port[i]));
+ }
+ }
+ /* restoring the output connections */
+ if (jack_out_connections[i]) {
+ for (int j=0;;j++) {
+ const char *port = jack_out_connections[i][j];
+ if (!port) break; /* we've connected all outgoing ports */
+ int status = jack_connect(jack_client, jack_port_name(output_port[i]), port);
+ if (status) error("cannot connect output ports %s -> %s", jack_port_name(output_port[i]), port);
+ }
+ }
+ }
+}
+
+struct t_audioapi jack_api = {
+ 0 /*jack_open_audio*/,
+ jack_close_audio,
+ jack_send_dacs,
+ jack_getdevs,
+};