예제 #1
0
 def testJoinPartStaffsB(self):
     '''
     Gapful first PartStaff, ensure <backup> in second PartStaff correct
     '''
     from music21 import layout
     from music21 import note
     s = stream.Score()
     ps1 = stream.PartStaff()
     ps1.insert(0, note.Note())
     # Gap
     ps1.insert(3, note.Note())
     ps2 = stream.PartStaff()
     ps2.insert(0, note.Note())
     s.append(ps1)
     s.append(ps2)
     s.insert(0, layout.StaffGroup([ps1, ps2]))
     root = self.getET(s)
     notes = root.findall('.//note')
     forward = root.find('.//forward')
     backup = root.find('.//backup')
     amountToBackup = (
         int(notes[0].find('duration').text)
         + int(forward.find('duration').text)
         + int(notes[1].find('duration').text)
     )
     self.assertEqual(int(backup.find('duration').text), amountToBackup)
예제 #2
0
    def testMultipleInstrumentsPiano(self):
        ps1 = stream.PartStaff([
            stream.Measure(
                [instrument.ElectricPiano(),
                 note.Note(type='whole')]),
            stream.Measure(
                [instrument.ElectricOrgan(),
                 note.Note(type='whole')]),
            stream.Measure([instrument.Piano(),
                            note.Note(type='whole')]),
        ])
        ps2 = stream.PartStaff([
            stream.Measure([instrument.Vocalist(),
                            note.Note(type='whole')]),
            stream.Measure([note.Note(type='whole')]),
            stream.Measure([note.Note(type='whole')]),
        ])
        sg = layout.StaffGroup([ps1, ps2])
        s = stream.Score([ps1, ps2, sg])
        scEx = ScoreExporter(s)
        tree = scEx.parse()

        self.assertEqual(
            [el.text for el in tree.findall('.//instrument-name')],
            ['Electric Piano', 'Voice', 'Electric Organ', 'Piano'])
        self.assertEqual(len(tree.findall('.//measure/note/instrument')), 6)
예제 #3
0
    def testJoinPartStaffsE(self):
        '''
        Measure numbers existing only in certain PartStaffs: don't collapse together
        '''
        from music21 import corpus
        from music21 import layout
        sch = corpus.parse('schoenberg/opus19', 2)

        s = stream.Score()
        ps1 = stream.PartStaff()
        ps2 = stream.PartStaff()
        s.append(ps1)
        s.append(ps2)
        s.insert(0, layout.StaffGroup([ps1, ps2]))
        m1 = sch.parts[0].measure(1)  # RH
        m2 = sch.parts[1].measure(2)  # LH
        m3 = sch.parts[0].measure(3)  # RH
        ps1.append(m1)
        ps1.append(m3)
        ps2.insert(m1.offset, m2)
        root = self.getET(s)
        m1tag, m2tag, m3tag = root.findall('part/measure')
        self.assertEqual({staff.text
                          for staff in m1tag.findall('note/staff')}, {'1'})
        self.assertEqual({staff.text
                          for staff in m2tag.findall('note/staff')}, {'2'})
        self.assertEqual({staff.text
                          for staff in m3tag.findall('note/staff')}, {'1'})
예제 #4
0
    def writeScoreWithAnalysis(self,
                               outPath: str = '.',
                               outFile: str = 'Analysis_on_score',
                               feedback: bool = True,
                               lieder: bool = True):
        '''
        Mostly to combine an off-score analysis with the corresponding score and write to disc.

        Option (feedback=True, default) to include '***' above relevant moments.

        Error raised in the case of a call on score with analysis already on there
        (i.e. with analysisLocation = 'On score' in the init)
        and feedback as false.
        Nothing to add in that case.

        Additional presentation option (lieder=True, default) for returning the
        Mensurstrich brace to the piano part of the lieder.
        Don't worry if that doesn't mean anything to you.
        '''

        if self.analysisLocation == 'On score':
            if not feedback:
                msg = 'This method is for combining a score with ' \
                      'an analysis hosted separately, and / or for ' \
                      'flagging up moments for which there is feedback available.\n' \
                      'You have called this on a score with the analysis already on there, ' \
                      'and declined feedback, so this method has nothing to offer.'
                raise ValueError(msg)
            else:
                self.scoreWithAnalysis = self.score
        else:
            self.scoreWithAnalysis = deepcopy(self.score)
            analysis = deepcopy(self.analysis)
            analysis.partName = 'Analysis'

            measureDiff = self.scoreMeasures - self.analysisMeasures
            if measureDiff > 0:
                for x in range(measureDiff):
                    analysis.append(stream.Measure())

            self.scoreWithAnalysis.insert(0, analysis)

        if lieder:  # If lieder option is set to true and ...
            if len(self.scoreWithAnalysis.parts) == 4:  # there are 4 parts inc. the analysis
                staffGrouping = layout.StaffGroup([self.scoreWithAnalysis.parts[1],
                                                   self.scoreWithAnalysis.parts[2]
                                                   ],
                                                  name='Piano', abbreviation='Pno.', symbol='brace')
                staffGrouping.barTogether = 'Mensurstrich'
                self.scoreWithAnalysis.insert(0, staffGrouping)

        # self._feedbackOnScore()

        if outFile == 'Analysis_on_score':
            if self.name:
                outFile = self.name + '_with_analysis_onscore',

        self.scoreWithAnalysis.write('mxl', fp=f'{os.path.join(outPath, outFile)}.mxl')
예제 #5
0
    def testStaffGroupsB(self):
        from music21 import layout

        p1 = stream.Part()
        p1.repeatAppend(note.Note(), 8)
        p2 = stream.Part()
        p3 = stream.Part()
        p4 = stream.Part()
        p5 = stream.Part()
        p6 = stream.Part()
        p7 = stream.Part()
        p8 = stream.Part()

        sg1 = layout.StaffGroup([p1, p2], symbol='brace', name='marimba')
        sg2 = layout.StaffGroup([p3, p4], symbol='bracket', name='xlophone')
        sg3 = layout.StaffGroup([p5, p6], symbol='line', barTogether=False)
        sg4 = layout.StaffGroup([p5, p6, p7], symbol='line', barTogether=False)

        s = stream.Score()
        s.insert([
            0, p1, 0, p2, 0, p3, 0, p4, 0, p5, 0, p6, 0, p7, 0, p8, 0, sg1, 0,
            sg2, 0, sg3, 0, sg4
        ])  # deprecated insert usage
        #s.show()

        raw = fromMusic21Object(s)

        match = '<group-symbol>brace</group-symbol>'
        self.assertEqual(raw.find(match) > 0, True)
        match = '<group-symbol>bracket</group-symbol>'
        self.assertEqual(raw.find(match) > 0, True)
        # TODO: order of attributes is not assured; allow for any order.
        match = '<part-group number="1" type="start">'
        self.assertEqual(raw.find(match) > 0, True)
        match = '<part-group number="1" type="stop"/>'
        self.assertEqual(raw.find(match) > 0, True)
        match = '<part-group number="2" type="start">'
        self.assertEqual(raw.find(match) > 0, True)
        match = '<part-group number="2" type="stop"/>'
        self.assertEqual(raw.find(match) > 0, True)
예제 #6
0
 def testJoinPartStaffsC(self):
     '''
     First PartStaff longer than second
     '''
     from music21 import layout
     from music21 import note
     s = stream.Score()
     ps1 = stream.PartStaff()
     ps1.repeatAppend(note.Note(), 8)
     ps1.makeNotation(inPlace=True)  # makeNotation to freeze notation
     s.insert(0, ps1)
     ps2 = stream.PartStaff()
     ps2.repeatAppend(note.Note(), 4)
     ps2.makeNotation(inPlace=True)  # makeNotation to freeze notation
     s.insert(0, ps2)
     s.insert(0, layout.StaffGroup([ps1, ps2]))
     root = self.getET(s)
     measures = root.findall('.//measure')
     notes = root.findall('.//note')
     self.assertEqual(len(measures), 2)
     self.assertEqual(len(notes), 12)
예제 #7
0
    def testMeterChanges(self):
        from music21 import layout
        from music21 import meter
        from music21 import note

        ps1 = stream.PartStaff()
        ps2 = stream.PartStaff()
        sg = layout.StaffGroup([ps1, ps2])
        s = stream.Score([ps1, ps2, sg])
        for ps in ps1, ps2:
            ps.insert(0, meter.TimeSignature('3/1'))
            ps.repeatAppend(note.Note(type='whole'), 6)
            ps.makeNotation(inPlace=True)  # makes measures
            ps[stream.Measure][1].insert(meter.TimeSignature('4/1'))

        root = self.getET(s)
        # Just two <attributes> tags, a 3/1 in measure 1 and a 4/1 in measure 2
        self.assertEqual(len(root.findall('part/measure/attributes/time')), 2)

        # Edge cases -- no expectation of correctness, just don't crash
        ps1[stream.Measure].last().number = 0  # was measure 2
        root = self.getET(s)
        self.assertEqual(len(root.findall('part/measure/attributes/time')), 3)
예제 #8
0
 def testJoinPartStaffsD(self):
     '''
     Same example as testJoinPartStaffsC but switch the hands:
     second PartStaff longer than first
     '''
     from music21 import layout
     from music21 import note
     s = stream.Score()
     ps1 = stream.PartStaff()
     ps1.repeatAppend(note.Note(), 8)
     ps1.makeNotation(inPlace=True)  # makeNotation to freeze notation
     ps2 = stream.PartStaff()
     ps2.repeatAppend(note.Note(), 4)
     ps2.makeNotation(inPlace=True)  # makeNotation to freeze notation
     s.insert(0, ps2)
     s.insert(0, ps1)
     s.insert(0, layout.StaffGroup([ps2, ps1]))
     root = self.getET(s)
     measures = root.findall('.//measure')
     notes = root.findall('.//note')
     # from music21.musicxml.helpers import dump
     # dump(root)
     self.assertEqual(len(measures), 2)
     self.assertEqual(len(notes), 12)
예제 #9
0
    cello.remove(n)
cello.makeMeasures(inPlace=True)

# --------------------------------------------------------------------------------------------------
# Build final score
# --------------------------------------------------------------------------------------------------

piano_right_hand = tools.convert_stream(piano_right_hand, stream.Part)
piano_left_hand = tools.convert_stream(piano_left_hand, stream.Part)
cello = tools.convert_stream(cello, stream.Part)
cello.partName = "Vc."
score = tools.merge_streams(
    cello, piano_right_hand, piano_left_hand, stream_class=stream.Score
)
piano_staff_group = layout.StaffGroup(
    [piano_right_hand, piano_left_hand],
    name="Piano",
    abbreviation="Pno.",
    symbol="brace",
)
score.insert(0, piano_staff_group)
score.metadata = metadata.Metadata()
score.metadata.title = "Liturgie de Cristal"
score.metadata.composer = "Olivier Messiaen"

# --------------------------------------------------------------------------------------------------
# Output xml file and show score in MuseScore
# --------------------------------------------------------------------------------------------------
score.write(fp="olivier_messiaen_quatuor.xml")
score.show()
def makeLiederExercise(score,
                       leaveRestBars: bool = True,
                       quarterLengthOfRest=2,
                       leaveBassLine: bool = False,
                       addition=None,  # Options: 'transferTune' and 'chordHints'
                       quarterLength=1,
                       writeFile: bool = False,
                       outPath: str = '~/Desktop/',
                       title: str = ''):
    """
    Removes the piano part of an input song to create an exercise.

    Optionally leaves in:
    1. the piano part for bars with a vocal part rest of a specified, minimum length;
    2. the bass line (piano LH);

    Optionally then adds one of the following to the piano RH as a draft solution:
    A. the vocal melody (notes and rests);
    B. new chords based on leaps in the vocal line
    within the harmonic rhythm (rate) specified by quarterLength variable.
    """

    score = deepcopy(score)

    # Bassline line Y / N. i.e. Select both piano parts, or just RH.
    parts = [1]  # For cutting out RH (partNumber[1])
    if not leaveBassLine:
        parts.append(2)  # For also cutting out LH

    topPart = score.parts[0]

    NumMeasures = len(score.parts[0].getElementsByClass('Measure'))

    # Find vocal rests
    restBars = []  # Remains empty if not leaveRestBars
    if leaveRestBars:
        for listIndex in range(0, NumMeasures - 1):  # NB list is 0 to N; measures is 1 to (N-1)
            count = 0
            thisMeasure = topPart.getElementsByClass('Measure')[listIndex]  # NB Above
            for item in thisMeasure.recurse().notesAndRests:
                if item.isRest:
                    count += item.quarterLength
                    if count >= thisMeasure.quarterLength:  # Special case for anacrustic pieces.
                        restBars.append(listIndex + 1)  # = measure number
                        break  # No need to look at this measure any further
                    elif count >= quarterLengthOfRest:
                        restBars.append(listIndex + 1)  # = measure number
                        break  # No need to look at this measure any further

    measuresToDo = [x for x in range(1, NumMeasures) if x not in restBars]

    # Removals
    for measureNumber in measuresToDo:
        for partNo in parts:
            for x in score.parts[partNo].measure(measureNumber).recurse().notesAndRests:
                score.parts[partNo].remove(x, recurse=True)

    # Additions
    validAdditions = [None, 'transferTune', 'chordHints']
    if addition not in validAdditions:
        raise ValueError(f'Invalid addition type: must be one of {validAdditions}.')
    elif addition == 'transferTune':
        tempScore = transferClefs(score, measuresToDo)
        score = transferTune(tempScore, measuresToDo)
    elif addition == 'chordHints':
        tempScore = transferClefs(score, measuresToDo)
        score = addChords(tempScore, quarterLength=quarterLength)

    # Scrub lyrics inherited into piano part
    for item in score.parts[1].recurse().notesAndRests:
        if item.lyric:
            item.lyric = None

    if not title:
        title = score.metadata.title

    staffGrouping = layout.StaffGroup([score.parts[1], score.parts[2]],
                                      name='Piano',
                                      abbreviation='Pno.',
                                      symbol='brace')
    staffGrouping.barTogether = 'Mensurstrich'
    score.insert(0, staffGrouping)

    if writeFile:
        score.write('mxl', fp=f'{outPath}Exercise-{title}.mxl')

    return score