From 904fba715518bcf9b20137240a4636537a93c288 Mon Sep 17 00:00:00 2001 From: Josh Green Date: Thu, 29 Jan 2009 09:21:34 +0000 Subject: [PATCH] Overhauled PortAudio driver for PortAudio V19 API. --- fluidsynth/ChangeLog | 6 + fluidsynth/configure.ac | 27 ++++ fluidsynth/src/Makefile.am | 9 +- fluidsynth/src/config.h.in | 3 + fluidsynth/src/fluid_adriver.c | 3 +- fluidsynth/src/fluid_portaudio.c | 236 ++++++++++++++++++++++--------- 6 files changed, 212 insertions(+), 72 deletions(-) diff --git a/fluidsynth/ChangeLog b/fluidsynth/ChangeLog index 64f77024..3d41f16a 100644 --- a/fluidsynth/ChangeLog +++ b/fluidsynth/ChangeLog @@ -1,3 +1,9 @@ +2009-01-29 Josh Green + * src/Makefile.am: Added PortAudio driver conditional build. + * src/fluid_adriver.c: Registered fluid_portaudio_driver_settings. + * src/fluid_portaudio.c: Completely overhauled for Portaudio 19. + This driver appears to have been unbuildable before. + 2009-01-08 Pedro Lopez-Cabanillas * configure.ac: detection of CoreMIDI support. Ticket #18. * src/Makefile.am: conditional build of CoreMIDI driver. diff --git a/fluidsynth/configure.ac b/fluidsynth/configure.ac index a89d90ef..cf9cc1b0 100644 --- a/fluidsynth/configure.ac +++ b/fluidsynth/configure.ac @@ -199,6 +199,27 @@ AC_SUBST(ALSA_CFLAGS) AC_SUBST(ALSA_LIBS) +dnl - Check support for PortAudio + +AC_ARG_ENABLE(portaudio-support, AS_HELP_STRING([--disable-portaudio-support], + [Do not compile PortAudio support (default=auto)]), + enable_portaudio_support=$enableval, enable_portaudio_support="yes") + +if test "x$enable_portaudio_support" != "xno"; then + PKG_CHECK_MODULES(PORTAUDIO, portaudio-2.0 >= 19, PORTAUDIO_SUPPORT=1, + PORTAUDIO_SUPPORT=0) +else + PORTAUDIO_SUPPORT=0 +fi + +if test "$PORTAUDIO_SUPPORT" = "1"; then + AC_DEFINE(PORTAUDIO_SUPPORT, 1, [Define to enable PortAudio driver]) +fi +AM_CONDITIONAL(PORTAUDIO_SUPPORT, test "$PORTAUDIO_SUPPORT" = "1") +AC_SUBST(PORTAUDIO_CFLAGS) +AC_SUBST(PORTAUDIO_LIBS) + + dnl - Check support for OSS audio AC_OSS_AUDIO AM_CONDITIONAL(OSS_SUPPORT, test "$OSS_SUPPORT" = "1") @@ -387,6 +408,12 @@ else echo "ALSA: no" fi +if test "${PORTAUDIO_SUPPORT}" = "1"; then + echo "PortAudio: yes" +else + echo "PortAudio: no" +fi + if test "${OSS_SUPPORT}" = "1"; then echo "OSS: yes" else diff --git a/fluidsynth/src/Makefile.am b/fluidsynth/src/Makefile.am index 4c08280e..7375d0a3 100644 --- a/fluidsynth/src/Makefile.am +++ b/fluidsynth/src/Makefile.am @@ -21,6 +21,10 @@ if JACK_SUPPORT fluid_jack = fluid_jack.c endif +if PORTAUDIO_SUPPORT +fluid_portaudio = fluid_portaudio.c +endif + if MINGW32_SUPPORT fluid_windows = fluid_dll.c fluid_dsound.c fluid_winmidi.c endif @@ -56,6 +60,7 @@ libfluidsynth_la_SOURCES = \ $(fluid_jack) \ $(fluid_lash) \ $(fluid_oss) \ + $(fluid_portaudio) \ $(fluid_pulse) \ $(fluid_windows) \ fluid_adriver.c \ @@ -115,11 +120,11 @@ libfluidsynth_la_SOURCES = \ fluid_aufile.c INCLUDES = -I$(top_srcdir)/include $(LASH_CFLAGS) $(LADCCA_CFLAGS) \ - $(READLINE_CFLAGS) $(JACK_CFLAGS) $(ALSA_CFLAGS) $(PULSE_CFLAGS) + $(READLINE_CFLAGS) $(JACK_CFLAGS) $(ALSA_CFLAGS) $(PULSE_CFLAGS) $(PORTAUDIO_CFLAGS) libfluidsynth_la_LIBADD = $(LIBFLUID_LIBS) $(LASH_LIBS) $(LADCCA_LIBS) \ $(READLINE_LIBS) $(COREAUDIO_LIBS) $(COREMIDI_LIBS) $(JACK_LIBS) \ - $(ALSA_LIBS) $(PULSE_LIBS) + $(ALSA_LIBS) $(PULSE_LIBS) $(PORTAUDIO_LIBS) libfluidsynth_la_LDFLAGS = \ -version-info @LT_VERSION_INFO@ \ -export-dynamic $(LIBFLUID_LDFLAGS) diff --git a/fluidsynth/src/config.h.in b/fluidsynth/src/config.h.in index 341ab1c4..86c340ea 100644 --- a/fluidsynth/src/config.h.in +++ b/fluidsynth/src/config.h.in @@ -160,6 +160,9 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to enable PortAudio driver */ +#undef PORTAUDIO_SUPPORT + /* Define to enable PulseAudio driver */ #undef PULSE_SUPPORT diff --git a/fluidsynth/src/fluid_adriver.c b/fluidsynth/src/fluid_adriver.c index 137cbf1e..3786a11b 100644 --- a/fluidsynth/src/fluid_adriver.c +++ b/fluidsynth/src/fluid_adriver.c @@ -82,6 +82,7 @@ void fluid_dsound_audio_driver_settings(fluid_settings_t* settings); #endif #if PORTAUDIO_SUPPORT +void fluid_portaudio_driver_settings (fluid_settings_t *settings); fluid_audio_driver_t* new_fluid_portaudio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_portaudio_driver(fluid_audio_driver_t* p); @@ -161,7 +162,7 @@ fluid_audriver_definition_t fluid_audio_drivers[] = { new_fluid_portaudio_driver, NULL, delete_fluid_portaudio_driver, - NULL }, + fluid_portaudio_driver_settings }, #endif #if SNDMAN_SUPPORT { "sndman", diff --git a/fluidsynth/src/fluid_portaudio.c b/fluidsynth/src/fluid_portaudio.c index 441af38a..4468c30c 100644 --- a/fluidsynth/src/fluid_portaudio.c +++ b/fluidsynth/src/fluid_portaudio.c @@ -25,17 +25,22 @@ * * Stephane Letz (letz@grame.fr) Grame * 12/20/01 Adapdation for new audio drivers + * + * Josh Green + * 2009-01-28 Overhauled for Portaudio 19 API and current FluidSynth API (was broken) */ #include "fluid_synth.h" #include "fluid_sys.h" +#include "fluid_settings.h" +#include "fluid_adriver.h" #if PORTAUDIO_SUPPORT #include #include #include -#include "portaudio.h" +#include /** fluid_portaudio_driver_t @@ -43,110 +48,203 @@ * This structure should not be accessed directly. Use audio port * functions instead. */ -typedef struct { - fluid_synth_t* synth; - fluid_audio_callback_t read; - PortAudioStream * stream; +typedef struct +{ + fluid_audio_driver_t driver; + fluid_synth_t *synth; + fluid_audio_callback_t read; + PaStream *stream; } fluid_portaudio_driver_t; -int delete_fluid_portaudio_driver(fluid_audio_driver_t* p); +static int +fluid_portaudio_run (const void *input, void *output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void *userData); +int delete_fluid_portaudio_driver (fluid_audio_driver_t *p); -/* PortAudio callback - * fluid_portaudio_run - */ -static int fluid_portaudio_run( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) + +void +fluid_portaudio_driver_settings (fluid_settings_t *settings) { - fluid_portaudio_driver_t* dev = (fluid_portaudio_driver_t*) userData; - /* it's as simple as that: */ - dev->read(dev->synth, framesPerBuffer, outputBuffer, 0, 2, outputBuffer, 1, 2); - return 0; + const PaDeviceInfo *deviceInfo; + int numDevices; + PaError err; + int i; + + fluid_settings_register_str (settings, "audio.portaudio.device", "default", 0, NULL, NULL); + fluid_settings_add_option (settings, "audio.portaudio.device", "default"); + + err = Pa_Initialize(); + + if (err != paNoError) + { + FLUID_LOG (FLUID_ERR, "Error initializing Portaudio driver: %s", + Pa_GetErrorText (err)); + return; + } + + numDevices = Pa_GetDeviceCount(); + + if (numDevices < 0) + { + FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices); + return; + } + + for (i = 0; i < numDevices; i++) + { + deviceInfo = Pa_GetDeviceInfo (i); + fluid_settings_add_option (settings, "audio.portaudio.device", + (char *)(deviceInfo->name)); + } } -/* - * new_fluid_portaudio_driver - */ - -fluid_audio_driver_t* -new_fluid_portaudio_driver(char* devname, int format, int chan, int sample_rate, - int bufsize, int queuesize, fluid_synth_t* synth) +fluid_audio_driver_t * +new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) { - fluid_portaudio_driver_t* dev = NULL; + fluid_portaudio_driver_t *dev = NULL; + PaStreamParameters outputParams; + char *device; + double sample_rate; + int period_size; PaError err; - PaSampleFormat portaudio_format; - dev = FLUID_NEW(fluid_portaudio_driver_t); - if (dev == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + dev = FLUID_NEW (fluid_portaudio_driver_t); + + if (dev == NULL) + { + FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } - FLUID_MEMSET(dev, 0, sizeof(fluid_portaudio_driver_t)); + + FLUID_MEMSET (dev, 0, sizeof (fluid_portaudio_driver_t)); dev->synth = synth; - switch (format) { - case FLUID_S16_FORMAT: - portaudio_format = paInt16; - dev->read = fluid_synth_write_s16; - break; + fluid_settings_getint (settings, "audio.period-size", &period_size); + fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate); + fluid_settings_getstr(settings, "audio.portaudio.device", &device); - case FLUID_FLOAT_FORMAT: - portaudio_format = paFloat32; - dev->read = fluid_synth_write_float; - break; + bzero (&outputParams, sizeof (outputParams)); + outputParams.channelCount = 2; + outputParams.suggestedLatency = (PaTime)period_size / sample_rate; + + /* Locate the device if specified */ + if (strcmp (device, "default") != 0) + { + const PaDeviceInfo *deviceInfo; + int numDevices; + int i; + + numDevices = Pa_GetDeviceCount (); + + if (numDevices < 0) + { + FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices); + goto error_recovery; + } + + for (i = 0; i < numDevices; i++) + { + deviceInfo = Pa_GetDeviceInfo (i); + + if (strcmp (device, deviceInfo->name) == 0) break; + } + + if (i == numDevices) + { + FLUID_LOG (FLUID_ERR, "PortAudio device '%s' was not found", device); + goto error_recovery; + } + } + else outputParams.device = 0; + + if (fluid_settings_str_equal (settings, "audio.sample-format", "16bits")) + { + outputParams.sampleFormat = paInt16; + dev->read = fluid_synth_write_s16; + } + else if (fluid_settings_str_equal (settings, "audio.sample-format", "float")) + { + outputParams.sampleFormat = paFloat32; + dev->read = fluid_synth_write_float; + } + else + { + FLUID_LOG (FLUID_ERR, "Unknown sample format"); + goto error_recovery; } /* PortAudio section */ - err = Pa_Initialize(); - if( err != paNoError ) goto error_recovery; + /* Open an audio I/O stream. */ + err = Pa_OpenStream (&dev->stream, + NULL, /* Input parameters */ + &outputParams, + sample_rate, + period_size, + paNoFlag, + fluid_portaudio_run, + dev); - err = Pa_OpenStream( - &dev->stream, - paNoDevice, /* default input device */ - 0, /* no input */ - portaudio_format, - NULL, - Pa_GetDefaultOutputDeviceID() , /* default output device */ - 2, /* stereo output */ - portaudio_format, - NULL, - sample_rate, - bufsize, /* 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 */ - fluid_portaudio_run, - dev ); + if (err != paNoError) + { + FLUID_LOG (FLUID_ERR, "Error opening Portaudio stream: %s", + Pa_GetErrorText (err)); + goto error_recovery; + } - if( err != paNoError ) goto error_recovery; - err = Pa_StartStream( dev->stream ); - if( err != paNoError ) goto error_recovery; + err = Pa_StartStream (dev->stream); - return (fluid_audio_driver_t*) dev; + if (err != paNoError) + { + FLUID_LOG (FLUID_ERR, "Error starting Portaudio stream: %s", + Pa_GetErrorText (err)); + goto error_recovery; + } + + return (fluid_audio_driver_t *)dev; error_recovery: - delete_fluid_portaudio_driver((fluid_audio_driver_t*) dev); + delete_fluid_portaudio_driver ((fluid_audio_driver_t *)dev); return NULL; } +/* PortAudio callback + * fluid_portaudio_run + */ +static int +fluid_portaudio_run (const void *input, void *output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void *userData) +{ + fluid_portaudio_driver_t *dev = (fluid_portaudio_driver_t *)userData; + /* it's as simple as that: */ + dev->read (dev->synth, frameCount, output, 0, 2, output, 1, 2); + return 0; +} + /* * delete_fluid_portaudio_driver */ -int delete_fluid_portaudio_driver(fluid_audio_driver_t* p) +int +delete_fluid_portaudio_driver(fluid_audio_driver_t *p) { fluid_portaudio_driver_t* dev; + PaError err; - dev = (fluid_portaudio_driver_t*) p; - if (dev == NULL) { - return FLUID_OK; - } + dev = (fluid_portaudio_driver_t*)p; + if (dev == NULL) return FLUID_OK; /* PortAudio section */ - if(dev->stream) Pa_CloseStream(dev->stream); - Pa_Terminate(); + if (dev->stream) Pa_CloseStream (dev->stream); - FLUID_FREE(dev); + err = Pa_Terminate(); + + if (err != paNoError) + printf ("PortAudio termination error: %s\n", Pa_GetErrorText (err) ); + + FLUID_FREE (dev); return FLUID_OK; }