Example #1
0
 def testBeams(self):
     beam_1 = musicscore_pb2.LineSegment(start=Point(x=10, y=20),
                                         end=Point(x=40, y=20))
     beam_2 = musicscore_pb2.LineSegment(start=Point(x=70, y=40),
                                         end=Point(x=90, y=40))
     beam_3 = musicscore_pb2.LineSegment(start=Point(x=70, y=60),
                                         end=Point(x=90, y=60))
     staff = musicscore_pb2.Staff(
         staffline_distance=10,
         center_line=[Point(x=0, y=50),
                      Point(x=100, y=50)],
         glyph=[
             Glyph(type=Glyph.CLEF_TREBLE,
                   x=1,
                   y_position=reader.TREBLE_CLEF_EXPECTED_Y),
             # 2 eighth notes.
             Glyph(type=Glyph.NOTEHEAD_FILLED,
                   x=10,
                   y_position=-4,
                   beam=[beam_1]),
             Glyph(type=Glyph.NOTEHEAD_FILLED,
                   x=40,
                   y_position=-1,
                   beam=[beam_1]),
             # 1 quarter note.
             Glyph(type=Glyph.NOTEHEAD_FILLED, x=50, y_position=0),
             # 2 sixteenth notes.
             Glyph(type=Glyph.NOTEHEAD_FILLED,
                   x=60,
                   y_position=-2,
                   beam=[beam_2, beam_3]),
             Glyph(type=Glyph.NOTEHEAD_FILLED,
                   x=90,
                   y_position=2,
                   beam=[beam_2, beam_3]),
         ])
     notes = conversions.page_to_notesequence(
         reader.ScoreReader().read_page(
             musicscore_pb2.Page(
                 system=[musicscore_pb2.StaffSystem(staff=[staff])])))
     self.assertEqual(
         notes,
         music_pb2.NoteSequence(notes=[
             Note(pitch=librosa.note_to_midi('E4'),
                  start_time=0,
                  end_time=0.5),
             Note(pitch=librosa.note_to_midi('A4'),
                  start_time=0.5,
                  end_time=1),
             Note(pitch=librosa.note_to_midi('B4'),
                  start_time=1,
                  end_time=2),
             Note(pitch=librosa.note_to_midi('G4'),
                  start_time=2,
                  end_time=2.25),
             Note(pitch=librosa.note_to_midi('D5'),
                  start_time=2.25,
                  end_time=2.5),
         ]))
Example #2
0
 def testDummy(self):
     # Create a single staff, and a single vertical which is the correct height
     # of a stem. The vertical has x = 20 and goes from
     struct = structure.Structure(
         staff_detector=staves_base.ComputedStaves(
             staves=[[[10, 50], [90, 50]]],
             staffline_distance=[12],
             staffline_thickness=2,
             staves_interpolated_y=[[50] * 100]),
         beams=beams.ComputedBeams(np.zeros((0, 2, 2))),
         verticals=verticals.ComputedVerticals(
             lines=[[[20, 38], [20, 38 + 12 * 4]]]),
         connected_components=components.ComputedComponents([]))
     stems = stems_module.Stems(struct)
     # Create a Page with Glyphs.
     input_page = musicscore_pb2.Page(system=[
         musicscore_pb2.StaffSystem(staff=[
             musicscore_pb2.Staff(
                 staffline_distance=12,
                 center_line=[
                     musicscore_pb2.Point(x=10, y=50),
                     musicscore_pb2.Point(x=90, y=50)
                 ],
                 glyph=[
                     # Cannot have a stem because it's a flat.
                     musicscore_pb2.Glyph(type=musicscore_pb2.Glyph.FLAT,
                                          x=15,
                                          y_position=-1),
                     # On the right side of the stem, the correct distance away.
                     musicscore_pb2.Glyph(
                         type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                         x=25,
                         y_position=-1),
                     # Too high for the stem.
                     musicscore_pb2.Glyph(
                         type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                         x=25,
                         y_position=4),
                     # Too far right from the stem.
                     musicscore_pb2.Glyph(
                         type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                         x=35,
                         y_position=-1),
                 ])
         ])
     ])
     page = stems.apply(input_page)
     self.assertFalse(page.system[0].staff[0].glyph[0].HasField("stem"))
     self.assertTrue(page.system[0].staff[0].glyph[1].HasField("stem"))
     self.assertEqual(
         page.system[0].staff[0].glyph[1].stem,
         musicscore_pb2.LineSegment(start=Point(x=20, y=38),
                                    end=Point(x=20, y=38 + 12 * 4)))
     self.assertFalse(page.system[0].staff[0].glyph[2].HasField("stem"))
     self.assertFalse(page.system[0].staff[0].glyph[3].HasField("stem"))
Example #3
0
    def apply(self, page):
        """Detects stems on the page.

    Using `self.stem_candidates`, finds verticals that align with a notehead
    glyph, and adds the stems.

    Args:
      page: The Page message.

    Returns:
      The same page, updated with stems.
    """
        for system in page.system:
            for staff, staff_ys in zip(
                    system.staff, self.staff_detector.staves_interpolated_y):
                allowed_distance = np.multiply(
                    _STEM_NOTEHEAD_DISTANCE_STAFFLINE_DISTANCE,
                    staff.staffline_distance)
                expected_horizontal_distance = np.multiply(
                    _STEM_NOTEHEAD_HORIZONTAL_STAFFLINE_DISTANCE,
                    staff.staffline_distance)
                for glyph in staff.glyph:
                    if glyph_types.is_stemmed_notehead(glyph):
                        glyph_y = (
                            staff_ys[glyph.x] -
                            glyph.y_position * staff.staffline_distance / 2.0)
                        # Compute the ideal coordinates for the glyph to be assigned to each
                        # stem.

                        # Clip the glyph_y to the stem start and end y to get the ideal y.
                        ideal_y = np.clip(glyph_y, self.stem_candidates[:, 0,
                                                                        1],
                                          self.stem_candidates[:, 1, 1])
                        # If the glyph is left of the stem, subtract the expected distance
                        # from the stem x; otherwise, add it.
                        ideal_x = self.stem_candidates[:, 0, 0] + np.where(
                            glyph.x < self.stem_candidates[:, 0, 0],
                            -expected_horizontal_distance,
                            expected_horizontal_distance)
                        stem_distance = np.linalg.norm(
                            np.c_[ideal_x - glyph.x, ideal_y - glyph_y],
                            axis=1)
                        stem = np.argmin(stem_distance)
                        if stem_distance[stem] <= allowed_distance:
                            stem_coords = self.stem_candidates[stem]
                            glyph.stem.CopyFrom(
                                musicscore_pb2.LineSegment(
                                    start=musicscore_pb2.Point(
                                        x=stem_coords[0, 0],
                                        y=stem_coords[0, 1]),
                                    end=musicscore_pb2.Point(
                                        x=stem_coords[1, 0],
                                        y=stem_coords[1, 1])))
        return page
Example #4
0
    def apply(self, page):
        """Adds beams that intersect with note stems to the page.

    Beams should intersect with two or more stems. Beams are currently
    implemented as a bounding box, so we just see whether that box intersects
    with each stem.

    Args:
      page: A Page message.

    Returns:
      The same page, with `beam`s added to the `Glyph`s.
    """
        for system in page.system:
            for staff in system.staff:
                # Extend the beams by the staffline distance on either side. Beams may
                # end immediately at a stem, so give an extra allowance for that stem.
                extended_beams = self.beams.copy()
                extended_beams[:, COLUMNS.X0] -= staff.staffline_distance
                extended_beams[:, COLUMNS.X1] += staff.staffline_distance
                for glyph in staff.glyph:
                    if glyph_types.is_beamed_notehead(
                            glyph) and glyph.HasField('stem'):
                        xs = [glyph.stem.start.x, glyph.stem.end.x]
                        ys = [glyph.stem.start.y, glyph.stem.end.y]
                        stem_bounding_box = np.asarray([[min(*xs),
                                                         min(*ys)],
                                                        [max(*xs),
                                                         max(*ys)]])
                        overlapping_beams = _get_overlapping_beams(
                            stem_bounding_box, extended_beams)
                        glyph.beam.extend(
                            musicscore_pb2.LineSegment(
                                start=musicscore_pb2.Point(x=beam[COLUMNS.X0],
                                                           y=beam[COLUMNS.Y0]),
                                end=musicscore_pb2.Point(x=beam[COLUMNS.X1],
                                                         y=beam[COLUMNS.Y1]))
                            for beam in overlapping_beams)
        return page
Example #5
0
    def testChords(self):
        stem_1 = musicscore_pb2.LineSegment(start=Point(x=20, y=10),
                                            end=Point(x=20, y=70))
        stem_2 = musicscore_pb2.LineSegment(start=Point(x=50, y=10),
                                            end=Point(x=50, y=70))
        staff = musicscore_pb2.Staff(
            staffline_distance=10,
            center_line=[Point(x=0, y=50),
                         Point(x=100, y=50)],
            glyph=[
                Glyph(type=Glyph.CLEF_TREBLE,
                      x=1,
                      y_position=reader.TREBLE_CLEF_EXPECTED_Y),
                # Chord of 2 notes.
                Glyph(type=Glyph.NOTEHEAD_FILLED,
                      x=10,
                      y_position=-4,
                      stem=stem_1),
                Glyph(type=Glyph.NOTEHEAD_FILLED,
                      x=10,
                      y_position=-1,
                      stem=stem_1),

                # Note not attached to a stem.
                Glyph(type=Glyph.NOTEHEAD_FILLED, x=30, y_position=3),

                # Chord of 3 notes.
                Glyph(type=Glyph.NOTEHEAD_FILLED,
                      x=40,
                      y_position=0,
                      stem=stem_2),
                Glyph(type=Glyph.NOTEHEAD_FILLED,
                      x=60,
                      y_position=2,
                      stem=stem_2),
                Glyph(type=Glyph.NOTEHEAD_FILLED,
                      x=60,
                      y_position=4,
                      stem=stem_2),
            ])
        notes = conversions.page_to_notesequence(
            reader.ScoreReader().read_page(
                musicscore_pb2.Page(
                    system=[musicscore_pb2.StaffSystem(staff=[staff])])))
        self.assertEqual(
            notes,
            music_pb2.NoteSequence(notes=[
                # First chord.
                Note(pitch=librosa.note_to_midi('E4'),
                     start_time=0,
                     end_time=1),
                Note(pitch=librosa.note_to_midi('A4'),
                     start_time=0,
                     end_time=1),

                # Note without a stem.
                Note(pitch=librosa.note_to_midi('E5'),
                     start_time=1,
                     end_time=2),

                # Second chord.
                Note(pitch=librosa.note_to_midi('B4'),
                     start_time=2,
                     end_time=3),
                Note(pitch=librosa.note_to_midi('D5'),
                     start_time=2,
                     end_time=3),
                Note(pitch=librosa.note_to_midi('F5'),
                     start_time=2,
                     end_time=3),
            ]))
Example #6
0
    def testSmallScore(self):
        score = musicscore_pb2.Score(page=[
            musicscore_pb2.Page(system=[
                musicscore_pb2.StaffSystem(staff=[
                    musicscore_pb2.Staff(glyph=[
                        musicscore_pb2.Glyph(
                            type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                            x=10,
                            y_position=0,
                            note=music_pb2.NoteSequence.Note(
                                start_time=0, end_time=1, pitch=71)),
                        musicscore_pb2.Glyph(
                            type=musicscore_pb2.Glyph.NOTEHEAD_EMPTY,
                            x=110,
                            y_position=-6,
                            note=music_pb2.NoteSequence.Note(
                                start_time=1, end_time=2.5, pitch=61)),
                    ]),
                    musicscore_pb2.Staff(glyph=[
                        musicscore_pb2.Glyph(
                            type=musicscore_pb2.Glyph.NOTEHEAD_WHOLE,
                            x=10,
                            y_position=2,
                            note=music_pb2.NoteSequence.Note(
                                start_time=0, end_time=4, pitch=50)),
                        musicscore_pb2.Glyph(
                            type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                            beam=[
                                musicscore_pb2.LineSegment(),
                                musicscore_pb2.LineSegment()
                            ],
                            x=110,
                            y_position=-4,
                            note=music_pb2.NoteSequence.Note(
                                start_time=4, end_time=4.25, pitch=60)),
                    ]),
                ]),
            ]),
        ])
        self.assertEqual(
            b"""<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE score-partwise PUBLIC
    "-//Recordare//DTD MusicXML 3.0 Partwise//EN"
    "http://www.musicxml.org/dtds/partwise.dtd">

<score-partwise version="3.0">
  <part-list>
    <score-part id="P1">
      <part-name>Part 1</part-name>
    </score-part>
    <score-part id="P2">
      <part-name>Part 2</part-name>
    </score-part>
  </part-list>
  <part id="P1">
    <measure number="1">
      <attributes>
        <divisions>1024</divisions>
        <time symbol="common">
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
      </attributes>
      <note>
        <voice>1</voice>
        <type>quarter</type>
        <duration>1024</duration>
        <pitch>
          <step>B</step>
          <alter>0</alter>
          <octave>4</octave>
        </pitch>
      </note>
      <note>
        <voice>1</voice>
        <type>half</type>
        <duration>1536</duration>
        <pitch>
          <step>C</step>
          <alter>1</alter>
          <octave>4</octave>
        </pitch>
      </note>
    </measure>
  </part>
  <part id="P2">
    <measure number="1">
      <attributes>
        <divisions>1024</divisions>
        <time symbol="common">
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
      </attributes>
      <note>
        <voice>1</voice>
        <type>whole</type>
        <duration>4096</duration>
        <pitch>
          <step>D</step>
          <alter>0</alter>
          <octave>3</octave>
        </pitch>
      </note>
      <note>
        <voice>1</voice>
        <type>16th</type>
        <duration>256</duration>
        <pitch>
          <step>C</step>
          <alter>0</alter>
          <octave>4</octave>
        </pitch>
      </note>
    </measure>
  </part>
</score-partwise>
""", musicxml.score_to_musicxml(score))
Example #7
0
    def testDummy(self):
        # Create a single staff, and a single vertical which is the correct height
        # of a stem. The vertical has x = 20 and goes from
        struct = structure.Structure(
            staff_detector=staves_base.ComputedStaves(
                staves=[[[10, 50], [90, 50]], [[11, 150], [91, 150]],
                        [[10, 250], [90, 250]], [[10, 350], [90, 350]]],
                staffline_distance=[12] * 4,
                staffline_thickness=2,
                staves_interpolated_y=[[50] * 100, [150] * 100, [250] * 100,
                                       [350] * 100]),
            beams=beams.ComputedBeams(np.zeros((0, 2, 2))),
            connected_components=components.ComputedComponents(np.zeros(
                (0, 5))),
            verticals=verticals.ComputedVerticals(lines=[
                # Joins the first 2 staves.
                [[10, 50 - 12 * 2], [10, 150 + 12 * 2]],
                # Another barline, too close to the first one.
                [[12, 50 - 12 * 2], [12, 150 + 12 * 2]],
                # This barline is far enough, because the second barline was
                # skipped.
                [[13, 50 - 12 * 2], [13, 150 + 12 * 2]],
                # Single staff barlines are skipped.
                [[30, 50 - 12 * 2], [30, 50 + 12 * 2]],
                [[31, 150 - 12 * 2], [31, 150 + 12 * 2]],
                # Too close to a stem.
                [[70, 50 - 12 * 2], [70, 50 + 12 * 2]],
                # Too short.
                [[90, 50 - 12 * 2], [90, 50 + 12 * 2]],
                # Another barline which is kept.
                [[90, 50 - 12 * 2], [90, 150 + 12 * 2]],
                # Staff 1 has no barlines.
                # Staff 2 has 2 barlines.
                [[11, 350 - 12 * 2], [11, 350 + 12 * 2]],
                [[90, 350 - 12 * 2], [90, 350 + 12 * 2]],
            ]))
        barlines = barlines_module.Barlines(struct, close_barline_threshold=3)
        # Create a Page with Glyphs.
        input_page = musicscore_pb2.Page(system=[
            musicscore_pb2.StaffSystem(staff=[
                musicscore_pb2.Staff(
                    staffline_distance=12,
                    center_line=[
                        musicscore_pb2.Point(x=10, y=50),
                        musicscore_pb2.Point(x=90, y=50)
                    ],
                    glyph=[
                        # Stem is close to the last vertical on the first staff, so
                        # a barline will not be detected there.
                        musicscore_pb2.Glyph(
                            type=musicscore_pb2.Glyph.NOTEHEAD_FILLED,
                            x=60,
                            y_position=2,
                            stem=musicscore_pb2.LineSegment(
                                start=musicscore_pb2.Point(x=72, y=40),
                                end=musicscore_pb2.Point(x=72, y=80))),
                    ]),
                musicscore_pb2.Staff(staffline_distance=12,
                                     center_line=[
                                         musicscore_pb2.Point(x=10, y=150),
                                         musicscore_pb2.Point(x=90, y=150)
                                     ]),
                musicscore_pb2.Staff(staffline_distance=12,
                                     center_line=[
                                         musicscore_pb2.Point(x=10, y=250),
                                         musicscore_pb2.Point(x=90, y=250)
                                     ]),
                musicscore_pb2.Staff(staffline_distance=12,
                                     center_line=[
                                         musicscore_pb2.Point(x=10, y=350),
                                         musicscore_pb2.Point(x=90, y=350)
                                     ]),
            ])
        ])
        page = barlines.apply(input_page)
        self.assertEqual(3, len(page.system))

        self.assertEqual(2, len(page.system[0].staff))
        self.assertItemsEqual([10, 13, 90],
                              (bar.x for bar in page.system[0].bar))

        self.assertEqual(1, len(page.system[1].staff))
        self.assertEqual(0, len(page.system[1].bar))

        self.assertEqual(1, len(page.system[2].staff))
        self.assertEqual(2, len(page.system[2].bar))
        self.assertItemsEqual([11, 90], (bar.x for bar in page.system[2].bar))