Пример #1
0
    def __init__(self,
                 beats_per_measure: int = None,
                 beat_note_dur: NoteDur = None,
                 tempo: int = None,
                 quantizing: bool = True):
        validate_optional_types(('beats_per_measure', beats_per_measure, int),
                                ('beat_dur', beat_note_dur, NoteDur),
                                ('tempo', tempo, int),
                                ('quantizing', quantizing, bool))

        self.quantizing = quantizing

        # Meter notation
        # Numerator of meter
        self.beats_per_measure = beats_per_measure
        # Inverse of denominator of meter, e.g. 4/4 is quarter note is 1 beat
        self.beat_note_dur = beat_note_dur
        # Meter in musical notation as a tuple, e.g. (4, 4)
        # noinspection PyTypeChecker
        self.meter_notation = (self.beats_per_measure,
                               int(1 / self.beat_note_dur.value))

        # Each note is some fraction of a quarter note. So for N / 4 meters, this ratio is 1.
        # For N / 8 meters, e.g. 6 / 8, this ration os 0.5. This ratio multiplied by the actual time duration
        # of a quarter note, derived from the tempo in qpm, is the duration of a note
        # noinspection PyTypeChecker
        self.quarter_notes_per_beat_note = int(self.beat_note_dur.value /
                                               Meter.QUARTER_NOTE_DUR)

        # Actual note duration
        # Map note durations from meter, which are unitless, to time, using tempo, which is a ratio of
        # quarter-note beats to time. qpm == quarter notes per minute
        self._set_tempo_attributes(tempo)
Пример #2
0
    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
Пример #3
0
    def __init__(self,
                 meter: Meter = None,
                 swing: Swing = None,
                 num_notes: int = None,
                 mn: MakeNoteConfig = None,
                 performance_attrs: PerformanceAttrs = None):
        validate_optional_types(
            ('meter', meter, Meter), ('swing', swing, Swing),
            ('performance_attrs', performance_attrs, PerformanceAttrs))
        super(Measure, self).__init__(num_notes=num_notes, mn=mn)

        # TODO Enforce duration of meter bpm and tempo and add unit test coverage, currently the onus is on
        #  the caller to put correct duration in note_config, as that is what is used to create notes, ignoring tempo

        # Maintain the invariant that notes are sorted ascending by start
        self._sort_notes_by_start_time()

        self.meter = meter or copy(Measure.DEFAULT_METER)
        self.swing = swing
        self.num_notes = num_notes or 0
        self.performance_attrs = performance_attrs

        # Support adding notes based on Meter
        self.beat = 0
        # Support adding notes offset from end of previous note
        self.next_note_start = 0.0
        self.max_duration = self.meter.beats_per_measure * self.meter.beat_note_dur_secs
Пример #4
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 = {}
Пример #5
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()
Пример #6
0
    def __init__(self,
                 measure_list: List[Measure],
                 meter: Optional[Meter] = None,
                 swing: Optional[Swing] = None,
                 name: str = None,
                 performance_attrs: Optional[PerformanceAttrs] = None):
        validate_optional_types(('measure_list', measure_list, List),
                                ('performance_attrs', performance_attrs, PerformanceAttrs),
                                ('meter', meter, Meter), ('swing', swing, Swing),
                                ('name', name, str))
        validate_optional_sequence_of_type('measure_list', measure_list, Measure)

        measure_list = measure_list or []
        super(Section, self).__init__(measure_list)
        # TODO REFACTOR NAME TO 'measures'
        self.measure_list = self.note_seq_seq

        self.name = name
        self._performance_attrs = performance_attrs
        self._meter = meter
        if meter:
            for measure in self.measure_list:
                measure.meter = meter
        self._swing = swing
        if swing:
            for measure in self.measure_list:
                measure.swing = swing
        self.index = 0

        if self._performance_attrs:
            for measure in self.measure_list:
                measure.performance_attrs = self._performance_attrs
Пример #7
0
 def __init__(self,
              song: Optional[Song] = None,
              append_mode: MidiPlayerAppendMode = None,
              midi_file_path: Path = None):
     validate_type('append_mode', append_mode, MidiPlayerAppendMode)
     validate_optional_types(('song', song, Song),
                             ('midi_file_path', midi_file_path, Path))
     self._song = song
     self.midi_file_path = midi_file_path
     # Type 1 - multiple synchronous tracks, all starting at the same time
     # https://mido.readthedocs.io/en/latest/midi_files.html
     self.midi_file = MidiFile(type=1)
     super(MidiWriter, self).__init__(song=song)
Пример #8
0
    def __init__(self,
                 csound_orchestra: Optional[CSoundOrchestra] = None,
                 csound_score: Optional[CSoundScore] = None,
                 song: Optional[Song] = None):
        validate_optional_types(
            ('csound_orchestra', csound_orchestra, CSoundOrchestra),
            ('csound_score', csound_score, CSoundScore), ('song', song, Song))
        super(CSoundCSDPlayer, self).__init__()

        self.orchestra = csound_orchestra
        if csound_orchestra and csound_score:
            self._csd = CSD(csound_orchestra, csound_score)

        self._song = song
Пример #9
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
Пример #10
0
    def __init__(self, to_add: Optional[Union[List[Measure], Section]] = None,
                 meter: Optional[Meter] = None,
                 swing: Optional[Swing] = None,
                 name: str = None,
                 instrument: Optional[Union[float, int]] = None,
                 performance_attrs: Optional[PerformanceAttrs] = None):
        validate_optional_types(('meter', meter, Meter),
                                ('swing', swing, Swing),
                                ('performance_attrs', performance_attrs, PerformanceAttrs))
        validate_optional_type_choice('instrument', instrument, (float, int))

        # Get the measure_list from either List[Measure] or Section
        self._section_map: Dict[str, Section] = {}
        measure_list = []
        if to_add:
            try:
                validate_optional_sequence_of_type('to_add', to_add, Measure)
                measure_list = to_add
            except ValueError:
                pass
            if not measure_list:
                validate_optional_type('to_add', to_add, Section)
                measure_list = to_add.measure_list
                if to_add.name:
                    self._section_map[to_add.name] = to_add
        super(Track, self).__init__(measure_list=measure_list,
                                    meter=meter,
                                    swing=swing,
                                    name=name,
                                    performance_attrs=performance_attrs)

        self.name = name
        self._instrument = instrument
        self.index = 0

        # Set the instrument stored at the Track level. Also if an `instrument` was passed in,
        # modify all Measures, which will in turn modify all of their Notes
        if instrument:
            for measure in measure_list:
                measure.set_attr('instrument', instrument)
            self.instrument = instrument
        else:
            self.instrument = Track.DEFAULT_INSTRUMENT
Пример #11
0
    def calculate_swing_adjust(self,
                               swing_direction: SwingDirection = None,
                               swing_jitter_type: SwingJitterType = None):
        validate_optional_types(
            ('swing_direction', swing_direction, Swing.SwingDirection),
            ('swing_jitter_type', swing_jitter_type, Swing.SwingJitterType))
        swing_direction = swing_direction or self.swing_direction
        swing_jitter_type = swing_jitter_type or self.swing_jitter_type

        swing_adjust = self.swing_range
        if swing_jitter_type == Swing.SwingJitterType.Random:
            swing_adjust *= random()

        if swing_direction == Swing.SwingDirection.Forward:
            return swing_adjust
        elif swing_direction == Swing.SwingDirection.Reverse:
            return -swing_adjust
        elif swing_direction == Swing.SwingDirection.Both:
            return sign() * swing_adjust
Пример #12
0
    def __init__(self,
                 swing_on: bool = None,
                 swing_range: float = None,
                 swing_direction: SwingDirection = None,
                 swing_jitter_type: SwingJitterType = None):
        validate_optional_types(
            ('swing_on', swing_on, bool), ('swing_range', swing_range, float),
            ('swing_direction', swing_direction, Swing.SwingDirection),
            ('swing_jitter_type', swing_jitter_type, Swing.SwingJitterType))

        if swing_on is None:
            swing_on = Swing.DEFAULT_SWING_ON
        self.swing_on = swing_on
        if swing_range is None:
            self.swing_range = Swing.DEFAULT_SWING_RANGE
        else:
            self.swing_range = swing_range
        self.swing_direction = swing_direction or Swing.DEFAULT_SWING_DIRECTION
        self.swing_jitter_type = swing_jitter_type or Swing.DEFAULT_SWING_JITTER_TYPE
Пример #13
0
    def __init__(self,
                 to_add: Optional[Union[List[Track], Track]] = None,
                 name: str = None,
                 meter: Optional[Meter] = None,
                 swing: Optional[Swing] = None,
                 performance_attrs: Optional[PerformanceAttrs] = None):
        validate_optional_types(
            ('meter', meter, Meter), ('swing', swing, Swing),
            ('performance_attrs', performance_attrs, PerformanceAttrs))
        self.name = name
        self.track_map = {}
        self.index = 0

        track_list = []
        if to_add:
            try:
                validate_optional_sequence_of_type('to_add', to_add, Track)
                track_list = to_add
            except ValueError:
                pass
            if not track_list:
                validate_optional_type('to_add', to_add, Track)
                track_list = [to_add]
        self.track_list = track_list
        for track in self.track_list:
            if track.name:
                self.track_map[track.name] = track

        self._meter = meter
        if meter:
            for track in self.track_list:
                track._meter = meter
        self._swing = swing
        if swing:
            for track in self.track_list:
                track._swing = swing
        self._performance_attrs = performance_attrs
        if performance_attrs:
            for track in self.track_list:
                track._performance_attrs = performance_attrs