Metadata-Version: 2.1
Name: jchord
Version: 0.2.1
Summary: Python toolkit for working with chord progressions
Home-page: https://github.com/jonathangjertsen/jchord
Author: Jonathan Reichelt Gjertsen
Author-email: jonath.re@gmail.com
License: MIT
Project-URL: Documentation, https://github.com/jonathangjertsen/jchord
Project-URL: Source, https://github.com/jonathangjertsen/jchord
Description: # jchord - Python toolkit for working with chord progressions
        
        [![Build Status](https://travis-ci.com/jonathangjertsen/jchord.svg?branch=master)](https://travis-ci.com/jonathangjertsen/jchord)
        [![codecov](https://codecov.io/gh/jonathangjertsen/jchord/branch/master/graph/badge.svg)](https://codecov.io/gh/jonathangjertsen/jchord)
        
        ```
        >>> from jchord.progressions import ChordProgression
        >>> prog = ChordProgression.from_string("C -- Fm7 -- C -- G7 -- C -- E7 Am F Bm7b5 E7 Am9 F Bo C69 --")
        >>> print(prog.to_string())
        C      --     Fm7    --
        C      --     G7     --
        C      --     E7     Am
        F      Bm7b5  E7     Am9
        F      Bo     C69    --
        >>> prog.to_midi("example.midi", tempo=100, beats_per_chord=2, instrument=4)
        ```
        
        * Chord progressions can be imported from:
            - string (`prog = ChordProgression.from_string(string)`)
            - text file (`prog = ChordProgression.from_txt(filename)`)
            - XLSX file (`prog = ChordProgression.from_xlsx(filename)`)
        * Chord progressions can be exported to:
            - string (`print(prog.to_string(...optional))`)
            - text file (`prog.to_txt(filename, ..optional)`)
            - XLSX file (`prog.to_xlsx(filename, ...optional)`)
            - MIDI file (`prog.to_midi(filename, ...optional)`)
        
        # Install
        
        * `pip install jchord`
        * If you want to use the MIDI and XLSX functionality, also `pip install mido openpyxl`
        
        # Development
        
        Clone the repo, and in the folder run `pip install -r requirements_dev.txt`
        
        ## Testing
        
        To run the tests:
        
        ```
        pytest --cov-report term-missing --cov jchord -vvv
        ```
        
        Make sure all tests pass and that the code has 100% test coverage.
        
        ## Formatting
        
        The code is formatted with black. In the root of the repo, run:
        
        ```
        black .
        ```
        
        ## Documenting
        
        To generate documentation, run the documentation generation script:
        
        ```
        python doc_gen/generate_doc.py -o README.md
        ```
        
        The script appends documentation from each source module to `doc_gen/index.md`. If the script ran successfully, README.md should contain a section called "Documentation" below.
        
        To decide which parts should be present in the documentation, go and edit `doc_gen/pydocmd.yml`. The reason for the format is that I originally wanted to use `pydocmd` (but found it too limiting).
        
        ---
        
        # Documentation
        
        ## Table of contents
        * <a href='#jchordcore'>`jchord.core`</a>
            * Class <a href='#Note'>`Note`</a>
                * Method `Note.__init__(self, name: str, octave: int)`
                * Method `Note.pitch(self) -> float`
                * Method `Note.transpose(self: 'Note', shift: int) -> 'Note'`
                * Method `Note.transpose_degree(self: 'Note', shift: str) -> 'Note'`
            * Class <a href='#CompositeObject'>`CompositeObject`</a>
                * Method `CompositeObject._keys(self) -> Hashable`
            * Function `split_to_base_and_shift(name_or_degree: str, name_before_accidental: bool) -> (<class 'str'>, <class 'int'>)`
            * Function `degree_to_semitone(degree: str) -> int`
            * Function `semitone_to_degree_options(semitone: int, max_accidentals: int = 1) -> List[str]`
            * Function `note_diff(name_low: str, name_high: str) -> int`
            * Exception `InvalidDegree`
        * <a href='#jchordmidi'>`jchord.midi`</a>
            * Class <a href='#PlayedNote'>`PlayedNote`</a>
                * Method `PlayedNote.time(self)`
                * Method `PlayedNote.note(self)`
                * Method `PlayedNote.duration(self)`
            * Function `note_to_midi(note: jchord.core.Note) -> int`
            * Function `midi_to_note(midi: int) -> jchord.core.Note`
            * Function `midi_to_pitch(midi: int) -> float`
            * Function `read_midi_file(filename: str) -> List[jchord.midi.PlayedNote]`
            * Function `group_notes_to_chords(notes: List[jchord.midi.PlayedNote]) -> Dict[float, jchord.midi.PlayedNote]`
            * Exception `InvalidNote`
        * <a href='#jchordknowledge'>`jchord.knowledge`</a>
        * <a href='#jchordchords'>`jchord.chords`</a>
            * Class <a href='#Chord'>`Chord`</a>
                * Method `Chord.__init__(self, name: str, semitones: List[int])`
                * Method `Chord.from_semitones(name: Union[str, NoneType], semitones: List[int]) -> 'Chord'`
                * Method `Chord.from_degrees(name: str, degrees: List[str]) -> 'Chord'`
                * Method `Chord.from_name(name: str) -> 'Chord'`
                * Method `Chord.intervals(self) -> List[int]`
                * Method `Chord.with_root(self, root: jchord.core.Note) -> 'ChordWithRoot'`
                * Method `Chord.add_semitone(self, semitone: int)`
            * Class <a href='#ChordWithRoot'>`ChordWithRoot`</a>
                * Method `ChordWithRoot.__init__(self, name: str, root: jchord.core.Note, chord: jchord.chords.Chord)`
                * Method `ChordWithRoot.from_root_and_semitones(root: jchord.core.Note, semitones: List[int]) -> 'ChordWithRoot'`
                * Method `ChordWithRoot.from_midi(midi: Set[int]) -> 'ChordWithRoot'`
                * Method `ChordWithRoot.from_name(name: str, octave: int = 4) -> 'ChordWithRoot'`
                * Method `ChordWithRoot.semitones(self)`
                * Method `ChordWithRoot.intervals(self) -> List[int]`
                * Method `ChordWithRoot.midi(self) -> List[int]`
                * Method `ChordWithRoot.transpose(self, shift: int) -> 'ChordWithRoot'`
            * Function `semitones_to_chord_name_options(semitones: Set[int], _rec=5) -> List[str]`
            * Exception `InvalidChord`
        * <a href='#jchordprogressions'>`jchord.progressions`</a>
            * Class <a href='#ChordProgression'>`ChordProgression`</a>
                * Method `ChordProgression.__init__(self, progression: List[jchord.chords.ChordWithRoot])`
                * Method `ChordProgression.from_string(string: str) -> 'ChordProgression'`
                * Method `ChordProgression.from_txt(filename: str) -> 'ChordProgression'`
                * Method `ChordProgression.from_xlsx(filename: str) -> 'ChordProgression'`
                * Method `ChordProgression.from_midi_file(filename: str) -> 'ChordProgression'`
                * Method `ChordProgression.chords(self) -> Set[jchord.chords.ChordWithRoot]`
                * Method `ChordProgression.midi(self) -> List[List[int]]`
                * Method `ChordProgression.to_string(self, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n') -> str`
                * Method `ChordProgression.to_txt(self, filename: str, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n')`
                * Method `ChordProgression.to_xlsx(self, filename: str, chords_per_row: int = 4)`
                * Method `ChordProgression.to_midi(self, filename: str, instrument: int = 1, tempo: int = 120, beats_per_chord: Union[int, list] = 2, velocity: int = 100)`
            * Class <a href='#SongSection'>`SongSection`</a>
                * Method `SongSection.name(self)`
                * Method `SongSection.progression(self)`
            * Class <a href='#Song'>`Song`</a>
                * Method `Song.__init__(self, sections: List[jchord.progressions.SongSection])`
                * Method `Song.to_string(self, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n')`
            * Exception `InvalidProgression`
        
        ## `jchord.core`
        
        
        Basic utilities for working with notes, base classes for objects etc.
        
        ### Classes
        
        #### `Note`
        
        Represents an absolute note with a name and an octave.
        
        Two `Note`s are equal if they have the same name and octave,
        or if they have the same octave and their names are enharmonic
        (so `Note('A#', 4) == Note('Gb', 4)`)
        
        
        ##### Methods
        
        ###### `__init__(self, name: str, octave: int)`
        
        
        
        
        ###### `pitch(self) -> float`
        
        Returns the absolute pitch of the note in Hz.
        
        Examples (equalities are approximate):
        
        * `Note("A", 4).pitch() == 440.0`
        * `Note("A", 0).pitch() == 27.5`
        * `Note("C", 4).pitch() == 261.62556`
        
        
        
        ###### `transpose(self: 'Note', shift: int) -> 'Note'`
        
        Transposes the note by the given number of semitones.
        
        Examples:
        
        * `Note("C", 0).transpose(1) == Note("C#", 0)`
        * `Note("C", 4).transpose(19) == Note("G", 5)`
        
        
        
        ###### `transpose_degree(self: 'Note', shift: str) -> 'Note'`
        
        Transposes the given note by the given scale degree
        
        Examples:
        
        * `Note("C", 0).transpose_degree("b2") == Note("C#", 0)`
        * `Note("C", 4).transpose_degree("12") == Note("G", 5)`
        
        
        
        ---
        
        
        
        #### `CompositeObject`
        
        
        Base class for objects which can be represented by a few attributes.
        
        Subclasses must implement a `_keys()` function which returns a hashable
        representation of these attributes (e.g. returns them as a tuple).
        
        A CompositeObject is considered equal to another if the return value of their 
        `_keys()` function is equal.
        
        Iterating over a CompositeObject is like iterating over the return value of its
        `_keys()` function.
        
        
        ##### Methods
        
        ###### `_keys(self) -> Hashable`
        
        Returns a hashable representation of the attributes that the object wraps.
        
        
        ---
        
        
        ### Functions
        
        #### `split_to_base_and_shift(name_or_degree: str, name_before_accidental: bool) -> (<class 'str'>, <class 'int'>)`
        
        Takes a string representation of a note name or a degree. Returns a 
        tuple where the first element is the string representation of the degree
        with accidentals removed, and the second element is the number of semitones
        needed to account fo accidentals.
        
        Examples:
        
        * `split_to_base_and_shift("A#", name_before_accidental=True) == ("A", 1)`
        * `split_to_base_and_shift("Ab", name_before_accidental=True) == ("A", -1)`
        * `split_to_base_and_shift("A", name_before_accidental=True) == ("A", 0)`
        * `split_to_base_and_shift("Abbbb", name_before_accidental=True) == ("A", -4)`
        * `split_to_base_and_shift("b9", name_before_accidental=False) == ("9", -1)`
        * `split_to_base_and_shift("#9", name_before_accidental=False) == ("9", 1)`
        
        
        
        #### `degree_to_semitone(degree: str) -> int`
        
        Converts a string representation of a scale degree to the number of semitones between the root and that scale degree.
        
        Examples:
        
        * `degree_to_semitone("b9") == 13`
        * `degree_to_semitone("5") == 7`
        
        
        
        #### `semitone_to_degree_options(semitone: int, max_accidentals: int = 1) -> List[str]`
        
        Converts the number of semitones between the root and the wanted scale degree to
        a list of possible names for that scale degree.
        
        The list of options is sorted by "reasonableness":
        
        * If option A has fewer accidentals than option B, option A comes first.
        * If option A has the same number of accidentals as option B, then the option with a "b" comes before the option with a "#".
        
        Examples:
        
        * `semitone_to_degree_options(semitone=3, max_accidentals=1) = ["b3", "#2"]`
        * `semitone_to_degree_options(semitone=3, max_accidentals=0) = []`
        * `semitone_to_degree_options(semitone=17, max_accidentals=1) = ["11", "#10"]`
        
        
        
        #### `note_diff(name_low: str, name_high: str) -> int`
        
        Returns the number of semitones between the first note and the second note.
        The first note is assumed to be the lower of the two notes.
        
        Examples:
        
        * `note_diff("G", "A") == 2`
        * `note_diff("A", "Bb") == 1`
        * `note_diff("A", "G") == 10`
        * `note_diff("A", "A") == 0`
        
        
        ### Exceptions
        
        #### `InvalidDegree`
        
        Raised if the string one attempts to interpret is not a valid scale degree
        
        
        
        ---
        
        
        
        ## `jchord.midi`
        
        
        Tools for working with MIDI.
        
        ### Classes
        
        #### `PlayedNote`
        
        namedtuple which represents a (MIDI) note played at a given time for a given duration.
        
        ##### Methods
        
        ###### `time(self)`
        
        Alias for field number 0
        
        
        ###### `note(self)`
        
        Alias for field number 1
        
        
        ###### `duration(self)`
        
        Alias for field number 2
        
        
        ---
        
        
        ### Functions
        
        #### `note_to_midi(note: jchord.core.Note) -> int`
        
        Returns the midi value corresponding to the given `Note`.
        
        
        #### `midi_to_note(midi: int) -> jchord.core.Note`
        
        Returns the `Note` corresponding to the given MIDI note value.
        
        
        #### `midi_to_pitch(midi: int) -> float`
        
        Returns the absolute pitch in Hz for the given MIDI note value.
        
        
        #### `read_midi_file(filename: str) -> List[jchord.midi.PlayedNote]`
        
        Reads the MIDI file for the given filename and returns the corresponding list of `PlayedNote`s.
        
        
        #### `group_notes_to_chords(notes: List[jchord.midi.PlayedNote]) -> Dict[float, jchord.midi.PlayedNote]`
        
        Groups the list of `PlayedNote`s by time.
        
        The return value maps time to a list of `PlayedNote`s for that time.
        
        There is no attempt at quantization at this time, so the notes must be played
        at the exact same time to be grouped together.
        
        
        ### Exceptions
        
        #### `InvalidNote`
        
        Raised when trying to get the MIDI value of a note that doesn't seem valid.
        
        
        
        ---
        
        
        
        ## `jchord.knowledge`
        
        
        Contains various literals that are used for computations throughout the library.
        
        * `REPETITION_SYMBOL` (`str`): The symbol that's used to indicate repetition in textual representations of chord progressions.
        * `MAJOR_SCALE_OFFSETS` (`Dict[int, int]`): Maps the scale degrees of the major scale to number of semitones from the root.
        * `ACCIDENTALS` (`Set[str]`): The set of accidentals.
        * `MAJOR_FROM_C` (`List[str]`): A list of the 7 note names in the C major scale.
        * `CHROMATIC` (`List[str]`): A list of the 12 chromatic notes starting from C. Only sharp notes are included here.
        * `ENHARMONIC` (`List[Tuple[str, str]])`): A list of the 5 pairs of enharmonic note names.
        * `CHORD_NAMES` (`Dict[str, List[str]]`): Maps chord names to the list of scale degrees in the chord, not including the root.
        * `CHORD_ALIASES`: (`Dict[str, str]`): Maps alternative chord names to the canonical chord name in `CHORD_NAMES`.
        * `DYADS`: (`Dict[int, str]`): A collection of two-note chords with names. Maps the number of semitones between the root and the other note to the chord name.
        * `TRIADS_WITH_FIFTH` (`Dict[int, str]`): A collection of three-note chords with names. All of these triads include a fifth. Maps the semitone that is not the root or the fifth to the chord name.
        
        
        
        ---
        
        
        
        ## `jchord.chords`
        
        
        Tools for working with chords.
        
        ### Classes
        
        #### `Chord`
        
        Represents a chord quality (no root).
        
        ##### Methods
        
        ###### `__init__(self, name: str, semitones: List[int])`
        
        
        
        
        ###### `from_semitones(name: Union[str, NoneType], semitones: List[int]) -> 'Chord'`
        
        Creates a Chord from a list of semitones from the root.
        
        If `name` is `None`, `semitones_to_chord_name_options` is used to guess a good name for the chord.
        
        
        
        ###### `from_degrees(name: str, degrees: List[str]) -> 'Chord'`
        
        Creates a Chord from a list of scale degrees.
        
        If `name` is `None`, `semitones_to_chord_name_options` is used to guess a good name for the chord.
        
        
        
        ###### `from_name(name: str) -> 'Chord'`
        
        Creates a Chord from a name.
        The `CHORD_NAMES` and `CHORD_ALIASES` dictionaries in `jchord.knowledge` are used to find the semitones in the chord.
        
        Raises `InvalidChord` if no chord is found.
        
        
        
        ###### `intervals(self) -> List[int]`
        
        Returns the list of internal intervals in the chord.
        
        Examples:
        
        * `Chord.from_name("minor").intervals() == [3, 4]`
        * `Chord.from_name("major7").intervals() == [4, 3, 4]`
        
        
        
        ###### `with_root(self, root: jchord.core.Note) -> 'ChordWithRoot'`
        
        Returns a `ChordWithRoot` based on the chord and the provided root.
        
        
        ###### `add_semitone(self, semitone: int)`
        
        Adds the given semitone (as a difference from the root degree) to the chord.
        
        The name of the chord does not get re-calculated, so use with care.
        
        
        
        ---
        
        
        
        #### `ChordWithRoot`
        
        Represents a chord with a chord quality and a root note.
        
        ##### Methods
        
        ###### `__init__(self, name: str, root: jchord.core.Note, chord: jchord.chords.Chord)`
        
        
        
        
        ###### `from_root_and_semitones(root: jchord.core.Note, semitones: List[int]) -> 'ChordWithRoot'`
        
        Creates a ChordWithRoot from a root `Note` and a list of semitones from the root.
        
        `semitones_to_chord_name_options` is used to guess a good name for the chord.
        
        
        
        ###### `from_midi(midi: Set[int]) -> 'ChordWithRoot'`
        
        Creates a ChordWithRoot from a set of MIDI note values.
        
        `semitones_to_chord_name_options` is used to guess a good name for the chord.
        
        
        
        ###### `from_name(name: str, octave: int = 4) -> 'ChordWithRoot'`
        
        Creates a ChordWithRoot from a name.
        
        `semitones_to_chord_name_options` is used to guess a good name for the chord.
        
        
        
        ###### `semitones(self)`
        
        Returns the semitones in the chord.
        
        
        ###### `intervals(self) -> List[int]`
        
        Returns the semitones in the chord.
        
        
        ###### `midi(self) -> List[int]`
        
        Returns the list of MIDI note values in the chord.
        
        
        ###### `transpose(self, shift: int) -> 'ChordWithRoot'`
        
        Transposes the chord by the given shift (in semitones).
        
        
        ---
        
        
        ### Functions
        
        #### `semitones_to_chord_name_options(semitones: Set[int], _rec=5) -> List[str]`
        
        Returns a set of chord names corresponding to the given set of semitones.
        
        The function tries to put the most reasonable chord names first and the more dubious ones last.
        
        The `_rec` argument is for internal use only; in some cases, it prevents infinite recursion while computing the chord name.
        
        
        ### Exceptions
        
        #### `InvalidChord`
        
        Raised if trying to construct a chord from an invalid chord name.
        
        
        
        ---
        
        
        
        ## `jchord.progressions`
        
        
        Tools for working with chord progressions.
        
        ### Classes
        
        #### `ChordProgression`
        
        Represents a chord progression.
        
        ##### Methods
        
        ###### `__init__(self, progression: List[jchord.chords.ChordWithRoot])`
        
        
        
        
        ###### `from_string(string: str) -> 'ChordProgression'`
        
        Creates a `ChordProgression` from its string representation.
        
        
        ###### `from_txt(filename: str) -> 'ChordProgression'`
        
        Creates a `ChordProgression` from a text file with its string representation.
        
        
        ###### `from_xlsx(filename: str) -> 'ChordProgression'`
        
        Creates a `ChordProgression` from an Excel file.
        
        
        ###### `from_midi_file(filename: str) -> 'ChordProgression'`
        
        Creates a `ChordProgression` from an MIDI file.
        
        There is no attempt at quantization at this time, so the notes must be played
        at the exact same time to be grouped together as chords.
        
        
        
        ###### `chords(self) -> Set[jchord.chords.ChordWithRoot]`
        
        Returns the set of chords in the progression.
        
        
        ###### `midi(self) -> List[List[int]]`
        
        Returns the MIDI values for each chord in the progression.
        
        
        ###### `to_string(self, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n') -> str`
        
        Returns the string representation of the chord progression.
        
        
        ###### `to_txt(self, filename: str, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n')`
        
        Saves the string representation of the chord progression to a text file.
        
        
        ###### `to_xlsx(self, filename: str, chords_per_row: int = 4)`
        
        Saves the chord progression to an Excel file.
        
        
        ###### `to_midi(self, filename: str, instrument: int = 1, tempo: int = 120, beats_per_chord: Union[int, list] = 2, velocity: int = 100)`
        
        Saves the chord progression to a MIDI file.
        
        
        ---
        
        
        
        #### `SongSection`
        
        Represents a section in a Song.
        
        ##### Methods
        
        ###### `name(self)`
        
        Alias for field number 0
        
        
        ###### `progression(self)`
        
        Alias for field number 1
        
        
        ---
        
        
        
        #### `Song`
        
        Represents a song (a series of sections).
        
        ##### Methods
        
        ###### `__init__(self, sections: List[jchord.progressions.SongSection])`
        
        
        
        
        ###### `to_string(self, chords_per_row: int = 4, column_spacing: int = 2, newline: str = '\n')`
        
        Returns the string representation of the song.
        
        
        ---
        
        
        ### Exceptions
        
        #### `InvalidProgression`
        
        Raised when encountering what seems like an invalid chord progression.
        
        
        
        ---
        
        
        
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Description-Content-Type: text/markdown
