Ejemplo n.º 1
0
 def copy(source_track: 'Track') -> 'Track':
     measure_list = None
     if source_track.measure_list:
         # noinspection PyTypeChecker
         measure_list = [Measure.copy(measure) for measure in source_track.measure_list]
     return Track(to_add=measure_list,
                  name=source_track.name,
                  instrument=source_track.instrument,
                  meter=source_track._meter,
                  swing=source_track._swing,
                  performance_attrs=source_track._performance_attrs)
Ejemplo n.º 2
0
def _measure(mn=None,
             meter=None,
             swing=None,
             num_notes=None,
             attr_val_default_map=None):
    num_notes = num_notes or NUM_NOTES
    mn.attr_val_default_map = attr_val_default_map or ATTR_VAL_DEFAULT_MAP
    measure = Measure(meter=meter, swing=swing, num_notes=num_notes, mn=mn)
    if len(measure) == 4:
        measure[1].start += DUR
        measure[2].start += (DUR * 2)
        measure[3].start += (DUR * 3)
    return measure
Ejemplo n.º 3
0
def _generate_tracks_and_layout(num_tracks, measures_per_track, meter):
    note_config, scale = _get_note_config_and_scale(meter)

    for track_idx in range(num_tracks):
        track = MidiTrack(meter=meter, channel=track_idx, instrument=INSTRUMENT)
        TRACKS.append(track)

        CHANNELS.append([])
        LAYOUT.append([])
        layout_measures_row = []

        mingus_keys = Chord.get_mingus_chord_for_harmonic_chord(key=KEY_PITCH, harmonic_chord=HarmonicChord.MajorTriad)
        mingus_key_to_enum_mapping = Scale.get_mingus_key_to_key_enum_mapping(KEY)

        chord_label = \
            f'{KEY_PITCH.name}.{HARMONIC_CHORD.name}:.{"-".join(mingus_key_to_enum_mapping[mingus_key].name for mingus_key in mingus_keys)}'
        chord_pitches = '_'.join(str(pitch) for pitch in
                                 get_chord_pitches(mingus_keys=mingus_keys,
                                                   mingus_key_to_key_enum_mapping=mingus_key_to_enum_mapping,
                                                   pitch_for_key=pitch_for_key,
                                                   octave=OCTAVE))

        for measure_idx in range(measures_per_track):
            measure = Measure(meter=meter, num_notes=meter.beats_per_measure, mn=note_config)
            track.append(measure)

            layout_notes = []
            for k in range(meter.beats_per_measure):
                # Initialize each note to the params of the root note in the Scale
                set_attr_vals_from_dict(measure[k], as_dict(scale[0]))

                # PySimpleGUI refers to UI objects by "key" and returns this key when events are trapped on the UI.
                # Prepend key with track num, this is the channel for the queue of messages from the UI to this track.
                # Key each button to it's index in the flattened Messages list, key * 2 because the
                # index into Messages is even indexes, because Messages are note_on/note_off pairs.
                # NOTE: 'key' is overloaded and here means unique id referring to PySimpleGUI object.
                # key must be unique. For note on_off the value is unique on each iteration so don't need key
                event_id = f'{track_idx}_{2 * ((measure_idx * meter.beats_per_measure) + k)}'
                start_key = f'note_on_off|{event_id}|{event_id}'
                chord_key = f'chord|{event_id}|{str(chord_pitches)}'

                note_checkbox = sg.Checkbox(str (k + 1), default=False, enable_events=True, key=start_key)
                layout_notes.append(note_checkbox)
                NOTE_ELEMENTS[track_idx].append(note_checkbox)
                # chord_key needs a unique key id and a value, because values (i.e. which chord a note is) can repeat
                layout_notes.append(sg.DropDown(values=str(chord_label), key=chord_key,
                                                enable_events=True, size=(15, 15)))
            layout_measures_row.append(sg.Frame(title=f'Measure {measure_idx + 1}', layout=[layout_notes]))
        LAYOUT[track_idx].append(sg.Frame(title=f'Track {track_idx + 1}', layout=[layout_measures_row]))
Ejemplo n.º 4
0
def test_getitem_insert_remove(make_note_config, section, measure, meter,
                               swing):
    expected_len = len(_measure_list(make_note_config, meter, swing))
    assert len(section) == expected_len
    old_first_measure = section[0]
    old_first_note = old_first_measure[0]
    old_first_note_amplitude = old_first_note.amplitude
    insert_measure = Measure.copy(measure)
    expected_new_first_note_amplitude = old_first_note_amplitude + 1
    insert_measure[0].amplitude = expected_new_first_note_amplitude
    section.insert(0, insert_measure)
    assert len(section) == expected_len + 1
    new_first_measure = section[0]
    new_first_note = new_first_measure[0]
    assert new_first_note.amplitude == expected_new_first_note_amplitude
    assert new_first_note.amplitude != old_first_note_amplitude
    section.remove((0, 1))
    assert len(section) == expected_len
    old_first_measure = section[0]
    old_first_note = old_first_measure[0]
    assert old_first_note.amplitude == old_first_note_amplitude
Ejemplo n.º 5
0
;	- str1, str2, str3 ... where str is a fixed harmonic partial
;		- the value of str# is the relative strength of the partial in the final mixed timbre
;		- partials to be skipped are given value 0
;
; Func # 	Loadtm 	TblSize GEN   Parameters ...
; First partial variations
f 1		    0		    8193		10		1'''
SCORE_HEADER_LINES = [SCORE_HEADER]

if __name__ == '__main__':
    meter = Meter(beats_per_measure=BEATS_PER_MEASURE,
                  beat_note_dur=BEAT_DUR,
                  tempo=TEMPO_QPM)
    swing = Swing(swing_range=SWING_RANGE)
    measure = Measure(num_notes=NUM_NOTES,
                      meter=meter,
                      swing=swing,
                      mn=DEFAULT_NOTE_CONFIG())
    for i in range(NUM_NOTES):
        measure[i].instrument = INSTRUMENT_1_ID
        measure[i].start = (i % NUM_NOTES) * DUR
        measure[i].duration = DUR
        measure[i].amplitude = BASE_AMP
        measure[i].pitch = PITCH
    measure.apply_swing()
    track = Track(to_add=[measure],
                  name='ostinato',
                  instrument=INSTRUMENT_1_ID)
    song = Song(to_add=[track], name=SONG_NAME)

    orchestra = CSoundOrchestra(instruments=INSTRUMENTS,
                                sampling_rate=SR,
Ejemplo n.º 6
0
    def _parse_pattern_to_section(self,
                                  pattern: str = None,
                                  instrument: Union[float, int] = None,
                                  swing: Swing = None,
                                  arpeggiate: bool = False,
                                  arpeggiator_chord: Optional[HarmonicChord] = None) -> Section:
        section = Section([])
        swing = swing or self.swing

        def _make_note_vals(_instrument, _start, _duration, _amplitude, _pitch):
            _note_vals = NoteValues(self.mn.attr_name_idx_map.keys())
            _note_vals.instrument = _instrument
            _note_vals.start = _start
            _note_vals.duration = _duration
            _note_vals.amplitude = _amplitude
            _note_vals.pitch = _pitch
            return _note_vals

        measure_tokens = [t.strip() for t in pattern.split(Sequencer.MEASURE_TOKEN_DELIMITER)]
        for measure_token in measure_tokens:
            note_tokens = [t.strip() for t in measure_token.split()]
            next_start = 0.0
            duration = self.default_note_duration
            # Sum up the duration of all note positions to validate that the notes fit in the measure. We look at
            # "note positions" because for chords we only count the duration of all the notes in the chord once,
            # because they sound simultaneously so that duration only contributes to the total duration once.
            measure_duration = 0.0
            note_vals_lst = []
            for i, note_token in enumerate(note_tokens):
                start = self.mn.attr_val_cast_map['start'](next_start)

                # It's a rest note
                if note_token == Sequencer.REST_TOKEN:
                    # Dummy values
                    amplitude = self.mn.attr_val_cast_map['amplitude'](0)
                    pitch = self.mn.attr_val_cast_map['pitch'](1)
                    note_vals = _make_note_vals(instrument, start, duration, amplitude, pitch)
                    note_vals_lst.append(note_vals)
                    measure_duration += duration
                # It's a sounding note or chord, parse the pattern and collect the note/chord parameters
                else:
                    key, octave, chord, amplitude, duration = note_token.split(Sequencer.NOTE_TOKEN_DELIMITER)
                    # Only major or minor notes supported
                    key = MAJOR_KEY_DICT.get(key) or MINOR_KEY_DICT.get(key)
                    if not key:
                        raise InvalidPatternException(f'Pattern \'{pattern}\' has invalid key {key} token')
                    octave = int(octave)
                    amplitude = self.mn.attr_val_cast_map['amplitude'](amplitude)
                    # If no duration provided we already assigned default note duration (quarter note)
                    if duration:
                        duration = float(duration)

                    # It's a chord. `arpeggiate=True` is ignored.
                    if chord:
                        # Chord can be empty, but if there is a token it must be valid
                        harmonic_chord = HARMONIC_CHORD_DICT.get(chord)
                        if not harmonic_chord:
                            raise InvalidPatternException(f'Pattern \'{pattern}\' has invalid chord {chord} token')
                        chord_sequence = Chord(harmonic_chord=harmonic_chord, octave=octave, key=key, mn=self.mn)
                        for note in chord_sequence:
                            note_vals_lst.append(_make_note_vals(instrument, start, duration, amplitude, note.pitch))
                        # Only count duration of the chord once in the total for the measure
                        measure_duration += duration
                    # It's a single sounding note. Converted into arpeggiated chords if `arpeggiate=True`.
                    else:
                        pitch = self.mn.pitch_for_key(key, octave)

                        if not arpeggiate:
                            note_vals = _make_note_vals(instrument, start, duration, amplitude, pitch)
                            note_vals_lst.append(note_vals)
                        else:
                            harmonic_chord = arpeggiator_chord or self.arpeggiator_chord
                            chord_sequence = Chord(harmonic_chord=harmonic_chord, octave=octave, key=key, mn=self.mn)
                            arpeggiation_offset = duration / len(chord_sequence)
                            chord_sequence.mod_ostinato(init_start_time=start, start_time_interval=arpeggiation_offset)
                            # Duration of arpeggiated notes are fit into the duration of the notated note
                            arpeggiated_note_duration = duration / len(chord_sequence)
                            for note in chord_sequence:
                                note_vals_lst.append(_make_note_vals(
                                        instrument, note.start, arpeggiated_note_duration, amplitude, note.pitch))

                        measure_duration += duration
                next_start += duration

            measure = Measure(num_notes=len(note_vals_lst),
                              meter=self.meter,
                              swing=swing,
                              mn=MakeNoteConfig.copy(self.mn))

            # TODO BETTER RULE THAN THIS FOR ARPEGGIATION
            # TODO WE SHOULD NOT NEED THIS ANYMORE BUT WE STILL DO OR TESTS FAIL ON MEASURE DURATION
            # Don't validate measure duration if we are arpeggiating, because arpeggiating on the last note will
            #  push offset start times beyond the end of the measure
            if not arpeggiate and measure_duration != \
                    pytest.approx(self.meter.beats_per_measure * self.meter.beat_note_dur.value):
                raise InvalidPatternException((f'Measure duration {measure_duration} != '
                                               f'self.meter.beats_per_measure {self.meter.beats_per_measure} * '
                                               f'self.meter_beat_note_dur {self.meter.beat_note_dur.value}'))
            for i, note_vals in enumerate(note_vals_lst):
                set_attr_vals_from_note_values(measure.note(i), note_vals)

            section.append(measure)

        return section
Ejemplo n.º 7
0
        channel=2)

    # Ostinato
    swing_factor = 0.008
    swing = Swing(swing_on=True,
                  swing_range=swing_factor,
                  swing_direction=Swing.SwingDirection.Both)

    dur = NoteDur.THIRTYSECOND
    # noinspection PyTypeChecker
    dur_val: float = dur.value
    notes_per_measure = int((1 / dur_val) * (BEATS_PER_MEASURE * BEAT_DUR_VAL))
    for _ in range(NUM_MEASURES):
        note_config = MakeNoteConfig.copy(NOTE_CONFIG)
        ostinato_measure = Measure(num_notes=notes_per_measure,
                                   meter=METER,
                                   swing=swing,
                                   mn=note_config)

        for i in range(notes_per_measure):
            note_values = NoteValues(ATTR_NAMES)
            note_values.time = i * dur_val
            note_values.duration = dur_val
            note_values.velocity = int(BASE_VELOCITY - (
                (i % notes_per_measure) / VELOCITY_FACTOR))
            note_values.pitch = SCALE[i % NUM_NOTES_IN_SCALE].pitch
            note_config.attr_val_default_map = note_values.as_dict()
            note = NoteSequence.new_note(note_config)
            ostinato_measure.append(note)
        ostinato_measure.apply_swing()
        ostinato_track.append(ostinato_measure)
Ejemplo n.º 8
0
    def copy(source: 'Section') -> 'Section':
        measure_list = None
        if source.measure_list:
            measure_list = [Measure.copy(measure) for measure in source.measure_list]

        return Section(measure_list=measure_list, performance_attrs=source._performance_attrs)