RealBook

Jazz Standards Data Provider for Elixir

RealBook is an Elixir library that provides access to 1,193 jazz standards with chord progressions from the Real Book. It's designed to be a simple, read-only data provider for other music projects in the Elixir ecosystem.

Features

Installation

Add real_book to your list of dependencies in mix.exs:

def deps do
  [
    {:real_book, path: "../real_book_ex"},
    {:harmony, "~> 0.1.2"}
  ]
end

Or if using from a local path:

def deps do
  [
    {:real_book, path: "../real_book_ex"}
  ]
end

Note: Harmony is automatically included as a dependency.

Quick Start

# Get a specific song
song = RealBook.get("Autumn Leaves")
# => %RealBook.Song{
#      title: "Autumn Leaves",
#      author: "Kosma, Joseph",
#      year: "1947",
#      genre: "jazz",
#      form: "A,A,B,A",
#      sections: %{...}
#    }

# Get all songs
all_songs = RealBook.all()
# => [%Song{}, %Song{}, ...] (1,193 songs)

# Get song count
RealBook.count()
# => 1193

# Search by author
coltrane_songs = RealBook.search(author: "Coltrane")
# => [%Song{title: "Giant Steps", ...}, %Song{title: "Impressions", ...}, ...]

# Get random song for practice
practice_song = RealBook.random()

API Reference

Core Functions

RealBook.get(title)

Get a song by its exact title. Returns an empty %RealBook.Song{} if not found.

RealBook.get("So What")
# => %Song{title: "So What", author: "Davis, Miles", ...}

RealBook.all()

Returns a list of all 1,193 songs.

all_songs = RealBook.all()
length(all_songs)
# => 1193

RealBook.count()

Get the total number of songs in the database.

RealBook.count()
# => 1193

Search & Discovery

RealBook.search(criteria)

Search for songs matching specific criteria. Supports multiple filters:

# Find all Coltrane compositions
RealBook.search(author: "Coltrane")

# Find songs from 1959
RealBook.search(year: "1959")

# Combine multiple criteria
RealBook.search(author: "Davis", genre: "jazz")

# Use regex for title search
RealBook.search(title: ~r/Blue/)

RealBook.random(opts \\ [])

Get a random song, optionally matching criteria.

# Random song from entire collection
RealBook.random()

# Random bebop song
RealBook.random(genre: "bebop")

# Get 5 random Miles Davis songs
RealBook.random(author: "Davis", count: 5)

RealBook.authors()

List all unique composers/authors in alphabetical order.

RealBook.authors()
# => ["Blake, Ran", "Blakey, Art", "Bloom, Rube", ...]

RealBook.genres()

List all unique genres in the database.

RealBook.genres()
# => ["jazz", "bebop", "swing", ...]

Chord Progressions

RealBook.progression(title)

Get a simplified chord progression structure for a song.

RealBook.progression("Giant Steps")
# => [
#   %{
#     section: "A",
#     key: "B",
#     time: [4, 4],
#     measures: [
#       ["Bmaj7", "D7"],
#       ["Gmaj7", "Bb7"],
#       ...
#     ]
#   },
#   ...
# ]

RealBook.chords(title)

Get a flat list of all unique chords used in a song.

RealBook.chords("All The Things You Are")
# => ["Fmaj7", "Bbm7", "Eb7", "Abmaj7", "Dbmaj7", "G7", ...]

Data Structures

Song

%RealBook.Song{
  title: String.t(),        # "Autumn Leaves"
  author: String.t(),       # "Kosma, Joseph"
  year: String.t(),         # "1947"
  genre: String.t(),        # "jazz"
  form: String.t(),         # "A,A,B,A"
  sections: %{              # Map of section name to Section struct
    "A" => %RealBook.Section{...},
    "B" => %RealBook.Section{...}
  }
}

Section

%RealBook.Section{
  name: String.t(),              # "A", "B", "Bridge", etc.
  key: String.t(),               # "C", "F#", etc.
  time: [integer()],             # [4, 4] for 4/4 time
  measures: [RealBook.Measure.t()]
}

Measure

%RealBook.Measure{
  chords: [RealBook.ChordBeat.t()]
}

ChordBeat

%RealBook.ChordBeat{
  beats: float(),            # Duration in beats (2.0, 4.0, etc.)
  chord: Harmony.Chord.t()   # Full Harmony chord struct
}

Use Cases & Examples

Practice & Education

# Random song generator for practice sessions
defmodule PracticeSession do
  def daily_song do
    song = RealBook.random()

    """
    Today's practice: #{song.title} by #{song.author} (#{song.year})
    Form: #{song.form}
    Tempo suggestion: #{suggest_tempo(song.genre)}
    """
  end

  # Practice specific skills
  def practice_ii_v_i do
    RealBook.all()
    |> Enum.filter(fn song ->
      chords = RealBook.chords(song.title)
      has_ii_v_i_pattern?(chords)
    end)
    |> Enum.random()
  end
end

Music Analysis & Research

# Analyze chord complexity across different eras
defmodule JazzAnalysis do
  def complexity_by_decade do
    RealBook.all()
    |> Enum.group_by(&decade_from_year/1)
    |> Enum.map(fn {decade, songs} ->
      avg_chords = songs
        |> Enum.map(&(length(RealBook.chords(&1.title))))
        |> Enum.sum()
        |> div(length(songs))

      {decade, avg_chords}
    end)
  end

  # Find songs with specific harmonic features
  def find_modal_jazz do
    RealBook.search(genre: "jazz")
    |> Enum.filter(fn song ->
      progression = RealBook.progression(song.title)
      is_modal?(progression)
    end)
  end
end

Interactive Applications

# Web API for jazz standards
defmodule JazzAPI do
  # Phoenix controller example
  def show(conn, %{"title" => title}) do
    song = RealBook.get(title)

    json(conn, %{
      title: song.title,
      author: song.author,
      year: song.year,
      chords: RealBook.chords(song.title),
      form: song.form
    })
  end

  # Random song endpoint
  def random(conn, params) do
    filters = build_filters(params) # author, genre, year
    song = RealBook.random(filters)

    json(conn, serialize(song))
  end
end

# Chat bot integration
defmodule MusicBot do
  def handle_message("!standards " <> query) do
    songs = RealBook.search(author: query)

    songs
    |> Enum.take(5)
    |> Enum.map(&"#{&1.title} (#{&1.year})")
    |> Enum.join("\n")
  end
end

Transposition & Arrangement

# Transpose songs for different instruments
defmodule Transposer do
  def for_instrument(song_title, instrument) do
    transposition = case instrument do
      :alto_sax -> "6M"  # Eb instrument
      :tenor_sax -> "9M" # Bb instrument
      :trumpet -> "2M"   # Bb instrument
      _ -> "1P"          # Concert pitch
    end

    RealBook.chords(song_title)
    |> Enum.map(&Harmony.Chord.transpose(&1, transposition))
  end

  # Find songs in a vocalist&#39;s range
  def songs_in_key(preferred_key) do
    RealBook.all()
    |> Enum.filter(fn song ->
      sections = Map.values(song.sections)
      Enum.any?(sections, &(&1.key == preferred_key))
    end)
  end
end

Learning Tools & Flashcards

# Chord progression trainer
defmodule ChordTrainer do
  def quiz_mode do
    song = RealBook.random()
    progression = RealBook.progression(song.title)

    first_section = List.first(progression)

    %{
      question: "What comes after these chords in #{song.title}?",
      given: Enum.take(first_section.measures, 4),
      answer: Enum.drop(first_section.measures, 4) |> Enum.take(4)
    }
  end

  # Ear training: guess the standard
  def name_that_tune do
    song = RealBook.random()
    first_four = song
      |> RealBook.progression()
      |> List.first()
      |> Map.get(:measures)
      |> Enum.take(4)

    %{chords: first_four, answer: song.title}
  end
end

Repertoire Building

# Setlist generator for gigs
defmodule SetlistBuilder do
  def create_setlist(duration_minutes, tempo \\ :medium) do
    songs_per_set = div(duration_minutes, 5) # ~5 min per song

    RealBook.random(count: songs_per_set)
    |> balance_tempos()
    |> balance_keys()
    |> order_by_energy()
  end

  # Find contrafacts (songs sharing the same changes)
  def find_contrafacts(song_title) do
    target_progression = RealBook.progression(song_title)

    RealBook.all()
    |> Enum.filter(fn song ->
      similar_progression?(
        RealBook.progression(song.title),
        target_progression
      )
    end)
  end
end

Data Source

The .jazz files in the data/ directory use a Humdrum-inspired format with metadata and chord changes:

!!!OTL: Autumn Leaves
!!!COM: Kosma, Joseph
!!!ODT: 1947
**jazz
*>[A,A,B,A]
*>A
*M4/4
*C:
2C:min7
2F7
=
...

Roadmap

Current (v0.1.0)

Future

Architecture

RealBook uses a simple GenServer architecture:

  1. Application Start - Loads all 1,193 .jazz files from data/
  2. Parse - Converts text format to structured Elixir data
  3. Index - Stores songs in memory by title for O(1) lookup
  4. Serve - Provides fast, read-only access via public API

All data is loaded at compile/start time, making runtime access extremely fast.

Development

# Get dependencies
mix deps.get

# Run tests
mix test

# Run performance benchmarks
mix bench

# Generate documentation
mix docs

# Interactive session
iex -S mix

Testing

# Run all tests
mix test

# Run specific test file
mix test test/real_book/songs_test.exs

License

MIT

Credits