Rename some struct members
[openal-soft.git] / Alc / backends / portaudio.cpp
blob8b8c7fefea49d711c0fabe01e276e66c4c6c5038
1 /**
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
21 #include "config.h"
23 #include "backends/portaudio.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include "alMain.h"
30 #include "alu.h"
31 #include "alconfig.h"
32 #include "ringbuffer.h"
33 #include "compat.h"
35 #include <portaudio.h>
38 namespace {
40 constexpr ALCchar pa_device[] = "PortAudio Default";
43 #ifdef HAVE_DYNLOAD
44 void *pa_handle;
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);
56 #undef MAKE_FUNC
58 #ifndef IN_IDE_PARSER
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
69 #endif
70 #endif
72 bool pa_load(void)
74 PaError err;
76 #ifdef HAVE_DYNLOAD
77 if(!pa_handle)
79 #ifdef _WIN32
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"
85 #else
86 # define PALIB "libportaudio.so.2"
87 #endif
89 pa_handle = LoadLib(PALIB);
90 if(!pa_handle)
91 return false;
93 #define LOAD_FUNC(f) do { \
94 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
95 if(p##f == nullptr) \
96 { \
97 CloseLib(pa_handle); \
98 pa_handle = nullptr; \
99 return false; \
101 } while(0)
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);
112 #undef LOAD_FUNC
114 if((err=Pa_Initialize()) != paNoError)
116 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
117 CloseLib(pa_handle);
118 pa_handle = nullptr;
119 return false;
122 #else
123 if((err=Pa_Initialize()) != paNoError)
125 ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
126 return false;
128 #endif
129 return true;
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;
169 if(err != 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);
187 return 0;
191 ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
193 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
194 PaError err;
196 if(!name)
197 name = pa_device;
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)
215 case DevFmtByte:
216 self->Params.sampleFormat = paInt8;
217 break;
218 case DevFmtUByte:
219 self->Params.sampleFormat = paUInt8;
220 break;
221 case DevFmtUShort:
222 /* fall-through */
223 case DevFmtShort:
224 self->Params.sampleFormat = paInt16;
225 break;
226 case DevFmtUInt:
227 /* fall-through */
228 case DevFmtInt:
229 self->Params.sampleFormat = paInt32;
230 break;
231 case DevFmtFloat:
232 self->Params.sampleFormat = paFloat32;
233 break;
236 retry_open:
237 err = Pa_OpenStream(&self->Stream, nullptr, &self->Params,
238 device->Frequency, device->UpdateSize, paNoFlag,
239 ALCportPlayback_WriteCallback, self
241 if(err != paNoError)
243 if(self->Params.sampleFormat == paFloat32)
245 self->Params.sampleFormat = paInt16;
246 goto retry_open;
248 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
249 return ALC_INVALID_VALUE;
252 device->DeviceName = name;
253 return ALC_NO_ERROR;
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;
276 else
278 ERR("Unexpected sample format: 0x%lx\n", self->Params.sampleFormat);
279 return ALC_FALSE;
282 if(self->Params.channelCount == 2)
283 device->FmtChans = DevFmtStereo;
284 else if(self->Params.channelCount == 1)
285 device->FmtChans = DevFmtMono;
286 else
288 ERR("Unexpected channel count: %u\n", self->Params.channelCount);
289 return ALC_FALSE;
291 SetDefaultChannelOrder(device);
293 return ALC_TRUE;
296 ALCboolean ALCportPlayback_start(ALCportPlayback *self)
298 PaError err;
300 err = Pa_StartStream(self->Stream);
301 if(err != paNoError)
303 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
304 return ALC_FALSE;
307 return ALC_TRUE;
310 void ALCportPlayback_stop(ALCportPlayback *self)
312 PaError err = Pa_StopStream(self->Stream);
313 if(err != paNoError)
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;
355 if(err != 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);
376 return 0;
380 ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
382 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
383 ALuint samples, frame_size;
384 PaError err;
386 if(!name)
387 name = pa_device;
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)
407 case DevFmtByte:
408 self->Params.sampleFormat = paInt8;
409 break;
410 case DevFmtUByte:
411 self->Params.sampleFormat = paUInt8;
412 break;
413 case DevFmtShort:
414 self->Params.sampleFormat = paInt16;
415 break;
416 case DevFmtInt:
417 self->Params.sampleFormat = paInt32;
418 break;
419 case DevFmtFloat:
420 self->Params.sampleFormat = paFloat32;
421 break;
422 case DevFmtUInt:
423 case DevFmtUShort:
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
433 if(err != paNoError)
435 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
436 return ALC_INVALID_VALUE;
439 device->DeviceName = name;
440 return ALC_NO_ERROR;
444 ALCboolean ALCportCapture_start(ALCportCapture *self)
446 PaError err = Pa_StartStream(self->Stream);
447 if(err != paNoError)
449 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
450 return ALC_FALSE;
452 return ALC_TRUE;
455 void ALCportCapture_stop(ALCportCapture *self)
457 PaError err = Pa_StopStream(self->Stream);
458 if(err != paNoError)
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);
471 return ALC_NO_ERROR;
474 } // namespace
477 bool PortBackendFactory::init()
478 { return pa_load(); }
480 void PortBackendFactory::deinit()
482 #ifdef HAVE_DYNLOAD
483 if(pa_handle)
485 Pa_Terminate();
486 CloseLib(pa_handle);
487 pa_handle = nullptr;
489 #else
490 Pa_Terminate();
491 #endif
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)
499 switch(type)
501 case ALL_DEVICE_PROBE:
502 case CAPTURE_DEVICE_PROBE:
503 /* Includes null char. */
504 outnames->append(pa_device, sizeof(pa_device));
505 break;
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);
526 return nullptr;
529 BackendFactory &PortBackendFactory::getFactory()
531 static PortBackendFactory factory{};
532 return factory;