# 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
- **1,193 Jazz Standards** - Comprehensive collection of classic jazz songs
- **Structured Chord Progressions** - Song sections, measures, and chord changes
- **Harmony Integration** - Full integration with the [Harmony](https://hex.pm/packages/harmony) library for music theory
- **Fast In-Memory Access** - GenServer loads all songs at startup for instant retrieval
- **Search & Filter** - Find songs by author, genre, year, or title
- **Clean API** - Simple, functional interface designed for composition
## Installation
Add `real_book` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:real_book, path: "../real_book_ex"},
{:harmony, "~> 0.1.2"}
]
end
```
Or if using from a local path:
```elixir
def deps do
[
{:real_book, path: "../real_book_ex"}
]
end
```
Note: Harmony is automatically included as a dependency.
## Quick Start
```elixir
# 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.
```elixir
RealBook.get("So What")
# => %Song{title: "So What", author: "Davis, Miles", ...}
```
#### `RealBook.all()`
Returns a list of all 1,193 songs.
```elixir
all_songs = RealBook.all()
length(all_songs)
# => 1193
```
#### `RealBook.count()`
Get the total number of songs in the database.
```elixir
RealBook.count()
# => 1193
```
### Search & Discovery
#### `RealBook.search(criteria)`
Search for songs matching specific criteria. Supports multiple filters:
- `:author` - Substring match (case-insensitive)
- `:genre` - Exact match
- `:year` - Exact match
- `:title` - Substring match or regex
```elixir
# 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.
```elixir
# 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.
```elixir
RealBook.authors()
# => ["Blake, Ran", "Blakey, Art", "Bloom, Rube", ...]
```
#### `RealBook.genres()`
List all unique genres in the database.
```elixir
RealBook.genres()
# => ["jazz", "bebop", "swing", ...]
```
### Chord Progressions
#### `RealBook.progression(title)`
Get a simplified chord progression structure for a song.
```elixir
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.
```elixir
RealBook.chords("All The Things You Are")
# => ["Fmaj7", "Bbm7", "Eb7", "Abmaj7", "Dbmaj7", "G7", ...]
```
## Data Structures
### Song
```elixir
%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
```elixir
%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
```elixir
%RealBook.Measure{
chords: [RealBook.ChordBeat.t()]
}
```
### ChordBeat
```elixir
%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
```elixir
# 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
```elixir
# 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
```elixir
# 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
```elixir
# 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'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
```elixir
# 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
```elixir
# 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)
- ✅ 1,193 jazz standards with chord progressions
- ✅ Search and filter API
- ✅ Harmony integration
- ✅ GenServer for fast in-memory access
### Future
- 📋 Melody data from PDF Real Books (using OCR/OMR)
- 📋 MusicXML import/export
- 📋 MIDI generation from chord progressions
- 📋 Scale recommendations for improvisation
- 📋 Voice leading analysis
## 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
```bash
# Get dependencies
mix deps.get
# Run tests
mix test
# Run performance benchmarks
mix bench
# Generate documentation
mix docs
# Interactive session
iex -S mix
```
## Testing
```elixir
# Run all tests
mix test
# Run specific test file
mix test test/real_book/songs_test.exs
```
## License
MIT
## Credits
- Jazz standard data sourced from the iRb Corpus 1.0
- Built with [Harmony](https://hex.pm/packages/harmony) for music theory
- Part of the ~/dev/music Elixir music ecosystem