I wrote;
Well, as you are basically using a single STKInstrument per voice here I don't think there is a need to have a shred per voice at all in this case. Just a array of a dozen or so UGens with some infrastructure to keep track of which ones are free and similar voice-cycling infrastructure should do the trick. That would mean a single shred and no garbage at all.
I'm pasting a edit of the last version Hans send to the list based on this strategy below. the basic strategy is starting a few voices, hopefully a amount our cpu can take. When a new key is pressed it will be assigned to the voice that already had that voice linked to it. If no such voice is found it'll take the voice that's not currently playing and gone longest since the last note off. When all voices are currently playing it'll steal the one that has gone longest since the last note on. No sporking and probably no garbage (I didn't verify the second part of that claim). There are likely some issues with this; while the strategy should be sound I wrote this quite quickly. I also changed the indentation style after getting confused about a "else" in the main loop. This is roughly the strategy hardware synths take; the amount of cpu used should be more or less static, clearly something more advanced is in order when we need to run several instruments on the same computer but I took this setup to be meant for a solo instrument. No warranties, no refunds, please mind your speakers and neighbours. Cheers, Kas. //======================8<============================= //set this to what your computer can take 5 => int NR_OF_VOICES; // The device number to open 1 => int deviceNum; //voice cycling by Kas, everything else by Hans 2009 /* Example of a generalized diatonic (or extended mentone) key map. Transposition takes place by keyboard translation, so each scale and chord need only one fingering pattern. The keyboard layout is altering pitches as follows: ^ # / . -> M / \ b v v m where M (resp. m) is the major (resp. minor) second, and the the sharp # (resp. flat b) raises (resp lower) with the interval M - m, that is, the difference between the major and minor seconds. Resulting key pattern: C# D# E# C D E F# G# A# B# Cb Db Eb F G A B Fb Gb Ab Bb C' M is set to 2^(5/31) and m = 2^(3/31), the E31 approximation of Renaissance meantone. Middle A = 440 Hz, and is in US keyboard layout on key "J". */ // keyboard Hid kb; // hid message HidMsg msg; // Open keyboard. if(!kb.openKeyboard(deviceNum)) me.exit(); // Successful! Print name of device. <<< "Keyboard '", kb.name(), "' ready." >>>; // Key x-axis coordinate int x[256]; 0 => x[100] => x[53]; 1 => x[41] => x[49] => x[30] => x[20] => x[4] => x[29]; 2 => x[58] => x[31] => x[26] => x[22] => x[27]; 3 => x[59] => x[32] => x[8] => x[7] => x[6]; 4 => x[60] => x[33] => x[21] => x[9] => x[25]; 5 => x[61] => x[34] => x[23] => x[10] => x[5]; 6 => x[62] => x[35] => x[28] => x[11] => x[17]; 7 => x[63] => x[36] => x[24] => x[13] => x[16]; 8 => x[64] => x[37] => x[12] => x[14] => x[54]; 9 => x[65] => x[38] => x[18] => x[15] => x[55]; 10 => x[66] => x[39] => x[19] => x[51] => x[56]; 11 => x[67] => x[45] => x[47] => x[52] => x[229]; 12 => x[68] => x[46] => x[48] => x[49]; // Key y-axis coordinate int y[256]; 0 => y[49] => y[58] => y[59] => y[60] => y[61] => y[62] => y[63] => y[64] => y[65] => y[66] => y[67] => y[68] => y[69]; 1 => y[100] => y[30] => y[31] => y[32] => y[33] => y[34] => y[35] => y[36] => y[37] => y[38] => y[39] => y[45] => y[46]; 2 => y[20] => y[26] => y[8] => y[21] => y[23] => y[28] => y[24] => y[12] => y[18] => y[19] => y[47] => y[48]; 3 => y[4] => y[22] => y[7] => y[9] => y[10] => y[11] => y[13] => y[14] => y[15] => y[51] => y[52] => y[49]; 4 => y[53] => y[29] => y[27] => y[6] => y[25] => y[5] => y[17] => y[16] => y[54] => y[55] => y[56] => y[229]; // Key serving as origin 7 => int x0; 3 => int y0; int init[256]; // Tuning frequency 440.0 => float f0; Voice voices[NR_OF_VOICES]; JCRev r => dac; r => Echo e => Echo e2 => dac; // set delays 240::ms => e.max => e.delay; 480::ms => e2.max => e2.delay; // set gains .6 => e.gain; .3 => e2.gain; .05 => r.mix; for(int n; n < NR_OF_VOICES; n++) { voices[n].signal => r; 1 => voices[n].signal.noteOff; } while(true) { kb => now; while(kb.recv(msg)) { // Print. /* not printing as writing to the screen can cause a delay as debated on the list if(msg.isButtonDown()) <<< " key", msg.which, "down (", x[msg.which], ",", y[msg.which], ")" >>>; else <<< " key", msg.which, "up (", x[msg.which], ",", y[msg.which], ")" >>>; */ // Play a note. if (!((x[msg.which] == 0) && (y[msg.which] == 0))) { if(msg.isButtonDown()) { int assigned; //check whether one of our voices already had this pitch assigned for(int n; n< NR_OF_VOICES; n++) { if (voices[n].key == msg.which) { f0 * Math.pow(2, (5*(x[msg.which] - x0) + 3*(y[msg.which] - y0))/31.0) => voices[n].signal.freq; 1 => voices[n].playing; msg.which => voices[n].key; now => voices[n].last_NoteOn; 1 => voices[n].signal.noteOn; 1 => assigned; break; } } //otherwise take the voice that has been silent for longest. if(!assigned) { now => time voice_age; int oldest_voice; for (int n; n< NR_OF_VOICES; n++) { if ( !voices[n].playing && voices[n].last_NoteOff < voice_age) { voices[n].last_NoteOff => voice_age; n => oldest_voice; } } f0 * Math.pow(2, (5*(x[msg.which] - x0) + 3*(y[msg.which] - y0))/31.0) => voices[oldest_voice].signal.freq; 1 => voices[oldest_voice].playing; msg.which => voices[oldest_voice].key; now => voices[oldest_voice].last_NoteOn; 1 => voices[oldest_voice].signal.noteOn; 1 => assigned; } //if that too fails we'll need to "steal" a voice //we'll take the one that has been playing for the longest time //"real" synths tend to reserve the highest and/or lowest pitch //such cleverness is left as a excersise for the reader if(!assigned) { now => time voice_age; int oldest_voice; for (int n; n< NR_OF_VOICES; n++) { if ( voices[n].last_NoteOff < voice_age) { voices[n].last_NoteOff => voice_age; n => oldest_voice; } } f0 * Math.pow(2, (5*(x[msg.which] - x0) + 3*(y[msg.which] - y0))/31.0) => voices[oldest_voice].signal.freq; 1 => voices[oldest_voice].playing; msg.which => voices[oldest_voice].key; now => voices[oldest_voice].last_NoteOn; 1 => voices[oldest_voice].signal.noteOn; 1 => assigned; } } else { for (int n; n< NR_OF_VOICES; n++) { if(voices[n].playing && voices[n].key == msg.which) { 1 => voices[n].signal.noteOff; now => voices[n].last_NoteOff; 0 => voices[n].playing; } } } } else { <<<"presumably keys reserved for control go here?", "">>>; } } } class Voice { BeeThree signal; int playing; int key; time last_NoteOn; time last_NoteOff; }