Hello folks, The recent posts on this subject show there is interest in the matter. Sorry for the length of this one... My work was initially homed by Grame’s libmusicxml2 library as an example of what it could be used for, in the lilypond branch at https://github.com/grame-cncm/libmusicxml/tree/lilypond. Dom Fober, the author and maintainer of libmusicxml2, and I decided some time ago to separate things for practical reasons, and I now push to the GitHub repository I created at https://github.com/jacques-menu/musicformats. I’m currenly finalizing version 1.0.0, which explains why the test and master branches are not up to date - only the dev branch is currently. The musicformats library is structured along the lines shown at page 16 in https://github.com/jacques-menu/musicformats/blob/dev/doc/maintainersGuideToMusicFormats/maintainersGuideToMusicformats.pdf (the users’s guide is not yet ‘usable’, sorry). The central component of musicformats is MSR (Music Score Representation), from which various formats can be obtained. In this picture, we see that we could create MusicXML output from within the LilyPond implementation going along the LilyPond - LPSR - MSR - MXSR - MusicMXL path. The missing part would be the creation of an LPSR (LilyPond Score Representation), the others already exist. As an example, the LPSR representation and LilyPond output produced by: xml2ly basic/HelloWorld.xml -display-lpsr > LPSR_contents.txt 2>&1 are in the attached LPSR_contents.txt file. The resulting score is: |
Jean and I have had discussions as to how the export to MusicXML could be tackled on the LilyPond side, but nothing concrete yet. Some of the information needed is readily accessible inside LilyPond, but grabbing the remaining part is not easy. The musicformats repository contains examples using the library to create scores in C++ applications, among them: jacquesmenu@macmini: ~/musicformats-git-dev/files/musicxml > Mikrokosmos3Wandering -a What Mikrokosmos3Wandering does: This multi-pass generator creates a textual representation of Zoltán Kodály's Mikrokosmos III Wandering score. It performs various passes depending on the output generated. Other passes are performed according to the options, such as displaying views of the internal data or printing a summary of the score. The activity log and warning/error messages go to standard error. jacquesmenu@macmini: ~/musicformats-git-dev/files/musicxml > Mikrokosmos3Wandering -apropos generate --- Help for atom "generate" in subgroup "Generated output" -generate, -gen GENERATED_OUTPUT_KIND Generate GENERATED_OUTPUT_KIND code to the output. The 5 generated output kinds available are: braille, guido, lilypond, midi and musicxml. The default is 'LilyPond output'. (midi output is actually not yet available, though) For example, one can run: jacquesmenu@macmini: ~/musicformats-git-dev/files/musicxml > Mikrokosmos3Wandering -generate musicxml -o Mikrokosmos3Wandering.xml -trace=passes %-------------------------------------------------------------- Pass 1: Creating the MSR score with the functions %-------------------------------------------------------------- *** MusicXML warning *** :91: The staffMeasuresSlicesSequence of staff "Part_OnlyPart_Staff_One" is null *** MusicXML warning *** :91: The staffMeasuresSlicesSequence of staff "Part_OnlyPart_Staff_Two" is null %-------------------------------------------------------------- Pass 2: Convert the MSR score into a second MSR %-------------------------------------------------------------- %-------------------------------------------------------------- Pass 3: Translating the MSR into an MXSR %-------------------------------------------------------------- %-------------------------------------------------------------- Pass 4: Convert the MXSR into MusicXML text %-------------------------------------------------------------- Opening file 'Mikrokosmos3Wandering.xml' for writing Warning message(s) were issued for input line 91 This creates file Mikrokosmos3Wandering.xml, attached. The functionality of musicformats is available as API C++ functions. For example, conversion from MusicXML data to LilyPond, as used by xml2ly and Grame’s experimental web site at https://libmusicxml.grame.fr, is available through these functions: /*! \brief Converts a MusicXML representation to the LilyPond format. \param file a file name \param out the output stream \return an error code (\c musicFormatsError::k_NoError when success) */ EXP musicFormatsError musicxmlfile2lilypond ( const char *file, const optionsVector& options, std::ostream& out, std::ostream& err); /*! \brief Converts a MusicXML representation to the LilyPond format. \param fd a file descriptor \param out the output stream \return an error code (\c musicFormatsError::k_NoError when success) */ EXP musicFormatsError musicxmlfd2lilypond ( FILE* fd, const optionsVector& options, std::ostream& out, std::ostream& err); /*! \brief Converts a MusicXML representation to the LilyPond format. \param buffer a string containing MusicXML code \param out the output stream \return an error code (\c musicFormatsError::k_NoError when success) */ EXP musicFormatsError musicxmlstring2lilypond ( const char *buffer, const optionsVector& options, std::ostream& out, std::ostream& err); I’m no Python nor Scheme developper, but I guess this can be used with suitable interfaces from applications written in these languages. I’ll be happy to collaborate to using musicformats to export from within LilyPond if such an attempt is done. HTH! JM |
Mikrokosmos3Wandering.xml
Description: XML document
%-------------------------------------------------------------- Optional pass: displaying the LPSR as text %--------------------------------------------------------------
LPSR Score MSR score summary partGroupsListSize : 1 fScoreNumberOfMeasures : 0 fScoreInstrumentNamesMaxLength : 0 fScoreInstrumentAbbreviationsMaxLength : 0 fInhibitGraceNotesGroupsBeforeBrowsing : false fInhibitGraceNotesGroupsAfterBrowsing : false fInhibitMeasureRepeatReplicasBrowsing : false fInhibitFullMeasureRestsBrowsing : false Part groups list: Part_POne (partID "P1", partName "Music") [Identification fIdentificationWorkTitle : "Hello World!" ] [PartGroup "PartGroup_1 ('0', fPartGroupName "Implicit")" (1 part) fPartGroupName : "Implicit" fPartGroupAbbrevation : "Impl." fPartGroupSymbolDefaultX : 0 fPartGroupSymbolKind : "kPartGroupSymbolNone" fPartGroupImplicit : kPartGroupImplicitYes fPartGroupBarLine : kPartGroupBarLineYes [Part Part_POne (1 staff, 1 measure) fPartID : "P1" fPartMsrName : "Part_POne" fPartName : "Music" fPartAbsoluteNumber : 3 fPartNameDisplayText : "" fPartAbbrevation : "" fPartAbbreviationDisplayText : "" fPartInstrumentName : "" fPartInstrumentAbbreviation : "" fPartNumberOfMeasures : 1 fPartContainsFullMeasureRests : false fPartCurrentPositionInMeasure : 0/1 fPartAllStavesList : [kStaffKindRegular staff "Part_POne_Staff_One", staff number '1', (1 voice) fStaffInstrumentName: "" ] ] ] TongueSchemeFunctionIsNeeded : false EditorialAccidentalSchemeFunctionIsNeeded : false Header HeaderIdentification: [Identification fIdentificationWorkTitle : "Hello World!" ] lilypondTitle : Hello World! Paper indent : none shortIndent : none pageCount : -1 systemCount : -1 Layout layoutGlobalStaffSize : 20 Voices & Stanzas [Regular voice "Part_POne_Staff_One_Voice_One", fVoiceNumber '1, line 34 (0 harmony, 0 figured bass, 1 actual note, 0 rest, 0 skip, 0 stanza) fVoiceStaffUpLink : Part_POne_Staff_One fVoiceCurrentMeasureNumber : "" fVoiceCurrentMeasureOrdinalNumber : 1 fVoiceCurrentMeasurePuristNumber : 2 fRegularVoiceStaffSequentialNumber : fWholeNotesSinceLastRegularMeasureEnd : 0/1 fCurrentVoiceRepeatPhaseKind : kVoiceRepeatPhaseNone fVoiceFirstClef : [Clef fClefKind: kClefTreble, fClefStaffNumber: 0, line 28] fVoiceCurrentClef : [Clef fClefKind: kClefTreble, fClefStaffNumber: 0, line 28] fVoiceCurrentKey : [Key, kKeyTraditional, c ***k_NoMode***, line 21 ] fVoiceCurrentTimeSignature : [Time, line 24 timeSignatureSymbolKind : kTimeSignatureSymbolNone timeIsCompound : false timeSignatureItemsVector.size() : 1 item fTimeSignatureItemsVector : TimeSignatureItem 4/4, line 26 ] fRegularVoiceHarmoniesVoiceForwardLink : none fRegularVoiceFiguredBassVoiceForwardLink : none fVoiceShortestNoteDuration : 1/1 fVoiceShortestNoteTupletFactor : TupletFactor fTupletActualNotes : 1 fTupletNormalNotes : 1 fVoiceHasBeenFinalized : true fCurrentPositionInVoice : 0/1 fCurrentMomentInVoice : Moment fWrittenPositionInMeseasure : 0/1 fSoundingRelativeOffset : 0/1 fMusicHasBeenInsertedInVoice : true fVoiceContainsFullMeasureRests : false fVoiceContainsMeasureRepeats : false fVoiceFirstSegment : '1' fVoiceLastAppendedMeasure : '[Measure 1, kMeasureKindRegular, Part_POne_Staff_One_Voice_One, 8 elements, line 17]' fVoiceFirstMeasure : '[Measure 1, kMeasureKindRegular, Part_POne_Staff_One_Voice_One, 8 elements, line 17]' fVoiceFirstNonGraceNote : [kNoteRegularInMeasure c1, o4, voice: 1, staff: 1, line 34] fVoiceLastAppendedNote : [kNoteRegularInMeasure c1, o4, voice: 1, staff: 1, line 34] fVoiceMeasuresFlatList : empty fVoiceInitialElementsList : 1 elements [Segment '1, fSegmentDebugNumber: '3', 1 measure, line 34 fSegmentVoiceUpLink : "Part_POne_Staff_One_Voice_One" [Measure '1', kMeasureKindRegular, 8 elements, line 17 fCurrentMeasureWholeNotesDuration : 1/1 fullMeasureWholeNotesDuration : 1/1 fMeasureOrdinalNumberInVoice : 1 fMeasurePuristNumber : 1 fMeasureDebugNumber : 3 fMeasureEndRegularKind : kMeasureEndRegularYes fMeasureRepeatContextKind : kMeasureRepeatContextNone fMeasureFirstInVoice : true fMeasureFirstInSegmentKind : kMeasureFirstInSegmentKindYes segmentUpLink : [Segment '1, fSegmentDebugNumber: '3', voice: "Part_POne_Staff_One_Voice_One"] voiceCurrentClef : [Clef fClefKind: kClefTreble, fClefStaffNumber: 0, line 28] voiceCurrentKey : [Key, kKeyTraditional, c ***k_NoMode***, line 21 ] voiceCurrentTimeSignature : [Time, line 24 timeSignatureSymbolKind : kTimeSignatureSymbolNone timeIsCompound : false timeSignatureItemsVector.size() : 1 item fTimeSignatureItemsVector : TimeSignatureItem 4/4, line 26 ] fMeasureLongestNote : none fMeasureContainsMusic : true fMeasureKindAndPuristNumberHaveBeenDetermined : true fMeasurePositionInVoice : 0/1 fMeasureMomentInVoice : [Moment writtenPositionInMeseasure: 0/1, soundingRelativeOffset: 0/1] fMeasureHasBeenFinalized : true fMeasureFinalizationContext : finalizeMeasureClone() fMeasureIsAFullMeasureRest : false nextMeasureNumber : "" fMeasureElementsList : 8 elements [Key, kKeyTraditional, c ***k_NoMode***, line 21 ] [Time, line 24 timeSignatureSymbolKind : kTimeSignatureSymbolNone timeIsCompound : false timeSignatureItemsVector.size() : 1 item fTimeSignatureItemsVector : TimeSignatureItem 4/4, line 26 ] [Clef fClefKind: kClefTreble, fClefStaffNumber: 0, line 28] [kNoteRegularInMeasure c1, o4, voice: 1, staff: 1, line 34] fMeasureElementMeasureNumber : 1 measureElementPositionInMeasure : 0/1 measureElementPositionInVoice : -987/1 measureElementMomentInVoice : Moment fWrittenPositionInMeseasure : -987/1 fSoundingRelativeOffset : -987/1 fNoteDirectMeasureUpLink : [Measure 1, kMeasureKindRegular, Part_POne_Staff_One_Voice_One, 8 elements, line 17] fNoteDirectChordUpLink : : none fNoteDirectGraceNotesGroupUpLink : : none fNoteDirectTupletUpLink : : none fMeasureElementSoundingWholeNotes : 1/1 fNoteDisplayWholeNotes : 1/1 measureFullLength : 1/1 fNoteBelongsToAChord : false fNoteBelongsToATuplet : false fNoteOccupiesAFullMeasure : true fNoteBelongsToAFullMeasureRests : false fNoteFullMeasureRestsSequenceNumber : -1 fNotePrintObjectKind : ***kPrintObjectNone*** fNoteHeadKind : kNoteHeadNormal fNoteHeadFilledKind : kNoteHeadFilledYes fNoteHeadParenthesesKind : kNoteHeadParenthesesNo fNoteAccidentalKind : accidentalNone fNoteEditorialAccidentalKind : kEditorialAccidentalNo fNoteCautionaryAccidentalKind : kCautionaryAccidentalNo fNoteIsACueNoteKind : kNoteIsACueNoteNo getNoteIsAGraceNote : false fNoteIsStemless : false fNoteIsAChordsFirstMemberNote : false fNoteIsFirstNoteInADoubleTremolo : false fNoteIsSecondNoteInADoubleTremolo : false noteSoundingWholeNotesAsMsrString : "1" noteDisplayWholeNotesAsMsrString : "1" noteGraphicDurationAsMsrString : "kWhole" fNoteAlphaRGBColor : color: colorRGB = "", colorAlpha = "" fNoteAlphaRGBColorHasBenSet : false fNoteSoloNoteOrRestInVoiceKind : kSoloNoteOrRestInVoiceNo fNoteSoloNoteOrRestInStaffKind : kSoloNoteOrRestInStaffNo fNoteBeams : none fNoteArticulations : none fNoteSpanners : none fNoteTechnicals : none fNoteTechnicalWithIntegers : none fNoteTechnicalWithFloats : none fNoteTechnicalWithStrings : none fNoteOrnaments : none fNoteGlissandos : none fNoteSlides : none fNoteSingleTremolo : none fNoteDynamics : none fNoteOtherDynamics : none fNoteWords : none fNoteSlurs : none fNoteLigatures : none fNotePedals : none fNoteSlashes : none fNoteWedges : none fNoteSegnos : none fNoteDalSegnos : none fNoteCodas : none fNoteEyeGlasses : none fNoteDamps : none fNoteDampAlls : none fNoteScordaturas : none fNoteHarmoniesList : none fNoteFiguredBassElementsList : none fNoteSyllables : none fNoteGraceNotesGroupAfter : none BarCheck, nextBarOriginalNumber = "", nextBarPuristNumber = "2", line 17 BarNumberCheck, nextBarOriginalNumber = "", nextBarPuristNumber = "2" BarCheck, nextBarOriginalNumber = "", nextBarPuristNumber = "2", line 17 BarNumberCheck, nextBarOriginalNumber = "", nextBarPuristNumber = "2" fMeasureNotesFlatList: 0 note ] ] fVoiceLastSegment : null fVoiceStanzasMap : empty ] Book blocks BookBlock BookBlockElements ScoreBlock ParallelMusicBLock, 1 part group PartGroupBlock for partGroup "PartGroup_1 ('0', fPartGroupName "Implicit")", kPartGroupSymbolNone, 1 element PartBlock for part Part_POne (partID "P1", partName "Music"), 1 element partName = "Music" partAbbreviation = "" partBlockInstrumentName = "Music" partBlockShortInstrumentName = "" StaffBlock for staff "Part_POne_Staff_One" (kStaffKindRegular), 1 element (StaffBlockInstrumentName = "") (StaffBlockShortInstrumentName = "") UseVoiceCommand "Part_POne_Staff_One_Voice_One", 0 stanza Layout layoutGlobalStaffSize : 20 [MidiTempo midiTempoDuration = 16 midiTempoPerSecond = 360 ] %-------------------------------------------------------------- \version "2.22.0" % Pick your choice from the next two lines as needed %myBreak = { \break } myBreak = {} % Pick your choice from the next two lines as needed %myPageBreak = { \break } myPageBreak = {} \header { title = "Hello World!" workTitle = "Hello World!" title = "Hello World!" } \paper { } \layout { \context { \Score autoBeaming = ##f % to display tuplets brackets } \context { \Voice } } Part_POne_Staff_One_Voice_One = \absolute { \language "nederlands" \key c \major \numericTimeSignature \time 4/4 \clef "treble" c'1 | % 2 \barNumberCheck #2 | % 2 \barNumberCheck #2 } \book { \score { << \new Staff = "Part_POne_Staff_One" \with { } << \context Voice = "Part_POne_Staff_One_Voice_One" << \Part_POne_Staff_One_Voice_One >> >> >> \layout { \context { \Score autoBeaming = ##f % to display tuplets brackets } \context { \Voice } } \midi { \tempo 16 = 360 } } }