Skip to content

Instantly share code, notes, and snippets.

@joslloand
Created June 22, 2019 01:00
Show Gist options
  • Save joslloand/b25273e0894ccbc4b186c3fc048cdd01 to your computer and use it in GitHub Desktop.
Save joslloand/b25273e0894ccbc4b186c3fc048cdd01 to your computer and use it in GitHub Desktop.
Test: magnitude design for multi-band decoding filters with focalisation
/*
define:
~meanE
~matchWeight
*/
// meanE from degree weights
(
~meanE = { |beamWeights, dim = 3|
var m = beamWeights.size - 1; // order
(dim == 2).if({
beamWeights.removeAt(0).squared + (2*beamWeights.squared.sum) // 2D
}, {
(Array.series(m + 1, 1, 2) * beamWeights.squared).sum // 3D
}).asFloat
}
)
~hoaOrder.meanE(\controlled, 3)
~meanE.value( ~hoaOrder.beamWeights(\controlled, 3), 3)
// matchWeight from degree weights
(
~matchWeight = { |beamWeights, dim = 3, match = 'amp', numChans = nil|
var m = beamWeights.size - 1; // order
var n;
switch( match,
'amp', { 1.0 },
'rms', {
(dim == 2).if({
n = 2*m + 1 // 2D
}, {
n = (m + 1).squared // 3D
});
(n/~meanE.value(beamWeights, dim)).sqrt
},
'energy', {
n = numChans;
(n/~meanE.value(beamWeights, dim)).sqrt
}
).asFloat
}
)
~hoaOrder.matchWeight(\controlled, 2, \rms)
~matchWeight.value( ~hoaOrder.beamWeights(\controlled, 2), 2, \rms)
~hoaOrder.matchWeight(\controlled, 2, \energy, 60)
~matchWeight.value( ~hoaOrder.beamWeights(\controlled, 2), 2, \energy, 60)
(
// "three band" dictionary version
~foclShelf = { arg size, radius, beamDict, dim = 3, match = \amp, numChans, order = (AtkHoa.defaultOrder), window = \reg, phase = \lin, sampleRate, speedOfSound = (AtkHoa.speedOfSound);
var hoaOrder, dftFreqs;
var foclMags, beamMags, mags;
hoaOrder = HoaOrder.new(order);
dftFreqs = size.dftFreqs(sampleRate); // dft freqs
// focalisation?
(radius != nil).if({
// focal magnitude - collected by degree
foclMags = dftFreqs.collectAs({ arg freq;
hoaOrder.foclWeights(freq, radius, window, speedOfSound);
},
List
).flop.asArray; // collect as List, due to Array -flop bug
}, {
// or... just unity
foclMags = Array.fill((order + 1) * size, { 1.0 }).reshape((order + 1), size);
});
switch( beamDict.at(\beamShapes).size,
1, {
beamMags = hoaOrder.beamWeights(beamDict.at(\beamShapes).first, dim)
},
2, {
(beamDict.at(\edgeFreqs).size != 2).if({
Error("Must supply two edge frequencies for a two band shelf. Supplied: % ".format(beamDict.at(\edgeFreqs).size)).throw
}, { var freqs = beamDict.at(\edgeFreqs).sort;
var beamWeights = beamDict.at(\beamShapes).collect({ arg beamShape;
hoaOrder.beamWeights(beamShape, dim)
});
beamMags = (order + 1).collect({ arg degree;
Array.logShelf(size, freqs.at(0), freqs.at(1), beamWeights.at(0).at(degree).ampdb, beamWeights.at(1).at(degree).ampdb, sampleRate)
})
})
},
3, {
(beamDict.at(\edgeFreqs).size != 4).if({
Error("Must supply four edge frequencies for a two band shelf. Supplied: % ".format(beamDict.at(\edgeFreqs).size)).throw
}, { var freqs = beamDict.at(\edgeFreqs).sort;
var beamWeights = beamDict.at(\beamShapes).collect({ arg beamShape;
hoaOrder.beamWeights(beamShape, dim)
});
beamMags = (order + 1).collect({ arg degree;
Array.logShelf(size, freqs.at(0), freqs.at(1), beamWeights.at(0).at(degree).ampdb, beamWeights.at(1).at(degree).ampdb, sampleRate) *
Array.logShelf(size, freqs.at(2), freqs.at(3), 0.0, (beamWeights.at(2).at(degree) / beamWeights.at(1).at(degree)).ampdb, sampleRate)
})
})
}
);
// // normalization
// (match != \amp).if({
// mags = mags * mags.flop.collect({ arg item;
// ~matchWeight.value(item, dim, match, numChans)
// }).flop
// });
//
// mags;
// normalization - two cases
case
{ match.class == Symbol } { // single match
mags = foclMags * beamMags;
(match != \amp).if({
mags = mags * mags.flop.collect({ arg item;
~matchWeight.value(item, dim, match, numChans)
}).flop
})
}
{ ((match.class == Array) && (match.size == 2)) } { // dual match
// [ foclMags, beamMags]
};
mags;
/* now need to generate the kernel! */
}
)
/*
Set params
*/
~order = 1
~order = 3
~order = 5
(
~beamDict = Dictionary.with(*[
// \beamShapes-> [ \basic ],
// \beamShapes-> [ \energy ],
\beamShapes-> [ \basic, \energy ],
// \beamShapes-> [ \basic, \controlled ],
// \beamShapes-> [ \energy, \controlled ],
\edgeFreqs-> [ 700.0, 1400.0 ],
// \edgeFreqs-> [ 400.0, 8000.0 ],
// \beamShapes-> [ \basic, \energy, \controlled ],
// \edgeFreqs-> [ 700.0, 1400.0, 4000.0, 8000.0 ],
])
)
~win = \hp
~win = \reg
~radius = nil // no focalisation
~radius = ~order.asHoaOrder.radiusAtFreq(1400.0, ~speedOfSound); // set effective radius for 1400.0 Hz
~mags = ~foclShelf.value(~kernelSize, ~radius, ~beamDict, 3, \amp, nil, ~order, ~win, \lin, ~sampleRate, (AtkHoa.speedOfSound)) // normalize amplitude, aka pressure
~mags = ~foclShelf.value(~kernelSize, ~radius, ~beamDict, 3, \rms, nil, ~order, ~win, \lin, ~sampleRate, (AtkHoa.speedOfSound)) // normalize rms, energy of spherical harmonics
~mags.do({ arg item, i; (item.keep(200) + -180.dbamp).ampdb.plot(i.asString, minval: -20.0, maxval: 20.0)}) // plot in dB
~mags.do({ arg item, i; (item.keep((~kernelSize/2).asInteger + 1) + -180.dbamp).ampdb.plot(i.asString, minval: -90.0, maxval: 20.0)}) // plot in dB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment