diff options
Diffstat (limited to 'alc')
-rw-r--r-- | alc/backends/alsa.cpp | 8 | ||||
-rw-r--r-- | alc/backends/coreaudio.cpp | 12 | ||||
-rw-r--r-- | alc/backends/dsound.cpp | 138 | ||||
-rw-r--r-- | alc/backends/jack.cpp | 31 | ||||
-rw-r--r-- | alc/backends/oboe.cpp | 3 | ||||
-rw-r--r-- | alc/backends/opensl.cpp | 3 | ||||
-rw-r--r-- | alc/backends/oss.cpp | 10 | ||||
-rw-r--r-- | alc/backends/portaudio.cpp | 35 | ||||
-rw-r--r-- | alc/backends/pulseaudio.cpp | 17 | ||||
-rw-r--r-- | alc/backends/sdl2.cpp | 41 | ||||
-rw-r--r-- | alc/backends/sndio.cpp | 8 | ||||
-rw-r--r-- | alc/backends/solaris.cpp | 8 | ||||
-rw-r--r-- | alc/backends/wasapi.cpp | 262 | ||||
-rw-r--r-- | alc/backends/wave.cpp | 9 | ||||
-rw-r--r-- | alc/backends/winmm.cpp | 32 |
15 files changed, 360 insertions, 257 deletions
diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index bdfdd040..6a7a1187 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -635,12 +635,16 @@ void AlsaPlayback::open(const char *name) name = alsaDevice; driver = GetConfigValue(nullptr, "alsa", "device", "default"); } - TRACE("Opening device \"%s\"\n", driver); - int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)}; + + snd_pcm_t *pcmHandle{}; + int err{snd_pcm_open(&pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)}; if(err < 0) throw al::backend_exception{al::backend_error::NoDevice, "Could not open ALSA device \"%s\"", driver}; + if(mPcmHandle) + snd_pcm_close(mPcmHandle); + mPcmHandle = pcmHandle; /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 8e38a777..977bdc78 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -118,10 +118,20 @@ void CoreAudioPlayback::open(const char *name) if(comp == nullptr) throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"}; - OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)}; + AudioUnit audioUnit{}; + OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::NoDevice, "Could not create component instance: %u", err}; + /* WARNING: I don't know if "valid" audio unit values are guaranteed to be + * non-0. If not, this logic is broken. + */ + if(mAudioUnit) + { + AudioUnitUninitialize(mAudioUnit); + AudioComponentInstanceDispose(mAudioUnit); + } + mAudioUnit = audioUnit; /* init and start the default audio unit... */ err = AudioUnitInitialize(mAudioUnit); diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index aa3a92a0..34fe25f4 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -107,6 +107,69 @@ HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallbac #endif +template<typename T> +class ComPtr { + T *mPtr{nullptr}; + +public: + ComPtr() noexcept = default; + ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } + ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } + ComPtr(std::nullptr_t) noexcept { } + explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } + ~ComPtr() { if(mPtr) mPtr->Release(); } + + ComPtr& operator=(const ComPtr &rhs) + { + if(!rhs.mPtr) + { + if(mPtr) + mPtr->Release(); + mPtr = nullptr; + } + else + { + rhs.mPtr->AddRef(); + try { + if(mPtr) + mPtr->Release(); + mPtr = rhs.mPtr; + } + catch(...) { + rhs.mPtr->Release(); + throw; + } + } + return *this; + } + ComPtr& operator=(ComPtr&& rhs) + { + if(mPtr) + mPtr->Release(); + mPtr = rhs.mPtr; + rhs.mPtr = nullptr; + return *this; + } + + operator bool() const noexcept { return mPtr != nullptr; } + + T& operator*() const noexcept { return *mPtr; } + T* operator->() const noexcept { return mPtr; } + T* get() const noexcept { return mPtr; } + T** getPtr() noexcept { return &mPtr; } + + T* release() noexcept + { + T *ret{mPtr}; + mPtr = nullptr; + return ret; + } + + void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); } + void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } +}; + + #define MONO SPEAKER_FRONT_CENTER #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) @@ -179,11 +242,11 @@ struct DSoundPlayback final : public BackendBase { void start() override; void stop() override; - IDirectSound *mDS{nullptr}; - IDirectSoundBuffer *mPrimaryBuffer{nullptr}; - IDirectSoundBuffer *mBuffer{nullptr}; - IDirectSoundNotify *mNotifies{nullptr}; - HANDLE mNotifyEvent{nullptr}; + ComPtr<IDirectSound> mDS; + ComPtr<IDirectSoundBuffer> mPrimaryBuffer; + ComPtr<IDirectSoundBuffer> mBuffer; + ComPtr<IDirectSoundNotify> mNotifies; + HANDLE mNotifyEvent{nullptr}; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -193,19 +256,11 @@ struct DSoundPlayback final : public BackendBase { DSoundPlayback::~DSoundPlayback() { - if(mNotifies) - mNotifies->Release(); mNotifies = nullptr; - if(mBuffer) - mBuffer->Release(); mBuffer = nullptr; - if(mPrimaryBuffer) - mPrimaryBuffer->Release(); mPrimaryBuffer = nullptr; - - if(mDS) - mDS->Release(); mDS = nullptr; + if(mNotifyEvent) CloseHandle(mNotifyEvent); mNotifyEvent = nullptr; @@ -234,8 +289,8 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() bool Playing{false}; DWORD LastCursor{0u}; mBuffer->GetCurrentPosition(&LastCursor, nullptr); - while(!mKillNow.load(std::memory_order_acquire) && - mDevice->Connected.load(std::memory_order_acquire)) + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) { // Get current play cursor DWORD PlayCursor; @@ -344,31 +399,34 @@ void DSoundPlayback::open(const char *name) } hr = DS_OK; - mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); - if(!mNotifyEvent) hr = E_FAIL; + if(!mNotifyEvent) + { + mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if(!mNotifyEvent) hr = E_FAIL; + } //DirectSound Init code + ComPtr<IDirectSound> ds; if(SUCCEEDED(hr)) - hr = DirectSoundCreate(guid, &mDS, nullptr); + hr = DirectSoundCreate(guid, ds.getPtr(), nullptr); if(SUCCEEDED(hr)) - hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY); + hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY); if(FAILED(hr)) throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", hr}; + mNotifies = nullptr; + mBuffer = nullptr; + mPrimaryBuffer = nullptr; + mDS = std::move(ds); + mDevice->DeviceName = name; } bool DSoundPlayback::reset() { - if(mNotifies) - mNotifies->Release(); mNotifies = nullptr; - if(mBuffer) - mBuffer->Release(); mBuffer = nullptr; - if(mPrimaryBuffer) - mPrimaryBuffer->Release(); mPrimaryBuffer = nullptr; switch(mDevice->FmtType) @@ -465,7 +523,7 @@ retry_open: DSBUFFERDESC DSBDescription{}; DSBDescription.dwSize = sizeof(DSBDescription); DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr); + hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr); } if(SUCCEEDED(hr)) hr = mPrimaryBuffer->SetFormat(&OutputType.Format); @@ -485,7 +543,7 @@ retry_open: DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign; DSBDescription.lpwfxFormat = &OutputType.Format; - hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr); + hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr); if(FAILED(hr) && mDevice->FmtType == DevFmtFloat) { mDevice->FmtType = DevFmtShort; @@ -499,7 +557,7 @@ retry_open: hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr); if(SUCCEEDED(hr)) { - mNotifies = static_cast<IDirectSoundNotify*>(ptr); + mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)}; uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; assert(num_updates <= MAX_UPDATES); @@ -517,14 +575,8 @@ retry_open: if(FAILED(hr)) { - if(mNotifies) - mNotifies->Release(); mNotifies = nullptr; - if(mBuffer) - mBuffer->Release(); mBuffer = nullptr; - if(mPrimaryBuffer) - mPrimaryBuffer->Release(); mPrimaryBuffer = nullptr; return false; } @@ -567,8 +619,8 @@ struct DSoundCapture final : public BackendBase { void captureSamples(al::byte *buffer, uint samples) override; uint availableSamples() override; - IDirectSoundCapture *mDSC{nullptr}; - IDirectSoundCaptureBuffer *mDSCbuffer{nullptr}; + ComPtr<IDirectSoundCapture> mDSC; + ComPtr<IDirectSoundCaptureBuffer> mDSCbuffer; DWORD mBufferBytes{0u}; DWORD mCursor{0u}; @@ -582,12 +634,8 @@ DSoundCapture::~DSoundCapture() if(mDSCbuffer) { mDSCbuffer->Stop(); - mDSCbuffer->Release(); mDSCbuffer = nullptr; } - - if(mDSC) - mDSC->Release(); mDSC = nullptr; } @@ -693,20 +741,16 @@ void DSoundCapture::open(const char *name) DSCBDescription.lpwfxFormat = &InputType.Format; //DirectSoundCapture Init code - hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr); + hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr); if(SUCCEEDED(hr)) - mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr); + mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr); if(SUCCEEDED(hr)) mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false); if(FAILED(hr)) { mRing = nullptr; - if(mDSCbuffer) - mDSCbuffer->Release(); mDSCbuffer = nullptr; - if(mDSC) - mDSC->Release(); mDSC = nullptr; throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index 42509d77..cf52e7a4 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -384,22 +384,23 @@ int JackPlayback::mixerProc() void JackPlayback::open(const char *name) { - mPortPattern.clear(); - - const PathNamePair &binname = GetProcBinary(); - const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - - jack_status_t status; - mClient = jack_client_open(client_name, ClientOptions, &status, nullptr); - if(mClient == nullptr) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to open client connection: 0x%02x", status}; - if((status&JackServerStarted)) - TRACE("JACK server started\n"); - if((status&JackNameNotUnique)) + if(!mClient) { - client_name = jack_get_client_name(mClient); - TRACE("Client name not unique, got '%s' instead\n", client_name); + const PathNamePair &binname = GetProcBinary(); + const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; + + jack_status_t status; + mClient = jack_client_open(client_name, ClientOptions, &status, nullptr); + if(mClient == nullptr) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to open client connection: 0x%02x", status}; + if((status&JackServerStarted)) + TRACE("JACK server started\n"); + if((status&JackNameNotUnique)) + { + client_name = jack_get_client_name(mClient); + TRACE("Client name not unique, got '%s' instead\n", client_name); + } } if(PlaybackList.empty()) diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp index d84cea8e..b94a0882 100644 --- a/alc/backends/oboe.cpp +++ b/alc/backends/oboe.cpp @@ -64,9 +64,10 @@ void OboePlayback::open(const char *name) name}; /* Open a basic output stream, just to ensure it can work. */ + oboe::ManagedStream stream; oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output) ->setPerformanceMode(oboe::PerformanceMode::LowLatency) - ->openManagedStream(mStream)}; + ->openManagedStream(stream)}; if(result != oboe::Result::OK) throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", oboe::convertToText(result)}; diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index 917e097f..cae17ce1 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -316,6 +316,9 @@ void OpenSLPlayback::open(const char *name) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", name}; + /* There's only one device, so if it's already open, there's nothing to do. */ + if(mEngineObj) return; + // create engine SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)}; PRINTERR(result, "slCreateEngine"); diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 4cff5959..196744b0 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -249,7 +249,7 @@ struct OSSPlayback final : public BackendBase { OSSPlayback::~OSSPlayback() { if(mFd != -1) - close(mFd); + ::close(mFd); mFd = -1; } @@ -328,11 +328,15 @@ void OSSPlayback::open(const char *name) devname = iter->device_name.c_str(); } - mFd = ::open(devname, O_WRONLY); - if(mFd == -1) + int fd{::open(devname, O_WRONLY)}; + if(fd == -1) throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname, strerror(errno)}; + if(mFd != -1) + ::close(mFd); + mFd = fd; + mDevice->DeviceName = name; } diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index d2e6a5a4..188f72f2 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -123,53 +123,58 @@ void PortPlayback::open(const char *name) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", name}; - mUpdateSize = mDevice->UpdateSize; - + PaStreamParameters params{}; auto devidopt = ConfigValueInt(nullptr, "port", "device"); - if(devidopt && *devidopt >= 0) mParams.device = *devidopt; - else mParams.device = Pa_GetDefaultOutputDevice(); - mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency); - mParams.hostApiSpecificStreamInfo = nullptr; + if(devidopt && *devidopt >= 0) params.device = *devidopt; + else params.device = Pa_GetDefaultOutputDevice(); + params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency); + params.hostApiSpecificStreamInfo = nullptr; - mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); + params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); switch(mDevice->FmtType) { case DevFmtByte: - mParams.sampleFormat = paInt8; + params.sampleFormat = paInt8; break; case DevFmtUByte: - mParams.sampleFormat = paUInt8; + params.sampleFormat = paUInt8; break; case DevFmtUShort: /* fall-through */ case DevFmtShort: - mParams.sampleFormat = paInt16; + params.sampleFormat = paInt16; break; case DevFmtUInt: /* fall-through */ case DevFmtInt: - mParams.sampleFormat = paInt32; + params.sampleFormat = paInt32; break; case DevFmtFloat: - mParams.sampleFormat = paFloat32; + params.sampleFormat = paFloat32; break; } retry_open: - PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize, + PaStream *stream{}; + PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize, paNoFlag, &PortPlayback::writeCallbackC, this)}; if(err != paNoError) { - if(mParams.sampleFormat == paFloat32) + if(params.sampleFormat == paFloat32) { - mParams.sampleFormat = paInt16; + params.sampleFormat = paInt16; goto retry_open; } throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", Pa_GetErrorText(err)}; } + Pa_CloseStream(mStream); + mStream = stream; + mParams = params; + mUpdateSize = mDevice->UpdateSize; + mDevice->DeviceName = name; } diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index a6aa93a5..90f56968 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -846,7 +846,8 @@ void PulsePlayback::open(const char *name) } auto plock = mMainloop.getUniqueLock(); - mContext = mMainloop.connectContext(plock); + if(!mContext) + mContext = mMainloop.connectContext(plock); pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS}; @@ -864,8 +865,18 @@ void PulsePlayback::open(const char *name) if(defname) pulse_name = defname->c_str(); } TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - mStream = mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr, - BackendType::Playback); + pa_stream *stream{mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec, + nullptr, BackendType::Playback)}; + if(mStream) + { + pa_stream_set_state_callback(mStream, nullptr, nullptr); + pa_stream_set_moved_callback(mStream, nullptr, nullptr); + pa_stream_set_write_callback(mStream, nullptr, nullptr); + pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr); + pa_stream_disconnect(mStream); + pa_stream_unref(mStream); + } + mStream = stream; pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this); mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream))); diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index 084b51c0..4c4c71ed 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -106,32 +106,34 @@ void Sdl2Backend::open(const char *name) /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't * necessarily the first in the list. */ + SDL_AudioDeviceID devid; if(!name || strcmp(name, defaultDeviceName) == 0) - mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, - SDL_AUDIO_ALLOW_ANY_CHANGE); + devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); else { const size_t prefix_len = strlen(DEVNAME_PREFIX); if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0) - mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have, + devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); else - mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, - SDL_AUDIO_ALLOW_ANY_CHANGE); + devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); } - if(mDeviceID == 0) + if(!devid) throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()}; - mDevice->Frequency = static_cast<uint>(have.freq); - + DevFmtChannels devchans{}; if(have.channels == 1) - mDevice->FmtChans = DevFmtMono; + devchans = DevFmtMono; else if(have.channels == 2) - mDevice->FmtChans = DevFmtStereo; + devchans = DevFmtStereo; else + { + SDL_CloseAudioDevice(devid); throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL channel count: %d", int{have.channels}}; + } + DevFmtType devtype{}; switch(have.format) { case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break; @@ -141,17 +143,20 @@ void Sdl2Backend::open(const char *name) case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break; case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break; default: + SDL_CloseAudioDevice(devid); throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x", have.format}; } - mDevice->UpdateSize = have.samples; - mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */ - mFrameSize = mDevice->frameSizeFromFmt(); - mFrequency = mDevice->Frequency; - mFmtChans = mDevice->FmtChans; - mFmtType = mDevice->FmtType; - mUpdateSize = mDevice->UpdateSize; + if(mDeviceID) + SDL_CloseAudioDevice(mDeviceID); + mDeviceID = devid; + + mFrameSize = FrameSizeFromDevFmt(devchans, devtype, 0); + mFrequency = static_cast<uint>(have.freq); + mFmtChans = devchans; + mFmtType = devtype; + mUpdateSize = have.samples; mDevice->DeviceName = name ? name : defaultDeviceName; } @@ -162,7 +167,7 @@ bool Sdl2Backend::reset() mDevice->FmtChans = mFmtChans; mDevice->FmtType = mFmtType; mDevice->UpdateSize = mUpdateSize; - mDevice->BufferSize = mUpdateSize * 2; + mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */ setDefaultWFXChannelOrder(); return true; } diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index 41bdb73b..a26eeaa6 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -122,10 +122,14 @@ void SndioPlayback::open(const char *name) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", name}; - mSndHandle = sio_open(nullptr, SIO_PLAY, 0); - if(mSndHandle == nullptr) + sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)}; + if(!sndHandle) throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"}; + if(mSndHandle) + sio_close(mSndHandle); + mSndHandle = sndHandle; + mDevice->DeviceName = name; } diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index a5bb45b0..68ab814c 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -148,11 +148,15 @@ void SolarisBackend::open(const char *name) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", name}; - mFd = ::open(solaris_driver.c_str(), O_WRONLY); - if(mFd == -1) + int fd{::open(solaris_driver.c_str(), O_WRONLY)}; + if(fd == -1) throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", solaris_driver.c_str(), strerror(errno)}; + if(mFd != -1) + ::close(mFd); + mFd = fd; + mDevice->DeviceName = name; } diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 0786a7d7..b594aebe 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -238,10 +238,9 @@ struct DevMap { bool checkName(const al::vector<DevMap> &list, const std::string &name) { - return std::find_if(list.cbegin(), list.cend(), - [&name](const DevMap &entry) -> bool - { return entry.name == name; } - ) != list.cend(); + auto match_name = [&name](const DevMap &entry) -> bool + { return entry.name == name; }; + return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend(); } al::vector<DevMap> PlaybackDevices; @@ -297,26 +296,26 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device) return std::make_pair(std::move(name), std::move(guid)); } -void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor) +EndpointFormFactor get_device_formfactor(IMMDevice *device) { ComPtr<IPropertyStore> ps; - HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr()); + HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())}; if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return; + return UnknownFormFactor; } + EndpointFormFactor formfactor{UnknownFormFactor}; PropVariant pvform; - hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get()); + hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get()); if(FAILED(hr)) WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr); else if(pvform->vt == VT_UI4) - *formfactor = static_cast<EndpointFormFactor>(pvform->ulVal); - else if(pvform->vt == VT_EMPTY) - *formfactor = UnknownFormFactor; - else + formfactor = static_cast<EndpointFormFactor>(pvform->ulVal); + else if(pvform->vt != VT_EMPTY) WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); + return formfactor; } @@ -484,6 +483,7 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format) enum class MsgType { OpenDevice, + ReopenDevice, ResetDevice, StartDevice, StopDevice, @@ -497,6 +497,7 @@ enum class MsgType { constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{ "Open Device", + "Reopen Device", "Reset Device", "Start Device", "Stop Device", @@ -511,7 +512,7 @@ constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{ struct WasapiProxy { virtual ~WasapiProxy() = default; - virtual HRESULT openProxy() = 0; + virtual HRESULT openProxy(const char *name) = 0; virtual void closeProxy() = 0; virtual HRESULT resetProxy() = 0; @@ -521,19 +522,20 @@ struct WasapiProxy { struct Msg { MsgType mType; WasapiProxy *mProxy; + const char *mParam; std::promise<HRESULT> mPromise; }; static std::deque<Msg> mMsgQueue; static std::mutex mMsgQueueLock; static std::condition_variable mMsgQueueCond; - std::future<HRESULT> pushMessage(MsgType type) + std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr) { std::promise<HRESULT> promise; std::future<HRESULT> future{promise.get_future()}; { std::lock_guard<std::mutex> _{mMsgQueueLock}; - mMsgQueue.emplace_back(Msg{type, this, std::move(promise)}); + mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)}); } mMsgQueueCond.notify_one(); return future; @@ -545,7 +547,7 @@ struct WasapiProxy { std::future<HRESULT> future{promise.get_future()}; { std::lock_guard<std::mutex> _{mMsgQueueLock}; - mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)}); + mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)}); } mMsgQueueCond.notify_one(); return future; @@ -600,9 +602,9 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) Msg msg; while(popMessage(msg)) { - TRACE("Got message \"%s\" (0x%04x, this=%p)\n", + TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n", MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType), - decltype(std::declval<void*>()){msg.mProxy}); + static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam)); switch(msg.mType) { @@ -611,7 +613,7 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) if(++deviceCount == 1) hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if(SUCCEEDED(hr)) - hr = msg.mProxy->openProxy(); + hr = msg.mProxy->openProxy(msg.mParam); msg.mPromise.set_value(hr); if(FAILED(hr)) @@ -621,6 +623,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) } continue; + case MsgType::ReopenDevice: + hr = msg.mProxy->openProxy(msg.mParam); + msg.mPromise.set_value(hr); + continue; + case MsgType::ResetDevice: hr = msg.mProxy->resetProxy(); msg.mPromise.set_value(hr); @@ -688,7 +695,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { int mixerProc(); void open(const char *name) override; - HRESULT openProxy() override; + HRESULT openProxy(const char *name) override; void closeProxy() override; bool reset() override; @@ -700,8 +707,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { ClockLatency getClockLatency() override; - std::wstring mDevId; - HRESULT mOpenStatus{E_FAIL}; ComPtr<IMMDevice> mMMDev{nullptr}; ComPtr<IAudioClient> mClient{nullptr}; @@ -796,83 +801,86 @@ void WasapiPlayback::open(const char *name) { HRESULT hr{S_OK}; - mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); - if(mNotifyEvent == nullptr) + if(!mNotifyEvent) { - ERR("Failed to create notify events: %lu\n", GetLastError()); - hr = E_FAIL; - } - - if(SUCCEEDED(hr)) - { - if(name) + mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if(mNotifyEvent == nullptr) { - if(PlaybackDevices.empty()) - pushMessage(MsgType::EnumeratePlayback).wait(); - + ERR("Failed to create notify events: %lu\n", GetLastError()); hr = E_FAIL; - auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), - [name](const DevMap &entry) -> bool - { return entry.name == name || entry.endpoint_guid == name; }); - if(iter == PlaybackDevices.cend()) - { - const std::wstring wname{utf8_to_wstr(name)}; - iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), - [&wname](const DevMap &entry) -> bool - { return entry.devid == wname; }); - } - if(iter == PlaybackDevices.cend()) - WARN("Failed to find device name matching \"%s\"\n", name); - else - { - mDevId = iter->devid; - mDevice->DeviceName = iter->name; - hr = S_OK; - } } } if(SUCCEEDED(hr)) - hr = pushMessage(MsgType::OpenDevice).get(); - mOpenStatus = hr; - - if(FAILED(hr)) { - if(mNotifyEvent != nullptr) - CloseHandle(mNotifyEvent); - mNotifyEvent = nullptr; + if(name && PlaybackDevices.empty()) + pushMessage(MsgType::EnumeratePlayback).wait(); - mDevId.clear(); + if(SUCCEEDED(mOpenStatus)) + hr = pushMessage(MsgType::ReopenDevice, name).get(); + else + { + hr = pushMessage(MsgType::OpenDevice, name).get(); + mOpenStatus = hr; + } + } + if(FAILED(hr)) throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", hr}; - } } -HRESULT WasapiPlayback::openProxy() +HRESULT WasapiPlayback::openProxy(const char *name) { + const wchar_t *devid{nullptr}; + if(name) + { + auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + [name](const DevMap &entry) -> bool + { return entry.name == name || entry.endpoint_guid == name; }); + if(iter == PlaybackDevices.cend()) + { + const std::wstring wname{utf8_to_wstr(name)}; + iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + [&wname](const DevMap &entry) -> bool + { return entry.devid == wname; }); + } + if(iter == PlaybackDevices.cend()) + { + WARN("Failed to find device name matching \"%s\"\n", name); + return E_FAIL; + } + name = iter->name.c_str(); + devid = iter->devid.c_str(); + } + void *ptr; HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr)}; - if(SUCCEEDED(hr)) + ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; + ComPtr<IMMDevice> mmdev; + if(!devid) + hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr()); + else + hr = enumerator->GetDevice(devid, mmdev.getPtr()); + enumerator = nullptr; + if(FAILED(hr)) { - ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; - if(mDevId.empty()) - hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mMMDev.getPtr()); - else - hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr()); + WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + return hr; } - if(SUCCEEDED(hr)) - hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr); - if(SUCCEEDED(hr)) + + hr = mmdev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr); + if(FAILED(hr)) { - mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; - if(mDevice->DeviceName.empty()) - mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first; + WARN("Failed to activate device \"%s\"\n", name?name:"(default)"); + return hr; } - if(FAILED(hr)) - mMMDev = nullptr; + mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; + mMMDev = std::move(mmdev); + if(name) mDevice->DeviceName = name; + else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first; return hr; } @@ -1105,8 +1113,7 @@ HRESULT WasapiPlayback::resetProxy() } mFrameStep = OutputType.Format.nChannels; - EndpointFormFactor formfactor{UnknownFormFactor}; - get_device_formfactor(mMMDev.get(), &formfactor); + const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())}; mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo && (formfactor == Headphones || formfactor == Headset)); @@ -1226,7 +1233,7 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { int recordProc(); void open(const char *name) override; - HRESULT openProxy() override; + HRESULT openProxy(const char *name) override; void closeProxy() override; HRESULT resetProxy() override; @@ -1238,8 +1245,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { void captureSamples(al::byte *buffer, uint samples) override; uint availableSamples() override; - std::wstring mDevId; - HRESULT mOpenStatus{E_FAIL}; ComPtr<IMMDevice> mMMDev{nullptr}; ComPtr<IAudioClient> mClient{nullptr}; @@ -1373,48 +1378,15 @@ void WasapiCapture::open(const char *name) if(SUCCEEDED(hr)) { - if(name) - { - if(CaptureDevices.empty()) - pushMessage(MsgType::EnumerateCapture).wait(); - - hr = E_FAIL; - auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [name](const DevMap &entry) -> bool - { return entry.name == name || entry.endpoint_guid == name; }); - if(iter == CaptureDevices.cend()) - { - const std::wstring wname{utf8_to_wstr(name)}; - iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [&wname](const DevMap &entry) -> bool - { return entry.devid == wname; }); - } - if(iter == CaptureDevices.cend()) - WARN("Failed to find device name matching \"%s\"\n", name); - else - { - mDevId = iter->devid; - mDevice->DeviceName = iter->name; - hr = S_OK; - } - } + if(name && CaptureDevices.empty()) + pushMessage(MsgType::EnumerateCapture).wait(); + hr = pushMessage(MsgType::OpenDevice, name).get(); } - - if(SUCCEEDED(hr)) - hr = pushMessage(MsgType::OpenDevice).get(); mOpenStatus = hr; if(FAILED(hr)) - { - if(mNotifyEvent != nullptr) - CloseHandle(mNotifyEvent); - mNotifyEvent = nullptr; - - mDevId.clear(); - throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", hr}; - } hr = pushMessage(MsgType::ResetDevice).get(); if(FAILED(hr)) @@ -1425,30 +1397,56 @@ void WasapiCapture::open(const char *name) } } -HRESULT WasapiCapture::openProxy() +HRESULT WasapiCapture::openProxy(const char *name) { + const wchar_t *devid{nullptr}; + if(name) + { + auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), + [name](const DevMap &entry) -> bool + { return entry.name == name || entry.endpoint_guid == name; }); + if(iter == CaptureDevices.cend()) + { + const std::wstring wname{utf8_to_wstr(name)}; + iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), + [&wname](const DevMap &entry) -> bool + { return entry.devid == wname; }); + } + if(iter == CaptureDevices.cend()) + { + WARN("Failed to find device name matching \"%s\"\n", name); + return E_FAIL; + } + name = iter->name.c_str(); + devid = iter->devid.c_str(); + } + void *ptr; HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr)}; - if(SUCCEEDED(hr)) - { - ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; - if(mDevId.empty()) - hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr()); - else - hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr()); - } - if(SUCCEEDED(hr)) - hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr); - if(SUCCEEDED(hr)) + ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; + if(!devid) + hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr()); + else + hr = enumerator->GetDevice(devid, mMMDev.getPtr()); + enumerator = nullptr; + if(FAILED(hr)) { - mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; - if(mDevice->DeviceName.empty()) - mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first; + WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + return hr; } + hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr); if(FAILED(hr)) + { + WARN("Failed to activate device \"%s\"\n", name?name:"(default)"); mMMDev = nullptr; + return hr; + } + + mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; + if(name) mDevice->DeviceName = name; + else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first; return hr; } diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 4f738230..e80fb3ae 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -132,8 +132,8 @@ int WaveBackend::mixerProc() int64_t done{0}; auto start = std::chrono::steady_clock::now(); - while(!mKillNow.load(std::memory_order_acquire) && - mDevice->Connected.load(std::memory_order_acquire)) + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) { auto now = std::chrono::steady_clock::now(); @@ -214,9 +214,12 @@ void WaveBackend::open(const char *name) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", name}; + /* There's only one "device", so if it's already open, we're done. */ + if(mFile) return; + #ifdef _WIN32 { - std::wstring wname = utf8_to_wstr(fname); + std::wstring wname{utf8_to_wstr(fname)}; mFile = _wfopen(wname.c_str(), L"wb"); } #else diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index 9b88f12e..14fda45b 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -224,27 +224,28 @@ void WinMMPlayback::open(const char *name) auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter)); retry_open: - mFormat = WAVEFORMATEX{}; + WAVEFORMATEX format{}; if(mDevice->FmtType == DevFmtFloat) { - mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - mFormat.wBitsPerSample = 32; + format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + format.wBitsPerSample = 32; } else { - mFormat.wFormatTag = WAVE_FORMAT_PCM; + format.wFormatTag = WAVE_FORMAT_PCM; if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte) - mFormat.wBitsPerSample = 8; + format.wBitsPerSample = 8; else - mFormat.wBitsPerSample = 16; + format.wBitsPerSample = 16; } - mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); - mFormat.nBlockAlign = static_cast<WORD>(mFormat.wBitsPerSample * mFormat.nChannels / 8); - mFormat.nSamplesPerSec = mDevice->Frequency; - mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign; - mFormat.cbSize = 0; - - MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, + format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); + format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8); + format.nSamplesPerSec = mDevice->Frequency; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + HWAVEOUT outHandle{}; + MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format, reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC), reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)}; if(res != MMSYSERR_NOERROR) @@ -257,6 +258,11 @@ retry_open: throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res}; } + if(mOutHdl) + waveOutClose(mOutHdl); + mOutHdl = outHandle; + mFormat = format; + mDevice->DeviceName = PlaybackDevices[DeviceID]; } |