Skip to content

Instantly share code, notes, and snippets.

@amokan
Last active December 9, 2019 19:11
Show Gist options
  • Save amokan/13acaa559c6868d2a290 to your computer and use it in GitHub Desktop.
Save amokan/13acaa559c6868d2a290 to your computer and use it in GitHub Desktop.
Expert Sleepers Disting simple example of audio processing

Some really basic examples of audio processing code for the Expert Sleepers Disting eurorack module.

You can find the base repo for a blank audio passthrough project at https://github.com/expertsleepersltd/disting

You will need to have a 'PICKit 3' USB programmer and the MPLAB IDE (free Microchip dev tools) installed. I grabbed my PICKit from Mouser a while ago and it should be easy to find, wherever you are.

If you want to reflash the stock firmware, you can download that from Expert Sleepers here - http://www.expert-sleepers.co.uk/downloads/firmware/disting_firmware_1_0.zip

Note that the stock firmware is already compiled, so you'll just want to flash it using the 'mplab_ipe' tool.

The example(s) below are meant to just replace the existing doAlgorithm0() function in the helloDisting project found in the official repo unless otherwise mentioned.

I do all of my testing with a cheap Owon digital oscilliscope and the Mutable Instruments Eurorack module testing tool http://mutable-instruments.net/module_tester.

So have fun, try different things with this cool little module and share your results!

Algorithm 'Slots'

If you want to have multiple algorithms on your Disting, here is the approach I have been using.

Near the bottom of int main() you will find this:

// main loop (never quits)
for ( ;; )
{
    // run the processing loop for the chosen algorithm
    switch ( selector )
    {
        case 0:
        default:
            doAlgorithm0();
            break;
    }
}

You can add more case statements going from 0 (which falls through to default on the example above) to 15. This makes sense as there are 16 selections on the Disting.

Having said that, here is an example of my switch/case:

// main loop (never quits)
for ( ;; )
{
    // run the processing loop for the chosen algorithm
    switch ( selector )
    {
        case 0:
            doAlgorithm1();
            break;
        case 1:
            byteBeatWaveshaper();
            break;
        case 2:
            sineosaur();
            break;
        case 3:
            dumbWaveshaperTwo();
            break;
        default:
            byteBeatWaveshaper();
            break;
    }
}

Note that the display LEDs will need to be coded to respond to the 'select' knob changes so don't expect to see them light up. I do have a LED switching function now that doesn't work real well, but it does change the lights. If you're dying to have that, drop me a line.

Bytebeat Waveshaper

This is something I was just tinkering with while drinking morning coffee with the Disting plugged into my scope. No real thought or plan, but it generates some interesting sounds at times.

Runs a silly bytebeat-like audio algorithm using the Z input as a modulator for various parameters. Can generate audio on it's own without any input.

Basic Usage

  • Z pot/input - modifies a couple parameters in the algorithm and also modifies the value of t on each cycle
  • X and Y inputs - optional audio inputs that will be summed with the generated audio
  • A and B outputs - the generated audio is sent to both, but mixed with the related input (X -> A, Y -> B)

Detailed Explanation/Ideas

I had the best success running audio rate signals (standard shapes like Sine, Saw, Ramp) into the Z input. Turning the Z pot clockwise seems to make the output closely resemble the input shape with some digital artifacts. Slowly turning counter-clockwise will take you into noise territory. LFOs sent into Z will present more of the 'bytebeat' digital vibe. Sending something like a square wave LFO into Z and you will notice the bytebeat sequence only seems to happen on the up cycle of the square.

When sending audio-rate signals into Z, I found it also interesting to split the signal and send into input X or Y. It will do a simple sum mix of the generated audio in addition to the two inputs (X & Y). This is optional, but will allow the output to contain a bit more of your source signal.

Also note that you don't need anything plugged into Z at all and can simply ride the potentiometer.

void doAlgorithm0()
{
  // setup
  DECLARATIONS();

  while ( 1 )
  {
    // wait for new audio frame
    IDLE();

    // read the inputs
    int vL = inL;
    int vR = inR;

    // The scaling here is real cheesy, but seems to work alright.
    // I need to read up on the ADC inputs for this more.
    int varZ = pot >> 6;
    int t = time >> (varZ - 2);

    // note that most 'bytebeat' examples you find will work in place of this.
    // Substitute different numeric values with 'varZ', which will allow that param to be controlled 
    // by the 'Z' pot/input on the disting
    int vTrash = (t*(((varZ+5)&t>>(varZ-2))%12)&varZ-(t>>5|t>>12)|t*(t>>varZ)*32);

    // write the outputs mixed with inputs of X and Y.
    // comment out these lines and see below if you just want to play the generated audio
    outL = vTrash + vL;
    outR = vTrash + vR;
    
    // uncomment to play generated audio without mixing what is plugged into X and Y on the disting
    // outL = vTrash;
    // outR = vTrash;

    // loop end processing
    // (including reading the ADC channels)
    LOOP_END();
  }
}

Sinosaur Animated Waveform Thing

My attempt at sinewave synthesis 101 without a lookup table. Not effecient and anyone in the know would laugh at this code, but it makes some interesting tones. Feel free to get rid of my noise and fix/optimize my datatype usage here as I know it is not good. I am but a simple caveman.

Basic Usage

  • Z pot/input - freq (voltage) modulation of a theoretical range of about 800hz
  • X input - main freq source for the waveform (required)
  • Y input - another modulation source. I just tried some random stuff here and if positive, it subtracts from X and if negative it adds to X. But I'm confusing myself even writing this.
  • A and B outputs - the generated audio waveform

Add this stuff to the top of your main.c file:

#include <math.h>

#ifndef M_PI
#define M_PI (3.14149265358979321)
#endif
#define TWOPI (2.0 * M_PI)

and define a global curphase variable:

double curphase = 0.0;

Then either replace your doAlgorithm0() function with the function below (and rename) or just add this function and call it near the bottom of int main() (but be sure to comment out doAlgorithm0())

void sineosaur()
{
    DECLARATIONS();

    while ( 1 )
    {
        IDLE();

        float in = ((inL + .5)/(0x7FFF + .5));
        float mod = ((inR + .5)/(0x7FFF + .5));

        double rate = 40000.0;
        double potfreq = (float)pot - 128;
        double freq = (double)in;
        
        if(potfreq > 0.0)
            freq += potfreq;

        if(mod > 0.01)
            freq -= mod;

        if(mod < -0.01)
            freq += mod;

        double incr = freq * TWOPI / rate;

        float waveform = (float) sin(curphase) * (in / 3);
        int output = (waveform) * (0x7FFF + .5) - .5;

        outL = output;
        outR = output;

        curphase += incr;
        if(curphase >= TWOPI)
            curphase -= TWOPI;

        if(curphase < 0.0)
            curphase += TWOPI;

        LOOP_END();
    }
}
@JamesHagerman
Copy link

MAN is that a good writeup! Thank you so much!

@amokan
Copy link
Author

amokan commented Mar 1, 2015

Glad to hear it helped someone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment