> 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 -------------------------------------------------------------------------