This file is indexed.

/usr/lib/python3/dist-packages/mmllib/musicxml.py is in python3-mmllib 0.3.0.post1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# ~*~ coding: utf-8 ~*~
#-
# Copyright © 2016
#       mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un‐
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said person’s immediate fault when using the work as intended.

""" Functions to convert an mmllib playlist into a MusicXML document """

from copy import deepcopy
from xml.dom import minidom

try:
    from math import gcd
except ImportError:
    def gcd(num_a, num_b):
        while num_b:
            (num_a, num_b) = (num_b, num_a % num_b)
        return num_a

from .mml import MML_NOTE2PITCH
from .parser import mml_file, mml_file_meta

## MusicXML note lengths, for display
_MUSICXML_NOTETYPES = {64: u'64th',
                       32: u'32nd',
                       16: u'16th',
                       8: u'eighth',
                       4: u'quarter',
                       2: u'half',
                       1: u'whole'}

def _overridden_play(res, bpm, art, note, length, dots, extra):
    """ Overridden function to pass to MML core

    This function is supposed to be passed as second argument
    to mml_file to collect the detailed data necessary for an
    export in the playlist.

    Same parameters as mmllib.mml.mml_play.
    """

    res.append((bpm, art, length, dots, extra))

def _create_xml_doc(meta, staves):
    """ Create a MusicXML document from collected data and metadata

    Exports metadata and staves as MusicXML document.

    meta - the metadata, as returned by mml_file_meta()
    staves - the collected tracks, as returned by mml_file() with _overridden_play

    Returns the MusicXML document as MiniDOM document.
    """

    # Use deep copies because we change and delete entries
    meta = deepcopy(meta)

    # create MusicXML document
    mdi = minidom.getDOMImplementation('')
    doc = mdi.createDocument(None, 'score-partwise',
                             mdi.createDocumentType('score-partwise',
                                                    '-//Recordare//DTD MusicXML 3.0 Partwise//EN',
                                                    'http://www.musicxml.org/dtds/partwise.dtd'))
    score = doc.documentElement
    score.setAttribute('version', '3.0')

    # carry over floppi.music-specific metadata
    if 'title' in meta:
        elem = doc.createElement('movement-title')
        elem.appendChild(doc.createTextNode(meta['title']))
        score.appendChild(elem)
        del meta['title']
    tmpel = doc.createElement('identification')
    if 'composer' in meta:
        elem = doc.createElement('creator')
        elem.setAttribute('type', 'composer')
        elem.appendChild(doc.createTextNode(meta['composer']))
        tmpel.appendChild(elem)
        del meta['composer']
    if 'lyrics' in meta:
        elem = doc.createElement('creator')
        elem.setAttribute('type', 'poet')
        elem.appendChild(doc.createTextNode(meta['lyrics']))
        tmpel.appendChild(elem)
        del meta['lyrics']
    if 'arranger' in meta:
        elem = doc.createElement('creator')
        elem.setAttribute('type', 'arranger')
        elem.appendChild(doc.createTextNode(meta['arranger']))
        tmpel.appendChild(elem)
        del meta['arranger']
    if 'translator' in meta:
        elem = doc.createElement('creator')
        elem.setAttribute('type', 'translator')
        elem.appendChild(doc.createTextNode(meta['translator']))
        tmpel.appendChild(elem)
        del meta['arranger']
    if 'artist' in meta:
        elem = doc.createElement('creator')
        elem.appendChild(doc.createTextNode(meta['artist']))
        tmpel.appendChild(elem)
        del meta['artist']
    if 'copyright' in meta:
        elem = doc.createElement('rights')
        elem.appendChild(doc.createTextNode(meta['copyright']))
        tmpel.appendChild(elem)
        del meta['copyright']
    tmpex = doc.createElement('encoding')
    if 'encoder' in meta:
        elem = doc.createElement('encoder')
        elem.appendChild(doc.createTextNode(meta['encoder']))
        tmpex.appendChild(elem)
        del meta['encoder']
    elem = doc.createElement('software')
    elem.appendChild(doc.createTextNode(u'MMLlib by Natureshadow, Creeparoo, and mirabilos'))
    tmpex.appendChild(elem)
    tmpel.appendChild(tmpex)
    if 'source' in meta:
        elem = doc.createElement('source')
        elem.appendChild(doc.createTextNode(meta['source']))
        tmpel.appendChild(elem)
        del meta['source']
    tmpex = doc.createElement('miscellaneous')
    meta["duration"] = int(meta["duration"])
    for tmp in sorted(meta):
        elem = doc.createElement('miscellaneous-field')
        elem.setAttribute('name', tmp)
        elem.appendChild(doc.createTextNode(str(meta[tmp])))
        tmpex.appendChild(elem)
    tmpel.appendChild(tmpex)
    score.appendChild(tmpel)

    # required metadata
    tmpel = doc.createElement('part-list')
    for trkno in range(1, len(staves) + 1):
        score_part = doc.createElement('score-part')
        score_part.setAttribute('id', 'P' + str(trkno))
        part_name = doc.createElement('part-name')
        part_name.appendChild(doc.createTextNode(u'Track ' + str(trkno)))
        score_part.appendChild(part_name)
        tmpel.appendChild(score_part)
    score.appendChild(tmpel)

    # figure out which duration to use
    notelens = 4
    for trkno in range(1, len(staves) + 1):
        for ply in staves[trkno - 1]:
            if isinstance(ply, tuple):
                dottedlen = ply[2]
                for tmp in range(0, ply[3]):
                    dottedlen *= 2
                # calculate lowest common multiple
                notelens = (notelens * dottedlen) // gcd(notelens, dottedlen)
    divisions = notelens // 4

    # add individual staves
    for trkno in range(1, len(staves) + 1):
        staff = staves[trkno - 1]
        trknode = doc.createElement('part')
        trknode.setAttribute('id', 'P' + str(trkno))

        # attribute node, once per part, located in the first bar
        tmpel = doc.createElement('attributes')
        elem = doc.createElement('divisions')
        elem.appendChild(doc.createTextNode(str(divisions)))
        tmpel.appendChild(elem)
        # use treble clef by default
        tmpex = doc.createElement('clef')
        elem = doc.createElement('sign')
        elem.appendChild(doc.createTextNode(u'G'))
        tmpex.appendChild(elem)
        elem = doc.createElement('line')
        elem.appendChild(doc.createTextNode(u'2'))
        tmpex.appendChild(elem)
        tmpel.appendChild(tmpex)

        # "current" bar node and number (first, here)
        barno = 1
        barnode = doc.createElement('measure')
        barnode.setAttribute('number', str(barno))
        barnode.appendChild(tmpel)
        # hack to never end on a bar line – end needs special love
        while len(staff) > 0 and staff[-1] == 1:
            staff.pop()

        # now iterate through the staff
        bpm = -1
        lastslur = None
        inslur = 0
        for ply in staff:
            # finish a bar?
            if ply == 1:
                trknode.appendChild(barnode)
                # force re-init on next note
                barnode = None
                continue

            # start a new bar?
            if barnode is None:
                barno += 1
                barnode = doc.createElement('measure')
                barnode.setAttribute('number', str(barno))

            # tempo change?
            if bpm != ply[0]:
                tmpel = doc.createElement('direction')
                tmpex = doc.createElement('metronome')
                elem = doc.createElement('beat-unit')
                elem.appendChild(doc.createTextNode(u'quarter'))
                tmpex.appendChild(elem)
                elem = doc.createElement('per-minute')
                elem.appendChild(doc.createTextNode(str(ply[0])))
                tmpex.appendChild(elem)
                elem = doc.createElement('direction-type')
                elem.appendChild(tmpex)
                tmpel.appendChild(elem)
                elem = doc.createElement('sound')
                elem.setAttribute('tempo', str(ply[0]))
                tmpel.appendChild(elem)
                barnode.appendChild(tmpel)

            # unpack and convert raw note to pitch (best guess)
            (bpm, art, length, ndots, extra) = ply
            if not isinstance(ply, tuple) and extra != -1:
                extra = MML_NOTE2PITCH[extra]

            # convert to MusicXML
            tmpel = doc.createElement('note')
            if extra == -1:
                tmpex = doc.createElement('rest')
            else:
                tmpex = doc.createElement('pitch')
                elem = doc.createElement('step')
                elem.appendChild(doc.createTextNode(str(extra[1])))
                tmpex.appendChild(elem)
                if extra[2] != u'♮':
                    elem = doc.createElement('alter')
                    if extra[2] == u'♭':
                        elem.appendChild(doc.createTextNode(u'-1'))
                    elif extra[2] == u'♯':
                        elem.appendChild(doc.createTextNode(u'1'))
                    tmpex.appendChild(elem)
                elem = doc.createElement('octave')
                elem.appendChild(doc.createTextNode(str(extra[0] + 2)))
                tmpex.appendChild(elem)
            tmpel.appendChild(tmpex)
            # if notelens calculation is correct, dottedlen is always integer
            dottedlen = notelens / length
            for tmp in range(0, ndots):
                dottedlen *= 1.5
            elem = doc.createElement('duration')
            elem.appendChild(doc.createTextNode(str(int(dottedlen))))
            tmpel.appendChild(elem)
            if length in _MUSICXML_NOTETYPES.keys():
                elem = doc.createElement('type')
                elem.appendChild(doc.createTextNode(_MUSICXML_NOTETYPES[length]))
                tmpel.appendChild(elem)
            # order is important!
            for tmp in range(0, ndots):
                tmpel.appendChild(doc.createElement('dot'))
            # articulation - is this a note (no pause)?
            if extra != -1:
                # not a pause then… ML started on the previous note?
                if inslur == 1:
                    # start Binde-/Haltebogen
                    elem = doc.createElement('slur')
                    elem.setAttribute('type', 'start')
                    lastslur.appendChild(elem)
                # ML while slur is active?
                if inslur > 0 and art == 'L':
                    # just cache this node and go on
                    lastslur = tmpel
                    inslur = 2
                # ML start?
                if inslur == 0 and art == 'L':
                    # cache notations node, filled in next note
                    lastslur = doc.createElement('notations')
                    tmpel.appendChild(lastslur)
                    inslur = 1
                # ML end on this note and/or MS? => common XML element
                if (inslur > 0 and art != 'L') or art == 'S':
                    tmpex = doc.createElement('notations')
                    tmpel.appendChild(tmpex)
                # ML end on this note?
                if inslur > 0 and art != 'L':
                    # end Binde-/Haltebogen
                    elem = doc.createElement('slur')
                    elem.setAttribute('type', 'stop')
                    tmpex.appendChild(elem)
                    lastslur = None
                    inslur = 0
                # MS for this note?
                if art == 'S':
                    # create staccato dot
                    elem = doc.createElement('articulations')
                    elem.appendChild(doc.createElement('staccato'))
                    tmpex.appendChild(elem)
                # MN does not need any extra handling
            else:
                # ML started on the previous note, detached legato
                if inslur == 1:
                    elem = doc.createElement('articulations')
                    elem.appendChild(doc.createElement('detached-legato'))
                    lastslur.appendChild(elem)
                    lastslur = None
                    inslur = 0
                # ML started before the previous note, end Binde-/Haltebogen
                if inslur == 2:
                    tmpex = doc.createElement('notations')
                    elem = doc.createElement('slur')
                    elem.setAttribute('type', 'stop')
                    tmpex.appendChild(elem)
                    lastslur.appendChild(tmpex)
                    lastslur = None
                    inslur = 0
            barnode.appendChild(tmpel)
        # any unfinished ML? detached legato or end Binde-/Haltebogen
        if inslur == 1:
            elem = doc.createElement('articulations')
            elem.appendChild(doc.createElement('detached-legato'))
            lastslur.appendChild(elem)
        if inslur == 2:
            tmpex = doc.createElement('notations')
            elem = doc.createElement('slur')
            elem.setAttribute('type', 'stop')
            tmpex.appendChild(elem)
            lastslur.appendChild(tmpex)
        # finish staff
        tmpex = doc.createElement('barline')
        elem = doc.createElement('bar-style')
        elem.appendChild(doc.createTextNode(u'light-heavy'))
        tmpex.appendChild(elem)
        barnode.appendChild(tmpex)
        trknode.appendChild(barnode)
        score.appendChild(trknode)

    return doc

def _create_xml_file(meta, staves):
    """ Export collected data and metadata as MusicXML

    Exports metadata and staves as MusicXML document,
    rendered as an XML file.

    The XML file is minified; if human inspection is required,
    pipe through “xmlstarlet fo”. Do n̲o̲t̲ use .toprettyxml() as
    it adds extra format-violating whitespace to text nodes!

     meta - the metadata, as returned by mml_file_meta()
     staves - the collected tracks, as returned by mml_file() with _overridden_play

    Returns the MusicXML document encoded as UTF-8 string
    """

    return _create_xml_doc(meta, staves).toxml("UTF-8")

def convert_mml_file(filename):
    """ Convert MML file to MusicXML

    Reads the MML file twice (once for metadata, once for data),
    converts it to MusicXML and renders it as XML file.

     filename - the MML file

    Returns the MusicXML document encoded as UTF-8 string.
    """

    meta = mml_file_meta(filename)
    staves = mml_file(filename, _overridden_play)
    return _create_xml_file(meta, staves)