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
23 #include "backends/portaudio.h"
32 #include "ringbuffer.h"
35 #include <portaudio.h>
40 constexpr ALCchar pa_device
[] = "PortAudio Default";
45 #define MAKE_FUNC(x) decltype(x) * p##x
46 MAKE_FUNC(Pa_Initialize
);
47 MAKE_FUNC(Pa_Terminate
);
48 MAKE_FUNC(Pa_GetErrorText
);
49 MAKE_FUNC(Pa_StartStream
);
50 MAKE_FUNC(Pa_StopStream
);
51 MAKE_FUNC(Pa_OpenStream
);
52 MAKE_FUNC(Pa_CloseStream
);
53 MAKE_FUNC(Pa_GetDefaultOutputDevice
);
54 MAKE_FUNC(Pa_GetDefaultInputDevice
);
55 MAKE_FUNC(Pa_GetStreamInfo
);
59 #define Pa_Initialize pPa_Initialize
60 #define Pa_Terminate pPa_Terminate
61 #define Pa_GetErrorText pPa_GetErrorText
62 #define Pa_StartStream pPa_StartStream
63 #define Pa_StopStream pPa_StopStream
64 #define Pa_OpenStream pPa_OpenStream
65 #define Pa_CloseStream pPa_CloseStream
66 #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
67 #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
68 #define Pa_GetStreamInfo pPa_GetStreamInfo
80 # define PALIB "portaudio.dll"
81 #elif defined(__APPLE__) && defined(__MACH__)
82 # define PALIB "libportaudio.2.dylib"
83 #elif defined(__OpenBSD__)
84 # define PALIB "libportaudio.so"
86 # define PALIB "libportaudio.so.2"
89 pa_handle
= LoadLib(PALIB
);
93 #define LOAD_FUNC(f) do { \
94 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
97 CloseLib(pa_handle); \
98 pa_handle = nullptr; \
102 LOAD_FUNC(Pa_Initialize
);
103 LOAD_FUNC(Pa_Terminate
);
104 LOAD_FUNC(Pa_GetErrorText
);
105 LOAD_FUNC(Pa_StartStream
);
106 LOAD_FUNC(Pa_StopStream
);
107 LOAD_FUNC(Pa_OpenStream
);
108 LOAD_FUNC(Pa_CloseStream
);
109 LOAD_FUNC(Pa_GetDefaultOutputDevice
);
110 LOAD_FUNC(Pa_GetDefaultInputDevice
);
111 LOAD_FUNC(Pa_GetStreamInfo
);
114 if((err
=Pa_Initialize()) != paNoError
)
116 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
123 if((err
=Pa_Initialize()) != paNoError
)
125 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err
));
133 struct ALCportPlayback final
: public ALCbackend
{
134 PaStream
*Stream
{nullptr};
135 PaStreamParameters Params
;
136 ALuint UpdateSize
{0u};
139 int ALCportPlayback_WriteCallback(const void *inputBuffer
, void *outputBuffer
,
140 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
141 const PaStreamCallbackFlags statusFlags
, void *userData
);
143 void ALCportPlayback_Construct(ALCportPlayback
*self
, ALCdevice
*device
);
144 void ALCportPlayback_Destruct(ALCportPlayback
*self
);
145 ALCenum
ALCportPlayback_open(ALCportPlayback
*self
, const ALCchar
*name
);
146 ALCboolean
ALCportPlayback_reset(ALCportPlayback
*self
);
147 ALCboolean
ALCportPlayback_start(ALCportPlayback
*self
);
148 void ALCportPlayback_stop(ALCportPlayback
*self
);
149 DECLARE_FORWARD2(ALCportPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
150 DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, ALCuint
, availableSamples
)
151 DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
152 DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, void, lock
)
153 DECLARE_FORWARD(ALCportPlayback
, ALCbackend
, void, unlock
)
154 DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback
)
156 DEFINE_ALCBACKEND_VTABLE(ALCportPlayback
);
159 void ALCportPlayback_Construct(ALCportPlayback
*self
, ALCdevice
*device
)
161 new (self
) ALCportPlayback
{};
162 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
163 SET_VTABLE2(ALCportPlayback
, ALCbackend
, self
);
166 void ALCportPlayback_Destruct(ALCportPlayback
*self
)
168 PaError err
= self
->Stream
? Pa_CloseStream(self
->Stream
) : paNoError
;
170 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
171 self
->Stream
= nullptr;
173 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
174 self
->~ALCportPlayback();
178 int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer
), void *outputBuffer
,
179 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*UNUSED(timeInfo
),
180 const PaStreamCallbackFlags
UNUSED(statusFlags
), void *userData
)
182 ALCportPlayback
*self
= static_cast<ALCportPlayback
*>(userData
);
184 ALCportPlayback_lock(self
);
185 aluMixData(STATIC_CAST(ALCbackend
, self
)->mDevice
, outputBuffer
, framesPerBuffer
);
186 ALCportPlayback_unlock(self
);
191 ALCenum
ALCportPlayback_open(ALCportPlayback
*self
, const ALCchar
*name
)
193 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
198 else if(strcmp(name
, pa_device
) != 0)
199 return ALC_INVALID_VALUE
;
201 self
->UpdateSize
= device
->UpdateSize
;
203 self
->Params
.device
= -1;
204 if(!ConfigValueInt(nullptr, "port", "device", &self
->Params
.device
) ||
205 self
->Params
.device
< 0)
206 self
->Params
.device
= Pa_GetDefaultOutputDevice();
207 self
->Params
.suggestedLatency
= (device
->UpdateSize
*device
->NumUpdates
) /
208 (float)device
->Frequency
;
209 self
->Params
.hostApiSpecificStreamInfo
= nullptr;
211 self
->Params
.channelCount
= ((device
->FmtChans
== DevFmtMono
) ? 1 : 2);
213 switch(device
->FmtType
)
216 self
->Params
.sampleFormat
= paInt8
;
219 self
->Params
.sampleFormat
= paUInt8
;
224 self
->Params
.sampleFormat
= paInt16
;
229 self
->Params
.sampleFormat
= paInt32
;
232 self
->Params
.sampleFormat
= paFloat32
;
237 err
= Pa_OpenStream(&self
->Stream
, nullptr, &self
->Params
,
238 device
->Frequency
, device
->UpdateSize
, paNoFlag
,
239 ALCportPlayback_WriteCallback
, self
243 if(self
->Params
.sampleFormat
== paFloat32
)
245 self
->Params
.sampleFormat
= paInt16
;
248 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
249 return ALC_INVALID_VALUE
;
252 device
->DeviceName
= name
;
257 ALCboolean
ALCportPlayback_reset(ALCportPlayback
*self
)
259 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
260 const PaStreamInfo
*streamInfo
;
262 streamInfo
= Pa_GetStreamInfo(self
->Stream
);
263 device
->Frequency
= streamInfo
->sampleRate
;
264 device
->UpdateSize
= self
->UpdateSize
;
266 if(self
->Params
.sampleFormat
== paInt8
)
267 device
->FmtType
= DevFmtByte
;
268 else if(self
->Params
.sampleFormat
== paUInt8
)
269 device
->FmtType
= DevFmtUByte
;
270 else if(self
->Params
.sampleFormat
== paInt16
)
271 device
->FmtType
= DevFmtShort
;
272 else if(self
->Params
.sampleFormat
== paInt32
)
273 device
->FmtType
= DevFmtInt
;
274 else if(self
->Params
.sampleFormat
== paFloat32
)
275 device
->FmtType
= DevFmtFloat
;
278 ERR("Unexpected sample format: 0x%lx\n", self
->Params
.sampleFormat
);
282 if(self
->Params
.channelCount
== 2)
283 device
->FmtChans
= DevFmtStereo
;
284 else if(self
->Params
.channelCount
== 1)
285 device
->FmtChans
= DevFmtMono
;
288 ERR("Unexpected channel count: %u\n", self
->Params
.channelCount
);
291 SetDefaultChannelOrder(device
);
296 ALCboolean
ALCportPlayback_start(ALCportPlayback
*self
)
300 err
= Pa_StartStream(self
->Stream
);
303 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err
));
310 void ALCportPlayback_stop(ALCportPlayback
*self
)
312 PaError err
= Pa_StopStream(self
->Stream
);
314 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
318 struct ALCportCapture final
: public ALCbackend
{
319 PaStream
*Stream
{nullptr};
320 PaStreamParameters Params
;
322 ll_ringbuffer_t
*Ring
{nullptr};
325 int ALCportCapture_ReadCallback(const void *inputBuffer
, void *outputBuffer
,
326 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*timeInfo
,
327 const PaStreamCallbackFlags statusFlags
, void *userData
);
329 void ALCportCapture_Construct(ALCportCapture
*self
, ALCdevice
*device
);
330 void ALCportCapture_Destruct(ALCportCapture
*self
);
331 ALCenum
ALCportCapture_open(ALCportCapture
*self
, const ALCchar
*name
);
332 DECLARE_FORWARD(ALCportCapture
, ALCbackend
, ALCboolean
, reset
)
333 ALCboolean
ALCportCapture_start(ALCportCapture
*self
);
334 void ALCportCapture_stop(ALCportCapture
*self
);
335 ALCenum
ALCportCapture_captureSamples(ALCportCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
336 ALCuint
ALCportCapture_availableSamples(ALCportCapture
*self
);
337 DECLARE_FORWARD(ALCportCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
338 DECLARE_FORWARD(ALCportCapture
, ALCbackend
, void, lock
)
339 DECLARE_FORWARD(ALCportCapture
, ALCbackend
, void, unlock
)
340 DECLARE_DEFAULT_ALLOCATORS(ALCportCapture
)
342 DEFINE_ALCBACKEND_VTABLE(ALCportCapture
);
345 void ALCportCapture_Construct(ALCportCapture
*self
, ALCdevice
*device
)
347 new (self
) ALCportCapture
{};
348 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
349 SET_VTABLE2(ALCportCapture
, ALCbackend
, self
);
352 void ALCportCapture_Destruct(ALCportCapture
*self
)
354 PaError err
= self
->Stream
? Pa_CloseStream(self
->Stream
) : paNoError
;
356 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
357 self
->Stream
= nullptr;
359 ll_ringbuffer_free(self
->Ring
);
360 self
->Ring
= nullptr;
362 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
363 self
->~ALCportCapture();
367 int ALCportCapture_ReadCallback(const void *inputBuffer
, void *UNUSED(outputBuffer
),
368 unsigned long framesPerBuffer
, const PaStreamCallbackTimeInfo
*UNUSED(timeInfo
),
369 const PaStreamCallbackFlags
UNUSED(statusFlags
), void *userData
)
371 ALCportCapture
*self
= static_cast<ALCportCapture
*>(userData
);
372 size_t writable
= ll_ringbuffer_write_space(self
->Ring
);
374 if(framesPerBuffer
> writable
) framesPerBuffer
= writable
;
375 ll_ringbuffer_write(self
->Ring
, inputBuffer
, framesPerBuffer
);
380 ALCenum
ALCportCapture_open(ALCportCapture
*self
, const ALCchar
*name
)
382 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
383 ALuint samples
, frame_size
;
388 else if(strcmp(name
, pa_device
) != 0)
389 return ALC_INVALID_VALUE
;
391 samples
= device
->UpdateSize
* device
->NumUpdates
;
392 samples
= maxu(samples
, 100 * device
->Frequency
/ 1000);
393 frame_size
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
);
395 self
->Ring
= ll_ringbuffer_create(samples
, frame_size
, false);
396 if(self
->Ring
== nullptr) return ALC_INVALID_VALUE
;
398 self
->Params
.device
= -1;
399 if(!ConfigValueInt(nullptr, "port", "capture", &self
->Params
.device
) ||
400 self
->Params
.device
< 0)
401 self
->Params
.device
= Pa_GetDefaultInputDevice();
402 self
->Params
.suggestedLatency
= 0.0f
;
403 self
->Params
.hostApiSpecificStreamInfo
= nullptr;
405 switch(device
->FmtType
)
408 self
->Params
.sampleFormat
= paInt8
;
411 self
->Params
.sampleFormat
= paUInt8
;
414 self
->Params
.sampleFormat
= paInt16
;
417 self
->Params
.sampleFormat
= paInt32
;
420 self
->Params
.sampleFormat
= paFloat32
;
424 ERR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
425 return ALC_INVALID_VALUE
;
427 self
->Params
.channelCount
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
429 err
= Pa_OpenStream(&self
->Stream
, &self
->Params
, nullptr,
430 device
->Frequency
, paFramesPerBufferUnspecified
, paNoFlag
,
431 ALCportCapture_ReadCallback
, self
435 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
436 return ALC_INVALID_VALUE
;
439 device
->DeviceName
= name
;
444 ALCboolean
ALCportCapture_start(ALCportCapture
*self
)
446 PaError err
= Pa_StartStream(self
->Stream
);
449 ERR("Error starting stream: %s\n", Pa_GetErrorText(err
));
455 void ALCportCapture_stop(ALCportCapture
*self
)
457 PaError err
= Pa_StopStream(self
->Stream
);
459 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err
));
463 ALCuint
ALCportCapture_availableSamples(ALCportCapture
*self
)
465 return ll_ringbuffer_read_space(self
->Ring
);
468 ALCenum
ALCportCapture_captureSamples(ALCportCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
470 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
477 bool PortBackendFactory::init()
478 { return pa_load(); }
480 void PortBackendFactory::deinit()
494 bool PortBackendFactory::querySupport(ALCbackend_Type type
)
495 { return (type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
); }
497 void PortBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
501 case ALL_DEVICE_PROBE
:
502 case CAPTURE_DEVICE_PROBE
:
503 /* Includes null char. */
504 outnames
->append(pa_device
, sizeof(pa_device
));
509 ALCbackend
*PortBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
511 if(type
== ALCbackend_Playback
)
513 ALCportPlayback
*backend
;
514 NEW_OBJ(backend
, ALCportPlayback
)(device
);
515 if(!backend
) return nullptr;
516 return STATIC_CAST(ALCbackend
, backend
);
518 if(type
== ALCbackend_Capture
)
520 ALCportCapture
*backend
;
521 NEW_OBJ(backend
, ALCportCapture
)(device
);
522 if(!backend
) return nullptr;
523 return STATIC_CAST(ALCbackend
, backend
);
529 BackendFactory
&PortBackendFactory::getFactory()
531 static PortBackendFactory factory
{};