Mercurial > audio-send
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alc/backends/portaudio.c Tue Oct 25 13:02:31 2011 -0700 1.3 @@ -0,0 +1,449 @@ 1.4 +/** 1.5 + * OpenAL cross platform audio library 1.6 + * Copyright (C) 1999-2007 by authors. 1.7 + * This library is free software; you can redistribute it and/or 1.8 + * modify it under the terms of the GNU Library General Public 1.9 + * License as published by the Free Software Foundation; either 1.10 + * version 2 of the License, or (at your option) any later version. 1.11 + * 1.12 + * This library is distributed in the hope that it will be useful, 1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.15 + * Library General Public License for more details. 1.16 + * 1.17 + * You should have received a copy of the GNU Library General Public 1.18 + * License along with this library; if not, write to the 1.19 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 1.20 + * Boston, MA 02111-1307, USA. 1.21 + * Or go to http://www.gnu.org/copyleft/lgpl.html 1.22 + */ 1.23 + 1.24 +#include "config.h" 1.25 + 1.26 +#include <stdio.h> 1.27 +#include <stdlib.h> 1.28 +#include <string.h> 1.29 +#include "alMain.h" 1.30 +#include "AL/al.h" 1.31 +#include "AL/alc.h" 1.32 + 1.33 +#include <portaudio.h> 1.34 + 1.35 + 1.36 +static const ALCchar pa_device[] = "PortAudio Default"; 1.37 + 1.38 + 1.39 +static void *pa_handle; 1.40 +#ifdef HAVE_DYNLOAD 1.41 +#define MAKE_FUNC(x) static typeof(x) * p##x 1.42 +MAKE_FUNC(Pa_Initialize); 1.43 +MAKE_FUNC(Pa_Terminate); 1.44 +MAKE_FUNC(Pa_GetErrorText); 1.45 +MAKE_FUNC(Pa_StartStream); 1.46 +MAKE_FUNC(Pa_StopStream); 1.47 +MAKE_FUNC(Pa_OpenStream); 1.48 +MAKE_FUNC(Pa_CloseStream); 1.49 +MAKE_FUNC(Pa_GetDefaultOutputDevice); 1.50 +MAKE_FUNC(Pa_GetStreamInfo); 1.51 +#undef MAKE_FUNC 1.52 + 1.53 +#define Pa_Initialize pPa_Initialize 1.54 +#define Pa_Terminate pPa_Terminate 1.55 +#define Pa_GetErrorText pPa_GetErrorText 1.56 +#define Pa_StartStream pPa_StartStream 1.57 +#define Pa_StopStream pPa_StopStream 1.58 +#define Pa_OpenStream pPa_OpenStream 1.59 +#define Pa_CloseStream pPa_CloseStream 1.60 +#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice 1.61 +#define Pa_GetStreamInfo pPa_GetStreamInfo 1.62 +#endif 1.63 + 1.64 +static ALCboolean pa_load(void) 1.65 +{ 1.66 + if(!pa_handle) 1.67 + { 1.68 + PaError err; 1.69 + 1.70 +#ifdef HAVE_DYNLOAD 1.71 +#ifdef _WIN32 1.72 +# define PALIB "portaudio.dll" 1.73 +#elif defined(__APPLE__) && defined(__MACH__) 1.74 +# define PALIB "libportaudio.2.dylib" 1.75 +#elif defined(__OpenBSD__) 1.76 +# define PALIB "libportaudio.so" 1.77 +#else 1.78 +# define PALIB "libportaudio.so.2" 1.79 +#endif 1.80 + 1.81 + pa_handle = LoadLib(PALIB); 1.82 + if(!pa_handle) 1.83 + return ALC_FALSE; 1.84 + 1.85 +#define LOAD_FUNC(f) do { \ 1.86 + p##f = GetSymbol(pa_handle, #f); \ 1.87 + if(p##f == NULL) \ 1.88 + { \ 1.89 + CloseLib(pa_handle); \ 1.90 + pa_handle = NULL; \ 1.91 + return ALC_FALSE; \ 1.92 + } \ 1.93 +} while(0) 1.94 + LOAD_FUNC(Pa_Initialize); 1.95 + LOAD_FUNC(Pa_Terminate); 1.96 + LOAD_FUNC(Pa_GetErrorText); 1.97 + LOAD_FUNC(Pa_StartStream); 1.98 + LOAD_FUNC(Pa_StopStream); 1.99 + LOAD_FUNC(Pa_OpenStream); 1.100 + LOAD_FUNC(Pa_CloseStream); 1.101 + LOAD_FUNC(Pa_GetDefaultOutputDevice); 1.102 + LOAD_FUNC(Pa_GetStreamInfo); 1.103 +#undef LOAD_FUNC 1.104 +#else 1.105 + pa_handle = (void*)0xDEADBEEF; 1.106 +#endif 1.107 + 1.108 + if((err=Pa_Initialize()) != paNoError) 1.109 + { 1.110 + ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); 1.111 + CloseLib(pa_handle); 1.112 + pa_handle = NULL; 1.113 + return ALC_FALSE; 1.114 + } 1.115 + } 1.116 + return ALC_TRUE; 1.117 +} 1.118 + 1.119 + 1.120 +typedef struct { 1.121 + PaStream *stream; 1.122 + ALuint update_size; 1.123 + 1.124 + RingBuffer *ring; 1.125 +} pa_data; 1.126 + 1.127 + 1.128 +static int pa_callback(const void *inputBuffer, void *outputBuffer, 1.129 + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, 1.130 + const PaStreamCallbackFlags statusFlags, void *userData) 1.131 +{ 1.132 + ALCdevice *device = (ALCdevice*)userData; 1.133 + 1.134 + (void)inputBuffer; 1.135 + (void)timeInfo; 1.136 + (void)statusFlags; 1.137 + 1.138 + aluMixData(device, outputBuffer, framesPerBuffer); 1.139 + return 0; 1.140 +} 1.141 + 1.142 +static int pa_capture_cb(const void *inputBuffer, void *outputBuffer, 1.143 + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, 1.144 + const PaStreamCallbackFlags statusFlags, void *userData) 1.145 +{ 1.146 + ALCdevice *device = (ALCdevice*)userData; 1.147 + pa_data *data = (pa_data*)device->ExtraData; 1.148 + 1.149 + (void)outputBuffer; 1.150 + (void)timeInfo; 1.151 + (void)statusFlags; 1.152 + 1.153 + WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); 1.154 + return 0; 1.155 +} 1.156 + 1.157 + 1.158 +static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) 1.159 +{ 1.160 + PaStreamParameters outParams; 1.161 + pa_data *data; 1.162 + PaError err; 1.163 + 1.164 + if(!deviceName) 1.165 + deviceName = pa_device; 1.166 + else if(strcmp(deviceName, pa_device) != 0) 1.167 + return ALC_FALSE; 1.168 + 1.169 + data = (pa_data*)calloc(1, sizeof(pa_data)); 1.170 + data->update_size = device->UpdateSize; 1.171 + 1.172 + device->ExtraData = data; 1.173 + 1.174 + outParams.device = GetConfigValueInt("port", "device", -1); 1.175 + if(outParams.device < 0) 1.176 + outParams.device = Pa_GetDefaultOutputDevice(); 1.177 + outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) / 1.178 + (float)device->Frequency; 1.179 + outParams.hostApiSpecificStreamInfo = NULL; 1.180 + 1.181 + switch(device->FmtType) 1.182 + { 1.183 + case DevFmtByte: 1.184 + outParams.sampleFormat = paInt8; 1.185 + break; 1.186 + case DevFmtUByte: 1.187 + outParams.sampleFormat = paUInt8; 1.188 + break; 1.189 + case DevFmtUShort: 1.190 + device->FmtType = DevFmtShort; 1.191 + /* fall-through */ 1.192 + case DevFmtShort: 1.193 + outParams.sampleFormat = paInt16; 1.194 + break; 1.195 + case DevFmtFloat: 1.196 + outParams.sampleFormat = paFloat32; 1.197 + break; 1.198 + } 1.199 + outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2); 1.200 + 1.201 + SetDefaultChannelOrder(device); 1.202 + 1.203 + err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency, 1.204 + device->UpdateSize, paNoFlag, pa_callback, device); 1.205 + if(err != paNoError) 1.206 + { 1.207 + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); 1.208 + device->ExtraData = NULL; 1.209 + free(data); 1.210 + return ALC_FALSE; 1.211 + } 1.212 + 1.213 + device->szDeviceName = strdup(deviceName); 1.214 + 1.215 + if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans)) 1.216 + { 1.217 + if(outParams.channelCount != 1 && outParams.channelCount != 2) 1.218 + { 1.219 + ERR("Unhandled channel count: %u\n", outParams.channelCount); 1.220 + Pa_CloseStream(data->stream); 1.221 + device->ExtraData = NULL; 1.222 + free(data); 1.223 + return ALC_FALSE; 1.224 + } 1.225 + if((device->Flags&DEVICE_CHANNELS_REQUEST)) 1.226 + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount); 1.227 + device->Flags &= ~DEVICE_CHANNELS_REQUEST; 1.228 + device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo); 1.229 + } 1.230 + 1.231 + return ALC_TRUE; 1.232 +} 1.233 + 1.234 +static void pa_close_playback(ALCdevice *device) 1.235 +{ 1.236 + pa_data *data = (pa_data*)device->ExtraData; 1.237 + PaError err; 1.238 + 1.239 + err = Pa_CloseStream(data->stream); 1.240 + if(err != paNoError) 1.241 + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); 1.242 + 1.243 + free(data); 1.244 + device->ExtraData = NULL; 1.245 +} 1.246 + 1.247 +static ALCboolean pa_reset_playback(ALCdevice *device) 1.248 +{ 1.249 + pa_data *data = (pa_data*)device->ExtraData; 1.250 + const PaStreamInfo *streamInfo; 1.251 + PaError err; 1.252 + 1.253 + streamInfo = Pa_GetStreamInfo(data->stream); 1.254 + if(device->Frequency != streamInfo->sampleRate) 1.255 + { 1.256 + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) 1.257 + ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate); 1.258 + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; 1.259 + device->Frequency = streamInfo->sampleRate; 1.260 + } 1.261 + device->UpdateSize = data->update_size; 1.262 + 1.263 + err = Pa_StartStream(data->stream); 1.264 + if(err != paNoError) 1.265 + { 1.266 + ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err)); 1.267 + return ALC_FALSE; 1.268 + } 1.269 + 1.270 + return ALC_TRUE; 1.271 +} 1.272 + 1.273 +static void pa_stop_playback(ALCdevice *device) 1.274 +{ 1.275 + pa_data *data = (pa_data*)device->ExtraData; 1.276 + PaError err; 1.277 + 1.278 + err = Pa_StopStream(data->stream); 1.279 + if(err != paNoError) 1.280 + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); 1.281 +} 1.282 + 1.283 + 1.284 +static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName) 1.285 +{ 1.286 + PaStreamParameters inParams; 1.287 + ALuint frame_size; 1.288 + pa_data *data; 1.289 + PaError err; 1.290 + 1.291 + if(!deviceName) 1.292 + deviceName = pa_device; 1.293 + else if(strcmp(deviceName, pa_device) != 0) 1.294 + return ALC_FALSE; 1.295 + 1.296 + data = (pa_data*)calloc(1, sizeof(pa_data)); 1.297 + if(data == NULL) 1.298 + { 1.299 + alcSetError(device, ALC_OUT_OF_MEMORY); 1.300 + return ALC_FALSE; 1.301 + } 1.302 + 1.303 + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); 1.304 + data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates); 1.305 + if(data->ring == NULL) 1.306 + { 1.307 + alcSetError(device, ALC_OUT_OF_MEMORY); 1.308 + goto error; 1.309 + } 1.310 + 1.311 + inParams.device = GetConfigValueInt("port", "capture", -1); 1.312 + if(inParams.device < 0) 1.313 + inParams.device = Pa_GetDefaultOutputDevice(); 1.314 + inParams.suggestedLatency = 0.0f; 1.315 + inParams.hostApiSpecificStreamInfo = NULL; 1.316 + 1.317 + switch(device->FmtType) 1.318 + { 1.319 + case DevFmtByte: 1.320 + inParams.sampleFormat = paInt8; 1.321 + break; 1.322 + case DevFmtUByte: 1.323 + inParams.sampleFormat = paUInt8; 1.324 + break; 1.325 + case DevFmtShort: 1.326 + inParams.sampleFormat = paInt16; 1.327 + break; 1.328 + case DevFmtFloat: 1.329 + inParams.sampleFormat = paFloat32; 1.330 + break; 1.331 + case DevFmtUShort: 1.332 + ERR("Unsigned short samples not supported\n"); 1.333 + goto error; 1.334 + } 1.335 + inParams.channelCount = ChannelsFromDevFmt(device->FmtChans); 1.336 + 1.337 + err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency, 1.338 + paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device); 1.339 + if(err != paNoError) 1.340 + { 1.341 + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); 1.342 + goto error; 1.343 + } 1.344 + 1.345 + device->szDeviceName = strdup(deviceName); 1.346 + 1.347 + device->ExtraData = data; 1.348 + return ALC_TRUE; 1.349 + 1.350 +error: 1.351 + DestroyRingBuffer(data->ring); 1.352 + free(data); 1.353 + return ALC_FALSE; 1.354 +} 1.355 + 1.356 +static void pa_close_capture(ALCdevice *device) 1.357 +{ 1.358 + pa_data *data = (pa_data*)device->ExtraData; 1.359 + PaError err; 1.360 + 1.361 + err = Pa_CloseStream(data->stream); 1.362 + if(err != paNoError) 1.363 + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); 1.364 + 1.365 + free(data); 1.366 + device->ExtraData = NULL; 1.367 +} 1.368 + 1.369 +static void pa_start_capture(ALCdevice *device) 1.370 +{ 1.371 + pa_data *data = device->ExtraData; 1.372 + PaError err; 1.373 + 1.374 + err = Pa_StartStream(data->stream); 1.375 + if(err != paNoError) 1.376 + ERR("Error starting stream: %s\n", Pa_GetErrorText(err)); 1.377 +} 1.378 + 1.379 +static void pa_stop_capture(ALCdevice *device) 1.380 +{ 1.381 + pa_data *data = (pa_data*)device->ExtraData; 1.382 + PaError err; 1.383 + 1.384 + err = Pa_StopStream(data->stream); 1.385 + if(err != paNoError) 1.386 + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); 1.387 +} 1.388 + 1.389 +static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) 1.390 +{ 1.391 + pa_data *data = device->ExtraData; 1.392 + if(samples <= (ALCuint)RingBufferSize(data->ring)) 1.393 + ReadRingBuffer(data->ring, buffer, samples); 1.394 + else 1.395 + alcSetError(device, ALC_INVALID_VALUE); 1.396 +} 1.397 + 1.398 +static ALCuint pa_available_samples(ALCdevice *device) 1.399 +{ 1.400 + pa_data *data = device->ExtraData; 1.401 + return RingBufferSize(data->ring); 1.402 +} 1.403 + 1.404 + 1.405 +static const BackendFuncs pa_funcs = { 1.406 + pa_open_playback, 1.407 + pa_close_playback, 1.408 + pa_reset_playback, 1.409 + pa_stop_playback, 1.410 + pa_open_capture, 1.411 + pa_close_capture, 1.412 + pa_start_capture, 1.413 + pa_stop_capture, 1.414 + pa_capture_samples, 1.415 + pa_available_samples 1.416 +}; 1.417 + 1.418 +ALCboolean alc_pa_init(BackendFuncs *func_list) 1.419 +{ 1.420 + if(!pa_load()) 1.421 + return ALC_FALSE; 1.422 + *func_list = pa_funcs; 1.423 + return ALC_TRUE; 1.424 +} 1.425 + 1.426 +void alc_pa_deinit(void) 1.427 +{ 1.428 + if(pa_handle) 1.429 + { 1.430 + Pa_Terminate(); 1.431 +#ifdef HAVE_DYNLOAD 1.432 + CloseLib(pa_handle); 1.433 +#endif 1.434 + pa_handle = NULL; 1.435 + } 1.436 +} 1.437 + 1.438 +void alc_pa_probe(enum DevProbe type) 1.439 +{ 1.440 + switch(type) 1.441 + { 1.442 + case DEVICE_PROBE: 1.443 + AppendDeviceList(pa_device); 1.444 + break; 1.445 + case ALL_DEVICE_PROBE: 1.446 + AppendAllDeviceList(pa_device); 1.447 + break; 1.448 + case CAPTURE_DEVICE_PROBE: 1.449 + AppendCaptureDeviceList(pa_device); 1.450 + break; 1.451 + } 1.452 +}