Use c++ headers
[openal-soft.git] / Alc / backends / portaudio.cpp
blob74c644f555b132133e18e09d9be259a7ac7df0f5
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 <cstdio>
26 #include <cstdlib>
27 #include <cstring>
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 PortPlayback final : public BackendBase {
134 PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
135 ~PortPlayback() override;
137 static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
138 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
139 const PaStreamCallbackFlags statusFlags, void *userData);
140 int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
141 const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
143 ALCenum open(const ALCchar *name) override;
144 ALCboolean reset() override;
145 ALCboolean start() override;
146 void stop() override;
148 PaStream *mStream{nullptr};
149 PaStreamParameters mParams{};
150 ALuint mUpdateSize{0u};
152 static constexpr inline const char *CurrentPrefix() noexcept { return "PortPlayback::"; }
153 DEF_NEWDEL(PortPlayback)
156 PortPlayback::~PortPlayback()
158 PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
159 if(err != paNoError)
160 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
161 mStream = nullptr;
165 int PortPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer,
166 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
167 const PaStreamCallbackFlags statusFlags, void *userData)
169 return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
170 framesPerBuffer, timeInfo, statusFlags);
173 int PortPlayback::writeCallback(const void* UNUSED(inputBuffer), void *outputBuffer,
174 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* UNUSED(timeInfo),
175 const PaStreamCallbackFlags UNUSED(statusFlags))
177 lock();
178 aluMixData(mDevice, outputBuffer, framesPerBuffer);
179 unlock();
180 return 0;
184 ALCenum PortPlayback::open(const ALCchar *name)
186 if(!name)
187 name = pa_device;
188 else if(strcmp(name, pa_device) != 0)
189 return ALC_INVALID_VALUE;
191 mUpdateSize = mDevice->UpdateSize;
193 mParams.device = -1;
194 if(!ConfigValueInt(nullptr, "port", "device", &mParams.device) || mParams.device < 0)
195 mParams.device = Pa_GetDefaultOutputDevice();
196 mParams.suggestedLatency = (mDevice->UpdateSize*mDevice->NumUpdates) /
197 static_cast<float>(mDevice->Frequency);
198 mParams.hostApiSpecificStreamInfo = nullptr;
200 mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
202 switch(mDevice->FmtType)
204 case DevFmtByte:
205 mParams.sampleFormat = paInt8;
206 break;
207 case DevFmtUByte:
208 mParams.sampleFormat = paUInt8;
209 break;
210 case DevFmtUShort:
211 /* fall-through */
212 case DevFmtShort:
213 mParams.sampleFormat = paInt16;
214 break;
215 case DevFmtUInt:
216 /* fall-through */
217 case DevFmtInt:
218 mParams.sampleFormat = paInt32;
219 break;
220 case DevFmtFloat:
221 mParams.sampleFormat = paFloat32;
222 break;
225 retry_open:
226 PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
227 paNoFlag, &PortPlayback::writeCallbackC, this)};
228 if(err != paNoError)
230 if(mParams.sampleFormat == paFloat32)
232 mParams.sampleFormat = paInt16;
233 goto retry_open;
235 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
236 return ALC_INVALID_VALUE;
239 mDevice->DeviceName = name;
240 return ALC_NO_ERROR;
244 ALCboolean PortPlayback::reset()
246 const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
247 mDevice->Frequency = streamInfo->sampleRate;
248 mDevice->UpdateSize = mUpdateSize;
250 if(mParams.sampleFormat == paInt8)
251 mDevice->FmtType = DevFmtByte;
252 else if(mParams.sampleFormat == paUInt8)
253 mDevice->FmtType = DevFmtUByte;
254 else if(mParams.sampleFormat == paInt16)
255 mDevice->FmtType = DevFmtShort;
256 else if(mParams.sampleFormat == paInt32)
257 mDevice->FmtType = DevFmtInt;
258 else if(mParams.sampleFormat == paFloat32)
259 mDevice->FmtType = DevFmtFloat;
260 else
262 ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
263 return ALC_FALSE;
266 if(mParams.channelCount == 2)
267 mDevice->FmtChans = DevFmtStereo;
268 else if(mParams.channelCount == 1)
269 mDevice->FmtChans = DevFmtMono;
270 else
272 ERR("Unexpected channel count: %u\n", mParams.channelCount);
273 return ALC_FALSE;
275 SetDefaultChannelOrder(mDevice);
277 return ALC_TRUE;
280 ALCboolean PortPlayback::start()
282 PaError err{Pa_StartStream(mStream)};
283 if(err != paNoError)
285 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
286 return ALC_FALSE;
288 return ALC_TRUE;
291 void PortPlayback::stop()
293 PaError err{Pa_StopStream(mStream)};
294 if(err != paNoError)
295 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
299 struct PortCapture final : public BackendBase {
300 PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
301 ~PortCapture() override;
303 static int readCallbackC(const void *inputBuffer, void *outputBuffer,
304 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
305 const PaStreamCallbackFlags statusFlags, void *userData);
306 int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
307 const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
309 ALCenum open(const ALCchar *name) override;
310 ALCboolean start() override;
311 void stop() override;
312 ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
313 ALCuint availableSamples() override;
315 PaStream *mStream{nullptr};
316 PaStreamParameters mParams;
318 RingBufferPtr mRing{nullptr};
320 static constexpr inline const char *CurrentPrefix() noexcept { return "PortCapture::"; }
321 DEF_NEWDEL(PortCapture)
324 PortCapture::~PortCapture()
326 PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
327 if(err != paNoError)
328 ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
329 mStream = nullptr;
333 int PortCapture::readCallbackC(const void *inputBuffer, void *outputBuffer,
334 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
335 const PaStreamCallbackFlags statusFlags, void* userData)
337 return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
338 framesPerBuffer, timeInfo, statusFlags);
341 int PortCapture::readCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
342 unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
343 const PaStreamCallbackFlags UNUSED(statusFlags))
345 mRing->write(inputBuffer, framesPerBuffer);
346 return 0;
350 ALCenum PortCapture::open(const ALCchar *name)
352 if(!name)
353 name = pa_device;
354 else if(strcmp(name, pa_device) != 0)
355 return ALC_INVALID_VALUE;
357 ALuint samples{mDevice->UpdateSize * mDevice->NumUpdates};
358 samples = maxu(samples, 100 * mDevice->Frequency / 1000);
359 ALsizei frame_size{mDevice->frameSizeFromFmt()};
361 mRing = CreateRingBuffer(samples, frame_size, false);
362 if(!mRing) return ALC_INVALID_VALUE;
364 mParams.device = -1;
365 if(!ConfigValueInt(nullptr, "port", "capture", &mParams.device) || mParams.device < 0)
366 mParams.device = Pa_GetDefaultInputDevice();
367 mParams.suggestedLatency = 0.0f;
368 mParams.hostApiSpecificStreamInfo = nullptr;
370 switch(mDevice->FmtType)
372 case DevFmtByte:
373 mParams.sampleFormat = paInt8;
374 break;
375 case DevFmtUByte:
376 mParams.sampleFormat = paUInt8;
377 break;
378 case DevFmtShort:
379 mParams.sampleFormat = paInt16;
380 break;
381 case DevFmtInt:
382 mParams.sampleFormat = paInt32;
383 break;
384 case DevFmtFloat:
385 mParams.sampleFormat = paFloat32;
386 break;
387 case DevFmtUInt:
388 case DevFmtUShort:
389 ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
390 return ALC_INVALID_VALUE;
392 mParams.channelCount = mDevice->channelsFromFmt();
394 PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
395 paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
396 if(err != paNoError)
398 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
399 return ALC_INVALID_VALUE;
402 mDevice->DeviceName = name;
403 return ALC_NO_ERROR;
407 ALCboolean PortCapture::start()
409 PaError err{Pa_StartStream(mStream)};
410 if(err != paNoError)
412 ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
413 return ALC_FALSE;
415 return ALC_TRUE;
418 void PortCapture::stop()
420 PaError err{Pa_StopStream(mStream)};
421 if(err != paNoError)
422 ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
426 ALCuint PortCapture::availableSamples()
427 { return mRing->readSpace(); }
429 ALCenum PortCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
431 mRing->read(buffer, samples);
432 return ALC_NO_ERROR;
435 } // namespace
438 bool PortBackendFactory::init()
439 { return pa_load(); }
441 void PortBackendFactory::deinit()
443 #ifdef HAVE_DYNLOAD
444 if(pa_handle)
446 Pa_Terminate();
447 CloseLib(pa_handle);
448 pa_handle = nullptr;
450 #else
451 Pa_Terminate();
452 #endif
455 bool PortBackendFactory::querySupport(BackendType type)
456 { return (type == BackendType::Playback || type == BackendType::Capture); }
458 void PortBackendFactory::probe(DevProbe type, std::string *outnames)
460 switch(type)
462 case ALL_DEVICE_PROBE:
463 case CAPTURE_DEVICE_PROBE:
464 /* Includes null char. */
465 outnames->append(pa_device, sizeof(pa_device));
466 break;
470 BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
472 if(type == BackendType::Playback)
473 return BackendPtr{new PortPlayback{device}};
474 if(type == BackendType::Capture)
475 return BackendPtr{new PortCapture{device}};
476 return nullptr;
479 BackendFactory &PortBackendFactory::getFactory()
481 static PortBackendFactory factory{};
482 return factory;