> Here's my basic architecture (criticism welcome):

One comment, remember that effects instruments can't have
their state updated at the k-rate via labelled control
statements, because there is no label on the effects
instr! Instead, for maximum portability, what you want
to do is to have a ksig global variable, and your control
driver write to it, and have your effects instrs import
it. Also, if you're using MIDI, the MIDI Master Channel's
standard name appears in the effects instr. And, you might
consider using the params[] standard name too:

imports exports ksig params[128]

that your control driver can update each kcycle, as described:

http://www.cs.berkeley.edu/~lazzaro/sa/sfman/devel/cdriver/data/index.html#bifs

although this has portability issues. 

> Right, I guess my question is, if I have an effects instrument "dly"
> that is in route and send statements, will I lose that routing if I
> release and then re-instantiate it, or is there a way to ensure that a
> new "dly" takes the place of the old one on the bus?  I thought that
> the only way to instantiate effects instruments was in send statements.

No, effects instruments get instantiated once, at startup, and the
bussing topology is fixed. As I mentioned in the earlier reply, for
your specific problem, a tapped delay line is the solution, that
is instantiated once at its maximum length, and then tapped at the
appropriate place to get the dynamic delay you want at the moment.

In general, though, you might find yourself in a situation where you
have a series of send/route statements that set up and audio signal
processing chain from input_bus to output_bus, but you want to tap
off that audio into an arbitrary instrument in the system. In this
case, the best thing to do is to either use a global wavetable or
a global ksig array as a buffer region, the basic architecture would
look something like this:

globals {
ksig lastkcyc[100]; // 100 audio samples per control period
krate 441;
srate 44100;
send(dispatch; ; input_bus);
sequence(dispatch, receiver);
}

instr dispatch ()

{
  ksig holding[100];
  ksig kidx;
  asig aidx;
  imports exports lastkcyc[100];

  // pushd last cycle into globals

  kidx = 0;
  while (kidx < 100)
   {
     lastkcyc[kidx] = holding[kidx];
     kidx = kidx + 1;
   }

  // at a-rate, buffer next cycle up

  holding[aidx] = input[0];  // mono input_bus assumed here
  aidx = (aidx == 99) ? 0 : aidx + 1;

}

instr receiver ()

{
   ksig imports lastkcyc[100];

   // at kcycle, read in lastcyc and prepare for
   // the arate, no need to make a local copy

   // note sequence() statement ensures ordering
   // is correct for this to work
}

This was all written on the fly and not tested or optimized.
But its rare that you'll need to use a technique like this,
only in situations where you want your SAOL code to behave
like a patch bay for effects that you can plug and unplug
on the fly, and even then you might be better off just using
a single effects instr, and doing the patch bay inside of
it, using user-defined aopcodes()'s to do the different
effects. 

Also, note the subtle way the code above is written, with
respect to the sequence order and such. The SAOL execution
ordering is quite explicit about what happenes when and
how when it comes to ksig imports and exports, this was
all purposely done so that compilers can do block coding
on the a-rate section without worrying about the effects
of inter-instrument communications. see:

http://www.cs.berkeley.edu/~lazzaro/sa/book/append/rules/index.html

all of the links under "Decoder Execution Order", to see
the logic behind this.

-------------------------------------------------------------------------
John Lazzaro -- Research Specialist -- CS Division -- EECS -- UC Berkeley
lazzaro [at] cs [dot] berkeley [dot] edu     www.cs.berkeley.edu/~lazzaro
-------------------------------------------------------------------------

Reply via email to