Let’s implement a minimalist MIDI software synthesizer by using sine waves!

**Sample output:**

## About MIDI Synthesis

Software synthesizers convert MIDI data into audio signals to be either played through an audio device or stored to a file.

### MIDI Data Examples

**Note On Event**: a note starts playing with a given velocity at a given time.**Note Off Event**: a note stops playing at a given time.

Based on MIDI data, software synthesizers implement techniques in order to emulate the sound of real instruments.

## A Minimal MIDI Synthesizer

Our synth aims at being as simple and fun to implement as possible.

Basically, for each playing note in the MIDI file we compute the corresponding sine wave and add it to the final waveform we’ll listen to.

*Step 1: MIDI Data Collection*

We collect the following MIDI data for each note to be played.

, which is the MIDI note number;*note*, which says how hard the note is played (from 0 to 127);*velocity*and*start-*, which say when the note starts and stops playing (in seconds).*end-time*

Let *S* denote the __total number of seconds__ of the MIDI tune (i.e. the maximum *end-time*).

*Step 2: Waveform Initialization + Time Array*

We zero-initialize the __waveform array__ *W* of length ⌈44100×*S*⌉ that we’ll populate with the amplitude values (i.e. the samples) of the final waveform.

Namely, *W* will contain a number of 44100 samples for each second in the MIDI tune. (Notice that 44100 is a standard sampling rate for audio.)

We also create the __time array__ *T* related to *W* by computing all the ⌈44100×*S*⌉ equidistant units of time (i.e. time steps) from 0 to *S*, namely

- The
*i*-th time step*T*[*i*] is the time of the*i*-th sample*W*[*i*]; in other words - The
*i*-th sample*W*[*i*] stores the amplitude of the final waveform*W*at time*t*=*T*[*i*].

*Step 3: Sine Waves Computation + Additive Synthesis*

For each collected tuple (*note*, *velocity*, *start-time*, *end-time*) we compute the corresponding sine wave and add it to *W*:

- Get the frequency
*f*associated to the*note*; - Compute the amplitude
*A*=*velocity*÷ 127, i.e. normalize the*velocity*to a 0-1 range; - Let
*s*and*e*denote*start-*and*end-time*, respectively; - Compute the sine wave
*A*· sin(2π ·*f*·*t*), for each*t*such that*s*≤*t*≤*e*. - Add the sine wave to the waveform array
*W*.

Summing up, we add the computed sine wave to *W* at the corresponding *t*-values as follows:

- For each
*i*from ⌊44100×*s*⌋ to ⌊44100×*e*⌋, do*t*=*T*[*i*];*W*[*i*] =*W*[*i*] +*A*· sin(2π ·*f*·*t*).

**To understand Step 3 better, read:**

- “
*Note names, MIDI numbers and frequencies*” by Joe Wolfe, on how to compute a note frequency w.r.t. its distance to the note A4 (440 Hz), namely*f*= 2^{(note distance from A4)÷12}× 440; - “
*Sinusoidal Waves as Sound*” by Jonathan Rogness, on how to play multiple notes by adding their sine waves together, which includes nice plots and examples.

*Final Step: Waveform Playback*

By default we automatically play the computed waveform *W* through the speakers. On user request, we store it on a specified WAV file without playing it.

## The Implementation

**See the example and get the code!**

This work was initially inspired by Robert Elder’s article *“Compose Music From Entropy in /dev/urandom”*.