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
}
}
}
