Building a MIDI Synthesizer in Julia: Recreating Plantasia

Mother Earth's Plantasia is a cult classic electronic album entirely composed using Moog synthesizers. The album was released in 1976 and intended to be listened to by plants. Some of my favorite tracks are "Plantasia," "Symphony for a Spider Plant," and "Swingin’ Spathiphyllums." A college roommate introduced me to Plantasia, and during a camping trip, I visited the Moog Museum in Asheville, NC. This inspired me to experiment with synthesizers to recreate the titular track.

Listen to Plantasia

Here's some output from the synthesizer:

Technical Implementation

MIDI Processing

The synthesizer takes MIDI files as input, which contain:

  • Note events (pitch, velocity, timing, noteOn, noteOff)
  • I used MIDI.jl to parse MIDI files and leveraged its convenient getNotes function to extract all notes from each track.
  • Metadata (tempo changes, pitch bends, etc. - currently ignored in this version)

Each note contains:

  • pitch: Starting from C-1 = 0, incrementing by semitone
  • velocity: How loud the note is (0-127)
  • position: Absolute time position in ticks
  • duration: Length in ticks

Synthesis Components

  1. Waveform Generation

    Basic waveforms implemented using Fourier series:

    • Sine wave with vibrato
    • Square wave
    • Triangle wave
    • Saw wave

    The sine wave supports a vibrato effect, while the other waveforms allow customization of the number of harmonics summed to shape the sound

  2. ADSR Envelope
    • An envelope shapes the dynamics of a sound over time, defining how it starts, sustains, and fades.
    • Attack: Initial rise time
    • Decay: Fall to sustain level
    • Sustain: Maintained amplitude level
    • Release: Fade out to the end of the note
    • Typically, the Release stage begins after a noteOff event. However, in this implementation, the sound stops immediately upon receiving noteOff.

    Prevents clicking and adds natural dynamics.

  3. Instrument Presets

    Each instrument combines:

    • A waveform function
    • ADSR envelope parameters

    Can create varied timbres from basic building blocks.

Core Functions


  # Converts a MIDI track to audio samples
  synthesize_track
  

  
  # Processes full MIDI file with multiple tracks
  synthesize_midi
  
  # Generates ADSR envelope
  generate_adsr_envelope
        

Audio Output

Renders to WAV files at 44.1kHz

Current Limitations

  • Metadata tracks (like pitch bend) are not processed
  • Performance optimization is needed for real-time playback and for all-around faster synthesis. It takes around 10 seconds to generate audio for longer files like plantasia and Lacrimosa, and less than two seconds for Avril 14th.
  • Limited set of basic waveforms and synthesis methods

Future Improvements

Topics that future posts could explore:

  • Performance optimizations
  • Effects such as non-linear distortion and delay
  • Additional synthesis techniques
  • Extended MIDI feature support
  • Drum kit synthesis
  • Sampling

The code is a work in progress, but it successfully renders simple MIDI files into listenable audio.

Bonus Track

Avril 14th

GitHub Repository Link