Created
May 31, 2020 14:10
-
-
Save archi/8261745be4e15c927d67f531a6805a99 to your computer and use it in GitHub Desktop.
Generate FreeDSP Aurora plugin.ini from SigmaStudio .params file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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