Giter Club home page Giter Club logo

pychord's Introduction

PyChord

PyChord Build Status Documentation Status

Overview

Pychord is a python library to handle musical chords.

Installation

$ pip install pychord

Basic Usage

Create a Chord

>>> from pychord import Chord
>>> c = Chord("Am7")
>>> c
<Chord: Am7>
>>> c.info()
"""
Am7
root=A
quality=m7
appended=[]
on=None
"""

Transpose a Chord

>>> c = Chord("Am7/G")
>>> c.transpose(3)
>>> c
<Chord: Cm7/Bb>

Get component notes

>>> c = Chord("Am7")
>>> c.components()
['A', 'C', 'E', 'G']
>>> c.components_with_pitch(root_pitch=3)
['A3', 'C4', 'E4', 'G4']

Compare Chords

>>> Chord("C") == Chord("D")
False
>>> Chord("C#") == Chord("Db")
True
>>> c = Chord("C")
>>> c.transpose(2)
>>> c == Chord("D")
True

Find Chords from notes

>>> from pychord import find_chords_from_notes
>>> find_chords_from_notes(["C", "E", "G"])
[ <Chord: C>]
>>> find_chords_from_notes(["F#", "A", "C", "D"])
[ <Chord: D7/F#>]
>>> find_chords_from_notes(["F", "G", "C"])
[ <Chord: Fsus2>, <Chord: Csus4/F>]

Create and handle chord progressions

>>> from pychord import ChordProgression
>>> cp = ChordProgression(["C", "G/B", "Am"])
>>> cp
<ChordProgression: C | G/B | Am>

>>> cp.append("Em/G")
>>> cp
<ChordProgression: C | G/B | Am | Em/G>

>>> cp.transpose(+3)
>>> cp
<ChordProgression: Eb | Bb/D | Cm | Gm/Bb>

>>> cp[1]
<Chord: Bb/D>

Advanced Usage

Create a Chord from note index in a scale

>>> Chord.from_note_index(note=1, quality="", scale="Cmaj")
<Chord: C>  # I of C major
>>> Chord.from_note_index(note=3, quality="m7", scale="Fmaj")
<Chord: Am7>  # IIIm7 of F major
>>> Chord.from_note_index(note=5, quality="7", scale="Amin")
<Chord: E7>  # V7 of A minor

Overwrite the default Quality components with yours

>>> from pychord import Chord, QualityManager
>>> Chord("C11").components()
['C', 'G', 'Bb', 'D', 'F']

>>> quality_manager = QualityManager()
>>> quality_manager.set_quality("11", (0, 4, 7, 10, 14, 17))
>>> Chord("C11").components()
['C', 'E', 'G', 'Bb', 'D', 'F']

Inversions

Chord inversions are created with a forward slash and a number indicating the order. This can optionally be combined with an additional forward slash to change the bass note:

>>> Chord("C/1").components() # First inversion of C
['E', 'G', 'C']
>>> Chord("C/2").components() # Second inversion of C
['G', 'C', 'E']

>>> Chord("Cm7/3/F").components() # Third inversion of Cm7 with an added F bass
['F', 'Bb', 'C', 'Eb', 'G']

Examples

Supported Python Versions

  • 3.6 and above

Python 2.7 and 3.5 compatibility was dropped from version 1.0.0.

Links

Author

License

  • MIT License

Icon is made by Freepik.

pychord's People

Contributors

dok avatar enigmacurry avatar hejops avatar jgvictores avatar kopongmensah avatar moustov avatar mstuttgart avatar nickurak avatar yuma-m avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

pychord's Issues

more qualities based on large data scraping

Hi!

this is a followup on some of the comments in issue#34
this followup analysis is based on 117k songs form UltimateGuitarTabs 1960-2023 in the rock, pop, country and folk genres, totaling at 9.9M chords instances and 6000 different chords. this is for my project https://github.com/eyaler/uku3le currently being reworked.

These are the most common issues and qualities that fail parsing, and seem to have sensible solutions

  1. German notation uses H, Hm for B, Bm and this is relevant also for base chords.
  2. 7sus, 9sus with out a following number. should probably be a synonym for 7sus4, 9sus4?
  3. Maj7, should be allowed for maj7
  4. mmaj7, mMaj7 should be allowed for mM7
  5. 7sus2 afaiu is (0, 2, 7, 10)
  6. (add9), m(add9), (maj7), m(maj7), same for maj9, (aug), (dim) (sus4), 7(sus4), (sus2), 7(sus2), (2), (4), (5), (7), (9), (11) -> remove brackets
  7. add2 and (add2) afaiu is (0, 2, 4, 7)
  8. 6sus2 afaiu is (0, 2, 7, 9)
  9. E# -> F, B# -> C, Fb -> E, Cb -> B, H# -> C, and also for base chords
  10. maj7sus2 is (0, 2, 7, 11)?
  11. maj7sus4 is (0, 5, 7, 11)?
  12. ends with + or +5 to donate aug,
  13. ends with m+ or m+5 or m# or m#5 for (0, 3, 8)?
  14. ends with 7+ to donate 7+5
  15. maj7+5 (or Maj7+5) to donate (0, 4, 8, 11)?
  16. 6sus4 (or just 6sus) afaikt is (0, 5, 7, 9)
  17. strip white space so e.g: "C " is "C", "A7 " is "A7"
  18. 7M is probably maj7
  19. 6add9 is 69? and m6add9 to m69
  20. madd11 to (0, 3, 7, 17)?
  21. sus7 is probably 7sus4?
  22. strip asterisks (*)
  23. replace ° or º to dim, also if quality (ignoring base) is just 'o'
  24. if the quality (ignoring the base) is just 'M' or 'mi' it is probably safe to assume it is 'm'
  25. fixing caps where obvious: ADD, Add -> add; MAJ, Maj -> maj, SUS...
  26. m(maj9) -> (0, 3, 7, 10, 14) ?
  27. add4add9, add9add4 -> (0, 4, 5, 7, 14)
  28. min7 -> m7
  29. ma7 -> maj7
  30. -5, (-5) -> omit5
  31. maj7#11, maj7+11 -> M7+11
  32. add#11 -> (0, 4, 7, 18) ?
  33. m13 -> (0, 3, 7, 10, 14, 21) ?
  34. s4 -> sus4
  35. 7add11 and 6add11 -> (0, 4, 7, 10, 17)) and (0, 4, 7, 9, 17)) ?
  36. i also take care of do/re/mi/fa/sol/la/si (case insensitive) which may be following by #/b/7/m and as the base chord

i could go on... but the above helped me reduce the song reject rate in my case from 6.3% to 1.1%

fixes may be required also in from_note_index()

of course instead of dealing with all specific cases it would be useful to have generic normalization rules as
fixing caps where no ambiguity, eg: ADD, Add -> add
fixing strings where no ambiguity, eg: maj -> M
removing brackets where no ambiguity
ends with + or +5 -> aug
etc.
such generic rules (where there is no danger of ambiguity) would greatly help maintaining the qualities table.

disclaimer: i do not know anything about music or music theory.

Incorrect chord recognition if the notes are in a non-rotated order

I ran into this issue. If you try to recognize a say cmaj like so

ch = find_chords_from_notes(["C", "E", "G"])

It works correctly.

However, if i reorder the notes in a way that is not a rotation of the chord

ch = find_chords_from_notes(["C", "G", "E"])

finds nothing.

Would sorting the nodes before recognizing the chord help?

Random chords

I would like to have classes which can generate a list of random chords. I will probably start the development very soon. This may be useful for music theory training

Create class for chord progressions

>>> cp = ChordProgression([Chord("C"), Chord("G")])
>>> cp.append(Chord("Am"))
>>> cp
["C", "G", "Am"]
>>> cp.transpose(+2)
>>> cp
["D", "A", "Bm"]

Perhaps wrong implementation of some chord qualities as 13th chords

Observation

  • I'm using pychord-0.5.1

  • In constant QUALITY_DICT, 13th quality does not have the same number of degrees. 11th degree or 9th degree is sometimes missing. While writing this message I can see similar situation with 11th chords. Here follow some examples:

Quality degree 1 degree 3 degree 5 degree 7 degree 9 degree 11 degree 13 Comments
7b9b13 0 4 7 10 13 17 20 Correct
13 0 4 7 10 14 21 WRONG: 11th degree is missing
13-9 & 13b9 0 4 7 10 13 21 WRONG: 11th degree is missing
13+9 & 13#9 0 4 7 10 15 21 WRONG: 11th degree is missing
13+11 & 13#11 0 4 7 10 18 21 WRONG: 9th degree is missing
7-13 & 7b13 0 4 7 10 20 WRONG: 9th degree & 11th degree are missing
11 0 7 10 14 17 X WRONG: 3rd degree is missing
7+11 & 7#11 0 4 7 10 18 X WRONG: 9th degree is missing

Suggestion

  • I think that basically:

    • a 7th chord has 4 notes (b5#5 is an exception)
    • a 9th chord has 5 notes (b9#9 is an exception)
    • a 11th chord has 6 notes
    • a 13th chord has 7 notes
  • Could you please take these observation into account and fix things that may be fixed ?

  • I can make these changes by myself and make a pull request but I need to know how I can contribute to your project on github.

Cheers,
Serge.

find_chords_from_notes returns too few chords

find_chords_from_notes(['A', 'C', 'E', 'G']) returns only 2 chords [<Chord: Am7>, <Chord: C6/A>]

  • on my side, i have 26 (with some possible errors regarding names - some are also missing in this list such as E or G based chord names):
    [<Chord: Am7>, <Chord: Am7/9>, <Chord: Am7/11>, <Chord: Am7/13>, <Chord: Am/G>, <Chord: Am7/C>, <Chord: Am7/E>, <Chord: Am7/G>, <Chord: Am7/9/C>, <Chord: Am7/9/E>, <Chord: Am7/9/G>, <Chord: Am7/11/C>, <Chord: Am7/11/E>, <Chord: Am7/11/G>, <Chord: Am7/13/C>, <Chord: Am7/13/E>, <Chord: Am7/13/G>, <Chord: C6>, <Chord: C6/9>, <Chord: C/A>, <Chord: C6/A>, <Chord: C6/E>, <Chord: C6/G>, <Chord: C6/9/A>, <Chord: C6/9/E>, <Chord: C6/9/G>]
  • the cause is due to notes_to_positions() which returns high positions (19, 21, up to 27 - as far as i can remember)
  • this forces to extend the qualities possibilities such as (0, 3, 10, 19) which would lead to fundamental changes such as updating Quality.find_quality_from_components() with a %12 half tone-based check instead of list(q.components) == components (l.128)
  • this leads to a possible change in the philosophy of the chord qualities since 19 sounds like 7 but this is not the exact same sound (1 octave difference)

What is your vision on this matter?

Missing quality

Hello, the quality "5+" is missing (it's the same as aug).
Example of use:
image

Ability to iterate qualities in Quality Manager

It'd be nice to be able to iterate what qualities pychord supports.

My specific use case is that I'm scanning existing qualities, and adding some related ones, specifically a no-5th and flat-5th variation on existing qualities.

Right now I have to peek inside QualityManager()._qualities to do that, which works, but is accessing a protected member, which is a little gross.

Could be included omitt chords and diminished 5th triad.

Could be included in qualities.py the diminished 5th triad
and chords with omitted notes using either 'no' or 'omitt

2 notes
('no5', (0, 4)), # ('omitt5', (0, 4))
('m(no5)', (0, 3)), # ('m(omitt5)', (0, 3))

3 notes
('(b5)', (0, 4, 6)),

4 notes
('sus4add2', (0, 2, 5, 7),
('sus4add9', (0, 5, 7, 14),

"pychord.analyzer.note_to_chord" wrong chord calculated

Dear all,
the example on the bottom shows the problems with "pychord.analyzer.note_to_chord"
Calling this function the second time using the same parameters returns a different result.
Debugging shows that def: "find_quality" in "pychord/pychord/analyzer.py" has a reference to "QUALITY_DICT" that includes crahed data in "dim6". Try this example and you will see.


-- coding: utf-8 --

#!/usr/bin/env python3

Imports

from future import print_function
import sys
from pychord.analyzer import note_to_chord
from pychord.utils import val_to_note

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Constants
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
k_NOTE_OFF = 0
k_NOTE_ON = 1

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Chords
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

def chord_receive(info,n1,n2,n3,n4):

print("\n",info,"  --------------------")
print("----with notes",  n1,n2,n3,n4)

a_chords = note_to_chord([n1,n2,n3,n4])
print("--------",a_chords)

for a_chord in a_chords:
    print('------------a_chordStr=%s' % (a_chord))
    print("------------components", a_chord.components(True))

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Main
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
try:
chord_receive("first","D","F","G#","B")
chord_receive("second","D","F","G#","B")

except KeyboardInterrupt:
pass

Create chord qualities diatonically (from_note_index)

Currently chords can be created with .from_note_index, with chord quality manually specified.

>>> [Chord.from_note_index(note=i, quality="", scale="Cmaj") for i in range(1,8)]
[<Chord: C>, <Chord: D>, <Chord: E>, <Chord: F>, <Chord: G>, <Chord: A>, <Chord: B>]

Basically note is only there to determine the root. I was thinking of adding a boolean option to adjust certain chord qualities according to the scale.

>>> [Chord.from_note_index(note=i, quality="", diatonic=True, scale="Cmaj") for i in range(1,8)]
[<Chord: C>, <Chord: Dm>, <Chord: Em>, <Chord: F>, <Chord: G>, <Chord: Am>, <Chord: Bdim>]

I think this option would be most useful for the most "generic" chord types, like triads and sevenths.

I'd be willing to implement this; I think it'll be mostly messing around with RELATIVE_KEY_DICT to get the relevant scale degrees, no?

2 notes chords. Probably "sus" chord is impemented in a wrong way

Hello,

Description

I'm using pychord-0.5.1

In constant QUALITY_DICT, there are 2 "2 chords qualities"

  • 5: (0, 7)
  • sus: (0,7)

Suggestion

  • I think that sus quality should be implemented with value (0,5) since I guess it corresponds to the interval between degree-V to degree-VIII (from G to upper C for example)

  • If I'm right, could you please make the change ?

Sincerely,
Serge.

How to transpose a chord in a given scale ?

Hello,
I would like to transpose my C chord (C, E, G) in order to get (D, F, A).
I tried:
1/ c = Chord.from_note_index(note=1, quality="", scale="Cmaj")
c.transpose(2)

2/ c = Chord.from_note_index(note=2, quality="", scale="Cmaj")

Both give (D, F#, A). I was expected to get (D, F, A) in the second case

Thanks !

quality aliases

some common notation that is missing:

Xsus = X5

Xmaj7 = XM7

Xmaj9 = XM9

Xm6 = (0, 3, 7, 9)

Xmadd9 = (0, 3, 7, 14)

also is there a general way to add quality for aliases?

One missing quality m7b9b5

Hello,
Please could you add this missing quality?

m7b9b5 = 0, 3, 6, 10, 13

It's the 9th version of the m7-5 which can be found in 6th degree chord in major scales.

Edge case bug in Chord.components()

An example:

In [12]: Chord('Abm').components()
Out[12]: ['Ab', 'B', 'Eb']          # Should be ['Ab', 'Cb', 'Eb']

I might be mistaken (not an expert in music theory), but I believe Cb is more correct, from a strict perspective.

Chord construction starts with note names: an A triad of any flavor must contain some flavor of A, C, and E. From there, you apply the sharps, flats, double-flats, or double-sharps as needed to achieve the desired chord root and quality. So, in our example, we start with an A triad (A, C, E), flatten everything because we want root of Ab (Ab, Cb, Eb). Since the
A-to-C distance is already minor, we're done.

Another example:

In [13]: Chord('Cbm').components()
Out[13]: ['B', 'D', 'Gb']          # Should be ['Cb', 'Ebb', 'Gb']

Start with C, E, G. Flatten everything to Cb, Eb, Gb. The C-to-E distance is major, so double-flatten it to get Cb, Ebb, Gb.

Thanks for taking a look and working on this project.

Support for Augmented and Diminished Triads?

I stumbled upon your repo while looking for a way to easily parse my database of chord progressions into midi files. However, my database has a lot of augmented and diminished triads in the progressions. The program was not able to recognize augmented or diminished chords, which slightly disappointed me. It would be nice to have these triads built-in instead of needing a custom parser to recognize them

Regards,
Connor

Support multiple variation of a chord

As discussed in #54, some notes of a chord can be omitted as customary.

>>> note_to_chord(["C", "E", "G", "Bb", "D", "F"])
[<Chord: C11>]

# omit thrid
>>> note_to_chord(["C", "G", "Bb", "D", "F"])
[<Chord: C11>]

However, a quality can have only one combination of notes in the current implementation. It should support multiple combinations or omittable notes.

'11', [(0, 4, 7, 10, 14, 17), (0, 7, 10, 14, 17)]),

'11', Quality((0, 4, 7, 10, 14, 17), omittable=[4])),

Transpose chord

Implement feature to transpose chord.

>>> c = Chord("Am7/G")
>>> c.transpose(3)
>>> c
Cm7/Bb

Output chord progressions as Roman numerals for analysis

First of all this is an excellent library. I'm using it at the moment to analyze pop song and their chord progression.

I have a question, is it possible to add support for Roman Numeral notations? I couldn't find it in documentation or code, but maybe I'm overlooking something.

So I would like to do something like this for example:

cp = pychord.ChordProgression(["Am", "Dm", "G", "C", "F", "Bdim", "E", "Am"])

cp = cp.as_roman_numeral(scale="Amin") # note: .as_roman_numeral() would be new function

>>> ["i", "iv", "VII", "III", "VI", "iidim", "V", "i"]

The way I do it now is using the code below, which I realise is not ideal and a bit hack-y

ROMAN_NUMERAL = {
   -1: "??",
    1: "I",
    2: "II",
    3: "III",
    4: "IV",
    5: "V",
    6: "VI",
    7: "VII",
    8: "VIII"
}

# create chord progression
cp = pychord.ChordProgression(["Am", "Dm", "G", "C", "F", "Bdim", "E", "Am"])
chords_roman = [];
scale = pychord.Chord("Amin")

# which scale
scale_root = scale.root
scale_type = ("min" if str(scale.quality) == 'min' else "maj")
scale_idx = NOTE_VAL_DICT[scale.root]

# iterate all chords
for c in cp:
    # next chord
    str_chord = c
    # determine root note, relative to scale, 
    note_idx = NOTE_VAL_DICT[c.root]
    relative_idx = (12 + note_idx - scale_idx) % 12

    # determine roman numeral
    roman_idx = (-1 if relative_idx not in RELATIVE_KEY_DICT[scale_type] else RELATIVE_KEY_DICT[scale_type].index(relative_idx) + 1)
    roman_num = ROMAN_NUMERAL[roman_idx]
    chord_qual = str(c.quality)
    roman_num = (roman_num.lower() if chord_qual in ['m', 'min'] else roman_num)
    roman_num = roman_num + (chord_qual if chord_qual in ['dim'] else "")

    chords_roman.append(roman_num)

# result
print(cp)
print(f'Roman numerals (scale is {scale_root}{scale_type})')
print(chords_roman)

>>> Am | Dm | G | C | F | Bdim | E | Am
>>> Roman numerals (scale is Amin)
>>> ['i', 'iv', 'VII', 'III', 'VI', 'IIdim', 'V', 'i']

Chords with time to generate a midi file

Hi,

I have a code that extracts the chords from the audio. It returns the following

  1. Start time of the chord
  2. Signature
  3. Name of the chord
    (Image attached)
    image

I want to recreate a MIDI file based on that information. How can I do that?

Thanks,
Suti

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.