Skip to main content

songbird-clips

Clip model, Bird file parser/serializer, and MIDI generation. This crate handles everything related to clips on the timeline — audio file references, MIDI note data, and the Bird text format that Songbird uses for human-readable project representation.

Bird Format Overview

Bird is a text-based musical notation — a domain-specific language (DSL) for electronic music composition. It sits in a unique niche: more structured than live-coding languages (TidalCycles, Sonic Pi), more compact than MusicXML, and more human-readable than binary DAW formats (FLP, ALS, Logic). After analyzing Bird against 12 other formats/tools, the design is fundamentally sound with a few gaps worth addressing.

Key Strengths

  • Git-friendly: Text-based, line-oriented — diffs are meaningful and mergeable
  • Human-readable: A musician can read and edit .bird files directly
  • Compact: An entire arrangement fits in a few dozen lines
  • Section/arrangement model: Sections define content, arrangements define structure — enables pattern cycling and reuse
  • DAW-native concepts: Sends, returns, effects, channel strips, plugins, automation — first-class citizens in the format
  • MPE support: Per-note pitch bend (bend), pressure (press), and slide (slide) data
  • Meter support: Time signatures via meter in the sig block (e.g., meter 3/4, meter 7/8)

Module Map

songbird-clips/src/
├── lib.rs              — Clip, ClipSource, MidiNote structs
├── audio_clip.rs       — Audio clip loading and sample management
├── bird_ast.rs         — Bird AST types (BirdFile, Section, Track, Pattern, Note, ...)
├── bird_tokenizer.rs   — Lexer: .bird text → token stream
├── bird_parser.rs      — Parser: token stream → BirdFile AST
├── bird_serializer.rs  — Serializer: BirdFile AST → .bird text
├── bird_populator.rs   — Populator: BirdFile AST → engine tracks/clips/MIDI
└── midi_gen.rs         — MIDI generation utilities (chord expansion, drum patterns)

Core Types

Clip

A region on the timeline referencing audio or MIDI data:
pub struct Clip {
    pub id: String,
    pub name: String,
    pub start_beat: f64,       // Position on timeline
    pub length_beats: f64,     // Duration
    pub offset_beats: f64,     // Trim offset into source
    pub source: ClipSource,    // Audio file or MIDI notes
    pub gain: f32,             // Linear gain
    pub muted: bool,
}

pub enum ClipSource {
    AudioFile { path: String, sample_rate: f64 },
    Midi { notes: Vec<MidiNote> },
}

pub struct MidiNote {
    pub beat: f64,
    pub duration_beats: f64,
    pub note: u8,          // MIDI note number (0-127)
    pub velocity: u8,      // MIDI velocity (0-127)
    pub channel: u8,       // MIDI channel (0-15)
}

Bird Format Pipeline

The Bird format pipeline transforms .bird text through four stages:
.bird text → Tokenizer → Parser → BirdFile AST → Populator → Engine tracks/clips

                                   Serializer → .bird text (round-trip)

Example Bird File

sig tempo 128 key Cm

section Intro bars 4
  track Bass type midi
    pattern P1
      C2 8th | . | E2 8th | . | G2 8th | . | Bb2 8th | .
    play P1 x4

  track Drums type drum
    pattern Beat
      kick 4th | . | snare 4th | . | kick 8th | kick 8th | snare 4th | .
    play Beat x4

AST Types

pub struct BirdFile {
    pub tempo: f64,
    pub key: String,
    pub time_signature: (u32, u32),
    pub sections: Vec<Section>,
}

pub struct Section {
    pub name: String,
    pub bars: u32,
    pub tracks: Vec<BirdTrack>,
}

pub struct BirdTrack {
    pub name: String,
    pub track_type: TrackType,    // Midi, Drum, Audio
    pub patterns: Vec<Pattern>,
    pub arrangement: Vec<PlayDirective>,
}

Testing

cargo test -p songbird-clips
Tests cover tokenizer round-trips, parser edge cases, serializer fidelity, and populator output.