Guess Scale or Chord from Notes
The music theory encoded in AutoMuse allows for some interesting applications. For example, automuse.guesser.guess_scale takes a set of notes, then guesses which scale or chords the notes may be from.
These tools were developed to help with transcription and reharmonisation. This awesome power is now yours to command.
This tutorial covers two functions, guess_scale and guess_chord.
[1]:
import random
random.seed(44313)
from automuse.guesser import guess_scale, guess_chord, tabulate_guess, MatchResult
from automuse.guesser import TEST_SCALES, TEST_SCALES_STANDARD
Note that guess takes two arguments: (a) a set of note names and (b) scales and chords to test against.
The module comes with several options for the latter:
Option |
Included |
|---|---|
|
Major and minor |
|
Natural, harmonic, and melodic minors |
|
Augmented, diminished, pentatonic, and blues |
|
Whole tone and chromatic scales |
|
From Ionian to Locrian |
|
Every scale. Probably has perfect matches for everything. |
|
Triads |
|
Seventh chords |
|
All chords |
Suppose we hear a melody and identify that it contains \(\text{G}\), \(\text{E}\), and \(\text{C}\#\). To transcribe, reharmonise, or improv from it, we would like to assign it a scale.
To find which scale contains these notes, call guess_scale then give the output to tabulate_guess.
The result includes a list of possible scales. While several are equally likely, it does give us some interesting information: because many dissonant scales (e.g. Diminished, Locrian) are matched, we may choose to rock it up a little. Blues also looks feasible.
[2]:
tabulate_guess(guess_scale(["G", "E", "C#"],
TEST_SCALES,
match_order="any")[:10],
tablefmt="html")
[2]:
| Key | Mode | Matched | Unmatched | Unused | Check | Against |
|---|---|---|---|---|---|---|
| C | Dim H-W | ['C#', 'G', 'E'] | [] | ['F#', 'D#', 'C', 'A', 'A#'] | ['G', 'E', 'C#'] | ['C', 'C#', 'D#', 'E', 'F#', 'G', 'A', 'A#'] |
| C# | Dim W-H | ['C#', 'G', 'E'] | [] | ['D#', 'F#', 'C', 'A', 'A#'] | ['G', 'E', 'C#'] | ['C#', 'D#', 'E', 'F#', 'G', 'A', 'A#', 'C'] |
| C# | Dim H-W | ['C#', 'G', 'E'] | [] | ['G#', 'D', 'F', 'B', 'A#'] | ['G', 'E', 'C#'] | ['C#', 'D', 'E', 'F', 'G', 'G#', 'A#', 'B'] |
| C# | m (Blues) | ['C#', 'G', 'E'] | [] | ['B', 'F#', 'G#'] | ['G', 'E', 'C#'] | ['C#', 'E', 'F#', 'G', 'G#', 'B'] |
| C# | Locrian | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['C#', 'D', 'E', 'F#', 'G', 'A', 'B'] |
| D | M | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F#', 'G', 'A', 'B', 'C#'] |
| D | m (Harmonic) | ['C#', 'G', 'E'] | [] | ['D', 'F', 'A#', 'A'] | ['G', 'E', 'C#'] | ['D', 'E', 'F', 'G', 'A', 'A#', 'C#'] |
| D | m (Melodic) | ['C#', 'G', 'E'] | [] | ['D', 'F', 'B', 'A'] | ['G', 'E', 'C#'] | ['D', 'E', 'F', 'G', 'A', 'B', 'C#'] |
| D | Dim W-H | ['C#', 'G', 'E'] | [] | ['G#', 'D', 'F', 'B', 'A#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F', 'G', 'G#', 'A#', 'B', 'C#'] |
| D | Ionian | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F#', 'G', 'A', 'B', 'C#'] |
Refining the Match
You, the musician, are in ultimate control of your creations. You can choose to only match against certain modes, or filter the result:
[3]:
tabulate_guess(guess_scale(["G", "E", "C#"],
TEST_SCALES_STANDARD,
match_order="any")[:5],
tablefmt="html")
[3]:
| Key | Mode | Matched | Unmatched | Unused | Check | Against |
|---|---|---|---|---|---|---|
| D | M | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F#', 'G', 'A', 'B', 'C#'] |
| B | m | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['B', 'C#', 'D', 'E', 'F#', 'G', 'A'] |
| C | M | ['G', 'E'] | ['C#'] | ['D', 'C', 'F', 'B', 'A'] | ['G', 'E', 'C#'] | ['C', 'D', 'E', 'F', 'G', 'A', 'B'] |
| C# | m | ['C#', 'E'] | ['G'] | ['D#', 'F#', 'G#', 'B', 'A'] | ['G', 'E', 'C#'] | ['C#', 'D#', 'E', 'F#', 'G#', 'A', 'B'] |
| D | m | ['G', 'E'] | ['C#'] | ['D', 'F', 'C', 'A', 'A#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F', 'G', 'A', 'A#', 'C'] |
[4]:
match_result: list[MatchResult] =\
[x for x in guess_scale(["G", "E", "C#"],
TEST_SCALES,
match_order="any")
if x.mode == "M"]
tabulate_guess(match_result[:5],
tablefmt="html")
[4]:
| Key | Mode | Matched | Unmatched | Unused | Check | Against |
|---|---|---|---|---|---|---|
| D | M | ['C#', 'G', 'E'] | [] | ['D', 'B', 'A', 'F#'] | ['G', 'E', 'C#'] | ['D', 'E', 'F#', 'G', 'A', 'B', 'C#'] |
| C | M | ['G', 'E'] | ['C#'] | ['D', 'C', 'F', 'B', 'A'] | ['G', 'E', 'C#'] | ['C', 'D', 'E', 'F', 'G', 'A', 'B'] |
| E | M | ['C#', 'E'] | ['G'] | ['D#', 'F#', 'G#', 'B', 'A'] | ['G', 'E', 'C#'] | ['E', 'F#', 'G#', 'A', 'B', 'C#', 'D#'] |
| F | M | ['G', 'E'] | ['C#'] | ['D', 'F', 'C', 'A', 'A#'] | ['G', 'E', 'C#'] | ['F', 'G', 'A', 'A#', 'C', 'D', 'E'] |
| G | M | ['G', 'E'] | ['C#'] | ['F#', 'D', 'C', 'B', 'A'] | ['G', 'E', 'C#'] | ['G', 'A', 'B', 'C', 'D', 'E', 'F#'] |
Matching in Order
You might wish to match against a particular progression, there is an option for that. The match_order parameter can take one of three values:
"any", the default: No order required."order": Match notes in order; other notes between matched notes are allowed."substring": Match exact substrings.
[5]:
tabulate_guess(guess_scale(["C", "C#", "E"],
TEST_SCALES,
match_order="substring")[:5],
tablefmt="html")
[5]:
| Key | Mode | Matched | Unmatched | Unused | Check | Against |
|---|---|---|---|---|---|---|
| F | m (Harmonic) | ['C', 'C#', 'E'] | [] | ['F', 'G', 'G#', 'A#'] | ['C', 'C#', 'E'] | ['F', 'G', 'G#', 'A#', 'C', 'C#', 'E'] |
| F | A | ['C', 'C#', 'E'] | [] | ['F', 'G#', 'A'] | ['C', 'C#', 'E'] | ['F', 'G#', 'A', 'C', 'C#', 'E'] |
| F# | m (Blues) | ['C', 'C#', 'E'] | [] | ['F#', 'A', 'B'] | ['C', 'C#', 'E'] | ['F#', 'A', 'B', 'C', 'C#', 'E'] |
| A | A | ['C', 'C#', 'E'] | [] | ['A', 'F', 'G#'] | ['C', 'C#', 'E'] | ['A', 'C', 'C#', 'E', 'F', 'G#'] |
| A | M (Blues) | ['C', 'C#', 'E'] | [] | ['A', 'B', 'F#'] | ['C', 'C#', 'E'] | ['A', 'B', 'C', 'C#', 'E', 'F#'] |
Match Chords
The function guess_chord is a convenience function that matches notes against standard chords.
Suppose that you would like a chord that contains exact \(\text{B}\), \(\text{F}\), and \(\text{G}\#\). guess_chord is able to check these notes against all roman numerals, all modes, and all inversions, you can call guess_chord. Be aware that because the function needs to construct a chord for every case, it takes significantly longer to run.
[6]:
tabulate_guess(guess_chord(
["B", "F", "G#"],
orders = [0],
inversions = list(range(3)),
match_order="substring")[:10],
tablefmt="html")
[6]:
| Key | Mode | Matched | Unmatched | Unused | Check | Against |
|---|---|---|---|---|---|---|
| F | Dim W-H⁶₄(I) | ['B', 'F', 'G#'] | [] | [] | ['B', 'F', 'G#'] | ['B', 'F', 'G#'] |
| F | Dim H-W⁶₄(I) | ['B', 'F', 'G#'] | [] | [] | ['B', 'F', 'G#'] | ['B', 'F', 'G#'] |
| F | Locrian⁶₄(I) | ['B', 'F', 'G#'] | [] | [] | ['B', 'F', 'G#'] | ['B', 'F', 'G#'] |
| F | Dim W-H⁶(I) | ['B', 'F'] | ['G#'] | ['G#'] | ['B', 'F', 'G#'] | ['G#', 'B', 'F'] |
| F | Dim W-H⁺(I) | ['B', 'F'] | ['G#'] | ['F', 'G#'] | ['B', 'F', 'G#'] | ['F', 'G#', 'B', 'F'] |
| F | Dim W-H⁺⁶(I) | ['B', 'F'] | ['G#'] | ['G#', 'F'] | ['B', 'F', 'G#'] | ['G#', 'B', 'F', 'F'] |
| F | Dim W-H⁺⁶₄(I) | ['B', 'F'] | ['G#'] | ['F', 'G#'] | ['B', 'F', 'G#'] | ['B', 'F', 'F', 'G#'] |
| F | Dim H-W⁶(I) | ['B', 'F'] | ['G#'] | ['G#'] | ['B', 'F', 'G#'] | ['G#', 'B', 'F'] |
| F | Dim H-W⁺(I) | ['B', 'F'] | ['G#'] | ['F', 'G#'] | ['B', 'F', 'G#'] | ['F', 'G#', 'B', 'F'] |
| F | Dim H-W⁺⁶(I) | ['B', 'F'] | ['G#'] | ['G#', 'F'] | ['B', 'F', 'G#'] | ['G#', 'B', 'F', 'F'] |
It would appear that … the second inversion of the diminished whole-half scale in \(\text{F}\) is a good candidate. Who’d have thought. You can perhaps reharmonise with this information.