Update a comment
[openal-soft.git] / Alc / backends / portaudio.c
blob7c0cf4a6cffec9c63ebc029b03a80626ec056062
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 <portaudio.h>
34 static const ALCchar pa_device[] = "PortAudio Default";
37 #ifdef HAVE_DYNLOAD
38 static void *pa_handle;
39 #define MAKE_FUNC(x) static __typeof(x) * p##x
40 MAKE_FUNC(Pa_Initialize);
41 MAKE_FUNC(Pa_Terminate);
42 MAKE_FUNC(Pa_GetErrorText);
43 MAKE_FUNC(Pa_StartStream);
44 MAKE_FUNC(Pa_StopStream);
45 MAKE_FUNC(Pa_OpenStream);
46 MAKE_FUNC(Pa_CloseStream);
47 MAKE_FUNC(Pa_GetDefaultOutputDevice);
48 MAKE_FUNC(Pa_GetDefaultInputDevice);
49 MAKE_FUNC(Pa_GetStreamInfo);
50 #undef MAKE_FUNC
52 #define Pa_Initialize pPa_Initialize
53 #define Pa_Terminate pPa_Terminate
54 #define Pa_GetErrorText pPa_GetErrorText
55 #define Pa_StartStream pPa_StartStream
56 #define Pa_StopStream pPa_StopStream
57 #define Pa_OpenStream pPa_OpenStream
58 #define Pa_CloseStream pPa_CloseStream
59 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
60 #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
61 #define Pa_GetStreamInfo pPa_GetStreamInfo
62 #endif
64 static ALCboolean pa_load(void)
66 PaError err;
68 #ifdef HAVE_DYNLOAD
69 if(!pa_handle)
71 #ifdef _WIN32
72 # define PALIB "portaudio.dll"
73 #elif defined(__APPLE__) && defined(__MACH__)
74 # define PALIB "libportaudio.2.dylib"
75 #elif defined(__OpenBSD__)
76 # define PALIB "libportaudio.so"
77 #else
78 # define PALIB "libportaudio.so.2"
79 #endif
81 pa_handle = LoadLib(PALIB);
82 if(!pa_handle)
83 return ALC_FALSE;
85 #define LOAD_FUNC(f) do { \
86 p##f = GetSymbol(pa_handle, #f); \
87 if(p##f == NULL) \
88 { \
89 CloseLib(pa_handle); \
90 pa_handle = NULL; \
91 return ALC_FALSE; \
92 } \
93 } while(0)
94 LOAD_FUNC(Pa_Initialize);
95 LOAD_FUNC(Pa_Terminate);
96 LOAD_FUNC(Pa_GetErrorText);
97 LOAD_FUNC(Pa_StartStream);
98 LOAD_FUNC(Pa_StopStream);
99 LOAD_FUNC(Pa_OpenStream);
100 LOAD_FUNC(Pa_CloseStream);
101 LOAD_FUNC(Pa_GetDefaultOutputDevice);
102 LOAD_FUNC(Pa_GetDefaultInputDevice);
103 LOAD_FUNC(Pa_GetStreamInfo);
104 #undef LOAD_FUNC
106 if((err=Pa_Initialize()) != paNoError)
108 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
109 CloseLib(pa_handle);
110 pa_handle = NULL;
111 return ALC_FALSE;
114 #else
115 if((err=Pa_Initialize()) != paNoError)
117 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
118 return ALC_FALSE;
120 #endif
121 return ALC_TRUE;
125 typedef struct {
126 PaStream *stream;
127 PaStreamParameters params;
128 ALuint update_size;
130 RingBuffer *ring;
131 } pa_data;
134 static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
135 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
136 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
138 ALCdevice *device = (ALCdevice*)userData;
140 aluMixData(device, outputBuffer, framesPerBuffer);
141 return 0;
144 static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
145 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
146 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
148 ALCdevice *device = (ALCdevice*)userData;
149 pa_data *data = (pa_data*)device->ExtraData;
151 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
152 return 0;
156 static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
158 pa_data *data;
159 PaError err;
161 if(!deviceName)
162 deviceName = pa_device;
163 else if(strcmp(deviceName, pa_device) != 0)
164 return ALC_INVALID_VALUE;
166 data = (pa_data*)calloc(1, sizeof(pa_data));
167 data->update_size = device->UpdateSize;
169 data->params.device = -1;
170 if(!ConfigValueInt("port", "device", &data->params.device) ||
171 data->params.device < 0)
172 data->params.device = Pa_GetDefaultOutputDevice();
173 data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
174 (float)device->Frequency;
175 data->params.hostApiSpecificStreamInfo = NULL;
177 data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
179 switch(device->FmtType)
181 case DevFmtByte:
182 data->params.sampleFormat = paInt8;
183 break;
184 case DevFmtUByte:
185 data->params.sampleFormat = paUInt8;
186 break;
187 case DevFmtUShort:
188 /* fall-through */
189 case DevFmtShort:
190 data->params.sampleFormat = paInt16;
191 break;
192 case DevFmtUInt:
193 /* fall-through */
194 case DevFmtInt:
195 data->params.sampleFormat = paInt32;
196 break;
197 case DevFmtFloat:
198 data->params.sampleFormat = paFloat32;
199 break;
202 retry_open:
203 err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
204 device->UpdateSize, paNoFlag, pa_callback, device);
205 if(err != paNoError)
207 if(data->params.sampleFormat == paFloat32)
209 data->params.sampleFormat = paInt16;
210 goto retry_open;
212 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
213 free(data);
214 return ALC_INVALID_VALUE;
217 device->ExtraData = data;
218 al_string_copy_cstr(&device->DeviceName, deviceName);
220 return ALC_NO_ERROR;
223 static void pa_close_playback(ALCdevice *device)
225 pa_data *data = (pa_data*)device->ExtraData;
226 PaError err;
228 err = Pa_CloseStream(data->stream);
229 if(err != paNoError)
230 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
232 free(data);
233 device->ExtraData = NULL;
236 static ALCboolean pa_reset_playback(ALCdevice *device)
238 pa_data *data = (pa_data*)device->ExtraData;
239 const PaStreamInfo *streamInfo;
241 streamInfo = Pa_GetStreamInfo(data->stream);
242 device->Frequency = streamInfo->sampleRate;
243 device->UpdateSize = data->update_size;
245 if(data->params.sampleFormat == paInt8)
246 device->FmtType = DevFmtByte;
247 else if(data->params.sampleFormat == paUInt8)
248 device->FmtType = DevFmtUByte;
249 else if(data->params.sampleFormat == paInt16)
250 device->FmtType = DevFmtShort;
251 else if(data->params.sampleFormat == paInt32)
252 device->FmtType = DevFmtInt;
253 else if(data->params.sampleFormat == paFloat32)
254 device->FmtType = DevFmtFloat;
255 else
257 ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
258 return ALC_FALSE;
261 if(data->params.channelCount == 2)
262 device->FmtChans = DevFmtStereo;
263 else if(data->params.channelCount == 1)
264 device->FmtChans = DevFmtMono;
265 else
267 ERR("Unexpected channel count: %u\n", data->params.channelCount);
268 return ALC_FALSE;
270 SetDefaultChannelOrder(device);
272 return ALC_TRUE;
275 static ALCboolean pa_start_playback(ALCdevice *device)
277 pa_data *data = (pa_data*)device->ExtraData;
278 PaError err;
280 err = Pa_StartStream(data->stream);
281 if(err != paNoError)
283 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
284 return ALC_FALSE;
287 return ALC_TRUE;
290 static void pa_stop_playback(ALCdevice *device)
292 pa_data *data = (pa_data*)device->ExtraData;
293 PaError err;
295 err = Pa_StopStream(data->stream);
296 if(err != paNoError)
297 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
301 static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
303 ALuint frame_size;
304 pa_data *data;
305 PaError err;
307 if(!deviceName)
308 deviceName = pa_device;
309 else if(strcmp(deviceName, pa_device) != 0)
310 return ALC_INVALID_VALUE;
312 data = (pa_data*)calloc(1, sizeof(pa_data));
313 if(data == NULL)
314 return ALC_OUT_OF_MEMORY;
316 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
317 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
318 if(data->ring == NULL)
319 goto error;
321 data->params.device = -1;
322 if(!ConfigValueInt("port", "capture", &data->params.device) ||
323 data->params.device < 0)
324 data->params.device = Pa_GetDefaultInputDevice();
325 data->params.suggestedLatency = 0.0f;
326 data->params.hostApiSpecificStreamInfo = NULL;
328 switch(device->FmtType)
330 case DevFmtByte:
331 data->params.sampleFormat = paInt8;
332 break;
333 case DevFmtUByte:
334 data->params.sampleFormat = paUInt8;
335 break;
336 case DevFmtShort:
337 data->params.sampleFormat = paInt16;
338 break;
339 case DevFmtInt:
340 data->params.sampleFormat = paInt32;
341 break;
342 case DevFmtFloat:
343 data->params.sampleFormat = paFloat32;
344 break;
345 case DevFmtUInt:
346 case DevFmtUShort:
347 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
348 goto error;
350 data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
352 err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
353 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
354 if(err != paNoError)
356 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
357 goto error;
360 al_string_copy_cstr(&device->DeviceName, deviceName);
362 device->ExtraData = data;
363 return ALC_NO_ERROR;
365 error:
366 DestroyRingBuffer(data->ring);
367 free(data);
368 return ALC_INVALID_VALUE;
371 static void pa_close_capture(ALCdevice *device)
373 pa_data *data = (pa_data*)device->ExtraData;
374 PaError err;
376 err = Pa_CloseStream(data->stream);
377 if(err != paNoError)
378 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
380 DestroyRingBuffer(data->ring);
381 data->ring = NULL;
383 free(data);
384 device->ExtraData = NULL;
387 static void pa_start_capture(ALCdevice *device)
389 pa_data *data = device->ExtraData;
390 PaError err;
392 err = Pa_StartStream(data->stream);
393 if(err != paNoError)
394 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
397 static void pa_stop_capture(ALCdevice *device)
399 pa_data *data = (pa_data*)device->ExtraData;
400 PaError err;
402 err = Pa_StopStream(data->stream);
403 if(err != paNoError)
404 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
407 static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
409 pa_data *data = device->ExtraData;
410 ReadRingBuffer(data->ring, buffer, samples);
411 return ALC_NO_ERROR;
414 static ALCuint pa_available_samples(ALCdevice *device)
416 pa_data *data = device->ExtraData;
417 return RingBufferSize(data->ring);
421 static const BackendFuncs pa_funcs = {
422 pa_open_playback,
423 pa_close_playback,
424 pa_reset_playback,
425 pa_start_playback,
426 pa_stop_playback,
427 pa_open_capture,
428 pa_close_capture,
429 pa_start_capture,
430 pa_stop_capture,
431 pa_capture_samples,
432 pa_available_samples
435 ALCboolean alc_pa_init(BackendFuncs *func_list)
437 if(!pa_load())
438 return ALC_FALSE;
439 *func_list = pa_funcs;
440 return ALC_TRUE;
443 void alc_pa_deinit(void)
445 #ifdef HAVE_DYNLOAD
446 if(pa_handle)
448 Pa_Terminate();
449 CloseLib(pa_handle);
450 pa_handle = NULL;
452 #else
453 Pa_Terminate();
454 #endif
457 void alc_pa_probe(enum DevProbe type)
459 switch(type)
461 case ALL_DEVICE_PROBE:
462 AppendAllDevicesList(pa_device);
463 break;
464 case CAPTURE_DEVICE_PROBE:
465 AppendCaptureDeviceList(pa_device);
466 break;