MUFF WIGGLER Forum Index
 FAQ & Terms of UseFAQ & Terms Of Use   Wiggler RadioMW Radio   Muff Wiggler TwitterTwitter   Support the site @ PatreonPatreon 
 SearchSearch   RegisterSign up   Log inLog in 
WIGGLING 'LITE' IN GUEST MODE

Arduino Quantizer code
MUFF WIGGLER Forum Index -> Music Tech DIY  
Author Arduino Quantizer code
satindas
Although this code lays out midi note values for 5 octaves, I only seem to be able to get a max of 4V at the output (via MCP4822 dac). I'm something of a noob with Arduino and not yet savvy enough to identify the problem if indeed there is one (the build docs are minimal).I've got a pcb laid out for this, its breadboarded (with a few extra features) and works a charm except for this limited range issue Any ideas how I can get the full 5 Octaves would be gratefully received. Cheers! (schematic further down the page).

Code:
#include <SPI.h>
#define NOTE_INPUT1         (1) //Quan01input
#define NOTE_INPUT2         (2) //Quan02input
#define NOTE_INPUT3         (3) //Quan03input
#define NOTE_INPUT4         (4) //Quan04input
#define SCALE_SELECTOR      (5) //ScaleSelect

int quant1 = -1;
int quant2 = -1;
int quant3 = -1;
int quant4 = -1;
const int gatePin = 4; // gate out
const int ledPin = 7;  // LED
const int clkPin = 2;  // clock input
volatile int clkState = HIGH;

uint16_t midiTable[] = {
//  C    C#     D     D#    E     F   F#    G    G#     A    A#      B
    0,   1,     2,    3,    4,    5,   6,   7,    8,    9,   10,    11, //0
   12,   13,   14,   15,   16,   17,  18,  19,   20,   21,   22,    23, //12
   24,   25,   26,   27,   28,   29,  30,  31,   32,   33,   34,    35, //24
   36,   37,   38,   39,   40,   41,  42,  43,   44,   45,   46,    47, //36
   48,   49,   50,   51,   52,   53,  54,  55,   56,   57,   58,    59, //48
   60                                                                   //60
};

uint16_t mapMidi(uint16_t input) {
  return (midiTable[(input)]);
}

uint16_t majTable[] = {
     0,  2,  4,  5,  7,  9, 11, //scale01 Major
    12, 14, 16, 17, 19, 21, 23,
    24, 26, 28, 29, 31, 33, 35,
    36, 38, 40, 41, 43, 45, 47,
    48, 50, 52, 53, 55, 57, 59,
    60
};

uint16_t minTable[] = {
    0,  2,  3,  5,  7,  8, 10, //scale02 Minor
   12, 14, 15, 17, 19, 20, 22,
   24, 26, 27, 29, 31, 32, 34,
   36, 38, 39, 41, 43, 44, 46,
   48, 50, 51, 53, 55, 56, 58,
   60
};

uint16_t pentaTable[] = {
    0,  2,  3,  5,  7, 10,     //scale03 Pentatonic
   12, 14, 15, 17, 19, 22,
   24, 26, 27, 29, 31, 34,
   36, 38, 39, 41, 43, 46,
   48, 50, 51, 53, 55, 58,
   60
};

uint16_t dorianTable[] = {
     0,  2,  3,  5, 7,  9,  10, //scale04 Dorian
    12, 14, 15, 17, 19, 21, 22,
    24, 26, 27, 29, 31, 33, 34,
    36, 38, 39, 41, 43, 45, 46,
    48, 50, 51, 53, 55, 57, 58,
    60
};

uint16_t maj3rdTable[] = {
    0, 4,  7, 11,              //scale05Maj7(9)
   12, 16, 19, 23,
   24, 26, 31, 35,
   36, 40, 43, 47,
   48, 50, 54, 55,  59,
   60 
};

uint16_t min3rdTable[] = {
    0,   3,   7,  10,          //scale06 Minor7(9,11)
   14,  15,  19,  22,
   24,  26,  27,  31,  34,
   36,  39,  41,  46,
   48,  50,  51,  55,  58,
   60     
};

uint16_t whTable[] = {
    0,   2,   4,   6,   8,  10, //scale07 (WholeTone)
   12,  14,  16,  18,  20,  22,
   24,  26,  28,  30,  32,  34,
   36,  38,  40,  42,  44,  46,
   48,  50,  52,  54,  56,  58,
   60
};

uint16_t chromaTable[] = {
    0,
    1,  2,  3,  4,  5,  6,  7,  8, 9,  10, 11, 12, //scale08 Chromatic
   13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
   37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
   49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
   60
};

uint8_t SelectTable[]={0,1,2,3,4,5,6,7,8,9,10,11,12};

uint16_t ScaleSelect(uint16_t input) {
  uint8_t value = (input) / (1024/7);
  return (SelectTable[value]);
}

uint16_t mapMaj(uint16_t input) {
  uint8_t value = (input) / 36;
  return(majTable[value]);     
}

uint16_t mapMin(uint16_t input) {
  uint8_t value = (input) / 36;
  return(minTable[value]);
}

uint16_t mapPenta(uint16_t input) {
  uint8_t value = (input) / 42;
  return (pentaTable[value]);
}

uint16_t mapDorian(uint16_t input) {
  uint8_t value = (input) / 36;
  return (dorianTable[value]);
}

uint16_t mapMaj3rd(uint16_t input) {
  uint8_t value = (input) / 61;
  return (maj3rdTable[value]);
}

uint16_t mapMin3rd(uint16_t input) {
  uint8_t value = (input) / 59;
  return (min3rdTable[value]);
}

uint16_t mapWh(uint16_t input) {
  uint8_t value = (input) / 42;
  return(whTable[value]);
}

uint16_t mapChromatic(uint16_t input) {
  uint8_t value = (input) / 21;
  return(chromaTable[value]);
}


int deJitter(int v, int test){ 
  if (abs(v - test) > 19) {
    return v;
  }
  return test;
}

void sendNote1(int key) {
  int pitch1 = 0xa00L * key / 30.3;
  digitalWrite(10, LOW);
  SPI.transfer(0x10 + (pitch1 >> 8)); //DAC a OUTA
  SPI.transfer(pitch1 & 0xff);
  digitalWrite(10, HIGH);
}

void sendNote2(int key2) {
  int pitch2 = 0xa00L * key2 / 30.3;
  digitalWrite(10, LOW);
  SPI.transfer(0x90 + (pitch2 >> 8)); //DAC a OUTB
  SPI.transfer(pitch2 & 0xff);
  digitalWrite(10, HIGH);
}

void sendNote3(int key3) {
  int pitch3 = 0xa00L * key3 / 30.3;
  digitalWrite(9, LOW);
  SPI.transfer(0x10 + (pitch3 >> 8)); //DAC b OUTA
  SPI.transfer(pitch3 & 0xff);
  digitalWrite(9, HIGH);
}

void sendNote4(int key4) {
  int pitch4 = 0xa00L * key4 / 30.3;
  digitalWrite(9, LOW);
  SPI.transfer(0x90 + (pitch4 >> 8)); //DAC b OUTB
  SPI.transfer(pitch4 & 0xff);
  digitalWrite(9, HIGH);
}

void my_interrupt_handler()
{
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = micros();

  if (interrupt_time - last_interrupt_time > 100)
  {
      clkState = LOW;
  }
  last_interrupt_time = interrupt_time;
}

void setup() {
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV128);
  pinMode(ledPin,OUTPUT);
  digitalWrite(7,HIGH);
  pinMode(9,OUTPUT);  //select dacB SS Pin
  pinMode(10,OUTPUT); //select dacA SS Pin
  digitalWrite(9,HIGH);
  digitalWrite(10,HIGH);
  pinMode(gatePin, OUTPUT);
  pinMode(clkPin, INPUT);
  attachInterrupt(0, my_interrupt_handler, LOW);
}

void loop() {
    uint16_t  tmp_midi_note1;
    uint16_t  tmp_midi_note2;
    uint16_t  tmp_midi_note3;
    uint16_t  tmp_midi_note4;

    uint16_t  tmp_read1 = deJitter(analogRead(NOTE_INPUT1), quant1);
    uint16_t  tmp_read2 = deJitter(analogRead(NOTE_INPUT2), quant2);
    uint16_t  tmp_read3 = deJitter(analogRead(NOTE_INPUT3), quant3);
    uint16_t  tmp_read4 = deJitter(analogRead(NOTE_INPUT4), quant4);
    uint8_t   tmp_scale = ScaleSelect(analogRead(SCALE_SELECTOR));
   
    quant1 = tmp_read1;
    quant2 = tmp_read2;
    quant3 = tmp_read3;
    quant4 = tmp_read4;
             
  if (clkState == LOW) {
      clkState = HIGH;
      digitalWrite(ledPin, LOW);
      digitalWrite(gatePin, LOW);
     
  switch(tmp_scale){
     case  0:
       tmp_midi_note1 = mapMaj(tmp_read1);
       tmp_midi_note2 = mapMaj(tmp_read2);
       tmp_midi_note3 = mapMaj(tmp_read3);
       tmp_midi_note4 = mapMaj(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  1:
       tmp_midi_note1 = mapMin(tmp_read1);
       tmp_midi_note2 = mapMin(tmp_read2);
       tmp_midi_note3 = mapMin(tmp_read3);
       tmp_midi_note4 = mapMin(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  2:
       tmp_midi_note1 = mapPenta(tmp_read1);
       tmp_midi_note2 = mapPenta(tmp_read2);
       tmp_midi_note3 = mapPenta(tmp_read3);
       tmp_midi_note4 = mapPenta(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  3:
       tmp_midi_note1 = mapDorian(tmp_read1);
       tmp_midi_note2 = mapDorian(tmp_read2);
       tmp_midi_note3 = mapDorian(tmp_read3);
       tmp_midi_note4 = mapDorian(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  4:
       tmp_midi_note1 = mapMaj3rd(tmp_read1);
       tmp_midi_note2 = mapMaj3rd(tmp_read2);
       tmp_midi_note3 = mapMaj3rd(tmp_read3);
       tmp_midi_note4 = mapMaj3rd(tmp_read4);
       
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  5:
       tmp_midi_note1 = mapMin3rd(tmp_read1);
       tmp_midi_note2 = mapMin3rd(tmp_read2);
       tmp_midi_note3 = mapMin3rd(tmp_read3);
       tmp_midi_note4 = mapMin3rd(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  6:
       tmp_midi_note1 = mapWh(tmp_read1);
       tmp_midi_note2 = mapWh(tmp_read2);
       tmp_midi_note3 = mapWh(tmp_read3);
       tmp_midi_note4 = mapWh(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
       break;
       
     case  7:
       tmp_midi_note1 = mapChromatic(tmp_read1);
       tmp_midi_note2 = mapChromatic(tmp_read2);
       tmp_midi_note3 = mapChromatic(tmp_read3);
       tmp_midi_note4 = mapChromatic(tmp_read4);
       
       sendNote1(tmp_midi_note1);
       sendNote2(tmp_midi_note2);
       sendNote3(tmp_midi_note3);
       sendNote4(tmp_midi_note4);
     default:
     break;
    }
    } else {
     digitalWrite(ledPin, HIGH);
     digitalWrite(gatePin, HIGH);
  }
}



and here's the original schematic....

Bricks
http://ww1.microchip.com/downloads/en/devicedoc/22249a.pdf

Quote:
The devices have a high precision internal voltage
reference (VREF = 2.048V). The user can configure the
full-scale range of the device to be 2.048V or 4.096V


If I understand correctly, that DAC puts out 2.048v, and has an internal opamp that can be turned on to scale to 4.096v. You could scale the signal yourself with another opamp
satindas
Cheers Bricks, that explains it thumbs up

.......so I guess I have three options:

1) scale the output to 5v.... which is simple, and adjust the code accordingly, .....which I would have no clue where to even start!

2) use a dac that puts out at least 5V.... (would the code need to change????)

3) make do!

hmmm.....
CLee
You should add an op amp with a trimable gain to get you to 0 to 5V. That will be your 1v/oct output trim.

The code doesn't need to change any, It's just an analog adjustment of the gain after the DAC conversion.

Check out the schematic for my quantizer at clsound.com on the modular synth page. You can see the output trim on the digital board. I used a different converter, and I boosted the gain up to 0 to 10V but the idea is the same. You can ignore the vast majority of the schematic, just look at the op amp after the MCP DAC.

Craig
satindas
Craig thanks again for your advice .
I'm not sure if that's the solution though.
At 4V the scaling is 1V/Oct perfect at the (quantized) dac output, so stretching the range to 5V would surely destroy the scaling.
I was looking at the datasheet for the MCP4922 just before I saw your reply and noticed that it outputs with a range between VCC and VDD. I also notice that you use the 4921 in your design, sooooo.... I'm guessing that this dac with your scaling solution is the way to go. Actually as the quantization is by midi to cv I'm also guessing that there shouldn't be any need for further scaling hmmm.....
sanders
Bricks, do you happen to know what changes would be necesary to output at 1.2v/octave scale? You use this scale right?

I'm intersted in ways of achieving quantization for 1.2v scale.
Bricks wrote:
http://ww1.microchip.com/downloads/en/devicedoc/22249a.pdf

Quote:
The devices have a high precision internal voltage
reference (VREF = 2.048V). The user can configure the
full-scale range of the device to be 2.048V or 4.096V


If I understand correctly, that DAC puts out 2.048v, and has an internal opamp that can be turned on to scale to 4.096v. You could scale the signal yourself with another opamp
Bricks
sanders wrote:
Bricks, do you happen to know what changes would be necesary to output at 1.2v/octave scale? You use this scale right?


You mean in my illucia stuff? None of that (so far...!) outputs voltage for modulars. They're all just controllers with state for routing things on computer.

I have been dabbling with CV<->OSC lately though, and messing with this very DAC.

I haven't looked at this quantizer code very closely so I'm not sure what would need to change for 1.2v/o

but I'm pretty sure that in both cases (1.2v, or the OP's 0-4v scaled to 0-5v) the code would need to change, no?

I don't think a quantizer would be particularly hard to write though.. might need some hand tweaking but nothing too crazy if you're just looking for 12 tone stuff.
withakay
As far as your code goes the voltage is irrelevant. Your dac is 12 bit so it can represent 4096 values. You want 5 octaves so that is 60 semitones. 4096/60 = 68.26.
I would round that 68.
As suggested you then need to adjust the v/oct after the dac

Btw interesting idea, I just ordered the same dacs to try out with the arduino.
CLee
satindas wrote:
Craig thanks again for your advice .
I'm not sure if that's the solution though.
At 4V the scaling is 1V/Oct perfect at the (quantized) dac output, so stretching the range to 5V would surely destroy the scaling.
I was looking at the datasheet for the MCP4922 just before I saw your reply and noticed that it outputs with a range between VCC and VDD. I also notice that you use the 4921 in your design, sooooo.... I'm guessing that this dac with your scaling solution is the way to go. Actually as the quantization is by midi to cv I'm also guessing that there shouldn't be any need for further scaling hmmm.....


I'm not sure if my DAC is any better than yours. I've been experimenting a little and different actual chips seem to be giving my varying amounts of linearity. I need to compare the sheets of yours and the 4921.

As far as scaling goes, I seem to be doing things a little differently than you. In mine I do a AD conversion which is 10 bits, for semitones I then drop the bottom 3 bits (I actually drop 2 because I'm working in quartertones). The input is scaled to convert to 120 (when the bottom 3 bits are dropped) at 10V into the module. That number is then the offset to a scale table. All my scale tables are the same size, they just repeat numbers. For semitones there are 120 values. for wholetones there are 60, each one repeated twice. I then scale the output of the DAC back up so a ADC'd value of 120 equals 10 volts at the output of the module.

This is basically what withakay is saying, but he's working with the full 12 bits and changing a 10v scale to a 5v one
mOBiTh
satindas wrote:

and here's the original schematic....



Guys what value should those zener diodes be on the buffered inputs to limit the voltage into the Atmega to 0-5V? Does this protect against negative voltages?

Cheers! Still trying to get my head around it...

Edit: Ah so Input 4 allows +/- 5V because of the zener pair but the other three inputs clip at -0V?

Does someone know the maths for calculating the zener voltage in these cases please?
mxmxmx
i don't think there's a great deal of difference between mcp482x and 492x, except the one can be supplied with an external vref and the other can't.

anyways, your 4v sound about right, when powered from vdd 5v and setting it to 2x gain (cf the equation 4.1 in the datasheet), so probably that code is a little misleading? either way, as the others have pointed out, amplifying will give you a greater range, even say 8, with some tweaking. i wouldn't worry too much about messing up the scale, the adjustment/calibration can always be done in software - there's already some floating points mulitplications in there so the code above doesn't appear to be particularly optimized towards some particular output voltage.
elemental
I've recently become very interested in different scales and more importantly temperaments and tuning systems.

I recently got an ADDAC 207 which is great but the thing I was really hoping it would do, that is the ability to change temperament, is not flexible at all.

I would like a quantizer where I can design the scale and temperament myself.

I have a strong programming background so can work out code examples.. looking at getting an Ardcore and Arduino Nano to check this out at some point. DIY seems to be the way forward for my needs!

Any pointers much appreciated.

Thanks
Hashtag Octothorpe
Here's a thread back from the dead! BRAINS... zombie

First: EXCELLENT project! The scales are exactly what I'm looking for!

I'm in the middle of building this project, and don't understand what the clock is needed for. I don't need a clock -- I'm simply quanitizing the output from a ribbon controller, and I'll be writing the tmp_midi_notes to one of the PWM pins on an Arduino nano, filtering the output and running it through an opamp with gain trimmer to get 1V/oct...

But the Arduino freezes when I'm not..... touching pin 2, the clkPin. I've got serial prints going on, and it'll halt mid-line unless I touch pin 2.

If I tie pin 2 high (or to 3.3 or REF), it runs great, but the tmp_midi_note numbers are all zero.

If I ground pin 2, it stops.

If I digitalWrite to pin 3 high and then low every 10 milliseconds, and bridge pin 2 to pin 3, it stops.

I know I'm kludging like crazy here, and there is totally a proper solution, but it's beyond me! LOL.


This is me, by the way: MODULAR FOR THE MASSES
satindas
Hashtag Octothorpe wrote:
Here's a thread back from the dead! BRAINS... zombie

First: EXCELLENT project! The scales are exactly what I'm looking for!

I'm in the middle of building this project, and don't understand what the clock is needed for. I don't need a clock -- I'm simply quanitizing the output from a ribbon controller, and I'll be writing the tmp_midi_notes to one of the PWM pins on an Arduino nano, filtering the output and running it through an opamp with gain trimmer to get 1V/oct...

But the Arduino freezes when I'm not..... touching pin 2, the clkPin. I've got serial prints going on, and it'll halt mid-line unless I touch pin 2.

If I tie pin 2 high (or to 3.3 or REF), it runs great, but the tmp_midi_note numbers are all zero.

If I ground pin 2, it stops.

If I digitalWrite to pin 3 high and then low every 10 milliseconds, and bridge pin 2 to pin 3, it stops.

I know I'm kludging like crazy here, and there is totally a proper solution, but it's beyond me! LOL.


This is me, by the way: MODULAR FOR THE MASSES


The clock input is referred to in the schematic as "sync" and I suppose should be labelled "gate input"

From what I can recall .. If you send a gate signal from your ribbon controller to the sync/clock input the quantization should work a bit like a sample and hold with the output being a quantized voltage relative to that present at the input when the gate is received.
Hashtag Octothorpe
I see. Thank you for replying! That would be a useful feature -- I was considering implementing some sort of s&h to the ribbon, I'll try to understand what's going on.

I won't be using SPI at all -- that's for the DAC chips if I'm not mistaken, and I'm just going to use Arduino's quick'n'dirty PWM analogWrite function. With smoothing, obvs. So I'll try removing that code and seeing if it's happier.

When I hang a resistor off pin 2 unconnected to anything it also runs well! It... needs?... interference to run! LOL
gabrielcichero
satindas

Hello,

I was looking at your code of the arduino quantizer, i would like to test it!

By the moment, i was studying the code and i saw that the semitoneShiftPin is on A5, but the DAC mcp4725 already is using this pin (A4 for SCA and A5 for SCL).

So, i just change the Analog pin for the "semitoneShiftPin " to another one and is ok?

#include <Wire.h>
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dac;
#define DAC_RESOLUTION (8)
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();

//digital pins
#define clockInPin 3
#define ledPin 8

//analogic pins
#define cvIn1Pin A0
#define cvIn2Pin A1
#define semitoneShiftPin A5
#define octaveShiftPin A6
#define scaleSelectorPin A7
MUFF WIGGLER Forum Index -> Music Tech DIY  
Page 1 of 1
Powered by phpBB © phpBB Group