def test_first_measures_of_josquin(self): # first three measures of highest part the_settings = LilyPondSettings() the_score = converter.parse('test_corpus/Jos2308.krn') actual = stream_to_lily(the_score.parts[0][:12], the_settings) actual = actual[8:] # remove the randomized part name expect = u""" = { \t\\clef treble \t\\key f \\major \t\\time 2/1 \tg'1 d''1 | \tr1 g'1 | \td''1 r1 | } """ self.assertEqual(actual, expect)
def test_first_measures_of_bach(self): # first two measures of soprano part the_settings = LilyPondSettings() the_score = converter.parse('test_corpus/bwv77.mxl') actual = stream_to_lily(the_score.parts[0][:3], the_settings) actual = actual[8:] # remove the randomized part name expect = u""" = { \t%% Soprano \t\\set Staff.instrumentName = \\markup{ "Soprano" } \t\\set Staff.shortInstrumentName = \\markup{ "Sop." } \t\\partial 4 \t\\clef treble \t\\key b \\minor \t\\time 4/4 \te'8 fis'8 | \tg'4 a'4 b'4 a'4 | } """ self.assertEqual(actual, expect)
def process_score(the_score, the_settings=None): """ Convert an entire :class:`music21.stream.Stream` object, nominally a :class:`Score`, into a unicode string for output as a LilyPond source file. :param the_score: The :class:`Stream` to output. This method works on any type of :class:`Stream`, but uses multiprocessing only for :class:`Score` objects. :type the_score: :class:`music21.stream.Stream` :param the_settings: An optional settings object that will be passed to all client functions. Use this object to modify runtime behaviour. :type the_settings: :class:`settings.LilyPondSettings` :returns: A string that holds an entire LilyPond source file (as complete as possible for the type of :class:`Stream` object provided). :rtype: ``unicode`` """ the_settings = settings.LilyPondSettings() if the_settings is None else the_settings if isinstance(the_score, stream.Score): # multiprocessing! return LilyMultiprocessor(the_score, the_settings).run() else: # not sure what to do here... guess we'll default to old style? # TODO: this won't work as-is return functions.stream_to_lily(the_score, the_settings)
def run(self): """ Process all the parts! Prepare a score! """ # Things Before Parts: # Our mark! // Version // Paper size post = [u'%% LilyPond output from music21 via "outputlilypond"\n' u'\\version "%s"\n' u'\n' u'\\paper {\n' u'\t#(set-paper-size "%s")\n' u'\t#(define left-margin (* 1.5 cm))\n' # TODO: this should be a setting u'}\n\n' % (self._setts.get_property('lilypond_version'), self._setts.get_property('paper_size'))] # Parts: # Initialize the length of finished "parts" (maybe they're other things, too, like Metadata # or whatever... doesn't really matter). self._finished_parts = [None for i in xrange(len(self._score))] self._setts._parts_in_this_score = [None for i in xrange(len(self._score))] # Go through the possible parts and see what we find. for i in xrange(len(self._score)): if isinstance(self._score[i], stream.Part): if hasattr(self._score[i], u'lily_analysis_voice') and \ self._score[i].lily_analysis_voice is True: self._setts._analysis_notation_parts.append(i) self._pool.apply_async(functions.stream_to_lily, (converter.freezeStr(self._score[i]), self._setts, i), callback=self.callback) else: self._finished_parts[i] = functions.stream_to_lily(self._score[i], self._setts) # Wait for the multiprocessing to finish self._pool.close() self._pool.join() del self._pool # Append the parts to the score we're building. In the future, it'll be important to # re-arrange the parts if necessary, or maybe to filter things, so we'll keep everything # in this supposedly efficient loop. for i in xrange(len(self._finished_parts)): if self._finished_parts[i] != u'' and self._finished_parts[i] is not None: post.append(self._finished_parts[i] + u'\n') # Things After Parts # Output the \score{} block post.append(u'\\score {\n\t\\new StaffGroup\n\t<<\n') for each_part in self._setts._parts_in_this_score: if each_part is None: continue elif each_part in self._setts._analysis_notation_parts: post.extend([u'\t\t\\new VisAnnotation = "', each_part, u'" \\' + each_part + u'\n']) else: post.extend([u'\t\t\\new Staff = "', each_part, u'" \\' + each_part + u'\n']) post.append(u'\t>>\n') # Output the \layout{} block post.append(u'\t\\layout{\n') if self._setts.get_property('indent') is not None: post.extend([u'\t\tindent = ', self._setts.get_property('indent'), u'\n']) post.append("""\t\t% VisAnnotation Context \t\t\\context \t\t{ \t\t\t\\type "Engraver_group" \t\t\t\\name VisAnnotation \t\t\t\\alias Staff \t\t\t\\consists "Output_property_engraver" \t\t\t\\consists "Script_engraver" \t\t\t\\consists "Text_engraver" \t\t\t\\consists "Axis_group_engraver" \t\t\t\\consists "Instrument_name_engraver" \t\t} \t\t% End VisAnnotation Context \t\t \t\t% Modify "StaffGroup" context to accept VisAnnotation context. \t\t\\context \t\t{ \t\t\t\\StaffGroup \t\t\t\\accepts VisAnnotation \t\t} \t}\n}\n """) self._final_result = u''.join(post) # Return the "finished score" return self._final_result