Mercurial > audio-send
view Alc/backends/portaudio.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
line wrap: on
line source
1 /**2 * OpenAL cross platform audio library3 * Copyright (C) 1999-2007 by authors.4 * This library is free software; you can redistribute it and/or5 * modify it under the terms of the GNU Library General Public6 * License as published by the Free Software Foundation; either7 * version 2 of the License, or (at your option) any later version.8 *9 * This library is distributed in the hope that it will be useful,10 * but WITHOUT ANY WARRANTY; without even the implied warranty of11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12 * Library General Public License for more details.13 *14 * You should have received a copy of the GNU Library General Public15 * License along with this library; if not, write to the16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,17 * Boston, MA 02111-1307, USA.18 * Or go to http://www.gnu.org/copyleft/lgpl.html19 */21 #include "config.h"23 #include <stdio.h>24 #include <stdlib.h>25 #include <string.h>26 #include "alMain.h"27 #include "AL/al.h"28 #include "AL/alc.h"30 #include <portaudio.h>33 static const ALCchar pa_device[] = "PortAudio Default";36 static void *pa_handle;37 #ifdef HAVE_DYNLOAD38 #define MAKE_FUNC(x) static typeof(x) * p##x39 MAKE_FUNC(Pa_Initialize);40 MAKE_FUNC(Pa_Terminate);41 MAKE_FUNC(Pa_GetErrorText);42 MAKE_FUNC(Pa_StartStream);43 MAKE_FUNC(Pa_StopStream);44 MAKE_FUNC(Pa_OpenStream);45 MAKE_FUNC(Pa_CloseStream);46 MAKE_FUNC(Pa_GetDefaultOutputDevice);47 MAKE_FUNC(Pa_GetStreamInfo);48 #undef MAKE_FUNC50 #define Pa_Initialize pPa_Initialize51 #define Pa_Terminate pPa_Terminate52 #define Pa_GetErrorText pPa_GetErrorText53 #define Pa_StartStream pPa_StartStream54 #define Pa_StopStream pPa_StopStream55 #define Pa_OpenStream pPa_OpenStream56 #define Pa_CloseStream pPa_CloseStream57 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice58 #define Pa_GetStreamInfo pPa_GetStreamInfo59 #endif61 static ALCboolean pa_load(void)62 {63 if(!pa_handle)64 {65 PaError err;67 #ifdef HAVE_DYNLOAD68 #ifdef _WIN3269 # define PALIB "portaudio.dll"70 #elif defined(__APPLE__) && defined(__MACH__)71 # define PALIB "libportaudio.2.dylib"72 #elif defined(__OpenBSD__)73 # define PALIB "libportaudio.so"74 #else75 # define PALIB "libportaudio.so.2"76 #endif78 pa_handle = LoadLib(PALIB);79 if(!pa_handle)80 return ALC_FALSE;82 #define LOAD_FUNC(f) do { \83 p##f = GetSymbol(pa_handle, #f); \84 if(p##f == NULL) \85 { \86 CloseLib(pa_handle); \87 pa_handle = NULL; \88 return ALC_FALSE; \89 } \90 } while(0)91 LOAD_FUNC(Pa_Initialize);92 LOAD_FUNC(Pa_Terminate);93 LOAD_FUNC(Pa_GetErrorText);94 LOAD_FUNC(Pa_StartStream);95 LOAD_FUNC(Pa_StopStream);96 LOAD_FUNC(Pa_OpenStream);97 LOAD_FUNC(Pa_CloseStream);98 LOAD_FUNC(Pa_GetDefaultOutputDevice);99 LOAD_FUNC(Pa_GetStreamInfo);100 #undef LOAD_FUNC101 #else102 pa_handle = (void*)0xDEADBEEF;103 #endif105 if((err=Pa_Initialize()) != paNoError)106 {107 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));108 CloseLib(pa_handle);109 pa_handle = NULL;110 return ALC_FALSE;111 }112 }113 return ALC_TRUE;114 }117 typedef struct {118 PaStream *stream;119 ALuint update_size;121 RingBuffer *ring;122 } pa_data;125 static int pa_callback(const void *inputBuffer, void *outputBuffer,126 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,127 const PaStreamCallbackFlags statusFlags, void *userData)128 {129 ALCdevice *device = (ALCdevice*)userData;131 (void)inputBuffer;132 (void)timeInfo;133 (void)statusFlags;135 aluMixData(device, outputBuffer, framesPerBuffer);136 return 0;137 }139 static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,140 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,141 const PaStreamCallbackFlags statusFlags, void *userData)142 {143 ALCdevice *device = (ALCdevice*)userData;144 pa_data *data = (pa_data*)device->ExtraData;146 (void)outputBuffer;147 (void)timeInfo;148 (void)statusFlags;150 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);151 return 0;152 }155 static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)156 {157 PaStreamParameters outParams;158 pa_data *data;159 PaError err;161 if(!deviceName)162 deviceName = pa_device;163 else if(strcmp(deviceName, pa_device) != 0)164 return ALC_FALSE;166 data = (pa_data*)calloc(1, sizeof(pa_data));167 data->update_size = device->UpdateSize;169 device->ExtraData = data;171 outParams.device = GetConfigValueInt("port", "device", -1);172 if(outParams.device < 0)173 outParams.device = Pa_GetDefaultOutputDevice();174 outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /175 (float)device->Frequency;176 outParams.hostApiSpecificStreamInfo = NULL;178 switch(device->FmtType)179 {180 case DevFmtByte:181 outParams.sampleFormat = paInt8;182 break;183 case DevFmtUByte:184 outParams.sampleFormat = paUInt8;185 break;186 case DevFmtUShort:187 device->FmtType = DevFmtShort;188 /* fall-through */189 case DevFmtShort:190 outParams.sampleFormat = paInt16;191 break;192 case DevFmtFloat:193 outParams.sampleFormat = paFloat32;194 break;195 }196 outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);198 SetDefaultChannelOrder(device);200 err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,201 device->UpdateSize, paNoFlag, pa_callback, device);202 if(err != paNoError)203 {204 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));205 device->ExtraData = NULL;206 free(data);207 return ALC_FALSE;208 }210 device->szDeviceName = strdup(deviceName);212 if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans))213 {214 if(outParams.channelCount != 1 && outParams.channelCount != 2)215 {216 ERR("Unhandled channel count: %u\n", outParams.channelCount);217 Pa_CloseStream(data->stream);218 device->ExtraData = NULL;219 free(data);220 return ALC_FALSE;221 }222 if((device->Flags&DEVICE_CHANNELS_REQUEST))223 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount);224 device->Flags &= ~DEVICE_CHANNELS_REQUEST;225 device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo);226 }228 return ALC_TRUE;229 }231 static void pa_close_playback(ALCdevice *device)232 {233 pa_data *data = (pa_data*)device->ExtraData;234 PaError err;236 err = Pa_CloseStream(data->stream);237 if(err != paNoError)238 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));240 free(data);241 device->ExtraData = NULL;242 }244 static ALCboolean pa_reset_playback(ALCdevice *device)245 {246 pa_data *data = (pa_data*)device->ExtraData;247 const PaStreamInfo *streamInfo;248 PaError err;250 streamInfo = Pa_GetStreamInfo(data->stream);251 if(device->Frequency != streamInfo->sampleRate)252 {253 if((device->Flags&DEVICE_FREQUENCY_REQUEST))254 ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate);255 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;256 device->Frequency = streamInfo->sampleRate;257 }258 device->UpdateSize = data->update_size;260 err = Pa_StartStream(data->stream);261 if(err != paNoError)262 {263 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));264 return ALC_FALSE;265 }267 return ALC_TRUE;268 }270 static void pa_stop_playback(ALCdevice *device)271 {272 pa_data *data = (pa_data*)device->ExtraData;273 PaError err;275 err = Pa_StopStream(data->stream);276 if(err != paNoError)277 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));278 }281 static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName)282 {283 PaStreamParameters inParams;284 ALuint frame_size;285 pa_data *data;286 PaError err;288 if(!deviceName)289 deviceName = pa_device;290 else if(strcmp(deviceName, pa_device) != 0)291 return ALC_FALSE;293 data = (pa_data*)calloc(1, sizeof(pa_data));294 if(data == NULL)295 {296 alcSetError(device, ALC_OUT_OF_MEMORY);297 return ALC_FALSE;298 }300 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);301 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);302 if(data->ring == NULL)303 {304 alcSetError(device, ALC_OUT_OF_MEMORY);305 goto error;306 }308 inParams.device = GetConfigValueInt("port", "capture", -1);309 if(inParams.device < 0)310 inParams.device = Pa_GetDefaultOutputDevice();311 inParams.suggestedLatency = 0.0f;312 inParams.hostApiSpecificStreamInfo = NULL;314 switch(device->FmtType)315 {316 case DevFmtByte:317 inParams.sampleFormat = paInt8;318 break;319 case DevFmtUByte:320 inParams.sampleFormat = paUInt8;321 break;322 case DevFmtShort:323 inParams.sampleFormat = paInt16;324 break;325 case DevFmtFloat:326 inParams.sampleFormat = paFloat32;327 break;328 case DevFmtUShort:329 ERR("Unsigned short samples not supported\n");330 goto error;331 }332 inParams.channelCount = ChannelsFromDevFmt(device->FmtChans);334 err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency,335 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);336 if(err != paNoError)337 {338 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));339 goto error;340 }342 device->szDeviceName = strdup(deviceName);344 device->ExtraData = data;345 return ALC_TRUE;347 error:348 DestroyRingBuffer(data->ring);349 free(data);350 return ALC_FALSE;351 }353 static void pa_close_capture(ALCdevice *device)354 {355 pa_data *data = (pa_data*)device->ExtraData;356 PaError err;358 err = Pa_CloseStream(data->stream);359 if(err != paNoError)360 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));362 free(data);363 device->ExtraData = NULL;364 }366 static void pa_start_capture(ALCdevice *device)367 {368 pa_data *data = device->ExtraData;369 PaError err;371 err = Pa_StartStream(data->stream);372 if(err != paNoError)373 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));374 }376 static void pa_stop_capture(ALCdevice *device)377 {378 pa_data *data = (pa_data*)device->ExtraData;379 PaError err;381 err = Pa_StopStream(data->stream);382 if(err != paNoError)383 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));384 }386 static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)387 {388 pa_data *data = device->ExtraData;389 if(samples <= (ALCuint)RingBufferSize(data->ring))390 ReadRingBuffer(data->ring, buffer, samples);391 else392 alcSetError(device, ALC_INVALID_VALUE);393 }395 static ALCuint pa_available_samples(ALCdevice *device)396 {397 pa_data *data = device->ExtraData;398 return RingBufferSize(data->ring);399 }402 static const BackendFuncs pa_funcs = {403 pa_open_playback,404 pa_close_playback,405 pa_reset_playback,406 pa_stop_playback,407 pa_open_capture,408 pa_close_capture,409 pa_start_capture,410 pa_stop_capture,411 pa_capture_samples,412 pa_available_samples413 };415 ALCboolean alc_pa_init(BackendFuncs *func_list)416 {417 if(!pa_load())418 return ALC_FALSE;419 *func_list = pa_funcs;420 return ALC_TRUE;421 }423 void alc_pa_deinit(void)424 {425 if(pa_handle)426 {427 Pa_Terminate();428 #ifdef HAVE_DYNLOAD429 CloseLib(pa_handle);430 #endif431 pa_handle = NULL;432 }433 }435 void alc_pa_probe(enum DevProbe type)436 {437 switch(type)438 {439 case DEVICE_PROBE:440 AppendDeviceList(pa_device);441 break;442 case ALL_DEVICE_PROBE:443 AppendAllDeviceList(pa_device);444 break;445 case CAPTURE_DEVICE_PROBE:446 AppendCaptureDeviceList(pa_device);447 break;448 }449 }