annotate 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
rev   line source
rlm@0 1 /**
rlm@0 2 * OpenAL cross platform audio library
rlm@0 3 * Copyright (C) 1999-2007 by authors.
rlm@0 4 * This library is free software; you can redistribute it and/or
rlm@0 5 * modify it under the terms of the GNU Library General Public
rlm@0 6 * License as published by the Free Software Foundation; either
rlm@0 7 * version 2 of the License, or (at your option) any later version.
rlm@0 8 *
rlm@0 9 * This library is distributed in the hope that it will be useful,
rlm@0 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@0 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rlm@0 12 * Library General Public License for more details.
rlm@0 13 *
rlm@0 14 * You should have received a copy of the GNU Library General Public
rlm@0 15 * License along with this library; if not, write to the
rlm@0 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rlm@0 17 * Boston, MA 02111-1307, USA.
rlm@0 18 * Or go to http://www.gnu.org/copyleft/lgpl.html
rlm@0 19 */
rlm@0 20
rlm@0 21 #include "config.h"
rlm@0 22
rlm@0 23 #include <stdio.h>
rlm@0 24 #include <stdlib.h>
rlm@0 25 #include <string.h>
rlm@0 26 #include "alMain.h"
rlm@0 27 #include "AL/al.h"
rlm@0 28 #include "AL/alc.h"
rlm@0 29
rlm@0 30 #include <portaudio.h>
rlm@0 31
rlm@0 32
rlm@0 33 static const ALCchar pa_device[] = "PortAudio Default";
rlm@0 34
rlm@0 35
rlm@0 36 static void *pa_handle;
rlm@0 37 #ifdef HAVE_DYNLOAD
rlm@0 38 #define MAKE_FUNC(x) static typeof(x) * p##x
rlm@0 39 MAKE_FUNC(Pa_Initialize);
rlm@0 40 MAKE_FUNC(Pa_Terminate);
rlm@0 41 MAKE_FUNC(Pa_GetErrorText);
rlm@0 42 MAKE_FUNC(Pa_StartStream);
rlm@0 43 MAKE_FUNC(Pa_StopStream);
rlm@0 44 MAKE_FUNC(Pa_OpenStream);
rlm@0 45 MAKE_FUNC(Pa_CloseStream);
rlm@0 46 MAKE_FUNC(Pa_GetDefaultOutputDevice);
rlm@0 47 MAKE_FUNC(Pa_GetStreamInfo);
rlm@0 48 #undef MAKE_FUNC
rlm@0 49
rlm@0 50 #define Pa_Initialize pPa_Initialize
rlm@0 51 #define Pa_Terminate pPa_Terminate
rlm@0 52 #define Pa_GetErrorText pPa_GetErrorText
rlm@0 53 #define Pa_StartStream pPa_StartStream
rlm@0 54 #define Pa_StopStream pPa_StopStream
rlm@0 55 #define Pa_OpenStream pPa_OpenStream
rlm@0 56 #define Pa_CloseStream pPa_CloseStream
rlm@0 57 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
rlm@0 58 #define Pa_GetStreamInfo pPa_GetStreamInfo
rlm@0 59 #endif
rlm@0 60
rlm@0 61 static ALCboolean pa_load(void)
rlm@0 62 {
rlm@0 63 if(!pa_handle)
rlm@0 64 {
rlm@0 65 PaError err;
rlm@0 66
rlm@0 67 #ifdef HAVE_DYNLOAD
rlm@0 68 #ifdef _WIN32
rlm@0 69 # define PALIB "portaudio.dll"
rlm@0 70 #elif defined(__APPLE__) && defined(__MACH__)
rlm@0 71 # define PALIB "libportaudio.2.dylib"
rlm@0 72 #elif defined(__OpenBSD__)
rlm@0 73 # define PALIB "libportaudio.so"
rlm@0 74 #else
rlm@0 75 # define PALIB "libportaudio.so.2"
rlm@0 76 #endif
rlm@0 77
rlm@0 78 pa_handle = LoadLib(PALIB);
rlm@0 79 if(!pa_handle)
rlm@0 80 return ALC_FALSE;
rlm@0 81
rlm@0 82 #define LOAD_FUNC(f) do { \
rlm@0 83 p##f = GetSymbol(pa_handle, #f); \
rlm@0 84 if(p##f == NULL) \
rlm@0 85 { \
rlm@0 86 CloseLib(pa_handle); \
rlm@0 87 pa_handle = NULL; \
rlm@0 88 return ALC_FALSE; \
rlm@0 89 } \
rlm@0 90 } while(0)
rlm@0 91 LOAD_FUNC(Pa_Initialize);
rlm@0 92 LOAD_FUNC(Pa_Terminate);
rlm@0 93 LOAD_FUNC(Pa_GetErrorText);
rlm@0 94 LOAD_FUNC(Pa_StartStream);
rlm@0 95 LOAD_FUNC(Pa_StopStream);
rlm@0 96 LOAD_FUNC(Pa_OpenStream);
rlm@0 97 LOAD_FUNC(Pa_CloseStream);
rlm@0 98 LOAD_FUNC(Pa_GetDefaultOutputDevice);
rlm@0 99 LOAD_FUNC(Pa_GetStreamInfo);
rlm@0 100 #undef LOAD_FUNC
rlm@0 101 #else
rlm@0 102 pa_handle = (void*)0xDEADBEEF;
rlm@0 103 #endif
rlm@0 104
rlm@0 105 if((err=Pa_Initialize()) != paNoError)
rlm@0 106 {
rlm@0 107 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
rlm@0 108 CloseLib(pa_handle);
rlm@0 109 pa_handle = NULL;
rlm@0 110 return ALC_FALSE;
rlm@0 111 }
rlm@0 112 }
rlm@0 113 return ALC_TRUE;
rlm@0 114 }
rlm@0 115
rlm@0 116
rlm@0 117 typedef struct {
rlm@0 118 PaStream *stream;
rlm@0 119 ALuint update_size;
rlm@0 120
rlm@0 121 RingBuffer *ring;
rlm@0 122 } pa_data;
rlm@0 123
rlm@0 124
rlm@0 125 static int pa_callback(const void *inputBuffer, void *outputBuffer,
rlm@0 126 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
rlm@0 127 const PaStreamCallbackFlags statusFlags, void *userData)
rlm@0 128 {
rlm@0 129 ALCdevice *device = (ALCdevice*)userData;
rlm@0 130
rlm@0 131 (void)inputBuffer;
rlm@0 132 (void)timeInfo;
rlm@0 133 (void)statusFlags;
rlm@0 134
rlm@0 135 aluMixData(device, outputBuffer, framesPerBuffer);
rlm@0 136 return 0;
rlm@0 137 }
rlm@0 138
rlm@0 139 static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
rlm@0 140 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
rlm@0 141 const PaStreamCallbackFlags statusFlags, void *userData)
rlm@0 142 {
rlm@0 143 ALCdevice *device = (ALCdevice*)userData;
rlm@0 144 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 145
rlm@0 146 (void)outputBuffer;
rlm@0 147 (void)timeInfo;
rlm@0 148 (void)statusFlags;
rlm@0 149
rlm@0 150 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
rlm@0 151 return 0;
rlm@0 152 }
rlm@0 153
rlm@0 154
rlm@0 155 static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
rlm@0 156 {
rlm@0 157 PaStreamParameters outParams;
rlm@0 158 pa_data *data;
rlm@0 159 PaError err;
rlm@0 160
rlm@0 161 if(!deviceName)
rlm@0 162 deviceName = pa_device;
rlm@0 163 else if(strcmp(deviceName, pa_device) != 0)
rlm@0 164 return ALC_FALSE;
rlm@0 165
rlm@0 166 data = (pa_data*)calloc(1, sizeof(pa_data));
rlm@0 167 data->update_size = device->UpdateSize;
rlm@0 168
rlm@0 169 device->ExtraData = data;
rlm@0 170
rlm@0 171 outParams.device = GetConfigValueInt("port", "device", -1);
rlm@0 172 if(outParams.device < 0)
rlm@0 173 outParams.device = Pa_GetDefaultOutputDevice();
rlm@0 174 outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
rlm@0 175 (float)device->Frequency;
rlm@0 176 outParams.hostApiSpecificStreamInfo = NULL;
rlm@0 177
rlm@0 178 switch(device->FmtType)
rlm@0 179 {
rlm@0 180 case DevFmtByte:
rlm@0 181 outParams.sampleFormat = paInt8;
rlm@0 182 break;
rlm@0 183 case DevFmtUByte:
rlm@0 184 outParams.sampleFormat = paUInt8;
rlm@0 185 break;
rlm@0 186 case DevFmtUShort:
rlm@0 187 device->FmtType = DevFmtShort;
rlm@0 188 /* fall-through */
rlm@0 189 case DevFmtShort:
rlm@0 190 outParams.sampleFormat = paInt16;
rlm@0 191 break;
rlm@0 192 case DevFmtFloat:
rlm@0 193 outParams.sampleFormat = paFloat32;
rlm@0 194 break;
rlm@0 195 }
rlm@0 196 outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
rlm@0 197
rlm@0 198 SetDefaultChannelOrder(device);
rlm@0 199
rlm@0 200 err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
rlm@0 201 device->UpdateSize, paNoFlag, pa_callback, device);
rlm@0 202 if(err != paNoError)
rlm@0 203 {
rlm@0 204 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
rlm@0 205 device->ExtraData = NULL;
rlm@0 206 free(data);
rlm@0 207 return ALC_FALSE;
rlm@0 208 }
rlm@0 209
rlm@0 210 device->szDeviceName = strdup(deviceName);
rlm@0 211
rlm@0 212 if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans))
rlm@0 213 {
rlm@0 214 if(outParams.channelCount != 1 && outParams.channelCount != 2)
rlm@0 215 {
rlm@0 216 ERR("Unhandled channel count: %u\n", outParams.channelCount);
rlm@0 217 Pa_CloseStream(data->stream);
rlm@0 218 device->ExtraData = NULL;
rlm@0 219 free(data);
rlm@0 220 return ALC_FALSE;
rlm@0 221 }
rlm@0 222 if((device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 223 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount);
rlm@0 224 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
rlm@0 225 device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo);
rlm@0 226 }
rlm@0 227
rlm@0 228 return ALC_TRUE;
rlm@0 229 }
rlm@0 230
rlm@0 231 static void pa_close_playback(ALCdevice *device)
rlm@0 232 {
rlm@0 233 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 234 PaError err;
rlm@0 235
rlm@0 236 err = Pa_CloseStream(data->stream);
rlm@0 237 if(err != paNoError)
rlm@0 238 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
rlm@0 239
rlm@0 240 free(data);
rlm@0 241 device->ExtraData = NULL;
rlm@0 242 }
rlm@0 243
rlm@0 244 static ALCboolean pa_reset_playback(ALCdevice *device)
rlm@0 245 {
rlm@0 246 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 247 const PaStreamInfo *streamInfo;
rlm@0 248 PaError err;
rlm@0 249
rlm@0 250 streamInfo = Pa_GetStreamInfo(data->stream);
rlm@0 251 if(device->Frequency != streamInfo->sampleRate)
rlm@0 252 {
rlm@0 253 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 254 ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate);
rlm@0 255 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 256 device->Frequency = streamInfo->sampleRate;
rlm@0 257 }
rlm@0 258 device->UpdateSize = data->update_size;
rlm@0 259
rlm@0 260 err = Pa_StartStream(data->stream);
rlm@0 261 if(err != paNoError)
rlm@0 262 {
rlm@0 263 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
rlm@0 264 return ALC_FALSE;
rlm@0 265 }
rlm@0 266
rlm@0 267 return ALC_TRUE;
rlm@0 268 }
rlm@0 269
rlm@0 270 static void pa_stop_playback(ALCdevice *device)
rlm@0 271 {
rlm@0 272 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 273 PaError err;
rlm@0 274
rlm@0 275 err = Pa_StopStream(data->stream);
rlm@0 276 if(err != paNoError)
rlm@0 277 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
rlm@0 278 }
rlm@0 279
rlm@0 280
rlm@0 281 static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
rlm@0 282 {
rlm@0 283 PaStreamParameters inParams;
rlm@0 284 ALuint frame_size;
rlm@0 285 pa_data *data;
rlm@0 286 PaError err;
rlm@0 287
rlm@0 288 if(!deviceName)
rlm@0 289 deviceName = pa_device;
rlm@0 290 else if(strcmp(deviceName, pa_device) != 0)
rlm@0 291 return ALC_FALSE;
rlm@0 292
rlm@0 293 data = (pa_data*)calloc(1, sizeof(pa_data));
rlm@0 294 if(data == NULL)
rlm@0 295 {
rlm@0 296 alcSetError(device, ALC_OUT_OF_MEMORY);
rlm@0 297 return ALC_FALSE;
rlm@0 298 }
rlm@0 299
rlm@0 300 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
rlm@0 301 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
rlm@0 302 if(data->ring == NULL)
rlm@0 303 {
rlm@0 304 alcSetError(device, ALC_OUT_OF_MEMORY);
rlm@0 305 goto error;
rlm@0 306 }
rlm@0 307
rlm@0 308 inParams.device = GetConfigValueInt("port", "capture", -1);
rlm@0 309 if(inParams.device < 0)
rlm@0 310 inParams.device = Pa_GetDefaultOutputDevice();
rlm@0 311 inParams.suggestedLatency = 0.0f;
rlm@0 312 inParams.hostApiSpecificStreamInfo = NULL;
rlm@0 313
rlm@0 314 switch(device->FmtType)
rlm@0 315 {
rlm@0 316 case DevFmtByte:
rlm@0 317 inParams.sampleFormat = paInt8;
rlm@0 318 break;
rlm@0 319 case DevFmtUByte:
rlm@0 320 inParams.sampleFormat = paUInt8;
rlm@0 321 break;
rlm@0 322 case DevFmtShort:
rlm@0 323 inParams.sampleFormat = paInt16;
rlm@0 324 break;
rlm@0 325 case DevFmtFloat:
rlm@0 326 inParams.sampleFormat = paFloat32;
rlm@0 327 break;
rlm@0 328 case DevFmtUShort:
rlm@0 329 ERR("Unsigned short samples not supported\n");
rlm@0 330 goto error;
rlm@0 331 }
rlm@0 332 inParams.channelCount = ChannelsFromDevFmt(device->FmtChans);
rlm@0 333
rlm@0 334 err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency,
rlm@0 335 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
rlm@0 336 if(err != paNoError)
rlm@0 337 {
rlm@0 338 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
rlm@0 339 goto error;
rlm@0 340 }
rlm@0 341
rlm@0 342 device->szDeviceName = strdup(deviceName);
rlm@0 343
rlm@0 344 device->ExtraData = data;
rlm@0 345 return ALC_TRUE;
rlm@0 346
rlm@0 347 error:
rlm@0 348 DestroyRingBuffer(data->ring);
rlm@0 349 free(data);
rlm@0 350 return ALC_FALSE;
rlm@0 351 }
rlm@0 352
rlm@0 353 static void pa_close_capture(ALCdevice *device)
rlm@0 354 {
rlm@0 355 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 356 PaError err;
rlm@0 357
rlm@0 358 err = Pa_CloseStream(data->stream);
rlm@0 359 if(err != paNoError)
rlm@0 360 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
rlm@0 361
rlm@0 362 free(data);
rlm@0 363 device->ExtraData = NULL;
rlm@0 364 }
rlm@0 365
rlm@0 366 static void pa_start_capture(ALCdevice *device)
rlm@0 367 {
rlm@0 368 pa_data *data = device->ExtraData;
rlm@0 369 PaError err;
rlm@0 370
rlm@0 371 err = Pa_StartStream(data->stream);
rlm@0 372 if(err != paNoError)
rlm@0 373 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
rlm@0 374 }
rlm@0 375
rlm@0 376 static void pa_stop_capture(ALCdevice *device)
rlm@0 377 {
rlm@0 378 pa_data *data = (pa_data*)device->ExtraData;
rlm@0 379 PaError err;
rlm@0 380
rlm@0 381 err = Pa_StopStream(data->stream);
rlm@0 382 if(err != paNoError)
rlm@0 383 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
rlm@0 384 }
rlm@0 385
rlm@0 386 static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
rlm@0 387 {
rlm@0 388 pa_data *data = device->ExtraData;
rlm@0 389 if(samples <= (ALCuint)RingBufferSize(data->ring))
rlm@0 390 ReadRingBuffer(data->ring, buffer, samples);
rlm@0 391 else
rlm@0 392 alcSetError(device, ALC_INVALID_VALUE);
rlm@0 393 }
rlm@0 394
rlm@0 395 static ALCuint pa_available_samples(ALCdevice *device)
rlm@0 396 {
rlm@0 397 pa_data *data = device->ExtraData;
rlm@0 398 return RingBufferSize(data->ring);
rlm@0 399 }
rlm@0 400
rlm@0 401
rlm@0 402 static const BackendFuncs pa_funcs = {
rlm@0 403 pa_open_playback,
rlm@0 404 pa_close_playback,
rlm@0 405 pa_reset_playback,
rlm@0 406 pa_stop_playback,
rlm@0 407 pa_open_capture,
rlm@0 408 pa_close_capture,
rlm@0 409 pa_start_capture,
rlm@0 410 pa_stop_capture,
rlm@0 411 pa_capture_samples,
rlm@0 412 pa_available_samples
rlm@0 413 };
rlm@0 414
rlm@0 415 ALCboolean alc_pa_init(BackendFuncs *func_list)
rlm@0 416 {
rlm@0 417 if(!pa_load())
rlm@0 418 return ALC_FALSE;
rlm@0 419 *func_list = pa_funcs;
rlm@0 420 return ALC_TRUE;
rlm@0 421 }
rlm@0 422
rlm@0 423 void alc_pa_deinit(void)
rlm@0 424 {
rlm@0 425 if(pa_handle)
rlm@0 426 {
rlm@0 427 Pa_Terminate();
rlm@0 428 #ifdef HAVE_DYNLOAD
rlm@0 429 CloseLib(pa_handle);
rlm@0 430 #endif
rlm@0 431 pa_handle = NULL;
rlm@0 432 }
rlm@0 433 }
rlm@0 434
rlm@0 435 void alc_pa_probe(enum DevProbe type)
rlm@0 436 {
rlm@0 437 switch(type)
rlm@0 438 {
rlm@0 439 case DEVICE_PROBE:
rlm@0 440 AppendDeviceList(pa_device);
rlm@0 441 break;
rlm@0 442 case ALL_DEVICE_PROBE:
rlm@0 443 AppendAllDeviceList(pa_device);
rlm@0 444 break;
rlm@0 445 case CAPTURE_DEVICE_PROBE:
rlm@0 446 AppendCaptureDeviceList(pa_device);
rlm@0 447 break;
rlm@0 448 }
rlm@0 449 }