diff options
-rw-r--r-- | alc/backends/wasapi.cpp | 68 | ||||
-rw-r--r-- | alc/converter.cpp | 25 | ||||
-rw-r--r-- | alc/converter.h | 9 |
3 files changed, 74 insertions, 28 deletions
diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index 52cf9afd..d7b763c4 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -1556,28 +1556,54 @@ HRESULT WasapiCapture::resetProxy() if(wfx != nullptr) { TraceFormat("Got capture format", wfx); - if(!(wfx->nChannels == InputType.Format.nChannels || - (wfx->nChannels == 1 && InputType.Format.nChannels == 2) || - (wfx->nChannels == 2 && InputType.Format.nChannels == 1))) + if(!MakeExtensible(&InputType, wfx)) { - ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n", - DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mDevice->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample, - wfx->nSamplesPerSec); CoTaskMemFree(wfx); return E_FAIL; } + CoTaskMemFree(wfx); + wfx = nullptr; - if(!MakeExtensible(&InputType, wfx)) + auto validate_fmt = [](ALCdevice *device, uint32_t chancount, DWORD chanmask) noexcept + -> bool { - CoTaskMemFree(wfx); + switch(device->FmtChans) + { + /* If the device wants mono, we can handle any input. */ + case DevFmtMono: + return true; + /* If the device wants stereo, we can handle mono or stereo input. */ + case DevFmtStereo: + return (chancount == 2 && (chanmask == 0 || (chanmask&StereoMask) == STEREO)) + || (chancount == 1 && (chanmask&MonoMask) == MONO); + /* Otherwise, the device must match the input type. */ + case DevFmtQuad: + return (chancount == 4 && (chanmask == 0 || (chanmask&QuadMask) == QUAD)); + /* 5.1 (Side) and 5.1 (Rear) are interchangeable here. */ + case DevFmtX51: + case DevFmtX51Rear: + return (chancount == 6 && (chanmask == 0 || (chanmask&X51Mask) == X5DOT1 + || (chanmask&X51RearMask) == X5DOT1REAR)); + case DevFmtX61: + return (chancount == 7 && (chanmask == 0 || (chanmask&X61Mask) == X6DOT1)); + case DevFmtX71: + return (chancount == 8 && (chanmask == 0 || (chanmask&X71Mask) == X7DOT1)); + case DevFmtAmbi3D: return (chanmask == 0 && device->channelsFromFmt()); + } + return false; + }; + if(!validate_fmt(mDevice, InputType.Format.nChannels, InputType.dwChannelMask)) + { + ERR("Failed to match format, wanted: %s %s %uhz, got: 0x%08lx mask %d channel%s %d-bit %luhz\n", + DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), + mDevice->Frequency, InputType.dwChannelMask, InputType.Format.nChannels, + (InputType.Format.nChannels==1)?"":"s", InputType.Format.wBitsPerSample, + InputType.Format.nSamplesPerSec); return E_FAIL; } - CoTaskMemFree(wfx); - wfx = nullptr; } - DevFmtType srcType; + DevFmtType srcType{}; if(IsEqualGUID(InputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) { if(InputType.Format.wBitsPerSample == 8) @@ -1608,10 +1634,20 @@ HRESULT WasapiCapture::resetProxy() return E_FAIL; } - if(mDevice->FmtChans == DevFmtMono && InputType.Format.nChannels == 2) + if(mDevice->FmtChans == DevFmtMono && InputType.Format.nChannels != 1) { - mChannelConv = ChannelConverter{srcType, DevFmtStereo, mDevice->FmtChans}; - TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType)); + ALuint chanmask{(1u<<InputType.Format.nChannels) - 1u}; + /* Exclude LFE from the downmix. */ + if((InputType.dwChannelMask&SPEAKER_LOW_FREQUENCY)) + { + constexpr auto lfemask = MaskFromTopBits(SPEAKER_LOW_FREQUENCY); + const int lfeidx{POPCNT32(InputType.dwChannelMask&lfemask) - 1}; + chanmask &= ~(1u << lfeidx); + } + + mChannelConv = ChannelConverter{srcType, InputType.Format.nChannels, chanmask, + mDevice->FmtChans}; + TRACE("Created %s multichannel-to-mono converter\n", DevFmtTypeString(srcType)); /* The channel converter always outputs float, so change the input type * for the resampler/type-converter. */ @@ -1619,7 +1655,7 @@ HRESULT WasapiCapture::resetProxy() } else if(mDevice->FmtChans == DevFmtStereo && InputType.Format.nChannels == 1) { - mChannelConv = ChannelConverter{srcType, DevFmtMono, mDevice->FmtChans}; + mChannelConv = ChannelConverter{srcType, 1, 0x1, mDevice->FmtChans}; TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType)); srcType = DevFmtFloat; } diff --git a/alc/converter.cpp b/alc/converter.cpp index ead0c2f6..b07e8619 100644 --- a/alc/converter.cpp +++ b/alc/converter.cpp @@ -135,14 +135,24 @@ void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noex } template<DevFmtType T> -void Stereo2Mono(float *RESTRICT dst, const void *src, const size_t frames) noexcept +void Multi2Mono(ALuint chanmask, const size_t step, const float scale, float *RESTRICT dst, + const void *src, const size_t frames) noexcept { using SampleType = typename DevFmtTypeTraits<T>::Type; const SampleType *ssrc = static_cast<const SampleType*>(src); + std::fill_n(dst, frames, 0.0f); + for(size_t c{0};chanmask;++c) + { + if LIKELY((chanmask&1)) + { + for(size_t i{0u};i < frames;i++) + dst[i] += LoadSample<T>(ssrc[i*step + c]); + } + chanmask >>= 1; + } for(size_t i{0u};i < frames;i++) - dst[i] = (LoadSample<T>(ssrc[i*2 + 0])+LoadSample<T>(ssrc[i*2 + 1])) * - 0.707106781187f; + dst[i] *= scale; } } // namespace @@ -328,11 +338,12 @@ ALuint SampleConverter::convert(const void **src, ALuint *srcframes, void *dst, void ChannelConverter::convert(const void *src, float *dst, ALuint frames) const { - if(mSrcChans == DevFmtStereo && mDstChans == DevFmtMono) + if(mDstChans == DevFmtMono) { + const float scale{std::sqrt(1.0f / static_cast<float>(POPCNT32(mChanMask)))}; switch(mSrcType) { -#define HANDLE_FMT(T) case T: Stereo2Mono<T>(dst, src, frames); break +#define HANDLE_FMT(T) case T: Multi2Mono<T>(mChanMask, mSrcStep, scale, dst, src, frames); break HANDLE_FMT(DevFmtByte); HANDLE_FMT(DevFmtUByte); HANDLE_FMT(DevFmtShort); @@ -343,7 +354,7 @@ void ChannelConverter::convert(const void *src, float *dst, ALuint frames) const #undef HANDLE_FMT } } - else if(mSrcChans == DevFmtMono && mDstChans == DevFmtStereo) + else if(mChanMask == 0x1 && mDstChans == DevFmtStereo) { switch(mSrcType) { @@ -358,6 +369,4 @@ void ChannelConverter::convert(const void *src, float *dst, ALuint frames) const #undef HANDLE_FMT } } - else - LoadSamples(dst, src, 1u, mSrcType, frames * ChannelsFromDevFmt(mSrcChans, 0)); } diff --git a/alc/converter.h b/alc/converter.h index b3ceea9a..17da3aae 100644 --- a/alc/converter.h +++ b/alc/converter.h @@ -49,11 +49,12 @@ SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, struct ChannelConverter { - DevFmtType mSrcType; - DevFmtChannels mSrcChans; - DevFmtChannels mDstChans; + DevFmtType mSrcType{}; + ALuint mSrcStep{}; + ALuint mChanMask{}; + DevFmtChannels mDstChans{}; - bool is_active() const noexcept { return mSrcChans != mDstChans; } + bool is_active() const noexcept { return mChanMask != 0; } void convert(const void *src, float *dst, ALuint frames) const; }; |