Mercurial > audio-send
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f9476ff7637e |
---|---|
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 */ | |
20 | |
21 #include "config.h" | |
22 | |
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" | |
29 | |
30 #include <portaudio.h> | |
31 | |
32 | |
33 static const ALCchar pa_device[] = "PortAudio Default"; | |
34 | |
35 | |
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 | |
49 | |
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 | |
60 | |
61 static ALCboolean pa_load(void) | |
62 { | |
63 if(!pa_handle) | |
64 { | |
65 PaError err; | |
66 | |
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 | |
77 | |
78 pa_handle = LoadLib(PALIB); | |
79 if(!pa_handle) | |
80 return ALC_FALSE; | |
81 | |
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 | |
104 | |
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 } | |
115 | |
116 | |
117 typedef struct { | |
118 PaStream *stream; | |
119 ALuint update_size; | |
120 | |
121 RingBuffer *ring; | |
122 } pa_data; | |
123 | |
124 | |
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; | |
130 | |
131 (void)inputBuffer; | |
132 (void)timeInfo; | |
133 (void)statusFlags; | |
134 | |
135 aluMixData(device, outputBuffer, framesPerBuffer); | |
136 return 0; | |
137 } | |
138 | |
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; | |
145 | |
146 (void)outputBuffer; | |
147 (void)timeInfo; | |
148 (void)statusFlags; | |
149 | |
150 WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); | |
151 return 0; | |
152 } | |
153 | |
154 | |
155 static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) | |
156 { | |
157 PaStreamParameters outParams; | |
158 pa_data *data; | |
159 PaError err; | |
160 | |
161 if(!deviceName) | |
162 deviceName = pa_device; | |
163 else if(strcmp(deviceName, pa_device) != 0) | |
164 return ALC_FALSE; | |
165 | |
166 data = (pa_data*)calloc(1, sizeof(pa_data)); | |
167 data->update_size = device->UpdateSize; | |
168 | |
169 device->ExtraData = data; | |
170 | |
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; | |
177 | |
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); | |
197 | |
198 SetDefaultChannelOrder(device); | |
199 | |
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 } | |
209 | |
210 device->szDeviceName = strdup(deviceName); | |
211 | |
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 } | |
227 | |
228 return ALC_TRUE; | |
229 } | |
230 | |
231 static void pa_close_playback(ALCdevice *device) | |
232 { | |
233 pa_data *data = (pa_data*)device->ExtraData; | |
234 PaError err; | |
235 | |
236 err = Pa_CloseStream(data->stream); | |
237 if(err != paNoError) | |
238 ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); | |
239 | |
240 free(data); | |
241 device->ExtraData = NULL; | |
242 } | |
243 | |
244 static ALCboolean pa_reset_playback(ALCdevice *device) | |
245 { | |
246 pa_data *data = (pa_data*)device->ExtraData; | |
247 const PaStreamInfo *streamInfo; | |
248 PaError err; | |
249 | |
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; | |
259 | |
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 } | |
266 | |
267 return ALC_TRUE; | |
268 } | |
269 | |
270 static void pa_stop_playback(ALCdevice *device) | |
271 { | |
272 pa_data *data = (pa_data*)device->ExtraData; | |
273 PaError err; | |
274 | |
275 err = Pa_StopStream(data->stream); | |
276 if(err != paNoError) | |
277 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); | |
278 } | |
279 | |
280 | |
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; | |
287 | |
288 if(!deviceName) | |
289 deviceName = pa_device; | |
290 else if(strcmp(deviceName, pa_device) != 0) | |
291 return ALC_FALSE; | |
292 | |
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 } | |
299 | |
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 } | |
307 | |
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; | |
313 | |
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); | |
333 | |
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 } | |
341 | |
342 device->szDeviceName = strdup(deviceName); | |
343 | |
344 device->ExtraData = data; | |
345 return ALC_TRUE; | |
346 | |
347 error: | |
348 DestroyRingBuffer(data->ring); | |
349 free(data); | |
350 return ALC_FALSE; | |
351 } | |
352 | |
353 static void pa_close_capture(ALCdevice *device) | |
354 { | |
355 pa_data *data = (pa_data*)device->ExtraData; | |
356 PaError err; | |
357 | |
358 err = Pa_CloseStream(data->stream); | |
359 if(err != paNoError) | |
360 ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); | |
361 | |
362 free(data); | |
363 device->ExtraData = NULL; | |
364 } | |
365 | |
366 static void pa_start_capture(ALCdevice *device) | |
367 { | |
368 pa_data *data = device->ExtraData; | |
369 PaError err; | |
370 | |
371 err = Pa_StartStream(data->stream); | |
372 if(err != paNoError) | |
373 ERR("Error starting stream: %s\n", Pa_GetErrorText(err)); | |
374 } | |
375 | |
376 static void pa_stop_capture(ALCdevice *device) | |
377 { | |
378 pa_data *data = (pa_data*)device->ExtraData; | |
379 PaError err; | |
380 | |
381 err = Pa_StopStream(data->stream); | |
382 if(err != paNoError) | |
383 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); | |
384 } | |
385 | |
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 } | |
394 | |
395 static ALCuint pa_available_samples(ALCdevice *device) | |
396 { | |
397 pa_data *data = device->ExtraData; | |
398 return RingBufferSize(data->ring); | |
399 } | |
400 | |
401 | |
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 }; | |
414 | |
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 } | |
422 | |
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 } | |
434 | |
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 } |