View Single Post
Old 08-28-2009, 05:22 PM   #19
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,646
Default General JS synth implementation

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.
Tale is online now   Reply With Quote