Copy samples if needed in the 'copy' resampler
[openal-soft.git] / Alc / backends / portaudio.c
blob0bb6372c7199a57743d1eeb0a1f0cba66ffea39e
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>
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_GetStreamInfo);
49 #undef MAKE_FUNC
51 #define Pa_Initialize pPa_Initialize
52 #define Pa_Terminate pPa_Terminate
53 #define Pa_GetErrorText pPa_GetErrorText
54 #define Pa_StartStream pPa_StartStream
55 #define Pa_StopStream pPa_StopStream
56 #define Pa_OpenStream pPa_OpenStream
57 #define Pa_CloseStream pPa_CloseStream
58 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
59 #define Pa_GetStreamInfo pPa_GetStreamInfo
60 #endif
62 static ALCboolean pa_load(void)
64 PaError err;
66 #ifdef HAVE_DYNLOAD
67 if(!pa_handle)
69 #ifdef _WIN32
70 # define PALIB "portaudio.dll"
71 #elif defined(__APPLE__) && defined(__MACH__)
72 # define PALIB "libportaudio.2.dylib"
73 #elif defined(__OpenBSD__)
74 # define PALIB "libportaudio.so"
75 #else
76 # define PALIB "libportaudio.so.2"
77 #endif
79 pa_handle = LoadLib(PALIB);
80 if(!pa_handle)
81 return ALC_FALSE;
83 #define LOAD_FUNC(f) do { \
84 p##f = GetSymbol(pa_handle, #f); \
85 if(p##f == NULL) \
86 { \
87 CloseLib(pa_handle); \
88 pa_handle = NULL; \
89 return ALC_FALSE; \
90 } \
91 } while(0)
92 LOAD_FUNC(Pa_Initialize);
93 LOAD_FUNC(Pa_Terminate);
94 LOAD_FUNC(Pa_GetErrorText);
95 LOAD_FUNC(Pa_StartStream);
96 LOAD_FUNC(Pa_StopStream);
97 LOAD_FUNC(Pa_OpenStream);
98 LOAD_FUNC(Pa_CloseStream);
99 LOAD_FUNC(Pa_GetDefaultOutputDevice);
100 LOAD_FUNC(Pa_GetStreamInfo);
101 #undef LOAD_FUNC
103 if((err=Pa_Initialize()) != paNoError)
105 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
106 CloseLib(pa_handle);
107 pa_handle = NULL;
108 return ALC_FALSE;
111 #else
112 if((err=Pa_Initialize()) != paNoError)
114 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
115 return ALC_FALSE;
117 #endif
118 return ALC_TRUE;
122 typedef struct {
123 PaStream *stream;
124 PaStreamParameters params;
125 ALuint update_size;
127 RingBuffer *ring;
128 } pa_data;
131 static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
132 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
133 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
135 ALCdevice *device = (ALCdevice*)userData;
137 aluMixData(device, outputBuffer, framesPerBuffer);
138 return 0;
141 static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
142 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
143 const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
145 ALCdevice *device = (ALCdevice*)userData;
146 pa_data *data = (pa_data*)device->ExtraData;
148 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
149 return 0;
153 static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
155 pa_data *data;
156 PaError err;
158 if(!deviceName)
159 deviceName = pa_device;
160 else if(strcmp(deviceName, pa_device) != 0)
161 return ALC_INVALID_VALUE;
163 data = (pa_data*)calloc(1, sizeof(pa_data));
164 data->update_size = device->UpdateSize;
166 data->params.device = -1;
167 if(!ConfigValueInt("port", "device", &data->params.device) ||
168 data->params.device < 0)
169 data->params.device = Pa_GetDefaultOutputDevice();
170 data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
171 (float)device->Frequency;
172 data->params.hostApiSpecificStreamInfo = NULL;
174 data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
176 switch(device->FmtType)
178 case DevFmtByte:
179 data->params.sampleFormat = paInt8;
180 break;
181 case DevFmtUByte:
182 data->params.sampleFormat = paUInt8;
183 break;
184 case DevFmtUShort:
185 /* fall-through */
186 case DevFmtShort:
187 data->params.sampleFormat = paInt16;
188 break;
189 case DevFmtUInt:
190 /* fall-through */
191 case DevFmtInt:
192 data->params.sampleFormat = paInt32;
193 break;
194 case DevFmtFloat:
195 data->params.sampleFormat = paFloat32;
196 break;
199 retry_open:
200 err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
201 device->UpdateSize, paNoFlag, pa_callback, device);
202 if(err != paNoError)
204 if(data->params.sampleFormat == paFloat32)
206 data->params.sampleFormat = paInt16;
207 goto retry_open;
209 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
210 free(data);
211 return ALC_INVALID_VALUE;
214 device->ExtraData = data;
215 al_string_copy_cstr(&device->DeviceName, deviceName);
217 return ALC_NO_ERROR;
220 static void pa_close_playback(ALCdevice *device)
222 pa_data *data = (pa_data*)device->ExtraData;
223 PaError err;
225 err = Pa_CloseStream(data->stream);
226 if(err != paNoError)
227 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
229 free(data);
230 device->ExtraData = NULL;
233 static ALCboolean pa_reset_playback(ALCdevice *device)
235 pa_data *data = (pa_data*)device->ExtraData;
236 const PaStreamInfo *streamInfo;
238 streamInfo = Pa_GetStreamInfo(data->stream);
239 device->Frequency = streamInfo->sampleRate;
240 device->UpdateSize = data->update_size;
242 if(data->params.sampleFormat == paInt8)
243 device->FmtType = DevFmtByte;
244 else if(data->params.sampleFormat == paUInt8)
245 device->FmtType = DevFmtUByte;
246 else if(data->params.sampleFormat == paInt16)
247 device->FmtType = DevFmtShort;
248 else if(data->params.sampleFormat == paInt32)
249 device->FmtType = DevFmtInt;
250 else if(data->params.sampleFormat == paFloat32)
251 device->FmtType = DevFmtFloat;
252 else
254 ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
255 return ALC_FALSE;
258 if(data->params.channelCount == 2)
259 device->FmtChans = DevFmtStereo;
260 else if(data->params.channelCount == 1)
261 device->FmtChans = DevFmtMono;
262 else
264 ERR("Unexpected channel count: %u\n", data->params.channelCount);
265 return ALC_FALSE;
267 SetDefaultChannelOrder(device);
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 ALuint frame_size;
301 pa_data *data;
302 PaError err;
304 if(!deviceName)
305 deviceName = pa_device;
306 else if(strcmp(deviceName, pa_device) != 0)
307 return ALC_INVALID_VALUE;
309 data = (pa_data*)calloc(1, sizeof(pa_data));
310 if(data == NULL)
311 return ALC_OUT_OF_MEMORY;
313 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
314 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
315 if(data->ring == NULL)
316 goto error;
318 data->params.device = -1;
319 if(!ConfigValueInt("port", "capture", &data->params.device) ||
320 data->params.device < 0)
321 data->params.device = Pa_GetDefaultOutputDevice();
322 data->params.suggestedLatency = 0.0f;
323 data->params.hostApiSpecificStreamInfo = NULL;
325 switch(device->FmtType)
327 case DevFmtByte:
328 data->params.sampleFormat = paInt8;
329 break;
330 case DevFmtUByte:
331 data->params.sampleFormat = paUInt8;
332 break;
333 case DevFmtShort:
334 data->params.sampleFormat = paInt16;
335 break;
336 case DevFmtInt:
337 data->params.sampleFormat = paInt32;
338 break;
339 case DevFmtFloat:
340 data->params.sampleFormat = paFloat32;
341 break;
342 case DevFmtUInt:
343 case DevFmtUShort:
344 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
345 goto error;
347 data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
349 err = Pa_OpenStream(&data->stream, &data->params, 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 al_string_copy_cstr(&device->DeviceName, 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 DestroyRingBuffer(data->ring);
378 data->ring = NULL;
380 free(data);
381 device->ExtraData = NULL;
384 static void pa_start_capture(ALCdevice *device)
386 pa_data *data = device->ExtraData;
387 PaError err;
389 err = Pa_StartStream(data->stream);
390 if(err != paNoError)
391 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
394 static void pa_stop_capture(ALCdevice *device)
396 pa_data *data = (pa_data*)device->ExtraData;
397 PaError err;
399 err = Pa_StopStream(data->stream);
400 if(err != paNoError)
401 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
404 static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
406 pa_data *data = device->ExtraData;
407 ReadRingBuffer(data->ring, buffer, samples);
408 return ALC_NO_ERROR;
411 static ALCuint pa_available_samples(ALCdevice *device)
413 pa_data *data = device->ExtraData;
414 return RingBufferSize(data->ring);
418 static const BackendFuncs pa_funcs = {
419 pa_open_playback,
420 pa_close_playback,
421 pa_reset_playback,
422 pa_start_playback,
423 pa_stop_playback,
424 pa_open_capture,
425 pa_close_capture,
426 pa_start_capture,
427 pa_stop_capture,
428 pa_capture_samples,
429 pa_available_samples,
430 ALCdevice_GetLatencyDefault
433 ALCboolean alc_pa_init(BackendFuncs *func_list)
435 if(!pa_load())
436 return ALC_FALSE;
437 *func_list = pa_funcs;
438 return ALC_TRUE;
441 void alc_pa_deinit(void)
443 #ifdef HAVE_DYNLOAD
444 if(pa_handle)
446 Pa_Terminate();
447 CloseLib(pa_handle);
448 pa_handle = NULL;
450 #else
451 Pa_Terminate();
452 #endif
455 void alc_pa_probe(enum DevProbe type)
457 switch(type)
459 case ALL_DEVICE_PROBE:
460 AppendAllDevicesList(pa_device);
461 break;
462 case CAPTURE_DEVICE_PROBE:
463 AppendCaptureDeviceList(pa_device);
464 break;