Exemplo n.º 1
0
    def __init__(self,
                 harmonic_chord: Any = None,
                 octave: int = None,
                 key: Union[MajorKey, MinorKey] = None,
                 mn: MakeNoteConfig = None):
        validate_types(('harmonic_chord', harmonic_chord, HarmonicChord), ('octave', octave, int),
                       ('mn', mn, MakeNoteConfig))
        validate_type_choice('key', key, (MajorKey, MinorKey))

        self.matched_key_type = Chord.get_key_type(key, harmonic_chord)
        # Assign attrs before completing validation because it's more convenient to check for required
        # attrs by using getattr(attr_name) after they have been assigned
        self.harmonic_chord = harmonic_chord
        self.octave = octave
        self.pitch_for_key = mn.pitch_for_key
        self.num_attributes = mn.num_attributes
        # Get the list of keys in the chord as string names from mingus
        self.key = key
        self.mingus_chord = Chord.get_mingus_chord_for_harmonic_chord(key, harmonic_chord)
        # Construct the sequence of notes for the chord in the NoteSequence base class
        super(Chord, self).__init__(num_notes=len(self.mingus_chord),
                                    mn=mn)

        # Convert to Notes for this chord's note_type with pitch assigned for the key in the chord
        self._mingus_key_to_key_enum_mapping = Scale.get_mingus_key_to_key_enum_mapping(self.matched_key_type)
        set_notes_pitches_to_mingus_keys(self.mingus_chord,
                                         self._mingus_key_to_key_enum_mapping,
                                         self,
                                         self.pitch_for_key,
                                         self.octave,
                                         validate=False)
Exemplo n.º 2
0
    def add_notes_on_start(self, to_add: NoteSequence) -> 'Measure':
        """Uses note as a template and makes copies of it to fill the measure. Each new note's start time is set
           to that of the previous notes start + duration.
           Validates that all the durations fit in the total duration of the measure.

           NOTE: This *replaces* all notes in the Measure with this sequence of notes on the beat
        """
        validate_types(('to_add', to_add, NoteSequence))

        # TODO DO THIS IN NUMPY NATIVE WAY
        sum_of_durations = sum(
            self._get_duration_for_tempo(note) for note in to_add)
        if self.next_note_start + sum_of_durations > self.meter.measure_dur_secs:
            raise ValueError(
                (f'measure.next_note_start {self.next_note_start} + '
                 f'sum of note.durations {sum_of_durations} > '
                 f'measure.max_duration {self.max_duration}'))

        for note in to_add:
            note.duration = self._get_duration_for_tempo(note)
            note.start = self.next_note_start
            self.next_note_start += note.duration
            super(Measure, self).append(note)
        self._sort_notes_by_start_time()

        return self
Exemplo n.º 3
0
    def __init__(self,
                 name: Optional[str] = None,
                 num_measures: int = None,
                 meter: Optional[Meter] = None,
                 swing: Optional[Swing] = None,
                 player: Optional[Union[Player, Writer]] = None,
                 arpeggiator_chord: Optional[Chord] = None,
                 mn: MakeNoteConfig = None):
        validate_types(('num_measures', num_measures, int), ('mn', mn, MakeNoteConfig))
        validate_optional_types(('name', name, str),
                                ('swing', swing, Swing),
                                ('arpeggiator_chord', arpeggiator_chord, Chord))
        validate_optional_type_choice('player', player, (Player, Writer))

        # Sequencer wraps song but starts with no Tracks. It provides an alternate API for generating and adding Tracks.
        to_add = []
        meter = meter or Sequencer.DEFAULT_METER
        super(Sequencer, self).__init__(to_add, name=name, meter=meter, swing=swing)

        self.player = player
        if self.player:
            self.player.song = self
        self.arpeggiator_chord = arpeggiator_chord or Sequencer.DEFAULT_ARPEGGIATOR_CHORD
        self.mn = mn

        self.num_measures = num_measures or Sequencer.DEFAULT_NUM_MEASURES
        self.default_note_duration: float = self.meter.beat_note_dur.value

        self.num_tracks = 0
        # Internal index to the next track to create when add_track() or add_pattern_as_track() are called
        self._next_track = 0
        self._track_name_idx_map = {}
        self._track_name_player_map = {}
Exemplo n.º 4
0
    def set_track_pattern(self,
                          track_name: str = None,
                          pattern: str = None,
                          instrument: Optional[Union[float, int]] = None,
                          swing: Optional[Swing] = None,
                          arpeggiate: bool = False,
                          arpeggiator_chord: Optional[HarmonicChord] = None):
        """
        - Sets the pattern, a section of measures in the track named `track_name`.
        - If the track already has a pattern, it is replaced. If the track is empty, its pattern is set to `pattern`.
        - If `swing` is arg is supplied, then the track will have swing applied using it
        - If the `instrument` arg is supplied, this instrument will be bound to the track, replacing whatever
          instrument it previously was bound to
        - If the `apply_swing` arg is True and the class has `self.swing` then the class swing
          object will be used to apply swing
        """
        validate_types(('track_name', track_name, str), ('pattern', pattern, str))
        validate_optional_types(('arpeggiate', arpeggiate, bool),
                                ('arpeggiator_chord', arpeggiator_chord, HarmonicChord), ('swing', swing, Swing))
        validate_optional_type_choice('instrument', instrument, (float, int))

        # Will raise if track_name is not valid
        track = self.track_list[self._track_name_idx_map[track_name]]
        if track.measure_list:
            track.remove((0, self.num_measures))
        instrument = instrument or track.instrument
        section = self._parse_pattern_to_section(pattern=pattern, instrument=instrument,
                                                 arpeggiate=arpeggiate, arpeggiator_chord=arpeggiator_chord)
        if len(section) < self.num_measures:
            self._fill_section_to_track_length(section)
        track.extend(to_add=section)
        swing = swing or self.swing
        if swing and swing.is_swing_on():
            track.apply_swing()
 def __setitem__(self, index: int, note_sequence: NoteSequence) -> None:
     validate_types(('index', index, int),
                    ('note_sequence', note_sequence, NoteSequence))
     if abs(index) >= len(self.note_seq_seq):
         raise IndexError(
             f'`index` out of range index: {index} len(note_seq_seq): {len(self.note_seq_seq)}'
         )
     self.note_seq_seq[index] = NoteSequence.copy(note_sequence)
Exemplo n.º 6
0
 def __init__(self, note: Any, measure: Measure, event_type: MidiEventType):
     validate_types(('measure', measure, Measure),
                    ('event_type', event_type, MidiEventType))
     self.note = note
     self.measure = measure
     self.event_type = event_type
     self.event_time = abs(self._event_time())
     self.tick = self._tick()
     self.tick_delta = 0
Exemplo n.º 7
0
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
    ]
Exemplo n.º 8
0
def set_note_pitch_to_mingus_key(mingus_key: str,
                                 mingus_key_to_key_enum_mapping: Mapping,
                                 note: Any,
                                 pitch_for_key: Any,
                                 octave: int,
                                 validate=True):
    if validate:
        validate_types(('mingus_key', mingus_key, str),
                       ('mingus_key_to_key_enum_mapping',
                        mingus_key_to_key_enum_mapping, Mapping),
                       ('octave', octave, int))
    key = mingus_key_to_key_enum_mapping[mingus_key.upper()]
    note.pitch = pitch_for_key(key, octave=octave)
Exemplo n.º 9
0
    def add_notes_on_beat(self, to_add: NoteSequence) -> 'Measure':
        """Uses note as a template and makes copies of it to fill the measure. Each new note's start time is set
           to that beat start time.

           NOTE: This *replaces* all notes in the Measure with this sequence of notes on the beat
        """
        validate_types(('to_add', to_add, NoteSequence))
        if len(to_add) > self.meter.beats_per_measure:
            raise ValueError(
                f'Sequence `to_add` must have a number of notes <= to the number of beats per measure'
            )

        # Now iterate the beats per measure and assign each note in note_list to the next start time on the beat
        for i, beat_start_time in enumerate(self.meter.beat_start_times_secs):
            # There might be fewer notes being added than beats per measure
            if i == len(to_add):
                break
            to_add[i].start = beat_start_time

        self.extend(to_add)

        return self
Exemplo n.º 10
0
    def __init__(self,
                 key: Union[MajorKey, MinorKey] = None,
                 octave: int = None,
                 harmonic_scale: HarmonicScale = None,
                 mn: MakeNoteConfig = None):
        validate_types(('octave', octave, int),
                       ('harmonic_scale', harmonic_scale, HarmonicScale),
                       ('mn', mn, MakeNoteConfig))
        # Use return value to detect which type of enum `key` is. Use this to determine which KEY_MAPPING
        # to use to convert the mingus key value (a string) to the enum key value (a member of MajorKey or MinorKey)
        _, matched_key_type = validate_type_reference_choice('key', key, (MajorKey, MinorKey))
        self.is_major_key = matched_key_type is MajorKey
        self.is_minor_key = matched_key_type is MinorKey

        self.key = key
        self.octave = octave
        self.harmonic_scale = harmonic_scale

        # Get the mingus keys (pitches) for the musical scale (`scale_type`) with its root at `key`
        str_key_dict = MAJOR_KEY_DICT if self.is_major_key else MINOR_KEY_DICT
        mingus_keys = harmonic_scale.value(list(str_key_dict.keys())[0]).ascending()
        # Trim the last element because mingus returns the first note in the next octave along with all the
        # notes in the scale of the octave requested. This behavior is observed and not exhaustively tested
        # so check and only remove if the first and last note returned are the same.
        if mingus_keys[0] == mingus_keys[-1]:
            mingus_keys = mingus_keys[:-1]
        mingus_key_to_key_enum_mapping = Scale.KEY_MAPS[matched_key_type.__name__]
        self.keys = [mingus_key_to_key_enum_mapping[mingus_key.upper()] for mingus_key in mingus_keys]

        # Construct the sequence of notes for the chord in the NoteSequence base class
        super(Scale, self).__init__(num_notes=len(mingus_keys), mn=mn)

        set_notes_pitches_to_mingus_keys(mingus_keys,
                                         mingus_key_to_key_enum_mapping,
                                         self,
                                         mn.pitch_for_key,
                                         self.octave,
                                         validate=False)
Exemplo n.º 11
0
    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}
Exemplo n.º 12
0
    def __init__(self,
                 song: Song = None,
                 out_file_path: Path = None,
                 score_file_path: Path = None,
                 orchestra_file_path: Path = None,
                 csound_path: Path = None,
                 verbose: bool = False):
        validate_types(('song', song, Song),
                       ('out_file_path', out_file_path, Path),
                       ('score_file_path', score_file_path, Path),
                       ('orchestra_file_path', orchestra_file_path, Path),
                       ('verbose', verbose, bool))
        validate_optional_type('csound_path', csound_path, Path)
        super(CSoundWriter, self).__init__()

        self._song = song
        self.out_file_path = out_file_path
        self.score_file_path = score_file_path
        self.orchestra_file_path = orchestra_file_path
        self._score_file_lines = []
        self.csound_path = csound_path or CSoundWriter.CSOUND_OSX_PATH
        self.verbose = verbose
        self._include_file_names = []
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
 def insert(self, index: int,
            to_add: NoteSequence) -> 'NoteSequenceSequence':
     validate_types(('index', index, int), ('to_add', to_add, NoteSequence))
     self.note_seq_seq.insert(index, to_add)
     return self
Exemplo n.º 15
0
 def add_player_for_track(self,
                          track_name: str = None,
                          player: Player = None):
     """Adds a Player specific to a track, which overrides self.player if present"""
     validate_types(('track_name', track_name, str), ('player', player, Player))
     self._track_name_player_map[track_name] = player
Exemplo n.º 16
0
 def set_tempo_for_track(self, track_name: str = None, tempo: int = None):
     validate_types(('track_name', track_name, str), ('tempo', tempo, int))
     # Make a new meter object set to the new tempo and assign it to the sequencer
     track = self.track_list[self._track_name_idx_map[track_name]]
     track.tempo = tempo