saysynth.core.chord

The Chord class creates a list of Note which can be played in parallel, producing polyphonic sounds.

  1"""
  2The Chord class creates a list of `Note` which can be played in parallel, producing polyphonic sounds.
  3<center><img src="/assets/img/coffee.png"></img></center>
  4"""
  5from typing import Any, Dict, List
  6
  7from midi_utils import midi_chord, note_to_midi
  8
  9from ..cli.options import prepare_options_for_say
 10from ..lib import say
 11from .note import Note
 12
 13
 14class Chord(object):
 15    """
 16    The Chord class creates a list of `Note` objects which can
 17    be played in parallel, producing polyphonic sounds.
 18
 19    Args:
 20        note/root: The base root of the chord
 21        chord_notes: An optional list of midi numbers to build a chord from based off of the root. For example, the notes '[0,3,7]' with the root of 'C1' would create a C-minor chord.
 22        **note_options: Additional options to pass to `Note`. These will affect how each note of the arpeggiator sounds.
 23    """
 24
 25    def __init__(self, note=None, root=None, chord_notes=[], **note_options):
 26        self.root = note or root or "A1"  # root == note
 27        self.root_midi = note_to_midi(self.root)
 28        self.chord_notes = chord_notes
 29        self.note_options = note_options
 30
 31    def _get_kwargs(self, **kwargs) -> Dict[str, Any]:
 32        """
 33        get kwargs + update with new ones
 34        used for mapping similar kwargs over different notes
 35        """
 36        d = dict(self.note_options.items())
 37        d.update(kwargs)
 38        return d
 39
 40    @property
 41    def midi_notes(self) -> List[int]:
 42        # check for arbitrary notes
 43        if len(self.chord_notes):
 44            return [self.root_midi + n for n in self.chord_notes]
 45        return midi_chord(root=self.root, **self.note_options)
 46
 47    @property
 48    def notes(self) -> List[Note]:
 49        """
 50        The generated list of `Note` in the Chord.
 51        """
 52        _notes = []
 53        for n in self.midi_notes:
 54            _notes.append(Note(**self._get_kwargs(note=n)))
 55        return _notes
 56
 57    @property
 58    def n_notes(self) -> int:
 59        """
 60        The number of Notes in the Chord.
 61        """
 62        return len(self.notes)
 63
 64    def write(self, output_file: str) -> None:
 65        """
 66        Write each Note in the Chord to an output file,
 67        adding the note name to the provided filepath.
 68        """
 69        for note in self.notes:
 70            fn = ".".join(output_file.split(".")[:-1])
 71            ext = (
 72                "txt" if "." not in output_file else output_file.split(".")[-1]
 73            )
 74            note_output_file = f"{fn}-{note.note}.{ext}"
 75            with open(note_output_file, "w") as f:
 76                f.write(note.to_say_text())
 77
 78    def _get_audio_output_file_for_note(self, filename, note: Note) -> str:
 79        fn = ".".join(filename.split(".")[:-1])
 80        ext = "aiff" if "." not in filename else filename.split(".")[-1]
 81        return f"{fn}-{note.name}.{ext}"
 82
 83    def _get_cmd_for_note(self, note: Note):
 84        cmd = prepare_options_for_say(
 85            input_text=note.to_say_text(),
 86            **self._get_kwargs(note=note.note, type="note"),
 87        )
 88        audio_output_file = cmd.get("audio_output_file", None)
 89        if audio_output_file:
 90            cmd["audio_output_file"] = self._get_audio_output_file_for_note(
 91                audio_output_file, note
 92            )
 93        return cmd
 94
 95    @property
 96    def commands(self) -> List[Dict[str, Any]]:
 97        """
 98        A list of note commands to spawn.
 99        """
100        return [self._get_cmd_for_note(note) for note in self.notes]
101
102    def play(self, **kwargs) -> None:
103        """
104        Play each `Note` of the Chord in parallel.
105        """
106        say.spawn(self.commands)
107
108    def cli(self, **kwargs) -> None:
109        """
110        Handle the execution of this Chord
111        within the context of the CLI.
112        """
113        chord = Chord(**kwargs)
114        output_file = kwargs.get("output_file")
115        if output_file:
116            chord.write(output_file)
117        else:
118            chord.play(**kwargs)
class Chord:
 15class Chord(object):
 16    """
 17    The Chord class creates a list of `Note` objects which can
 18    be played in parallel, producing polyphonic sounds.
 19
 20    Args:
 21        note/root: The base root of the chord
 22        chord_notes: An optional list of midi numbers to build a chord from based off of the root. For example, the notes '[0,3,7]' with the root of 'C1' would create a C-minor chord.
 23        **note_options: Additional options to pass to `Note`. These will affect how each note of the arpeggiator sounds.
 24    """
 25
 26    def __init__(self, note=None, root=None, chord_notes=[], **note_options):
 27        self.root = note or root or "A1"  # root == note
 28        self.root_midi = note_to_midi(self.root)
 29        self.chord_notes = chord_notes
 30        self.note_options = note_options
 31
 32    def _get_kwargs(self, **kwargs) -> Dict[str, Any]:
 33        """
 34        get kwargs + update with new ones
 35        used for mapping similar kwargs over different notes
 36        """
 37        d = dict(self.note_options.items())
 38        d.update(kwargs)
 39        return d
 40
 41    @property
 42    def midi_notes(self) -> List[int]:
 43        # check for arbitrary notes
 44        if len(self.chord_notes):
 45            return [self.root_midi + n for n in self.chord_notes]
 46        return midi_chord(root=self.root, **self.note_options)
 47
 48    @property
 49    def notes(self) -> List[Note]:
 50        """
 51        The generated list of `Note` in the Chord.
 52        """
 53        _notes = []
 54        for n in self.midi_notes:
 55            _notes.append(Note(**self._get_kwargs(note=n)))
 56        return _notes
 57
 58    @property
 59    def n_notes(self) -> int:
 60        """
 61        The number of Notes in the Chord.
 62        """
 63        return len(self.notes)
 64
 65    def write(self, output_file: str) -> None:
 66        """
 67        Write each Note in the Chord to an output file,
 68        adding the note name to the provided filepath.
 69        """
 70        for note in self.notes:
 71            fn = ".".join(output_file.split(".")[:-1])
 72            ext = (
 73                "txt" if "." not in output_file else output_file.split(".")[-1]
 74            )
 75            note_output_file = f"{fn}-{note.note}.{ext}"
 76            with open(note_output_file, "w") as f:
 77                f.write(note.to_say_text())
 78
 79    def _get_audio_output_file_for_note(self, filename, note: Note) -> str:
 80        fn = ".".join(filename.split(".")[:-1])
 81        ext = "aiff" if "." not in filename else filename.split(".")[-1]
 82        return f"{fn}-{note.name}.{ext}"
 83
 84    def _get_cmd_for_note(self, note: Note):
 85        cmd = prepare_options_for_say(
 86            input_text=note.to_say_text(),
 87            **self._get_kwargs(note=note.note, type="note"),
 88        )
 89        audio_output_file = cmd.get("audio_output_file", None)
 90        if audio_output_file:
 91            cmd["audio_output_file"] = self._get_audio_output_file_for_note(
 92                audio_output_file, note
 93            )
 94        return cmd
 95
 96    @property
 97    def commands(self) -> List[Dict[str, Any]]:
 98        """
 99        A list of note commands to spawn.
100        """
101        return [self._get_cmd_for_note(note) for note in self.notes]
102
103    def play(self, **kwargs) -> None:
104        """
105        Play each `Note` of the Chord in parallel.
106        """
107        say.spawn(self.commands)
108
109    def cli(self, **kwargs) -> None:
110        """
111        Handle the execution of this Chord
112        within the context of the CLI.
113        """
114        chord = Chord(**kwargs)
115        output_file = kwargs.get("output_file")
116        if output_file:
117            chord.write(output_file)
118        else:
119            chord.play(**kwargs)

The Chord class creates a list of Note objects which can be played in parallel, producing polyphonic sounds.

Arguments:
  • note/root: The base root of the chord
  • chord_notes: An optional list of midi numbers to build a chord from based off of the root. For example, the notes '[0,3,7]' with the root of 'C1' would create a C-minor chord.
  • **note_options: Additional options to pass to Note. These will affect how each note of the arpeggiator sounds.
Chord(note=None, root=None, chord_notes=[], **note_options)
26    def __init__(self, note=None, root=None, chord_notes=[], **note_options):
27        self.root = note or root or "A1"  # root == note
28        self.root_midi = note_to_midi(self.root)
29        self.chord_notes = chord_notes
30        self.note_options = note_options

The generated list of Note in the Chord.

n_notes: int

The number of Notes in the Chord.

def write(self, output_file: str) -> None:
65    def write(self, output_file: str) -> None:
66        """
67        Write each Note in the Chord to an output file,
68        adding the note name to the provided filepath.
69        """
70        for note in self.notes:
71            fn = ".".join(output_file.split(".")[:-1])
72            ext = (
73                "txt" if "." not in output_file else output_file.split(".")[-1]
74            )
75            note_output_file = f"{fn}-{note.note}.{ext}"
76            with open(note_output_file, "w") as f:
77                f.write(note.to_say_text())

Write each Note in the Chord to an output file, adding the note name to the provided filepath.

commands: List[Dict[str, Any]]

A list of note commands to spawn.

def play(self, **kwargs) -> None:
103    def play(self, **kwargs) -> None:
104        """
105        Play each `Note` of the Chord in parallel.
106        """
107        say.spawn(self.commands)

Play each Note of the Chord in parallel.

def cli(self, **kwargs) -> None:
109    def cli(self, **kwargs) -> None:
110        """
111        Handle the execution of this Chord
112        within the context of the CLI.
113        """
114        chord = Chord(**kwargs)
115        output_file = kwargs.get("output_file")
116        if output_file:
117            chord.write(output_file)
118        else:
119            chord.play(**kwargs)

Handle the execution of this Chord within the context of the CLI.