Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
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
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
"""
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
"""
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
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")]
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
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:
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:
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
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)