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.
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.
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.