Implement opening and closing devices in the router
[openal-soft.git] / Alc / backends / portaudio.c
blob807e800015b6cf737490309f3a25004f19b71b69
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 ALCportPlayback_lock(self);
181 aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
182 ALCportPlayback_unlock(self);
183 return 0;
187 static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
189 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
190 PaError err;
192 if(!name)
193 name = pa_device;
194 else if(strcmp(name, pa_device) != 0)
195 return ALC_INVALID_VALUE;
197 self->update_size = device->UpdateSize;
199 self->params.device = -1;
200 if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
201 self->params.device < 0)
202 self->params.device = Pa_GetDefaultOutputDevice();
203 self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
204 (float)device->Frequency;
205 self->params.hostApiSpecificStreamInfo = NULL;
207 self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
209 switch(device->FmtType)
211 case DevFmtByte:
212 self->params.sampleFormat = paInt8;
213 break;
214 case DevFmtUByte:
215 self->params.sampleFormat = paUInt8;
216 break;
217 case DevFmtUShort:
218 /* fall-through */
219 case DevFmtShort:
220 self->params.sampleFormat = paInt16;
221 break;
222 case DevFmtUInt:
223 /* fall-through */
224 case DevFmtInt:
225 self->params.sampleFormat = paInt32;
226 break;
227 case DevFmtFloat:
228 self->params.sampleFormat = paFloat32;
229 break;
232 retry_open:
233 err = Pa_OpenStream(&self->stream, NULL, &self->params,
234 device->Frequency, device->UpdateSize, paNoFlag,
235 ALCportPlayback_WriteCallback, self
237 if(err != paNoError)
239 if(self->params.sampleFormat == paFloat32)
241 self->params.sampleFormat = paInt16;
242 goto retry_open;
244 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
245 return ALC_INVALID_VALUE;
248 alstr_copy_cstr(&device->DeviceName, name);
250 return ALC_NO_ERROR;
254 static void ALCportPlayback_close(ALCportPlayback *self)
256 PaError err = Pa_CloseStream(self->stream);
257 if(err != paNoError)
258 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
259 self->stream = NULL;
262 static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
264 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
265 const PaStreamInfo *streamInfo;
267 streamInfo = Pa_GetStreamInfo(self->stream);
268 device->Frequency = streamInfo->sampleRate;
269 device->UpdateSize = self->update_size;
271 if(self->params.sampleFormat == paInt8)
272 device->FmtType = DevFmtByte;
273 else if(self->params.sampleFormat == paUInt8)
274 device->FmtType = DevFmtUByte;
275 else if(self->params.sampleFormat == paInt16)
276 device->FmtType = DevFmtShort;
277 else if(self->params.sampleFormat == paInt32)
278 device->FmtType = DevFmtInt;
279 else if(self->params.sampleFormat == paFloat32)
280 device->FmtType = DevFmtFloat;
281 else
283 ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
284 return ALC_FALSE;
287 if(self->params.channelCount == 2)
288 device->FmtChans = DevFmtStereo;
289 else if(self->params.channelCount == 1)
290 device->FmtChans = DevFmtMono;
291 else
293 ERR("Unexpected channel count: %u\n", self->params.channelCount);
294 return ALC_FALSE;
296 SetDefaultChannelOrder(device);
298 return ALC_TRUE;
301 static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
303 PaError err;
305 err = Pa_StartStream(self->stream);
306 if(err != paNoError)
308 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
309 return ALC_FALSE;
312 return ALC_TRUE;
315 static void ALCportPlayback_stop(ALCportPlayback *self)
317 PaError err = Pa_StopStream(self->stream);
318 if(err != paNoError)
319 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
323 typedef struct ALCportCapture {
324 DERIVE_FROM_TYPE(ALCbackend);
326 PaStream *stream;
327 PaStreamParameters params;
329 ll_ringbuffer_t *ring;
330 } ALCportCapture;
332 static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
333 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
334 const PaStreamCallbackFlags statusFlags, void *userData);
336 static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
337 static void ALCportCapture_Destruct(ALCportCapture *self);
338 static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
339 static void ALCportCapture_close(ALCportCapture *self);
340 static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
341 static ALCboolean ALCportCapture_start(ALCportCapture *self);
342 static void ALCportCapture_stop(ALCportCapture *self);
343 static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
344 static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
345 static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
346 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
347 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
348 DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
350 DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
353 static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
355 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
356 SET_VTABLE2(ALCportCapture, ALCbackend, self);
358 self->stream = NULL;
361 static void ALCportCapture_Destruct(ALCportCapture *self)
363 if(self->stream)
364 Pa_CloseStream(self->stream);
365 self->stream = NULL;
367 if(self->ring)
368 ll_ringbuffer_free(self->ring);
369 self->ring = NULL;
371 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
375 static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
376 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
377 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
379 ALCportCapture *self = userData;
380 size_t writable = ll_ringbuffer_write_space(self->ring);
382 if(framesPerBuffer > writable)
383 framesPerBuffer = writable;
384 ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
385 return 0;
389 static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
391 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
392 ALuint samples, frame_size;
393 PaError err;
395 if(!name)
396 name = pa_device;
397 else if(strcmp(name, pa_device) != 0)
398 return ALC_INVALID_VALUE;
400 samples = device->UpdateSize * device->NumUpdates;
401 samples = maxu(samples, 100 * device->Frequency / 1000);
402 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
404 self->ring = ll_ringbuffer_create(samples, frame_size);
405 if(self->ring == NULL) return ALC_INVALID_VALUE;
407 self->params.device = -1;
408 if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
409 self->params.device < 0)
410 self->params.device = Pa_GetDefaultInputDevice();
411 self->params.suggestedLatency = 0.0f;
412 self->params.hostApiSpecificStreamInfo = NULL;
414 switch(device->FmtType)
416 case DevFmtByte:
417 self->params.sampleFormat = paInt8;
418 break;
419 case DevFmtUByte:
420 self->params.sampleFormat = paUInt8;
421 break;
422 case DevFmtShort:
423 self->params.sampleFormat = paInt16;
424 break;
425 case DevFmtInt:
426 self->params.sampleFormat = paInt32;
427 break;
428 case DevFmtFloat:
429 self->params.sampleFormat = paFloat32;
430 break;
431 case DevFmtUInt:
432 case DevFmtUShort:
433 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
434 return ALC_INVALID_VALUE;
436 self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
438 err = Pa_OpenStream(&self->stream, &self->params, NULL,
439 device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
440 ALCportCapture_ReadCallback, self
442 if(err != paNoError)
444 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
445 return ALC_INVALID_VALUE;
448 alstr_copy_cstr(&device->DeviceName, name);
450 return ALC_NO_ERROR;
453 static void ALCportCapture_close(ALCportCapture *self)
455 PaError err = Pa_CloseStream(self->stream);
456 if(err != paNoError)
457 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
458 self->stream = NULL;
460 ll_ringbuffer_free(self->ring);
461 self->ring = NULL;
465 static ALCboolean ALCportCapture_start(ALCportCapture *self)
467 PaError err = Pa_StartStream(self->stream);
468 if(err != paNoError)
470 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
471 return ALC_FALSE;
473 return ALC_TRUE;
476 static void ALCportCapture_stop(ALCportCapture *self)
478 PaError err = Pa_StopStream(self->stream);
479 if(err != paNoError)
480 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
484 static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
486 return ll_ringbuffer_read_space(self->ring);
489 static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
491 ll_ringbuffer_read(self->ring, buffer, samples);
492 return ALC_NO_ERROR;
496 typedef struct ALCportBackendFactory {
497 DERIVE_FROM_TYPE(ALCbackendFactory);
498 } ALCportBackendFactory;
499 #define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
501 static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
502 static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
503 static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
504 static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
505 static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
507 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
510 static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
512 if(!pa_load())
513 return ALC_FALSE;
514 return ALC_TRUE;
517 static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
519 #ifdef HAVE_DYNLOAD
520 if(pa_handle)
522 Pa_Terminate();
523 CloseLib(pa_handle);
524 pa_handle = NULL;
526 #else
527 Pa_Terminate();
528 #endif
531 static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
533 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
534 return ALC_TRUE;
535 return ALC_FALSE;
538 static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
540 switch(type)
542 case ALL_DEVICE_PROBE:
543 AppendAllDevicesList(pa_device);
544 break;
545 case CAPTURE_DEVICE_PROBE:
546 AppendCaptureDeviceList(pa_device);
547 break;
551 static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
553 if(type == ALCbackend_Playback)
555 ALCportPlayback *backend;
556 NEW_OBJ(backend, ALCportPlayback)(device);
557 if(!backend) return NULL;
558 return STATIC_CAST(ALCbackend, backend);
560 if(type == ALCbackend_Capture)
562 ALCportCapture *backend;
563 NEW_OBJ(backend, ALCportCapture)(device);
564 if(!backend) return NULL;
565 return STATIC_CAST(ALCbackend, backend);
568 return NULL;
571 ALCbackendFactory *ALCportBackendFactory_getFactory(void)
573 static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
574 return STATIC_CAST(ALCbackendFactory, &factory);