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
33 #include <portaudio.h>
35 static void *pa_handle
;
36 #define MAKE_FUNC(x) static typeof(x) * p##x
37 MAKE_FUNC(Pa_Initialize
);
38 MAKE_FUNC(Pa_Terminate
);
39 MAKE_FUNC(Pa_GetErrorText
);
40 MAKE_FUNC(Pa_StartStream
);
41 MAKE_FUNC(Pa_StopStream
);
42 MAKE_FUNC(Pa_OpenStream
);
43 MAKE_FUNC(Pa_CloseStream
);
44 MAKE_FUNC(Pa_GetDefaultOutputDevice
);
45 MAKE_FUNC(Pa_GetStreamInfo
);
49 static const ALCchar pa_device
[] = "PortAudio Software";
50 static volatile ALuint load_count
;
61 #if defined(__APPLE__) && defined(__MACH__)
62 # define PALIB "libportaudio.2.dylib"
64 # define PALIB "libportaudio.so.2"
66 pa_handle
= dlopen(PALIB
, RTLD_NOW
);
71 #define LOAD_FUNC(f) do { \
72 p##f = (typeof(f)*)dlsym(pa_handle, #f); \
73 if((str=dlerror()) != NULL) \
77 AL_PRINT("Could not load %s from "PALIB": %s\n", #f, str); \
83 pa_handle
= (void*)0xDEADBEEF;
84 #define LOAD_FUNC(f) p##f = f
87 LOAD_FUNC(Pa_Initialize
);
88 LOAD_FUNC(Pa_Terminate
);
89 LOAD_FUNC(Pa_GetErrorText
);
90 LOAD_FUNC(Pa_StartStream
);
91 LOAD_FUNC(Pa_StopStream
);
92 LOAD_FUNC(Pa_OpenStream
);
93 LOAD_FUNC(Pa_CloseStream
);
94 LOAD_FUNC(Pa_GetDefaultOutputDevice
);
95 LOAD_FUNC(Pa_GetStreamInfo
);
99 if((err
=pPa_Initialize()) != paNoError
)
101 AL_PRINT("Pa_Initialize() returned an error: %s\n", pPa_GetErrorText(err
));
116 if(load_count
== 0 || --load_count
> 0)
133 static int pa_callback(const void *inputBuffer
, void *outputBuffer
,
134 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
135 const PaStreamCallbackFlags statusFlags
, void *userData
)
137 ALCdevice
*device
= (ALCdevice
*)userData
;
143 aluMixData(device
, outputBuffer
, framesPerBuffer
);
148 static ALCboolean
pa_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
150 const PaStreamInfo
*streamInfo
;
151 PaStreamParameters outParams
;
156 deviceName
= pa_device
;
157 else if(strcmp(deviceName
, pa_device
) != 0)
163 data
= (pa_data
*)calloc(1, sizeof(pa_data
));
164 data
->update_size
= device
->UpdateSize
;
166 device
->ExtraData
= data
;
168 outParams
.device
= GetConfigValueInt("port", "device", -1);
169 if(outParams
.device
< 0)
170 outParams
.device
= pPa_GetDefaultOutputDevice();
171 outParams
.suggestedLatency
= (device
->UpdateSize
*device
->NumUpdates
) /
172 (float)device
->Frequency
;
173 outParams
.hostApiSpecificStreamInfo
= NULL
;
175 switch(aluBytesFromFormat(device
->Format
))
178 outParams
.sampleFormat
= paUInt8
;
181 outParams
.sampleFormat
= paInt16
;
184 outParams
.sampleFormat
= paFloat32
;
187 AL_PRINT("Unknown format: 0x%x\n", device
->Format
);
188 device
->ExtraData
= NULL
;
193 outParams
.channelCount
= aluChannelsFromFormat(device
->Format
);
195 SetDefaultChannelOrder(device
);
197 err
= pPa_OpenStream(&data
->stream
, NULL
, &outParams
, device
->Frequency
,
198 device
->UpdateSize
, paNoFlag
, pa_callback
, device
);
201 AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err
));
202 device
->ExtraData
= NULL
;
207 streamInfo
= pPa_GetStreamInfo(data
->stream
);
209 device
->szDeviceName
= strdup(deviceName
);
210 device
->Frequency
= streamInfo
->sampleRate
;
215 static void pa_close_playback(ALCdevice
*device
)
217 pa_data
*data
= (pa_data
*)device
->ExtraData
;
220 err
= pPa_CloseStream(data
->stream
);
222 AL_PRINT("Error closing stream: %s\n", pPa_GetErrorText(err
));
225 device
->ExtraData
= NULL
;
230 static ALCboolean
pa_reset_playback(ALCdevice
*device
)
232 pa_data
*data
= (pa_data
*)device
->ExtraData
;
233 const PaStreamInfo
*streamInfo
;
236 streamInfo
= pPa_GetStreamInfo(data
->stream
);
237 device
->Frequency
= streamInfo
->sampleRate
;
238 device
->UpdateSize
= data
->update_size
;
240 err
= pPa_StartStream(data
->stream
);
243 AL_PRINT("Pa_StartStream() returned an error: %s\n", pPa_GetErrorText(err
));
250 static void pa_stop_playback(ALCdevice
*device
)
252 pa_data
*data
= (pa_data
*)device
->ExtraData
;
255 err
= pPa_StopStream(data
->stream
);
257 AL_PRINT("Error stopping stream: %s\n", pPa_GetErrorText(err
));
261 static ALCboolean
pa_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
270 static const BackendFuncs pa_funcs
= {
283 void alc_pa_init(BackendFuncs
*func_list
)
285 *func_list
= pa_funcs
;
288 void alc_pa_deinit(void)
292 void alc_pa_probe(int type
)
294 if(!pa_load()) return;
296 if(type
== DEVICE_PROBE
)
297 AppendDeviceList(pa_device
);
298 else if(type
== ALL_DEVICE_PROBE
)
299 AppendAllDeviceList(pa_device
);