diff options
Diffstat (limited to 'Alc/backends')
-rw-r--r-- | Alc/backends/oss.cpp | 104 | ||||
-rw-r--r-- | Alc/backends/portaudio.cpp | 88 | ||||
-rw-r--r-- | Alc/backends/pulseaudio.cpp | 475 |
3 files changed, 316 insertions, 351 deletions
diff --git a/Alc/backends/oss.cpp b/Alc/backends/oss.cpp index c35c7247..54d7e0af 100644 --- a/Alc/backends/oss.cpp +++ b/Alc/backends/oss.cpp @@ -242,18 +242,19 @@ int log2i(ALCuint x) struct ALCplaybackOSS final : public ALCbackend { + ALCplaybackOSS(ALCdevice *device) noexcept : ALCbackend{device} { } + ~ALCplaybackOSS() override; + + int mixerProc(); + int mFd{-1}; al::vector<ALubyte> mMixData; std::atomic<ALenum> mKillNow{AL_TRUE}; std::thread mThread; - - ALCplaybackOSS(ALCdevice *device) noexcept : ALCbackend{device} { } }; -int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self); - void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device); void ALCplaybackOSS_Destruct(ALCplaybackOSS *self); ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name); @@ -276,41 +277,40 @@ void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device) } void ALCplaybackOSS_Destruct(ALCplaybackOSS *self) -{ - if(self->mFd != -1) - close(self->mFd); - self->mFd = -1; +{ self->~ALCplaybackOSS(); } - self->~ALCplaybackOSS(); +ALCplaybackOSS::~ALCplaybackOSS() +{ + if(mFd != -1) + close(mFd); + mFd = -1; } -int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self) +int ALCplaybackOSS::mixerProc() { - ALCdevice *device{self->mDevice}; - SetRTPriority(); althrd_setname(MIXER_THREAD_NAME); - const int frame_size{device->frameSizeFromFmt()}; + const int frame_size{mDevice->frameSizeFromFmt()}; - ALCplaybackOSS_lock(self); - while(!self->mKillNow.load(std::memory_order_acquire) && - device->Connected.load(std::memory_order_acquire)) + ALCplaybackOSS_lock(this); + while(!mKillNow.load(std::memory_order_acquire) && + mDevice->Connected.load(std::memory_order_acquire)) { pollfd pollitem{}; - pollitem.fd = self->mFd; + pollitem.fd = mFd; pollitem.events = POLLOUT; - ALCplaybackOSS_unlock(self); + ALCplaybackOSS_unlock(this); int pret{poll(&pollitem, 1, 1000)}; - ALCplaybackOSS_lock(self); + ALCplaybackOSS_lock(this); if(pret < 0) { if(errno == EINTR || errno == EAGAIN) continue; ERR("poll failed: %s\n", strerror(errno)); - aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno)); + aluHandleDisconnect(mDevice, "Failed waiting for playback buffer: %s", strerror(errno)); break; } else if(pret == 0) @@ -319,18 +319,18 @@ int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self) continue; } - ALubyte *write_ptr{self->mMixData.data()}; - size_t to_write{self->mMixData.size()}; - aluMixData(device, write_ptr, to_write/frame_size); - while(to_write > 0 && !self->mKillNow.load()) + ALubyte *write_ptr{mMixData.data()}; + size_t to_write{mMixData.size()}; + aluMixData(mDevice, write_ptr, to_write/frame_size); + while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(self->mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, write_ptr, to_write)}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; ERR("write failed: %s\n", strerror(errno)); - aluHandleDisconnect(device, "Failed writing playback samples: %s", + aluHandleDisconnect(mDevice, "Failed writing playback samples: %s", strerror(errno)); break; } @@ -339,7 +339,7 @@ int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self) write_ptr += wrote; } } - ALCplaybackOSS_unlock(self); + ALCplaybackOSS_unlock(this); return 0; } @@ -467,7 +467,7 @@ ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) self->mMixData.resize(device->UpdateSize * device->frameSizeFromFmt()); self->mKillNow.store(AL_FALSE); - self->mThread = std::thread(ALCplaybackOSS_mixerProc, self); + self->mThread = std::thread{std::mem_fn(&ALCplaybackOSS::mixerProc), self}; return ALC_TRUE; } catch(std::exception& e) { @@ -492,18 +492,19 @@ void ALCplaybackOSS_stop(ALCplaybackOSS *self) struct ALCcaptureOSS final : public ALCbackend { + ALCcaptureOSS(ALCdevice *device) noexcept : ALCbackend{device} { } + ~ALCcaptureOSS() override; + + int recordProc(); + int mFd{-1}; RingBufferPtr mRing{nullptr}; std::atomic<ALenum> mKillNow{AL_TRUE}; std::thread mThread; - - ALCcaptureOSS(ALCdevice *device) noexcept : ALCbackend{device} { } }; -int ALCcaptureOSS_recordProc(ALCcaptureOSS *self); - void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device); void ALCcaptureOSS_Destruct(ALCcaptureOSS *self); ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name); @@ -526,28 +527,26 @@ void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device) } void ALCcaptureOSS_Destruct(ALCcaptureOSS *self) -{ - if(self->mFd != -1) - close(self->mFd); - self->mFd = -1; +{ self->~ALCcaptureOSS(); } - self->~ALCcaptureOSS(); +ALCcaptureOSS::~ALCcaptureOSS() +{ + if(mFd != -1) + close(mFd); + mFd = -1; } -int ALCcaptureOSS_recordProc(ALCcaptureOSS *self) +int ALCcaptureOSS::recordProc() { - ALCdevice *device{self->mDevice}; - RingBuffer *ring{self->mRing.get()}; - SetRTPriority(); althrd_setname(RECORD_THREAD_NAME); - const int frame_size{device->frameSizeFromFmt()}; - while(!self->mKillNow.load()) + const int frame_size{mDevice->frameSizeFromFmt()}; + while(!mKillNow.load(std::memory_order_acquire)) { pollfd pollitem{}; - pollitem.fd = self->mFd; + pollitem.fd = mFd; pollitem.events = POLLIN; int sret{poll(&pollitem, 1, 1000)}; @@ -556,7 +555,7 @@ int ALCcaptureOSS_recordProc(ALCcaptureOSS *self) if(errno == EINTR || errno == EAGAIN) continue; ERR("poll failed: %s\n", strerror(errno)); - aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno)); + aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno)); break; } else if(sret == 0) @@ -565,19 +564,20 @@ int ALCcaptureOSS_recordProc(ALCcaptureOSS *self) continue; } - auto vec = ring->getWriteVector(); + auto vec = mRing->getWriteVector(); if(vec.first.len > 0) { - ssize_t amt{read(self->mFd, vec.first.buf, vec.first.len*frame_size)}; + ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)}; if(amt < 0) { ERR("read failed: %s\n", strerror(errno)); - ALCcaptureOSS_lock(self); - aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno)); - ALCcaptureOSS_unlock(self); + ALCcaptureOSS_lock(this); + aluHandleDisconnect(mDevice, "Failed reading capture samples: %s", + strerror(errno)); + ALCcaptureOSS_unlock(this); break; } - ring->writeAdvance(amt/frame_size); + mRing->writeAdvance(amt/frame_size); } } @@ -700,7 +700,7 @@ ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self) { try { self->mKillNow.store(AL_FALSE); - self->mThread = std::thread(ALCcaptureOSS_recordProc, self); + self->mThread = std::thread{std::mem_fn(&ALCcaptureOSS::recordProc), self}; return ALC_TRUE; } catch(std::exception& e) { diff --git a/Alc/backends/portaudio.cpp b/Alc/backends/portaudio.cpp index 10c9079b..44ffd9bd 100644 --- a/Alc/backends/portaudio.cpp +++ b/Alc/backends/portaudio.cpp @@ -131,17 +131,20 @@ bool pa_load(void) struct ALCportPlayback final : public ALCbackend { + ALCportPlayback(ALCdevice *device) noexcept : ALCbackend{device} { } + ~ALCportPlayback() override; + + static int writeCallbackC(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData); + int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags); + PaStream *mStream{nullptr}; PaStreamParameters mParams{}; ALuint mUpdateSize{0u}; - - ALCportPlayback(ALCdevice *device) noexcept : ALCbackend{device} { } }; -int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData); - void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device); void ALCportPlayback_Destruct(ALCportPlayback *self); ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name); @@ -165,25 +168,32 @@ void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device) } void ALCportPlayback_Destruct(ALCportPlayback *self) +{ self->~ALCportPlayback(); } + +ALCportPlayback::~ALCportPlayback() { - PaError err = self->mStream ? Pa_CloseStream(self->mStream) : paNoError; + PaError err{mStream ? Pa_CloseStream(mStream) : paNoError}; if(err != paNoError) ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - self->mStream = nullptr; - - self->~ALCportPlayback(); + mStream = nullptr; } -int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), - const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) +int ALCportPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) { - auto self = static_cast<ALCportPlayback*>(userData); + return static_cast<ALCportPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); +} - ALCportPlayback_lock(self); - aluMixData(self->mDevice, outputBuffer, framesPerBuffer); - ALCportPlayback_unlock(self); +int ALCportPlayback::writeCallback(const void* UNUSED(inputBuffer), void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* UNUSED(timeInfo), + const PaStreamCallbackFlags UNUSED(statusFlags)) +{ + ALCportPlayback_lock(this); + aluMixData(mDevice, outputBuffer, framesPerBuffer); + ALCportPlayback_unlock(this); return 0; } @@ -236,7 +246,7 @@ ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name) retry_open: err = Pa_OpenStream(&self->mStream, nullptr, &self->mParams, device->Frequency, device->UpdateSize, paNoFlag, - ALCportPlayback_WriteCallback, self + &ALCportPlayback::writeCallbackC, self ); if(err != paNoError) { @@ -312,18 +322,21 @@ void ALCportPlayback_stop(ALCportPlayback *self) struct ALCportCapture final : public ALCbackend { + ALCportCapture(ALCdevice *device) noexcept : ALCbackend{device} { } + ~ALCportCapture() override; + + static int readCallbackC(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData); + int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags); + PaStream *mStream{nullptr}; PaStreamParameters mParams; RingBufferPtr mRing{nullptr}; - - ALCportCapture(ALCdevice *device) noexcept : ALCbackend{device} { } }; -int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData); - void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device); void ALCportCapture_Destruct(ALCportCapture *self); ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name); @@ -347,23 +360,30 @@ void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device) } void ALCportCapture_Destruct(ALCportCapture *self) +{ self->~ALCportCapture(); } + +ALCportCapture::~ALCportCapture() { - PaError err = self->mStream ? Pa_CloseStream(self->mStream) : paNoError; + PaError err{mStream ? Pa_CloseStream(mStream) : paNoError}; if(err != paNoError) ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - self->mStream = nullptr; - - self->~ALCportCapture(); + mStream = nullptr; } -int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer), +int ALCportCapture::readCallbackC(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void* userData) +{ + return static_cast<ALCportCapture*>(userData)->readCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); +} + +int ALCportCapture::readCallback(const void *inputBuffer, void *UNUSED(outputBuffer), unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), - const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) + const PaStreamCallbackFlags UNUSED(statusFlags)) { - auto self = static_cast<ALCportCapture*>(userData); - RingBuffer *ring{self->mRing.get()}; - ring->write(inputBuffer, framesPerBuffer); + mRing->write(inputBuffer, framesPerBuffer); return 0; } @@ -419,7 +439,7 @@ ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name) err = Pa_OpenStream(&self->mStream, &self->mParams, nullptr, device->Frequency, paFramesPerBufferUnspecified, paNoFlag, - ALCportCapture_ReadCallback, self + &ALCportCapture::readCallbackC, self ); if(err != paNoError) { diff --git a/Alc/backends/pulseaudio.cpp b/Alc/backends/pulseaudio.cpp index 6e5e07b8..3130df19 100644 --- a/Alc/backends/pulseaudio.cpp +++ b/Alc/backends/pulseaudio.cpp @@ -522,74 +522,132 @@ al::vector<DevMap> PlaybackDevices; al::vector<DevMap> CaptureDevices; -struct PulsePlayback final : public ALCbackend { - std::string mDeviceName; +pa_stream *pulse_connect_stream(const char *device_name, pa_threaded_mainloop *loop, + pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap, ALCbackend_Type type) +{ + pa_stream *stream{pa_stream_new_with_proplist(context, + (type==ALCbackend_Playback) ? "Playback Stream" : "Capture Stream", spec, chanmap, + prop_filter)}; + if(!stream) + { + ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); + return nullptr; + } - pa_buffer_attr mAttr; - pa_sample_spec mSpec; + pa_stream_set_state_callback(stream, stream_state_callback, loop); - pa_threaded_mainloop *mLoop{nullptr}; + int err{(type==ALCbackend_Playback) ? + pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) : + pa_stream_connect_record(stream, device_name, attr, flags)}; + if(err < 0) + { + ERR("Stream did not connect: %s\n", pa_strerror(err)); + pa_stream_unref(stream); + return nullptr; + } - pa_stream *mStream{nullptr}; - pa_context *mContext{nullptr}; + pa_stream_state_t state; + while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return nullptr; + } - ALuint mBufferSize{0u}; - ALuint mFrameSize{0u}; + pa_threaded_mainloop_wait(loop); + } + pa_stream_set_state_callback(stream, nullptr, nullptr); - PulsePlayback(ALCdevice *device) noexcept : ALCbackend{device} { } -}; + return stream; +} -void PulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); -void PulsePlayback_probeDevices(void); -void PulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata); -void PulsePlayback_contextStateCallback(pa_context *context, void *pdata); -void PulsePlayback_streamStateCallback(pa_stream *stream, void *pdata); -void PulsePlayback_streamWriteCallback(pa_stream *p, size_t nbytes, void *userdata); -void PulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); -void PulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); -void PulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata); -pa_stream *PulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop, - pa_context *context, pa_stream_flags_t flags, - pa_buffer_attr *attr, pa_sample_spec *spec, - pa_channel_map *chanmap); +void device_sink_callback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) +{ + auto loop = static_cast<pa_threaded_mainloop*>(pdata); -void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device); -void PulsePlayback_Destruct(PulsePlayback *self); -ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name); -ALCboolean PulsePlayback_reset(PulsePlayback *self); -ALCboolean PulsePlayback_start(PulsePlayback *self); -void PulsePlayback_stop(PulsePlayback *self); -DECLARE_FORWARD2(PulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) -DECLARE_FORWARD(PulsePlayback, ALCbackend, ALCuint, availableSamples) -ClockLatency PulsePlayback_getClockLatency(PulsePlayback *self); -void PulsePlayback_lock(PulsePlayback *self); -void PulsePlayback_unlock(PulsePlayback *self); -DECLARE_DEFAULT_ALLOCATORS(PulsePlayback) + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } -DEFINE_ALCBACKEND_VTABLE(PulsePlayback); + /* Skip this device is if it's already in the list. */ + if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + [info](const DevMap &entry) -> bool + { return entry.device_name == info->name; } + ) != PlaybackDevices.cend()) + return; + /* Make sure the display name (description) is unique. Append a number + * counter as needed. + */ + int count{1}; + std::string newname{info->description}; + while(checkName(PlaybackDevices, newname)) + { + newname = info->description; + newname += " #"; + newname += std::to_string(++count); + } + PlaybackDevices.emplace_back(std::move(newname), info->name); + DevMap &newentry = PlaybackDevices.back(); -void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device) -{ - new (self) PulsePlayback{device}; - SET_VTABLE2(PulsePlayback, ALCbackend, self); + TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); } -void PulsePlayback_Destruct(PulsePlayback *self) +void probePlaybackDevices(void) { - if(self->mLoop) + PlaybackDevices.clear(); + + pa_threaded_mainloop *loop{pa_threaded_mainloop_new()}; + if(loop && pa_threaded_mainloop_start(loop) >= 0) { - pulse_close(self->mLoop, self->mContext, self->mStream); - self->mLoop = nullptr; - self->mContext = nullptr; - self->mStream = nullptr; + unique_palock palock{loop}; + + pa_context *context{connect_context(loop, AL_FALSE)}; + if(context) + { + pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE}; + + pa_sample_spec spec; + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + pa_stream *stream{pulse_connect_stream(nullptr, loop, context, flags, nullptr, &spec, + nullptr, ALCbackend_Playback)}; + if(stream) + { + pa_operation *op{pa_context_get_sink_info_by_name(context, + pa_stream_get_device_name(stream), device_sink_callback, loop)}; + wait_for_operation(op, loop); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = nullptr; + } + + pa_operation *op{pa_context_get_sink_info_list(context, + device_sink_callback, loop)}; + wait_for_operation(op, loop); + + pa_context_disconnect(context); + pa_context_unref(context); + } + palock = unique_palock{}; + pa_threaded_mainloop_stop(loop); } - self->~PulsePlayback(); + if(loop) + pa_threaded_mainloop_free(loop); } -void PulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) +void device_source_callback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) { auto loop = static_cast<pa_threaded_mainloop*>(pdata); @@ -600,10 +658,10 @@ void PulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_inf } /* Skip this device is if it's already in the list. */ - if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), [info](const DevMap &entry) -> bool { return entry.device_name == info->name; } - ) != PlaybackDevices.cend()) + ) != CaptureDevices.cend()) return; /* Make sure the display name (description) is unique. Append a number @@ -611,21 +669,21 @@ void PulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_inf */ int count{1}; std::string newname{info->description}; - while(checkName(PlaybackDevices, newname)) + while(checkName(CaptureDevices, newname)) { newname = info->description; newname += " #"; newname += std::to_string(++count); } - PlaybackDevices.emplace_back(std::move(newname), info->name); - DevMap &newentry = PlaybackDevices.back(); + CaptureDevices.emplace_back(std::move(newname), info->name); + DevMap &newentry = CaptureDevices.back(); TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); } -void PulsePlayback_probeDevices(void) +void probeCaptureDevices(void) { - PlaybackDevices.clear(); + CaptureDevices.clear(); pa_threaded_mainloop *loop{pa_threaded_mainloop_new()}; if(loop && pa_threaded_mainloop_start(loop) >= 0) @@ -641,16 +699,14 @@ void PulsePlayback_probeDevices(void) pa_sample_spec spec; spec.format = PA_SAMPLE_S16NE; spec.rate = 44100; - spec.channels = 2; + spec.channels = 1; - pa_stream *stream{PulsePlayback_connectStream(nullptr, - loop, context, flags, nullptr, &spec, nullptr - )}; + pa_stream *stream{pulse_connect_stream(nullptr, loop, context, flags, nullptr, &spec, + nullptr, ALCbackend_Capture)}; if(stream) { - pa_operation *op{pa_context_get_sink_info_by_name(context, - pa_stream_get_device_name(stream), PulsePlayback_deviceCallback, loop - )}; + pa_operation *op{pa_context_get_source_info_by_name(context, + pa_stream_get_device_name(stream), device_source_callback, loop)}; wait_for_operation(op, loop); pa_stream_disconnect(stream); @@ -658,15 +714,14 @@ void PulsePlayback_probeDevices(void) stream = nullptr; } - pa_operation *op{pa_context_get_sink_info_list(context, - PulsePlayback_deviceCallback, loop - )}; + pa_operation *op{pa_context_get_source_info_list(context, + device_source_callback, loop)}; wait_for_operation(op, loop); pa_context_disconnect(context); pa_context_unref(context); } - palock = unique_palock{}; + palock.unlock(); pa_threaded_mainloop_stop(loop); } if(loop) @@ -674,6 +729,69 @@ void PulsePlayback_probeDevices(void) } +struct PulsePlayback final : public ALCbackend { + PulsePlayback(ALCdevice *device) noexcept : ALCbackend{device} { } + ~PulsePlayback() override; + + std::string mDeviceName; + + pa_buffer_attr mAttr; + pa_sample_spec mSpec; + + pa_threaded_mainloop *mLoop{nullptr}; + + pa_stream *mStream{nullptr}; + pa_context *mContext{nullptr}; + + ALuint mBufferSize{0u}; + ALuint mFrameSize{0u}; +}; + +void PulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata); +void PulsePlayback_contextStateCallback(pa_context *context, void *pdata); +void PulsePlayback_streamStateCallback(pa_stream *stream, void *pdata); +void PulsePlayback_streamWriteCallback(pa_stream *p, size_t nbytes, void *userdata); +void PulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +void PulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +void PulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata); + +void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device); +void PulsePlayback_Destruct(PulsePlayback *self); +ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name); +ALCboolean PulsePlayback_reset(PulsePlayback *self); +ALCboolean PulsePlayback_start(PulsePlayback *self); +void PulsePlayback_stop(PulsePlayback *self); +DECLARE_FORWARD2(PulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +DECLARE_FORWARD(PulsePlayback, ALCbackend, ALCuint, availableSamples) +ClockLatency PulsePlayback_getClockLatency(PulsePlayback *self); +void PulsePlayback_lock(PulsePlayback *self); +void PulsePlayback_unlock(PulsePlayback *self); +DECLARE_DEFAULT_ALLOCATORS(PulsePlayback) + +DEFINE_ALCBACKEND_VTABLE(PulsePlayback); + + +void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device) +{ + new (self) PulsePlayback{device}; + SET_VTABLE2(PulsePlayback, ALCbackend, self); +} + +void PulsePlayback_Destruct(PulsePlayback *self) +{ self->~PulsePlayback(); } + +PulsePlayback::~PulsePlayback() +{ + if(!mLoop) + return; + + pulse_close(mLoop, mContext, mStream); + mLoop = nullptr; + mContext = nullptr; + mStream = nullptr; +} + + void PulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata) { auto self = static_cast<PulsePlayback*>(pdata); @@ -826,53 +944,6 @@ void PulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata) } -pa_stream *PulsePlayback_connectStream(const char *device_name, - pa_threaded_mainloop *loop, pa_context *context, - pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, - pa_channel_map *chanmap) -{ - if(!device_name) - { - device_name = getenv("ALSOFT_PULSE_DEFAULT"); - if(device_name && !device_name[0]) - device_name = nullptr; - } - - pa_stream *stream{pa_stream_new_with_proplist(context, - "Playback Stream", spec, chanmap, prop_filter)}; - if(!stream) - { - ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); - return nullptr; - } - - pa_stream_set_state_callback(stream, stream_state_callback, loop); - - if(pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) < 0) - { - ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return nullptr; - } - - pa_stream_state_t state; - while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) - { - if(!PA_STREAM_IS_GOOD(state)) - { - ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return nullptr; - } - - pa_threaded_mainloop_wait(loop); - } - pa_stream_set_state_callback(stream, nullptr, nullptr); - - return stream; -} - - ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name) { const char *pulse_name{nullptr}; @@ -881,7 +952,7 @@ ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name) if(name) { if(PlaybackDevices.empty()) - PulsePlayback_probeDevices(); + probePlaybackDevices(); auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), [name](const DevMap &entry) -> bool @@ -909,8 +980,13 @@ ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name) spec.channels = 2; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - self->mStream = PulsePlayback_connectStream(pulse_name, self->mLoop, self->mContext, flags, - nullptr, &spec, nullptr); + if(!pulse_name) + { + pulse_name = getenv("ALSOFT_PULSE_DEFAULT"); + if(pulse_name && !pulse_name[0]) pulse_name = nullptr; + } + self->mStream = pulse_connect_stream(pulse_name, self->mLoop, self->mContext, flags, nullptr, + &spec, nullptr, ALCbackend_Playback); if(!self->mStream) { palock = unique_palock{}; @@ -1044,8 +1120,8 @@ ALCboolean PulsePlayback_reset(PulsePlayback *self) self->mAttr.minreq = period_size; self->mAttr.fragsize = -1; - self->mStream = PulsePlayback_connectStream(self->mDeviceName.c_str(), self->mLoop, - self->mContext, flags, &self->mAttr, &self->mSpec, &chanmap); + self->mStream = pulse_connect_stream(self->mDeviceName.c_str(), self->mLoop, self->mContext, + flags, &self->mAttr, &self->mSpec, &chanmap, ALCbackend_Playback); if(!self->mStream) return ALC_FALSE; pa_stream_set_state_callback(self->mStream, PulsePlayback_streamStateCallback, self); @@ -1167,6 +1243,9 @@ void PulsePlayback_unlock(PulsePlayback *self) struct PulseCapture final : public ALCbackend { + PulseCapture(ALCdevice *device) noexcept : ALCbackend{device} { } + ~PulseCapture() override; + std::string mDeviceName; const void *mCapStore{nullptr}; @@ -1182,21 +1261,12 @@ struct PulseCapture final : public ALCbackend { pa_stream *mStream{nullptr}; pa_context *mContext{nullptr}; - - PulseCapture(ALCdevice *device) noexcept : ALCbackend{device} { } }; -void PulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); -void PulseCapture_probeDevices(void); - void PulseCapture_contextStateCallback(pa_context *context, void *pdata); void PulseCapture_streamStateCallback(pa_stream *stream, void *pdata); void PulseCapture_sourceNameCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); void PulseCapture_streamMovedCallback(pa_stream *stream, void *pdata); -pa_stream *PulseCapture_connectStream(const char *device_name, - pa_threaded_mainloop *loop, pa_context *context, - pa_stream_flags_t flags, pa_buffer_attr *attr, - pa_sample_spec *spec, pa_channel_map *chanmap); void PulseCapture_Construct(PulseCapture *self, ALCdevice *device); void PulseCapture_Destruct(PulseCapture *self); @@ -1221,100 +1291,16 @@ void PulseCapture_Construct(PulseCapture *self, ALCdevice *device) } void PulseCapture_Destruct(PulseCapture *self) -{ - if(self->mLoop) - { - pulse_close(self->mLoop, self->mContext, self->mStream); - self->mLoop = nullptr; - self->mContext = nullptr; - self->mStream = nullptr; - } - self->~PulseCapture(); -} - +{ self->~PulseCapture(); } -void PulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) +PulseCapture::~PulseCapture() { - auto loop = static_cast<pa_threaded_mainloop*>(pdata); - - if(eol) - { - pa_threaded_mainloop_signal(loop, 0); + if(!mLoop) return; - } - - /* Skip this device is if it's already in the list. */ - if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [info](const DevMap &entry) -> bool - { return entry.device_name == info->name; } - ) != CaptureDevices.cend()) - return; - - /* Make sure the display name (description) is unique. Append a number - * counter as needed. - */ - int count{1}; - std::string newname{info->description}; - while(checkName(CaptureDevices, newname)) - { - newname = info->description; - newname += " #"; - newname += std::to_string(++count); - } - CaptureDevices.emplace_back(std::move(newname), info->name); - DevMap &newentry = CaptureDevices.back(); - - TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); -} - -void PulseCapture_probeDevices(void) -{ - CaptureDevices.clear(); - - pa_threaded_mainloop *loop{pa_threaded_mainloop_new()}; - if(loop && pa_threaded_mainloop_start(loop) >= 0) - { - unique_palock palock{loop}; - - pa_context *context{connect_context(loop, AL_FALSE)}; - if(context) - { - pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | - PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE}; - - pa_sample_spec spec; - spec.format = PA_SAMPLE_S16NE; - spec.rate = 44100; - spec.channels = 1; - - pa_stream *stream{PulseCapture_connectStream(nullptr, - loop, context, flags, nullptr, &spec, nullptr - )}; - if(stream) - { - pa_operation *op{pa_context_get_source_info_by_name(context, - pa_stream_get_device_name(stream), PulseCapture_deviceCallback, loop - )}; - wait_for_operation(op, loop); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = nullptr; - } - - pa_operation *op{pa_context_get_source_info_list(context, - PulseCapture_deviceCallback, loop - )}; - wait_for_operation(op, loop); - - pa_context_disconnect(context); - pa_context_unref(context); - } - palock.unlock(); - pa_threaded_mainloop_stop(loop); - } - if(loop) - pa_threaded_mainloop_free(loop); + pulse_close(mLoop, mContext, mStream); + mLoop = nullptr; + mContext = nullptr; + mStream = nullptr; } @@ -1366,47 +1352,6 @@ void PulseCapture_streamMovedCallback(pa_stream *stream, void *pdata) } -pa_stream *PulseCapture_connectStream(const char *device_name, - pa_threaded_mainloop *loop, pa_context *context, - pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, - pa_channel_map *chanmap) -{ - pa_stream *stream{pa_stream_new_with_proplist(context, - "Capture Stream", spec, chanmap, prop_filter - )}; - if(!stream) - { - ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); - return nullptr; - } - - pa_stream_set_state_callback(stream, stream_state_callback, loop); - - if(pa_stream_connect_record(stream, device_name, attr, flags) < 0) - { - ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return nullptr; - } - - pa_stream_state_t state; - while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) - { - if(!PA_STREAM_IS_GOOD(state)) - { - ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - return nullptr; - } - - pa_threaded_mainloop_wait(loop); - } - pa_stream_set_state_callback(stream, nullptr, nullptr); - - return stream; -} - - ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name) { ALCdevice *device{self->mDevice}; @@ -1415,7 +1360,7 @@ ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name) if(name) { if(CaptureDevices.empty()) - PulseCapture_probeDevices(); + probeCaptureDevices(); auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), [name](const DevMap &entry) -> bool @@ -1518,8 +1463,8 @@ ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name) flags |= PA_STREAM_DONT_MOVE; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - self->mStream = PulseCapture_connectStream(pulse_name, self->mLoop, self->mContext, flags, - &self->mAttr, &self->mSpec, &chanmap); + self->mStream = pulse_connect_stream(pulse_name, self->mLoop, self->mContext, flags, + &self->mAttr, &self->mSpec, &chanmap, ALCbackend_Capture); if(!self->mStream) return ALC_INVALID_VALUE; pa_stream_set_moved_callback(self->mStream, PulseCapture_streamMovedCallback, self); @@ -1739,12 +1684,12 @@ void PulseBackendFactory::probe(DevProbe type, std::string *outnames) switch(type) { case ALL_DEVICE_PROBE: - PulsePlayback_probeDevices(); + probePlaybackDevices(); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case CAPTURE_DEVICE_PROBE: - PulseCapture_probeDevices(); + probeCaptureDevices(); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } |