// -------------------------------------- /*
- match energy across front stage ( 0 & pi/2)
custom five channel "line array" decoder / ~pantoNumChannels = 8; ~pantoOrientation = \point; // need a center speaker / for discussion on decoder k, see this:
https://depts.washington.edu/dxscdoc/Help/Classes/FoaDecoderMatrix.html#Decoder%20k */
// energy beam performs "best" ~k = \energy; // most "focus", super-cardioid
// design panto decoder ~pantoDecoder = FoaDecoderMatrix.newPanto(~pantoNumChannels, ~pantoOrientation, ~k);
// extract matrix ~pantoMatrix = ~pantoDecoder.matrix
// use dominance to match center / side gain (0, pi/2) ~centerGain = 3.0677817524435.neg / 4; // in dB - energy energy <-- ~dominanceMatrix = FoaXformerMatrix.newDominate(~centerGain).matrix; /* Discard Z from dominance matrix... ... as the ATK's FOA panto convention doesn't include Z, for efficiency purposes.
NOTE: the ATK's HOA convention does include Z! */ ~dominanceMatrix = ~dominanceMatrix.getSub(0, 0, 3, 3); ~pantoMatrix = ~pantoMatrix.mulMatrix(~dominanceMatrix);
// design new truncated matrix /* channel ordering: [ farLeft, left, center, right, farRight ] */ ~linearMatrix = Matrix.with([ ~pantoMatrix.getRow(2), // farLeft ~pantoMatrix.getRow(1), // left ~pantoMatrix.getRow(0), // center ~pantoMatrix.getRow(7), // right ~pantoMatrix.getRow(6), // farRight ]);
// specify loudspeaker positions /* this step is really just a convenience... for analysis
should match actual target array
the dominance gain selected above matches for the specified value below */ ~linearNumChannels = 5; // ~linearSpread = 90.degrad; // in radians ~linearSpread = 120.degrad; // in radians ~linearDirections = Array.series(~linearNumChannels, ~linearSpread / 2, ~linearSpread.neg / (~linearNumChannels - 1));
// the new decoder!! ~linearDecoder = FoaDecoderMatrix.newFromMatrix(~linearMatrix, ~linearDirections);
/* the resulting decoder can be saved via -writeToFile */
/* analyze
... need to convert to HOA to use the HOA analysis tools */ ~foaMatrix = ~linearDecoder.matrix;
// append zeros for Z (that's what ATK's HOA convention expects!) ~foaMatrix = Matrix.with((~foaMatrix.asArray.flop ++ [ 0.0.dup(5) ]).flop);
// convert from foa to hoa1 ~order = 1; ~hoaMatrix = ~foaMatrix.mulMatrix(HoaMatrixDecoder.newFormat(\fuma, ~order).matrix);
// make HOA decoder for analysis ~hoaDecoder = HoaMatrixDecoder.newFromMatrix(~hoaMatrix, ~linearDirections, ~order);
// analyze ~analysisDirections = Array.series(180).degrad; // 1/2 way round (center front / center back) ~linearAnalysis = ~hoaDecoder.analyzeDirections(~analysisDirections); // test...
// plot ~linearAnalysis[\amp].ampdb.plot("%: amp".format(~k), minval: -40, maxval: 10); ~linearAnalysis[\energy].ampdb.plot("%: energy".format(~k), minval: -40, maxval: 10); ~linearAnalysis[\rE][\directions].raddeg.flop.first.plot("%: rE angle".format(~k), minval: 0, maxval: ~linearSpread.raddeg / 2); ~linearAnalysis[\rE][\magnitudes].plot("%: |rE|".format(~k), minval: 0, maxval: 1);
/* discussion
- amp & energy performance good (fairly constant) across the front stage
- imaging angle is max at source angle ~145deg, then rapidly returns to center
- image remains relatively stable until ~145deg, then rapidly decreases */