> As i-rate statements in a kopcode, these lines will never execute in
> sfront, and produce a warning message -- in compliance with the standard
> as I read it currently, as we discussed off-line.
>
"Everything Not Explicitly Forbidden is Mandatory"
Sign outside the ant nest.
-- T. H. White. The Once and Future King.
Quoted by Bjarne Stroustrup in the C++ ARM.
I have to admit, I cannot find an explicit statement in the standard that
says that support of mixed rate opcodes is mandatory. However, there are
numerous cases where this is implied, and it is nowhere forbidden.
(1) SAOLC does it. SAOLC is included in the standard by reference. If SAOLC
does it, you have to do it too. (ignoring, of course, explicit discrepencies
between the reference implementation and the standard, which will,
presumably be corrected over time). I have sample files containing
mixed-statement-rate opcodes that execute correctly on SAOLC (although
rather slowly). I couldn't swear that SAOLC hasn't rewritten all my ksigs as
asigs, but I think that unlikely.
(2) If opcodes were supposed to run at a uniform rate, why put rate tags on
arguments at all? you could quite reasonably assume that the rate of
arguments is the same as the rate of the opcode tag. e.g. aopcodes would
have only asig arguments since they can't do any processing of arguments at
a slower rate, so why bother?
(3) 6.8.7.6 "No statement shall be faster than the rate of the opcode". Note
the lack of the resitriction that no statement shall be slower than the rate
of the opcode.
(6) The construct is grammatically legal. In the absense of a semantic
restriction the construct is semantically legal as well. What you decide to
do semantically, I suppose is your business, but accepting it, giving a
warning, and doing nothing seems dubious. If you're going to do nothing,
then at least make it an error, not a warning.
Are there ambiguities? No. Clause 6.8.8.6.4 states: "it is not permissible
for the block of code governed by the if statement to contain statements
slower than the guard rate". If this applies to statements within UDOs
contained in the block of code, then assignments to sub-guard-rate arguments
(if these are allowed at all) are forbidden. I agree. The spec does not
forbid sub-guard-rate arguments, but isn't forthcoming on how these should
be handled. That's an ambiguity, I think. How to handle them? Either
evaluate the expression prior to evaluation of the guard statement at the
appropriate lower-than-guard-rate execution pass, and promote them the same
way you do with sub-statement-rate expressions already; or, explicitly make
them illegal in the next version of the standard What if somebody copies out
to the variable? A: It can't be done. That could only be done in an
assignment statement with rate less than the guard rate.
Are there problems with oparrays? Oh yes, definitely. Proposed solution? The
following:
(1) The index to an oparray should place a lower-bound rate restriction on
statements within the oparray. If the index is k-rate than the opcode may
not contain i-rate expressions. If the index is a-rate, then the opcode may
not contain a-rate or k-rate expression.
(2) The declaration for fracdelay should be changed from
aopcode fracdelay(ksig method, xsig p1 [, xsig p2])
to
aopcode fracelay(asig method, asig p1, [, asig p2])
The SAOLC implementation currently ferets away the ksig method in a
mysterious peice of storage, and then only uses the method data during
a-rate evaluation. The same is true of p1 and p2. The standard does not
currently describe the mechanism whereby a ksig argument is passed to an
opcode that executes only at a-rate (sample files contain use of fracdelay
within a-rate-guarded while loops, so clearly SAOLC permits this). The
method argument clearly can and does change at a-rate (with respect to the
opcode state referenced by the indexed oparray) in the sample files. It also
can and does change within a-rate-guarded while statements. This should be
corrected.
e.g.:
if (iCondition) {
SomeOpArray[kIndex](kArg, aArg); // legal. Execute k pass and then
execute a-pass.
}
aResult = ASomeOpArray[0](iArg); // Legal. Execute i, k and a pass on
SomeOp[0];
fracdelay[0](2, kTap); // 2 and tap promoted to a-rate to match formal
arguments.
if (kCondition) {
KSomeOpArray[kIndex](iArg); // illegal. i-rate argument k-rate
guarded.
}
kIndex = 0;
while (kIndex < 10) {
KSomeOp[kIndex](
kIndex = kIndex+1;
}
(3) Rate restrictions on statement execution imposed by if, else, while and
oparray should be extended to the rate of formal arguments as well. It
doesn't make sense to assign a k-rate expression to a k-rate formal argument
within an a-rate-guarded code block.
I'm really not sure I understand what the issue is from an implementation
point of view. The things I thought were poisonously difficult in the
implementation of SAOL were: determination of return widths, particularly
wrt/ returns derived from input[] (which you've handled already). Multi-pass
rebinding of expression widths, which you've already dealt with; mixed-rate
expressions within statements (which you've implemented); constant folding
(which you've done already).
You say that you've implemented sophisticated optimizations like doing usage
analysis of expressions and l-value assignments to determine whether a
variable has integer or floating point storage type.
So what's so difficult about evaluating opcodes with mixed statement rates?
Last but not least, there's the issue of performance and authorability.
You raised the concern that you were unsure how you would teach high-school
students to write SAOL programs. Teaching them nightmare programming
practices isn't the solution. I'm more concerned about how professional
programmers are going to author SAOL streams that run in remotely real-time
on machines in the remotely near future.
The way you teach high-school students to write SAOL is that you give them a
well-crafted toolkit that's easy to use. A library that contains
straighforward library functions like aopcode SquareWave(xsig cps) that
gives them a *nice* bandlimited square wave instead of a nasty OPL3 hard
squarewave; or kopcode ADSR(ivar attack, ivar decay, ivar sustain, ivar
release, ksig bMidiReleased). Heck. I'm not sure that *I* could write a
decent ADSR in saol; no way a highschool student could. They write
"output(SquareWave(cpsmidi(midiNote)*Lfo(5,0.1))*ADSR(0.1, 0.5, 3, 0.8,
released))", perhaps muddle through routing the result to a reverb unit and
leave suitably edified and rewarded for their efforts.
Your version of gen_allpass (used by the small_room and large_room reverb)
in elpelele.saol runs on my PII 333 laptop at 400% of real-time. My version
runs at 15% of realtime. Why? Because I precalculated the arguments to
fracdelay at i-rate. You say: Just write two halves for the library. I say:
Can't be done. I have 8 arrays and 4 tables, each of which would have to be
passed as copy-in-copy-out parameters, presumably at a-rate if the UDO rule
holds (although that probably can't be done, because the arrays are
calculated at i-rate, and used at a-rate, and we seem to have already
esablished that would raise the question of when exactly does an i-rate
array actually get copied into an aopcode guarded by an a-rate while
statement.
Regards,
Robin Davies.
This archive was generated by hypermail 2b29 : Mon Jan 28 2002 - 12:03:55 EST