/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ /* fluid_portaudio.c * * Drivers for the PortAudio API : www.portaudio.com * Implementation files for PortAudio on each platform have to be added * * 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 /** fluid_portaudio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; fluid_synth_t *synth; fluid_audio_callback_t read; PaStream *stream; } fluid_portaudio_driver_t; 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); #define PORTAUDIO_DEFAULT_DEVICE "PortAudio Default" void fluid_portaudio_driver_settings (fluid_settings_t *settings) { const PaDeviceInfo *deviceInfo; int numDevices; PaError err; int i; fluid_settings_register_str (settings, "audio.portaudio.device", PORTAUDIO_DEFAULT_DEVICE, 0, NULL, NULL); fluid_settings_add_option (settings, "audio.portaudio.device", PORTAUDIO_DEFAULT_DEVICE); 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); if ( deviceInfo->maxOutputChannels >= 2 ) fluid_settings_add_option (settings, "audio.portaudio.device", (char *)(deviceInfo->name)); } } fluid_audio_driver_t * new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) { fluid_portaudio_driver_t *dev = NULL; PaStreamParameters outputParams; char *device; double sample_rate; int period_size; PaError err; 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)); dev->synth = synth; 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); bzero (&outputParams, sizeof (outputParams)); outputParams.channelCount = 2; outputParams.suggestedLatency = (PaTime)period_size / sample_rate; /* Locate the device if specified */ if (strcmp (device, PORTAUDIO_DEFAULT_DEVICE) != 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) { outputParams.device = i; break; } } if (i == numDevices) { FLUID_LOG (FLUID_ERR, "PortAudio device '%s' was not found", device); goto error_recovery; } } else outputParams.device = Pa_GetDefaultOutputDevice(); 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 */ /* Open an audio I/O stream. */ err = Pa_OpenStream (&dev->stream, NULL, /* Input parameters */ &outputParams, sample_rate, period_size, paNoFlag, fluid_portaudio_run, dev); if (err != paNoError) { FLUID_LOG (FLUID_ERR, "Error opening PortAudio stream: %s", Pa_GetErrorText (err)); goto error_recovery; } err = Pa_StartStream (dev->stream); 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); 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) { fluid_portaudio_driver_t* dev; PaError err; dev = (fluid_portaudio_driver_t*)p; if (dev == NULL) return FLUID_OK; /* PortAudio section */ if (dev->stream) Pa_CloseStream (dev->stream); err = Pa_Terminate(); if (err != paNoError) printf ("PortAudio termination error: %s\n", Pa_GetErrorText (err) ); FLUID_FREE (dev); return FLUID_OK; } #endif /*#if PORTAUDIO_SUPPORT */