def test_middle_c_at_with_explicit_clefs(self): staff = Staff((Mm(0), Mm(0)), Mm(100), self.flowable) Clef(staff, Mm(0), "treble") Clef(staff, Mm(10), "bass") # Test between two clefs should be in treble mode assert staff.middle_c_at(Mm(1)) == staff.unit(5) # Test after bass clef goes into effect assert staff.middle_c_at(Mm(11)) == staff.unit(-1)
def test_position_on_ledger_with_odd_line_count(self): staff = Staff((Mm(0), Mm(0)), Mm(100), self.flowable, line_count=5) assert staff.y_on_ledger(staff.unit(-1)) is True assert staff.y_on_ledger(staff.unit(-0.5)) is False assert staff.y_on_ledger(staff.unit(0)) is False assert staff.y_on_ledger(staff.unit(4)) is False assert staff.y_on_ledger(staff.unit(4.5)) is False assert staff.y_on_ledger(staff.unit(5)) is True
class TestFlag(unittest.TestCase): def setUp(self): neoscore.setup() self.staff = Staff((Mm(0), Mm(0)), Mm(100), flowable=None) def test_glyphnames(self): # All flags with durations in the following denominations should # be init-able without error. for i in [1024, 512, 256, 128, 64, 32, 16, 8]: Flag(Beat(1, i), 1, self.staff) Flag(Beat(1, i), -1, self.staff) def test_invalid_direction_raises_value_error(self): with pytest.raises(ValueError): Flag(Beat(1, 8), 0, self.staff) with pytest.raises(ValueError): Flag(Beat(1, 8), 2, self.staff) with pytest.raises(ValueError): Flag(Beat(1, 8), -2, self.staff) def test_needs_flag(self): assert Flag.needs_flag(Beat(1, 4)) is False assert Flag.needs_flag(Beat(1, 2)) is False assert Flag.needs_flag(Beat(1, 8)) is True assert Flag.needs_flag(Beat(1, 16)) is True def test_vertical_offset_needed(self): self.assertEqual( Flag.vertical_offset_needed(Beat(1, 4), self.staff.unit), self.staff.unit(0)) self.assertEqual( Flag.vertical_offset_needed(Beat(1, 8), self.staff.unit), self.staff.unit(1)) self.assertEqual( Flag.vertical_offset_needed(Beat(1, 16), self.staff.unit), self.staff.unit(1), ) def test_raises_no_flag_needed_error(self): # Test valid durations Flag(Beat(1, 16), 1, self.staff) Flag(Beat(1, 8), 1, self.staff) # Test invalid durations with pytest.raises(NoFlagNeededError): Flag(Beat(1, 4), 1, self.staff) with pytest.raises(NoFlagNeededError): Flag(Beat(1, 2), 1, self.staff) with pytest.raises(NoFlagNeededError): Flag(Beat(1, 1), 1, self.staff)
def test_middle_c_at_with_active_octave_line(self): staff = Staff((Mm(0), Mm(0)), Mm(100), self.flowable) Clef(staff, Mm(0), "treble") octave_line = OctaveLine((Mm(20), Mm(0)), staff, Mm(80), indication="8va") # Before octave_line goes into effect assert staff.middle_c_at(Mm(0)) == staff.unit(5) # While octave_line is in effect assert staff.middle_c_at(Mm(20)) == staff.unit(8.5) assert staff.middle_c_at(Mm(100)) == staff.unit(8.5) # After octave_line ends assert staff.middle_c_at(Mm(101)) == staff.unit(5)
class TestMultiStaffObject(unittest.TestCase): def setUp(self): neoscore.setup() self.flowable = Flowable((Mm(0), Mm(0)), Mm(10000), Mm(30), Mm(5)) self.staff_1 = Staff((Mm(0), Mm(0)), Mm(100), self.flowable) self.staff_2 = Staff((Mm(0), Mm(30)), Mm(100), self.flowable) self.staff_3 = Staff((Mm(0), Mm(50)), Mm(100), self.flowable) def test_visually_sorted_staves(self): multi_object = MultiStaffObject( {self.staff_1, self.staff_2, self.staff_3}) assert multi_object.visually_sorted_staves == [ self.staff_1, self.staff_2, self.staff_3, ] def test_highest_staff(self): multi_object = MultiStaffObject( {self.staff_1, self.staff_2, self.staff_3}) assert multi_object.highest_staff == self.staff_1 def test_lowest_staff(self): multi_object = MultiStaffObject( {self.staff_1, self.staff_2, self.staff_3}) assert multi_object.lowest_staff == self.staff_3 def test_vertical_span(self): multi_object = MultiStaffObject( {self.staff_1, self.staff_2, self.staff_3}) assert_almost_equal(multi_object.vertical_span, self.staff_3.unit(4) + Mm(50))
def __init__( self, pos_x: Unit, staff: Staff, pitches: Optional[list[PitchDef]], duration: BeatDef, stem_direction: Optional[int] = None, ): """ Args: pos_x: The horizontal position staff: The staff the object is attached to pitches: A list of pitch strings representing noteheads. An empty list or `None` indicates a rest. duration: The duration of the Chordrest stem_direction: An optional stem direction override where `1` points down and `-1` points up. If omitted, the direction is automatically calculated to point away from the furthest-out notehead. """ StaffObject.__init__(self, staff) ObjectGroup.__init__(self, Point(pos_x, staff.unit(0)), staff, None) self.duration = duration self._noteheads = set() self._accidentals = set() self._ledgers = set() self._dots = set() self._noteheads = set() self._stem_direction_override = stem_direction if pitches: for pitch in pitches: self._noteheads.add(Notehead(staff.unit(0), pitch, self.duration, self)) self.rest = None else: # TODO LOW support explicit rest Y positioning self.rest = Rest(Point(staff.unit(0), staff.unit(2)), self, duration) self._stem = None self._flag = None
def __init__( self, pos_x: Unit, staff: Staff, key_signature_type: Union[KeySignatureType, str], ): """ Args: pos_x (Unit): The x position relative to the parent staff. staff (Staff): The parent staff key_signature_type (KeySignatureType or str): A description of the key signature. Any KeySignatureType may be used, or a str of one's name. """ ObjectGroup.__init__(self, Point(pos_x, staff.unit(0)), staff) StaffObject.__init__(self, staff) self._key_signature_type = ( key_signature_type if isinstance(key_signature_type, KeySignatureType) else KeySignatureType[key_signature_type.upper()]) self._create_pseudo_accidentals()
def __init__(self, staff: Staff, pos_x: Unit, clef_type: Union[ClefType, str]): """ Args: staff (Staff): pos_x (Unit): clef_type (ClefType or str): The type of clef. For convenience, any `str` of a `ClefType` enum name may be passed. Raises: KeyError: If the given `clef_type` is not a valid `ClefType` or `ClefType` enum name. """ if isinstance(clef_type, ClefType): self._clef_type = clef_type else: self._clef_type = ClefType[clef_type.upper()] MusicText.__init__(self, (pos_x, staff.unit(0)), self._canonical_names[self._clef_type], staff) StaffObject.__init__(self, staff) self.y = self.staff_position
class TestNotehead(unittest.TestCase): def setUp(self): neoscore.setup() self.flowable = Flowable((Mm(0), Mm(0)), Mm(10000), Mm(30), Mm(5)) self.staff = Staff((Mm(0), Mm(0)), Mm(10000), flowable=self.flowable) Clef(self.staff, Mm(0), "treble") def test_staff_position_middle_c_treble(self): self.assertEqual( Notehead(Mm(10), "c'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(5) ) def test_staff_position_low_octaves(self): self.assertEqual( Notehead(Mm(10), "c", Beat(1, 4), self.staff).staff_pos, self.staff.unit(8.5), ) self.assertEqual( Notehead(Mm(10), "c,", Beat(1, 4), self.staff).staff_pos, self.staff.unit(12), ) self.assertEqual( Notehead(Mm(10), "c,,", Beat(1, 4), self.staff).staff_pos, self.staff.unit(15.5), ) self.assertEqual( Notehead(Mm(10), "c,,,", Beat(1, 4), self.staff).staff_pos, self.staff.unit(19), ) def test_staff_position_high_octaves(self): self.assertEqual( Notehead(Mm(10), "c''", Beat(1, 4), self.staff).staff_pos, self.staff.unit(1.5), ) self.assertEqual( Notehead(Mm(10), "c'''", Beat(1, 4), self.staff).staff_pos, self.staff.unit(-2), ) self.assertEqual( Notehead(Mm(10), "c''''", Beat(1, 4), self.staff).staff_pos, self.staff.unit(-5.5), ) self.assertEqual( Notehead(Mm(10), "c'''''", Beat(1, 4), self.staff).staff_pos, self.staff.unit(-9), ) def test_staff_position_with_accidentals(self): self.assertEqual( Notehead(Mm(10), "cf'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(5), ) self.assertEqual( Notehead(Mm(10), "cn'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(5), ) self.assertEqual( Notehead(Mm(10), "cs'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(5), ) def test_staff_position_with_all_letter_names(self): self.assertEqual( Notehead(Mm(10), "d'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(4.5), ) self.assertEqual( Notehead(Mm(10), "e'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(4) ) self.assertEqual( Notehead(Mm(10), "f'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(3.5), ) self.assertEqual( Notehead(Mm(10), "g'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(3) ) self.assertEqual( Notehead(Mm(10), "a'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(2.5), ) self.assertEqual( Notehead(Mm(10), "b'", Beat(1, 4), self.staff).staff_pos, self.staff.unit(2) ) def test_staff_position_on_later_flowable_line(self): self.assertEqual( Notehead(Mm(1000), "c", Beat(1, 4), self.staff).staff_pos, self.staff.unit(8.5), )
def test_ledgers_needed_from_position_with_odd_line_count(self): staff = Staff((Mm(0), Mm(0)), Mm(100), self.flowable, line_count=5) # Inside the staff, no ledgers assert staff.ledgers_needed_for_y(staff.unit(0)) == [] assert staff.ledgers_needed_for_y(staff.unit(4)) == [] # Just outside the staff, no ledgers assert staff.ledgers_needed_for_y(staff.unit(-0.5)) == [] assert staff.ledgers_needed_for_y(staff.unit(4.5)) == [] # Right on the first ledger assert staff.ledgers_needed_for_y(staff.unit(-1)) == [staff.unit(-1)] assert staff.ledgers_needed_for_y(staff.unit(5)) == [staff.unit(5)] # Further outside with multiple ledgers, directly on lines assert staff.ledgers_needed_for_y(staff.unit(6)) == [ staff.unit(6), staff.unit(5), ] assert staff.ledgers_needed_for_y(staff.unit(-2)) == [ staff.unit(-2), staff.unit(-1), ] # Further outside with multiple ledgers, between lines assert staff.ledgers_needed_for_y(staff.unit(6.5)) == [ staff.unit(6), staff.unit(5), ] assert staff.ledgers_needed_for_y(staff.unit(-2.5)) == [ staff.unit(-2), staff.unit(-1), ]
def test_position_inside_staff_with_even_line_count(self): staff = Staff((Mm(0), Mm(0)), Mm(100), self.flowable, line_count=4) assert staff.y_inside_staff(staff.unit(0)) is True assert staff.y_inside_staff(staff.unit(3)) is True assert staff.y_inside_staff(staff.unit(4)) is False assert staff.y_inside_staff(staff.unit(-4)) is False
class TestChordrest(unittest.TestCase): def setUp(self): neoscore.setup() self.flowable = Flowable(Point(Mm(0), Mm(0)), Mm(10000), Mm(100)) self.staff = Staff(Point(Mm(0), Mm(0)), Mm(100), self.flowable) Clef(self.staff, Mm(0), "treble") def test_ledger_line_positions(self): pitches = ["c'", "b'", "f'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.ledger_line_positions == [ self.staff.unit(5), self.staff.unit(-3), self.staff.unit(-2), self.staff.unit(-1), ] def test_ledger_line_positions_with_different_clef(self): Clef(self.staff, Mm(10), "bass") pitches = ["e,", "d", "e'"] chord = Chordrest(Mm(15), self.staff, pitches, Beat(1, 4)) assert chord.ledger_line_positions == [ self.staff.unit(5), self.staff.unit(-2), self.staff.unit(-1), ] def test_rhythm_dot_positions_with_rest(self): chord = Chordrest(Mm(1), self.staff, None, Beat(7, 16)) dots = list(chord.rhythm_dot_positions) dots.sort(key=lambda d: d.x) assert_almost_equal( dots[0], Point(self.staff.unit(1.326), self.staff.unit(1.5)) ) assert_almost_equal( dots[1], Point(self.staff.unit(1.826), self.staff.unit(1.5)) ) def test_rhythm_dot_positions_with_noteheads(self): pitches = ["e,", "d", "e'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(7, 16)) dots = list(chord.rhythm_dot_positions) dots.sort(key=lambda d: d.x) dots.sort(key=lambda d: d.y) assert_almost_equal( dots[0], Point(self.staff.unit(1.43), self.staff.unit(-3.5)) ) assert_almost_equal( dots[1], Point(self.staff.unit(1.93), self.staff.unit(-3.5)) ) assert_almost_equal(dots[2], Point(self.staff.unit(1.43), self.staff.unit(7.5))) assert_almost_equal(dots[3], Point(self.staff.unit(1.93), self.staff.unit(7.5))) assert_almost_equal( dots[4], Point(self.staff.unit(1.43), self.staff.unit(10.5)) ) assert_almost_equal( dots[5], Point(self.staff.unit(1.93), self.staff.unit(10.5)) ) def test_furthest_notehead_with_one_note(self): pitches = ["b'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("b'") pitches = ["f'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("f'''") pitches = ["c,,,,"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("c,,,,") def test_furthest_notehead_with_many_notes(self): pitches = ["b''", "bs'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("b''") pitches = ["b'", "b,,,"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("b,,,") pitches = ["f''''", "b"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("f''''") pitches = ["c'", "c,,,,", "b'", "c'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.furthest_notehead.pitch == Pitch("c,,,,") def test_highest_notehead(self): pitches = ["c'", "b'", "c'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.highest_notehead.pitch == Pitch("c'''") def test_lowest_notehead(self): pitches = ["c'", "b'", "c'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.lowest_notehead.pitch == Pitch("c'") def test_highest_and_lowest_notehead_same_with_one_note(self): pitches = ["c'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.highest_notehead == chord.lowest_notehead def test_stem_direction_down(self): pitches = ["c'", "b'", "c''''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.stem_direction == 1 def test_stem_direction_up(self): pitches = ["c,,,,,", "b'", "c'''"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.stem_direction == -1 def test_stem_direction_down_with_one_note_at_staff_center(self): pitches = ["b'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert chord.stem_direction == 1 def test_stem_direction_override(self): pitches = ["b'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4), -1) assert chord.stem_direction == -1 # Setting stem_direction = None should revert to default 1 chord.stem_direction = None assert chord.stem_direction == 1 def test_stem_height_min(self): pitches = ["b'"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert_almost_equal(chord.stem_height, self.staff.unit(3)) def test_stem_height_fitted(self): pitches = ["c'''", "g"] chord = Chordrest(Mm(1), self.staff, pitches, Beat(1, 4)) assert_almost_equal(chord.stem_height, self.staff.unit(-10.5))