Fix an infinite loop
[openal-soft.git] / Alc / backends / portaudio.c
blob1dbca9416877b12492849d5ad055bfe203cb8422
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "compat.h"
31 #include "backends/base.h"
33 #include <portaudio.h>
36 static const ALCchar pa_device[] = "PortAudio Default";
39 #ifdef HAVE_DYNLOAD
40 static void *pa_handle;
41 #define MAKE_FUNC(x) static __typeof(x) * p##x
42 MAKE_FUNC(Pa_Initialize);
43 MAKE_FUNC(Pa_Terminate);
44 MAKE_FUNC(Pa_GetErrorText);
45 MAKE_FUNC(Pa_StartStream);
46 MAKE_FUNC(Pa_StopStream);
47 MAKE_FUNC(Pa_OpenStream);
48 MAKE_FUNC(Pa_CloseStream);
49 MAKE_FUNC(Pa_GetDefaultOutputDevice);
50 MAKE_FUNC(Pa_GetDefaultInputDevice);
51 MAKE_FUNC(Pa_GetStreamInfo);
52 #undef MAKE_FUNC
54 #define Pa_Initialize pPa_Initialize
55 #define Pa_Terminate pPa_Terminate
56 #define Pa_GetErrorText pPa_GetErrorText
57 #define Pa_StartStream pPa_StartStream
58 #define Pa_StopStream pPa_StopStream
59 #define Pa_OpenStream pPa_OpenStream
60 #define Pa_CloseStream pPa_CloseStream
61 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
62 #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
63 #define Pa_GetStreamInfo pPa_GetStreamInfo
64 #endif
66 static ALCboolean pa_load(void)
68 PaError err;
70 #ifdef HAVE_DYNLOAD
71 if(!pa_handle)
73 #ifdef _WIN32
74 # define PALIB "portaudio.dll"
75 #elif defined(__APPLE__) && defined(__MACH__)
76 # define PALIB "libportaudio.2.dylib"
77 #elif defined(__OpenBSD__)
78 # define PALIB "libportaudio.so"
79 #else
80 # define PALIB "libportaudio.so.2"
81 #endif
83 pa_handle = LoadLib(PALIB);
84 if(!pa_handle)
85 return ALC_FALSE;
87 #define LOAD_FUNC(f) do { \
88 p##f = GetSymbol(pa_handle, #f); \
89 if(p##f == NULL) \
90 { \
91 CloseLib(pa_handle); \
92 pa_handle = NULL; \
93 return ALC_FALSE; \
94 } \
95 } while(0)
96 LOAD_FUNC(Pa_Initialize);
97 LOAD_FUNC(Pa_Terminate);
98 LOAD_FUNC(Pa_GetErrorText);
99 LOAD_FUNC(Pa_StartStream);
100 LOAD_FUNC(Pa_StopStream);
101 LOAD_FUNC(Pa_OpenStream);
102 LOAD_FUNC(Pa_CloseStream);
103 LOAD_FUNC(Pa_GetDefaultOutputDevice);
104 LOAD_FUNC(Pa_GetDefaultInputDevice);
105 LOAD_FUNC(Pa_GetStreamInfo);
106 #undef LOAD_FUNC
108 if((err=Pa_Initialize()) != paNoError)
110 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
111 CloseLib(pa_handle);
112 pa_handle = NULL;
113 return ALC_FALSE;
116 #else
117 if((err=Pa_Initialize()) != paNoError)
119 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
120 return ALC_FALSE;
122 #endif
123 return ALC_TRUE;
127 typedef struct ALCportPlayback {
128 DERIVE_FROM_TYPE(ALCbackend);
130 PaStream *stream;
131 PaStreamParameters params;
132 ALuint update_size;
133 } ALCportPlayback;
135 static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
136 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
137 const PaStreamCallbackFlags statusFlags, void *userData);
139 static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
140 static void ALCportPlayback_Destruct(ALCportPlayback *self);
141 static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
142 static void ALCportPlayback_close(ALCportPlayback *self);
143 static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
144 static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
145 static void ALCportPlayback_stop(ALCportPlayback *self);
146 static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
147 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
148 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
149 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
150 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
151 DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
153 DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
156 static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
158 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
159 SET_VTABLE2(ALCportPlayback, ALCbackend, self);
161 self->stream = NULL;
164 static void ALCportPlayback_Destruct(ALCportPlayback *self)
166 if(self->stream)
167 Pa_CloseStream(self->stream);
168 self->stream = NULL;
170 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
174 static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
175 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
176 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
178 ALCportPlayback *self = userData;
180 aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
181 return 0;
185 static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
187 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
188 PaError err;
190 if(!name)
191 name = pa_device;
192 else if(strcmp(name, pa_device) != 0)
193 return ALC_INVALID_VALUE;
195 self->update_size = device->UpdateSize;
197 self->params.device = -1;
198 if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
199 self->params.device < 0)
200 self->params.device = Pa_GetDefaultOutputDevice();
201 self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
202 (float)device->Frequency;
203 self->params.hostApiSpecificStreamInfo = NULL;
205 self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
207 switch(device->FmtType)
209 case DevFmtByte:
210 self->params.sampleFormat = paInt8;
211 break;
212 case DevFmtUByte:
213 self->params.sampleFormat = paUInt8;
214 break;
215 case DevFmtUShort:
216 /* fall-through */
217 case DevFmtShort:
218 self->params.sampleFormat = paInt16;
219 break;
220 case DevFmtUInt:
221 /* fall-through */
222 case DevFmtInt:
223 self->params.sampleFormat = paInt32;
224 break;
225 case DevFmtFloat:
226 self->params.sampleFormat = paFloat32;
227 break;
230 retry_open:
231 err = Pa_OpenStream(&self->stream, NULL, &self->params,
232 device->Frequency, device->UpdateSize, paNoFlag,
233 ALCportPlayback_WriteCallback, self
235 if(err != paNoError)
237 if(self->params.sampleFormat == paFloat32)
239 self->params.sampleFormat = paInt16;
240 goto retry_open;
242 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
243 return ALC_INVALID_VALUE;
246 al_string_copy_cstr(&device->DeviceName, name);
248 return ALC_NO_ERROR;
252 static void ALCportPlayback_close(ALCportPlayback *self)
254 PaError err = Pa_CloseStream(self->stream);
255 if(err != paNoError)
256 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
257 self->stream = NULL;
260 static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
262 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
263 const PaStreamInfo *streamInfo;
265 streamInfo = Pa_GetStreamInfo(self->stream);
266 device->Frequency = streamInfo->sampleRate;
267 device->UpdateSize = self->update_size;
269 if(self->params.sampleFormat == paInt8)
270 device->FmtType = DevFmtByte;
271 else if(self->params.sampleFormat == paUInt8)
272 device->FmtType = DevFmtUByte;
273 else if(self->params.sampleFormat == paInt16)
274 device->FmtType = DevFmtShort;
275 else if(self->params.sampleFormat == paInt32)
276 device->FmtType = DevFmtInt;
277 else if(self->params.sampleFormat == paFloat32)
278 device->FmtType = DevFmtFloat;
279 else
281 ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
282 return ALC_FALSE;
285 if(self->params.channelCount == 2)
286 device->FmtChans = DevFmtStereo;
287 else if(self->params.channelCount == 1)
288 device->FmtChans = DevFmtMono;
289 else
291 ERR("Unexpected channel count: %u\n", self->params.channelCount);
292 return ALC_FALSE;
294 SetDefaultChannelOrder(device);
296 return ALC_TRUE;
299 static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
301 PaError err;
303 err = Pa_StartStream(self->stream);
304 if(err != paNoError)
306 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
307 return ALC_FALSE;
310 return ALC_TRUE;
313 static void ALCportPlayback_stop(ALCportPlayback *self)
315 PaError err = Pa_StopStream(self->stream);
316 if(err != paNoError)
317 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
321 typedef struct ALCportCapture {
322 DERIVE_FROM_TYPE(ALCbackend);
324 PaStream *stream;
325 PaStreamParameters params;
327 ll_ringbuffer_t *ring;
328 } ALCportCapture;
330 static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
331 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
332 const PaStreamCallbackFlags statusFlags, void *userData);
334 static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
335 static void ALCportCapture_Destruct(ALCportCapture *self);
336 static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
337 static void ALCportCapture_close(ALCportCapture *self);
338 static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
339 static ALCboolean ALCportCapture_start(ALCportCapture *self);
340 static void ALCportCapture_stop(ALCportCapture *self);
341 static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
342 static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
343 static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
344 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
345 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
346 DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
348 DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
351 static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
353 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
354 SET_VTABLE2(ALCportCapture, ALCbackend, self);
356 self->stream = NULL;
359 static void ALCportCapture_Destruct(ALCportCapture *self)
361 if(self->stream)
362 Pa_CloseStream(self->stream);
363 self->stream = NULL;
365 if(self->ring)
366 ll_ringbuffer_free(self->ring);
367 self->ring = NULL;
369 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
373 static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
374 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
375 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
377 ALCportCapture *self = userData;
378 size_t writable = ll_ringbuffer_write_space(self->ring);
380 if(framesPerBuffer > writable)
381 framesPerBuffer = writable;
382 ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
383 return 0;
387 static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
389 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
390 ALuint samples, frame_size;
391 PaError err;
393 if(!name)
394 name = pa_device;
395 else if(strcmp(name, pa_device) != 0)
396 return ALC_INVALID_VALUE;
398 samples = device->UpdateSize * device->NumUpdates;
399 samples = maxu(samples, 100 * device->Frequency / 1000);
400 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
402 self->ring = ll_ringbuffer_create(samples, frame_size);
403 if(self->ring == NULL) return ALC_INVALID_VALUE;
405 self->params.device = -1;
406 if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
407 self->params.device < 0)
408 self->params.device = Pa_GetDefaultInputDevice();
409 self->params.suggestedLatency = 0.0f;
410 self->params.hostApiSpecificStreamInfo = NULL;
412 switch(device->FmtType)
414 case DevFmtByte:
415 self->params.sampleFormat = paInt8;
416 break;
417 case DevFmtUByte:
418 self->params.sampleFormat = paUInt8;
419 break;
420 case DevFmtShort:
421 self->params.sampleFormat = paInt16;
422 break;
423 case DevFmtInt:
424 self->params.sampleFormat = paInt32;
425 break;
426 case DevFmtFloat:
427 self->params.sampleFormat = paFloat32;
428 break;
429 case DevFmtUInt:
430 case DevFmtUShort:
431 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
432 return ALC_INVALID_VALUE;
434 self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
436 err = Pa_OpenStream(&self->stream, &self->params, NULL,
437 device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
438 ALCportCapture_ReadCallback, self
440 if(err != paNoError)
442 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
443 return ALC_INVALID_VALUE;
446 al_string_copy_cstr(&device->DeviceName, name);
448 return ALC_NO_ERROR;
451 static void ALCportCapture_close(ALCportCapture *self)
453 PaError err = Pa_CloseStream(self->stream);
454 if(err != paNoError)
455 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
456 self->stream = NULL;
458 ll_ringbuffer_free(self->ring);
459 self->ring = NULL;
463 static ALCboolean ALCportCapture_start(ALCportCapture *self)
465 PaError err = Pa_StartStream(self->stream);
466 if(err != paNoError)
468 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
469 return ALC_FALSE;
471 return ALC_TRUE;
474 static void ALCportCapture_stop(ALCportCapture *self)
476 PaError err = Pa_StopStream(self->stream);
477 if(err != paNoError)
478 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
482 static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
484 return ll_ringbuffer_read_space(self->ring);
487 static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
489 ll_ringbuffer_read(self->ring, buffer, samples);
490 return ALC_NO_ERROR;
494 typedef struct ALCportBackendFactory {
495 DERIVE_FROM_TYPE(ALCbackendFactory);
496 } ALCportBackendFactory;
497 #define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
499 static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
500 static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
501 static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
502 static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
503 static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
505 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
508 static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
510 if(!pa_load())
511 return ALC_FALSE;
512 return ALC_TRUE;
515 static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
517 #ifdef HAVE_DYNLOAD
518 if(pa_handle)
520 Pa_Terminate();
521 CloseLib(pa_handle);
522 pa_handle = NULL;
524 #else
525 Pa_Terminate();
526 #endif
529 static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
531 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
532 return ALC_TRUE;
533 return ALC_FALSE;
536 static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
538 switch(type)
540 case ALL_DEVICE_PROBE:
541 AppendAllDevicesList(pa_device);
542 break;
543 case CAPTURE_DEVICE_PROBE:
544 AppendCaptureDeviceList(pa_device);
545 break;
549 static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
551 if(type == ALCbackend_Playback)
553 ALCportPlayback *backend;
554 NEW_OBJ(backend, ALCportPlayback)(device);
555 if(!backend) return NULL;
556 return STATIC_CAST(ALCbackend, backend);
558 if(type == ALCbackend_Capture)
560 ALCportCapture *backend;
561 NEW_OBJ(backend, ALCportCapture)(device);
562 if(!backend) return NULL;
563 return STATIC_CAST(ALCbackend, backend);
566 return NULL;
569 ALCbackendFactory *ALCportBackendFactory_getFactory(void)
571 static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
572 return STATIC_CAST(ALCbackendFactory, &factory);