Quantization in ChucK, for that 8-bit sound

With the help of the ChucK mailing list, especially this post by Kassen, I put together this handy class, Cruncher, which quantizes at the desired bit level, and for kicks, adds some interesting distortion.

public class Cruncher {
    // connect with these
    Gain input;
    LiSa output;

    // private

    int bits;
    float levels;
    Gain mix;
    Step dc;

    // public

    fun void setBits( int num ) {
        num => bits;
        Math.pow( 2, bits ) => levels;

        // pre-calculate some constants for the loop
        Math.pow( bits - 1, 2 ) => float QUANT;
        (output.duration()/ samp) $ int => int NUM_SAMPS;
        2.0 / NUM_SAMPS => float STEP_SIZE;

        // fill LiSa buffer with quantization map
        for( int x; x< NUM_SAMPS; x++ ) {
            -1 + x * STEP_SIZE => float in; // calculate input value as [-1, 1)
            ( in * QUANT ) $ int / QUANT => float value; // quantize
            ulaw( value ) => value; // distort
            output.valueAt( value, x::samp ); // save
        }
    }

    // private

    fun float sgn( float f ) {
        return f >= 0 ? 1. : -1.;
    }

    fun float ulaw( float f ) {
        return Math.sgn(f) * Math.log( 1 + levels * Std.fabs( f ) ) / Math.log( 1 + levels );
    }

    fun void initialize() {
        input => output;
        dc => output;

        // configure LiSa
        second => output.duration;
        1 => output.sync;
        1 => output.play;

        // map input from [-1, 1] to (0, 1)
        .49 => input.gain;
        .5 => dc.next;

        // set default quantization level
        setBits( 8 );
    }

    initialize();
}

And here is a small patch that uses it (and requires you to load scale.ck ahead of time):

// ugens
Cruncher cruncher;
TriOsc osc;
ADSR env;

// patch
osc => env => cruncher.input;
cruncher.output => dac;

// configure
env.set( 1::ms, 40::ms, .1, 300::ms );
cruncher.setBits( 5 );

// GO!
Scale sc;
[ 0, 1, 3, 8, 6, 4 ] @=> int notes[];

while( true ) {
    for( 0 => int i; i < notes.size(); i++ ) {
        sc.scale( notes[ i ], sc.maj ) + 60 => Std.mtof => osc.freq;
        env.keyOn( 1 );
        second / 6 => now;
        env.keyOff( 1 );
        second / 7 => now;
    }
}

Hear the original, then the 5-bit version, then the 5-bit, distorted version.

Did I level up with this post?


Comments

Click here to view the comments on this post, or just send me an e-mail.