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 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
};
160 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
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
))
178 aluMixData(mDevice
, outputBuffer
, framesPerBuffer
);
184 ALCenum
PortPlayback::open(const ALCchar
*name
)
188 else if(strcmp(name
, pa_device
) != 0)
189 return ALC_INVALID_VALUE
;
191 mUpdateSize
= mDevice
->UpdateSize
;
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
)
205 mParams
.sampleFormat
= paInt8
;
208 mParams
.sampleFormat
= paUInt8
;
213 mParams
.sampleFormat
= paInt16
;
218 mParams
.sampleFormat
= paInt32
;
221 mParams
.sampleFormat
= paFloat32
;
226 PaError err
{Pa_OpenStream(&mStream
, nullptr, &mParams
, mDevice
->Frequency
, mDevice
->UpdateSize
,
227 paNoFlag
, &PortPlayback::writeCallbackC
, this)};
230 if(mParams
.sampleFormat
== paFloat32
)
232 mParams
.sampleFormat
= paInt16
;
235 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
236 return ALC_INVALID_VALUE
;
239 mDevice
->DeviceName
= name
;
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
;
262 ERR("Unexpected sample format: 0x%lx\n", mParams
.sampleFormat
);
266 if(mParams
.channelCount
== 2)
267 mDevice
->FmtChans
= DevFmtStereo
;
268 else if(mParams
.channelCount
== 1)
269 mDevice
->FmtChans
= DevFmtMono
;
272 ERR("Unexpected channel count: %u\n", mParams
.channelCount
);
275 SetDefaultChannelOrder(mDevice
);
280 ALCboolean
PortPlayback::start()
282 PaError err
{Pa_StartStream(mStream
)};
285 ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err
));
291 void PortPlayback::stop()
293 PaError err
{Pa_StopStream(mStream
)};
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
};
328 ERR("Error closing stream: %s\n", Pa_GetErrorText(err
));
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
);
350 ALCenum
PortCapture::open(const ALCchar
*name
)
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
;
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
)
373 mParams
.sampleFormat
= paInt8
;
376 mParams
.sampleFormat
= paUInt8
;
379 mParams
.sampleFormat
= paInt16
;
382 mParams
.sampleFormat
= paInt32
;
385 mParams
.sampleFormat
= paFloat32
;
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)};
398 ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err
));
399 return ALC_INVALID_VALUE
;
402 mDevice
->DeviceName
= name
;
407 ALCboolean
PortCapture::start()
409 PaError err
{Pa_StartStream(mStream
)};
412 ERR("Error starting stream: %s\n", Pa_GetErrorText(err
));
418 void PortCapture::stop()
420 PaError err
{Pa_StopStream(mStream
)};
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
);
438 bool PortBackendFactory::init()
439 { return pa_load(); }
441 void PortBackendFactory::deinit()
455 bool PortBackendFactory::querySupport(BackendType type
)
456 { return (type
== BackendType::Playback
|| type
== BackendType::Capture
); }
458 void PortBackendFactory::probe(DevProbe type
, std::string
*outnames
)
462 case ALL_DEVICE_PROBE
:
463 case CAPTURE_DEVICE_PROBE
:
464 /* Includes null char. */
465 outnames
->append(pa_device
, sizeof(pa_device
));
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
}};
479 BackendFactory
&PortBackendFactory::getFactory()
481 static PortBackendFactory factory
{};