Avoid modifying the ALCdevice in portaudio's open method
[openal-soft.git] / Alc / backends / portaudio.c
blobc4f80e1ad6b0c1b50bc7d07c9cb5b2aa73e1ac13
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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>
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 #ifdef HAVE_DYNLOAD
37 static void *pa_handle;
38 #define MAKE_FUNC(x) static typeof(x) * p##x
39 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_FUNC
50 #define Pa_Initialize pPa_Initialize
51 #define Pa_Terminate pPa_Terminate
52 #define Pa_GetErrorText pPa_GetErrorText
53 #define Pa_StartStream pPa_StartStream
54 #define Pa_StopStream pPa_StopStream
55 #define Pa_OpenStream pPa_OpenStream
56 #define Pa_CloseStream pPa_CloseStream
57 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
58 #define Pa_GetStreamInfo pPa_GetStreamInfo
59 #endif
61 static ALCboolean pa_load(void)
63 PaError err;
65 #ifdef HAVE_DYNLOAD
66 if(!pa_handle)
68 #ifdef _WIN32
69 # 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 #else
75 # define PALIB "libportaudio.so.2"
76 #endif
78 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_FUNC
102 if((err=Pa_Initialize()) != paNoError)
104 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
105 CloseLib(pa_handle);
106 pa_handle = NULL;
107 return ALC_FALSE;
110 #else
111 if((err=Pa_Initialize()) != paNoError)
113 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
114 return ALC_FALSE;
116 #endif
117 return ALC_TRUE;
121 typedef struct {
122 PaStream *stream;
123 PaStreamParameters params;
124 ALuint update_size;
126 RingBuffer *ring;
127 } pa_data;
130 static int pa_callback(const void *inputBuffer, void *outputBuffer,
131 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
132 const PaStreamCallbackFlags statusFlags, void *userData)
134 ALCdevice *device = (ALCdevice*)userData;
136 (void)inputBuffer;
137 (void)timeInfo;
138 (void)statusFlags;
140 aluMixData(device, outputBuffer, framesPerBuffer);
141 return 0;
144 static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
145 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
146 const PaStreamCallbackFlags statusFlags, void *userData)
148 ALCdevice *device = (ALCdevice*)userData;
149 pa_data *data = (pa_data*)device->ExtraData;
151 (void)outputBuffer;
152 (void)timeInfo;
153 (void)statusFlags;
155 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
156 return 0;
160 static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
162 pa_data *data;
163 PaError err;
165 if(!deviceName)
166 deviceName = pa_device;
167 else if(strcmp(deviceName, pa_device) != 0)
168 return ALC_INVALID_VALUE;
170 data = (pa_data*)calloc(1, sizeof(pa_data));
171 data->update_size = device->UpdateSize;
173 data->params.device = -1;
174 if(!ConfigValueInt("port", "device", &data->params.device) ||
175 data->params.device < 0)
176 data->params.device = Pa_GetDefaultOutputDevice();
177 data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
178 (float)device->Frequency;
179 data->params.hostApiSpecificStreamInfo = NULL;
181 data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
183 switch(device->FmtType)
185 case DevFmtByte:
186 data->params.sampleFormat = paInt8;
187 break;
188 case DevFmtUByte:
189 data->params.sampleFormat = paUInt8;
190 break;
191 case DevFmtUShort:
192 /* fall-through */
193 case DevFmtShort:
194 data->params.sampleFormat = paInt16;
195 break;
196 case DevFmtUInt:
197 /* fall-through */
198 case DevFmtInt:
199 data->params.sampleFormat = paInt32;
200 break;
201 case DevFmtFloat:
202 data->params.sampleFormat = paFloat32;
203 break;
206 retry_open:
207 err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
208 device->UpdateSize, paNoFlag, pa_callback, device);
209 if(err != paNoError)
211 if(data->params.sampleFormat == paFloat32)
213 data->params.sampleFormat = paInt16;
214 goto retry_open;
216 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
217 free(data);
218 return ALC_INVALID_VALUE;
221 device->ExtraData = data;
222 device->szDeviceName = strdup(deviceName);
224 return ALC_NO_ERROR;
227 static void pa_close_playback(ALCdevice *device)
229 pa_data *data = (pa_data*)device->ExtraData;
230 PaError err;
232 err = Pa_CloseStream(data->stream);
233 if(err != paNoError)
234 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
236 free(data);
237 device->ExtraData = NULL;
240 static ALCboolean pa_reset_playback(ALCdevice *device)
242 pa_data *data = (pa_data*)device->ExtraData;
243 const PaStreamInfo *streamInfo;
245 streamInfo = Pa_GetStreamInfo(data->stream);
246 device->Frequency = streamInfo->sampleRate;
247 device->UpdateSize = data->update_size;
249 if(data->params.sampleFormat == paInt8)
250 device->FmtType = DevFmtByte;
251 else if(data->params.sampleFormat == paUInt8)
252 device->FmtType = DevFmtUByte;
253 else if(data->params.sampleFormat == paInt16)
254 device->FmtType = DevFmtShort;
255 else if(data->params.sampleFormat == paInt32)
256 device->FmtType = DevFmtInt;
257 else if(data->params.sampleFormat == paFloat32)
258 device->FmtType = DevFmtFloat;
259 else
261 ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
262 return ALC_FALSE;
265 if(data->params.channelCount == 2)
266 device->FmtChans = DevFmtStereo;
267 else if(data->params.channelCount == 1)
268 device->FmtChans = DevFmtMono;
269 else
271 ERR("Unexpected channel count: %u\n", data->params.channelCount);
272 return ALC_FALSE;
274 SetDefaultChannelOrder(device);
276 return ALC_TRUE;
279 static ALCboolean pa_start_playback(ALCdevice *device)
281 pa_data *data = (pa_data*)device->ExtraData;
282 PaError err;
284 err = Pa_StartStream(data->stream);
285 if(err != paNoError)
287 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
288 return ALC_FALSE;
291 return ALC_TRUE;
294 static void pa_stop_playback(ALCdevice *device)
296 pa_data *data = (pa_data*)device->ExtraData;
297 PaError err;
299 err = Pa_StopStream(data->stream);
300 if(err != paNoError)
301 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
305 static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
307 ALuint frame_size;
308 pa_data *data;
309 PaError err;
311 if(!deviceName)
312 deviceName = pa_device;
313 else if(strcmp(deviceName, pa_device) != 0)
314 return ALC_INVALID_VALUE;
316 data = (pa_data*)calloc(1, sizeof(pa_data));
317 if(data == NULL)
318 return ALC_OUT_OF_MEMORY;
320 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
321 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
322 if(data->ring == NULL)
323 goto error;
325 data->params.device = -1;
326 if(!ConfigValueInt("port", "capture", &data->params.device) ||
327 data->params.device < 0)
328 data->params.device = Pa_GetDefaultOutputDevice();
329 data->params.suggestedLatency = 0.0f;
330 data->params.hostApiSpecificStreamInfo = NULL;
332 switch(device->FmtType)
334 case DevFmtByte:
335 data->params.sampleFormat = paInt8;
336 break;
337 case DevFmtUByte:
338 data->params.sampleFormat = paUInt8;
339 break;
340 case DevFmtShort:
341 data->params.sampleFormat = paInt16;
342 break;
343 case DevFmtInt:
344 data->params.sampleFormat = paInt32;
345 break;
346 case DevFmtFloat:
347 data->params.sampleFormat = paFloat32;
348 break;
349 case DevFmtUInt:
350 case DevFmtUShort:
351 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
352 goto error;
354 data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
356 err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
357 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
358 if(err != paNoError)
360 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
361 goto error;
364 device->szDeviceName = strdup(deviceName);
366 device->ExtraData = data;
367 return ALC_NO_ERROR;
369 error:
370 DestroyRingBuffer(data->ring);
371 free(data);
372 return ALC_INVALID_VALUE;
375 static void pa_close_capture(ALCdevice *device)
377 pa_data *data = (pa_data*)device->ExtraData;
378 PaError err;
380 err = Pa_CloseStream(data->stream);
381 if(err != paNoError)
382 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
384 free(data);
385 device->ExtraData = NULL;
388 static void pa_start_capture(ALCdevice *device)
390 pa_data *data = device->ExtraData;
391 PaError err;
393 err = Pa_StartStream(data->stream);
394 if(err != paNoError)
395 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
398 static void pa_stop_capture(ALCdevice *device)
400 pa_data *data = (pa_data*)device->ExtraData;
401 PaError err;
403 err = Pa_StopStream(data->stream);
404 if(err != paNoError)
405 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
408 static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
410 pa_data *data = device->ExtraData;
411 ReadRingBuffer(data->ring, buffer, samples);
412 return ALC_NO_ERROR;
415 static ALCuint pa_available_samples(ALCdevice *device)
417 pa_data *data = device->ExtraData;
418 return RingBufferSize(data->ring);
422 static const BackendFuncs pa_funcs = {
423 pa_open_playback,
424 pa_close_playback,
425 pa_reset_playback,
426 pa_start_playback,
427 pa_stop_playback,
428 pa_open_capture,
429 pa_close_capture,
430 pa_start_capture,
431 pa_stop_capture,
432 pa_capture_samples,
433 pa_available_samples
436 ALCboolean alc_pa_init(BackendFuncs *func_list)
438 if(!pa_load())
439 return ALC_FALSE;
440 *func_list = pa_funcs;
441 return ALC_TRUE;
444 void alc_pa_deinit(void)
446 #ifdef HAVE_DYNLOAD
447 if(pa_handle)
449 Pa_Terminate();
450 CloseLib(pa_handle);
451 pa_handle = NULL;
453 #else
454 Pa_Terminate();
455 #endif
458 void alc_pa_probe(enum DevProbe type)
460 switch(type)
462 case ALL_DEVICE_PROBE:
463 AppendAllDeviceList(pa_device);
464 break;
465 case CAPTURE_DEVICE_PROBE:
466 AppendCaptureDeviceList(pa_device);
467 break;