Fix typo
[openal-soft/openal-hmr.git] / Alc / backends / portaudio.c
blob03e54d149d1832d950d3f87236d039ebdf25e6d5
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 ALuint update_size;
125 RingBuffer *ring;
126 } pa_data;
129 static int pa_callback(const void *inputBuffer, void *outputBuffer,
130 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
131 const PaStreamCallbackFlags statusFlags, void *userData)
133 ALCdevice *device = (ALCdevice*)userData;
135 (void)inputBuffer;
136 (void)timeInfo;
137 (void)statusFlags;
139 aluMixData(device, outputBuffer, framesPerBuffer);
140 return 0;
143 static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
144 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
145 const PaStreamCallbackFlags statusFlags, void *userData)
147 ALCdevice *device = (ALCdevice*)userData;
148 pa_data *data = (pa_data*)device->ExtraData;
150 (void)outputBuffer;
151 (void)timeInfo;
152 (void)statusFlags;
154 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
155 return 0;
159 static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
161 PaStreamParameters outParams;
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 device->ExtraData = data;
175 outParams.device = -1;
176 if(!ConfigValueInt("port", "device", &outParams.device) || outParams.device < 0)
177 outParams.device = Pa_GetDefaultOutputDevice();
178 outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
179 (float)device->Frequency;
180 outParams.hostApiSpecificStreamInfo = NULL;
182 outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
184 retry_open:
185 switch(device->FmtType)
187 case DevFmtByte:
188 outParams.sampleFormat = paInt8;
189 break;
190 case DevFmtUByte:
191 outParams.sampleFormat = paUInt8;
192 break;
193 case DevFmtUShort:
194 device->FmtType = DevFmtShort;
195 /* fall-through */
196 case DevFmtShort:
197 outParams.sampleFormat = paInt16;
198 break;
199 case DevFmtUInt:
200 device->FmtType = DevFmtInt;
201 /* fall-through */
202 case DevFmtInt:
203 outParams.sampleFormat = paInt32;
204 break;
205 case DevFmtFloat:
206 outParams.sampleFormat = paFloat32;
207 break;
210 err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
211 device->UpdateSize, paNoFlag, pa_callback, device);
212 if(err != paNoError)
214 if(device->FmtType == DevFmtFloat)
216 device->FmtType = DevFmtShort;
217 goto retry_open;
219 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
220 device->ExtraData = NULL;
221 free(data);
222 return ALC_INVALID_VALUE;
225 if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans))
227 if(outParams.channelCount != 1 && outParams.channelCount != 2)
229 ERR("Unhandled channel count: %u\n", outParams.channelCount);
230 Pa_CloseStream(data->stream);
231 device->ExtraData = NULL;
232 free(data);
233 return ALC_INVALID_VALUE;
235 if((device->Flags&DEVICE_CHANNELS_REQUEST))
236 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount);
237 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
238 device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo);
240 SetDefaultChannelOrder(device);
242 device->szDeviceName = strdup(deviceName);
244 return ALC_NO_ERROR;
247 static void pa_close_playback(ALCdevice *device)
249 pa_data *data = (pa_data*)device->ExtraData;
250 PaError err;
252 err = Pa_CloseStream(data->stream);
253 if(err != paNoError)
254 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
256 free(data);
257 device->ExtraData = NULL;
260 static ALCboolean pa_reset_playback(ALCdevice *device)
262 pa_data *data = (pa_data*)device->ExtraData;
263 const PaStreamInfo *streamInfo;
265 streamInfo = Pa_GetStreamInfo(data->stream);
266 device->Frequency = streamInfo->sampleRate;
267 device->UpdateSize = data->update_size;
269 return ALC_TRUE;
272 static ALCboolean pa_start_playback(ALCdevice *device)
274 pa_data *data = (pa_data*)device->ExtraData;
275 PaError err;
277 err = Pa_StartStream(data->stream);
278 if(err != paNoError)
280 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
281 return ALC_FALSE;
284 return ALC_TRUE;
287 static void pa_stop_playback(ALCdevice *device)
289 pa_data *data = (pa_data*)device->ExtraData;
290 PaError err;
292 err = Pa_StopStream(data->stream);
293 if(err != paNoError)
294 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
298 static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
300 PaStreamParameters inParams;
301 ALuint frame_size;
302 pa_data *data;
303 PaError err;
305 if(!deviceName)
306 deviceName = pa_device;
307 else if(strcmp(deviceName, pa_device) != 0)
308 return ALC_INVALID_VALUE;
310 data = (pa_data*)calloc(1, sizeof(pa_data));
311 if(data == NULL)
312 return ALC_OUT_OF_MEMORY;
314 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
315 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
316 if(data->ring == NULL)
317 goto error;
319 inParams.device = -1;
320 if(!ConfigValueInt("port", "capture", &inParams.device) || inParams.device < 0)
321 inParams.device = Pa_GetDefaultOutputDevice();
322 inParams.suggestedLatency = 0.0f;
323 inParams.hostApiSpecificStreamInfo = NULL;
325 switch(device->FmtType)
327 case DevFmtByte:
328 inParams.sampleFormat = paInt8;
329 break;
330 case DevFmtUByte:
331 inParams.sampleFormat = paUInt8;
332 break;
333 case DevFmtShort:
334 inParams.sampleFormat = paInt16;
335 break;
336 case DevFmtInt:
337 inParams.sampleFormat = paInt32;
338 break;
339 case DevFmtFloat:
340 inParams.sampleFormat = paFloat32;
341 break;
342 case DevFmtUInt:
343 case DevFmtUShort:
344 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
345 goto error;
347 inParams.channelCount = ChannelsFromDevFmt(device->FmtChans);
349 err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency,
350 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
351 if(err != paNoError)
353 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
354 goto error;
357 device->szDeviceName = strdup(deviceName);
359 device->ExtraData = data;
360 return ALC_NO_ERROR;
362 error:
363 DestroyRingBuffer(data->ring);
364 free(data);
365 return ALC_INVALID_VALUE;
368 static void pa_close_capture(ALCdevice *device)
370 pa_data *data = (pa_data*)device->ExtraData;
371 PaError err;
373 err = Pa_CloseStream(data->stream);
374 if(err != paNoError)
375 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
377 free(data);
378 device->ExtraData = NULL;
381 static void pa_start_capture(ALCdevice *device)
383 pa_data *data = device->ExtraData;
384 PaError err;
386 err = Pa_StartStream(data->stream);
387 if(err != paNoError)
388 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
391 static void pa_stop_capture(ALCdevice *device)
393 pa_data *data = (pa_data*)device->ExtraData;
394 PaError err;
396 err = Pa_StopStream(data->stream);
397 if(err != paNoError)
398 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
401 static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
403 pa_data *data = device->ExtraData;
404 ReadRingBuffer(data->ring, buffer, samples);
405 return ALC_NO_ERROR;
408 static ALCuint pa_available_samples(ALCdevice *device)
410 pa_data *data = device->ExtraData;
411 return RingBufferSize(data->ring);
415 static const BackendFuncs pa_funcs = {
416 pa_open_playback,
417 pa_close_playback,
418 pa_reset_playback,
419 pa_start_playback,
420 pa_stop_playback,
421 pa_open_capture,
422 pa_close_capture,
423 pa_start_capture,
424 pa_stop_capture,
425 pa_capture_samples,
426 pa_available_samples
429 ALCboolean alc_pa_init(BackendFuncs *func_list)
431 if(!pa_load())
432 return ALC_FALSE;
433 *func_list = pa_funcs;
434 return ALC_TRUE;
437 void alc_pa_deinit(void)
439 #ifdef HAVE_DYNLOAD
440 if(pa_handle)
442 Pa_Terminate();
443 CloseLib(pa_handle);
444 pa_handle = NULL;
446 #else
447 Pa_Terminate();
448 #endif
451 void alc_pa_probe(enum DevProbe type)
453 switch(type)
455 case ALL_DEVICE_PROBE:
456 AppendAllDeviceList(pa_device);
457 break;
458 case CAPTURE_DEVICE_PROBE:
459 AppendCaptureDeviceList(pa_device);
460 break;