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
31 #include <portaudio.h>
34 static const ALCchar pa_device
[] = "PortAudio Default";
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
);
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
62 static ALCboolean
pa_load(void)
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"
76 # define PALIB "libportaudio.so.2"
79 pa_handle
= LoadLib(PALIB
);
83 #define LOAD_FUNC(f) do { \
84 p##f = GetSymbol(pa_handle, #f); \
87 CloseLib(pa_handle); \
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
);
103 if((err
=Pa_Initialize()) != paNoError
)
105 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
112 if((err
=Pa_Initialize()) != paNoError
)
114 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
124 PaStreamParameters params
;
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
);
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
);
153 static ALCenum
pa_open_playback(ALCdevice
*device
, const ALCchar
*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
)
179 data
->params
.sampleFormat
= paInt8
;
182 data
->params
.sampleFormat
= paUInt8
;
187 data
->params
.sampleFormat
= paInt16
;
192 data
->params
.sampleFormat
= paInt32
;
195 data
->params
.sampleFormat
= paFloat32
;
200 err
= Pa_OpenStream(&data
->stream
, NULL
, &data
->params
, device
->Frequency
,
201 device
->UpdateSize
, paNoFlag
, pa_callback
, device
);
204 if(data
->params
.sampleFormat
== paFloat32
)
206 data
->params
.sampleFormat
= paInt16
;
209 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
211 return ALC_INVALID_VALUE
;
214 device
->ExtraData
= data
;
215 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
220 static void pa_close_playback(ALCdevice
*device
)
222 pa_data
*data
= (pa_data
*)device
->ExtraData
;
225 err
= Pa_CloseStream(data
->stream
);
227 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
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
;
254 ERR("Unexpected sample format: 0x%lx\n", data
->params
.sampleFormat
);
258 if(data
->params
.channelCount
== 2)
259 device
->FmtChans
= DevFmtStereo
;
260 else if(data
->params
.channelCount
== 1)
261 device
->FmtChans
= DevFmtMono
;
264 ERR("Unexpected channel count: %u\n", data
->params
.channelCount
);
267 SetDefaultChannelOrder(device
);
272 static ALCboolean
pa_start_playback(ALCdevice
*device
)
274 pa_data
*data
= (pa_data
*)device
->ExtraData
;
277 err
= Pa_StartStream(data
->stream
);
280 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err
));
287 static void pa_stop_playback(ALCdevice
*device
)
289 pa_data
*data
= (pa_data
*)device
->ExtraData
;
292 err
= Pa_StopStream(data
->stream
);
294 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
298 static ALCenum
pa_open_capture(ALCdevice
*device
, const ALCchar
*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
));
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
)
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
)
328 data
->params
.sampleFormat
= paInt8
;
331 data
->params
.sampleFormat
= paUInt8
;
334 data
->params
.sampleFormat
= paInt16
;
337 data
->params
.sampleFormat
= paInt32
;
340 data
->params
.sampleFormat
= paFloat32
;
344 ERR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
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
);
353 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
357 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
359 device
->ExtraData
= data
;
363 DestroyRingBuffer(data
->ring
);
365 return ALC_INVALID_VALUE
;
368 static void pa_close_capture(ALCdevice
*device
)
370 pa_data
*data
= (pa_data
*)device
->ExtraData
;
373 err
= Pa_CloseStream(data
->stream
);
375 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
377 DestroyRingBuffer(data
->ring
);
381 device
->ExtraData
= NULL
;
384 static void pa_start_capture(ALCdevice
*device
)
386 pa_data
*data
= device
->ExtraData
;
389 err
= Pa_StartStream(data
->stream
);
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
;
399 err
= Pa_StopStream(data
->stream
);
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
);
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
= {
429 pa_available_samples
,
430 ALCdevice_GetLatencyDefault
433 ALCboolean
alc_pa_init(BackendFuncs
*func_list
)
437 *func_list
= pa_funcs
;
441 void alc_pa_deinit(void)
455 void alc_pa_probe(enum DevProbe type
)
459 case ALL_DEVICE_PROBE
:
460 AppendAllDevicesList(pa_device
);
462 case CAPTURE_DEVICE_PROBE
:
463 AppendCaptureDeviceList(pa_device
);