Friday, October 27, 2017

On Generating a Pitch Lookup Table for a DAC

A given DAC (digital to analog converter) will contain imperfections which can be compensated for by analog and digital means. These imperfections vary depending on the load, the relative data point position, the gain error, the quality of the power supply etc.

I am interested in powering a DAC - in this case used for pitch control of a VCO (voltage controlled oscillator) - just from a USB bus. The aim is to control a modular synthesiser. The power supply is a single 5V rail from USB. The output of the DAC can be measured and a linear transfer function implied, however this transfer function will fail at the extremes, as the data either reaches 0 or full code. A more "real world" conversion between data and output can be generated using a brute force lookup table, and comparing data input to pitch output. 

In this example, an MCP4822 DAC is used to control an STO module, and a pitch lookup table is generated. 

The MCP4822 DAC has two 12-bit channels, is powered from a 3.3v to 5V supply, and has an internal voltage reference of 2.048V with an output gain of either one or two. In theory, this means a linear transfer of 4096 values across a voltage range of exactly 0 to 2.048V or 4.096V depending on the gain setting. In practice, the DAC has imperfections as the output approaches 0V and 4.096V. 

The STO module is a VCO by Make Noise with a 1V per octave input, which expects a range of -5V to 5V for a ten octave range of pitch. 



The aim is to control the STO module using the MCP4822 DAC when powered from USB (without a bipolar supply or op-amp), which translates to just over 4 octaves of pitch range. 

The method is to measure each data value in the range 0 - 4095 of the MCP4822 DAC in terms of pitch output, and then generate a lookup table. The lookup table will take, as an input, a pitch in absolute cents. 6000 cents would equate to middle C, if the midway DAC data point is tuned to middle C - but of course the STO can be tuned arbitrarily. 

A Max patch is used to step through each data value, and then measure the output frequency, and then store the values as text. The data value (from 0 - 4095) is sent to a Teensy microcontroller as a 14-bit pitch bend value. 

The Teensy writes the value to the MCP4822, which then converts the value into a voltage between 0 and 4.096V. The output of the MCP4822 is connected to the STO module. The audio output of the STO module is connected to a sound card input, which is represented in the Max patch as the adc~ object. Download the Max patch here. The patch uses the fiddle~ object. 




The Arduino sketch is shown below, and will compile for a range of Teensy boards if USB MIDI mode is selected in the tools menu. The code uses the SPI library, included with Teensyduino



On receiving a pitchbend message via USB MIDI, the Teensy will write the pitchbend value to both DAC channels of the MCP4822. Data is transferred to the MCP4822 as two bytes. The first byte contains the DAC channel, the amplifier gain, enabling the DAC output, and bits 8 - 11 of the DAC value. The second byte contains bits 0 - 7 of the DAC value. Download the Arduino sketch here

The Teensy is connected to the MCP4822 via the SPI bus and one chip select pin. 








  • Teensy digital pin 0 is the chip select pin, and is connected to MCP4822 pin 2 (!CS). 
  • Teensy digital pin 13 is the clock pin, and is connected to MCP4822 pin 3 (SCK). 
  • Teensy digital pin 11 is the data output pin, and is connected to MCP4822 pin 4 (SDI). 
  • Teensy VIN (5V) is connected to MCP4822 pin 1 (VDD). 
  • Teensy ground is connected to MCP4822 pin 7 (VSS) and pin 5 (LDAC). 
  • MCP4822 is connected to the 1V per octave input of the STO module via a 100 ohm resistor




Once each DAC data value has been correlated to a pitch output of the STO module, an excel table can be created that then does the opposite - takes a pitch input in cents, and outputs the data value of the DAC required to generate that pitch. 


The spreadsheet has a total of five data columns: A, B, C and E, F. The first three is the data collected from measuring the pitch output based on the DAC data input (a DAC data value range of 0 - 4095). The second set of two columns is a lookup table that matches a given pitch input to the DAC data value output required to generate that pitch (a pitch value range of 3545 - 8468 cents). 

The aim is to have a list of continuous values in cents, whereby an input pitch value in cents will correspond to the DAC value used to generate that pitch (or the closest possible pitch)



The data of the DAC vs output pitch (i.e. first three columns) is almost linear, however there are enough discrepancies to warrant the use of a lookup table, as the memory size cost is not huge (depending on application, of course). 

Column A is the data value that was sent to the DAC. Column B is the resultant pitch as measured from the STO module. Note that the STO module was tuned so that a data value of 2048 is middle C (MIDI pitch 60 or 6000 cents in this table). Column C is the pitch from column B converted to absolute cents. Columns A to C have a total of 4096 rows, each row corresponding to a data point of the MCP4822. 

Column E is the entire possible pitch range available (via this setup of the MCP4822) from 3545 cents to 8468 cents. Column F is the closest matching data point from column A, whereby column C matches the input of column E. The formula for column F is: "=MATCH(E3,C:C)", where E3 is the current row of E relative to F. 

To use this table in an Arduino sketch, a constant array can be created which can be referenced against an input cents value. 

For example: 

First, to create the pitch array: 

const int pitch_data_values[] = { // all of the values from column F, separated by commas 
};

And then to read from the pitch array: 

writeDAC(cs_pin, 0, pitch_data_values[constrain(((pitch * 100) + pitchbend_value) - 3545  , 0, 4923)]);

Note that in this case, the variable pitch is a MIDI note from 0 - 127, and pitchbend_value is a variable from -1200 to 1200 cents. This function will result in a value being written to the MCP4822 between 0 - 4096, corresponding to the MIDI note and pitch bend value. See code below for full context. 



Download an Arduino sketch with a monophonic implementation of this lookup table here

Sunday, October 08, 2017

Four Channel Control Voltage to MIDI CC

This is a very simple four channel control voltage to MIDI CC converter. This device scales positive voltages, then limits positive and negative voltages, before being converted to a digital value and sent as a USB MIDI continuous controller message. Note that negative voltages are simply clipped.




This data can then be used to map control voltage signals to digital software.

In the example video, four control voltages from an LFO, an ADSR and two channels of Maths are converted to MIDI CC messages and mapped to parameters in Lumen.




Schematic




Code 



Download the code here: http://milkcrate.com.au/_other/downloads/arduino/CV_to_MIDI.ino



Example Video





Friday, October 06, 2017

Progress on Eight Voice USB MIDI to CV


Yesterday I made this eight voice USB MIDI to CV interface. Bus powered, with each voice corresponding to a MIDI channel. Each voice currently has a note gate, pitch CV and velocity CV. All outputs are buffered. The gate is 5V. The pitch has a range of just over four octaves. Tuning seems relatively stable.

I would like to add a 'mode' control, where one of four modes can be selected:

  • Melodic mode: Eight melodic voice outputs; each voice has note gate, pitch CV and velocity CV
  • Rhythmic mode: Eight drum voice outputs; each voice output has note gate and velocity CV; plus eight CV outputs that are assigned to MIDI CC
  • Mixed mode: Four melodic voices; four drum voices; plus four MIDI CC CV
  • Gate mode: Eight note gates; plus sixteen CV outputs that are assigned to MIDI CC
  • High resolution mode: Eight note gates; plus sixteen CV outputs that are assigned to pitch bend

Stay tuned!


Tuesday, October 03, 2017

New Me-ism: Monthly Wrap Up - September 2017

Cross-posted from New Me-ism

---



September was a mixed bag.

---

Even though I generated more waste than in August, I did get a chance to try out dehydrated food extensively by hiking for seven days total throughout this month at Baroota and the KIWT.


Here's my rubbish for this month:



Far too much plastic! Damn.

Monday, October 02, 2017

Comparing Optomix Vactrol Response



A comparison between channel 1 and channel 2 of my Optomix module, in terms of using the 'strike' input with a 20ms 5V gate.

Sunday, October 01, 2017

Mixed-mode USB MIDI to CV


This is a simple mixed-mode USB to MIDI device. Mixed-mode because it supports both pitched and percussive sounds.





MIDI channels 1 - 4 are mapped as monophonic data to Pitch Control Voltage 1 - 4, Velocity Control Voltage 1 - 4 and Note Gate 1 - 4. These are suitable for controlling melodic synthesisers with pitch and velocity being mapped to different parameters, and the note gate triggering an envelope.


With MIDI channel 5, MIDI note 60 is mapped to Note Gate 5, MIDI note 62 is mapped to Note Gate 6, MIDI note 64 is mapped to Note Gate 7 and MIDI note 65 is mapped to Note Gate 8. These are suitable for triggering percussive sounds.

The range of the pitch output is approximately 5 octaves. Velocity is mapped to a voltage range of 0 - 5 volts. The note gate outputs are 0V for note off, and 5V for note on.

A set of four MCP4922 dual DAC chips are used. These provide 12-bit accuracy with a buffered output. A 100 ohm resistor is used between the DAC output and the CV output for pitch. A 1000 ohm resistor is used between the DAC output and the CV output for velocity. Each gate output pin is routed directly from the Teensy to the gate output socket via a 1000 ohm resistor.

The code works for Teensy 2.0 and 3.6, as well as other models. Simply adjust cs_offset (for chip select offset for the DACs) and gate_offset (for gate pin offset), and route the pin outputs accordingly.

More information about connecting an MCP4922 can be found here.

These are ideas I am still fleshing out. Once I am happy I will make a more in-depth tutorial, covering the setup and mapping in more detail.

In the meantime, download the code so far here: http://milkcrate.com.au/_other/downloads/arduino/multi_cv/multi_cv.ino