b2bAI: Evolutionary MIDI Generation

b2bAI is an interactive audio plugin that uses evolutionary algorithms to generate, continue, and mutate MIDI sequences. Developed as my undergraduate thesis in Computer Science at the University of Bucharest, the project brings algorithmic composition directly into digital audio workstations (DAWs) as a collaborative, real-time creative tool.

Plugin Interface

The Core Concept

Most algorithmic composition systems function as autonomous black boxes, generating entire pieces without human intervention. b2bAI treats algorithmic music as a collaborative process. The musician sets musical constraints, including syncopation levels, scale modes, consonance ratios, and note densities, while genetic algorithms evolve candidate phrases in real time.

System Architecture

The software pairs a high-performance C++ audio plugin with a Python evolutionary engine:

System Architecture

Evolutionary Mechanics

Chromosome Representation

Musical sequences are represented as discrete metric pulse arrays (ticks). Each Gene struct stores:

When mutation or crossover alters note lengths across several ticks, custom decorators repair remaining_ticks across consecutive genes to maintain rhythmic continuity.

Individual: [ (C4, 90, 3), (C4, 90, 2), (C4, 90, 1), (Rest, 0, 0), (G4, 80, 2), (G4, 80, 1) ]

Generation with Multi-Objective Optimization (NSGA-II)

The generate command evolves melodic phrases from scratch using NSGA-II selection across three fitness criteria:

  1. Syncopation: Measured via the Weighted Note-to-Beat Distance (WNBD) metric or rhythmic Off-beatness.
  2. Note Density: The ratio of sounding pulses to total bar length.
  3. Consonance Ratio: The proportion of pitches matching the selected root key and mode (Major, Minor, Dorian, Phrygian, Lydian, Mixolydian, or Locrian).

Fitness Evolution

Continuation and Combination via Normalized Compression Distance (NCD)

The continue and combine functions extend musical motifs or merge two parent sequences. To measure how closely a candidate phrase matches the structural patterns of a seed melody, the fitness function computes the Normalized Compression Distance (NCD):

\[NCD(x, y) = \frac{\max(C(xy) - C(x), C(yx) - C(y))}{\max(C(x), C(y))}\]

$C(x)$ estimates the Kolmogorov complexity of a sequence using dictionary compression (LZ77, LZ78, or LZW). Phrases that compress efficiently alongside the reference motif receive higher fitness scores.

Plugin Features

Piano Roll

Technical Stack

Github