/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright © Grame 2003 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. Grame Research Laboratory, 9, rue du Garet 69001 Lyon - France grame@rd.grame.fr 02-09-03 : Modify jack port naming : add the name of the used driver Add the -n option to load a specific driver using it's name 04-09-03 : Correct bug in -n option management : input and ouput have to be treated separately 08-09-03 : More robust driver loading code : new portaudio_load_default and portaudio_load_driver functions. 24-09-03 : Does not tries to load default device if the required one is not found, returns and error. 14-10-03 : After jack port name size extension, does not use fixed length for CoreAudio driver name anymore 09-01-04 : Handle different value for channel in and channel out (using -i and -o) 12-01-04 : Connect port names (inverse "in" and "out") 13-01-04 : Correct the length of jack port : use JACK_PORT_NAME_SIZE 22-03-04 : Remove jack_init_time, rename input and ouput ports using "capture" and "playback" 10-11-04 : S. Letz: Add management of -I option for use with JackPilot. 17-11-04 : S. Letz: Better debug code. 03-02-05 : S. Letz: fix several driver detection bugs on OSX. 06-08-05 : S.Letz: Remove the "-I" parameter, change the semantic of "-n" parameter on OSX : -n (driver name) now correctly uses the PropertyDeviceUID (persistent accross reboot...) as the identifier for the used coreaudio driver. */ #include #include #include #include #include #include "portaudio_driver.h" #ifdef JACK_USE_MACH_THREADS #include #include static OSStatus get_device_name_from_id(AudioDeviceID id, char name[60]) { UInt32 size = sizeof(char) * 60; OSStatus stat = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, &name[0]); return stat; } static OSStatus get_device_id_from_num(int i, AudioDeviceID * id) { OSStatus theStatus; UInt32 theSize; int nDevices; AudioDeviceID *theDeviceList; theStatus = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &theSize, NULL); nDevices = theSize / sizeof(AudioDeviceID); theDeviceList = (AudioDeviceID *) malloc(nDevices * sizeof(AudioDeviceID)); theStatus = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &theSize, theDeviceList); *id = theDeviceList[i]; return theStatus; } static OSStatus get_device_id_from_uid(char* UID, AudioDeviceID* id) { UInt32 size = sizeof(AudioValueTranslation); CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; if (inIUD == NULL) { return kAudioHardwareUnspecifiedError; } else { OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); CFRelease(inIUD); return res; } } #else typedef unsigned int AudioDeviceID; #endif #define PRINTDEBUG 1 void PALog(char *fmt, ...) { #ifdef PRINTDEBUG va_list ap; va_start(ap, fmt); fprintf(stderr, "JCA: "); vfprintf(stderr, fmt, ap); va_end(ap); #endif } static int paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) { portaudio_driver_t * driver = (portaudio_driver_t*)userData; driver->inPortAudio = (float*)inputBuffer; driver->outPortAudio = (float*)outputBuffer; driver->last_wait_ust = jack_get_microseconds(); return driver->engine->run_cycle(driver->engine, framesPerBuffer, 0); } static int portaudio_driver_attach (portaudio_driver_t *driver, jack_engine_t *engine) { jack_port_t *port; int port_flags; channel_t chn; char buf[JACK_PORT_NAME_SIZE]; driver->engine = engine; driver->engine->set_buffer_size (engine, driver->frames_per_cycle); driver->engine->set_sample_rate (engine, driver->frame_rate); port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; /* if (driver->has_hw_monitoring) { port_flags |= JackPortCanMonitor; } */ for (chn = 0; chn < driver->capture_nchannels; chn++) { //snprintf (buf, sizeof(buf) - 1, "%s:capture%lu", driver->driver_name, chn+1); snprintf (buf, sizeof(buf) - 1, "%s:out%lu", driver->driver_name, chn+1); if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { jack_error ("portaudio: cannot register port for %s", buf); break; } /* XXX fix this so that it can handle: systemic (external) latency */ jack_port_set_latency (port, driver->frames_per_cycle); driver->capture_ports = jack_slist_append (driver->capture_ports, port); } port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; for (chn = 0; chn < driver->playback_nchannels; chn++) { //snprintf (buf, sizeof(buf) - 1, "%s:playback%lu", driver->driver_name, chn+1); snprintf (buf, sizeof(buf) - 1, "%s:in%lu", driver->driver_name, chn+1); if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { jack_error ("portaudio: cannot register port for %s", buf); break; } /* XXX fix this so that it can handle: systemic (external) latency */ jack_port_set_latency (port, driver->frames_per_cycle); driver->playback_ports = jack_slist_append (driver->playback_ports, port); } jack_activate (driver->client); return 0; } static int portaudio_driver_detach (portaudio_driver_t *driver, jack_engine_t *engine) { JSList *node; if (driver->engine == 0) { return -1; } for (node = driver->capture_ports; node; node = jack_slist_next (node)) { jack_port_unregister (driver->client, ((jack_port_t *) node->data)); } jack_slist_free (driver->capture_ports); driver->capture_ports = 0; for (node = driver->playback_ports; node; node = jack_slist_next (node)) { jack_port_unregister (driver->client, ((jack_port_t *) node->data)); } jack_slist_free (driver->playback_ports); driver->playback_ports = 0; driver->engine = 0; return 0; } static int portaudio_driver_null_cycle (portaudio_driver_t* driver, jack_nframes_t nframes) { memset(driver->outPortAudio, 0, (driver->playback_nchannels * nframes * sizeof(float))); return 0; } static int portaudio_driver_read (portaudio_driver_t *driver, jack_nframes_t nframes) { jack_default_audio_sample_t *buf; channel_t chn; jack_port_t *port; JSList *node; int i; for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { port = (jack_port_t *)node->data; if (jack_port_connected (port) && (driver->inPortAudio != NULL)) { int channels = driver->capture_nchannels; float* in = driver->inPortAudio; buf = jack_port_get_buffer (port, nframes); for (i = 0; i< nframes; i++) buf[i] = in[channels*i+chn]; } } driver->engine->transport_cycle_start (driver->engine, jack_get_microseconds ()); return 0; } static int portaudio_driver_write (portaudio_driver_t *driver, jack_nframes_t nframes) { jack_default_audio_sample_t *buf; channel_t chn; jack_port_t *port; JSList *node; int i,bytes = nframes*sizeof(float); /* Clear in case of nothing is connected */ memset(driver->outPortAudio, 0, driver->playback_nchannels*bytes); for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { port = (jack_port_t *)node->data; if (jack_port_connected (port) && (driver->outPortAudio != NULL)) { int channels = driver->playback_nchannels; float* out = driver->outPortAudio; buf = jack_port_get_buffer (port, nframes); for (i = 0; i< nframes; i++) out[channels*i+chn] = buf[i]; } } return 0; } static int portaudio_driver_audio_start (portaudio_driver_t *driver) { PaError err = Pa_StartStream(driver->stream); return (err != paNoError) ? -1 : 0; } static int portaudio_driver_audio_stop (portaudio_driver_t *driver) { PaError err = Pa_StopStream(driver->stream); return (err != paNoError) ? -1 : 0; } static int portaudio_driver_set_parameters (portaudio_driver_t* driver, jack_nframes_t nframes, jack_nframes_t rate) { int capturing = driver->capturing; int playing = driver->playing; int err = Pa_OpenStream( &driver->stream, ((capturing) ? Pa_GetDefaultInputDeviceID() : paNoDevice), ((capturing) ? driver->capture_nchannels : 0), paFloat32, /* 32-bit float input */ NULL, ((playing) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), ((playing) ? driver->playback_nchannels : 0), paFloat32, /* 32-bit float output */ NULL, rate, /* sample rate */ nframes, /* frames per buffer */ 0, /* number of buffers = default min */ paClipOff, /* we won't output out of * range samples so don't * bother clipping them */ paCallback, driver); if (err == paNoError) { driver->period_usecs = (((float) driver->frames_per_cycle) / driver->frame_rate) * 1000000.0f; driver->frame_rate = rate; driver->frames_per_cycle = nframes; /* tell engine about buffer size */ if (driver->engine) { driver->engine->set_buffer_size ( driver->engine, driver->frames_per_cycle); } return 0; } else { // JOQ: this driver is dead. How do we terminate it? Pa_Terminate(); jack_error("Unable to set portaudio parameters"); jack_error("Error number: %d", err); jack_error("Error message: %s", Pa_GetErrorText(err)); return EIO; } } static int portaudio_driver_reset_parameters (portaudio_driver_t* driver, jack_nframes_t nframes, jack_nframes_t rate) { if (!jack_power_of_two(nframes)) { jack_error("PA: frames must be a power of two " "(64, 512, 1024, ...)"); return EINVAL; } Pa_CloseStream(driver->stream); return portaudio_driver_set_parameters (driver, nframes, rate); } static int portaudio_driver_bufsize (portaudio_driver_t* driver, jack_nframes_t nframes) { int rc; /* This gets called from the engine server thread, so it must * be serialized with the driver thread. Stopping the audio * also stops that thread. */ if (portaudio_driver_audio_stop (driver)) { jack_error ("PA: cannot stop to set buffer size"); return EIO; } rc = portaudio_driver_reset_parameters (driver, nframes, driver->frame_rate); if (portaudio_driver_audio_start (driver)) { jack_error ("PA: cannot restart after setting buffer size"); rc = EIO; } return rc; } //== instance creation/destruction ============================================= static int portaudio_load_default (portaudio_driver_t *driver, int numDevices, int capturing, int playing, int* inputDeviceID, int* outputDeviceID) { const PaDeviceInfo *pdi; int i,j; int found = 0; PALog("Look for default driver\n"); *inputDeviceID = Pa_GetDefaultInputDeviceID(); *outputDeviceID = Pa_GetDefaultOutputDeviceID(); for(i=0; icapture_nchannels = (capturing) ? pdi->maxInputChannels : 0; strcpy (driver->driver_name,pdi->name); found = 1; } if (i == Pa_GetDefaultOutputDeviceID()){ driver->playback_nchannels = (playing) ? pdi->maxOutputChannels : 0; strcpy (driver->driver_name,pdi->name); found = 1; } PALog("\nName = %s\n", pdi->name); PALog("Max Inputs = %d ", pdi->maxInputChannels); PALog("Max Outputs = %d\n", pdi->maxOutputChannels); if( pdi->numSampleRates == -1 ){ PALog("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1]); }else{ PALog("Sample Rates ="); for(j=0; jnumSampleRates; j++){ PALog(" %8.2f,", pdi->sampleRates[j]); } PALog("\n"); } PALog("Native Sample Formats = "); if (pdi->nativeSampleFormats & paInt8) PALog("paInt8, "); if (pdi->nativeSampleFormats & paUInt8) PALog("paUInt8, "); if (pdi->nativeSampleFormats & paInt16) PALog("paInt16, "); if (pdi->nativeSampleFormats & paInt32) PALog("paInt32, "); if (pdi->nativeSampleFormats & paFloat32) PALog("paFloat32, "); if (pdi->nativeSampleFormats & paInt24) PALog("paInt24, "); if (pdi->nativeSampleFormats & paPackedInt24) PALog("paPackedInt24, "); PALog("\n"); } return found; } static int portaudio_load_driver (portaudio_driver_t *driver, int numDevices, int capturing, int playing, int* inputDeviceID, int* outputDeviceID, char* driver_name) { const PaDeviceInfo *pdi; int found = 0; int i,j; PALog("Look for %s driver\n",driver_name); for(i=0; iname, JACK_DRIVER_PARAM_STRING_MAX) == 0) { if (pdi->maxInputChannels > 0) { *inputDeviceID = i; driver->capture_nchannels = (capturing) ? pdi->maxInputChannels : 0; strcpy(driver->driver_name,pdi->name); PALog("Found input driver = %s\n", driver_name); found = 1; } if (pdi->maxOutputChannels > 0) { *outputDeviceID = i; driver->playback_nchannels = (playing) ? pdi->maxOutputChannels : 0; strcpy (driver->driver_name,pdi->name); PALog("Found output driver = %s\n", driver_name); found = 1; } else { PALog("Found driver without input or ouput = %s\n", driver_name); } } PALog("\nName = %s\n", pdi->name); PALog("Max Inputs = %d ", pdi->maxInputChannels); PALog("Max Outputs = %d\n", pdi->maxOutputChannels); if( pdi->numSampleRates == -1 ){ PALog("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1]); }else{ PALog("Sample Rates ="); for(j=0; jnumSampleRates; j++){ PALog(" %8.2f,", pdi->sampleRates[j]); } PALog("\n"); } PALog("Native Sample Formats = "); if (pdi->nativeSampleFormats & paInt8) PALog("paInt8, "); if (pdi->nativeSampleFormats & paUInt8) PALog("paUInt8, "); if (pdi->nativeSampleFormats & paInt16) PALog("paInt16, "); if (pdi->nativeSampleFormats & paInt32) PALog("paInt32, "); if (pdi->nativeSampleFormats & paFloat32) PALog("paFloat32, "); if (pdi->nativeSampleFormats & paInt24) PALog("paInt24, "); if (pdi->nativeSampleFormats & paPackedInt24) PALog("paPackedInt24, "); PALog("\n"); } return found; } /** create a new driver instance */ static jack_driver_t * portaudio_driver_new (char *name, jack_client_t* client, jack_nframes_t frames_per_cycle, jack_nframes_t rate, int capturing, int playing, int chan_in, int chan_out, DitherAlgorithm dither, char* driver_name) { portaudio_driver_t *driver; PaError err = paNoError; int numDevices; int inputDeviceID,outputDeviceID; int found; PALog("portaudio driver version : %d\n", kVersion); PALog("creating portaudio driver ... %" PRIu32 "|%" PRIu32 "\n", frames_per_cycle, rate); driver = (portaudio_driver_t *) calloc (1, sizeof (portaudio_driver_t)); jack_driver_init ((jack_driver_t *) driver); if (!jack_power_of_two(frames_per_cycle)) { jack_error ("PA: -p must be a power of two."); goto error; } driver->frames_per_cycle = frames_per_cycle; driver->frame_rate = rate; driver->capturing = capturing; driver->playing = playing; driver->attach = (JackDriverAttachFunction) portaudio_driver_attach; driver->detach = (JackDriverDetachFunction) portaudio_driver_detach; driver->read = (JackDriverReadFunction) portaudio_driver_read; driver->write = (JackDriverReadFunction) portaudio_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) portaudio_driver_null_cycle; driver->bufsize = (JackDriverBufSizeFunction) portaudio_driver_bufsize; driver->start = (JackDriverStartFunction) portaudio_driver_audio_start; driver->stop = (JackDriverStopFunction) portaudio_driver_audio_stop; driver->stream = NULL; #ifdef JACK_USE_MACH_THREADS AudioDeviceID device_id; if (driver_name) { if (get_device_id_from_uid(driver_name, &device_id) != noErr) goto error; if (get_device_name_from_id(device_id, driver->driver_name) != noErr) goto error; } else { if (get_device_id_from_num(0, &device_id) != noErr) goto error; if (get_device_name_from_id(device_id, driver->driver_name) != noErr) goto error; } #endif err = Pa_Initialize(); PALog("Pa_Initialize OK \n"); PALog("Driver name required %s\n",driver->driver_name); numDevices = Pa_CountDevices(); if( numDevices < 0 ){ PALog("ERROR: Pa_CountDevices returned 0x%x\n", numDevices); err = numDevices; goto error; } PALog("Number of devices = %d\n", numDevices); if (strcmp(driver->driver_name,"") == 0) { found = portaudio_load_default(driver,numDevices,capturing,playing,&inputDeviceID,&outputDeviceID); if (!found) { PALog("ERROR : default driver has not been found\n"); err = paHostError; goto error; } }else{ found = portaudio_load_driver(driver,numDevices,capturing,playing,&inputDeviceID,&outputDeviceID,driver->driver_name); if (!found) { PALog("ERROR : driver %s has not been found \n",driver->driver_name); err = paHostError; goto error; } } if (err != paNoError) goto error; PALog("Pa_GetDefaultOutputDeviceID() %ld\n", (long)Pa_GetDefaultOutputDeviceID()); PALog("Pa_GetDefaultInputDeviceID() %ld\n", (long)Pa_GetDefaultInputDeviceID()); PALog("--------------------------------------------------\n"); PALog("CoreAudio driver %s will be loaded\n", driver->driver_name); PALog("inputDeviceID %ld\n", (long)inputDeviceID); PALog("outputDeviceID %ld\n", (long)outputDeviceID); PALog("driver->capture_nchannels %ld\n", driver->capture_nchannels); PALog("driver->playback_nchannels %ld\n", driver->playback_nchannels); PALog("chan_in, chan_out %ld %ld\n", (long)chan_in, (long)chan_out); if (chan_in > 0) driver->capture_nchannels = (driver->capture_nchannels < chan_in) ? driver->capture_nchannels : chan_in; if (chan_out > 0) driver->playback_nchannels = (driver->playback_nchannels < chan_out) ? driver->playback_nchannels : chan_out; PALog("driver->capture_nchannels %ld\n", driver->capture_nchannels); PALog("driver->playback_nchannels %ld\n", driver->playback_nchannels); err = Pa_OpenStream(&driver->stream, ((capturing && (driver->capture_nchannels > 0)) ? inputDeviceID : paNoDevice), ((capturing) ? driver->capture_nchannels : 0), paFloat32, // 32 bit floating point input NULL, ((playing && (driver->playback_nchannels > 0)) ? outputDeviceID : paNoDevice), ((playing) ? driver->playback_nchannels : 0), paFloat32, // 32 bit floating point output NULL, rate, frames_per_cycle, // frames per buffer 0, // number of buffers, if zero then use default minimum paClipOff, // we won't output out of range samples so don't bother clipping them paCallback, driver); if (err != paNoError) goto error; driver->client = client; driver->period_usecs = (((float) driver->frames_per_cycle) / driver->frame_rate) * 1000000.0f; return((jack_driver_t *) driver); error: Pa_Terminate(); jack_error("An error occured while using the portaudio stream"); jack_error("Error number: %d", err); jack_error("Error message: %s", Pa_GetErrorText(err)); free(driver); return NULL; } /** free all memory allocated by a driver instance */ static void portaudio_driver_delete (portaudio_driver_t *driver) { /* Close PortAudio stream and terminate */ Pa_CloseStream(driver->stream); Pa_Terminate(); free(driver); } //== driver "plugin" interface ================================================= /* DRIVER "PLUGIN" INTERFACE */ jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; unsigned int i; desc = calloc (1, sizeof (jack_driver_desc_t)); strcpy (desc->name, "portaudio"); desc->nparams = 10; desc->params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); i = 0; strcpy (desc->params[i].name, "channel"); desc->params[i].character = 'c'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.ui = 0; strcpy (desc->params[i].short_desc, "Maximum number of channels"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "channelin"); desc->params[i].character = 'i'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.ui = 0; strcpy (desc->params[i].short_desc, "Maximum number of input channels"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "channelout"); desc->params[i].character = 'o'; desc->params[i].type = JackDriverParamInt; desc->params[i].value.ui = 0; strcpy (desc->params[i].short_desc, "Maximum number of ouput channels"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "capture"); desc->params[i].character = 'C'; desc->params[i].type = JackDriverParamBool; desc->params[i].value.i = TRUE; strcpy (desc->params[i].short_desc, "Whether or not to capture"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "playback"); desc->params[i].character = 'P'; desc->params[i].type = JackDriverParamBool; desc->params[i].value.i = TRUE; strcpy (desc->params[i].short_desc, "Whether or not to playback"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "duplex"); desc->params[i].character = 'D'; desc->params[i].type = JackDriverParamBool; desc->params[i].value.i = TRUE; strcpy (desc->params[i].short_desc, "Capture and playback"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "rate"); desc->params[i].character = 'r'; desc->params[i].type = JackDriverParamUInt; desc->params[i].value.ui = 48000U; strcpy (desc->params[i].short_desc, "Sample rate"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "period"); desc->params[i].character = 'p'; desc->params[i].type = JackDriverParamUInt; desc->params[i].value.ui = 1024U; strcpy (desc->params[i].short_desc, "Frames per period"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "name"); desc->params[i].character = 'n'; desc->params[i].type = JackDriverParamString; desc->params[i].value.ui = 128U; strcpy (desc->params[i].short_desc, "Driver name"); strcpy (desc->params[i].long_desc, desc->params[i].short_desc); i++; strcpy (desc->params[i].name, "dither"); desc->params[i].character = 'z'; desc->params[i].type = JackDriverParamChar; desc->params[i].value.c = '-'; strcpy (desc->params[i].short_desc, "Dithering mode"); strcpy (desc->params[i].long_desc, " Dithering Mode:\n" " r : rectangular\n" " t : triangular\n" " s : shaped\n" " - : no dithering"); return desc; } const char driver_client_name[] = "portaudio"; jack_driver_t * driver_initialize (jack_client_t *client, const JSList * params) { jack_nframes_t srate = 48000; jack_nframes_t frames_per_interrupt = 1024; AudioDeviceID deviceID = 0; int capture = FALSE; int playback = FALSE; int chan_in = -1; int chan_out = -1; DitherAlgorithm dither = None; const JSList * node; const jack_driver_param_t * param; char *name = NULL; #ifdef JACK_USE_MACH_THREADS get_device_id_from_num(0,&deviceID); // takes a default value (first device) #endif for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'n': name = (char *) param->value.str; PALog("Driver name found %s\n",name); break; case 'D': capture = TRUE; playback = TRUE; break; case 'c': chan_in = (int) param->value.ui; chan_out = (int) param->value.ui; break; case 'i': chan_in = (int) param->value.ui; break; case 'o': chan_out = (int) param->value.ui; break; case 'C': capture = param->value.i; break; case 'P': playback = param->value.i; break; case 'r': srate = param->value.ui; break; case 'p': frames_per_interrupt = (unsigned int) param->value.ui; break; case 'z': switch ((int) param->value.c) { case '-': dither = None; break; case 'r': dither = Rectangular; break; case 's': dither = Shaped; break; case 't': default: dither = Triangular; break; } break; } } /* duplex is the default */ if (!capture && !playback) { capture = TRUE; playback = TRUE; } return portaudio_driver_new ("portaudio", client, frames_per_interrupt, srate, capture, playback, chan_in, chan_out, dither, name); } void driver_finish (jack_driver_t *driver) { portaudio_driver_delete ((portaudio_driver_t *) driver); }