diff --git a/drivers/portaudio/portaudio_driver.c b/drivers/portaudio/portaudio_driver.c new file mode 100644 index 0000000..a99050a --- /dev/null +++ b/drivers/portaudio/portaudio_driver.c @@ -0,0 +1,464 @@ +/* + 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 + +*/ + +#include +#include +#include "portaudio_driver.h" + +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; + + 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[32]; + + 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, "capture_%lu", 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, "playback_%lu", 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 jack_nframes_t +portaudio_driver_wait (portaudio_driver_t *driver, int extra_fd, int *status, float *delayed_usecs) +{ + *status = 0; + *delayed_usecs = 0; + return driver->frames_per_cycle; +} + +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)) { + buf = jack_port_get_buffer (port, nframes); + int channels = driver->capture_nchannels; + float* in = driver->inPortAudio; + for (i = 0; i< nframes; i++) buf[i] = in[channels*i+chn]; + } + + } + + driver->engine->control->current_time.usecs = 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; + + /* Clear in case of nothing is connected */ + memset(driver->outPortAudio, 0, (driver->playback_nchannels * nframes * sizeof(float))); + + 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)) { + buf = jack_port_get_buffer (port, nframes); + int channels = driver->playback_nchannels; + float* out = driver->outPortAudio; + 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; +} + +//== instance creation/destruction ============================================= + +/** 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, + DitherAlgorithm dither) +{ + portaudio_driver_t *driver; + PaError err; + const PaDeviceInfo *pdi; + int numDevices; + int i,j; + + printf ("creating portaudio driver ... %lu|%lu|%lu\n", frames_per_cycle, rate); + + driver = (portaudio_driver_t *) calloc (1, sizeof (portaudio_driver_t)); + + jack_driver_init ((jack_driver_t *) driver); + + driver->frame_rate = rate; + driver->frames_per_cycle = frames_per_cycle; + + driver->attach = (JackDriverAttachFunction) portaudio_driver_attach; + driver->detach = (JackDriverDetachFunction) portaudio_driver_detach; + driver->wait = (JackDriverWaitFunction) portaudio_driver_wait; + driver->read = (JackDriverReadFunction) portaudio_driver_read; + driver->write = (JackDriverReadFunction) portaudio_driver_write; + driver->null_cycle = (JackDriverNullCycleFunction) portaudio_driver_null_cycle; + driver->start = (JackDriverStartFunction) portaudio_driver_audio_start; + driver->stop = (JackDriverStopFunction) portaudio_driver_audio_stop; + + err = Pa_Initialize(); + numDevices = Pa_CountDevices(); + + if( numDevices < 0 ) + { + printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + + printf("Number of devices = %d\n", numDevices ); + + for( i=0; icapture_nchannels = (capturing) ? pdi->maxInputChannels : 0; + } + if( i == Pa_GetDefaultOutputDeviceID() ){ + driver->playback_nchannels = (playing) ? pdi->maxOutputChannels : 0; + } + printf("\nName = %s\n", pdi->name); + printf("Max Inputs = %d ", pdi->maxInputChannels); + printf("Max Outputs = %d\n", pdi->maxOutputChannels); + if( pdi->numSampleRates == -1 ){ + printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] ); + }else{ + printf("Sample Rates ="); + for(j=0; jnumSampleRates; j++){ + printf(" %8.2f,", pdi->sampleRates[j] ); + } + printf("\n"); + } + + printf("Native Sample Formats = "); + if( pdi->nativeSampleFormats & paInt8 ) printf("paInt8, "); + if( pdi->nativeSampleFormats & paUInt8 ) printf("paUInt8, "); + if( pdi->nativeSampleFormats & paInt16 ) printf("paInt16, "); + if( pdi->nativeSampleFormats & paInt32 ) printf("paInt32, "); + if( pdi->nativeSampleFormats & paFloat32 ) printf("paFloat32, "); + if( pdi->nativeSampleFormats & paInt24 ) printf("paInt24, "); + if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, "); + printf("\n"); + } + + if(err != paNoError) goto error; + + printf("Pa_GetDefaultOutputDeviceID() %ld\n", Pa_GetDefaultOutputDeviceID()); + printf("Pa_GetDefaultInputDeviceID() %ld\n", Pa_GetDefaultInputDeviceID()); + + if (chan > 0) { + driver->capture_nchannels = (driver->capture_nchannels < chan) ? driver->capture_nchannels : chan; + driver->playback_nchannels = (driver->playback_nchannels < chan) ? driver->playback_nchannels : chan; + } + + err = Pa_OpenStream(&driver->stream, + ((capturing) ? Pa_GetDefaultInputDeviceID() : paNoDevice), + ((capturing) ? driver->capture_nchannels : 0), + paFloat32, /* 32 bit floating point input */ + NULL, + ((playing) ? Pa_GetDefaultOutputDeviceID() : 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; + + jack_init_time(); + + return((jack_driver_t *) driver); + +error: + + Pa_Terminate(); + fprintf(stderr, "An error occured while using the portaudio stream\n"); + fprintf(stderr, "Error number: %d\n", err); + fprintf(stderr, "Error message: %s\n", 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 */ + +const char driver_client_name[] = "portaudio"; + +static void +portaudio_usage () +{ + fprintf (stderr, "\n" + + "portaudio PCM driver args:\n" + " -r sample-rate (default: 44.1kHz)\n" + " -c chan (default: harware)\n" + " -p frames-per-period (default: 128)\n" + " -D (duplex, default: yes)\n" + " -C (capture, default: duplex)\n" + " -P (playback, default: duplex)\n" + " -z[r|t|s|-] (dither, rect|tri|shaped|off, default: off)\n" + ); +} + +jack_driver_t * +driver_initialize (jack_client_t *client, int argc, char **argv) +{ + jack_nframes_t srate = 44100; + jack_nframes_t frames_per_interrupt = 128; + int capture = FALSE; + int playback = FALSE; + int chan = -1; + DitherAlgorithm dither = None; + int i; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + + switch (argv[i][1]) { + + case 'D': + capture = TRUE; + playback = TRUE; + break; + + case 'c': + chan = atoi (argv[i+1]); + i++; + break; + + case 'C': + capture = TRUE; + break; + + case 'P': + playback = TRUE; + break; + + case 'r': + srate = atoi (argv[i+1]); + i++; + break; + + case 'p': + frames_per_interrupt = atoi (argv[i+1]); + i++; + break; + + case 'z': + switch (argv[i][2]) { + case '-': + dither = None; + break; + + case 'r': + dither = Rectangular; + break; + + case 's': + dither = Shaped; + break; + + case 't': + default: + dither = Triangular; + break; + } + break; + + default: + portaudio_usage (); + return NULL; + } + } else { + portaudio_usage (); + return NULL; + } + } + + /* duplex is the default */ + + if (!capture && !playback) { + capture = TRUE; + playback = TRUE; + } + + return portaudio_driver_new ("portaudio", client, frames_per_interrupt, srate, capture, playback, chan, dither); +} + +void +driver_finish (jack_driver_t *driver) +{ + portaudio_driver_delete ((portaudio_driver_t *) driver); +} + diff --git a/drivers/portaudio/portaudio_driver.h b/drivers/portaudio/portaudio_driver.h new file mode 100644 index 0000000..0ae2cba --- /dev/null +++ b/drivers/portaudio/portaudio_driver.h @@ -0,0 +1,58 @@ +/* + 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 +*/ + +#ifndef __jack_portaudio_driver_h__ +#define __jack_portaudio_driver_h__ + +#include +#include +#include +#include +#include +#include + +#include + +typedef struct { + + JACK_DRIVER_DECL + + struct _jack_engine *engine; + + jack_nframes_t frame_rate; + jack_nframes_t frames_per_cycle; + unsigned long user_nperiods; + + channel_t playback_nchannels; + channel_t capture_nchannels; + + jack_client_t *client; + JSList *capture_ports; + JSList *playback_ports; + + float *inPortAudio; + float *outPortAudio; + + PortAudioStream* stream; + +} portaudio_driver_t; + +#endif /* __jack_portaudio_driver_h__ */