Sunday, June 17, 2018

Exo Sequencer for Eurorack



The Exo Sequencer (as in... exotic, external, exoplanet, exothermic...) is a sixteen step sequencer with control voltage and trigger outputs. The control voltage range is relatively small (around 3V), and is not quantised. 




The width of the module is 18HP. 

The step order is selected in a number of ways: 
• Sending a gate or trigger to the INC input will increment the sequence
• Sending a gate or trigger to the DEC input will decrement the sequence
• Sending a combination of four LOW or HIGH signals to the four inputs A0, A1, A2 and A3 will select the step as a binary address e.g. 0000 will select step 1, 0010 will select step 3, 0111 will select step 8, 1111 will select step 16 and so on
• Sending a control voltage to the STEP input will directly select the step number, where 0V is step 1 and roughly 3.3V is step 16. This control voltage can be scaled using the STEP potentiometer
These methods for selecting the step order are not mutually exclusive, and can be used in conjunction with one another. 

The length of the sequence (used for calculating when using the INC, DEC or STEP inputs) is selected via the LENGTH potentiometer and can be modulated via a control voltage input. 

The offset of the sequence (determining the starting step) is selected via the OFFSET potentiometer and can be modulated via a control voltage input. 

Circuit board layouts, schematics, code, panel files for laser cutting and a bill of materials can be found here: https://github.com/little-scale/eurorack/tree/master/exo_sequencer 

This module is also on Modular Grid
A demonstration video is coming soon. This module can be seen and heard in some of the videos in this post






Tuesday, June 12, 2018

little-scale: Atomic Century (2018)

Friday, June 08, 2018

Eurorack Patch Examples of Sequencing, LFOs and MIDI Control

Here are a few examples of patches and modules I've been making lately.















Sunday, June 03, 2018

Monophonic USB MIDI to CV Converter with 8 Octave Range

This is a USB-powered monophonic USB MIDI to CV converter with a roughly 8 octave range, from roughly -3.3V to 5V for pitch CV, 0V to 4.096V for velocity CV and a 3.3V gate for note on / note off events. The DAC is 12 bits, meaning that the range of 8.32V at a rate of 1V per octave is distributed across 4096 steps, yielding a pitch resolution of approximately 2.43 cents per data step.

The Teensy LC separates an incoming MIDI note message into values for pitch CV, velocity CV and a note gate. The pitch and velocity values are sent to an MCP4822 dual 12-bit DAC.

The first channel of the MCP4822 is amplified by two using the first op-amp of a TL072, and then offset by negative 3.3V by using the second op-amp of a TL072 resulting in a pitch CV range of approximately -3.3V to 5V. The velocity CV and note gate are fed directly to the Eurorack setup via a 1k resistors.

A Digilent Powerbrick uses 5V from the VIN on the Teensy LC and transforms this into  ±12V power supply, used to power the TL072.

This type of setup and can be refined and scaled to higher bit depth resolutions by using various DACs, different voltage ranges by changing the ratio between R2 and R1 (for scale) and feeding a different voltage into R8 (for offset).

#include <SPI.h>

int cs_pin = 0;
int gate_pin = 14;
int amp_gain = 0; // gain for amplifier - 0 = 2x, 1 = 1x
int previous_pitch;
float pitch_change_value;
float pitch_value;
float maximum_voltage = 8.32; // voltage output after op amps
float maximum_pitch_value = maximum_voltage * 12.0; 
float number_of_steps = 4096; // DAC resolution

void setup() {
  pinMode(cs_pin, OUTPUT);
  pinMode(gate_pin, OUTPUT);
  digitalWriteFast(cs_pin, HIGH);

  SPI.begin();
  usbMIDI.setHandleNoteOn(OnNoteOn);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandlePitchChange(OnPitchChange);

}

void loop() {
  usbMIDI.read();
}

void OnNoteOn (byte channel, byte pitch, byte velocity) {
  if (channel == 1) {
    if (velocity > 0) {

      previous_pitch = pitch;
      pitch_value = map(previous_pitch + pitch_change_value, 0.0, maximum_pitch_value, 0.0, number_of_steps);
      writeDAC(cs_pin, 0, pitch_value);
      writeDAC(cs_pin, 1, velocity << 5);
      digitalWriteFast(gate_pin, HIGH);
    }

    else {
      digitalWriteFast(gate_pin, LOW);
    }
  }
}

void OnNoteOff (byte channel, byte pitch, byte velocity) {
  if (channel == 1) {
    digitalWriteFast(gate_pin, LOW);
  }
}

void OnPitchChange (byte channel, int pitch_change) {
  if (channel == 1) {
    pitch_change_value = map((float) pitch_change, 0.0, 16383.0, -12.0, 12.0);
    pitch_value = map(previous_pitch + pitch_change_value, 0.0, maximum_pitch_value, 0.0, number_of_steps);
    writeDAC(cs_pin, 0, pitch_value);
  }
}

void writeDAC (int cs, int dac, int val) {
  digitalWrite(cs, LOW);
  dac = dac & 1;
  val = val & 4095;
  SPI.transfer(dac << 7 | amp_gain << 5 | 1 << 4 | val >> 8);
  SPI.transfer(val & 255);
  digitalWrite(cs, HIGH);
}






Sunday, May 27, 2018

Pitch CV to Frequency Conversion with Offset Opamp and ADC

As a follow on from the previous post, another (related) way of converting a pitch CV signal to a frequency value is to offset any negative voltages, and then divide the pitch CV signal and then measure the voltage. In this case, a Teensy LC is used.

This method makes a few assumptions:
• That pitch CV signals may be unipolar, with negative voltages and positive voltages
• A user can set the offset via a pot, thereby having a variable range in terms of negative and positive voltages
• The pitch CV signal will be 1 volt per octave
• The pitch CV signal will be roughly a 10v range

12V and -12V power supply rails are required. In this case, the Digilent PowerBrick supplies both from USB 5V. A Eurorack power supply could also deliver 12V and -12V.

The pitch CV signal is summed with an offset voltage. The offset voltage is derived by dividing +12v to -12v with a 10k potentiometer. The sum of both signals is fed into an inverting opamp with unity gain, whose output is fed into a second inverting opamp. The resulting signal is a phase-correct unipolar pitch CV signal (assuming the 10k potentiometer is set correctly) in the range of perhaps 0V to 10V.

This signal is then divided by 3 by using a resistor voltage divider, where 20k is on the input side and 10k goes to ground - this will then equate to 1/3V per octave. The output of this voltage divider is then fed through some clamping diodes (such as 1n4148), to limit the range to 0V - 3.3V. The voltage is then read as a 12-bit analog to digital value.

The value range of the ADC conversion is 0 - 4095, and this represents a voltage range of 0V - 3.3V, which in turn represents a pitch range of 0 to 9.9 octaves (at 1/3V per octave) with a resolution of about 2.9 cents.

The ADC conversion value is mapped to MIDI pitch, giving a range of 120 - 132 semitones. This MIDI note value is then converted to frequency using my Arduino mtof library, which can be downloaded here.

The frequency value could be used to control another synthesiser or oscillator, or used for analysis and further conversion.

In this particular example, the resulting frequency value is sent via two MIDI pitch messages - one for the integer portion and one for the floating portion - and then reconstructed as a frequency value for a sine wave in a Max patch. A comparison can then be made to the analog output of a VCO to see how accurate the pitch conversion is. Note that a scaler value of 1.03 corrects for tuning issues - this value may change depending on setup.

#include <mtof.h>
#include <math.h>

float data; 
float pitch; 
float pitch_offset = 0; 
float freq;

float max_voltage_of_adc = 3.3; 
float voltage_division_ratio = 0.3333333333333;  
float notes_per_octave = 12;
float volts_per_octave = 1; 
float scaler = 1.03;

float mapping_upper_limit = (max_voltage_of_adc / voltage_division_ratio) * notes_per_octave * volts_per_octave * scaler;

void setup() {
  analogReadResolution(12); // 12-bit ADC resolution
}

void loop() {
  data = analogRead(0); // read pitch CV as data value using ADC
  pitch = pitch_offset + map(data, 0.0, 4095.0, 0.0, mapping_upper_limit); // convert pitch CV data value to a MIDI note number
  freq = mtof.toFrequency(pitch); // convert MIDI note number to frequency

  /*  To test out the frequency values that are being generated from pitch cv conversion, the frequency value is sent over MIDI pitch bend. 
   *  Pitch bend channel 1 is used for the integer component, and pitch bend channel 2 is used for the float component
   *  The pitch bend messages are re-constituted in a Max patch for testing. 
   *  The pitch bend messages are not intended for use with a 'normal' MIDI synth
   *  This is because they used as a way of conveying the data and not as a regular pitch bend message. 
  */
  
  usbMIDI.sendPitchBend(freq, 1); // split up integer part of the frequency and send as a pitch bend value
  usbMIDI.sendPitchBend(fmod(freq, 1.0) * 10000.0, 2);
  
  delay(1); 
}