@@ -1870,6 +1870,21 @@ def getNotesFromEvents(
18701870 # 'midiTrackToStream(): cannot find a note off for a note on', e])
18711871 return notes
18721872
1873+ def getLyricsFromEvents (
1874+ events : list [tuple [int , MidiEvent ]],
1875+ encoding_type : str = 'utf-8' ,
1876+ ) -> dict [int , str ]:
1877+ lyrics : dict [int , str ] = {}
1878+ for time , e in events :
1879+ if e .type == MetaEvents .LYRIC and isinstance (e .data , bytes ):
1880+ try :
1881+ lyrics [time ] = e .data .decode (encoding_type )
1882+ except UnicodeDecodeError :
1883+ warnings .warn (
1884+ f'Unable to decode lyrics from { e } ' ,
1885+ TranslateWarning )
1886+ return lyrics
1887+
18731888
18741889def getMetaEvents (
18751890 events : list [tuple [int , MidiEvent ]]
@@ -1936,6 +1951,7 @@ def midiTrackToStream(
19361951 conductorPart : stream .Part | None = None ,
19371952 isFirst : bool = False ,
19381953 quarterLengthDivisors : Sequence [int ] = (),
1954+ encoding_type : str = 'utf-8' ,
19391955 ** keywords
19401956) -> stream .Part :
19411957 # noinspection PyShadowingNames
@@ -2013,6 +2029,7 @@ def midiTrackToStream(
20132029 # need to build chords and notes
20142030 notes = getNotesFromEvents (events )
20152031 metaEvents = getMetaEvents (events )
2032+ lyricsDict = getLyricsFromEvents (events , encoding_type = encoding_type )
20162033
20172034 # first create meta events
20182035 for tick , obj in metaEvents :
@@ -2089,9 +2106,12 @@ def midiTrackToStream(
20892106 if chordSub :
20902107 # composite.append(chordSub)
20912108 c = midiEventsToChord (chordSub , ticksPerQuarter )
2092- o = notes [i ][0 ][0 ] / ticksPerQuarter
2093- c .editorial .midiTickStart = notes [i ][0 ][0 ]
2094-
2109+ tickStart = notes [i ][0 ][0 ]
2110+ o = tickStart / ticksPerQuarter
2111+ c .editorial .midiTickStart = tickStart
2112+ lyric = lyricsDict .get (tickStart )
2113+ if (lyric is not None ):
2114+ c .lyric = lyric
20952115 s .coreInsert (o , c )
20962116 # iSkip = len(chordSub) # amount of accumulated chords
20972117 chordSub = []
@@ -2100,8 +2120,12 @@ def midiTrackToStream(
21002120 n : note .NotRest = midiEventsToNote (notes [i ], ticksPerQuarter )
21012121 # the time is the first value in the first pair
21022122 # need to round, as floating point error is likely
2103- o = notes [i ][0 ][0 ] / ticksPerQuarter
2104- n .editorial .midiTickStart = notes [i ][0 ][0 ]
2123+ tickStart = notes [i ][0 ][0 ]
2124+ o = tickStart / ticksPerQuarter
2125+ n .editorial .midiTickStart = tickStart
2126+ lyric = lyricsDict .get (tickStart )
2127+ if (lyric is not None ):
2128+ n .lyric = lyric
21052129
21062130 s .coreInsert (o , n )
21072131 # iSkip = 1
@@ -2112,8 +2136,12 @@ def midiTrackToStream(
21122136 singleN : note .NotRest = midiEventsToNote (notes [0 ], ticksPerQuarter )
21132137 # the time is the first value in the first pair
21142138 # need to round, as floating point error is likely
2115- o = notes [0 ][0 ][0 ] / ticksPerQuarter
2116- singleN .editorial .midiTickStart = notes [0 ][0 ][0 ]
2139+ tickStart = notes [i ][0 ][0 ]
2140+ o = tickStart / ticksPerQuarter
2141+ singleN .editorial .midiTickStart = tickStart
2142+ lyric = lyricsDict .get (tickStart )
2143+ if (lyric is not None ):
2144+ singleN .lyric = lyric
21172145 s .coreInsert (o , singleN )
21182146
21192147 s .sort (force = True ) # will also run coreElementsChanged()
0 commit comments