def make_note(note_attr_vals: ndarray, attr_name_idx_map: Mapping[str, int], attr_val_cast_map: Mapping[str, Any] = None): validate_type('note_attr_vals', note_attr_vals, ndarray) validate_type('attr_name_idx_map', attr_name_idx_map, Mapping) validate_sequence_of_type('attr_name_idx_map', attr_name_idx_map.keys(), str) validate_optional_type('attr_val_cast_map', attr_val_cast_map, Mapping) if attr_val_cast_map: validate_optional_sequence_of_type('attr_val_cast_map', attr_val_cast_map.keys(), str) cls = _make_cls(attr_name_idx_map) note = cls() # Assign core attributes note.note_attr_vals = note_attr_vals note.attr_name_idx_map = attr_name_idx_map # Set mapping of attribute names to functions that cast return type of get() calls, e.g. cast instrument to int note.attr_val_cast_map = attr_val_cast_map or {} for attr_name in note.attr_name_idx_map: if attr_name not in note.attr_val_cast_map: note.attr_val_cast_map[attr_name] = lambda x: x # These are always returned as an int note.attr_val_cast_map['instrument'] = int note.attr_val_cast_map['velocity'] = int note.attr_val_cast_map['amplitude'] = int note.attr_val_cast_map['pitch'] = int note.attr_val_cast_map['channel'] = int return note
def __init__(self, instruments: Sequence[str] = None, sampling_rate: int = 44100, ksmps: int = 100, num_channels: int = 1, zed_dbfs: int = 1): validate_sequence_of_type('instruments', instruments, str) validate_optional_types( ('sampling_rate', sampling_rate, int), ('ksmps', ksmps, int), ('num_channels', num_channels, int), ('zed_dbfs', zed_dbfs, int)) # These keys should always be defined. Their values are sensible CSound defaults that can be set # to other values if you know what you are doing. self.global_vars = { # Output sampling rate 'sr': sampling_rate, # Ratio of output sampling rate to control rate (actual samples per control period) 'ksmps': ksmps, # Number output channels (mono, stereo, quadraphonic) 'nchnls': num_channels, # Value of 0 decibels, 1 means don't alert amp of output and is most compatible with plugins # Must be written as '0dbfs' in CSound output, but Py vars can't start with a number '0dbfs': zed_dbfs } self.instruments = instruments
def extend(self, to_add: List[Track]) -> 'Song': validate_sequence_of_type('to_add', to_add, Track) self.track_list.extend(to_add) for track in to_add: if track.name: self.track_map[track.name] = track return self
def remove(self, range_to_remove: Tuple[int, int]) -> 'NoteSequence': assert len(range_to_remove) == 2 validate_sequence_of_type('range_to_remove', range_to_remove, int) # noinspection PyTupleAssignmentBalance range_start, range_end = range_to_remove self.note_attr_vals = np_delete(self.note_attr_vals, range(range_start, range_end), axis=0) self.update_range_map() return self
def get_chord_pitches(mingus_keys: Sequence[str], mingus_key_to_key_enum_mapping: Mapping, pitch_for_key: Any, octave: int) -> Sequence[Any]: validate_types(('mingus_key_to_key_enum_mapping', mingus_key_to_key_enum_mapping, Mapping), ('octave', octave, int)) validate_sequence_of_type('mingus_keys', mingus_keys, str) return [ pitch_for_key(mingus_key_to_key_enum_mapping[mingus_key.upper()], octave=octave) for mingus_key in mingus_keys ]
def remove(self, to_remove: Tuple[int, int]) -> 'Song': assert len(to_remove) == 2 validate_sequence_of_type('to_remove', to_remove, int) start_range = to_remove[0] end_range = to_remove[1] assert start_range >= 0 and end_range <= len(self.track_list) tracks_to_remove = self.track_list[start_range:end_range] for track in tracks_to_remove: if track.name: del self.track_map[track.name] del self.track_list[start_range:end_range] return self
def __init__(self, note_lines: Sequence[str] = None, include_lines: Optional[Sequence[str]] = None, header_lines: Optional[Sequence[str]] = None): validate_sequence_of_type('note_lines', note_lines, str) validate_optional_sequence_of_type('include_lines', include_lines, str) validate_optional_sequence_of_type('header_lines', header_lines, str) self.score_lines = [] if include_lines: self.score_lines.extend(include_lines) if header_lines: self.score_lines.extend(header_lines) self.score_lines.extend(note_lines)
def make_note(note_attr_vals: np.array, attr_name_idx_map: Mapping[str, int], attr_val_cast_map: Mapping[str, Any] = None) -> Any: # TODO THIS VALIDATION BREAKS BECAUSE numpy.array IS A FUNCTION NOT A TYPE # validate_type('note_attr_vals', note_attr_vals, np.array) validate_type('attr_name_idx_map', attr_name_idx_map, Mapping) validate_sequence_of_type('attr_name_idx_map', attr_name_idx_map.keys(), str) validate_optional_type('attr_val_cast_map', attr_val_cast_map, Mapping) if attr_val_cast_map: validate_optional_sequence_of_type('attr_val_cast_map', attr_val_cast_map.keys(), str) cls = _make_cls(attr_name_idx_map) note = cls() # Assign core attributes note.note_attr_vals = note_attr_vals note.attr_name_idx_map = attr_name_idx_map # Set string formatters for note attributes, this is specific to CSound per the comments note.set_attr_str_formatter('instrument', lambda x: str(x)) note.set_attr_str_formatter('start', lambda x: f'{x:.5f}') note.set_attr_str_formatter('duration', lambda x: f'{x:.5f}') note.set_attr_str_formatter('amplitude', lambda x: str(x)) # Handle case that pitch is a float and will have rounding but that sometimes we want # to use it to represent fixed pitches in Western scale, e.g. 4.01 == Middle C, and other times # we want to use to represent arbitrary floats in Hz. The former case requires .2f precision, # and for the latter case we default to .5f precision but allow any precision. # This is DEFAULT_PITCH_PRECISION to start with. User can call setter to update the value. note.set_attr_str_formatter('pitch', pitch_to_str(note.pitch_precision)) # Set mapping of attribute names to functions that cast return type of get() calls, e.g. cast instrument to int note.attr_val_cast_map = attr_val_cast_map or {} for attr_name in note.attr_name_idx_map: if attr_name not in note.attr_val_cast_map: note.attr_val_cast_map[attr_name] = lambda x: x # Instrument is always returned as an int note.attr_val_cast_map['instrument'] = int return note
def insert(self, index: int, to_add: Union[List[Track], Track]) -> 'Song': validate_type('index', index, int) try: validate_type('to_add', to_add, Track) self.track_list.insert(index, to_add) if to_add.name: self.track_map[to_add.name] = to_add return self except ValueError: pass validate_sequence_of_type('to_add', to_add, Track) for track in to_add: self.track_list.insert(index, track) # noinspection PyUnresolvedReferences if track.name: # noinspection PyUnresolvedReferences self.track_map[track.name] = track index += 1 return self
def set_notes_pitches_to_mingus_keys(mingus_keys: Sequence[str], mingus_key_to_key_enum_mapping: Mapping, notes: NoteSequence, pitch_for_key: Any, octave: int, validate=True): if validate: validate_sequence_of_type('mingus_key_list', mingus_keys, str) validate_types(('mingus_key_to_key_enum_mapping', mingus_key_to_key_enum_mapping, Dict), ('notes', notes, NoteSequence), ('octave', octave, int)) if len(mingus_keys) != len(notes): raise ValueError(( 'mingus_keys and notes must have same length. ' f'len(mingus_keys): {len(mingus_keys)} len(notes): {len(notes)}' )) for i, mingus_key in enumerate(mingus_keys): set_note_pitch_to_mingus_key(mingus_key, mingus_key_to_key_enum_mapping, notes[i], pitch_for_key, octave, validate=False)
def __init__(self, num_notes: int = None, child_sequences: Sequence['NoteSequence'] = None, mn: MakeNoteConfig = None): validate_types(('num_notes', num_notes, int), ('num_attributes', mn.num_attributes, int), ('attr_name_idx_map', mn.attr_name_idx_map, dict)) validate_optional_type('attr_val_default_map', mn.attr_val_default_map, dict) validate_sequence_of_type('attr_name_idx_map', mn.attr_name_idx_map.keys(), str) validate_sequence_of_type('attr_name_idx_map', mn.attr_name_idx_map.values(), int) if mn.attr_val_default_map: validate_sequence_of_type('attr_vals_map', list(mn.attr_val_default_map.keys()), str) validate_sequence_of_type_choice('attr_vals_map', list(mn.attr_val_default_map.values()), (float, int)) validate_optional_type_choice('child_sequences', child_sequences, (list, set)) validate_optional_sequence_of_type('child_sequences', child_sequences, NoteSequence) self.mn = mn # Construct empty 2D numpy array of the specified dimensions. Each row stores a Note's values. rows = [[0.0] * self.mn.num_attributes for _ in range(num_notes)] self.note_attr_vals = np_array(rows) if num_notes > 0: # THIS MUST NOT BE ALTERED self._num_attributes = self.note_attr_vals.shape[1] if self.mn.attr_val_default_map: assert set(self.mn.attr_val_default_map.keys()) <= set(self.mn.attr_name_idx_map.keys()) for note_attr in self.note_attr_vals: for attr_name, attr_val in self.mn.attr_val_default_map.items(): note_attr[self.mn.attr_name_idx_map[attr_name]] = attr_val self.child_sequences = child_sequences or [] # Absolute index position over all sequences, that is self.note_attr_vals and the note_attr_vals of each # child_sequence, and, recursively, any of its child sequences. # So if this sequence has 10 notes and it has one child sequence with 11 notes then self.index # will move from 0 to 20 and then reset to 0. self.index = 0 self.range_map = {0: self}
def add_score_events(self, events: Sequence[CSoundScoreEvent]): validate_sequence_of_type('events', events, CSoundScoreEvent) for event in events: self.add_score_event(event)
def remove(self, to_remove: Tuple[int, int]) -> 'Track': assert len(to_remove) == 2 validate_sequence_of_type('to_remove', to_remove, int) del self.measure_list[to_remove[0]:to_remove[1]] return self
def remove(self, to_remove: Tuple[int, int]) -> 'NoteSequenceSequence': validate_type('to_remove', to_remove, Tuple) validate_sequence_of_type('to_remove', to_remove, int) del self.note_seq_seq[to_remove[0]:to_remove[1]] return self
def extend(self, seqs: Sequence[NoteSequence]) -> 'NoteSequenceSequence': validate_sequence_of_type('seqs', seqs, NoteSequence) self.note_seq_seq.extend(seqs) return self