view Alc/backends/portaudio.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
line wrap: on
line source
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.
8 *
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.
13 *
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
19 */
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 static void *pa_handle;
37 #ifdef HAVE_DYNLOAD
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)
62 {
63 if(!pa_handle)
64 {
65 PaError err;
67 #ifdef HAVE_DYNLOAD
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
101 #else
102 pa_handle = (void*)0xDEADBEEF;
103 #endif
105 if((err=Pa_Initialize()) != paNoError)
106 {
107 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
108 CloseLib(pa_handle);
109 pa_handle = NULL;
110 return ALC_FALSE;
111 }
112 }
113 return ALC_TRUE;
114 }
117 typedef struct {
118 PaStream *stream;
119 ALuint update_size;
121 RingBuffer *ring;
122 } pa_data;
125 static int pa_callback(const void *inputBuffer, void *outputBuffer,
126 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
127 const PaStreamCallbackFlags statusFlags, void *userData)
128 {
129 ALCdevice *device = (ALCdevice*)userData;
131 (void)inputBuffer;
132 (void)timeInfo;
133 (void)statusFlags;
135 aluMixData(device, outputBuffer, framesPerBuffer);
136 return 0;
137 }
139 static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
140 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
141 const PaStreamCallbackFlags statusFlags, void *userData)
142 {
143 ALCdevice *device = (ALCdevice*)userData;
144 pa_data *data = (pa_data*)device->ExtraData;
146 (void)outputBuffer;
147 (void)timeInfo;
148 (void)statusFlags;
150 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
151 return 0;
152 }
155 static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
156 {
157 PaStreamParameters outParams;
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_FALSE;
166 data = (pa_data*)calloc(1, sizeof(pa_data));
167 data->update_size = device->UpdateSize;
169 device->ExtraData = data;
171 outParams.device = GetConfigValueInt("port", "device", -1);
172 if(outParams.device < 0)
173 outParams.device = Pa_GetDefaultOutputDevice();
174 outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
175 (float)device->Frequency;
176 outParams.hostApiSpecificStreamInfo = NULL;
178 switch(device->FmtType)
179 {
180 case DevFmtByte:
181 outParams.sampleFormat = paInt8;
182 break;
183 case DevFmtUByte:
184 outParams.sampleFormat = paUInt8;
185 break;
186 case DevFmtUShort:
187 device->FmtType = DevFmtShort;
188 /* fall-through */
189 case DevFmtShort:
190 outParams.sampleFormat = paInt16;
191 break;
192 case DevFmtFloat:
193 outParams.sampleFormat = paFloat32;
194 break;
195 }
196 outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
198 SetDefaultChannelOrder(device);
200 err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
201 device->UpdateSize, paNoFlag, pa_callback, device);
202 if(err != paNoError)
203 {
204 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
205 device->ExtraData = NULL;
206 free(data);
207 return ALC_FALSE;
208 }
210 device->szDeviceName = strdup(deviceName);
212 if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans))
213 {
214 if(outParams.channelCount != 1 && outParams.channelCount != 2)
215 {
216 ERR("Unhandled channel count: %u\n", outParams.channelCount);
217 Pa_CloseStream(data->stream);
218 device->ExtraData = NULL;
219 free(data);
220 return ALC_FALSE;
221 }
222 if((device->Flags&DEVICE_CHANNELS_REQUEST))
223 ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount);
224 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
225 device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo);
226 }
228 return ALC_TRUE;
229 }
231 static void pa_close_playback(ALCdevice *device)
232 {
233 pa_data *data = (pa_data*)device->ExtraData;
234 PaError err;
236 err = Pa_CloseStream(data->stream);
237 if(err != paNoError)
238 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
240 free(data);
241 device->ExtraData = NULL;
242 }
244 static ALCboolean pa_reset_playback(ALCdevice *device)
245 {
246 pa_data *data = (pa_data*)device->ExtraData;
247 const PaStreamInfo *streamInfo;
248 PaError err;
250 streamInfo = Pa_GetStreamInfo(data->stream);
251 if(device->Frequency != streamInfo->sampleRate)
252 {
253 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
254 ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate);
255 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
256 device->Frequency = streamInfo->sampleRate;
257 }
258 device->UpdateSize = data->update_size;
260 err = Pa_StartStream(data->stream);
261 if(err != paNoError)
262 {
263 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
264 return ALC_FALSE;
265 }
267 return ALC_TRUE;
268 }
270 static void pa_stop_playback(ALCdevice *device)
271 {
272 pa_data *data = (pa_data*)device->ExtraData;
273 PaError err;
275 err = Pa_StopStream(data->stream);
276 if(err != paNoError)
277 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
278 }
281 static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
282 {
283 PaStreamParameters inParams;
284 ALuint frame_size;
285 pa_data *data;
286 PaError err;
288 if(!deviceName)
289 deviceName = pa_device;
290 else if(strcmp(deviceName, pa_device) != 0)
291 return ALC_FALSE;
293 data = (pa_data*)calloc(1, sizeof(pa_data));
294 if(data == NULL)
295 {
296 alcSetError(device, ALC_OUT_OF_MEMORY);
297 return ALC_FALSE;
298 }
300 frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
301 data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
302 if(data->ring == NULL)
303 {
304 alcSetError(device, ALC_OUT_OF_MEMORY);
305 goto error;
306 }
308 inParams.device = GetConfigValueInt("port", "capture", -1);
309 if(inParams.device < 0)
310 inParams.device = Pa_GetDefaultOutputDevice();
311 inParams.suggestedLatency = 0.0f;
312 inParams.hostApiSpecificStreamInfo = NULL;
314 switch(device->FmtType)
315 {
316 case DevFmtByte:
317 inParams.sampleFormat = paInt8;
318 break;
319 case DevFmtUByte:
320 inParams.sampleFormat = paUInt8;
321 break;
322 case DevFmtShort:
323 inParams.sampleFormat = paInt16;
324 break;
325 case DevFmtFloat:
326 inParams.sampleFormat = paFloat32;
327 break;
328 case DevFmtUShort:
329 ERR("Unsigned short samples not supported\n");
330 goto error;
331 }
332 inParams.channelCount = ChannelsFromDevFmt(device->FmtChans);
334 err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency,
335 paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
336 if(err != paNoError)
337 {
338 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
339 goto error;
340 }
342 device->szDeviceName = strdup(deviceName);
344 device->ExtraData = data;
345 return ALC_TRUE;
347 error:
348 DestroyRingBuffer(data->ring);
349 free(data);
350 return ALC_FALSE;
351 }
353 static void pa_close_capture(ALCdevice *device)
354 {
355 pa_data *data = (pa_data*)device->ExtraData;
356 PaError err;
358 err = Pa_CloseStream(data->stream);
359 if(err != paNoError)
360 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
362 free(data);
363 device->ExtraData = NULL;
364 }
366 static void pa_start_capture(ALCdevice *device)
367 {
368 pa_data *data = device->ExtraData;
369 PaError err;
371 err = Pa_StartStream(data->stream);
372 if(err != paNoError)
373 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
374 }
376 static void pa_stop_capture(ALCdevice *device)
377 {
378 pa_data *data = (pa_data*)device->ExtraData;
379 PaError err;
381 err = Pa_StopStream(data->stream);
382 if(err != paNoError)
383 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
384 }
386 static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
387 {
388 pa_data *data = device->ExtraData;
389 if(samples <= (ALCuint)RingBufferSize(data->ring))
390 ReadRingBuffer(data->ring, buffer, samples);
391 else
392 alcSetError(device, ALC_INVALID_VALUE);
393 }
395 static ALCuint pa_available_samples(ALCdevice *device)
396 {
397 pa_data *data = device->ExtraData;
398 return RingBufferSize(data->ring);
399 }
402 static const BackendFuncs pa_funcs = {
403 pa_open_playback,
404 pa_close_playback,
405 pa_reset_playback,
406 pa_stop_playback,
407 pa_open_capture,
408 pa_close_capture,
409 pa_start_capture,
410 pa_stop_capture,
411 pa_capture_samples,
412 pa_available_samples
413 };
415 ALCboolean alc_pa_init(BackendFuncs *func_list)
416 {
417 if(!pa_load())
418 return ALC_FALSE;
419 *func_list = pa_funcs;
420 return ALC_TRUE;
421 }
423 void alc_pa_deinit(void)
424 {
425 if(pa_handle)
426 {
427 Pa_Terminate();
428 #ifdef HAVE_DYNLOAD
429 CloseLib(pa_handle);
430 #endif
431 pa_handle = NULL;
432 }
433 }
435 void alc_pa_probe(enum DevProbe type)
436 {
437 switch(type)
438 {
439 case DEVICE_PROBE:
440 AppendDeviceList(pa_device);
441 break;
442 case ALL_DEVICE_PROBE:
443 AppendAllDeviceList(pa_device);
444 break;
445 case CAPTURE_DEVICE_PROBE:
446 AppendCaptureDeviceList(pa_device);
447 break;
448 }
449 }