diff --git a/Cavern/Channels/ChannelPrototype.Consts.cs b/Cavern/Channels/ChannelPrototype.Consts.cs
index 0f6cdf0b..1c9fc865 100644
--- a/Cavern/Channels/ChannelPrototype.Consts.cs
+++ b/Cavern/Channels/ChannelPrototype.Consts.cs
@@ -114,6 +114,11 @@ public static readonly ChannelPrototype
///
public static readonly ReferenceChannel[] ref510 = { ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE, ReferenceChannel.SideLeft, ReferenceChannel.SideRight };
+ ///
+ /// Standard 5.1.2 setup (L, R, C, LFE, SL, SR, TSL, TSR).
+ ///
+ public static readonly ReferenceChannel[] ref512 = { ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE, ReferenceChannel.SideLeft, ReferenceChannel.SideRight, ReferenceChannel.TopSideLeft, ReferenceChannel.TopSideRight };
+
///
/// Standard 5.1.4 setup (L, R, C, LFE, SL, SR, TFL, TFR, TRL, TRR).
///
diff --git a/Cavern/Channels/SpatialRemapping.cs b/Cavern/Channels/SpatialRemapping.cs
index 6a00aac9..8edd04f6 100644
--- a/Cavern/Channels/SpatialRemapping.cs
+++ b/Cavern/Channels/SpatialRemapping.cs
@@ -1,4 +1,7 @@
using System;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Text;
using Cavern.Utilities;
@@ -14,17 +17,17 @@ public static class SpatialRemapping {
/// specific channel. The dimensions are [output channels][input channels].
///
public static float[][] GetMatrix(Channel[] playedContent, Channel[] usedLayout) {
- Channel[] oldChannels = Listener.Channels;
- Listener.ReplaceChannels(usedLayout);
int inputs = playedContent.Length,
outputs = usedLayout.Length;
// Create simulation
- Listener simulator = new Listener() {
+ Listener simulator = new Listener(false) {
UpdateRate = Math.Max(inputs, 16),
LFESeparation = true,
DirectLFE = true
};
+ Channel[] oldChannels = Listener.Channels;
+ Listener.ReplaceChannels(usedLayout);
for (int i = 0; i < inputs; i++) {
simulator.AttachSource(new Source() {
Clip = GetClipForChannel(i, inputs, simulator.SampleRate),
@@ -49,6 +52,51 @@ public static float[][] GetMatrix(Channel[] playedContent, Channel[] usedLayout)
return output;
}
+ ///
+ /// Get a mixing matrix that maps the to the user-set of the
+ /// current system. The result is a set of multipliers for each output (playback) channel, with which the input (content)
+ /// channels should be multiplied and mixed to that specific channel. The dimensions are [output channels][input channels].
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float[][] GetMatrix(Channel[] playedContent) => GetMatrix(playedContent, Listener.Channels);
+
+ ///
+ /// Convert a spatial remapping matrix to an Equalizer APO Copy filter.
+ ///
+ public static string ToEqualizerAPO(float[][] matrix) {
+ StringBuilder result = new StringBuilder("Copy:");
+ for (int i = 0; i < matrix.Length; i++) {
+ float[] input = matrix[i];
+ bool started = false;
+ string label = EqualizerAPOUtils.GetChannelLabel(i, matrix.Length);
+ for (int j = 0; j < input.Length; j++) {
+ if (input[j] != 0) {
+ if (!started) {
+ result.Append(' ').Append(label).Append('=');
+ started = true;
+ } else {
+ result.Append('+');
+ }
+ if (input[j] != 1) {
+ result.Append(input[j].ToString(CultureInfo.InvariantCulture)).Append('*');
+ }
+ result.Append(EqualizerAPOUtils.GetChannelLabel(j, input.Length));
+ }
+ }
+ if (!started) {
+ result.Append(' ').Append(label).Append("=0");
+ }
+ }
+ return result.ToString();
+ }
+
+ ///
+ /// Create an Equalizer APO Copy filter that matrix mixes the to the user-set
+ /// of the current system.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string ToEqualizerAPO(Channel[] playedContent) => ToEqualizerAPO(GetMatrix(playedContent));
+
///
/// Create a that is 1 at the channel's index and 0 everywhere else.
///
diff --git a/CavernSamples/CavernSamples.sln b/CavernSamples/CavernSamples.sln
index 28b348ec..ee31efdf 100644
--- a/CavernSamples/CavernSamples.sln
+++ b/CavernSamples/CavernSamples.sln
@@ -37,10 +37,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnhancedAC3Merger", "Enhanc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuickEQResultMerger", "QuickEQResultMerger\QuickEQResultMerger.csproj", "{AF9655E4-8DC7-4D30-8E35-59C81EFB3132}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EQAPOtoFIR", "EQAPOtoFIR\EQAPOtoFIR.csproj", "{9D7888F3-02C1-4B07-87C3-7272A254E477}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EQAPOtoFIR", "EQAPOtoFIR\EQAPOtoFIR.csproj", "{9D7888F3-02C1-4B07-87C3-7272A254E477}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cavern.QuickEQ.Format", "..\Cavern.QuickEQ.Format\Cavern.QuickEQ.Format.csproj", "{24E5737B-A035-4EC4-BA8E-21375BC81219}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CavernizeLive", "CavernizeLive\CavernizeLive.csproj", "{21AFB2EA-E96F-4C12-A526-064FA498B6EA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -107,6 +109,10 @@ Global
{24E5737B-A035-4EC4-BA8E-21375BC81219}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24E5737B-A035-4EC4-BA8E-21375BC81219}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24E5737B-A035-4EC4-BA8E-21375BC81219}.Release|Any CPU.Build.0 = Release|Any CPU
+ {21AFB2EA-E96F-4C12-A526-064FA498B6EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {21AFB2EA-E96F-4C12-A526-064FA498B6EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {21AFB2EA-E96F-4C12-A526-064FA498B6EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {21AFB2EA-E96F-4C12-A526-064FA498B6EA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/CavernSamples/CavernizeGUI/Elements/RenderTarget.cs b/CavernSamples/CavernizeGUI/Elements/RenderTarget.cs
index e178a44f..b36a059f 100644
--- a/CavernSamples/CavernizeGUI/Elements/RenderTarget.cs
+++ b/CavernSamples/CavernizeGUI/Elements/RenderTarget.cs
@@ -110,63 +110,63 @@ public ReferenceChannel[] GetNameMappedChannels() {
///
/// Top rears are used instead of sides for smooth height transitions and WAVEFORMATEXTENSIBLE support.
public static readonly RenderTarget[] Targets = {
- new RenderTarget("4.0.4", new[] {
+ new RenderTarget("4.0.4", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.RearLeft, ReferenceChannel.RearRight,
ReferenceChannel.TopFrontLeft, ReferenceChannel.TopFrontRight, ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }),
- new RenderTarget("4.1.1", new[] {
+ ]),
+ new RenderTarget("4.1.1", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearCenter, ReferenceChannel.TopFrontCenter
- }),
+ ]),
new RenderTarget("5.1 side", ChannelPrototype.ref510),
- new RenderTarget("5.1 rear", new[] {
+ new RenderTarget("5.1 rear", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight
- }),
+ ]),
new DownmixedRenderTarget("5.1.2 front", ChannelPrototype.ref514, (8, 4), (9, 5)),
- new RenderTarget("5.1.2 side", new[] {
+ new RenderTarget("5.1.2 side", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.SideLeft, ReferenceChannel.SideRight, ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }),
+ ]),
new RenderTarget("5.1.4", ChannelPrototype.ref514),
- new DownmixedRenderTarget("5.1.4 matrix", new[] {
+ new DownmixedRenderTarget("5.1.4 matrix", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.SideLeft, ReferenceChannel.SideRight, ReferenceChannel.TopFrontLeft, ReferenceChannel.TopFrontRight,
ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }, (8, 0), (~8, 4), (9, 1), (~9, 5)),
+ ], (8, 0), (~8, 4), (9, 1), (~9, 5)),
new RenderTarget("5.1.6 with top sides", ChannelPrototype.ref516),
new RenderTarget("5.1.6 for WAVE", ChannelPrototype.wav516),
new RenderTarget("7.1", ChannelPrototype.ref710),
new DownmixedRenderTarget("7.1.2 front", ChannelPrototype.ref714, (10, 4), (11, 5)),
- new RenderTarget("7.1.2 side", new[] {
+ new RenderTarget("7.1.2 side", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight, ReferenceChannel.SideLeft, ReferenceChannel.SideRight,
ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }),
- new DownmixedRenderTarget("7.1.2 matrix", new[] {
+ ]),
+ new DownmixedRenderTarget("7.1.2 matrix", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight, ReferenceChannel.SideLeft, ReferenceChannel.SideRight,
ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }, (8, 6), (~8, 4), (9, 7), (~9, 5)),
- new DownmixedRenderTarget("7.1.3 matrix", new[] {
+ ], (8, 6), (~8, 4), (9, 7), (~9, 5)),
+ new DownmixedRenderTarget("7.1.3 matrix", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight, ReferenceChannel.SideLeft, ReferenceChannel.SideRight,
ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight, ReferenceChannel.TopFrontCenter
- }, (8, 6), (~8, 4), (9, 7), (~9, 5), (10, 0), (~10, 1)),
+ ], (8, 6), (~8, 4), (9, 7), (~9, 5), (10, 0), (~10, 1)),
new RenderTarget("7.1.4", ChannelPrototype.ref714),
new RenderTarget("7.1.6 with top sides", ChannelPrototype.ref716),
new RenderTarget("7.1.6 for WAVE", ChannelPrototype.wav716),
- new RenderTarget("9.1", new[] {
+ new RenderTarget("9.1", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight, ReferenceChannel.SideLeft, ReferenceChannel.SideRight,
ReferenceChannel.WideLeft, ReferenceChannel.WideRight
- }),
+ ]),
new DownmixedRenderTarget("9.1.2 front", ChannelPrototype.ref914, (12, 4), (13, 5)),
- new RenderTarget("9.1.2 side", new[] {
+ new RenderTarget("9.1.2 side", [
ReferenceChannel.FrontLeft, ReferenceChannel.FrontRight, ReferenceChannel.FrontCenter, ReferenceChannel.ScreenLFE,
ReferenceChannel.RearLeft, ReferenceChannel.RearRight, ReferenceChannel.SideLeft, ReferenceChannel.SideRight,
ReferenceChannel.WideLeft, ReferenceChannel.WideRight, ReferenceChannel.TopRearLeft, ReferenceChannel.TopRearRight
- }),
+ ]),
new RenderTarget("9.1.4", ChannelPrototype.ref914),
new RenderTarget("9.1.6 with top sides", ChannelPrototype.ref916),
new RenderTarget("9.1.6 for WAVE", ChannelPrototype.wav916),
diff --git a/Tests/Test.Cavern/Channels/SpatialRemapping_Tests.cs b/Tests/Test.Cavern/Channels/SpatialRemapping_Tests.cs
index f99e854d..9fffde4f 100644
--- a/Tests/Test.Cavern/Channels/SpatialRemapping_Tests.cs
+++ b/Tests/Test.Cavern/Channels/SpatialRemapping_Tests.cs
@@ -12,8 +12,8 @@ public class SpatialRemapping_Tests {
///
[TestMethod, Timeout(1000)]
public void Remap5Point1() {
- Channel[] content = ChannelPrototype.ToLayoutAlternative(ChannelPrototype.GetStandardMatrix(6)),
- playback = ChannelPrototype.ToLayout(ChannelPrototype.GetStandardMatrix(6));
+ Channel[] content = ChannelPrototype.ToLayoutAlternative(ChannelPrototype.ref510),
+ playback = ChannelPrototype.ToLayout(ChannelPrototype.ref510);
float[][] matrix = SpatialRemapping.GetMatrix(content, playback);
Assert.AreEqual(1, matrix[0][0]); // FL
Assert.AreEqual(1, matrix[1][1]); // FR
@@ -25,5 +25,16 @@ public void Remap5Point1() {
Assert.AreEqual(.820972264f, matrix[5][5]); // SR side mix
TestUtils.AssertNumberOfZeros(matrix, 28);
}
+
+ ///
+ /// Tests if remapping 7.1 is done correctly to 5.1.2 and converted to the valid Equalizer APO line.
+ ///
+ [TestMethod, Timeout(1000)]
+ public void Remap7Point1To512APO() {
+ const string expected = "Copy: L=L R=R C=C SUB=SUB RL=0.92387956*RL+0.3826834*RR+SL RR=0.38268337*RL+0.92387956*RR+SR SL=0 SR=0";
+ string result = SpatialRemapping.ToEqualizerAPO(SpatialRemapping.GetMatrix(ChannelPrototype.ToLayout(ChannelPrototype.ref710),
+ ChannelPrototype.ToLayout(ChannelPrototype.ref512)));
+ Assert.AreEqual(expected, result);
+ }
}
}
\ No newline at end of file