Skip to content

Commit

Permalink
Remove solver module and chord locks
Browse files Browse the repository at this point in the history
  • Loading branch information
p3zo committed Jun 18, 2024
1 parent 9243e26 commit a6c9c83
Show file tree
Hide file tree
Showing 13 changed files with 54 additions and 1,035 deletions.
23 changes: 6 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Chord progressions

`chord-progressions` provides utility functions for extracting, analyzing, and generating chord progressions.
`chord-progressions` provides utility functions for extracting and analyzing chord progressions.

It can be installed from PyPI:

Expand All @@ -18,13 +18,13 @@ the [Makefile](Makefile) for all available actions.

To upgrade the version and trigger a new release, use `bump-my-version bump minor chord_progressions/__init__.py`.

## Docs
### Docs

The documentation uses [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) to build a static site from
Markdown files. Use `mkdocs serve` to start a live preview server of the site that automatically rebuilds upon saving.
Use `mkdocs build` to build the site.

## TODO
### TODO

Analysis

Expand All @@ -34,28 +34,17 @@ Analysis

I/O

- [feature] add `from_audio` and `from_midi` constructors to Progression class to enable
e.g. `progression = chord_progressions.Progression.from_midi('my_file.mid')`
- [feature] add `from_audio` and `from_midi` constructors to Progression class to enable e.g. `Progression.from_midi('my_file.mid')`
- [maintainability] use a singular method of counting in `extract_harman.py`
- `get_segment_label` uses counter
- `get_segment_pc_weights` uses defaultdict
- `MinimalSegment.get_pitch_class_weights` uses list.count()
- [performance] `extract.midi_harman.segment_and_label()` gets really slow for many consecutive segments. test that a
- [performance] `extract.midi_harman.segment_and_label()` is slow for many consecutive segments. test that a
maximum of six segment evaluations occur per note

Generation

- [feature] VAE solver
- [feature] use variable chord durations
- [feature] consider lower and upper structures in select_voicing
- [feature] add the possibility of repeated notes in different octaves
- [feature] ability to constrain key

Misc

- [feature] Host docs site
- [feature] Generalize `noteNumberToFrequency` to use any periodic
tuning (see https://github.com/soul-lang/SOUL/pull/26/files)
- [feature] Generalize `noteNumberToFrequency` to use any periodic tuning (see https://github.com/soul-lang/SOUL/pull/26/files)
- [feature] implement `is_partial_circular_match`, `chord_contained_in_type`, and `get_possible_types_of_chord`
- [maintainability] mk `pitch_class` refer to "C" and call 0 `pitch_class_ix`
- [maintainability] pass midi note numbers everywhere as "notes" and include note names as "noteNames"
Expand Down
55 changes: 3 additions & 52 deletions chord_progressions/progression.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from chord_progressions.chord import Chord
from chord_progressions.io.audio import make_audio_progression, save_audio_buffer
from chord_progressions.io.midi import get_midi_from_progression
from chord_progressions.solver import select_chords

"""
Durations are specified in Tone.Time.Notation format.
Expand Down Expand Up @@ -55,7 +54,7 @@ def seconds_to_duration(seconds, bpm):

class Progression:
"""
A sequence of Chords with metadata such as bpm & chord locks.
A sequence of Chords with metadata such as bpm.
Parameters
----------
Expand All @@ -64,9 +63,6 @@ class Progression:
durations: list[str] or list[float], default []
The duration of the chords, specified in Tone.Time notation or in seconds.
Seconds will be quantized to Tone.Time units.
locks: str, default None
Binary string with length equal to the number of existing chords, e.g. 101011, where 1 = locked, 0 = unlocked.
Defaults to all unlocked if not provided.
bpm: float, default DEFAULT_BPM
The beats per minute (BPM) of the progression.
name: str, default ""
Expand All @@ -77,7 +73,6 @@ def __init__(
self,
chords: list[Chord] = [],
durations: list[str] = [],
locks: str = None,
bpm: float = DEFAULT_BPM,
name: str = "",
):
Expand Down Expand Up @@ -107,7 +102,6 @@ def __init__(
self.durations = durations
self.bpm = bpm
self.name = name
self.locks = locks or "0" * len(chords)
self.metrics = {} # TODO: add metrics

def __iter__(self):
Expand All @@ -122,7 +116,7 @@ def __repr__(self):
return "Progression " + self.to_string()

def __getitem__(self, ix):
# TODO: include progression-chord metadata, e.g. its duration & lock status
# TODO: include progression-chord metadata, e.g. its duration
return self.chords[ix]

def to_string(self):
Expand All @@ -132,15 +126,12 @@ def to_json(self):
# TODO: include progression-level attrs like name, bpm
result = []

for ix, (chord, duration, locked) in enumerate(
list(zip(self.chords, self.durations, self.locks))
):
for ix, (chord, duration) in enumerate(list(zip(self.chords, self.durations))):
result.append(
{
"id": chord.id,
"ix": ix,
"duration": duration,
"locked": locked,
"type": chord.type,
"typeId": chord.typeId,
"notes": chord.notes,
Expand Down Expand Up @@ -169,43 +160,3 @@ def to_midi(self, outpath=None):
mid.filename = outpath
mid.save(outpath)
logger.info(f"Midi saved to {outpath}")

def get_new_solution(self, **constraints):
"""Given existing locked chords and constraints, returns a new chord progression of the same length"""

existing_chords = self.chords

chords = select_chords(
n_chords=len(existing_chords),
existing_chords=existing_chords,
locks=self.locks,
**constraints,
)

return Progression(chords, durations=self.durations)

def get_addition(self, **constraints):
existing_chords = self.chords

chords = select_chords(
n_chords=len(existing_chords) + 1,
existing_chords=existing_chords,
locks="1" * len(existing_chords) + "0",
**constraints,
)

durations = self.durations + ["1m"]

return Progression(chords, durations=durations)

def lock(self, ix):
"""Locks the chord at index `ix`"""
locks = list(self.locks)
locks[ix] = "1"
self.locks = "".join(locks)

def unlock(self, ix):
"""Unlocks the chord at index `ix`"""
locks = list(self.locks)
locks[ix] = "0"
self.locks = "".join(locks)
Loading

0 comments on commit a6c9c83

Please sign in to comment.