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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
30 #include "ringbuffer.h"
33 #include "backends/base.h"
35 #include <portaudio.h>
38 static const ALCchar pa_device
[] = "PortAudio Default";
42 static void *pa_handle
;
43 #define MAKE_FUNC(x) static __typeof(x) * p##x
44 MAKE_FUNC(Pa_Initialize
);
45 MAKE_FUNC(Pa_Terminate
);
46 MAKE_FUNC(Pa_GetErrorText
);
47 MAKE_FUNC(Pa_StartStream
);
48 MAKE_FUNC(Pa_StopStream
);
49 MAKE_FUNC(Pa_OpenStream
);
50 MAKE_FUNC(Pa_CloseStream
);
51 MAKE_FUNC(Pa_GetDefaultOutputDevice
);
52 MAKE_FUNC(Pa_GetDefaultInputDevice
);
53 MAKE_FUNC(Pa_GetStreamInfo
);
56 #define Pa_Initialize pPa_Initialize
57 #define Pa_Terminate pPa_Terminate
58 #define Pa_GetErrorText pPa_GetErrorText
59 #define Pa_StartStream pPa_StartStream
60 #define Pa_StopStream pPa_StopStream
61 #define Pa_OpenStream pPa_OpenStream
62 #define Pa_CloseStream pPa_CloseStream
63 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
64 #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
65 #define Pa_GetStreamInfo pPa_GetStreamInfo
68 static ALCboolean
pa_load(void)
76 # define PALIB "portaudio.dll"
77 #elif defined(__APPLE__) && defined(__MACH__)
78 # define PALIB "libportaudio.2.dylib"
79 #elif defined(__OpenBSD__)
80 # define PALIB "libportaudio.so"
82 # define PALIB "libportaudio.so.2"
85 pa_handle
= LoadLib(PALIB
);
89 #define LOAD_FUNC(f) do { \
90 p##f = GetSymbol(pa_handle, #f); \
93 CloseLib(pa_handle); \
98 LOAD_FUNC(Pa_Initialize
);
99 LOAD_FUNC(Pa_Terminate
);
100 LOAD_FUNC(Pa_GetErrorText
);
101 LOAD_FUNC(Pa_StartStream
);
102 LOAD_FUNC(Pa_StopStream
);
103 LOAD_FUNC(Pa_OpenStream
);
104 LOAD_FUNC(Pa_CloseStream
);
105 LOAD_FUNC(Pa_GetDefaultOutputDevice
);
106 LOAD_FUNC(Pa_GetDefaultInputDevice
);
107 LOAD_FUNC(Pa_GetStreamInfo
);
110 if((err
=Pa_Initialize()) != paNoError
)
112 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
119 if((err
=Pa_Initialize()) != paNoError
)
121 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
129 typedef struct ALCportPlayback
{
130 DERIVE_FROM_TYPE(ALCbackend
);
133 PaStreamParameters params
;
137 static int ALCportPlayback_WriteCallback(const void *inputBuffer
, void *outputBuffer
,
138 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
139 const PaStreamCallbackFlags statusFlags
, void *userData
);
141 static void ALCportPlayback_Construct(ALCportPlayback
*self
, ALCdevice
*device
);
142 static void ALCportPlayback_Destruct(ALCportPlayback
*self
);
143 static ALCenum
ALCportPlayback_open(ALCportPlayback
*self
, const ALCchar
*name
);
144 static ALCboolean
ALCportPlayback_reset(ALCportPlayback
*self
);
145 static ALCboolean
ALCportPlayback_start(ALCportPlayback
*self
);
146 static void ALCportPlayback_stop(ALCportPlayback
*self
);
147 static DECLARE_FORWARD2(ALCportPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
148 static DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, ALCuint
, availableSamples
)
149 static DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
150 static DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, void, lock
)
151 static DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, void, unlock
)
152 DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback
)
154 DEFINE_ALCBACKEND_VTABLE(ALCportPlayback
);
157 static void ALCportPlayback_Construct(ALCportPlayback
*self
, ALCdevice
*device
)
159 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
160 SET_VTABLE2(ALCportPlayback
, ALCbackend
, self
);
165 static void ALCportPlayback_Destruct(ALCportPlayback
*self
)
167 PaError err
= self
->stream
? Pa_CloseStream(self
->stream
) : paNoError
;
169 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
172 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
176 static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer
), void *outputBuffer
,
177 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*UNUSED(timeInfo
),
178 const PaStreamCallbackFlags
UNUSED(statusFlags
), void *userData
)
180 ALCportPlayback
*self
= userData
;
182 ALCportPlayback_lock(self
);
183 aluMixData(STATIC_CAST(ALCbackend
, self
)->mDevice
, outputBuffer
, framesPerBuffer
);
184 ALCportPlayback_unlock(self
);
189 static ALCenum
ALCportPlayback_open(ALCportPlayback
*self
, const ALCchar
*name
)
191 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
196 else if(strcmp(name
, pa_device
) != 0)
197 return ALC_INVALID_VALUE
;
199 self
->update_size
= device
->UpdateSize
;
201 self
->params
.device
= -1;
202 if(!ConfigValueInt(NULL
, "port", "device", &self
->params
.device
) ||
203 self
->params
.device
< 0)
204 self
->params
.device
= Pa_GetDefaultOutputDevice();
205 self
->params
.suggestedLatency
= (device
->UpdateSize
*device
->NumUpdates
) /
206 (float)device
->Frequency
;
207 self
->params
.hostApiSpecificStreamInfo
= NULL
;
209 self
->params
.channelCount
= ((device
->FmtChans
== DevFmtMono
) ? 1 : 2);
211 switch(device
->FmtType
)
214 self
->params
.sampleFormat
= paInt8
;
217 self
->params
.sampleFormat
= paUInt8
;
222 self
->params
.sampleFormat
= paInt16
;
227 self
->params
.sampleFormat
= paInt32
;
230 self
->params
.sampleFormat
= paFloat32
;
235 err
= Pa_OpenStream(&self
->stream
, NULL
, &self
->params
,
236 device
->Frequency
, device
->UpdateSize
, paNoFlag
,
237 ALCportPlayback_WriteCallback
, self
241 if(self
->params
.sampleFormat
== paFloat32
)
243 self
->params
.sampleFormat
= paInt16
;
246 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
247 return ALC_INVALID_VALUE
;
250 alstr_copy_cstr(&device
->DeviceName
, name
);
256 static ALCboolean
ALCportPlayback_reset(ALCportPlayback
*self
)
258 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
259 const PaStreamInfo
*streamInfo
;
261 streamInfo
= Pa_GetStreamInfo(self
->stream
);
262 device
->Frequency
= streamInfo
->sampleRate
;
263 device
->UpdateSize
= self
->update_size
;
265 if(self
->params
.sampleFormat
== paInt8
)
266 device
->FmtType
= DevFmtByte
;
267 else if(self
->params
.sampleFormat
== paUInt8
)
268 device
->FmtType
= DevFmtUByte
;
269 else if(self
->params
.sampleFormat
== paInt16
)
270 device
->FmtType
= DevFmtShort
;
271 else if(self
->params
.sampleFormat
== paInt32
)
272 device
->FmtType
= DevFmtInt
;
273 else if(self
->params
.sampleFormat
== paFloat32
)
274 device
->FmtType
= DevFmtFloat
;
277 ERR("Unexpected sample format: 0x%lx\n", self
->params
.sampleFormat
);
281 if(self
->params
.channelCount
== 2)
282 device
->FmtChans
= DevFmtStereo
;
283 else if(self
->params
.channelCount
== 1)
284 device
->FmtChans
= DevFmtMono
;
287 ERR("Unexpected channel count: %u\n", self
->params
.channelCount
);
290 SetDefaultChannelOrder(device
);
295 static ALCboolean
ALCportPlayback_start(ALCportPlayback
*self
)
299 err
= Pa_StartStream(self
->stream
);
302 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err
));
309 static void ALCportPlayback_stop(ALCportPlayback
*self
)
311 PaError err
= Pa_StopStream(self
->stream
);
313 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
317 typedef struct ALCportCapture
{
318 DERIVE_FROM_TYPE(ALCbackend
);
321 PaStreamParameters params
;
323 ll_ringbuffer_t
*ring
;
326 static int ALCportCapture_ReadCallback(const void *inputBuffer
, void *outputBuffer
,
327 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
328 const PaStreamCallbackFlags statusFlags
, void *userData
);
330 static void ALCportCapture_Construct(ALCportCapture
*self
, ALCdevice
*device
);
331 static void ALCportCapture_Destruct(ALCportCapture
*self
);
332 static ALCenum
ALCportCapture_open(ALCportCapture
*self
, const ALCchar
*name
);
333 static DECLARE_FORWARD(ALCportCapture
, ALCbackend
, ALCboolean
, reset
)
334 static ALCboolean
ALCportCapture_start(ALCportCapture
*self
);
335 static void ALCportCapture_stop(ALCportCapture
*self
);
336 static ALCenum
ALCportCapture_captureSamples(ALCportCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
337 static ALCuint
ALCportCapture_availableSamples(ALCportCapture
*self
);
338 static DECLARE_FORWARD(ALCportCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
339 static DECLARE_FORWARD(ALCportCapture
, ALCbackend
, void, lock
)
340 static DECLARE_FORWARD(ALCportCapture
, ALCbackend
, void, unlock
)
341 DECLARE_DEFAULT_ALLOCATORS(ALCportCapture
)
343 DEFINE_ALCBACKEND_VTABLE(ALCportCapture
);
346 static void ALCportCapture_Construct(ALCportCapture
*self
, ALCdevice
*device
)
348 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
349 SET_VTABLE2(ALCportCapture
, ALCbackend
, self
);
355 static void ALCportCapture_Destruct(ALCportCapture
*self
)
357 PaError err
= self
->stream
? Pa_CloseStream(self
->stream
) : paNoError
;
359 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
362 ll_ringbuffer_free(self
->ring
);
365 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
369 static int ALCportCapture_ReadCallback(const void *inputBuffer
, void *UNUSED(outputBuffer
),
370 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*UNUSED(timeInfo
),
371 const PaStreamCallbackFlags
UNUSED(statusFlags
), void *userData
)
373 ALCportCapture
*self
= userData
;
374 size_t writable
= ll_ringbuffer_write_space(self
->ring
);
376 if(framesPerBuffer
> writable
)
377 framesPerBuffer
= writable
;
378 ll_ringbuffer_write(self
->ring
, inputBuffer
, framesPerBuffer
);
383 static ALCenum
ALCportCapture_open(ALCportCapture
*self
, const ALCchar
*name
)
385 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
386 ALuint samples
, frame_size
;
391 else if(strcmp(name
, pa_device
) != 0)
392 return ALC_INVALID_VALUE
;
394 samples
= device
->UpdateSize
* device
->NumUpdates
;
395 samples
= maxu(samples
, 100 * device
->Frequency
/ 1000);
396 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
398 self
->ring
= ll_ringbuffer_create(samples
, frame_size
, false);
399 if(self
->ring
== NULL
) return ALC_INVALID_VALUE
;
401 self
->params
.device
= -1;
402 if(!ConfigValueInt(NULL
, "port", "capture", &self
->params
.device
) ||
403 self
->params
.device
< 0)
404 self
->params
.device
= Pa_GetDefaultInputDevice();
405 self
->params
.suggestedLatency
= 0.0f
;
406 self
->params
.hostApiSpecificStreamInfo
= NULL
;
408 switch(device
->FmtType
)
411 self
->params
.sampleFormat
= paInt8
;
414 self
->params
.sampleFormat
= paUInt8
;
417 self
->params
.sampleFormat
= paInt16
;
420 self
->params
.sampleFormat
= paInt32
;
423 self
->params
.sampleFormat
= paFloat32
;
427 ERR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
428 return ALC_INVALID_VALUE
;
430 self
->params
.channelCount
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
432 err
= Pa_OpenStream(&self
->stream
, &self
->params
, NULL
,
433 device
->Frequency
, paFramesPerBufferUnspecified
, paNoFlag
,
434 ALCportCapture_ReadCallback
, self
438 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
439 return ALC_INVALID_VALUE
;
442 alstr_copy_cstr(&device
->DeviceName
, name
);
448 static ALCboolean
ALCportCapture_start(ALCportCapture
*self
)
450 PaError err
= Pa_StartStream(self
->stream
);
453 ERR("Error starting stream: %s\n", Pa_GetErrorText(err
));
459 static void ALCportCapture_stop(ALCportCapture
*self
)
461 PaError err
= Pa_StopStream(self
->stream
);
463 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
467 static ALCuint
ALCportCapture_availableSamples(ALCportCapture
*self
)
469 return ll_ringbuffer_read_space(self
->ring
);
472 static ALCenum
ALCportCapture_captureSamples(ALCportCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
474 ll_ringbuffer_read(self
->ring
, buffer
, samples
);
479 typedef struct ALCportBackendFactory
{
480 DERIVE_FROM_TYPE(ALCbackendFactory
);
481 } ALCportBackendFactory
;
482 #define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
484 static ALCboolean
ALCportBackendFactory_init(ALCportBackendFactory
*self
);
485 static void ALCportBackendFactory_deinit(ALCportBackendFactory
*self
);
486 static ALCboolean
ALCportBackendFactory_querySupport(ALCportBackendFactory
*self
, ALCbackend_Type type
);
487 static void ALCportBackendFactory_probe(ALCportBackendFactory
*self
, enum DevProbe type
);
488 static ALCbackend
* ALCportBackendFactory_createBackend(ALCportBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
490 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory
);
493 static ALCboolean
ALCportBackendFactory_init(ALCportBackendFactory
* UNUSED(self
))
500 static void ALCportBackendFactory_deinit(ALCportBackendFactory
* UNUSED(self
))
514 static ALCboolean
ALCportBackendFactory_querySupport(ALCportBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
516 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
521 static void ALCportBackendFactory_probe(ALCportBackendFactory
* UNUSED(self
), enum DevProbe type
)
525 case ALL_DEVICE_PROBE
:
526 AppendAllDevicesList(pa_device
);
528 case CAPTURE_DEVICE_PROBE
:
529 AppendCaptureDeviceList(pa_device
);
534 static ALCbackend
* ALCportBackendFactory_createBackend(ALCportBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
536 if(type
== ALCbackend_Playback
)
538 ALCportPlayback
*backend
;
539 NEW_OBJ(backend
, ALCportPlayback
)(device
);
540 if(!backend
) return NULL
;
541 return STATIC_CAST(ALCbackend
, backend
);
543 if(type
== ALCbackend_Capture
)
545 ALCportCapture
*backend
;
546 NEW_OBJ(backend
, ALCportCapture
)(device
);
547 if(!backend
) return NULL
;
548 return STATIC_CAST(ALCbackend
, backend
);
554 ALCbackendFactory
*ALCportBackendFactory_getFactory(void)
556 static ALCportBackendFactory factory
= ALCPORTBACKENDFACTORY_INITIALIZER
;
557 return STATIC_CAST(ALCbackendFactory
, &factory
);