# tonal
Tools for music analysis and generation
To install: ```pip install tonal```
# Examples
## notes
The `notes` module provides tools for working with musical scales, notes, and MIDI data.
### Key Concepts
**Scale Quality**: The characteristic sound or "flavor" of a scale, determined by its specific pattern of intervals (semitones) from the root note. Examples include major, minor, pentatonic, blues, or whole-tone.
**Semitone Pattern**: A numerical representation of a musical scale, showing the precise distance in semitones (half-steps) of each note from the starting (root) note. For example, the semitone pattern for a major scale is `(0, 2, 4, 5, 7, 9, 11)`.
### Working with Scales and MIDI Notes
The `scale_midi_notes` function converts scale specifications into MIDI note numbers:
```python
from tonal.notes import scale_midi_notes
# Get all C major notes in the middle octave range
notes = scale_midi_notes('C major', midi_range=(60, 72))
print(notes) # (60, 62, 64, 65, 67, 69, 71, 72)
# Minor pentatonic scale
notes = scale_midi_notes('A minor pentatonic', midi_range=(57, 70))
print(notes) # (57, 60, 62, 64, 67, 69)
# If no root is specified, defaults to C
notes = scale_midi_notes('dorian', midi_range=(60, 72))
print(notes) # (60, 62, 63, 65, 67, 69, 70, 72)
```
### Other Utilities
The module also provides:
- **`semitone_pattern(quality)`**: Get the semitone intervals for any scale quality
- **`scale_params(scale_string)`**: Parse scale strings into root note and quality components
- **Registration functions**: Dynamically add new scales, chords, and aliases:
- `register_scale_quality()`, `register_chord_quality()`
- `register_root_note()`, `register_scale_quality_alias()`
- **Listing functions**: Explore available musical elements:
- `list_scale_qualities()`, `list_chord_qualities()`
- `list_root_notes()`, `list_scale_quality_aliases()`
```python
from tonal.notes import semitone_pattern, scale_params, list_scale_qualities
# Get the interval pattern for blues scale
pattern = semitone_pattern('blues')
print(pattern) # (0, 3, 5, 6, 7, 10)
# Parse a scale string
root, quality = scale_params('F# harmonic minor')
print(f"Root: {root}, Quality: {quality}") # Root: F#, Quality: harmonic minor
# See all available scale qualities
qualities = list_scale_qualities()
print(f"Available scales: {len(qualities)} total")
```
## chords
```python
from tonal import chords_to_wav
chord_sequence = [
('Bdim', 120),
('Em11', 120),
('Amin9', 120),
('Dm7', 120),
'G7',
'Cmaj7',
]
wav_filepath = chords_to_wav(chord_sequence)
```
If you have [hum](https://pypi.org/project/hum/) you can use it to diplay (and hear)
the sound:
```python
from hum import Sound
Sound.from_file(wav_filepath).display()
```

Change the way the chords are played, and what the name (really, filepath) of the
midi and wav files produce are.
```python
from tonal.chords import play_arpeggio
Sound.from_file(
chords_to_wav(chord_sequence, name='test_arpeggio', render_chord=play_arpeggio)
).display()
```

## counterpoint
```python
The `translate_in_scale` allows you to translate a sequence of notes, or multiple
tracks of notes by the given number of steps within the given scale.
>>> stream = translate_in_scale(['C4', 'E4', 'B3', 'C4'], -2, 'C')
>>> stream # doctest: +ELLIPSIS
<music21.stream.Stream ...>
>>> note_names(stream)
['A3', 'C4', 'G3', 'A3']
For multiple tracks:
>>> tracks = [['C4', 'E4', 'G4'], ['A4', 'C5', 'E5']]
>>> translated_tracks = translate_in_scale(tracks, -2, 'C')
>>> multi_note_names(translated_tracks)
[['A3', 'C4', 'E4'], ['F4', 'A4', 'C5']]
Using some other scales:
With a E major scale:
>>> tracks = [['E4', 'G#4', 'B4'], ['C#5', 'E5', 'G#5']]
>>> translated_tracks = translate_in_scale(tracks, 1, 'E')
>>> multi_note_names(translated_tracks)
[['F#4', 'A4', 'C#5'], ['D#5', 'F#5', 'A5']]
With a D flat major scale:
>>> tracks = [['Db4', 'F4', 'Ab4'], ['Bb4', 'Db5', 'F5']]
>>> translated_tracks = translate_in_scale(tracks, -3, 'Db')
>>> multi_note_names(translated_tracks)
[['A-3', 'C4', 'E-4'], ['F4', 'A-4', 'C5']]
Now let's use a different, "custom" scale, as well as demonstrate the use
of a partial function to get a translator with a fixed input scale:
>>> from functools import partial
>>> from music21.scale import HarmonicMinorScale
>>> translate = partial(
... translate_in_scale, input_scale='A', scale_creator=HarmonicMinorScale
... )
>>> tracks = [['A4', 'C5', 'E5'], ['G#5', 'A5', 'C6']]
>>> translated_tracks = translate(tracks, 2)
>>> multi_note_names(translated_tracks)
[['C5', 'E5', 'G#5'], ['B5', 'C6', 'E6']]
```
Let's make a four part cycling V-I progression.
```python
from tonal.counterpoint import translate_in_scale, create_score_from_tracks
from tonal.util import play_music21_object
motif = [
"C4 C4".split(),
"E4 E4".split(),
"G4 F4".split(),
"B4 A4".split(),
]
tracks = translate_in_scale(motif, range(7, -14, -1), 'C')
score = create_score_from_tracks(tracks)
score.show()
play_music21_object(score)
```
<img width="653" alt="image" src="https://github.com/thorwhalen/tonal/assets/1906276/95f14372-e711-4fb5-8024-4692cd25956a">
Raw data
{
"_id": null,
"home_page": "https://github.com/thorwhalen/tonal",
"name": "tonal",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Thor Whalen",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/04/05/ab3617fa28feffbffb380de7d547a57efaf69d1db0e229e584dd2260cf5a/tonal-0.0.13.tar.gz",
"platform": "any",
"description": "\n# tonal\nTools for music analysis and generation\n\n\nTo install:\t```pip install tonal```\n\n\n# Examples\n\n## notes\n\nThe `notes` module provides tools for working with musical scales, notes, and MIDI data.\n\n### Key Concepts\n\n**Scale Quality**: The characteristic sound or \"flavor\" of a scale, determined by its specific pattern of intervals (semitones) from the root note. Examples include major, minor, pentatonic, blues, or whole-tone.\n\n**Semitone Pattern**: A numerical representation of a musical scale, showing the precise distance in semitones (half-steps) of each note from the starting (root) note. For example, the semitone pattern for a major scale is `(0, 2, 4, 5, 7, 9, 11)`.\n\n### Working with Scales and MIDI Notes\n\nThe `scale_midi_notes` function converts scale specifications into MIDI note numbers:\n\n```python\nfrom tonal.notes import scale_midi_notes\n\n# Get all C major notes in the middle octave range\nnotes = scale_midi_notes('C major', midi_range=(60, 72))\nprint(notes) # (60, 62, 64, 65, 67, 69, 71, 72)\n\n# Minor pentatonic scale\nnotes = scale_midi_notes('A minor pentatonic', midi_range=(57, 70))\nprint(notes) # (57, 60, 62, 64, 67, 69)\n\n# If no root is specified, defaults to C\nnotes = scale_midi_notes('dorian', midi_range=(60, 72))\nprint(notes) # (60, 62, 63, 65, 67, 69, 70, 72)\n```\n\n### Other Utilities\n\nThe module also provides:\n\n- **`semitone_pattern(quality)`**: Get the semitone intervals for any scale quality\n- **`scale_params(scale_string)`**: Parse scale strings into root note and quality components\n- **Registration functions**: Dynamically add new scales, chords, and aliases:\n - `register_scale_quality()`, `register_chord_quality()`\n - `register_root_note()`, `register_scale_quality_alias()`\n- **Listing functions**: Explore available musical elements:\n - `list_scale_qualities()`, `list_chord_qualities()`\n - `list_root_notes()`, `list_scale_quality_aliases()`\n\n```python\nfrom tonal.notes import semitone_pattern, scale_params, list_scale_qualities\n\n# Get the interval pattern for blues scale\npattern = semitone_pattern('blues')\nprint(pattern) # (0, 3, 5, 6, 7, 10)\n\n# Parse a scale string\nroot, quality = scale_params('F# harmonic minor')\nprint(f\"Root: {root}, Quality: {quality}\") # Root: F#, Quality: harmonic minor\n\n# See all available scale qualities\nqualities = list_scale_qualities()\nprint(f\"Available scales: {len(qualities)} total\")\n```\n\n## chords\n\n\n```python\nfrom tonal import chords_to_wav\n\nchord_sequence = [\n ('Bdim', 120),\n ('Em11', 120),\n ('Amin9', 120),\n ('Dm7', 120),\n 'G7',\n 'Cmaj7',\n]\n\nwav_filepath = chords_to_wav(chord_sequence)\n\n```\n\nIf you have [hum](https://pypi.org/project/hum/) you can use it to diplay (and hear) \nthe sound:\n\n```python\nfrom hum import Sound\nSound.from_file(wav_filepath).display()\n```\n\n\n\n\nChange the way the chords are played, and what the name (really, filepath) of the \nmidi and wav files produce are.\n\n```python\nfrom tonal.chords import play_arpeggio\n\nSound.from_file(\n chords_to_wav(chord_sequence, name='test_arpeggio', render_chord=play_arpeggio)\n).display()\n```\n\n\n\n\n## counterpoint\n\n```python\nThe `translate_in_scale` allows you to translate a sequence of notes, or multiple \ntracks of notes by the given number of steps within the given scale.\n\n>>> stream = translate_in_scale(['C4', 'E4', 'B3', 'C4'], -2, 'C')\n>>> stream # doctest: +ELLIPSIS\n<music21.stream.Stream ...>\n>>> note_names(stream)\n['A3', 'C4', 'G3', 'A3']\n\nFor multiple tracks:\n\n>>> tracks = [['C4', 'E4', 'G4'], ['A4', 'C5', 'E5']]\n>>> translated_tracks = translate_in_scale(tracks, -2, 'C')\n>>> multi_note_names(translated_tracks)\n[['A3', 'C4', 'E4'], ['F4', 'A4', 'C5']]\n\nUsing some other scales:\n\nWith a E major scale:\n\n>>> tracks = [['E4', 'G#4', 'B4'], ['C#5', 'E5', 'G#5']]\n>>> translated_tracks = translate_in_scale(tracks, 1, 'E')\n>>> multi_note_names(translated_tracks)\n[['F#4', 'A4', 'C#5'], ['D#5', 'F#5', 'A5']]\n\nWith a D flat major scale:\n\n>>> tracks = [['Db4', 'F4', 'Ab4'], ['Bb4', 'Db5', 'F5']]\n>>> translated_tracks = translate_in_scale(tracks, -3, 'Db')\n>>> multi_note_names(translated_tracks)\n[['A-3', 'C4', 'E-4'], ['F4', 'A-4', 'C5']]\n\nNow let's use a different, \"custom\" scale, as well as demonstrate the use\nof a partial function to get a translator with a fixed input scale:\n\n>>> from functools import partial\n>>> from music21.scale import HarmonicMinorScale\n>>> translate = partial(\n... translate_in_scale, input_scale='A', scale_creator=HarmonicMinorScale\n... )\n>>> tracks = [['A4', 'C5', 'E5'], ['G#5', 'A5', 'C6']]\n>>> translated_tracks = translate(tracks, 2)\n>>> multi_note_names(translated_tracks)\n[['C5', 'E5', 'G#5'], ['B5', 'C6', 'E6']]\n```\n\nLet's make a four part cycling V-I progression.\n\n```python\nfrom tonal.counterpoint import translate_in_scale, create_score_from_tracks\nfrom tonal.util import play_music21_object\n\nmotif = [\n \"C4 C4\".split(),\n \"E4 E4\".split(),\n \"G4 F4\".split(),\n \"B4 A4\".split(),\n]\n\ntracks = translate_in_scale(motif, range(7, -14, -1), 'C')\nscore = create_score_from_tracks(tracks)\nscore.show()\nplay_music21_object(score)\n```\n\n<img width=\"653\" alt=\"image\" src=\"https://github.com/thorwhalen/tonal/assets/1906276/95f14372-e711-4fb5-8024-4692cd25956a\">\n",
"bugtrack_url": null,
"license": "mit",
"summary": "Tools for music analysis and generation",
"version": "0.0.13",
"project_urls": {
"Homepage": "https://github.com/thorwhalen/tonal"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "1160d62e0acc8b28581e8f42d2aa40651edeafedd9659e505645c64f7f433645",
"md5": "849e6c2d8e8e2162b32dad8899c0829d",
"sha256": "ff14dd5506a5c98571b2b9b0afbe3b2584fc6a3377510a5db7cbf4df31d42605"
},
"downloads": -1,
"filename": "tonal-0.0.13-py3-none-any.whl",
"has_sig": false,
"md5_digest": "849e6c2d8e8e2162b32dad8899c0829d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 25651,
"upload_time": "2025-08-14T15:37:12",
"upload_time_iso_8601": "2025-08-14T15:37:12.997125Z",
"url": "https://files.pythonhosted.org/packages/11/60/d62e0acc8b28581e8f42d2aa40651edeafedd9659e505645c64f7f433645/tonal-0.0.13-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "0405ab3617fa28feffbffb380de7d547a57efaf69d1db0e229e584dd2260cf5a",
"md5": "ffd6b17c52692448664ba276ef9f035d",
"sha256": "cce855b7f5689cba93ccfccee830efb892ee030569d5f60e53770686ce055437"
},
"downloads": -1,
"filename": "tonal-0.0.13.tar.gz",
"has_sig": false,
"md5_digest": "ffd6b17c52692448664ba276ef9f035d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24959,
"upload_time": "2025-08-14T15:37:14",
"upload_time_iso_8601": "2025-08-14T15:37:14.076644Z",
"url": "https://files.pythonhosted.org/packages/04/05/ab3617fa28feffbffb380de7d547a57efaf69d1db0e229e584dd2260cf5a/tonal-0.0.13.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-14 15:37:14",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "thorwhalen",
"github_project": "tonal",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "tonal"
}