Music theory library — notes, chords, scales, and harmonic fields
Project description
🪇 Gingo
An expressive music theory and rhythm toolkit for Python, powered by a C++17 core.
From pitch classes to harmonic trees and rhythmic grids — with audio playback and a friendly CLI.
Notes, intervals, chords, scales, and harmonic fields are just the beginning: Gingo also ships with durations, tempo markings (nomes de tempo), time signatures, and sequence playback.
Português (pt-BR): https://sauloverissimo.github.io/gingo/ (guia e referência completos)
About
Gingo is a pragmatic library for analysis, composition, and teaching. It prioritizes correctness, ergonomics, and speed, while keeping the API compact and consistent across concepts.
Highlights
- C++17 core + Python API — fast and deterministic, with full type hints.
- Pitch & harmony —
Note,Interval,Chord,Scale,Field,Tree, andProgressionwith identification, deduction, and comparison utilities. - Rhythm & time —
Duration,Tempo(BPM + nomes de tempo),TimeSignature, andSequencewith note/chord events. - Audio —
.play()and.to_wav()on musical objects, plus CLI--play/--wavwith waveform and strum controls. - CLI-first exploration — query and inspect theory concepts without leaving the terminal.
Installation
pip install gingo
Optional audio playback dependency:
pip install "gingo[audio]"
Requires Python 3.10+. Pre-built binary wheels are available for Linux, macOS, and Windows — no C++17 compiler needed. If no wheel is available for your platform, pip will build from source automatically.
Quick Start
from gingo import (
Note, Interval, Chord, Scale, Field, Tree, ScaleType,
Duration, Tempo, TimeSignature, Sequence,
NoteEvent, ChordEvent, Rest,
)
# Notes
note = Note("Bb")
note.natural() # "A#"
note.semitone() # 10
note.frequency(4) # 466.16 Hz
note.play(octave=4) # Listen to Bb4
# Intervals
iv = Interval("5J")
iv.semitones() # 7
iv.anglo_saxon() # "P5"
# Chords
chord = Chord("Cm7")
chord.root() # Note("C")
chord.type() # "m7"
chord.notes() # [Note("C"), Note("Eb"), Note("G"), Note("Bb")]
chord.interval_labels() # ["P1", "3m", "5J", "7m"]
chord.play() # Listen to Cm7
# Identify a chord from notes
Chord.identify(["C", "E", "G"]) # Chord("CM")
# Identify a scale or field from a full note/chord set
Scale.identify(["C", "D", "E", "F", "G", "A", "B"]) # Scale("C", "major")
Field.identify(["CM", "Dm", "Em", "FM", "GM", "Am"]) # Field("C", "major")
# Deduce likely fields from partial evidence (ranked)
matches = Field.deduce(["CM", "FM"])
matches[0].field # Field("C", "major") or Field("F", "major")
matches[0].score # 1.0
# Compare two chords (absolute, context-free)
r = Chord("CM").compare(Chord("Am"))
r.common_notes # [Note("C"), Note("E")]
r.root_distance # 3
r.transformation # "R" (neo-Riemannian Relative)
r.transposition # -1 (not related by transposition)
r.dissonance_a # 0.057... (psychoacoustic roughness)
r.to_dict() # full dict serialization
# Scales
scale = Scale("C", ScaleType.Major)
[n.natural() for n in scale.notes()] # ["C", "D", "E", "F", "G", "A", "B"]
scale.degree(5) # Note("G")
scale.play() # Listen to C major scale
# Harmonic fields
field = Field("C", ScaleType.Major)
[c.name() for c in field.chords()]
# ["CM", "Dm", "Em", "FM", "GM", "Am", "Bdim"]
# Compare two chords within a harmonic field (contextual)
r = field.compare(Chord("CM"), Chord("GM"))
r.degree_a # 1 (I)
r.degree_b # 5 (V)
r.function_a # HarmonicFunction.Tonic
r.function_b # HarmonicFunction.Dominant
r.root_motion # "ascending_fifth"
r.to_dict() # full dict serialization
# Harmonic trees (progressions and voice leading)
from gingo import Tree, Progression
tree = Tree("C", ScaleType.Major, "harmonic_tree")
tree.branches() # All available harmonic branches
tree.paths("I") # All progressions from tonic
tree.shortest_path("I", "V7") # ["I", "V7"]
tree.is_valid(["IIm", "V7", "I"]) # True
tree.function("V7") # HarmonicFunction.Dominant
tree.schemas() # Named patterns for this tradition
tree.to_dot() # Export to Graphviz
tree.to_mermaid() # Export to Mermaid diagram
# Cross-tradition analysis with Progression
prog = Progression("C", "major")
prog.traditions() # ["harmonic_tree", "jazz"]
prog.identify(["IIm", "V7", "I"]) # ProgressionMatch
prog.deduce(["IIm", "V7"]) # Ranked matches
prog.predict(["I", "IIm"]) # Suggested next chords
# Rhythm
q = Duration("quarter")
dotted = Duration("eighth", dots=1)
triplet = Duration("eighth", tuplet=3)
Tempo("Allegro").bpm() # 140.0
Tempo(120).marking() # "Allegretto"
TimeSignature(6, 8).classification() # "compound"
# Sequence (events in time)
seq = Sequence(Tempo(120), TimeSignature(4, 4))
seq.add(NoteEvent(Note("C"), Duration("quarter"), octave=4))
seq.add(ChordEvent(Chord("G7"), Duration("half"), octave=4))
seq.add(Rest(Duration("quarter")))
seq.total_seconds()
# Audio
Note("C").play()
Chord("Am7").play(waveform="square")
Scale("C", "major").to_wav("c_major.wav")
CLI (quick exploration)
gingo note C#
gingo note C --fifths
gingo interval 7 --all
gingo scale "C major" --degree 5 5
gingo scale "C,D,E,F,G,A,B" --identify
gingo field "C major" --functions
gingo field "CM,FM,G7" --identify
gingo field "CM,FM" --deduce
gingo compare CM GM --field "C major"
gingo note C --play --waveform triangle
gingo chord Am7 --play --strum 0.05
gingo chord Am7 --wav am7.wav
gingo duration quarter --tempo 120
gingo tempo Allegro --all
gingo timesig 6 8 --tempo 120
Audio flags:
--playoutputs to the system audio device--wav FILEexports a WAV file--waveform(sine,square,sawtooth,triangle)--strumand--gapcontrol timing between chord tones and events
Detailed Guide
Note
The Note class is the atomic unit of the library. It represents a single pitch class (C, D, E, F, G, A, B) with optional accidentals.
from gingo import Note
# Construction — accepts any common notation
c = Note("C") # Natural
bb = Note("Bb") # Flat
fs = Note("F#") # Sharp
eb = Note("E♭") # Unicode flat
gs = Note("G##") # Double sharp
# Core properties
bb.name() # "Bb" — the original input
bb.natural() # "A#" — canonical sharp-based form
bb.sound() # "B" — base letter (no accidentals)
bb.semitone() # 10 — chromatic position (C=0, C#=1, ..., B=11)
# Frequency calculation (A4 = 440 Hz standard tuning)
Note("A").frequency(4) # 440.0 Hz
Note("A").frequency(3) # 220.0 Hz
Note("C").frequency(4) # 261.63 Hz
Note("A").frequency(5) # 880.0 Hz
# Enharmonic equivalence
Note("Bb").is_enharmonic(Note("A#")) # True
Note("Db").is_enharmonic(Note("C#")) # True
Note("C").is_enharmonic(Note("D")) # False
# Equality (compares natural forms)
Note("Bb") == Note("A#") # True — same natural form
Note("C") == Note("C") # True
Note("C") != Note("D") # True
# Transposition
Note("C").transpose(7) # Note("G") — up a perfect fifth
Note("C").transpose(12) # Note("C") — up an octave
Note("A").transpose(-2) # Note("G") — down a whole step
Note("E").transpose(1) # Note("F") — up a semitone
# Audio playback (requires gingo[audio])
Note("A").play(octave=4) # A4 (440 Hz)
Note("C").play(octave=5, waveform="square") # C5 with square wave
Note("Eb").to_wav("eb.wav", octave=4) # Export to WAV file
# Static utilities
Note.to_natural("Bb") # "A#"
Note.to_natural("G##") # "A"
Note.to_natural("Bbb") # "A"
Note.extract_root("C#m7") # "C#"
Note.extract_root("Bbdim") # "Bb"
Note.extract_sound("Gb") # "G"
Note.extract_type("C#m7") # "m7"
Note.extract_type("F#m7(b5)") # "m7(b5)"
Note.extract_type("C") # ""
Enharmonic Resolution Table
Gingo resolves 89 enharmonic spellings to a canonical sharp-based form:
| Input | Natural | Category |
|---|---|---|
Bb |
A# |
Standard flat |
Db |
C# |
Standard flat |
Eb |
D# |
Standard flat |
Gb |
F# |
Standard flat |
Ab |
G# |
Standard flat |
E# |
F |
Special sharp (no sharp exists) |
B# |
C |
Special sharp (no sharp exists) |
Fb |
E |
Special flat (no flat exists) |
Cb |
B |
Special flat (no flat exists) |
G## |
A |
Double sharp |
C## |
D |
Double sharp |
E## |
F# |
Double sharp |
Bbb |
A |
Double flat |
Abb |
G |
Double flat |
B♭ |
A# |
Unicode flat symbol |
E♭♭ |
D |
Unicode double flat |
♭♭G |
F |
Prefix accidentals |
Interval
The Interval class represents the distance between two pitches, covering two full octaves (24 semitones).
from gingo import Interval
# Construction — from label or semitone count
p1 = Interval("P1") # Perfect unison
m3 = Interval("3m") # Minor third
M3 = Interval("3M") # Major third
p5 = Interval("5J") # Perfect fifth
m7 = Interval("7m") # Minor seventh
# From semitone count
iv = Interval(7) # Same as Interval("5J")
# Properties
m3.label() # "3m"
m3.anglo_saxon() # "mi3"
m3.semitones() # 3
m3.degree() # 3
m3.octave() # 1
# Second octave intervals
b9 = Interval("b9")
b9.semitones() # 13
b9.octave() # 2
# Equality (by semitone distance)
Interval("P1") == Interval(0) # True
Interval("5J") == Interval(7) # True
All 24 Interval Labels
| Semitones | Label | Anglo-Saxon | Degree |
|---|---|---|---|
| 0 | P1 | P1 | 1 |
| 1 | 2m | mi2 | 2 |
| 2 | 2M | ma2 | 2 |
| 3 | 3m | mi3 | 3 |
| 4 | 3M | ma3 | 3 |
| 5 | 4J | P4 | 4 |
| 6 | d5 | d5 | 5 |
| 7 | 5J | P5 | 5 |
| 8 | #5 | mi6 | 6 |
| 9 | M6 | ma6 | 6 |
| 10 | 7m | mi7 | 7 |
| 11 | 7M | ma7 | 7 |
| 12 | 8J | P8 | 8 |
| 13 | b9 | mi9 | 9 |
| 14 | 9 | ma9 | 9 |
| 15 | #9 | mi10 | 10 |
| 16 | b11 | ma10 | 10 |
| 17 | 11 | P11 | 11 |
| 18 | #11 | d11 | 11 |
| 19 | 5 | P12 | 12 |
| 20 | b13 | mi13 | 13 |
| 21 | 13 | ma13 | 13 |
| 22 | #13 | mi14 | 14 |
| 23 | bI | ma14 | 14 |
Chord
The Chord class represents a musical chord — a root note plus a set of intervals from a database of 42 chord formulas.
from gingo import Chord, Note
# Construction from name
cm = Chord("CM") # C major
dm7 = Chord("Dm7") # D minor seventh
bb7m = Chord("Bb7M") # Bb major seventh
fsdim = Chord("F#dim") # F# diminished
# Root, type, and name
cm.root() # Note("C")
cm.root().natural() # "C"
cm.type() # "M"
cm.name() # "CM"
# Notes — with correct enharmonic spelling
[n.name() for n in Chord("CM").notes()]
# ["C", "E", "G"]
[n.name() for n in Chord("Am7").notes()]
# ["A", "C", "E", "G"]
[n.name() for n in Chord("Dbm7").notes()]
# ["Db", "Fb", "Ab", "Cb"] — proper flat spelling
# Notes can also be accessed as natural (sharp-based) canonical form
[n.natural() for n in Chord("Dbm7").notes()]
# ["C#", "E", "G#", "B"]
# Interval structure
Chord("Am7").interval_labels()
# ["P1", "3m", "5J", "7m"]
Chord("CM").interval_labels()
# ["P1", "3M", "5J"]
Chord("Bdim").interval_labels()
# ["P1", "3m", "d5"]
# Size
Chord("CM").size() # 3 (triad)
Chord("Am7").size() # 4 (seventh chord)
Chord("G7").size() # 4
# Contains — check if a note belongs to the chord
Chord("CM").contains(Note("E")) # True
Chord("CM").contains(Note("F")) # False
# Identify chord from notes (reverse lookup)
c = Chord.identify(["C", "E", "G"])
c.name() # "CM"
c.type() # "M"
c2 = Chord.identify(["D", "F#", "A", "C#", "E"])
c2.type() # "9"
# Equality
Chord("CM") == Chord("CM") # True
Chord("CM") != Chord("Cm") # True
# Audio playback (requires gingo[audio])
Chord("Am7").play() # Play Am7 chord
Chord("G7").play(waveform="sawtooth") # Custom waveform
Chord("Dm").play(strum=0.05) # Arpeggiated/strummed
Chord("CM").to_wav("cmajor.wav", octave=4) # Export to WAV file
Supported Chord Types (42 formulas)
Triads (7): M, m, dim, aug, sus2, sus4, 5
Seventh chords (10): 7, m7, 7M, m7M, dim7, m7(b5), 7(b5), 7(#5), 7M(#5), sus7
Sixth chords (3): 6, m6, 6(9)
Ninth chords (4): 9, m9, M9, sus9
Extended chords (6): 11, m11, m7(11), 13, m13, M13
Altered chords (6): 7(b9), 7(#9), 7(#11), 13(#11), (b9), (b13)
Add chords (4): add9, add2, add11, add4
Other (2): sus, 7+5
Scale
The Scale class builds a scale from a tonic note and a scale pattern. It supports 10 parent families, mode names, pentatonic filters, and a chainable API.
from gingo import Scale, ScaleType, Note
# Construction — from enum, string, or mode name
s1 = Scale("C", ScaleType.Major)
s2 = Scale("C", "major") # string form
s3 = Scale("D", "dorian") # mode name → Major, mode 2
s4 = Scale("E", "phrygian dominant") # mode name → HarmonicMinor, mode 5
s5 = Scale("C", "altered") # mode name → MelodicMinor, mode 7
# Scale identity
d = Scale("D", "dorian")
d.parent() # ScaleType.Major
d.mode_number() # 2
d.mode_name() # "Dorian"
d.quality() # "minor"
d.brightness() # 3
# Scale notes (with correct enharmonic spelling)
[n.name() for n in Scale("C", "major").notes()]
# ["C", "D", "E", "F", "G", "A", "B"]
[n.name() for n in Scale("D", "dorian").notes()]
# ["D", "E", "F", "G", "A", "B", "C"]
[n.name() for n in Scale("Gb", "major").notes()]
# ["Gb", "Ab", "Bb", "Cb", "Db", "Eb", "F"]
# Natural form (canonical sharp-based) also available
[n.natural() for n in Scale("Gb", "major").notes()]
# ["F#", "G#", "A#", "B", "C#", "D#", "F"]
# Degree access (1-indexed, supports chaining)
s = Scale("C", "major")
s.degree(1) # Note("C") — tonic
s.degree(5) # Note("G") — dominant
s.degree(5, 5) # Note("D") — V of V
s.degree(5, 5, 3) # Note("F") — III of V of V
# Walk: navigate along the scale
s.walk(1, 4) # Note("F") — from I, a fourth = IV
s.walk(5, 5) # Note("D") — from V, a fifth = II
# Modes by number or name
s.mode(2) # D Dorian
s.mode("lydian") # F Lydian
# Pentatonic
s.pentatonic() # C major pentatonic (5 notes)
Scale("C", "major pentatonic") # same thing
Scale("A", "minor pentatonic") # A C D E G
# Color notes (what distinguishes this mode from a reference)
Scale("C", "dorian").colors("ionian") # [Eb, Bb]
# Other families
Scale("C", "whole tone").size() # 6
Scale("A", "blues").size() # 6
Scale("C", "chromatic").size() # 12
Scale("C", "diminished").size() # 8
# Audio playback (requires gingo[audio])
Scale("C", "major").play() # Play C major scale
Scale("D", "dorian").play(waveform="triangle") # Custom waveform
Scale("A", "minor").to_wav("a_minor.wav") # Export to WAV file
Scale Types (10 parent families)
| Type | Notes | Pattern | Description |
|---|---|---|---|
Major |
7 | W-W-H-W-W-W-H | Ionian mode, the most common Western scale |
NaturalMinor |
7 | W-H-W-W-H-W-W | Aeolian mode, relative minor |
HarmonicMinor |
7 | W-H-W-W-H-A2-H | Raised 7th degree, characteristic V7 chord |
MelodicMinor |
7 | W-H-W-W-W-W-H | Raised 6th and 7th degrees (ascending) |
HarmonicMajor |
7 | W-W-H-W-H-A2-H | Major with lowered 6th degree |
Diminished |
8 | W-H-W-H-W-H-W-H | Symmetric octatonic scale |
WholeTone |
6 | W-W-W-W-W-W | Symmetric whole-tone scale |
Augmented |
6 | A2-H-A2-H-A2-H | Symmetric augmented scale |
Blues |
6 | m3-W-H-H-m3-W | Minor pentatonic + blue note |
Chromatic |
12 | H-H-H-H-H-H-H-H-H-H-H-H | All 12 pitch classes |
W = whole step, H = half step, A2 = augmented second, m3 = minor third
Field (Harmonic Field)
The Field class generates the diatonic chords built from each degree of a scale — the harmonic field.
from gingo import Field, ScaleType, HarmonicFunction
# Construction
f = Field("C", ScaleType.Major)
# Triads (3-note chords on each degree)
triads = f.chords()
[c.name() for c in triads]
# ["CM", "Dm", "Em", "FM", "GM", "Am", "Bdim"]
# I ii iii IV V vi vii°
# Seventh chords (4-note chords on each degree)
sevenths = f.sevenths()
[c.name() for c in sevenths]
# ["CM7", "Dm7", "Em7", "FM7", "G7", "Am7", "Bm7(b5)"]
# Imaj7 ii7 iii7 IVmaj7 V7 vi7 vii-7(b5)
# Access by degree (1-indexed)
f.chord(1) # Chord("CM")
f.chord(5) # Chord("GM")
f.seventh(5) # Chord("G7")
# Harmonic function (Tonic / Subdominant / Dominant)
f.function(1) # HarmonicFunction.Tonic
f.function(5) # HarmonicFunction.Dominant
f.function(5).name # "Dominant"
f.function(5).short # "D"
# Role within function group
f.role(1) # "primary"
f.role(6) # "relative of I"
# Query by chord name or object
f.function("FM") # HarmonicFunction.Subdominant
f.function("F#M") # None (not in the field)
f.role("Am") # "relative of I"
# Applied chords (tonicization)
f.applied("V7", 2) # Chord("A7") — V7 of degree II
f.applied("V7", "V") # Chord("D7") — V7 of degree V
f.applied("IIm7(b5)", 5) # Chord("Am7(b5)")
f.applied(5, 2) # Chord("A7") — numeric shorthand
# Number of degrees
f.size() # 7
# Works with any scale type
f_minor = Field("A", ScaleType.HarmonicMinor)
[c.name() for c in f_minor.chords()]
# Harmonic minor field: Am, Bdim, Caug, Dm, EM, FM, G#dim
Tree (Harmonic Graph)
The Tree class represents harmonic progressions and voice leading paths within a scale's harmonic field. It requires a tradition parameter specifying which harmonic school to use.
from gingo import Tree, ScaleType, HarmonicFunction
# Construction — now requires tradition parameter
tree = Tree("C", ScaleType.Major, "harmonic_tree")
# List all available harmonic branches
branches = tree.branches()
# ["I", "IIm", "IIIm", "IV", "V7", "VIm", "VIIdim", "V7/IV", "IVm", "bVI", "bVII", ...]
# Get tradition metadata
tradition = tree.tradition()
tradition.name # "harmonic_tree"
tradition.description # "Alencar harmonic tree theory"
# Get named patterns (schemas)
schemas = tree.schemas()
# [Schema(name="descending", branches=["I", "V7/IIm", "IIm", "V7", "I"]), ...]
# Get all possible paths from a branch
paths = tree.paths("I")
for path in paths[:3]:
print(f"{path.id}: {path.branch} → {path.chord.name()}")
# 0: I → CM
# 1: IIm / IV → Dm
# 2: VIm → Am
# Find shortest path between two branches
path = tree.shortest_path("I", "V7")
# ["I", "V7"]
# Validate a progression
tree.is_valid(["IIm", "V7", "I"]) # True (II-V-I)
tree.is_valid(["I", "IV", "V7"]) # True
tree.is_valid(["I", "INVALID"]) # False
# Harmonic function classification
tree.function("I") # HarmonicFunction.Tonic
tree.function("IV") # HarmonicFunction.Subdominant
tree.function("V7") # HarmonicFunction.Dominant
# Get all branches with a specific function
tonics = tree.branches_with_function(HarmonicFunction.Tonic)
# ["I", "VIm", ...]
# Export to visualization formats
dot = tree.to_dot(show_functions=True)
mermaid = tree.to_mermaid()
# Works with minor scales
tree_minor = Tree("A", ScaleType.NaturalMinor, "harmonic_tree")
tree_minor.branches()
# ["Im", "IIdim", "bIII", "IVm", "Vm", "bVI", "bVII", ...]
Progression (Cross-Tradition Analysis)
The Progression class coordinates harmonic analysis across multiple traditions.
from gingo import Progression
# Construction
prog = Progression("C", "major")
# List available traditions
traditions = Progression.traditions()
# [Tradition(name="harmonic_tree"), Tradition(name="jazz")]
# Get a tree for a specific tradition
tree = prog.tree("harmonic_tree")
jazz_tree = prog.tree("jazz")
# Identify tradition and schema from a progression
match = prog.identify(["IIm", "V7", "I"])
match.tradition # "harmonic_tree"
match.schema # "descending"
match.score # 1.0
match.matched # 2 (transitions)
match.total # 2
# Deduce likely traditions from partial input
matches = prog.deduce(["IIm", "V7"], limit=5)
for m in matches:
print(f"{m.tradition}: {m.score}")
# Predict next chords
routes = prog.predict(["I", "IIm"])
for r in routes:
print(f"Next: {r.next} (from {r.tradition}, conf={r.confidence})")
API Reference Summary
Note
| Method | Returns | Description |
|---|---|---|
Note(name) |
Note |
Construct from any notation |
.name() |
str |
Original input name |
.natural() |
str |
Canonical sharp form |
.sound() |
str |
Base letter only |
.semitone() |
int |
Chromatic index 0-11 |
.frequency(octave=4) |
float |
Concert pitch in Hz |
.is_enharmonic(other) |
bool |
Same pitch class? |
.transpose(semitones) |
Note |
Shifted note |
Note.to_natural(name) |
str |
Static: resolve spelling |
Note.extract_root(name) |
str |
Static: root from chord name |
Note.extract_sound(name) |
str |
Static: base letter from name |
Note.extract_type(name) |
str |
Static: chord type suffix |
Interval
| Method | Returns | Description |
|---|---|---|
Interval(label) |
Interval |
From label string |
Interval(semitones) |
Interval |
From semitone count |
.label() |
str |
Short label |
.anglo_saxon() |
str |
Anglo-Saxon formal name |
.semitones() |
int |
Semitone distance |
.degree() |
int |
Diatonic degree number |
.octave() |
int |
Octave (1 or 2) |
Chord
| Method | Returns | Description |
|---|---|---|
Chord(name) |
Chord |
From chord name |
.name() |
str |
Full chord name |
.root() |
Note |
Root note |
.type() |
str |
Quality suffix |
.notes() |
list[Note] |
Chord tones (natural) |
.formal_notes() |
list[Note] |
Chord tones (diatonic spelling) |
.intervals() |
list[Interval] |
Interval objects |
.interval_labels() |
list[str] |
Interval label strings |
.size() |
int |
Number of notes |
.contains(note) |
bool |
Note membership test |
.compare(other) |
ChordComparison |
Detailed comparison (18 dimensions) |
Chord.identify(names) |
Chord |
Static: reverse lookup |
Scale
| Method | Returns | Description |
|---|---|---|
Scale(tonic, type) |
Scale |
From tonic + ScaleType/string/mode name |
.tonic() |
Note |
Tonic note |
.parent() |
ScaleType |
Parent family (Major, HarmonicMinor, ...) |
.mode_number() |
int |
Mode number (1-7) |
.mode_name() |
str |
Mode name (Ionian, Dorian, ...) |
.quality() |
str |
Tonal quality ("major" / "minor") |
.brightness() |
int |
Brightness (1=Locrian, 7=Lydian) |
.is_pentatonic() |
bool |
Whether pentatonic filter is active |
.type() |
ScaleType |
Scale type enum (backward compat, = parent) |
.modality() |
Modality |
Modality enum (backward compat) |
.notes() |
list[Note] |
Scale notes (natural) |
.formal_notes() |
list[Note] |
Scale notes (diatonic) |
.degree(*degrees) |
Note |
Chained degree: degree(5, 5) = V of V |
.walk(start, *steps) |
Note |
Walk: walk(1, 4) = IV |
.size() |
int |
Number of notes |
.contains(note) |
bool |
Note membership |
.mode(n_or_name) |
Scale |
Mode by number (int) or name (str) |
.pentatonic() |
Scale |
Pentatonic version of the scale |
.colors(reference) |
list[Note] |
Notes differing from a reference mode |
.mask() |
list[int] |
24-bit active positions |
Scale.parse_type(name) |
ScaleType |
Static: string to enum |
Scale.parse_modality(name) |
Modality |
Static: string to enum |
Scale.identify(notes) |
Scale |
Static: detect scale from full note set |
Field
| Method | Returns | Description |
|---|---|---|
Field(tonic, type) |
Field |
From tonic + ScaleType/string |
.tonic() |
Note |
Tonic note |
.scale() |
Scale |
Underlying scale |
.chords() |
list[Chord] |
Triads per degree |
.sevenths() |
list[Chord] |
Seventh chords per degree |
.chord(degree) |
Chord |
Triad at degree N |
.seventh(degree) |
Chord |
7th chord at degree N |
.applied(func, target) |
Chord |
Applied chord (tonicization) |
.function(degree) |
HarmonicFunction |
Harmonic function (T/S/D) |
.function(chord) |
HarmonicFunction? |
Function by chord (None if not in field) |
.role(degree) |
str |
Role: "primary", "relative of I", etc. |
.role(chord) |
str? |
Role by chord (None if not in field) |
.compare(a, b) |
FieldComparison |
Contextual comparison (21 dimensions) |
.size() |
int |
Number of degrees |
Field.identify(items) |
Field |
Static: detect field from full notes/chords |
Field.deduce(items, limit=10) |
list[FieldMatch] |
Static: ranked candidates from partial input |
Tree
| Method | Returns | Description |
|---|---|---|
Tree(tonic, type, tradition) |
Tree |
From tonic + ScaleType/string + tradition name |
.tonic() |
Note |
Tonic note |
.type() |
ScaleType |
Scale type |
.tradition() |
Tradition |
Tradition metadata |
.branches() |
list[str] |
All harmonic branches |
.paths(branch) |
list[HarmonicPath] |
All paths from a branch |
.shortest_path(from, to) |
list[str] |
Shortest progression |
.is_valid(branches) |
bool |
Validate progression |
.schemas() |
list[Schema] |
Named patterns for this tradition |
.function(branch) |
HarmonicFunction |
Harmonic function (T/S/D) |
.branches_with_function(func) |
list[str] |
Branches with function |
.to_dot(show_functions=False) |
str |
Graphviz DOT export |
.to_mermaid() |
str |
Mermaid diagram export |
Progression
| Method | Returns | Description |
|---|---|---|
Progression(tonic, type) |
Progression |
From tonic + ScaleType/string |
.tonic() |
Note |
Tonic note |
.type() |
ScaleType |
Scale type |
Progression.traditions() |
list[Tradition] |
Static: available traditions |
.tree(tradition) |
Tree |
Get tree for a tradition |
.identify(branches) |
ProgressionMatch |
Identify tradition/schema |
.deduce(branches, limit=10) |
list[ProgressionMatch] |
Ranked matches |
.predict(branches, tradition="") |
list[ProgressionRoute] |
Suggest next chords |
Tradition (struct)
| Field | Type | Description |
|---|---|---|
.name |
str |
Tradition name ("harmonic_tree", "jazz") |
.description |
str |
Human-readable description |
Schema (struct)
| Field | Type | Description |
|---|---|---|
.name |
str |
Pattern name ("descending", "ii-V-I") |
.description |
str |
Human-readable description |
.branches |
list[str] |
Branch sequence |
ProgressionMatch (struct)
| Field | Type | Description |
|---|---|---|
.tradition |
str |
Matched tradition |
.schema |
str |
Matched schema (or "") |
.score |
float |
Match ratio (0.0–1.0) |
.matched |
int |
Valid transitions |
.total |
int |
Total transitions |
.branches |
list[str] |
Resolved branches |
ProgressionRoute (struct)
| Field | Type | Description |
|---|---|---|
.next |
str |
Suggested next branch |
.tradition |
str |
From which tradition |
.schema |
str |
Motivating schema (or "") |
.path |
list[str] |
Complete suggested path |
.confidence |
float |
Confidence (0.0–1.0) |
HarmonicPath (struct)
Returned by Tree.paths(). Represents a harmonic progression step.
| Field | Type | Description |
|---|---|---|
.id |
int |
Path identifier |
.branch |
str |
Target branch name |
.chord |
Chord |
Resolved chord |
.interval_labels |
list[str] |
Chord intervals |
.note_names |
list[str] |
Chord note names |
ChordComparison (struct)
Returned by Chord.compare(). Absolute (context-free) comparison of two chords.
| Field | Type | Description |
|---|---|---|
.common_notes |
list[Note] |
Notes present in both chords |
.exclusive_a |
list[Note] |
Notes only in chord A |
.exclusive_b |
list[Note] |
Notes only in chord B |
.root_distance |
int |
Root distance in semitones (0-6, shortest arc) |
.root_direction |
int |
Signed root direction (-6 to +6) |
.same_quality |
bool |
Same chord type (M, m, dim, etc.) |
.same_size |
bool |
Same number of notes |
.common_intervals |
list[str] |
Interval labels present in both |
.enharmonic |
bool |
Same pitch class set |
.subset |
str |
"", "a_subset_of_b", "b_subset_of_a", "equal" |
.voice_leading |
int |
Optimal voice pairing in semitones (Tymoczko 2011). -1 if different sizes |
.transformation |
str |
Neo-Riemannian transformation (Cohn 2012): "", "P", "L", "R", "RP", "LP", "PL", "PR", "LR", "RL" (triads only) |
.inversion |
bool |
Same notes, different root |
.interval_vector_a |
list[int] |
Interval-class vector (Forte 1973): 6 elements counting ic1-6 for chord A |
.interval_vector_b |
list[int] |
Interval-class vector (Forte 1973) for chord B |
.same_interval_vector |
bool |
Same vector = Z-relation candidate (Forte 1973) |
.transposition |
int |
Transposition index T_n (Lewin 1987): 0-11, or -1 if not related |
.dissonance_a |
float |
Psychoacoustic roughness (Plomp & Levelt 1965 / Sethares 1998) for chord A |
.dissonance_b |
float |
Psychoacoustic roughness (Plomp & Levelt 1965 / Sethares 1998) for chord B |
| Method | Returns | Description |
|---|---|---|
.to_dict() |
dict |
Serialize all fields to a plain Python dict (Notes as strings) |
FieldComparison (struct)
Returned by Field.compare(). Contextual comparison within a harmonic field.
| Field | Type | Description |
|---|---|---|
.degree_a, .degree_b |
int? |
Scale degree (None if non-diatonic) |
.function_a, .function_b |
HarmonicFunction? |
Harmonic function |
.role_a, .role_b |
str? |
Role within function group |
.degree_distance |
int? |
Distance between degrees |
.same_function |
bool? |
Same harmonic function |
.relative |
bool |
Relative chord pair |
.progression |
bool |
Reserved for future use |
.root_motion |
str |
Diatonic root motion (Kostka & Payne): "", "ascending_fifth", "descending_fifth", "ascending_third", "descending_third", "ascending_step", "descending_step", "tritone", "unison" |
.secondary_dominant |
str |
Secondary dominant (Kostka & Payne): "", "a_is_V7_of_b", "b_is_V7_of_a" |
.applied_diminished |
str |
Applied diminished vii/x (Gauldin 1997): "", "a_is_viidim_of_b", "b_is_viidim_of_a" |
.diatonic_a, .diatonic_b |
bool |
Belongs to the field |
.borrowed_a, .borrowed_b |
BorrowedInfo? |
Modal borrowing origin |
.pivot |
list[PivotInfo] |
Keys where both chords have a degree |
.tritone_sub |
bool |
Tritone substitution (Kostka & Payne): both dom7, roots 6 st apart |
.chromatic_mediant |
str |
Chromatic mediant (Cohn 2012): "", "upper", "lower" |
.foreign_a, .foreign_b |
list[Note] |
Notes outside the scale |
| Method | Returns | Description |
|---|---|---|
.to_dict() |
dict |
Serialize all fields to a plain Python dict |
FieldMatch (struct)
Returned by Field.deduce(). Ranked candidate field match.
| Field | Type | Description |
|---|---|---|
.field |
Field |
Candidate field |
.score |
float |
Match ratio (0.0–1.0) |
.matched |
int |
Number of matched items |
.total |
int |
Total items in input |
.roles |
list[str] |
Roles for each item (Roman numerals) |
| Method | Returns | Description |
|---|---|---|
.to_dict() |
dict |
Serialize to dict |
BorrowedInfo (struct)
| Field | Type | Description |
|---|---|---|
.scale_type |
str |
Origin scale type ("NaturalMinor", etc.) |
.degree |
int |
Degree in that scale |
.function |
HarmonicFunction |
Function in that scale |
.role |
str |
Role in that scale |
| Method | Returns | Description |
|---|---|---|
.to_dict() |
dict |
Serialize to dict (function as string name) |
PivotInfo (struct)
| Field | Type | Description |
|---|---|---|
.tonic |
str |
Tonic of the pivot key |
.scale_type |
str |
Scale type |
.degree_a |
int |
Degree of chord A in that key |
.degree_b |
int |
Degree of chord B in that key |
| Method | Returns | Description |
|---|---|---|
.to_dict() |
dict |
Serialize to dict |
HarmonicFunction (enum)
| Property | Returns | Description |
|---|---|---|
.name |
str |
Full name: "Tonic", "Subdominant", "Dominant" |
.short |
str |
Abbreviation: "T", "S", "D" |
Rhythm & Time
Gingo models rhythm with first-class objects that match standard music notation.
Duration
Durations can be created by name (e.g., quarter, eighth) or as rational values. Dots and tuplets are built in.
from gingo import Duration
Duration("quarter")
Duration("eighth", dots=1) # dotted eighth
Duration("eighth", tuplet=3) # triplet eighth
Duration(3, 16) # 3/16
Tempo (nomos de tempo)
Tempo accepts either BPM or traditional tempo markings (nomos/nomes de tempo) such as Allegro or Adagio, and converts between them.
from gingo import Tempo, Duration
Tempo(120).marking() # "Allegretto"
Tempo("Adagio").bpm() # 60
Tempo("Allegro").seconds(Duration("quarter"))
Time Signature
Time signatures provide beats-per-bar, beat unit, classification, and bar duration.
from gingo import TimeSignature, Tempo
ts = TimeSignature(6, 8)
ts.classification() # "compound"
ts.bar_duration().beats()
Tempo(120).seconds(ts.bar_duration())
Sequence & Events
Build a timeline of note/chord events with a tempo and time signature. Sequences can be transposed and played back.
from gingo import (
Sequence, Tempo, TimeSignature, NoteEvent, ChordEvent, Rest,
Note, Chord, Duration,
)
seq = Sequence(Tempo(96), TimeSignature(4, 4))
seq.add(NoteEvent(Note("C"), Duration("quarter"), octave=4))
seq.add(ChordEvent(Chord("G7"), Duration("half"), octave=4))
seq.add(Rest(Duration("quarter")))
seq.total_seconds()
CLI helpers for rhythm:
gingo duration quarter --tempo 120gingo tempo Allegro --allgingo timesig 6 8 --tempo 120
Audio & Playback
Any musical object can be rendered to audio with .play() or .to_wav() (monophonic synthesis). Playback uses simpleaudio when available; install the optional dependency with pip install gingo[audio] for the best cross-platform experience.
from gingo import Note, Chord, Scale
Note("C").play(waveform="sine")
Chord("Am7").play(waveform="square", strum=0.04)
Scale("C", "major").to_wav("c_major.wav", waveform="triangle")
CLI audio flags are available on note, chord, scale, and field:
--playoutputs to speakers--wav FILEexports a WAV file--waveform sine|square|sawtooth|triangle--strumand--gapfor timing feel
Architecture
gingo/
├── cpp/ # C++17 core library
│ ├── include/gingo/ # Public headers
│ │ ├── note.hpp # Note class
│ │ ├── interval.hpp # Interval class
│ │ ├── chord.hpp # Chord class (42 formulas)
│ │ ├── scale.hpp # Scale class (10 families, modes, pentatonic)
│ │ ├── field.hpp # Harmonic field
│ │ ├── tree.hpp # Harmonic tree (beta)
│ │ ├── duration.hpp # Duration class (rhythm)
│ │ ├── tempo.hpp # Tempo class (BPM + markings)
│ │ ├── time_signature.hpp # TimeSignature class
│ │ ├── event.hpp # NoteEvent, ChordEvent, Rest
│ │ ├── sequence.hpp # Sequence class (timeline)
│ │ ├── gingo.hpp # Umbrella include
│ │ └── internal/ # Internal infrastructure
│ │ ├── types.hpp # TypeElement, TypeVector, TypeTable
│ │ ├── table.hpp # Lookup table class
│ │ ├── data_ops.hpp # rotate, spread, spin operations
│ │ ├── notation_utils.hpp # Formal notation helpers
│ │ ├── lookup_data.hpp # Singleton with all music data
│ │ ├── lookup_progression.hpp # Singleton with tradition data
│ │ └── mode_data.hpp # Mode metadata
│ └── src/ # All implementations
├── bindings/
│ └── pybind_module.cpp # pybind11 Python bridge
├── python/gingo/
│ ├── __init__.py # Public API re-exports
│ ├── __init__.pyi # Type stubs (PEP 561)
│ ├── __main__.py # CLI entry point
│ ├── audio.py # Audio playback (requires simpleaudio)
│ └── py.typed # PEP 561 marker
├── tests/
│ ├── cpp/ # Catch2 test suite
│ └── python/ # pytest suite
├── CMakeLists.txt # CMake build system
├── pyproject.toml # scikit-build-core packaging
├── MANIFEST.in # Source distribution manifest
└── .github/workflows/
├── ci.yml # Cross-platform CI
└── publish.yml # PyPI publishing via cibuildwheel
Design decisions:
- C++ core — All music theory computation runs in compiled C++17 for performance. This is critical for real-time MIDI, FFT, and machine learning workloads.
- pybind11 bridge — Exposes the C++ types to Python with zero-copy where possible and full type stub support for IDE autocompletion.
- Lazy computation — Chord notes, scale notes, and formal spellings are computed on first access and cached internally using mutable fields.
- Meyer's singleton — All lookup data (enharmonic maps, chord formulas, scale masks) is initialized once on first use, with no manual setup required.
- Domain types over generic tables — Instead of the original generic
Tabledata structure, the new API uses dedicatedNote,Interval,Chord,Scale, andFieldtypes with clear, discoverable methods.
Building from Source
Python package
pip install -v .
This triggers scikit-build-core, which runs CMake, compiles the C++ core, links the pybind11 module, and installs the Python package.
C++ only
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
C++ with tests
cmake -B build -DGINGO_BUILD_TESTS=ON
cmake --build build
cd build && ctest --output-on-failure
Run Python tests
pip install -v ".[test]"
pytest tests/python -v
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run both C++ and Python test suites
- Submit a pull request
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gingo-1.0.2.tar.gz.
File metadata
- Download URL: gingo-1.0.2.tar.gz
- Upload date:
- Size: 194.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cee3f048d28b13c942cdb537298e2a41b5a8e89bcb653622e2dc0c68503c282
|
|
| MD5 |
9c8545bcee13da9e3f05079b7e5dc80e
|
|
| BLAKE2b-256 |
3f6fc84a2d1389e7bf3352bfbbfeff31d0ff2cfbabc6a08c6db58d1bfd82e874
|
File details
Details for the file gingo-1.0.2-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: gingo-1.0.2-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 456.5 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39089c544fe3a75deafb83fe9c436a7aff11951d9f32ea1bb150615c66267f01
|
|
| MD5 |
108e0e34f2458f935a831d1db864bb02
|
|
| BLAKE2b-256 |
0fe40fda3bab9190316322e45831665cce49d3f886f3498fb6431ba343782701
|
File details
Details for the file gingo-1.0.2-cp313-cp313-win32.whl.
File metadata
- Download URL: gingo-1.0.2-cp313-cp313-win32.whl
- Upload date:
- Size: 407.5 kB
- Tags: CPython 3.13, Windows x86
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc93383799693954e6abf4b9ad27bb79dcf4fe6c7807446b4bb71e6d4f04faa7
|
|
| MD5 |
98201a063048ec8153890a2b4bac0931
|
|
| BLAKE2b-256 |
906842b5a8f8f64ee199fbabe5da9721fc583b73105ecda14a0b513fc5fc9263
|
File details
Details for the file gingo-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: gingo-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 620.7 kB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9779ec44c0e5b35bb6af3345e03dad7569e843db876482d681e5046b8cf18d4
|
|
| MD5 |
dc07dafc78a2240dabb3723421891127
|
|
| BLAKE2b-256 |
064c8cc36f0eb6419e5ff44e1baf2359457b861191c12202ff6c80329b28a2e4
|
File details
Details for the file gingo-1.0.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl.
File metadata
- Download URL: gingo-1.0.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl
- Upload date:
- Size: 636.0 kB
- Tags: CPython 3.13, manylinux: glibc 2.17+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6745415a6a58711933df89a248f69f73d75ebfbcad4c0f6073405e5c71c8741
|
|
| MD5 |
88a95ce4d136332eb37812de9412d411
|
|
| BLAKE2b-256 |
9c43ce2627afd9a27ae99bb573983ef417076b5b1a2c203de4187e23b2b316e9
|
File details
Details for the file gingo-1.0.2-cp313-cp313-macosx_11_0_arm64.whl.
File metadata
- Download URL: gingo-1.0.2-cp313-cp313-macosx_11_0_arm64.whl
- Upload date:
- Size: 487.8 kB
- Tags: CPython 3.13, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ca5d4975b5392923fe679bef38b6c56d42987724d6961780fef9b3c684b4d47
|
|
| MD5 |
d629b1121ac7299e621cab70bb64b368
|
|
| BLAKE2b-256 |
61014af25c7f0c4777a11bb86061cfb79021a34b231acc5b0ee9db04cdb41327
|
File details
Details for the file gingo-1.0.2-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: gingo-1.0.2-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 456.4 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db6c386db12499c2ff86a80210088c643c4e692d9b3f891810f47626adafc3c3
|
|
| MD5 |
cfced4fb475e6331575218afd2a43691
|
|
| BLAKE2b-256 |
d6f5ea633669a708787fcd4304832dca0938234e75ba5f00b2070f0fcbbb8385
|
File details
Details for the file gingo-1.0.2-cp312-cp312-win32.whl.
File metadata
- Download URL: gingo-1.0.2-cp312-cp312-win32.whl
- Upload date:
- Size: 407.5 kB
- Tags: CPython 3.12, Windows x86
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08c7a0a7a21e7f402915d60a3d8b463560fd9e4854b1d77d7fd4f02f54f221e4
|
|
| MD5 |
ba97da85b91be8f9dceff6f1b4b06c91
|
|
| BLAKE2b-256 |
e622758d1ad065943358c5a5f71fb3794cb1c9d7c8c8a213271be4ef38531ebd
|
File details
Details for the file gingo-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: gingo-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 620.2 kB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a07e52a419922434b9f8d0cacd9d9d6fa54f45533b7244e864574e7fd5059a6b
|
|
| MD5 |
05494397524c35b19703a7e76446ad0c
|
|
| BLAKE2b-256 |
b9bd0251aca8cf177a55b992e8a312c1e05f915c2e8ce0e648d4ca3ec1e4ce87
|
File details
Details for the file gingo-1.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl.
File metadata
- Download URL: gingo-1.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl
- Upload date:
- Size: 636.5 kB
- Tags: CPython 3.12, manylinux: glibc 2.17+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58cb55f423c4c9abe8249c8d7f187427e2744189cf3718106088aac288090c6a
|
|
| MD5 |
55ad02eb50314bd1e3aec3c684a9ba4a
|
|
| BLAKE2b-256 |
e48806721d9704bfdceec272bc761bb6001f0aba46c8f7396cb1c7f2c777aeac
|
File details
Details for the file gingo-1.0.2-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: gingo-1.0.2-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 487.7 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
940f63026b61db0aa3c459eaa84665d6002eab9f37c9d890af80609379331206
|
|
| MD5 |
f5c69bc788a3493003d54be2c9af5cf3
|
|
| BLAKE2b-256 |
e7de516a3ea9cc589c2ac1ea396db6bd5db5aabc0d2dd8ac5a892c6c312f6667
|
File details
Details for the file gingo-1.0.2-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: gingo-1.0.2-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 455.4 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7cc2fe629fca6767f0ae12fe4a0769d6ae089af6e08763b832b1d2e27b9004c
|
|
| MD5 |
4c45807ac7375e134613b98c4a1cccf2
|
|
| BLAKE2b-256 |
2d70b945861883e94b827e75a0374e11dc4c0ba998982ca72a8b26d3a0f09c2a
|
File details
Details for the file gingo-1.0.2-cp311-cp311-win32.whl.
File metadata
- Download URL: gingo-1.0.2-cp311-cp311-win32.whl
- Upload date:
- Size: 407.1 kB
- Tags: CPython 3.11, Windows x86
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
784bde0704ad948de67bd00fdace390edde0755f19c9e5e7f578b11e604a322e
|
|
| MD5 |
7f4fd9efbfed20eea985ba7fedca752b
|
|
| BLAKE2b-256 |
f2cba85ab84c517dba18fb577e4013907645bbc6d7ced84ae6fdfb2714fa1812
|
File details
Details for the file gingo-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: gingo-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 621.4 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4ee15cea3b3be1bf92fab022b746d781f2186b628dbf51a1cae34117694b3e5
|
|
| MD5 |
c413a5e58a68ac1aa7863f08ad45e16c
|
|
| BLAKE2b-256 |
390c945c5001c7483f8cbdf563d1ed3bfcf54291112c6eb56fc8b5d9b0abae78
|
File details
Details for the file gingo-1.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl.
File metadata
- Download URL: gingo-1.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl
- Upload date:
- Size: 634.1 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00ab523ed3b1376db5feb5bbeeff744c9ca4c8f2b411a623db0bdd0e4a674819
|
|
| MD5 |
f7e55766c4778ebbd9e85658a036df4b
|
|
| BLAKE2b-256 |
da0e8f92dfc2b213eb6f99cafbfc94f6aa4af8c451b7adcbffaf07b7fc299164
|
File details
Details for the file gingo-1.0.2-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: gingo-1.0.2-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 485.4 kB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f5e39103a30e4dd1ffe77e1d0a94fad8edc011e83fb46e81549986ce1d62aa6
|
|
| MD5 |
e7c482a940b0ba5fc59de481af6d3a6e
|
|
| BLAKE2b-256 |
5967b94e4e045df9aa46445afea42cf79babd9e6fb6027fedbfd0b68510ff348
|
File details
Details for the file gingo-1.0.2-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: gingo-1.0.2-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 454.8 kB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc0b7360408e49b7969e60df0b1e42e88b57ab7d0211d66051b45be9d1928d4a
|
|
| MD5 |
8bf17a866b2b9480db8588833d073a96
|
|
| BLAKE2b-256 |
11d7a63a075dadb45e5b8a3d7605c6e85613326f4c5a768192c9afecc92f3871
|
File details
Details for the file gingo-1.0.2-cp310-cp310-win32.whl.
File metadata
- Download URL: gingo-1.0.2-cp310-cp310-win32.whl
- Upload date:
- Size: 406.6 kB
- Tags: CPython 3.10, Windows x86
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c615dfc626f7de3b7eb9c7c1c29e5d63be67e876b0dd138f9415d71097dafbac
|
|
| MD5 |
ad38dc0e6e30c3b728f6a8bb92db259c
|
|
| BLAKE2b-256 |
7931cba388d59bfa82f8498d6a449f6c1ecd2c6274b73fcaa65981f515d2744d
|
File details
Details for the file gingo-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: gingo-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 620.2 kB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a36016fba74d5e28e8f4946c5d561d865ec986eccf59f1df918e27be511cee62
|
|
| MD5 |
b2985ab60d7232eb93b7f8f6388921bf
|
|
| BLAKE2b-256 |
fc48deba54f8e4414c6fdd33925cf6af6d7d22386e31a996fed8c835144ec5ae
|
File details
Details for the file gingo-1.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl.
File metadata
- Download URL: gingo-1.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl
- Upload date:
- Size: 633.6 kB
- Tags: CPython 3.10, manylinux: glibc 2.17+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
539db7ad6f52dccd4b0755a09537550988ccbf1a79bcf51e87df37cfab875ce7
|
|
| MD5 |
519b7ea4ed1e093ccd16efd4bfc7868f
|
|
| BLAKE2b-256 |
f5e2042640e82c32c50aecfa2c479cf91836f38575f06ff6de60fb294a20fab6
|
File details
Details for the file gingo-1.0.2-cp310-cp310-macosx_11_0_arm64.whl.
File metadata
- Download URL: gingo-1.0.2-cp310-cp310-macosx_11_0_arm64.whl
- Upload date:
- Size: 484.1 kB
- Tags: CPython 3.10, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a0a766aa6e70cd1e60a50d073f9971bd09de189d252b551ff70bbe3add4e0b6
|
|
| MD5 |
7135398aa4ccf6b724051754d9ff3052
|
|
| BLAKE2b-256 |
39cf11cd1bad9e7a60f876558863ad1d5290cb8537761b034d2473a7e81a34d2
|