How to use the scamp.settings.engraving_settings function in scamp

To help you get started, we’ve selected a few scamp examples, based on popular ways it is used in public projects.

Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.

github MarcTheSpark / scamp / scamp / score.py View on Github external
for part in performance.parts:
            if engraving_settings.ignore_empty_parts and part.num_measures() == 0:
                # if this is an empty part, and we're not including empty parts, skip it
                continue
            staff_group = StaffGroup.from_quantized_performance_part(part)
            if len(staff_group.staves) > 1:
                contents.append(staff_group)
            elif len(staff_group.staves) == 1:
                contents.append(staff_group.staves[0])
        out = cls(
            contents,
            title=engraving_settings.get_default_title() if title == "default" else title,
            composer=engraving_settings.get_default_composer() if composer == "default" else composer,
            tempo_envelope=performance.tempo_envelope
        )
        if engraving_settings.pad_incomplete_parts:
            out._pad_incomplete_parts()
        return out
github MarcTheSpark / scamp / scamp / score.py View on Github external
def from_length_and_divisor(cls, length: float, divisor: int) -> Optional['Tuplet']:
        """
        Constructs and returns the appropriate tuplet from the length and the divisor. Returns None if no tuplet needed.

        :param length: length of the beat in quarters
        :param divisor: divisor for the beat
        :return: a Tuplet, or None
        """
        beat_length_fraction = Fraction(length).limit_denominator()

        # 1.5 / 2 can be represented as two dotted 8ths or as a duple tuplet of 8ths.
        # if we don't want duple tuplets in compound time (i.e. beat lengths of 1.5, etc.),
        # then whenever the length of the beat division is duple, we don't use a tuplet.
        if not engraving_settings.allow_duple_tuplets_in_compound_time and \
                is_x_pow_of_y((beat_length_fraction / divisor).denominator, 2):
            return None

        # We have to figure out the ratio of tuplet divisions to normal divisions and the note type associated with
        # the normal divisions. E.g. consider a beat length of 1.5 and a tuplet of 11: normal_divisions gets set
        # initially to 3 and normal type gets set to 8, since it's 3 eighth notes long
        normal_divisions = beat_length_fraction.numerator
        # (if denominator is 1, normal type is quarter note, 2 -> eighth note, etc.)
        normal_type = 4 * beat_length_fraction.denominator

        # now, we keep dividing the beat in two until we're just about to divide it into more pieces than the divisor
        # so in our example, we start with 3 8th notes, then 6 16th notes, but we don't go up to 12 32nd notes, since
        # that is more than the beat divisor of 11. Now we know that we are looking at 11 in the space of 6 16th notes.
        while normal_divisions * 2 <= divisor:
            normal_divisions *= 2
            normal_type *= 2
github MarcTheSpark / scamp / scamp / score.py View on Github external
"""
    Takes a list of points on an isochronous grid representing the start and end times of the components of a note,
        along with a list of the beat hierarchies for that grid. Returns a merged list of component start and end
        times, in which important division point are preserved and less important ones are removed.

    :param note_division_points: list of the points on the isochronous grid representing note component starts and ends
    :param beat_hierarchy_list: the result of _get_beat_division_hierarchy; a list of values for each beat in an
        isochronous grid, where 0 is the most important beat (always the downbeat), 1 is the next most important kind
        of beat, etc.
    :param is_rest: changes the settings, by default in such a way that less recombination happens
    :return: a new, better, I dare say shinier, list of note division points.
    """
    # the factor that the badness goes up from one rung of the beat hierarchy to the next.
    # A high value makes greater differentiation between important and less important beats, probably
    # leading to less recombination in favor of clearer delineation of the beat structure.
    beat_hierarchy_spacing = engraving_settings.rest_beat_hierarchy_spacing if is_rest \
        else engraving_settings.beat_hierarchy_spacing
    # ranging from 0 to 1, this penalizes using more than one component to represent a note.
    # It acts as a counterbalance to beat_hierarchy_spacing, as it encourages recombination. The balance of the two
    # parameters needs to be correct if we want to get notation that expresses the beat hierarchy, but with as few
    # tied notes as possible.
    num_divisions_penalty = engraving_settings.rest_num_divisions_penalty if is_rest \
        else engraving_settings.num_divisions_penalty

    adjusted_hierarchies = [beat_hierarchy_spacing ** x for x in beat_hierarchy_list]

    # translate time-points on the isochronous grid to durations in isochronous units after the start of the note
    component_lengths = [division - last_division
                         for last_division, division in zip(note_division_points[:-1], note_division_points[1:])]
    assert all(_is_single_note_viable_grouping(x, max_dots=engraving_settings.max_dots_allowed)
               for x in component_lengths), "Somehow we got an division of a note into un-notatable components"
    # get every possible combination of these components that keeps each component representable as a single note
github MarcTheSpark / scamp / scamp / score.py View on Github external
"""
        if not performance.is_quantized():
            raise ValueError("Performance was not quantized.")
        contents = []
        for part in performance.parts:
            if engraving_settings.ignore_empty_parts and part.num_measures() == 0:
                # if this is an empty part, and we're not including empty parts, skip it
                continue
            staff_group = StaffGroup.from_quantized_performance_part(part)
            if len(staff_group.staves) > 1:
                contents.append(staff_group)
            elif len(staff_group.staves) == 1:
                contents.append(staff_group.staves[0])
        out = cls(
            contents,
            title=engraving_settings.get_default_title() if title == "default" else title,
            composer=engraving_settings.get_default_composer() if composer == "default" else composer,
            tempo_envelope=performance.tempo_envelope
        )
        if engraving_settings.pad_incomplete_parts:
            out._pad_incomplete_parts()
        return out
github MarcTheSpark / scamp / scamp / score.py View on Github external
def _get_release_articulations(self):
        # articulations to be placed on the last note of the gliss
        return [a for a in self.properties.articulations if a not in engraving_settings.articulation_split_protocols
                or engraving_settings.articulation_split_protocols[a] in ("last", "both", "all")]
github MarcTheSpark / scamp / scamp / score.py View on Github external
def _set_clefs(self):
        if engraving_settings.clef_selection_policy == "part-wise":
            average_pitch = 0
            num_notes = 0
            for staff in self.staves:
                for measure in staff.measures:
                    for voice in measure:
                        for note in voice.iterate_notes(include_rests=False):
                            average_pitch += note.average_pitch()
            if num_notes > 0:
                average_pitch /= num_notes
                clef_choice = _get_clef_from_average_pitch_and_instrument_name(average_pitch, self.name)
                for staff in self.staves:
                    staff.measures[0].clef = clef_choice
        else:
            assert engraving_settings.clef_selection_policy == "measure-wise"
            for staff in self.staves:
                last_clef_used = None
                for i, measure in enumerate(staff.measures):
                    this_measure_clef = measure.get_appropriate_clef(self.name)
                    if this_measure_clef is not None:
                        # there are some pitches in this measure from which to choose a clef
                        if last_clef_used is None:
                            # if we haven't yet encountered a measure with any pitches in it, set the clef for
                            # the first measure to the this clef, which is the best clef once we start getting pitches
                            last_clef_used = staff.measures[0].clef = this_measure_clef
                        elif this_measure_clef != last_clef_used:
                            # otherwise we simply check if this clef is new information. If it's different from the
                            # clef in the last measure, then we set this measure to explicitly change it
                            last_clef_used = measure.clef = this_measure_clef
github MarcTheSpark / scamp / scamp / score.py View on Github external
def to_music_xml(self, source_id_dict=None) -> Sequence[_XMLNote]:
        notations = [notations_to_xml_notations_element[x] for x in self.properties.notations
                     if x in notations_to_xml_notations_element]

        if self.is_rest():
            return pymusicxml.Rest(self.written_length),
        elif self.is_chord():
            start_pitches = tuple(p.start_level() if isinstance(p, Envelope) else p for p in self.pitch)

            # if any of the starting pitches are not integers, and we are showing microtonal annotations,
            # add a text direction stating what the true pitches of the chord should be
            if any(x != int(x) for x in start_pitches) and engraving_settings.show_microtonal_annotations:
                directions = (pymusicxml.TextAnnotation("({})".format(
                    "; ".join(str(round(p, 3)) for p in start_pitches)
                ), italic=True), )
            else:
                directions = ()

            out = [pymusicxml.Chord(
                tuple(self.properties.spelling_policy.resolve_music_xml_pitch(p) for p in start_pitches),
                self.written_length, ties=self._get_xml_tie_state(),
                noteheads=tuple(get_xml_notehead(notehead) if notehead != "normal" else None
                                for notehead in self.properties.noteheads),
                directions=directions, notations=notations
            )]
            if self.does_glissando():
                grace_points = self._get_grace_points(engraving_settings.glissandi.max_inner_graces_music_xml)
                for t in grace_points:
github MarcTheSpark / scamp / scamp / performance_note.py View on Github external
second_part_chord.append(pitch_curve_end)
                    self.pitch = tuple(first_part_chord)
                    second_part.pitch = tuple(second_part_chord)

                # also, if this isn't a rest, then we're going to need to keep track of ties that will be needed
                self.properties["_starts_tie"] = True
                second_part.properties["_ends_tie"] = True

                for articulation in self.properties.articulations:
                    if articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "first":
                        # this articulation is about the attack, so should appear only in the first part
                        # of a split note, so remove it from the second
                        second_part.properties.articulations.remove(articulation)
                    elif articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "last":
                        # this articulation is about the release, so should appear only in the second part
                        # of a split note, so remove it from the first
                        self.properties.articulations.remove(articulation)
                    elif articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "both":
                        # this articulation is about the attack and release, but it doesn't really make
                        # sense to play it on a note the middle of a tied group
                        if self.properties.starts_tie() and self.properties.ends_tie():
                            self.properties.articulations.remove(articulation)
                        if second_part.properties.starts_tie() and second_part.properties.ends_tie():
                            second_part.properties.articulations.remove(articulation)
                    # note, if the split protocol says "all" (or doesn't exist), then we just
                    # default to keeping the articulation on everything

                # we also want to keep track of which notes came from the same original note for doing ties and such
                if "_source_id" in self.properties:
github MarcTheSpark / scamp / scamp / performance.py View on Github external
def __init__(self, instrument: ScampInstrument = None, name: str = None, voices: Union[dict, Sequence] = None,
                 instrument_id: Tuple[str, int] = None, voice_quantization_records: dict = None,
                 clef_preference: Sequence[Union[str, Tuple[str, Real]]] = None):
        self.instrument = instrument  # A ScampInstrument instance
        self.clef_preference = clef_preference if clef_preference is not None \
            else instrument.resolve_clef_preference() if instrument is not None \
            else engraving_settings.clefs_by_instrument["default"]
        # the name of the part can be specified directly, or if not derives from the instrument it's attached to
        # if the part is not attached to an instrument, it starts with a name of None
        self.name = name if name is not None else instrument.name if instrument is not None else None
        # this is used for serializing to and restoring from a json file. It should be enough to find
        # the instrument in the ensemble, so long as the ensemble is compatible.
        self._instrument_id = instrument_id if instrument_id is not None else \
            ((instrument.name, instrument.name_count) if instrument is not None else None)

        if voices is None:
            # no voices specified; create a dictionary with the catch-all voice "_unspecified_"
            self.voices = {"_unspecified_": []}
        elif isinstance(voices, dict):
            self.voices = {}
            for key, value in voices.items():
                # for each key, if it can be parsed as an integer, make sure it's doing so in only one way
                # this prevents confusion from two voices called "01" and "1" for instance
github MarcTheSpark / scamp / scamp / performance.py View on Github external
second_part_chord = []
                    for pitch_curve in self.pitch:
                        assert isinstance(pitch_curve, Envelope)
                        pitch_curve_start, pitch_curve_end = pitch_curve.split_at(self.length_sum())
                        first_part_chord.append(pitch_curve_start)
                        second_part_chord.append(pitch_curve_end)
                    self.pitch = tuple(first_part_chord)
                    second_part.pitch = tuple(second_part_chord)

                # also, if this isn't a rest, then we're going to need to keep track of ties that will be needed
                self.properties["_starts_tie"] = True
                second_part.properties["_ends_tie"] = True

                for articulation in self.properties.articulations:
                    if articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "first":
                        # this articulation is about the attack, so should appear only in the first part
                        # of a split note, so remove it from the second
                        second_part.properties.articulations.remove(articulation)
                    elif articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "last":
                        # this articulation is about the release, so should appear only in the second part
                        # of a split note, so remove it from the first
                        self.properties.articulations.remove(articulation)
                    elif articulation in engraving_settings.articulation_split_protocols and \
                            engraving_settings.articulation_split_protocols[articulation] == "both":
                        # this articulation is about the attack and release, but it doesn't really make
                        # sense to play it on a note the middle of a tied group
                        if self.properties.starts_tie() and self.properties.ends_tie():
                            self.properties.articulations.remove(articulation)
                        if second_part.properties.starts_tie() and second_part.properties.ends_tie():
                            second_part.properties.articulations.remove(articulation)