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
30 #include <portaudio.h>
33 static const ALCchar pa_device
[] = "PortAudio Default";
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
);
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
61 static ALCboolean
pa_load(void)
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"
75 # define PALIB "libportaudio.so.2"
78 pa_handle
= LoadLib(PALIB
);
82 #define LOAD_FUNC(f) do { \
83 p##f = GetSymbol(pa_handle, #f); \
86 CloseLib(pa_handle); \
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
);
102 if((err
=Pa_Initialize()) != paNoError
)
104 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
111 if((err
=Pa_Initialize()) != paNoError
)
113 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
123 PaStreamParameters params
;
130 static int pa_callback(const void *inputBuffer
, void *outputBuffer
,
131 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
132 const PaStreamCallbackFlags statusFlags
, void *userData
)
134 ALCdevice
*device
= (ALCdevice
*)userData
;
140 aluMixData(device
, outputBuffer
, framesPerBuffer
);
144 static int pa_capture_cb(const void *inputBuffer
, void *outputBuffer
,
145 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
146 const PaStreamCallbackFlags statusFlags
, void *userData
)
148 ALCdevice
*device
= (ALCdevice
*)userData
;
149 pa_data
*data
= (pa_data
*)device
->ExtraData
;
155 WriteRingBuffer(data
->ring
, inputBuffer
, framesPerBuffer
);
160 static ALCenum
pa_open_playback(ALCdevice
*device
, const ALCchar
*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 data
->params
.device
= -1;
174 if(!ConfigValueInt("port", "device", &data
->params
.device
) ||
175 data
->params
.device
< 0)
176 data
->params
.device
= Pa_GetDefaultOutputDevice();
177 data
->params
.suggestedLatency
= (device
->UpdateSize
*device
->NumUpdates
) /
178 (float)device
->Frequency
;
179 data
->params
.hostApiSpecificStreamInfo
= NULL
;
181 data
->params
.channelCount
= ((device
->FmtChans
== DevFmtMono
) ? 1 : 2);
183 switch(device
->FmtType
)
186 data
->params
.sampleFormat
= paInt8
;
189 data
->params
.sampleFormat
= paUInt8
;
194 data
->params
.sampleFormat
= paInt16
;
199 data
->params
.sampleFormat
= paInt32
;
202 data
->params
.sampleFormat
= paFloat32
;
207 err
= Pa_OpenStream(&data
->stream
, NULL
, &data
->params
, device
->Frequency
,
208 device
->UpdateSize
, paNoFlag
, pa_callback
, device
);
211 if(data
->params
.sampleFormat
== paFloat32
)
213 data
->params
.sampleFormat
= paInt16
;
216 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
218 return ALC_INVALID_VALUE
;
221 device
->ExtraData
= data
;
222 device
->DeviceName
= strdup(deviceName
);
227 static void pa_close_playback(ALCdevice
*device
)
229 pa_data
*data
= (pa_data
*)device
->ExtraData
;
232 err
= Pa_CloseStream(data
->stream
);
234 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
237 device
->ExtraData
= NULL
;
240 static ALCboolean
pa_reset_playback(ALCdevice
*device
)
242 pa_data
*data
= (pa_data
*)device
->ExtraData
;
243 const PaStreamInfo
*streamInfo
;
245 streamInfo
= Pa_GetStreamInfo(data
->stream
);
246 device
->Frequency
= streamInfo
->sampleRate
;
247 device
->UpdateSize
= data
->update_size
;
249 if(data
->params
.sampleFormat
== paInt8
)
250 device
->FmtType
= DevFmtByte
;
251 else if(data
->params
.sampleFormat
== paUInt8
)
252 device
->FmtType
= DevFmtUByte
;
253 else if(data
->params
.sampleFormat
== paInt16
)
254 device
->FmtType
= DevFmtShort
;
255 else if(data
->params
.sampleFormat
== paInt32
)
256 device
->FmtType
= DevFmtInt
;
257 else if(data
->params
.sampleFormat
== paFloat32
)
258 device
->FmtType
= DevFmtFloat
;
261 ERR("Unexpected sample format: 0x%lx\n", data
->params
.sampleFormat
);
265 if(data
->params
.channelCount
== 2)
266 device
->FmtChans
= DevFmtStereo
;
267 else if(data
->params
.channelCount
== 1)
268 device
->FmtChans
= DevFmtMono
;
271 ERR("Unexpected channel count: %u\n", data
->params
.channelCount
);
274 SetDefaultChannelOrder(device
);
279 static ALCboolean
pa_start_playback(ALCdevice
*device
)
281 pa_data
*data
= (pa_data
*)device
->ExtraData
;
284 err
= Pa_StartStream(data
->stream
);
287 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err
));
294 static void pa_stop_playback(ALCdevice
*device
)
296 pa_data
*data
= (pa_data
*)device
->ExtraData
;
299 err
= Pa_StopStream(data
->stream
);
301 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
305 static ALCenum
pa_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
312 deviceName
= pa_device
;
313 else if(strcmp(deviceName
, pa_device
) != 0)
314 return ALC_INVALID_VALUE
;
316 data
= (pa_data
*)calloc(1, sizeof(pa_data
));
318 return ALC_OUT_OF_MEMORY
;
320 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
321 data
->ring
= CreateRingBuffer(frame_size
, device
->UpdateSize
*device
->NumUpdates
);
322 if(data
->ring
== NULL
)
325 data
->params
.device
= -1;
326 if(!ConfigValueInt("port", "capture", &data
->params
.device
) ||
327 data
->params
.device
< 0)
328 data
->params
.device
= Pa_GetDefaultOutputDevice();
329 data
->params
.suggestedLatency
= 0.0f
;
330 data
->params
.hostApiSpecificStreamInfo
= NULL
;
332 switch(device
->FmtType
)
335 data
->params
.sampleFormat
= paInt8
;
338 data
->params
.sampleFormat
= paUInt8
;
341 data
->params
.sampleFormat
= paInt16
;
344 data
->params
.sampleFormat
= paInt32
;
347 data
->params
.sampleFormat
= paFloat32
;
351 ERR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
354 data
->params
.channelCount
= ChannelsFromDevFmt(device
->FmtChans
);
356 err
= Pa_OpenStream(&data
->stream
, &data
->params
, NULL
, device
->Frequency
,
357 paFramesPerBufferUnspecified
, paNoFlag
, pa_capture_cb
, device
);
360 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
364 device
->DeviceName
= strdup(deviceName
);
366 device
->ExtraData
= data
;
370 DestroyRingBuffer(data
->ring
);
372 return ALC_INVALID_VALUE
;
375 static void pa_close_capture(ALCdevice
*device
)
377 pa_data
*data
= (pa_data
*)device
->ExtraData
;
380 err
= Pa_CloseStream(data
->stream
);
382 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
385 device
->ExtraData
= NULL
;
388 static void pa_start_capture(ALCdevice
*device
)
390 pa_data
*data
= device
->ExtraData
;
393 err
= Pa_StartStream(data
->stream
);
395 ERR("Error starting stream: %s\n", Pa_GetErrorText(err
));
398 static void pa_stop_capture(ALCdevice
*device
)
400 pa_data
*data
= (pa_data
*)device
->ExtraData
;
403 err
= Pa_StopStream(data
->stream
);
405 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
408 static ALCenum
pa_capture_samples(ALCdevice
*device
, ALCvoid
*buffer
, ALCuint samples
)
410 pa_data
*data
= device
->ExtraData
;
411 ReadRingBuffer(data
->ring
, buffer
, samples
);
415 static ALCuint
pa_available_samples(ALCdevice
*device
)
417 pa_data
*data
= device
->ExtraData
;
418 return RingBufferSize(data
->ring
);
422 static ALint64
pa_get_latency(ALCdevice
*device
)
429 static const BackendFuncs pa_funcs
= {
440 pa_available_samples
,
444 ALCboolean
alc_pa_init(BackendFuncs
*func_list
)
448 *func_list
= pa_funcs
;
452 void alc_pa_deinit(void)
466 void alc_pa_probe(enum DevProbe type
)
470 case ALL_DEVICE_PROBE
:
471 AppendAllDevicesList(pa_device
);
473 case CAPTURE_DEVICE_PROBE
:
474 AppendCaptureDeviceList(pa_device
);