atk-sc3
atk-sc3 copied to clipboard
Implement meaningful error for Float -asHoaOrder ?
In case when one miscalculates the number of parameters in Hoa*
ugens, one might pass a float to the order
argument. The resulting error ERROR: Message 'asHoaOrder' not understood.
was confusing to me at first.
I'm not saying that floats should be allowed (e.g. using .asInteger
), since this could lead to silently erroneous behavior. However, I was wondering if -asHoaOrder
could be implemented for Float
s to throw a more meaningful message? (like HoaOrder needs to be an Integer
).
Example:
x.free; x = {HoaEncodeDirection.ar(DC.ar(0), 0, 0, AtkHoa.refRadius, 1)}.play;
// ^ one parameter too many
// we end up with AtkHoa.refRadius as the "order"
Result:
ERROR: Message 'asHoaOrder' not understood.
RECEIVER:
Float 1.500000 00000000 3FF80000
ARGS:
(...)
^^ ERROR: Message 'asHoaOrder' not understood.
RECEIVER: 1.5
@joslloand I'd be happy to submit a PR adding an Error message for Float -asHoaOrder
if you think this is a desired feature.
We might consider a test like
// pseudocode
Float:-asHoaOrder
if(myFloat == myFloat.asInteger) {^myFloat} {error("Fractional orders aren't supported")}
If fractional orders make sense in other contexts (analyzing effective decoder orders, for example), there may need to be more plumbing involved to isolate cases where fractional orders are not allowed.
Hm... I like the idea of accepting float representations of (integer) order as general, but I also remember that comparing equality of floats is generally not a good idea. Maybe something along the lines of what UnitTest -assertArrayFloatEquals
do would be more accurate?
if((myFloat - myFloat.asInteger).abs <= someSmallNumber) {^myFloat} {error("Fractional orders aren't supported")}
Actually... looking at this I see that HoaOrder can be instantiated with a float:
HoaOrder(1.2); // no error
Of course it would error out later on when trying to access other methods.
With this in mind, I think that it would be better to move the check for float to HoaOrder
, so that any possible future handling of floats is resolved there, and move -asHoaOrder
to SimpleNumber
instead of Integer
. What do you think?
if((myFloat - myFloat.asInteger).abs <= someSmallNumber) {^myFloat} {error("Fractional orders aren't supported")}
good idea!
I suppose the rest of the implementation question comes down to the fundamental question: should HoaOrder support float
representations for order? What do you think @joslloand ?
Somewhat relatedly, we have used float tolerance elsewhere: https://github.com/ambisonictoolkit/atk-sc3/blob/94b336028ec30434fb97750886ceb239a88790fc/Classes/Tests/AtkTests.sc#L58 Maybe we should promote this value to an Atk class var?
I suppose the rest of the implementation question comes down to the fundamental question: should HoaOrder support
float
representations for order? What do you think @joslloand ?
Just to be clear, I'm not saying it should support it necessarily, but if it won't work properly with a float (i.e. the current situation), it should check for it at the initialization time...
Yep, you're right to point out the inconsistency between instantiating HoaOder
with a float, and -asHoaOrder
not responding to a float, it's just brought up higher level question :)
I'm suggesting that we might want to support fractional/float orders, but I haven't thought much about use cases for fractional orders. If the use cases are few, then maybe it's supported only in those contexts (effective decoder order, as mentioned before, or franctional-order beam patterns).
I'm suggesting that we might want to support fractional/float orders,
I think that's good, and IMO this future direction would also support my suggestion to check for float inside HoaOrder
(we would remove that check once HoaOrder is ready for floats) and have both Integers and Floats respond to -asHoaOrder
.
Argh! Thanks @dyfer ;-)
Actually, this brings up a few things to consider that can probably be divided into practical / theoretical AND authoring / analyzing.
On the practical side, for instantiating HOA streams, we can only have integer orders (in terms of number of coefficients... weighting is another matter... see below). Theoretically, these directly relate to a boxcar, e.g., truncation, windowing. In our practical system, we want to know how big the signal set should be, so having a check for @dyfer's original error would be a good idea:
x.free; x = {HoaEncodeDirection.ar(DC.ar(0), 0, 0, AtkHoa.refRadius, 1)}.play;
// ^ one parameter too many
// we end up with AtkHoa.refRadius as the "order"
OK, so you asked for it ;-)
Having a closer look at HoaOrder
and -asHoaOrder
:
~orderTest = 1.5.asHoaOrder // fails, only works for Integer
whereas:
~orderTest = HoaOrder.new(1.5) // instantiates, but bad...(unless you know what you're doing)
Where the latter fails makes good sense:
~orderTest.indices // fails
~orderTest.sph // fails
~orderTest.normalisation // fails
~orderTest.proxWeights(440, 1.0, AtkHoa.speedOfSound) // fails
Where the latter succeeds makes some sense (if you know what you're doing):
~orderTest.size // useful?
~orderTest.radiusAtFreq(440, AtkHoa.speedOfSound) // useful?
~orderTest.spreadE // useful?
I believe I left in the ability to declare floating point fractional orders as a way for expert users (actually, me) to have access to rule of thumb comparisons when designing crossovers, frequency dependent radial filters, etc.
More theory...
In practice, the idea of fractional orders are not super standardized in a general way... which makes some sense, as what we're really doing is smoothing in the spatial domain. What we're really talking about is different kinds of LP filtering of the SH.
We DO have two standardized LP frequency independent (truncated, fractional order) filters:
- energy weighting
- controlled opposites weighting
We ALSO have some specialized LP frequency dependent (fractional order) filters. These are the focalisation filters:
- Regularised
- (Butterworth) High Pass
- Cosine
- Sine
More practice...
If there is a limited use for expert users to instantiating a fractional instance of HoaOrder
, why aren't we throwing a warning? Well... if we're attempting to design a custom focalisation filter (bin by bin), we'll have the post window filled with warnings... which won't be pretty. I'm fairly sure that's whey I didn't put one in.
And... my assumption was that novices would never go into the deep water.
So... some possible solutions:
- for pseudo-UGens, check and throw an error for floats
- update the SCHelp for
HoaOrder
to state fractional orders are accepted, but WARN not all instance methods are supported and/or throw in checks where not. (I think the checks could become more tedious... and I think I'd prefer to just warn users that they being handed a length of rope, which may result in their own hanging.)
Thanks @joslloand
My main point is that -asHoaOrder
should be implemented for either SimpleNumber
or for both Integer
and Float
, Afterwards we can deal with floating point orders as we see fit.
So... some possible solutions:
- for pseudo-UGens, check and throw an error for floats
- update the SCHelp for HoaOrder to state fractional orders are accepted, but WARN not all instance methods are supported and/or throw in checks where not. (I think the checks could become more tedious... and I think I'd prefer to just warn users that they being handed a length of rope, which may result in their own hanging.)
Either would be fine, with different caveats/amount of work.
I'd also suggest a possible 3rd option (not necessarily better, but different):
3. HoaOrder.new(order, allowFloat: false);
which would throw an error if the order is float and the flag is default (false
). Advanced users could then instantiate the class with the flag set to true
if needed (in which case there would be no error and probably no warning either). SimpleNumer -asHoaOrder
would call the default option and thus throw on float orders.
I'd also suggest a possible 3rd option (not necessarily better, but different): 3.
HoaOrder.new(order, allowFloat: false);
which would throw an error if the order is float and the flag is default (false
). Advanced users could then instantiate the class with the flag set totrue
if needed (in which case there would be no error and probably no warning either).SimpleNumer -asHoaOrder
would call the default option and thus throw on float orders.
@dyfer, I'm feeling an allowFloat
flag would be the most attractive option, as it makes everything explicit.
If moved to SimpleNumber
, -asHoaOrder
for a Float
will end up being nearly as verbose. E.g.:
-
HoaOrder.new(3.2, true)
-
3.2.asHoaOrder(true)
More verbose is fine for expert users, though!!
As an aside, by adding an allowFloat
flag to HoaOrder
, we'll lose the explicit symmetry w/ the related classes:
Another side thought, while it would be great to support mixed orders (another kind of fractional order) like the ADT does for decoding, practically this is a headache best left to the ADT!!!
As an aside, by adding an allowFloat flag to HoaOrder, we'll lose the explicit symmetry w/ the related classes:
Again, not necessarily a better option, but we could consider having *allowFloat
as a class variable for HoaOrder
.
Pros:
- set once for a project etc
-
-asHoaOrder
would not need a flag - symmetry with aforementioned classes
Cons:
- lack of granularity
- less explicit interface (i.e. if one sets it globally and forgets it, there are no checks in place)
Having been away from SC for a while and returning now, I have to say the proliferation of method arguments or state flags to fork functionality doesn't feel great (looking back at some of my more elaborate class methods) making future development more cumbersome, especially with experimental/expert functionality like fractional orders.
Distilling a bit here, we know now that fractional/tapered orders is something to support. Am I correct that an implementation as SimpleNumber
would mean all current functionality is retained and we can patch in context-specific safeguards?
@joslloand's helpful theory discussion points toward a division of instantiating stream channels and beam pattern design... For example, a new method could be added: SimpleNumber:-asHoaSignalOrder
, which performs
if((myFloat - myFloat.asInteger).abs <= someSmallNumber) {^myFloat} {error("Fractional orders aren't supported")}
Not yet a fully flushed out idea, but seems like it's a good direction?
Relatedly:
- While looking into this, i noticed that given the use of
HoaUGen:*confirmOrder
, it may be better called*confirmSignalOrder
. - We may be bumping into a limit of a definition, and perhaps it's asking too much of the concept of 'order', as is widely understood, to be fractional. There may be room for a new concept/term that draws from factional polynomial degrees, etc., the representation for which in the ATK would be more sandboxed in the 'expert' corners of the library. ... but that's a wider research topic ;)