aboutsummaryrefslogtreecommitdiffstats
path: root/alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2019-12-19 04:38:34 -0800
committerChris Robinson <[email protected]>2019-12-19 04:38:34 -0800
commit4634002104a753714451e3701524a258ae2d0add (patch)
tree9062fcf052523c305c99a2daa959bd8a422950b6 /alc
parent6480c2c854b67979e07c5b6113169733760395ec (diff)
Remix missing channels with direct channels enabled
Instead of dropping them.
Diffstat (limited to 'alc')
-rw-r--r--alc/alc.cpp64
-rw-r--r--alc/alcmain.h9
-rw-r--r--alc/alu.cpp16
3 files changed, 83 insertions, 6 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp
index 06f8ce59..9c4dbcbd 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -1308,8 +1308,10 @@ ALuint ChannelsFromDevFmt(DevFmtChannels chans, ALuint ambiorder) noexcept
return 0;
}
+namespace {
+
struct DevFmtPair { DevFmtChannels chans; DevFmtType type; };
-static al::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
+al::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
{
static const struct {
ALenum format;
@@ -1350,7 +1352,7 @@ static al::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
return al::nullopt;
}
-static ALCboolean IsValidALCType(ALCenum type)
+ALCboolean IsValidALCType(ALCenum type)
{
switch(type)
{
@@ -1366,7 +1368,7 @@ static ALCboolean IsValidALCType(ALCenum type)
return ALC_FALSE;
}
-static ALCboolean IsValidALCChannels(ALCenum channels)
+ALCboolean IsValidALCChannels(ALCenum channels)
{
switch(channels)
{
@@ -1382,7 +1384,7 @@ static ALCboolean IsValidALCChannels(ALCenum channels)
return ALC_FALSE;
}
-static ALCboolean IsValidAmbiLayout(ALCenum layout)
+ALCboolean IsValidAmbiLayout(ALCenum layout)
{
switch(layout)
{
@@ -1393,7 +1395,7 @@ static ALCboolean IsValidAmbiLayout(ALCenum layout)
return ALC_FALSE;
}
-static ALCboolean IsValidAmbiScaling(ALCenum scaling)
+ALCboolean IsValidAmbiScaling(ALCenum scaling)
{
switch(scaling)
{
@@ -1405,6 +1407,45 @@ static ALCboolean IsValidAmbiScaling(ALCenum scaling)
return ALC_FALSE;
}
+
+/* Downmixing channel arrays, to map the given format's missing channels to
+ * existing ones. Based on Wine's DSound downmix values, which are based on
+ * PulseAudio's.
+ */
+const std::array<InputRemixMap,6> StereoDownmix{{
+ { FrontCenter, {{{FrontLeft, 0.5f}, {FrontRight, 0.5f}}} },
+ { SideLeft, {{{FrontLeft, 1.0f/9.0f}, {FrontRight, 0.0f}}} },
+ { SideRight, {{{FrontLeft, 0.0f}, {FrontRight, 1.0f/9.0f}}} },
+ { BackLeft, {{{FrontLeft, 1.0f/9.0f}, {FrontRight, 0.0f}}} },
+ { BackRight, {{{FrontLeft, 0.0f}, {FrontRight, 1.0f/9.0f}}} },
+ { BackCenter, {{{FrontLeft, 0.5f/9.0f}, {FrontRight, 0.5f/9.0f}}} },
+}};
+const std::array<InputRemixMap,4> QuadDownmix{{
+ { FrontCenter, {{{FrontLeft, 0.5f}, {FrontRight, 0.5f}}} },
+ { SideLeft, {{{FrontLeft, 0.5f}, {BackLeft, 0.5f}}} },
+ { SideRight, {{{FrontRight, 0.5f}, {BackRight, 0.5f}}} },
+ { BackCenter, {{{BackLeft, 0.5f}, {BackRight, 0.5f}}} },
+}};
+const std::array<InputRemixMap,3> X51Downmix{{
+ { BackLeft, {{{SideLeft, 1.0f}, {SideRight, 0.0f}}} },
+ { BackRight, {{{SideLeft, 0.0f}, {SideRight, 1.0f}}} },
+ { BackCenter, {{{SideLeft, 0.5f}, {SideRight, 0.5f}}} },
+}};
+const std::array<InputRemixMap,3> X51RearDownmix{{
+ { SideLeft, {{{BackLeft, 1.0f}, {BackRight, 0.0f}}} },
+ { SideRight, {{{BackLeft, 0.0f}, {BackRight, 1.0f}}} },
+ { BackCenter, {{{BackLeft, 0.5f}, {BackRight, 0.5f}}} },
+}};
+const std::array<InputRemixMap,2> X61Downmix{{
+ { BackLeft, {{{BackCenter, 0.5f}, {SideLeft, 0.5f}}} },
+ { BackRight, {{{BackCenter, 0.5f}, {SideRight, 0.5f}}} },
+}};
+const std::array<InputRemixMap,1> X71Downmix{{
+ { BackCenter, {{{BackLeft, 0.5f}, {BackRight, 0.5f}}} },
+}};
+
+} // namespace
+
/************************************************
* Miscellaneous ALC helpers
************************************************/
@@ -1859,6 +1900,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->Dry.AmbiMap.fill(BFChannelConfig{});
device->Dry.Buffer = {};
std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0u);
+ device->RealOut.RemixMap = {};
device->RealOut.ChannelIndex.fill(INVALID_CHANNEL_INDEX);
device->RealOut.Buffer = {};
device->MixBuffer.clear();
@@ -1935,6 +1977,18 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, device->UpdateSize, device->BufferSize);
+ switch(device->FmtChans)
+ {
+ case DevFmtStereo: device->RealOut.RemixMap = StereoDownmix; break;
+ case DevFmtQuad: device->RealOut.RemixMap = QuadDownmix; break;
+ case DevFmtX51: device->RealOut.RemixMap = X51Downmix; break;
+ case DevFmtX51Rear: device->RealOut.RemixMap = X51RearDownmix; break;
+ case DevFmtX61: device->RealOut.RemixMap = X61Downmix; break;
+ case DevFmtX71: device->RealOut.RemixMap = X71Downmix; break;
+ case DevFmtMono:
+ case DevFmtAmbi3D: break;
+ }
+
aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
device->NumAuxSends = new_sends;
diff --git a/alc/alcmain.h b/alc/alcmain.h
index 1da332c0..673997dc 100644
--- a/alc/alcmain.h
+++ b/alc/alcmain.h
@@ -60,6 +60,14 @@ enum RenderMode {
};
+struct InputRemixMap {
+ struct TargetMix { Channel channel; float mix; };
+
+ Channel channel;
+ std::array<TargetMix,2> targets;
+};
+
+
struct BufferSubList {
uint64_t FreeMask{~0_u64};
ALbuffer *Buffers{nullptr}; /* 64 */
@@ -184,6 +192,7 @@ struct MixParams {
};
struct RealMixParams {
+ al::span<const InputRemixMap> RemixMap;
std::array<ALuint,MaxChannels> ChannelIndex{};
al::span<FloatBufferLine> Buffer;
diff --git a/alc/alu.cpp b/alc/alu.cpp
index f228ff29..deb0bb54 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -942,9 +942,23 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
for(ALuint c{0};c < num_channels;c++)
{
- const ALuint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
+ ALuint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
if(idx != INVALID_CHANNEL_INDEX)
voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain;
+ else
+ {
+ auto match_channel = [chans,c](const InputRemixMap &map) -> bool
+ { return chans[c].channel == map.channel; };
+ auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(),
+ Device->RealOut.RemixMap.cend(), match_channel);
+ if(remap != Device->RealOut.RemixMap.cend())
+ for(const auto &target : remap->targets)
+ {
+ idx = GetChannelIdxByName(Device->RealOut, target.channel);
+ if(idx != INVALID_CHANNEL_INDEX)
+ voice->mChans[c].mDryParams.Gains.Target[idx] = target.mix;
+ }
+ }
}
/* Auxiliary sends still use normal channel panning since they mix to