aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alc/backends/wasapi.cpp68
-rw-r--r--alc/converter.cpp25
-rw-r--r--alc/converter.h9
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;
};