Turn a MIDI TO CV Converter from Linear to Exponential Out!

Thread Starter

Jack Tranckle

Joined Jan 20, 2016
73
Hello! So I am in the middle of a home project, where I am trying to build a very simple 5 octave monophonic analogue synthesizer, which is controlled via MIDI. So I have this code from acidbourbon at the moment which sends the MIDI data to a DAC, producing linear voltages that will power a sawtooth core VCO. I am wondering if it is possible to take out the analogue exponential converter and create exponential CV via the microcontroller. I am using a attiny2313. What would I need to do in order to do this? Any help would be very appreciated.

Code:
#define F_CPU 12e6
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define NOTE_ON 0b10010000
#define NOTE_OFF 0b10000000
#define PITCH_WHEEL 0b11100000
#define CONTROL_CHANGE 0b10110000
#define CC_MOD_WHEEL 1
#define CC_PORTAMENTO 5



void init_USART(void)
{
    UCSRB |= (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
    UCSRC |= (1<<UCSZ0)|(1<<UCSZ1);
    UBRRH = 0;
    UBRRL = 23;
}


void playnote(int note)
{
PORTB = note;
_delay_ms(100);
}








#define STACKSIZE 8

struct Notestack {
    uint8_t data[STACKSIZE];
    uint8_t fill;
    } notestack = {{},0};
int8_t match_pos = 0;
uint8_t porta_mux= 0;


uint8_t to_stack(uint8_t input) {
    for(int8_t i=notestack.fill; i>=1; i--) {   
        notestack.data[i]=notestack.data[i-1]; // move each element in array to next place
    }
    notestack.data[0] = input; // insert new item at the top
    notestack.fill ++; // increment fill counter
    return notestack.fill;
}

uint8_t from_stack(uint8_t item_to_delete) {

    for(int8_t i=notestack.fill-1; i>=0; i--) {    // search backwards for item to remove (by value)
        if(notestack.data[i] == item_to_delete) {
        match_pos = i;
        break;
        }
    }   

    for(int8_t i=match_pos; i<=notestack.fill-2; i++) {    // shift back by one pos everything after item_to_delete
        notestack.data[i] = notestack.data[i+1];
    }   

    notestack.fill --; //decrement fill counter
    return notestack.fill;
}

uint8_t last_active_key() {
    return notestack.data[0]; // return new top element of key list
    }

void make_gate() {
    if(notestack.fill >0) {
        PORTD |= (1<<6);
    }
    else {
        PORTD &= ~(1<<6);
    }

}
void make_cv() {
    static uint8_t dac_value = 0;
    dac_value = (last_active_key()-36)<<1;
    PORTD &= ~(0b00111100);
    PORTD |= (dac_value & 0x0F)<<2;
    PORTB &= ~(0b11100000);
    PORTB |= (dac_value & 0xF0)<<1;
}

void make_portamento(uint8_t setting) {
    if (setting <=2 ) {
        OCR1B = 1023;
        porta_mux=0;
    }
    else if(setting < 43+2) {
        porta_mux=1;
        OCR1B = 1024/(setting+1-2)-1;
    }
    else if(setting <= 43+2+43) {
        porta_mux=2;
        OCR1B = 1024/(setting+23+1-2-43)-1;
    }
    else {
        porta_mux=3;
        OCR1B = 1024/(setting+33+1-2-2*43)-1;
    }
}




int main (void) {    


uint8_t counter=0, status_byte=0, data_byte_1=0, data_byte_2=0;

uint8_t temp;   

init_USART();

PORTB = 0;
DDRB = 0xFF;


PORTD &= ~(1<<6);
PORTD |= 1<<1; //activate pullup for gate mode pin
DDRD |= 0b001111100;


TCCR0A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1);
TCCR0B = (0<<CS12) | (1<<CS11)| (1<<CS10);
TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(1<<COM1B1);
TCCR1B = (0<<CS12) | (1<<CS11)| (0<<CS10);



// default settings

OCR1A = 511; // pitch wheel in the middle
make_portamento(35);


while(1)
{
    while(!(UCSRA&(1<<RXC))){};

    //store received data to temp
    temp = UDR;

    if (temp >=128) { // is status byte
        counter = 0; // start counting
        status_byte = temp;
    }

    if(counter ==1){ // store first data byte
        data_byte_1 = temp;
    }
    if(counter ==2){ // store second data byte and ...
        data_byte_2 = temp;

        switch(status_byte){ // evaluate !
            case NOTE_ON:
                if(data_byte_2) { // if key velocity greater zero
                    to_stack(data_byte_1); // add note to stack
                    if(notestack.fill >1) // activate glide when >1 keys pressed
                        PORTB = (PORTB & ~(0b00000011)) | porta_mux; // select right resistor for RC filter
                    make_cv();
                    make_gate();
                    break;
                }
                // else: if key velocity is zero, act the same as key off event
            case NOTE_OFF:
                from_stack(data_byte_1); // remove note from stack
                if(notestack.fill == 0) {// when stack empty turn off glide
                    PORTB &= ~(0b00000011);
                    }
                make_cv();
                make_gate();
                break;
            case PITCH_WHEEL:
                OCR1A = data_byte_2<<3; // output analogue voltage via 10 bit PWM
                break;
            case CONTROL_CHANGE:
                switch(data_byte_1) {
                    case CC_MOD_WHEEL:
                        OCR0A = data_byte_2 <<1; // output analogue voltage via 8 bit PWM
                        break;
       
                    case CC_PORTAMENTO:
                        make_portamento(data_byte_2);
                    }
                break;

               
        }
    }

   

    counter ++;
    if (counter ==3)
    {counter = 1;}





}




   return 0;             
}
<6
 
Top