예제 #1
0
def pitch_for_key(key: Union[MajorKey, MinorKey], octave: int) -> float:
    validate_type_choice('key', key, (MajorKey, MinorKey))
    validate_type('octave', octave, int)
    if not (MIN_OCTAVE < octave < MAX_OCTAVE):
        raise ValueError((f'Arg `octave` must be in range '
                          f'{MIN_OCTAVE} <= octave <= {MAX_OCTAVE}'))
    return PITCH_MAP[key] + (float(octave) - 1.0)
예제 #2
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)
예제 #3
0
 def _setter(self, attr_val) -> None:
     if attr_name in self.attr_name_idx_map:
         validate_type('attr_name', attr_name, str)
         validate_type_choice('attr_val', attr_val, (float, int))
         self.note_attr_vals[self.attr_name_idx_map[attr_name]] = attr_val
     else:
         setattr(self, attr_name, attr_val)
예제 #4
0
    def add_track(self,
                  track_name: str = None,
                  instrument: Union[float, int] = None) -> Track:
        validate_type('track_name', track_name, str)
        validate_type_choice('instrument', instrument, (float, int))

        track_name = track_name or str(self._next_track)
        track = Track(meter=self.meter,
                      swing=self.swing, name=track_name,
                      instrument=instrument)
        self.append(track)
        self.num_tracks += 1
        self._track_name_idx_map[track_name] = self._next_track
        self._next_track += 1
        return track
예제 #5
0
    def get_secs_for_note_time(self, note_time_val: Union[float, int,
                                                          NoteDur]):
        """Helper to convert a note time in NoteDur or float that represents either a note start_time or
           note duration within a measure in the measure's meter into an absolute floating point value in
           seconds.
        """
        validate_type_choice('note_time_val', note_time_val,
                             (float, int, NoteDur))

        dur = note_time_val
        if not isinstance(note_time_val, float) \
                and not isinstance(note_time_val, int) \
                and note_time_val in NoteDur:
            dur = note_time_val.value
        # noinspection PyTypeChecker
        return self.beat_note_dur_secs * dur
예제 #6
0
    def add_pattern_as_new_track(self,
                                 track_name: Optional[str] = None,
                                 pattern: str = None,
                                 instrument: Union[float, int] = None,
                                 swing: Optional[Swing] = None,
                                 track_type: Optional[Any] = Track,
                                 arpeggiate: bool = False,
                                 arpeggiator_chord: Optional[HarmonicChord] = None) -> Track:
        """
        - Sets the pattern, a section of measures in a new track named `track_name` or if no name is provided
          in a track with a default name of its track number.
        - Track is bound to `instrument`
        - If `track_name` is not supplied, a default name of `Track N`, where N is the index of the track, is assigned
        - If `swing` is arg is supplied and `is_swing_on()` is `True`, then the track will have swing applied using it
        - If `self.swing` `is_swing_on()` is `True` then the track will have swing applied using it
        - If `track_type` is provided, this method constructs a subclass of Track. This allows callers to
          construct for example a MidiTrack, which derives from Track and adds attributes specific to MIDI.
        """
        validate_type('pattern', pattern, str)
        validate_optional_types(('track_name', track_name, str), ('swing', swing, Swing),
                                ('arpeggiate', arpeggiate, bool),
                                ('arpeggiator_chord', arpeggiator_chord, HarmonicChord))
        validate_type_choice('instrument', instrument, (float, int))
        validate_type_reference('track_type', track_type, Track)

        # Set the measures in the section to add to the track
        section = self._parse_pattern_to_section(pattern=pattern, instrument=instrument,
                                                 arpeggiate=arpeggiate, arpeggiator_chord=arpeggiator_chord)
        # If the section is shorter than num_measures, the length of all tracks, repeat it to fill the track
        if len(section) < self.num_measures:
            self._fill_section_to_track_length(section)

        # Create the track, add the section to it, apply quantize and swing according to the logic in the docstring
        track_name = track_name or str(self._next_track)
        self._track_name_idx_map[track_name] = self._next_track
        swing = swing or self.swing
        track = track_type(name=track_name, instrument=instrument, meter=self.meter, swing=swing)
        track.extend(to_add=section)
        if swing and swing.is_swing_on():
            track.apply_swing()

        # Add the track to the sequencer, update bookkeeping
        self.append(track)
        self.num_tracks += 1
        self._next_track += 1

        return track
예제 #7
0
def pitch_for_key(key: Union[MajorKey, MinorKey], octave: int) -> int:
    """MIDI pitches sequence from 21 A0 to 127, the 3 highest notes below C1 to the last note of C7.
       The algorithm is that we store the values for C1-12 as ints in the PITCH_MAP
       and thus increment by + 12 for every octave > 1, handle the special case for the 3 notes < C1 and
       validate that the (key, octave) combination is a valid MIDI pitch.
    """
    validate_type_choice('key', key, (MajorKey, MinorKey))
    validate_type('octave', octave, int)
    if not (MIN_OCTAVE < octave < MAX_OCTAVE):
        raise ValueError(
            f'Arg `octave` must be in range {MIN_OCTAVE} <= octave <= {MAX_OCTAVE}'
        )

    if octave == MIN_OCTAVE:
        # Handle edge case of only 3 notes being valid when `octave` == 0
        if key not in KEYS_IN_MIN_OCTAVE:
            raise ValueError(('If arg `octave` == 0 then `key` must be in '
                              f'{KEYS_IN_MIN_OCTAVE}'))
        return PITCH_MAP[key] - NUM_INTERVALS_IN_OCTAVE
    else:
        interval_offset = (octave - 1) * NUM_INTERVALS_IN_OCTAVE
        return PITCH_MAP[key] + interval_offset
예제 #8
0
 def get_key_type(key, harmonic_chord):
     # 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)
     # Note that this arg is not required so we may not be able to determine key type from it.
     # If we can't get key type from key, use the name of the Chord, which may be associated with
     # MinorKey. Otherwise default to MajorKey, because the mingus functions we use to return lists of string
     # names of keys return upper-case values valid for MajorKey
     matched, matched_key_type = validate_type_choice('key', key, (MajorKey, MinorKey))
     if not matched:
         if harmonic_chord is HarmonicChord and harmonic_chord.name.startswith('Minor'):
             matched_key_type = MinorKey
         else:
             matched_key_type = MajorKey
     return matched_key_type
예제 #9
0
 def get_mingus_chord_for_harmonic_chord(key: Union[MajorKey, MinorKey] = None,
                                         harmonic_chord: HarmonicChord = None) -> str:
     validate_type_choice('key', key, (MajorKey, MinorKey))
     validate_type('harmonic_chord', harmonic_chord, HarmonicChord)
     return harmonic_chord.value(key.name)