Skip to content

Instantly share code, notes, and snippets.

@archi
Created May 31, 2020 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save archi/8261745be4e15c927d67f531a6805a99 to your computer and use it in GitHub Desktop.
Save archi/8261745be4e15c927d67f531a6805a99 to your computer and use it in GitHub Desktop.
Generate FreeDSP Aurora plugin.ini from SigmaStudio .params file
#!/usr/bin/perl
use strict;
use warnings;
# Process SigmaStudio '.params' file to produce a JSON file for FreeDSP-Aurora
# Written by Sebastian Meyer <github.com/archi>
#
# The Author is not affiliated with FreeDSP.
#
# License: Public Domain, if you use it in your project, it'd be nice to mention me as a contributor.
# I reserve the right to relicense future versions.
#
# Script comes with no warranty - do not blindly trust its output!!
#
# Be aware XOs need to manually fixed since they look just like a pair of LP/HP.
# I am not using them right now.
#
# Version 0.1, 31.05.2020
# Check user supplied parameters:
my $in_file = shift;
my $out_file = shift;
my $bad = 0;
$bad++ unless defined $in_file;
$bad++ unless defined $out_file;
# make sure user didn't supply more parameters:
$bad++ if defined shift;
if ($bad > 0 || $in_file =~ m/^[-?]-h(elp)?$/) {
print "Usage: plugininigen.pl <input_file> <output_file>\n",
"\n",
"Usually you want:\n",
" <input_file> Parameter file: '<plugin>.params'\n",
" <output_file> 'plugin.ini'\n";
exit $bad > 0 ? 1 : 0;
}
# the aim is to fill an output result hash (and convert that to json):
# i use 'result' to avoid confusion with dsp 'output'
my %result;
# now we want to translate things like
# > Cell Name = InputSelect_8.Nx1-1_UAC
# > Parameter Name = monomuxSigma300ns44index
# > Parameter Address = 20413
# > Parameter Value = 7
# > Parameter Data :
# > 0x00, 0x00, 0x00, 0x07,
# into the correct format for plugin.ini
# The aim is to do as much with pattern matching instead of hardcoding detectors.
# so we build this map of pattern (on cell name) -> handler:
# I'll explain the structure on one example.
my %name_to_handler;
# Here we parse the InputSelect's
# So the pattern should grab us the size ($1) and the name ($2)
# Which input this is (e.g. 7) we get from the "Parameter Value"
$name_to_handler{"InputSelect_(.)\.Nx1-1(.*)"} = sub {
# this is a hash references with the collected data:
# 1 => regex match 1 (This inputSelect's size, e.g. 8)
# 2 => regex match 2 (This InputSelect's source, e.g. "_UAC")
# ... (this goes up to 6, but we're only using two here)
# cell_name => Cell Name
# name => Parameter Name
# address => Parameter Address
# value => Parameter Value
my $data = shift;
# Input could be any of "_Analog", "_UAC", "_ESP32" (ignored), "_Exp", "_SPDIF" and "" (= port)
# Let's normalize that to the plugin.ini format:
my $input_type = lc $data->{2};
# "" is "port"
$input_type = "port" if $input_type =~ m/^\s*$/;
$input_type =~ s/^_//;
# esp32 is ignored for now
return if $input_type eq "esp32";
# now we have analog, uac, exp, spdif and port -- these are also the names used in the plugin.ini
# check if the corresponding array already exists in the output hash, if not, create it:
$result{$input_type} = [] if not defined $result{$input_type};
# now put the address at the correct location in the array
# and remember, the channel in InputSelect_$$CHANNEL$$ counts from 1 to 8, the array from 0 to 7
my $channel = int($data->{1});
$result{$input_type}[$channel - 1] = $data->{address};
# last but not least, the input select tells us how many channels we have
# make sure this is consistent with whatever we know (or use it as initial knowledge)
if (not defined $result{nchn} or $result{nchn} < $channel) {
$result{nchn} = $channel;
}
};
$name_to_handler{"MasterVolume"} = sub {
my $data = shift;
# we have Parameter Name HWGainADAU145XAlg9target and HWGainADAU145XAlg9slew_mode
# 8channel uses the '...target' variant, so we do the same here:
return if not $data->{name} =~ m/^HWGainADAU.*target$/;
if (not defined ($result{master})) {
$result{master} = $data->{address};
} elsif ($result{master} ne $data->{address}) {
die "Can not detect MasterVolume address, is it " . $result{master} . " or " . $data->{address} . "?\n";
}
};
$name_to_handler{"BypassVolPoti"} = sub {
my $data = shift;
if (not defined ($result{vpot})) {
$result{vpot} = $data->{address};
} elsif ($result{vpot} ne $data->{address}) {
die "Can not detect MasterVolume address, is it " . $result{vpot} . " or " . $data->{vpot} . "?\n";
}
};
# high pass and low pass
$name_to_handler{"(L|H)P([0-9]+)_([0-9]+)"} = sub {
my $data = shift;
# the high passes have 5 parameter addresses each
# we're interested in the lowest one, with the name ending in B2_1
# (i'm not 100% sure on that, so we'll check the address to be safe)
my $pass = ($data->{1} eq "L") ? "lp" : "hp";
$result{$pass} = [] if (not defined $result{$pass});
# we compute the array index from HPx_y
# e.g.:
# 0: HP1_1
# 1: HP1_2
# 2: HP1_3
# 3: HP1_4
# 4: HP2_1
my $idx = 4 * (int($data->{2}) - 1) + int($data->{3}) - 1;
# name ending in B2_1 => add the value
if ($data->{name} =~ m/B2_1$/) {
$result{$pass}[$idx] = $data->{address};
} elsif (defined $result{$pass}[$idx]) {
die "high/low pass address assumption failed!" if $result{$pass}[$idx] > $data->{address};
}
};
# virtually the same as above, but different index computation
$name_to_handler{"(Low Shelv|High Shelv|Phase) ([0-9]+)"} = sub {
my $data = shift;
# the shelfs also have 5 parameter addresses each
# we're interested in the lowest one, with the name ending in B2_1
# (i'm not 100% sure on that, so we'll check the address to be safe)
my $arr = undef;
if (($data->{1} eq "Low Shelv")) {
$arr = "lshelv";
} elsif (($data->{1} eq "High Shelv")) {
$arr = "hshelv";
} elsif (($data->{1} eq "Phase")) {
$arr = "phase";
}
die "Internal error" if not defined $arr;
$result{$arr} = [] if (not defined $result{$arr});
# index for "Low Shelv x" is probably (x - 1)
my $idx = (int($data->{2}) - 1);
# name ending in B2_1 => add the value
if ($data->{name} =~ m/B2_1$/) {
$result{$arr}[$idx] = $data->{address};
} elsif (defined $result{$arr}[$idx]) {
die "high/low shelv address assumption failed!" if $result{$arr}[$idx] > $data->{address};
}
};
# delay and gain are again pretty similar
$name_to_handler{"(Delay|Gain|FIR) ?([0-9]+)"} = sub {
my $data = shift;
# delay's name is 'DelaySigma300Alg1delay', and Gain 'HWGainADAU145XAlg2target'
# filter out 'HWGainADAU145XAlg2slew_mode'
return if $data->{name} =~ m/slew_mode$/;
my $arr = undef;
if (($data->{1} eq "Delay")) {
$arr = "dly";
} elsif (($data->{1} eq "Gain")) {
$arr = "gain";
} elsif (($data->{1} eq "FIR")) {
$arr = "fir";
}
die "Internal error" if not defined $arr;
$result{$arr} = [] if (not defined $result{$arr});
my $idx = (int($data->{2}) - 1);
$result{$arr}[$idx] = $data->{address};
};
# parametric eq are similar, but a little bit too different again: I want to handle different # of PEQ per channel, so more magic is necessary
$name_to_handler{"Param EQ ([0-9]+)"} = sub {
my $data = shift;
# each PEQ consists of multiple bands.
# eq. "Param EQ 1" has
# - General2ndOrderDPSigma300Alg6B2_1 [that's the address we want]
# - General2ndOrderDPSigma300Alg6B1_1
# - General2ndOrderDPSigma300Alg6B0_1
# - General2ndOrderDPSigma300Alg6A2_1
# - General2ndOrderDPSigma300Alg6A1_1
# but now comes the next band, still part of "Param EQ 1":
# - General2ndOrderDPSigma300Alg6B2_2
# - General2ndOrderDPSigma300Alg6B1_2
# - General2ndOrderDPSigma300Alg6B0_2
# - General2ndOrderDPSigma300Alg6A2_2
# - General2ndOrderDPSigma300Alg6A1_2
# and so on for all the other bands
# NOW, careful: I'm doing this because I want to have 15 bands per PEQ in the first two channels, and for the test
# i do not care. so we just push them into the array and hope the order in the params-file is what we want in the output array :(
return if (not $data->{name} =~ m/^.*300Alg[0-9]+B2_([0-9])+$/);
$result{peq} = [] if (not defined $result{peq});
push @{$result{peq}}, $data->{address};
};
# Open the file, and parse it
open my $IN, "<$in_file" or die "Could not open '$in_file' for reading: $!\n";
my %data;
my @name_to_handler_ptrns = keys %name_to_handler;
while (my $line = <$IN>) {
next unless $line =~ m/^(Cell Name|Parameter Name|Parameter Address|Parameter Value)\s+=\s*(.*)$/;
my $param = $2;
my $key = $1;
$key =~ s/ /_/g;
$key = lc $key;
$key =~ s/^parameter_//;
$param =~ s/\s*$//g;
$data{$key} = $key eq "address" ? int($param) : $param;
if ($key eq "cell_name") {
#print "cell_name = $param\n";
foreach my $ptrn (@name_to_handler_ptrns) {
if ($param =~ m/^$ptrn$/) {
$data{_handler} = $name_to_handler{$ptrn};
$data{1} = $1 if defined $1;
$data{2} = $2 if defined $2;
$data{3} = $3 if defined $3;
$data{4} = $4 if defined $4;
$data{5} = $5 if defined $5;
$data{6} = $6 if defined $6;
# add more if necessary
}
}
} elsif ($key eq "value") {
$data{_handler}->(\%data) if defined $data{_handler};
%data = ();
}
}
close $IN;
# Generate the "nhp" and so on...
# nxo has to be manually generated from xolp/xohp (once those are added automatically)
foreach my $key ("hp", "lshelv", "peq", "hshelv", "lp", "phase", "dly", "gain", "fir") {
next if defined $result{"n$key"};
$result{"n$key"} = 0;
next if not defined $result{$key};
if ($key =~ m/^(lp|hp)$/) {
$result{"n$key"} = scalar @{$result{$key}} / 4;
} else {
$result{"n$key"} = scalar @{$result{$key}};
}
}
open my $OUT, ">$out_file" or die "Could not open '$out_file' for writing: $!\n";
# my $!!!@ lovely windows doesn't have JSON.pm, so let's be rude...
# make the output more beautiful by printing one channel per line, unless it's just one datum per channel
my $nchn = $result{nchn};
print $OUT "{\n";
my $first = 1;
foreach my $k (sort keys %result) {
print $OUT ",\n" if not $first;
$first = 0;
print $OUT "\"$k\":";
my $type = ref $result{$k};
if ($type eq "") {
print $OUT $result{$k};
} elsif ($type eq "ARRAY") {
my $cnt = scalar @{$result{$k}};
my $insert_line_break = 4096;
my $prefix = "";
if ($cnt > $nchn and $cnt % $nchn == 0) {
$insert_line_break = $cnt / $nchn;
$prefix = sprintf("%".(length($k)+4)."s", "");
}
print $OUT "[";
my $afirst = 1;
foreach my $a (@{$result{$k}}) {
if ($insert_line_break == 0) {
print $OUT ",\n$prefix";
$insert_line_break = $cnt / $nchn;
} else {
print $OUT ", " if not $afirst;
}
$insert_line_break--;
$afirst = 0;
if (not defined $a) {
$bad++;
print $OUT "undef";
print "Undefined array member for '$k'!\n";
} else {
print $OUT $a;
}
}
print $OUT "]";
} else {
print "Error: Don't know how to print '$k'!\n";
$bad++;
}
}
print $OUT "\n}";
close $OUT;
print "Script finished with $bad errors. Remember to ALWAYS review the output. If you're using an XO, adapt the result! (The script can not differentiate between an XO and a LP/HP!\n\n\n";
print "This script (and the result) come with no warranty for correctness. If this kills your speakers or DSP, it is your fault for not properly reviewing the output!\n";
exit 1 if $bad > 0;
exit 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment