MIDI2CV
Convert MIDI messages to control voltage signals for use with a modular synthesizer
Install / Use
/learn @dlynch7/MIDI2CVREADME
MIDI2CV
Convert MIDI messages to control voltage signals for use with a modular synthesizer
Jump to Arduino Program if you want to see the current Arduino program. Otherwise, read on!
Contents
Motivation
Today, most music keyboards and synthesizers use MIDI (Musical Instrument Digital Interface), a digital communication protocol almost universally accepted in the electronic music industry. However, before MIDI's emergence in the 1980s, analog synthesizers often used control voltage (CV) signals for control (surprisingly!) and communication.
I recently started building a modular synthesizer, using the Music From Outer Space (MFOS) series of modules as a starting point, and I wanted to play it with my keyboards and the DAW (digital audio workstation) on my computer.
With a device to convert MIDI messages to CV signals, I could play my modular synth using any MIDI-capable keyboard or my computer. While using a keyboard to play the modular synth makes improvising and jamming easier and more fun, I'm especially excited about the possibility of writing music on my computer and playing it back on the modular synth, because this will greatly expand my toolset for writing and recording music.
Plan
This project is essentially a specialized digital-to-analog converter (DAC). While there are already many options for MIDI-to-CV conversion, I wanted to make my own converter.
In retrospect, this was a great decision
- I learned a lot more about MIDI and about the CV method it replaced.
- I gained an appreciation for the level of complexity required to generate some very common synth sounds (like this one!). I also really gained an appreciation for the complexity required for polyphony in synthesizers!
- Designing and building this converter gave me an opportunity to apply what I'd learned in my coursework and improve my troubleshooting skills.
While there are some MIDI-to-CV converters out there that do the job without resorting to a microcontroller, I wanted to go the microcontroller route, for a few reasons:
- it's easier to reprogram a _μ_C than it is to rebuild a circuit
- more documentation available
- more room for error/experimentation
- I already had an Arduino Uno on hand
Given the last bullet point above, my converter could be represented like this:

Details
Sort of. Rather than try to explain the MIDI communication protocal myself, here are some other websites and people who've done a great job explaining it:
- This Instructable gives a comprehensive and comprehensible overview of MIDI. The author also wrote an Instructable about using an Arduino to send and receive MIDI messages; this particular Instructable was an invaluable resource throughout my project.
- This blog post also gives a clear description of the MIDI input circuit.
- Here is a library of code for Arduino to interface with MIDI. This is what I used to interface with my MIDI keyboard.
The schematic below shows the three circuits required by MIDI. Since my project only has a MIDI input, I only used the topmost circuit.

Note: MIDI transmits at a baud rate of 31250. The Arduino IDE doesn't support this baud rate, so if you want to print data for debugging purposes, you'll have to use an additional program to view serial data. I use PuTTY. Just follow this tutorial.
Briefly, MIDI uses bytes to convey a wide array of musical information, such as
- note On/Off (are you pressing a key?)
- pitch (what key are you pressing?)
- velocity (how hard are you pressing the key?)
- pitchbend (most MIDI keyboards have a control wheel for bending a note up or down)
- modulation (most MIDI keyboards also have a modulation wheel that can be assigned to modulate another parameter, such as vibrato, filter cutoff frequency, etc.)
- sustain (are you pressing down on a sustain pedal, like on a piano?)
There are many, many more MIDI messages available, but these are some of the most common and are also the ones I use most often when playing.
CV conveys the same information as MIDI but does it differently, using both analog and logic-level signals.
- MIDI note on/off messages map to logic-level GATE and TRIGGER CV signals.
- Pretty much every other MIDI message maps to an analog CV signal
- pitch
- velocity
- modulation The beauty of CV is that these analog CV signals are interchangeable. This flexibility opens more possibilities for sound design. Here are just a few examples:
- velocity CV -> filter cutoff frequency (sound brightens with harder keypresses)
- pitch CV -> oscillator mix (use pitch to blend between two different waveforms)
- modulation -> low frequency oscillator (LFO) amplitude (LFOs are another signal generator that will also output CV signals and can modulate more aspects of your sound)
Digital-to-Analog Converter Options
I considered two methods for converting the Arduino's digital output to analog CV signals, and I actually wound up using both methods.
- Dedicated DAC chip
- Pulse Width Modulation (PWM) + Low-Pass Filter (LPF).
Before this project, I never used a dedicated DAC, and because I initially felt hesitant about that method, I decided to see if I could do all my digital-to-analog conversion using filtered PWM.
Filtered PWM
The Arduino's analogWrite() function uses PWM to approximate a voltage between 0V and 5V. While some devices (LEDs, DC motors) can be controlled directly by PWM, the synthesizer is not one of those devices (unless you want to generate some interesting 'talking-robot' sounds). For PWM to interface nicely with my modular, it had to be low-pass filtered to 'smooth out' the signal. The simplest LPF is a resistor-capacitor (RC) circuit, as shown below, so I started there, with the expectation that a more sophisticated filter might be necessary.

A buffer is necessary to prevent whatever is connected to the filter's output from loading down the filter. This can be done with a voltage-follower, shown below with the original RC circuit.

Note: I might use "bandwidth frequency" and "cutoff frequency" interchangeably, because for low-pass filters, they mean the same thing.
An RC circuit is a 1-pole filter, which means that above its cutoff frequency, the filter has a slope of -20 dB per decade (i.e. if the filter attenuates a 100 Hz input signal by 20 dB, it will attenuate a 1000 Hz input signal by 40 dB). The Bode plot below illustrates the frequency response of this circuit.

This Bode plot is also quite helpful for understanding my design process; the process applies for higher-order filters, too, and the only difference is that the Bode plot would look different (it would have a steeper slope).
For clarity, I want to list the parameters relevant to using an RC circuit to filter a PWM signal from the Arduino.
- Arduino PWM frequency
- LPF cutoff frequency
- PWM amplitude
- Ripple amplitude (amplitude of filtered PWM signal)
For the RC circuit described above, the cutoff frequency is a function of the resistor and capacitor values, R and C. Also, the frequency and amplitude of the Arduino's PWM signal are fixed: 490 Hz (980 Hz on pins 5 and 6) and 5V, respectively.
The design procedure then is this:
1. Determine a desirable ripple magnitude.
2. Given the filter's -20 dB/dec slope, calculate the cutoff frequency required to achieve the ripple amplitude.
1-pole LPF math
The transfer function for an RC LPF is
[1]
The bandwidth frequency or cutoff frequency of a filter is the frequency at which the filter's output is 3 dB less than its DC level. For an RC LPF, this is simply -3 dB.
[2]
Solving for ω gives the cutoff frequency as a function of R and C:
[3]
Define RC as a time constant τ, and the equation above can be rewritten as
[4]
Below, I'll describe how I followed this procedure to design a filter to smooth out a CV signal which could be used for pitch control:
1-pole LPF for Pitch CV
Spoiler Alert! This design doesn't work (as I'll explain below). That's why there are two section below labeled Sallen-Key Filter and 2-pole Sallen-Key LPF for Pitch CV. Nevertheless, it's important to understand why this filter didn't work and why a more sophisticated fil
Related Skills
node-connect
344.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
96.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
344.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
344.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
