/* TO TO: - do asys_isetup and asys_iosetup. - FIgure out why 2-channel output is working but sounds totally fucked up. - buffer size change: jack_get_buffer_size() can be called ONLY before client activation to find out current max buffer size. While running, if jack changes buffer size, it calls function registered with jack_set_buffer_size_callback. On the sfront side: asys_orun's 2nd argument is a pointer to long whose value represents the max number of sample periods available. Hmm, I'm not sure I need to register a function with jack; looks like sfront can handle any buffer size at any time (as long as it is a multiple of the number of channels). */ /* # Sfront, a SAOL to C translator # This file: jackd audio driver for sfront # copyright (c) 2002 Paul M. Winkler, Brooklyn, NY # www.slinkp.com # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License (Version 2) as # published by the Free Software Foundation. # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Maintainer: Paul Winkler www.slinkp.com */ /* includes needed for JACK */ #include #include #include #include /* include */ /* defines needed by sfront */ #define ASYSN_JACK_DEBUG 1 /* debugging printouts */ #define ASYSN_JACK_SLEEPMS 5 /* exit interval */ /* declarations needed for jack */ jack_port_t *asysn_jack_input_port[ASYS_ICHAN] ; jack_port_t *asysn_jack_output_port[ASYS_OCHAN]; jack_client_t *asysn_jack_client; /* declarations needed for sfront */ volatile int asysn_jack_proc_status; /* used to signal status */ /* my own stuff */ char asysn_jack_client_name[256] = "SAOL Test Client"; int asysn_jack_srate_was_called = 0; int asysn_jack_oports_connected = 0; int asysn_jack_iports_connected = 0; void asysn_jack_print_usage(void); void asysn_jack_print_usage(void) { fprintf(stderr, "Usage: ... write useful blahblah here\n"); } /* callback functions used by jack in various scenarios */ /** OUTPUT ONLY **/ #if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT) int asysn_jack_process (jack_nframes_t nframes, void * arg ) /* cast arg to pointer to whatever I need. */ { /* OUTPUT ONLY */ /* vars. copied from portaudio driver.... but this is my jack process() function, which only gets a nframes & a pointer-to-void argument. */ long nsamples = (long) nframes * ASYS_OCHAN; long oremaining = nsamples ; long optr = 0; long osize; jack_default_audio_sample_t* obuf[ASYS_OCHAN]; ASYS_OTYPE* tempbuf; int i, j; if (asysn_jack_srate_was_called >1) { /* force jack to remove this client as per PBD message on jackit-devel on 4/24/02 (thread: "stereo?") */ printf("uh-oh... called srate again, and we can't handle it\n"); return 1; } /* printf("calling process...\n"); */ /* Get memory buffers associated with the jack output ports. */ for (i=0; i< ASYS_OCHAN; i++) { obuf[i] = (jack_default_audio_sample_t *) jack_port_get_buffer(asysn_jack_output_port[i], nframes); } /* sfront wants one interleaved buffer, so we need a separate one for that */ tempbuf = (ASYS_OTYPE *) calloc(nsamples, sizeof(ASYS_OTYPE)); /* Keep running while result is not ASYS_EXIT */ while ((asysn_jack_proc_status == ASYS_DONE) && (oremaining > 0)) { osize = oremaining; /* WHITE NOISE TEST */ /* output should be random between -0.5 and 0.5 */ /* for(; optr < osize; optr++) { tempbuf[optr]= ((float) rand()) / RAND_MAX - 0.5; } */ /* asys_orun is defined by sfront */ /* According to the sfront manual, it "writes at most */ /* the next *osize channel-interleaved sample values */ /* into the buffer". */ /* remember that asys_orun will leave osize set to the actual number of samples that it wrote! */ asysn_jack_proc_status =asys_orun(tempbuf, &osize); oremaining -= osize; //if (oremaining == 0) { // printf("wrote: %d\n", osize); //} //else { //printf("remaining: %d written: %d\n", oremaining, osize); // we didn't write into the whole buffer! why not? // look at what portaudio driver does here. // I think it fills the rest with zeroes... //} /* DE-INTERLEAVE */ for(i=0; i < nsamples; i++) { // cast is probably a no-op but can't hurt. obuf[i%ASYS_OCHAN][i/ASYS_OCHAN] = (jack_default_audio_sample_t) tempbuf[i]; } free(tempbuf); } return 0; } #endif /** INPUT ONLY **/ #if defined(ASYS_HASINPUT) && !defined(ASYS_HASOUTPUT) int asysn_jack_process (jack_nframes_t nframes, void * arg) { /* INPUT ONLY */ printf("I"); return 0; } #endif /** INPUT AND OUTPUT **/ #if defined(ASYS_HASINPUT) && defined(ASYS_HASOUTPUT) int asysn_jack_process (jack_nframes_t nframes, void *arg) { /* INPUT AND OUTPUT*/ printf("IO"); return 0; } #endif /* FUNCTIONS to REGISTER WITH JACK */ int asysn_jack_bufsize(jack_nframes_t nframes, void *arg) { /* looks like this gets called once when we start up. */ printf("can I set buffer size in an sfront program?\n"); return 0; } int asysn_jack_srate (jack_nframes_t nframes, void *arg) { /* looks like this gets called once when we start up. */ printf("Can't adjust srate at runtime in an sfront program.\n"); // set a flag which will force us to be removed by jackd asysn_jack_srate_was_called++; return 1; } void asysn_jack_shutdown(void *arg) { // called if jack every shuts down or decides to stop me jack_client_close(asysn_jack_client); exit (1); } /* SFRONT INITIALIZATION FUNCTIONS */ /* These are called ONCE at program startup. */ /** OUTPUT ONLY **/ #if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT) int asys_osetup(long srate, long ochannels, long osample, char * oname, long toption) { char outchan_name[256]; int i, j; /* connect as client to jack server */ if ((asysn_jack_client = jack_client_new (asysn_jack_client_name)) == 0) { fprintf( stderr, "could not connect... jack not running?\n"); return ASYS_ERROR; } /* register asysn_jack_process() with jack. will be called whenever jack decides there's work to do. */ jack_set_process_callback(asysn_jack_client, asysn_jack_process, 0); /* register bufsize() with jack, to be called when max. nframes will change */ jack_set_buffer_size_callback(asysn_jack_client, asysn_jack_bufsize, 0); /* register srate() as function for jack to call when sampling rate changes */ jack_set_sample_rate_callback (asysn_jack_client, asysn_jack_srate, 0); /* register for anytime we get disconnected, i.e. jack shuts down or stops calling asysn_jack_client */ jack_on_shutdown(asysn_jack_client, asysn_jack_shutdown, 0); /* print sampling rate at startup */ printf("jack sampling rate: %lu\n", jack_get_sample_rate(asysn_jack_client)); printf("OK "); /* create ports */ printf("creating output port(s)\n"); for(i=0; i < ASYS_OCHAN; i++) { asysn_jack_output_port[i] = jack_port_register(asysn_jack_client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } /* tell jack asysn_jack_client is ready */ if (jack_activate (asysn_jack_client)) { fprintf(stderr, "can't activate client!\n"); return ASYS_ERROR; } else printf("client activated OK\n"); /* now we can connect ports */ /* if (jack_connect (asysn_jack_client, "alsa_pcm:in_1", */ /* jack_port_name(asysn_jack_input_port))) { */ /* fprintf(stderr, "can't get input ports!\n"); */ /* } */ /* PROCESS COMMAND-LINE ARGS */ for(i=1; i < asys_argc; i++) { /* connect to requested output ports */ if(! strcmp(asys_argv[i], "-asys_jack_out")) { if (((i + 1) >= asys_argc)) { // || (strncmp(asys_argv[i+1], "-", 1))) { asysn_jack_print_usage(); return ASYS_ERROR; } for(j=i+1; j < asys_argc && strncmp(asys_argv[j], "-", 1); j++) { if (j-i > ASYS_OCHAN) { fprintf(stderr, "Too many out channels requested! %s can only do %d\n", asys_argv[0], ASYS_OCHAN); return ASYS_ERROR; } if (jack_connect (asysn_jack_client, jack_port_name(asysn_jack_output_port[j-(i+1)]), asys_argv[j])) { fprintf(stderr, "can't get output ports!\n"); fprintf(stderr, "failed trying to connect output %d to %s\n", j - (i+1), asys_argv[j]); return ASYS_ERROR; } asysn_jack_oports_connected++; } } /* connect to requested input ports */ else if (! strcmp(asys_argv[i], "-asys_jack_in")) { } } /* connect remaining output ports to alsa client by default */ /* ... whoops, what if user explicitly used one already? Forget it. */ /* for(i=asysn_jack_oports_connected; i < ASYS_OCHAN; i++) { */ /* sprintf(outchan_name, "alsa_pcm:out_%d", i + 1); */ /* if (jack_connect (asysn_jack_client, */ /* jack_port_name(asysn_jack_output_port[i]), */ /* outchan_name)) { */ /* fprintf(stderr, "can't get output port %d!\n", i); */ /* return ASYS_ERROR; */ /* } */ /* } */ if (asysn_jack_oports_connected != ASYS_OCHAN) { fprintf(stderr, "Oops! You've connected %d out channels and you need %d.\n", asysn_jack_oports_connected, ASYS_OCHAN); return ASYS_ERROR; } printf("asys_osetup is all done!\n"); /* all is well */ return ASYS_DONE; } #endif /**************** END OF SETUP **************************/ /******* shutdowns called in various scenarios *******/ #if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT) void asys_oshutdown(void) { printf("running asys_oshutdown.\n"); }; #endif #if defined(ASYS_HASINPUT) && !defined(ASYS_HASOUTPUT) void asys_ishutdown(void) { printf("running asys_ishutdown.\n"); } #endif #if defined(ASYS_HASINPUT) && defined(ASYS_HASOUTPUT) void asys_ioshutdown(void) { printf("running asys_ioshutdown.\n"); } #endif /***** active audio main - works for all I/O types, I hope ************/ /* Called once by sfront app. *after* setup; when it terminates, we're done. and we run asys_[io]shutdown. */ void asys_main(void) { int i=0; printf("Started asys_main...\n"); asysn_jack_proc_status = ASYS_DONE; /* Run until ASYS_EXIT... */ while (asysn_jack_proc_status == ASYS_DONE) { /*printf("now in main loop %d\n", i); i++;*/ usleep(ASYSN_JACK_SLEEPMS * 1000); // doesn't seem to matter } jack_client_close(asysn_jack_client); /* return(0); */ }