|
|
- /**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
- #include "config.h"
-
- #include "backends/dsound.h"
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <memory.h>
-
- #include <cguid.h>
- #include <mmreg.h>
- #ifndef _WAVEFORMATEXTENSIBLE_
- #include <ks.h>
- #include <ksmedia.h>
- #endif
-
- #include <atomic>
- #include <thread>
- #include <string>
- #include <vector>
- #include <algorithm>
- #include <functional>
-
- #include "alMain.h"
- #include "alu.h"
- #include "ringbuffer.h"
- #include "compat.h"
-
- /* MinGW-w64 needs this for some unknown reason now. */
- using LPCWAVEFORMATEX = const WAVEFORMATEX*;
- #include <dsound.h>
-
-
- #ifndef DSSPEAKER_5POINT1
- # define DSSPEAKER_5POINT1 0x00000006
- #endif
- #ifndef DSSPEAKER_5POINT1_BACK
- # define DSSPEAKER_5POINT1_BACK 0x00000006
- #endif
- #ifndef DSSPEAKER_7POINT1
- # define DSSPEAKER_7POINT1 0x00000007
- #endif
- #ifndef DSSPEAKER_7POINT1_SURROUND
- # define DSSPEAKER_7POINT1_SURROUND 0x00000008
- #endif
- #ifndef DSSPEAKER_5POINT1_SURROUND
- # define DSSPEAKER_5POINT1_SURROUND 0x00000009
- #endif
-
-
- /* Some headers seem to define these as macros for __uuidof, which is annoying
- * since some headers don't declare them at all. Hopefully the ifdef is enough
- * to tell if they need to be declared.
- */
- #ifndef KSDATAFORMAT_SUBTYPE_PCM
- DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
- #endif
- #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
- DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
- #endif
-
- namespace {
-
- #define DEVNAME_HEAD "OpenAL Soft on "
-
-
- #ifdef HAVE_DYNLOAD
- void *ds_handle;
- HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
- HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
- HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
- HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
-
- #ifndef IN_IDE_PARSER
- #define DirectSoundCreate pDirectSoundCreate
- #define DirectSoundEnumerateW pDirectSoundEnumerateW
- #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
- #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
- #endif
- #endif
-
-
- #define MAX_UPDATES 128
-
- struct DevMap {
- std::string name;
- GUID guid;
-
- template<typename T0, typename T1>
- DevMap(T0&& name_, T1&& guid_)
- : name{std::forward<T0>(name_)}, guid{std::forward<T1>(guid_)}
- { }
- };
-
- al::vector<DevMap> PlaybackDevices;
- al::vector<DevMap> CaptureDevices;
-
- 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();
- }
-
- BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
- {
- if(!guid)
- return TRUE;
-
- auto& devices = *static_cast<al::vector<DevMap>*>(data);
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(devices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- devices.emplace_back(std::move(newname), *guid);
- const DevMap &newentry = devices.back();
-
- OLECHAR *guidstr{nullptr};
- HRESULT hr{StringFromCLSID(*guid, &guidstr)};
- if(SUCCEEDED(hr))
- {
- TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr);
- CoTaskMemFree(guidstr);
- }
-
- return TRUE;
- }
-
-
- struct DSoundPlayback final : public BackendBase {
- DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- IDirectSound *mDS{nullptr};
- IDirectSoundBuffer *mPrimaryBuffer{nullptr};
- IDirectSoundBuffer *mBuffer{nullptr};
- IDirectSoundNotify *mNotifies{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundPlayback::"; }
- DEF_NEWDEL(DSoundPlayback)
- };
-
- 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;
- }
-
-
- FORCE_ALIGN int DSoundPlayback::mixerProc()
- {
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- DSBCAPS DSBCaps{};
- DSBCaps.dwSize = sizeof(DSBCaps);
- HRESULT err{mBuffer->GetCaps(&DSBCaps)};
- if(FAILED(err))
- {
- ERR("Failed to get buffer caps: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure retrieving playback buffer info: 0x%lx", err);
- return 1;
- }
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD FragSize{mDevice->UpdateSize * FrameSize};
-
- bool Playing{false};
- DWORD LastCursor{0u};
- mBuffer->GetCurrentPosition(&LastCursor, nullptr);
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- // Get current play cursor
- DWORD PlayCursor;
- mBuffer->GetCurrentPosition(&PlayCursor, nullptr);
- DWORD avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
-
- if(avail < FragSize)
- {
- if(!Playing)
- {
- err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
- if(FAILED(err))
- {
- ERR("Failed to play buffer: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure starting playback: 0x%lx", err);
- return 1;
- }
- Playing = true;
- }
-
- avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
- if(avail != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
- continue;
- }
- avail -= avail%FragSize;
-
- // Lock output buffer
- void *WritePtr1, *WritePtr2;
- DWORD WriteCnt1{0u}, WriteCnt2{0u};
- err = mBuffer->Lock(LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
-
- // If the buffer is lost, restore it and lock
- if(err == DSERR_BUFFERLOST)
- {
- WARN("Buffer lost, restoring...\n");
- err = mBuffer->Restore();
- if(SUCCEEDED(err))
- {
- Playing = false;
- LastCursor = 0;
- err = mBuffer->Lock(0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1,
- &WritePtr2, &WriteCnt2, 0);
- }
- }
-
- if(SUCCEEDED(err))
- {
- lock();
- aluMixData(mDevice, WritePtr1, WriteCnt1/FrameSize);
- if(WriteCnt2 > 0)
- aluMixData(mDevice, WritePtr2, WriteCnt2/FrameSize);
- unlock();
-
- mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
- }
- else
- {
- ERR("Buffer lock error: %#lx\n", err);
- aluHandleDisconnect(mDevice, "Failed to lock output buffer: 0x%lx", err);
- return 1;
- }
-
- // Update old write cursor location
- LastCursor += WriteCnt1+WriteCnt2;
- LastCursor %= DSBCaps.dwBufferBytes;
- }
-
- return 0;
- }
-
- ALCenum DSoundPlayback::open(const ALCchar *name)
- {
- HRESULT hr;
- if(PlaybackDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !PlaybackDevices.empty())
- {
- name = PlaybackDevices[0].name.c_str();
- guid = &PlaybackDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- hr = DS_OK;
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(!mNotifyEvent) hr = E_FAIL;
-
- //DirectSound Init code
- if(SUCCEEDED(hr))
- hr = DirectSoundCreate(guid, &mDS, nullptr);
- if(SUCCEEDED(hr))
- hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
- }
-
- ALCboolean DSoundPlayback::reset()
- {
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- break;
- case DevFmtFloat:
- if((mDevice->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
- break;
- /* fall-through */
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- break;
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- break;
- }
-
- WAVEFORMATEXTENSIBLE OutputType{};
- DWORD speakers;
- HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
- if(SUCCEEDED(hr))
- {
- speakers = DSSPEAKER_CONFIG(speakers);
- if(!(mDevice->Flags&DEVICE_CHANNELS_REQUEST))
- {
- if(speakers == DSSPEAKER_MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
- mDevice->FmtChans = DevFmtStereo;
- else if(speakers == DSSPEAKER_QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(speakers == DSSPEAKER_5POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX51;
- else if(speakers == DSSPEAKER_5POINT1_BACK)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX71;
- else
- ERR("Unknown system speaker config: 0x%lx\n", speakers);
- }
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- speakers == DSSPEAKER_HEADPHONE);
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- }
-
- retry_open:
- hr = S_OK;
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.nChannels = mDevice->channelsFromFmt();
- OutputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
- OutputType.Format.cbSize = 0;
- }
-
- if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- if(mDevice->FmtType == DevFmtFloat)
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- }
- else
- {
- if(SUCCEEDED(hr) && !mPrimaryBuffer)
- {
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
- }
- if(SUCCEEDED(hr))
- hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
- }
-
- if(SUCCEEDED(hr))
- {
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- if(num_updates > MAX_UPDATES)
- num_updates = MAX_UPDATES;
- mDevice->BufferSize = mDevice->UpdateSize * num_updates;
-
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 |
- DSBCAPS_GLOBALFOCUS;
- DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
- DSBDescription.lpwfxFormat = &OutputType.Format;
-
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
- if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
- {
- mDevice->FmtType = DevFmtShort;
- goto retry_open;
- }
- }
-
- if(SUCCEEDED(hr))
- {
- void *ptr;
- hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
- if(SUCCEEDED(hr))
- {
- auto Notifies = static_cast<IDirectSoundNotify*>(ptr);
- mNotifies = Notifies;
-
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- assert(num_updates <= MAX_UPDATES);
-
- std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
- for(ALuint i{0};i < num_updates;++i)
- {
- nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
- nots[i].hEventNotify = mNotifyEvent;
- }
- if(Notifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- {
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- return ALC_FALSE;
- }
-
- ResetEvent(mNotifyEvent);
- SetDefaultWFXChannelOrder(mDevice);
-
- return ALC_TRUE;
- }
-
- ALCboolean DSoundPlayback::start()
- {
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
- }
-
- void DSoundPlayback::stop()
- {
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- mBuffer->Stop();
- }
-
-
- struct DSoundCapture final : public BackendBase {
- DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundCapture() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- IDirectSoundCapture *mDSC{nullptr};
- IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
- DWORD mBufferBytes{0u};
- DWORD mCursor{0u};
-
- RingBufferPtr mRing;
-
- static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundCapture::"; }
- DEF_NEWDEL(DSoundCapture)
- };
-
- DSoundCapture::~DSoundCapture()
- {
- if(mDSCbuffer)
- {
- mDSCbuffer->Stop();
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- }
-
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
- }
-
-
- ALCenum DSoundCapture::open(const ALCchar *name)
- {
- HRESULT hr;
- if(CaptureDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !CaptureDevices.empty())
- {
- name = CaptureDevices[0].name.c_str();
- guid = &CaptureDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_ENUM;
-
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
- }
-
- WAVEFORMATEXTENSIBLE InputType{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtStereo:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtAmbi3D:
- WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
- return ALC_INVALID_ENUM;
- }
-
- InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- InputType.Format.nChannels = mDevice->channelsFromFmt();
- InputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
- InputType.Format.nSamplesPerSec = mDevice->Frequency;
- InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
- InputType.Format.cbSize = 0;
- InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
- if(mDevice->FmtType == DevFmtFloat)
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- }
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-
- DSCBUFFERDESC DSCBDescription{};
- DSCBDescription.dwSize = sizeof(DSCBDescription);
- DSCBDescription.dwFlags = 0;
- DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
- DSCBDescription.lpwfxFormat = &InputType.Format;
-
- //DirectSoundCapture Init code
- hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
- if(SUCCEEDED(hr))
- mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
- if(SUCCEEDED(hr))
- {
- mRing = CreateRingBuffer(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
- if(!mRing) hr = DSERR_OUTOFMEMORY;
- }
-
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
-
- mRing = nullptr;
- if(mDSCbuffer)
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mBufferBytes = DSCBDescription.dwBufferBytes;
- SetDefaultWFXChannelOrder(mDevice);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
- }
-
- ALCboolean DSoundCapture::start()
- {
- HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
- if(FAILED(hr))
- {
- ERR("start failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure starting capture: 0x%lx", hr);
- return ALC_FALSE;
- }
- return ALC_TRUE;
- }
-
- void DSoundCapture::stop()
- {
- HRESULT hr{mDSCbuffer->Stop()};
- if(FAILED(hr))
- {
- ERR("stop failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure stopping capture: 0x%lx", hr);
- }
- }
-
- ALCenum DSoundCapture::captureSamples(void *buffer, ALCuint samples)
- {
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
- }
-
- ALCuint DSoundCapture::availableSamples()
- {
- if(!mDevice->Connected.load(std::memory_order_acquire))
- return static_cast<ALCuint>(mRing->readSpace());
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD BufferBytes{mBufferBytes};
- DWORD LastCursor{mCursor};
-
- DWORD ReadCursor;
- void *ReadPtr1, *ReadPtr2;
- DWORD ReadCnt1, ReadCnt2;
- HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
- if(SUCCEEDED(hr))
- {
- DWORD NumBytes{(ReadCursor-LastCursor + BufferBytes) % BufferBytes};
- if(!NumBytes) return static_cast<ALCubyte>(mRing->readSpace());
- hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
- }
- if(SUCCEEDED(hr))
- {
- mRing->write(ReadPtr1, ReadCnt1/FrameSize);
- if(ReadPtr2 != nullptr && ReadCnt2 > 0)
- mRing->write(ReadPtr2, ReadCnt2/FrameSize);
- hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
- mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
- }
-
- if(FAILED(hr))
- {
- ERR("update failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure retrieving capture data: 0x%lx", hr);
- }
-
- return static_cast<ALCuint>(mRing->readSpace());
- }
-
- } // namespace
-
-
- BackendFactory &DSoundBackendFactory::getFactory()
- {
- static DSoundBackendFactory factory{};
- return factory;
- }
-
- bool DSoundBackendFactory::init()
- {
- #ifdef HAVE_DYNLOAD
- if(!ds_handle)
- {
- ds_handle = LoadLib("dsound.dll");
- if(!ds_handle)
- {
- ERR("Failed to load dsound.dll\n");
- return false;
- }
-
- #define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
- if(!p##f) \
- { \
- CloseLib(ds_handle); \
- ds_handle = nullptr; \
- return false; \
- } \
- } while(0)
- LOAD_FUNC(DirectSoundCreate);
- LOAD_FUNC(DirectSoundEnumerateW);
- LOAD_FUNC(DirectSoundCaptureCreate);
- LOAD_FUNC(DirectSoundCaptureEnumerateW);
- #undef LOAD_FUNC
- }
- #endif
- return true;
- }
-
- bool DSoundBackendFactory::querySupport(BackendType type)
- { return (type == BackendType::Playback || type == BackendType::Capture); }
-
- void DSoundBackendFactory::probe(DevProbe type, std::string *outnames)
- {
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
-
- /* Initialize COM to prevent name truncation */
- HRESULT hr;
- HRESULT hrcom{CoInitialize(nullptr)};
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices.clear();
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices.clear();
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
- {
- if(type == BackendType::Playback)
- return BackendPtr{new DSoundPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new DSoundCapture{device}};
- return nullptr;
- }
|