OSCtoCV Library

Dependents:   OSCtoCVConverter

OSCtoCV_Euclidean.cpp

Committer:
casiotone401
Date:
2016-01-28
Revision:
1:981b62bb5c87
Parent:
0:cd43a974c54c
Child:
2:9fa7540890c9

File content as of revision 1:981b62bb5c87:

/*
 OSCtoCV_Euclidean.cpp 
*/

#include "mbed.h"
#include "OSCtoCV_Euclidean.h"
#include "OSCtoCV.h"


//-------------------------------------------------------------
// Global Variables

int channels = MAXCHANNELS;
unsigned int beat_holder[MAXCHANNELS];
unsigned int channelbeats[MAXCHANNELS][5];

bool pulses_active = false; // is active while a beat pulse is playing 
bool lights_active = false;
int pulse_length = TRIGGER_DURATION; //pulse length

unsigned int last_read[MAXCHANNELS];
unsigned int last_changed[MAXCHANNELS];
unsigned int last_sync;

unsigned int euc_time;

int masterclock;

//-------------------------------------------------------------
// Euclidean Sequencer

void EuclideanSeq(int trigger, bool reset, bool gatesoff, bool auto_offset) {
    /*
       What's in the loop: 
       Update euc_time variable 
       Check to see if it is euc_time go go to sleep 
       Changes routine - update beat_holder when channelbeats changes - triggered by changes == true
       Trigger routines - on trigget update displays and pulse
       Read encoders 
       Read switches
     */
    
    int offset;
    static uint8_t nn, kk, oo;
    static unsigned int cnt_auto_offset;
    static uint8_t changes[MAXCHANNELS] = {0};
    static int nknob, kknob, oknob;
    static int _nknob, _kknob, _oknob;
    static bool triggerState = false;
    
    uint8_t i, ch;
    uint8_t maxn = MAXSTEPS; // maximums and minimums for n and k
    uint8_t minn = 1;
    uint8_t mink = 1; 
    uint8_t mino = 0; 
    
    static uint8_t active_channel;  
    
    euc_time = gTimer.read_ms();

    nn = channelbeats[active_channel][0];  
    kk = channelbeats[active_channel][1]; 
    oo = channelbeats[active_channel][3];
    
    // Stop & Reset (gCtrlSW[0])
    if (reset) {
        
        for (ch = 0; ch < channels; ++ch) {
            
            for (i = 0; i < MAXSTEPS; ++i) {
                
                for (ch = 0; ch < channels; ++ch) {
                    sendMes.setTopAddress(SetMatrixAddress(ch * 2 + 1, i, EUCLID)); // ch * 2 + 1
                    sendMes.setArgs("i", 0);
                    osc.sendOsc(&sendMes);
                }
                
            }
            
        }
        
        masterclock = cnt_auto_offset = active_channel = channelbeats[ch][2] = 0;
        
        return; 
    }
        
    // UPDATE BEAT HOLDER WHEN KNOBS ARE MOVED
    if (changes[active_channel]) {
        
        beat_holder[active_channel] = Euclid(nn, kk, oo);
        
        switch (changes[active_channel]) 
        {
            case 1:
            case 3:
                for (int i = 0; i < MAXSTEPS; ++i) {
                
                    if (BitRead(beat_holder[active_channel], nn - 1 - i) && (i < nn)) {
                        sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, EUCLID));
                        sendMes.setArgs("i", 1);
                        osc.sendOsc(&sendMes);          
                    
                    } else {
                        
                        sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, EUCLID));
                        sendMes.setArgs("i", 0);
                        osc.sendOsc(&sendMes);      
                    }
                }
            
                break;
            
            case 2:
                for (int i = 0; i < MAXSTEPS; ++i) {
            
                    if (i < nn) {
                        sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, EUCLID));
                        sendMes.setArgs("i", 1);
                        osc.sendOsc(&sendMes);          
                    
                    } else {
                        
                        sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, EUCLID));
                        sendMes.setArgs("i", 0);
                        osc.sendOsc(&sendMes);          
                    }
                }
        
                break;
            
            default: 
                break;
        }
        
        changes[active_channel] = 0;
        last_changed[active_channel] = gTimer.read_ms();
    }
    
    // ANALOG PULSE TRIGGER
    if (trigger && !triggerState) {
        
        Sync(active_channel, gatesoff);
        triggerState = true;
        
        if (auto_offset) {
            
            cnt_auto_offset++;
        
            if (cnt_auto_offset == 64) // auto offset
            {
                cnt_auto_offset = 0;
                
                for (i = 0; i < MAXCHANNELS; ++i)
                {
                    offset = (MAXSTEPS / 2) - (rand() % (MAXSTEPS - 1));
            
                    if ((oo + offset) > (nn - 1)) {
                
                        offset = 0;
                        oo = (nn - 1);
            
                    } else if ((oo + offset) < mino) {
                
                        offset = 0;
                        oo = mino;
                    }
                
                    channelbeats[i][3] = (oo + offset);
        
                    last_read[i] = gTimer.read_ms();
                    changes[i] = 3; // o change = 3
                }
            }
            
        }
        
            
    } else if (!trigger) {
        
        triggerState = false;
    }

    // READ K KNOB 
    kknob = EncodeReadK(active_channel);
    
    if (_kknob != kknob) {
        
        _kknob = kknob;
        
        if (kknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) {
        
            if ((kk + kknob) > nn) {          
            
                kknob = 0;
                kk = nn;
            
            } else if ((kk + kknob) < mink) {
            
                kknob = 0;
                kk = mink;
            };

            kk = channelbeats[active_channel][1] = (kk + kknob); // update with encoder reading

            last_read[active_channel] = gTimer.read_ms();
            changes[active_channel] = 1; // k change = 1
        }
    }
    
    // READ N KNOB 
    nknob = EncodeReadN(active_channel);
    
    if (_nknob != nknob) {
        
        _nknob = nknob;
        
        if (nknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) { 

            if ((nn + nknob) > maxn) {
            
                nknob = 0;
                nn = maxn;
            
            } else if ((nn + nknob) < minn) {
            
                nknob = 0;
                nn = minn;
            };

            if (kk > (nn + nknob)) {// check if new n is lower than k + reduce K if it is 
                channelbeats[active_channel][1] = (nn + nknob);
            }; 

            if (oo > (nn + nknob - 1)) {// check if new n is lower than o + reduce o if it is 
                channelbeats[active_channel][3] = (nn + nknob - 1);
            }; 

            nn = channelbeats[active_channel][0] = (nn + nknob); // update with encoder reading
            oo = channelbeats[active_channel][3];

            last_read[active_channel] = gTimer.read_ms();
            changes[active_channel] = 2; // n change = 2 
        }
        
    }
    
    // READ O KNOB 
    oknob = EncodeReadO(active_channel);
    
    if (_oknob != oknob) {
        
        _oknob = oknob;
            
        if (oknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) { 
            // Sense check o encoder reading to prevent crashes 

            if ((oo + oknob) > (nn - 1)) {
            
                oknob = 0;
                oo = (nn - 1);
        
            } else if ((oo + oknob) < mino) {
            
                oknob = 0;
                oo = mino;
            }

            channelbeats[active_channel][3] = (oo + oknob);
        
            last_read[active_channel] = gTimer.read_ms();
            changes[active_channel] = 3; // o change = 3
        }
        
    }
    
    // ENABLE RESET BUTTON ** ADD FLASH RESET HERE ***
    if (gCtrlSW[1] && channelbeats[active_channel][2]) {
        
        for (ch = 0; ch < channels; ++ch) {
            channelbeats[ch][2] = 0; 
        }
    }
        
    // TURN OFF ANY LIGHTS THAT ARE ON 
    if ((euc_time - last_sync) > pulse_length && lights_active) {

        for (ch = 0; ch < channels; ++ch) {
            sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 3 - ch, EUCLID));
            sendMes.setArgs("i", 0);
            osc.sendOsc(&sendMes);
            
            sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 5, EUCLID));
            sendMes.setArgs("i", 0);
            osc.sendOsc(&sendMes);
            
        }

      lights_active = false; 
    }
    
    // FINISH ANY PULSES THAT ARE ACTIVE - PULSES LAST 1/4 AS LONG AS LIGHTS 
    if (euc_time - last_sync > (pulse_length / 4) && pulses_active) {
        
        for (ch = 0; ch < channels; ++ch) {
            
            if (!gatesoff)
            {
                gGATES[ch] = false;
                gCLOCKOUT = false;
                //digitalWrite(sparepin, LOW);
            }
        }
        
        pulses_active = false; 
    }
    
    ++active_channel;
    active_channel &= (channels - 1);
}

//-------------------------------------------------------------
// Init Euclidean Sequencer

void InitEuclideanSeq(void) {
    // Initialize Euclid Sequencer
    channelbeats[0][0] = 16;
    channelbeats[0][1] = 8;
    channelbeats[0][2] = 0;
    channelbeats[0][3] = 0;
    
    channelbeats[1][0] = 16;
    channelbeats[1][1] = 9;
    channelbeats[1][2] = 0;
    channelbeats[1][3] = 0;
    
    channelbeats[2][0] = 16;
    channelbeats[2][1] = 7;
    channelbeats[2][2] = 0;
    channelbeats[2][3] = 0;
    
    channelbeats[3][0] = 16;
    channelbeats[3][1] = 9;
    channelbeats[3][2] = 0;
    channelbeats[3][3] = 0;

    for (int i = 0; i < channels; ++i) 
    {
        beat_holder[i] = Euclid(channelbeats[i][0], channelbeats[i][1], channelbeats[i][3]);
    }
}


//-------------------------------------------------------------
// Euclid calculation function

unsigned int Euclid(int n, int k, int o) { // inputs: n=total, k=beats, o = offset
    int pauses = (n - k);
    int pulses = k;
    int offset = o;
    int steps = n;
    int per_pulse = (pauses / k);
    int remainder = (pauses % pulses);  
    unsigned int workbeat[n];
    unsigned int outbeat;
    uint16_t outbeat2;
    int workbeat_count = n;
    int a_remainder, b_remainder;
    int groupa, groupb;
    int i, j; 
    int trim_count;

    for (i = 0; i < n; ++i) { // Populate workbeat with unsorted pulses and pauses 

        if (i < pulses) {

            workbeat[i] = 1;

        } else {

            workbeat[i] = 0;
        }
    }

    if (per_pulse > 0 && remainder < 2) { // Handle easy cases where there is no or only one remainer

        for (i = 0; i < pulses; ++i) {

            for (j = (workbeat_count - 1); j > (workbeat_count - per_pulse - 1); --j) {
                workbeat[i]  = ConcatBin(workbeat[i], workbeat[j]);
            }

            workbeat_count = (workbeat_count - per_pulse);
            
        }

        outbeat = 0; // Concatenate workbeat into outbeat - according to workbeat_count

        for (i = 0; i < workbeat_count; ++i) {
            outbeat = ConcatBin(outbeat, workbeat[i]);
        }


        if (offset != 0) {
            
            outbeat2 = BitReadOffset(offset, outbeat, steps); // Add offset to the step pattern

        } else {
            
            outbeat2 = outbeat;
        }

        return outbeat2;

    } else { 

        groupa = pulses;
        groupb = pauses; 

        while (groupb > 1) { //main recursive loop

            if (groupa > groupb) { // more Group A than Group B
                
                a_remainder = (groupa - groupb); // what will be left of groupa once groupB is interleaved 
                trim_count = 0;

                for (i = 0; i < (groupa - a_remainder); ++i) { //count through the matching sets of A, ignoring remaindered
                    workbeat[i]  = ConcatBin(workbeat[i], workbeat[workbeat_count - 1 - i]);
                    ++trim_count;
                }
                
                workbeat_count = (workbeat_count - trim_count);

                groupa = groupb;
                groupb = a_remainder;

            } else if (groupb > groupa) { // More Group B than Group A
                
                b_remainder = (groupb - groupa); // what will be left of group once group A is interleaved 
                trim_count = 0;

                for (i = workbeat_count-1; i >= (groupa + b_remainder); --i) { //count from right back through the Bs
                    workbeat[workbeat_count - i - 1] = ConcatBin(workbeat[workbeat_count - 1 - i], workbeat[i]);
                    ++trim_count;
                }

                workbeat_count = (workbeat_count - trim_count);
                groupb = b_remainder;

            } else if (groupa == groupb) { // groupa = groupb 
                
                trim_count = 0;

                for (i = 0; i < groupa; ++i) {
                    workbeat[i] = ConcatBin(workbeat[i], workbeat[workbeat_count - 1 - i]);
                    ++trim_count;
                }

                workbeat_count = (workbeat_count - trim_count);
                groupb = 0;

            }
        }

        outbeat = 0; // Concatenate workbeat into outbeat - according to workbeat_count

        for (i = 0; i < workbeat_count; ++i) {
            
            outbeat = ConcatBin(outbeat, workbeat[i]);
        }

        if (offset != 0) {
            
            outbeat2 = BitReadOffset(offset, outbeat, steps); // Add offset to the step pattern

        } else {

            outbeat2 = outbeat;
        }

        return outbeat2;
    }
}

//-------------------------------------------------------------
// Reads a bit of a number

int BitRead(uint16_t b, int bitPos) {
    int x;
        
    x = b & (1 << bitPos);
    
    return x == 0 ? 0 : 1;
}

//-------------------------------------------------------------
// Function to right rotate n by d bits

uint16_t BitReadOffset(int shift, uint16_t value, uint16_t pattern_length) {
    uint16_t mask = ((1 << pattern_length) - 1);
    value &= mask;

    return ((value >> shift) | (value << (pattern_length - shift))) & mask;
}

//-------------------------------------------------------------
// Function to find the binary length of a number by counting bitwise

int findlength(unsigned int bnry) {
    bool lengthfound = false;
    int i;
    int length = 1; // no number can have a length of zero - single 0 has a length of one, but no 1s for the sytem to count

    for (i = 32; i >= 0; i--) {

        if ((BitRead(bnry, i)) && !lengthfound) {
            length = (i + 1);
            lengthfound = true;
        }
        
    }

    return length;
}

//-------------------------------------------------------------
// Function to concatenate two binary numbers bitwise

unsigned int ConcatBin(unsigned int bina, unsigned int binb) {
    int binb_len = findlength(binb);
    unsigned int sum = (bina << binb_len);

    sum = sum | binb;

    return sum;
}

//-------------------------------------------------------------
// routine triggered by each beat

void Sync(int active_channel, bool gatesoff) {
    int read_head, erase;
    int rand_vel, rand_len;
    int ch, i;

    if (masterclock % 2 == 0) {
        sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 7, EUCLID));
        sendMes.setArgs("i", 1);
        osc.sendOsc(&sendMes);
    
    } else {
        
        sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 7, EUCLID));
        sendMes.setArgs("i", 0);
        osc.sendOsc(&sendMes);
    }

    // Cycle through channels 
    for (ch = 0; ch < channels; ++ch) {
        
        read_head = (channelbeats[ch][0] - channelbeats[ch][2] - 1);  

        if (ch != active_channel || (euc_time - last_changed[active_channel]) > DISPLAY_UPDATE) {
            
            if (channelbeats[ch][2] < MAXSTEPS) {
                
                for (i = 0; i < MAXSTEPS; ++i) {
                    
                    if (BitRead(beat_holder[ch],channelbeats[ch][0] - 1 - i) && i < channelbeats[ch][0]) {
                        
                        sendMes.setTopAddress(SetMatrixAddress(ch * 2, i, EUCLID));
                        sendMes.setArgs("i", 1);
                        osc.sendOsc(&sendMes);
                    
                    } else {
                        
                        sendMes.setTopAddress(SetMatrixAddress(ch * 2, i, EUCLID));
                        sendMes.setArgs("i", 0);
                        osc.sendOsc(&sendMes);
                    }
                    
                }
            }
        }
        
        if (!masterclock) {
            
            erase = MAXSTEPS - 1;
            
        } else {
            
            erase = masterclock - 1;
        }
        
        sendMes.setTopAddress(SetMatrixAddress((ch * 2) + 1, erase, EUCLID));
        sendMes.setArgs("i", 0);
        osc.sendOsc(&sendMes);

        sendMes.setTopAddress(SetMatrixAddress((ch * 2) + 1, masterclock, EUCLID));
        sendMes.setArgs("i", 1);
        osc.sendOsc(&sendMes);
            
        // turn on pulses on channels where a beat is present  
        if (BitRead(beat_holder[ch], read_head)) {
            
            if (!gatesoff)
            {
                gGATES[ch] = true; // pulse out
            }

            sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 3 - ch, EUCLID));
            sendMes.setArgs("i", 1);
            osc.sendOsc(&sendMes);

            lights_active = pulses_active = true;
            
            if (!ch || (ch == 2)) { 
                
                rand_vel = 127 - (rand() % 20); // random velocity ch1, ch3
                
            } else {
                
                rand_vel = 95 - (rand() % 10);  // random velocity ch2, ch4
            }
            
            rand_len = 127 - (rand() % 17);    // random Amp EG Decay
            
            midi.sendControlChange(0x07, rand_vel, (ch + 1)); // volca sample Vol
            midi.sendControlChange(0x30, rand_len, (ch + 1)); // volca sample Amp EG Decay
            midi.sendNoteOn(0, 127, (ch + 1)); // volca sample trriger on
        }

        // send off pulses to spare output for the first channel 
        if (!(BitRead(beat_holder[ch], read_head)) && !ch) { // only relates to first channel
            
            if (!gatesoff)
            {
                gCLOCKOUT = true;
            }
            
            sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 5, EUCLID));
            sendMes.setArgs("i", 1);
            osc.sendOsc(&sendMes);
            
            lights_active = pulses_active = true;
        }

        // move counter to next position, ready for next pulse  
        ++channelbeats[ch][2];

        if ((channelbeats[ch][2]) >= (channelbeats[ch][0])) {
            channelbeats[ch][2] = 0; 
        }
    }
    
    ++masterclock;
    masterclock &= (MAXSTEPS - 1);

    pulse_length = ((euc_time - last_sync) / 5);
    last_sync = euc_time;
}

/* 3 functions to read each encoder   
   returns +1, 0 or -1 dependent on direction 
   Contains no internal debounce, so calls should be delayed 
 */

//-------------------------------------------------------------
// Check Euclidean Seq N(length) Value

int EncodeReadN(int ch) {
    static float _enc[4];
    int result = 0;
    
    switch (ch)
    {
        case 0:
        
            if (gEucA[0] == 0) {
                _enc[ch] = result = 0;
        
            } else if (gEucA[0] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucA[0];
    
            } else if (gEucA[0] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucA[0];
            }
            
            break;
        
        case 1:
        
            if (gEucA[3] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucA[3] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucA[3];

            } else if (gEucA[3] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucA[3];
            }
        
            break;
        
        case 2:
        
            if (gEucB[0] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[0] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[0];

            } else if (gEucB[0] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[0];
            }
        
            break;
        
        case 3:
        
            if (gEucB[3] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[3] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[3];

            } else if (gEucB[3] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[3];
            }
        
            break;
        
        default:
            break;
    }
    
    return result;
}

//-------------------------------------------------------------
// Check Euclidean Seq K(Density) Value

int EncodeReadK(int ch) { 
    static float _enc[4];
    int result = 0;
    
    switch (ch)
    {
        case 0:
        
            if (gEucA[1] == 0) {
                _enc[ch] = result = 0;
        
            } else if (gEucA[1] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucA[1];
    
            } else if (gEucA[1] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucA[1];
            }
            
            break;
        
        case 1:
        
            if (gEucA[4] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucA[4] < _enc[ch]) {  
                result = -4;
                _enc[ch] = gEucA[4];

            } else if (gEucA[4] > _enc[ch]) { 
                result = 4;
                _enc[ch] = gEucA[4];
            }
        
            break;
        
        case 2:
        
            if (gEucB[1] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[1] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[1];

            } else if (gEucB[1] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[1];
            }
        
            break;
        
        case 3:
        
            if (gEucB[4] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[4] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[4];

            } else if (gEucB[4] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[4];
            }
        
            break;
        
        default:
            break;      
    }
    
    return result;
}

//-------------------------------------------------------------
// Check Euclidean Seq O(Offset) Value

int EncodeReadO(int ch) { 
    static float _enc[4];
    int result = 0;
    
    switch (ch)
    {
        case 0:
        
            if (gEucA[2] == 0) {
                _enc[ch] = result = 0;
        
            } else if (gEucA[2] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucA[2];
    
            } else if (gEucA[2] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucA[2];
            }
            
            break;
        
        case 1:
        
            if (gEucA[5] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucA[5] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucA[5];

            } else if (gEucA[5] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucA[5];
            }
        
            break;
        
        case 2:
        
            if (gEucB[2] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[2] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[2];

            } else if (gEucB[2] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[2];
            }
        
            break;
        
        case 3:
        
            if (gEucB[5] == 0) {
                _enc[ch] = result = 0;
    
            } else if (gEucB[5] < _enc[ch]) {  
                result = -1;
                _enc[ch] = gEucB[5];

            } else if (gEucB[5] > _enc[ch]) { 
                result = 1;
                _enc[ch] = gEucB[5];
            }
        
            break;
        
        default:
            break;
    }
    
    return result;
}