Skip to content

Commit

Permalink
Full CBF support
Browse files Browse the repository at this point in the history
  • Loading branch information
VoidXH committed Jul 2, 2024
1 parent db82361 commit 9476fec
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 43 deletions.
13 changes: 2 additions & 11 deletions Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,26 +265,17 @@ protected void FinishEmpty(ReferenceChannel[] channels) {
}

(FilterGraphNode node, int channel)[] result = new (FilterGraphNode, int)[orderedNodes.Count];
Dictionary<int, FilterGraphNode> lastNodes = new Dictionary<int, FilterGraphNode>(); // For channel indices
int lowestChannel = 0;
for (int i = 0; i < result.Length; i++) {
FilterGraphNode source = orderedNodes[i];
int channelIndex = 0;
int channelIndex;
if (source.Children.Count == 0 && source.Filter is OutputChannel output) { // Actual exit node, not terminated virtual ch
channelIndex = GetChannelIndex(output.Channel);
} else if (source.Parents.Count == 0) { // Entry node
channelIndex = GetChannelIndex(((InputChannel)source.Filter).Channel);
} else if (source.Parents.Count == 1 && source.Parents[0].Children.Count == 1) { // Direct path
FilterGraphNode parent = source.Parents[0];
foreach (KeyValuePair<int, FilterGraphNode> node in lastNodes) {
if (node.Value == parent) {
channelIndex = node.Key;
}
}
} else { // Either merge node or exit from a split: new virtual channel
} else { // TODO: greedily keep direct paths on the same channel index, don't have all filters on separate nodes
channelIndex = --lowestChannel;
}
lastNodes[channelIndex] = source;
result[i] = (source, channelIndex);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ void Cleanup() {
for (int i = 0, c = mixes.Count; i < c; i++) {
int source = mixes[i].source;
int[] targets = mixes[i].targets;
if (targets.Length == 0) {
toRemove.Add(i);
continue;
}

for (int target = 0; target < targets.Length; target++) {
for (int j = i + 1; j < c; j++) {
if (toRemove.Contains(j)) {
Expand All @@ -101,10 +106,11 @@ void Cleanup() {
}
}
}

toRemove.Sort();
for (int i = toRemove.Count - 1; i >= 0; i--) {
mixes.RemoveAt(toRemove[i]);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public ConvolutionBoxFormatConfigurationFile(string path) : base(Path.GetFileNam
using FileStream stream = File.OpenRead(path);
stream.Position = 4;
sampleRate = stream.ReadInt32();
Optimize();
}

/// <summary>
Expand All @@ -81,8 +82,15 @@ FilterGraphNode GetChannel(int index) { // Get an actual channel's last node
return newChannel;
}

#if DEBUG // Seeing all entries is better for debugging, but hurts memory usage
CBFEntry[] allEntries = Enumerable.Range(0, entries).Select(x => CBFEntry.Read(stream)).ToArray();
#endif
for (int i = 0; i < entries; i++) {
#if DEBUG
CBFEntry entry = allEntries[i];
#else
CBFEntry entry = CBFEntry.Read(stream);
#endif
if (entry is MatrixEntry matrix) {
Dictionary<int, FilterGraphNode> createdNodes = new Dictionary<int, FilterGraphNode>();
for (int mix = 0, c = matrix.Mixes.Count; mix < c; mix++) {
Expand Down Expand Up @@ -110,10 +118,16 @@ FilterGraphNode GetChannel(int index) { // Get an actual channel's last node
}
}

for (int i = 0, c = inputChannels.Count; i < c; i++) {
if (i >= 0) {
inputChannels.Sort((a, b) => a.name.CompareTo(b.name));
foreach (KeyValuePair<int, FilterGraphNode> node in lastNodes) {
if (node.Key >= 0) {
// TODO: many points make input or output channels from channels, create them from names instead
lastNodes[i].AddChild(new OutputChannel(((InputChannel)inputChannels[i].root.Filter).ChannelName));
OutputChannel outputFilter = new OutputChannel(((InputChannel)inputChannels[node.Key].root.Filter).ChannelName);
if (node.Value.Filter == null) {
node.Value.Filter = outputFilter;
} else {
node.Value.AddChild(outputFilter);
}
}
}
return inputChannels.ToArray();
Expand All @@ -125,15 +139,6 @@ public override void Export(string path) {
(FilterGraphNode node, int channel)[] exportOrder = GetExportOrder();
List<CBFEntry> entries = new List<CBFEntry>();

MatrixEntry GetMixer() { // Get the last entry if it's a mixer of make a new and return that
int count = entries.Count;
if (count == 0 || !(entries[count - 1] is MatrixEntry result)) {
result = new MatrixEntry();
entries.Add(result);
}
return result;
}

int GetIndex(FilterGraphNode node) { // Get filter index by node
for (int i = 0; i < exportOrder.Length; i++) {
if (exportOrder[i].node == node) {
Expand All @@ -145,31 +150,18 @@ int GetIndex(FilterGraphNode node) { // Get filter index by node

for (int i = 0; i < exportOrder.Length; i++) {
int channel = exportOrder[i].channel;
// TODO: fix the broken copy filter order
IReadOnlyList<FilterGraphNode> parents = exportOrder[i].node.Parents;
if (parents.Count > 1) {
GetMixer().Expand(parents.Select(x => GetIndex(x)).ToArray(), channel);
}
// Keeping only incoming nodes is a full solution - optimizing for that few bytes of space would be possible if you're bored
int[] parents = exportOrder[i].node.Parents.Select(x => GetIndex(x)).ToArray();
MatrixEntry mixer = new MatrixEntry();
mixer.Expand(parents, channel);
entries.Add(mixer);

Filter filter = exportOrder[i].node.Filter;
if (filter is FastConvolver fastConvolver) {
entries.Add(new ConvolutionEntry(channel, fastConvolver.Impulse));
} else if (filter is Convolver convolver) {
entries.Add(new ConvolutionEntry(channel, convolver.Impulse));
}

IReadOnlyList<FilterGraphNode> children = exportOrder[i].node.Children;
int totalChildren = children.Count;
if (totalChildren > 1) {
GetMixer().Expand(channel, children
.Where(x => x.Parents.Count == 1) // When this is not the case, the parent copy above will happen
.Select(x => GetIndex(x)).ToArray());
} else if (totalChildren == 1) {
int targetChannel = GetIndex(children[0]);
if (channel != targetChannel) {
GetMixer().Expand(channel, targetChannel);
}
}
}

using FileStream file = File.Open(path, FileMode.Create);
Expand Down
2 changes: 1 addition & 1 deletion Cavern/Filters/FastConvolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public partial class FastConvolver : Filter, IDisposable, ILocalizableToString {
/// <summary>
/// Get a clone of the <see cref="filter"/>'s impulse response.
/// </summary>
public float[] Impulse => Measurements.GetRealPart(filter.IFFT(cache));
public float[] Impulse => Measurements.GetRealPartHalf(filter.IFFT(cache)); // The constructor doubles the length

/// <summary>
/// Number of samples in the impulse response.
Expand Down

0 comments on commit 9476fec

Please sign in to comment.