I don't have any general optimizations, nor any ingenius DSP code snippets, but I do have a polyphone JS synth implementation. It receives MIDI notes in full polyphony (within the chosen note range), stores the notes in a buffer, and then caches this buffer for optimal speed. In this case sound is generated using a bunch of sinuses (with the frequencies for each note pre-calculated to speed up things a litle more), but you could easily replace that with any other sound generating code snippet.
Code:
// Sinas v0.0.1
// (c) 2009 Theo Niessink <http://www.taletn.com/>
// License: GPL <http://www.gnu.org/licenses/gpl.html>
desc:Simple stacked sine synth
in_pin:none
out_pin:Mono
out_pin:Mono
@init
bot_key = 36; // C2
top_key = 96; // C7
num_key = top_key - bot_key + 1;
// Pre-calculate the frequency for each key.
//freq_buf = 0;
key = 69 - bot_key; // A @ 440 Hz
// Calculate the top octave.
i = num_key;
loop(12, freq_buf[i -= 1] = 440.0 * 2 ^ ((i - key) / 12));
// Caclulate the lower octaves by dividing down the top octave.
loop(i, freq_buf[i -= 1] = 0.5 * freq_buf[i + 12]);
key_buf = freq_buf + num_key;
//memset(key_buf, 0, num_key);
cache_buf = key_buf + num_key;
//num_cache = 0;
pi2 = $pi * 2;
speriod = 1/srate * pi2;
//position = 0.0;
@block
while(
midirecv(ofs, msg1, msg23) ? (
event = msg1&$xF0;
velocity = msg23&$x7F00;
// Note On
event == $x90 && velocity ? (
key = msg23&$x007F;
key >= bot_key && key <= top_key ? (
key -= bot_key;
// Add the key to the cache.
key_buf[key] == 0 ? (
cache_buf[num_cache] = key;
num_cache += 1;
);
// Set the velocity value / (127 * 256) in the keyboard buffer.
key_buf[key] = 0.000030757874015748031496062992125984 * velocity;
);
) :
// Note Off
(event == $x80 || (event == $x90 && !velocity)) ? (
key = msg23&$x007F;
key >= bot_key && key <= top_key ? (
key -= bot_key;
// Remove the key from the cache.
loop(i = num_cache,
cache_buf[i -= 1] == key ? (
num_cache -= 1;
i < num_cache ? memcpy(cache_buf + i, cache_buf + i + 1, num_cache - i);
);
);
// Clear the velocity value in the keyboard buffer.
key_buf[key] = 0;
);
) :
// Control Change
event == $xB0 ? (
cc = msg23&$x007F;
// All Notes Off
cc >= 123 && (num_cache > 0 || position > 0.0) ? (
memset(key_buf, 0, num_key);
num_cache = 0;
position = 0.0;
);
);
midisend(ofs, msg1, msg23);
1; // Loop
);
);
@sample
spl0 = 0.0;
// Calculate a sample for each of the keys in the cache.
loop(i = num_cache,
key = cache_buf[i -= 1];
f = position * freq_buf[key];
spl0 += key_buf[key] * (sin(f) + sin(2 * f) + sin(3 * f) + sin(4 * f) + sin(5 * f));
);
spl0 *= 0.1;
spl1 = spl0;
position += speriod;
num_cache == 0 && position >= pi2 ? position -= pi2;
Without the cache the sound generating loop in the @sample section would look something like this:
Code:
loop(key = num_key,
velocity = key_buf[key -= 1];
velocity > 0 ? (
f = position * freq_buf[key];
spl0 += velocity * (sin(f) + sin(2 * f) + sin(3 * f) + sin(4 * f) + sin(5 * f));
);
);
This would make the @sample section considerably slower, especially in idle or with only a few keys playing. On my system the cached version takes 0.3% CPU time in idle, and 3.3% with 3 keys playing. The non-cached version takes 6.4% in idle, and 9.0% with 3 keys playing.