Wishful Coding

Didn't you ever wish your
computer understood you?

Microrack: A Small Modular Synthesizer

Inspired by the Modulin, I’ve been making my own synthesizer, starting with a Game Boy violin, adding pressure sensitivity, and adding analog delay.

Over the past weeks I’ve been thinking about how I want to connect everything together. I knew I wanted to make it modular, but also that it had to be small enough to become a portable instrument, and hopefully easy to prototype and not too expensive. So I came up with what I call Microrack, a compact mixed-signal bus that is electronically compatible with CV. I typed up a rough description here. In short, it uses a bus with analog multiplexers for audio, and an I2C bus for control signals.

I started by making the power supply and base board. Ideally you’d have something more efficient and powerful, but I started with a simple half-wave rectifier into linear regulators. The I2C lines are exposed to an external Arduino board that will control the user interface and the digital bus. Here is a rough schematic. One thing that is regrettably absent is any sort of current limit or fuse.

power supply schematic

Then I started working on the first module. I decided to start a little drum machine based on a noise source, a filter, and an envelope generator. The drum machine was mostly driven by the idea to make white noise in discrete logic. The heart of this module is a linear feedback shift register, implemented with two 74HC595 shift registers and a 4030 quad XOR gate.

linear feedback shift register

The shift clock of the registers is driven by an atmega328p. The output clock of the last shift register is driven by a NOT-wired XOR gate to close the feedback loop. The output clock of the first shift register is driven by the atmega at a lower rate, to sample the noise. The outputs of the first shift register are fed to a R-R2 resistor ladder.

resistor ladder

So by controlling the shift clock and the output clock, the bandwidth and randomness of the noise can be controlled. The DAC output is then fed into an opamp to translate from [0 5] V to [-5 +5] V, which is then output via the analog multiplexer. I’m pretty happy with the result.

microrack

Except then I fried the atmega.

Bucket Brigade Delay

So far in my quest to build a synthesizer, I’ve controlled a Game Boy synth with a linear potentionmeter and made a pressure sensing circuit for the SoftPot. Today I want to make an analog delay using a bucket brigade device.

To do this, I used a BL3207 bucket brigade device, and a BL3102 clock generator, which are clones of the MN3207 and MN3102, respectively. The datasheets for the former are a single page and not of much use, but since they are clones, the original datasheets provide more useful information. Most importantly, this reference circuit.

MN3207 reference schematic

I did not build that exact circuit, but kind of used it as a hookup guide for the two chips. The MN3102 basically uses the RC filter to generate two opposing clock signals. It also generates a special voltage that’s 15/16 of Vdd for some reason. The MN3207 then takes an analog input, and with some pull-down resistors, produces a delayed output signal. In the above circuit there is a lot of filtering going on, while my circuit uses a simple first order RC filter for now. Both the delayed output and the input signal are fed into an opamp adder to make a nice feedback loop. In the above video I’m simply turning the feedback gain.

ball of wires

To be honest, the current circuit sounds quite bad and is not very flexible. It just adds a fixed delay and loops it back on itself, going from inaudible to metallic to oscillating in the blink of an eye. The best sound I got out of it is the click track when it’s almost oscillating and becomes this funny tone by itself. Maybe all of the filtering in the reference circuit make it a lot better, but I have some other ideas.

I have like 5 of these delay chips, so it’d be fun to chain them together for a longer delay. The other thing is the clock generator: You can disconnect the RC and drive the oscillator externally. I’m thinking I could create an external clock, and modulate that with an LFO to create a vibrato effect.

Update: Vibrato works like a charm. I simply drove the clock with an Arduino as follows. Then I played a sine wave and some acoustic guitar into it for demonstration.

void setup() {
}

void loop() {
  int i;
  for(i=0; i<2000; i+=30) {
    tone(2, 40000+i);
    delay(1);
  }
  for(i=2000; i>0; i-=30) {
    tone(2, 40000+i);
    delay(1);
  }
}

Sensing pressure with a SoftPot

In my previous post I connected a SoftPot to an Arduino and used a Game Boy as the synth. In this post I’m focusing more on the SoftPot, and specifically on reading pressure.

The SoftPot consists of a resistive bottom layer and a conductive top layer. When pressed together, the bottom resistor forms a voltage divider with the top conductor. When the conductive top side is pressed down, it shorts part of the bottom track, creating a lower total resistance. The harder you press down, the larger the area of the bottom track that is shorted, the lower the resistance.

So to measure both pressure and position, we’ll need to simultaneously read the voltage on the top track, and the resistance of the bottom track. To get an accurate position, we’d like to have the full voltage swing available, without any changes in voltage due to pressure. Meanwhile, we’d like to sense small DC variations in resistance of a large total resistance. This is my best attempt so far.

circuit

It uses a current mirror to maintain an almost fixed voltage on the pot (R1) and only lose 0.6V of the full swing. The current mirror then feeds into a biased transimpedance amplifier. The rightmost voltage divider is used to set the DC output voltage. (I later added a capacitor in parallel to the TIA feedback resistor to reject noise)

In reality the voltage divider on the positive TIA input is a trim pot. The changes in resistance of the SoftPot are smaller than the process variations. For example, this particular pot changes from 20.7k to 20.3k when pressed, so the bias voltage has to be adjusted for this.

sensing pressuer and pitch

In the above figure, the blue line is me sliding my finger up and down, while the yellow line is the pressure of my finger, with the green/red lines being the max/min. This seems to work just fine, except after letting it run for a while the pressure value starts to drift. Remember, we need the constant, absolute value, not some high-pass filtered version.

I was really confused, and tried various things to reproduce the problem and improve the situation. Then I blew on the circuit.

temperature sensitivity

OMG, look at that! The current mirror goes completely crazy when it gets warm. One solution is to add more degeneration, which adds voltage drop. Another is to use a Wilson current mirror, which also adds voltage drop. And finally, you could get two integrated transistors on the same die, which makes them the same temperature and improves matching. I guess that’s what I’ll have to do. To be continued.

const int pitchPin = A0;
const int pressurePin = A1;
const int pwmPin = 12;

const int pressureThreshold = 20;

int pitchValue = 0;        // value from SoftPot
int pressureValue = 0;        // value from TIA
int minPressureValue = INT16_MAX;
int maxPressureValue = 0;
int pressure = 0;
int frequency = 0;
int polarity = 1;

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(1);
  pitchValue = analogRead(pitchPin);
  delay(1);
  pressureValue = analogRead(pressurePin);
  delay(1);
  minPressureValue = min(pressureValue, minPressureValue);
  maxPressureValue = max(pressureValue, maxPressureValue);
  pressure = map(pressureValue, minPressureValue, maxPressureValue, 0, 10);
  frequency = map(pitchValue, 0, 1023, 440, 1320);
  if (pressureValue < minPressureValue+pressureThreshold) {
    pitchValue = 0;
    noTone(pwmPin);
  } else if (pressure > 6) {
    tone(pwmPin, frequency+pressure*polarity);
    polarity *= -1;
  } else {
    tone(pwmPin, frequency);
  }
  Serial.print(pitchValue);
  Serial.print(", ");
  Serial.print(minPressureValue);
  Serial.print(", ");
  Serial.print(maxPressureValue);
  Serial.print(", ");
  Serial.println(pressureValue);
  delay(50);
}