MMLlib-0.3.0.post1/0000755000175000017500000000000013043255733013264 5ustar niknik00000000000000MMLlib-0.3.0.post1/MMLlib.egg-info/0000755000175000017500000000000013043255733016072 5ustar niknik00000000000000MMLlib-0.3.0.post1/MMLlib.egg-info/PKG-INFO0000644000175000017500000001705213043255733017174 0ustar niknik00000000000000Metadata-Version: 1.1 Name: MMLlib Version: 0.3.0.post1 Summary: Modern library for handling Music Macro Language Home-page: http://github.com/Natureshadow/mmllib Author: Dominik George Author-email: nik@naturalnet.de License: MirOS Description: MMLlib - Modern library for handling Music Macro Language ========================================================= About ----- MMLlib is a pure Python implementation of functionality related to the `Music Macro Language `__ as implemented by Microsoft® GW-BASIC® and compatibles, which is its most common form, also implemented by the PC speaker driver in Linux and `BSD `__, with a number of extensions and changes: - ``|`` denotes a bar line, ignored by the Linux/BSD driver - support for multiple (parallel) instrument tracks - a per-file header with work metadata (optional) - lines starting with a ``#`` are comments - the alias ``~`` is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - a duration estimate and the number of tracks is added to the metadata - return a list of tuples (frequency, duration) to play (in a threaded interpreter, such as Floppi-Music) - bar lines are indicated separately as integer ``1`` - return an event-oriented list of measures / bars across all tracks; each measure has a duration and a list of time-fixed tone on/off events; facilitates playing in non-threaded interpreters and, possibly, MIDI export - export (extended) MML to MusicXML - which can be imported by e.g. `MuseScore `__ to… - double-check the MML score for mistakes in a visual representation - play, arrange, etc. the music - beautify the score to print it as sheet music - export into other formats or share on their website - check tracks for synchronisation error - missing tracks, i.e. with 0 bars - missing bars (can only be detected at the end, of course) - missing notes within a bar, relative to other tracks Examples -------- Some example extended MML files are contained within the examples/ directory, in lieu of better documentation for the extended format, in addition to the `MML format documentation `__. Projects using MMLlib --------------------- ::Floppi-Music:: `Floppi-Music `__ has MML as input format for floppy drive music on Raspberry Pi and uses MMLlib for processing. Floppi-Music is also the origin of MMLlib. Description of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and https://www.mirbsd.org/man4/spkr . Symbols of MML -------------- :A-G[#,+,-][length]: A-G are notes. # or + following a note produces a sharp; - produces a flat. :L(n): Sets the length of each note. L4 is a quarter note, L1 is a whole note, and so on. n may be from 1 to 64. Length may also follow the note to change the length for that note only. A16 is equivalent to L16A. Default is L4. :ML: Music legato. Each note plays the full period set by L. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). :MS: Music staccato. Each note plays three-quarters of the time determined by L. :N(n): Play note n. n may range from 0 to 84. In the 7 possible octaves, there are 84 notes. n set to 0 (or omitted) indicates a rest. :O(n): Octave 0 sets the current octave. There are 7 octaves (0 through 6). Default is 4. Middle C is at the beginning of octave 2. :P(n): Pause. n may range from 1-64; the current L value is used if omitted. :T(n): Tempo. T sets the number of L4s in a minute. n may range from 32-255. Default is 120. :. (period): A period after a note increases the playing time of the note by 3/2 times the period determined by L (length of note) times T (tempo). Multiple periods can appear after a note, and the playing time is scaled accordingly. For example, A. will cause the note A to play one and half times the playing time determined by L (length of the note) times T (the tempo); two periods placed after A (A..) will cause the note to be played at 9/4 times its ascribed value; an A with three periods (A...) at 27/8, etc. Periods may also appear after a P (pause), and increase the pause length as described above. :>: A greater-than symbol raises the current octave by one. :<: A less-than symbol lowers the current octave by one. :\|: Optionally used as a synchronisation mark for multi-track music. This is a proprietary extension in the Floppi-Music project. Comments -------- Lines starting with # are comments. At the beginning of the file, comments may be used to encode metadata. This is yet to be specified. The current implementation parses key/value pairs separated by a colon and a space, strips both key and value, lower-cases the key and adds it to a dictionary. The MusicXML export currently specifically recognises these keys: - Title, Copyright, Encoder (person), Source - Composer, Lyrics, Arranger, Translator *xor* Artist (deprecated, only one) Any other key is treated as miscellaneous field. Voices ------ The voices of a song are interleaved. They are grouped per notation system, and the notation systems are seperated by empty lines. Changelog for MMLlib ==================== 0.3 --- - Enable Python 3 compatibility. - Add new mmllint script. - Include example MML songs. - Add first parts of test suite. - Some bugfixes. 0.2 --- - Add mml2musicxml script. 0.1 --- - Separate from Floppi-Music. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Artistic Software Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion Classifier: Topic :: Software Development :: Libraries :: Python Modules MMLlib-0.3.0.post1/MMLlib.egg-info/SOURCES.txt0000644000175000017500000000104113043255733017752 0ustar niknik00000000000000CHANGELOG.rst LICENCE MANIFEST.in MMLFILE.rst README.rst setup.py MMLlib.egg-info/PKG-INFO MMLlib.egg-info/SOURCES.txt MMLlib.egg-info/dependency_links.txt MMLlib.egg-info/entry_points.txt MMLlib.egg-info/not-zip-safe MMLlib.egg-info/top_level.txt examples/alle_meine_entchen.mml examples/bruder_jakob.mml examples/loreley.mml mmllib/__init__.py mmllib/cmds.py mmllib/mml.py mmllib/musicxml.py mmllib/parser.py mmllib/playlist.py test/__init__.py test/test_musicxml.py test/test_parser.py test/test_playlist.py test/util.py test/data/loreley.xmlMMLlib-0.3.0.post1/MMLlib.egg-info/dependency_links.txt0000644000175000017500000000000113043255733022140 0ustar niknik00000000000000 MMLlib-0.3.0.post1/MMLlib.egg-info/entry_points.txt0000644000175000017500000000013113043255733021363 0ustar niknik00000000000000[console_scripts] mml2musicxml = mmllib.cmds:mml2musicxml mmllint = mmllib.cmds:mmllint MMLlib-0.3.0.post1/MMLlib.egg-info/not-zip-safe0000644000175000017500000000000113043254425020315 0ustar niknik00000000000000 MMLlib-0.3.0.post1/MMLlib.egg-info/top_level.txt0000644000175000017500000000000713043255733020621 0ustar niknik00000000000000mmllib MMLlib-0.3.0.post1/examples/0000755000175000017500000000000013043255733015102 5ustar niknik00000000000000MMLlib-0.3.0.post1/examples/alle_meine_entchen.mml0000644000175000017500000000025513043200724021377 0ustar niknik00000000000000# Title: Alle meine Entchen # Copyright: MML scores (c) 2013, Dominik George , The MirOS Licence o2 c d e f g2 g2 aaaa g1 aaaa g1 ffff e2 e2 dddd c1 MMLlib-0.3.0.post1/examples/bruder_jakob.mml0000644000175000017500000000214213043200724020227 0ustar niknik00000000000000# Title: Bruder Jakob # Copyright: MML scores (c) 2015, Dominik George , The MirOS Licence o2l4t120 f g a f | f g a f | a b- >c2 | c2 | o2l4t120 p1 | p1 | f g a f | f g a f | o2l4t120 p1 | p1 | p1 | p1 | o2l4t120 p1 | p1 | p1 | p1 | l8 c d c c d c c2 | c2 | f g a f | f g a f | p1 | p1 | g c f2 | g c f2 | p1 | l8 c d c c d c c2 | c2 | l8 c d c c2 | p1 | p1 | p1 | p1 | p1 || g c f2 | p1 | p1 | p1 | p1 || l8 >c d c c2 | l8 c d c c d c c< mnb a | g4. f4 f | # ⒈ Ich weiß nicht, was soll es be- deu- ten, daß # ⒉ Die schön-______ ste Jung-___frau si- tzet dort # ⒊ Den Schif-______ fer im klei-nen Schif- fe er- o2t76l8 p | mle. mnf16 e mlf mnf f | e4. d4 d | o1t76l8 p | p2. | p2. | o1t76l8 p | mlc16g16>cdcf16amnf | e. e16 e mldmnc d | mle4. mnep g | mlg. mna16 g ml>c< mnb a | # ich______ so trau-_rig bin?_____ Ein Mähr- chen aus al-____ ten # o-_______ ben wun-_der- bar,_____ ihr gold’- nes Ge- schmei-_ de # greift es mit wil-_dem Weh,_____ er schaut nicht die Fel-___ sen- c. c16 c | mlc4. mncp p | mle. mnf16 e mlf mnf f | p4. g4 g | mlg ppmnp4 p | p2. | mle16gmne | mlcp | mlc16g16>cdcf16amnf | g c | mlc mnc16< b b b b | >d4. c4 c | c<< | p p p >d p p | g4. e4 e | d4 d d mng d mng< | mlg>dmngemna | mldmnb p4 f# | # The next measure missed an o1g as last eighth, to fit on four tracks # (but it’s doubled by the o2g in the vocals, so it’s okay musically) mlg4.mng4 g | g. a16 g>mlccdc>e. f16 e mlfmn f f | mld4.mnd4> f< | p2. | mlb>defe mnd< | mlc16g16>cd e d4 d | c. c16c mlc4.mncp || # fun-______ kelt im A-_______ bend-son-_ nen-schein.__ # sa-_______ me, ge- wal- ti-ge Me-__ lo- dei._____ # Sin-______ gen die Lo-______ re-_ ley__ ge- than.____ mle4mn g f4 f | e. e16e ml f mnf f | ml e4.mnep || p2. | p p p g g g | ml gppmnpp || mlcg>mnca>mndg>mncpl8p d c d | ml # Copyright © 2013, 2016 # mirabilos #- # 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. """ Script entry points for use by setuptools """ import argparse import sys from pprint import pprint from .musicxml import convert_mml_file from .mml import mml from .parser import mml_file from .playlist import convert_to_timedlist, timedlist_duration, timedlist_ntracks def mml2musicxml(): """ Entry point for converting MML to MusicXML Returns 0 on success, >0 on error. """ # parse arguments aparser = argparse.ArgumentParser() aparser.add_argument("path", help="path to the file to convert") args = aparser.parse_args() if hasattr(sys.stdout, "buffer"): sys.stdout.buffer.write(convert_mml_file(args.path)) else: sys.stdout.write(convert_mml_file(args.path)) sys.stdout.write("\n") return 0 def mmllint(): """ Entry point for checking MML Returns 0 on success, >0 on error. """ aparser = argparse.ArgumentParser() aparser.add_argument("path_or_mml", help="path to the file to check or mml string") aparser.add_argument("-q", "--quiet", help="only output final state", action="store_true") args = aparser.parse_args() if not args.path_or_mml.endswith(".mml"): playlist = [mml(args.path_or_mml)] else: playlist = mml_file(args.path_or_mml) timedlist, errors = convert_to_timedlist(playlist) if not args.quiet: pprint(timedlist) if len(timedlist): (highesttrack, ntracks) = timedlist_ntracks(timedlist) print("Tracks : " + str(ntracks) + ", up to #" + str(highesttrack)) print("Measures: " + str(len(timedlist))) print("Duration: " + str(timedlist_duration(timedlist))) if len(errors): for (track, barno, delta) in errors: if barno == 0: print("Error: missing track " + str(track)) elif delta == 0: print("Error: track " + str(track) + " missing measure " + str(barno)) else: print("Error: underfull measure (-" + str(delta) + "s) in track " + str(track) + ", measure " + str(barno)) return 1 else: print("No errors.") return 0 MMLlib-0.3.0.post1/mmllib/mml.py0000644000175000017500000002156413043253322015677 0ustar niknik00000000000000# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016 # mirabilos # Copyright © 2013, 2016 # Dominik George # Copyright © 2013 # Eike Tim Jesinghaus #- # 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 and data to deal with Music Macro Language parsing and conversion into frequency and duration tuples, as well as generic tools for dealing with MML. """ # Pre-calculated note frequencies # # The list comprehension creates a list of 84 frequencies # from C2 (C, o0c) to B8 (h''''', o6b) mapping the MML note # number (minus one) to its 12-EDO, A=440 Hz, frequency _MML_NOTE2FREQ = [(440.0 * pow(2, (n - 33) / 12.)) for n in range(0, 84)] # Note offsets # Map pitch names to half-tone steps from the current C _MML_NAME2STEP = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11} # Note offsets, reverse # # Map MML note numbers (minus one) to tuple (MML octave, pitch name, # accidental sign) # # For enharmonic equivalents, ♯ is used for all half-tones except B♭ # (common guess) MML_NOTE2PITCH = [item for sublist in [[(octave, 'C', u'♮'), (octave, 'C', u'♯'), (octave, 'D', u'♮'), (octave, 'D', u'♯'), (octave, 'E', u'♮'), (octave, 'F', u'♮'), (octave, 'F', u'♯'), (octave, 'G', u'♮'), (octave, 'G', u'♯'), (octave, 'A', u'♮'), (octave, 'B', u'♭'), (octave, 'B', u'♮')] for octave in range(0, 7)] for item in sublist] def _addtoplaylist(res, freq, dur): """Merge note or pause onto playlist Helper function to merge a note or pause onto the playlist Important: may only be called from mml_play because it directly manipulates the result playlist, which only overridable functions are permitted to do; this isn’t one. res - the result playlist freq - the frequency (0 to pause) dur - the length """ # simple case if len(res) == 0 or not isinstance(res[-1], tuple): res.append((freq, dur)) return # merge same-frequency occurrences prec = res.pop() if prec[0] == freq: res.append((freq, prec[1] + dur)) return # oops, no; restore preceding element and add a new one res.append(prec) res.append((freq, dur)) def _mml_barline(res): """ Add bar line to playlist Helper function to merge a synchronisation mark onto the playlist (may be overridden) res - the result playlist """ if len(res) > 0 and res[-1] != 1: res.append(1) def _mml_play(res, bpm, art, note, length, dots, extra): """ Play a note or pause Helper function to play an MML note after parsing sustain dots by adding it onto the result playlist (may be overridden) The “extra” information are not read by this implementation but must be passed by all callers! Values are: • tuple(mmloctave, basenote, accidentalsign) ‣ mmloctave is int, 0‥6 ‣ basenote ∈ ("C", "D", "E", "F", "G", "A", "B") ‣ accidentalsign ∈ (u'♭', u'♮', u'♯') • int(-1) to play a pause (rest) • int mmlnote-1, 0‥83 ‣ tuple form preferred as this involves guessing the accidental sign res - the result playlist bpm - the current tempo art - the current articulation note - the note number minus 1, or -1 for pause length - the time value (length) to play dots - amount of sustain dots extra - additional information for export """ # calculate duration length /= 1.5**dots duration = ((60.0 / bpm) * 4) / length # articulate note if note == -1: # pause _addtoplaylist(res, 0, duration) elif art == 'L': # legato _addtoplaylist(res, _MML_NOTE2FREQ[note], duration) else: # normal or staccato if art == 'N': part = 7.0/8 else: part = 3.0/4 _addtoplaylist(res, _MML_NOTE2FREQ[note], duration * part) _addtoplaylist(res, 0, duration - duration * part) def _getint(macro, minval, maxval, defval): """ Parse an MML number Parses an MML number (positive) with bounds checking macro - the MML input list minval - the minimum allowed value maxval - the maximum allowed value defval - the default value to use if bounds are exceeded Returns an int, or defval. """ num = "" while macro and macro[0].isdigit(): num += macro.pop(0) if not num: return defval i = int(num) if i < minval or i > maxval: return defval return i def mml(macro, _nplay=None, _barline=None): """ Parse a string in the "music macro language" The rsulting playlist is ordinarily a list of (frequency, duration) tuples, but the two overridable functions are the only ones modifying it, so it can be anything these do. macro - string in the music macro language _nplay - a function playing a note or pause, or None (_mml_play) _barline - a function playing a bar line, or None (_mml_barline) Returns the playlist """ # overridden functions if _nplay is None: _nplay = _mml_play if _barline is None: _barline = _mml_barline # result playlist, may be manipulated only by overridable functions res = [] # state machine variables art = 'N' bpm = 120 octave = 4 timevalue = 4 # normalise macro string, create input list macro = macro.upper() macro = macro.replace(" ", "") macro = list(macro) # helper function to parse sustain dots and call _nplay def _play(macro, res, bpm, art, note, length, extra): # parse sustain dots ndots = 0 while macro and macro[0] == ".": macro.pop(0) ndots += 1 _nplay(res, bpm, art, note, length, ndots, extra) while macro: char = macro.pop(0) if char in _MML_NAME2STEP.keys(): # base note note = 12 * octave + _MML_NAME2STEP[char] extra = (octave, char, u'♮') # accidental sign if macro and macro[0] in ("#", "+"): if note < 83: note += 1 extra = (octave, char, u'♯') macro.pop(0) elif macro and macro[0] == "-": if note > 0: note -= 1 extra = (octave, char, u'♭') macro.pop(0) # length _length = _getint(macro, 1, 64, timevalue) # sustain dots, and play the note _play(macro, res, bpm, art, note, _length, extra) elif char == "L": timevalue = _getint(macro, 1, 64, 4) elif char == "M": if macro: char = macro.pop(0) if char in ("L", "N", "S"): art = char elif char == "N": note = _getint(macro, 0, 84, -1) - 1 if note != -2: _play(macro, res, bpm, art, note, timevalue, note) elif char == "O": octave = _getint(macro, 0, 6, 4) elif char == "P": _length = _getint(macro, 1, 64, timevalue) _play(macro, res, bpm, art, -1, _length, -1) elif char == "T": bpm = _getint(macro, 32, 255, 120) elif char == "<": if octave > 0: octave -= 1 elif char == ">": if octave < 6: octave += 1 elif char == "|": _barline(res) elif char == "X": while macro and macro[0] != ";": macro.pop(0) return res MMLlib-0.3.0.post1/mmllib/musicxml.py0000644000175000017500000003576013043252117016757 0ustar niknik00000000000000# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016 # mirabilos # # 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) MMLlib-0.3.0.post1/mmllib/parser.py0000644000175000017500000000766413043252335016416 0ustar niknik00000000000000# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016 # mirabilos # Copyright © 2013, 2016 # Dominik George # Copyright © 2013 # Eike Tim Jesinghaus # # 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 parse extended MML files into metadata and playlists """ from __future__ import with_statement import codecs from .mml import mml from .playlist import estimate_duration def mml_file(path, _play=None, _barline=None): """ Parse a file in the music macro language The playlist returned is ordinarily a list of tracks, i.e. a list of lists of (frequency, duration) tuples. This changes depending on the override function. path - the path to the MML file _play - the override play function to pass to mml() _barline - the override barline function to pass to mml() Returns the playlist. """ vstrings = [] vlists = [] vcount = 0 with codecs.open(path, "r", "UTF-8") as mmlfile: for line in mmlfile: if line.strip().startswith("#"): continue elif line.strip() == "": vcount = 0 else: if len(vstrings) <= vcount: vstrings.append("") vstrings[vcount] += line.strip() vcount += 1 for vstring in vstrings: vlists.append(mml(vstring, _play, _barline)) return vlists def mml_file_meta(path): """ Parse a file in the music macro language and return metadata path - the path to the MML file Returns a dictionary of metadata, with lowercase keys """ state = 0 vcount = 0 meta = {} with codecs.open(path, "r", "UTF-8") as mmlfile: for line in mmlfile: if state == 0: # Header fields if line.strip() == "": state = 1 elif line.strip().startswith("# ") and ":" in line: parts = line.strip()[1:].split(":") key = str(parts.pop(0)) value = ":".join(parts) meta[key.strip().lower()] = value.strip() if state == 1: # Skip first empty line (header/body separator) if not line.strip() == "": state = 2 if state == 2: if line.strip().startswith("#"): continue if line.strip() == "": if not vcount: # comment block preceding music continue # everything of interest has gone break vcount += 1 # Add discovered voice count to meta if not explicitly given if "voices" not in meta: meta["voices"] = vcount # Estimate duration of tracks and add maximum to meta if not # explicitly given if "duration" not in meta: meta["duration"] = max([estimate_duration(x) for x in mml_file(path)]) # Ensure number types meta["duration"] = float(meta["duration"]) meta["voices"] = int(meta["voices"]) return meta MMLlib-0.3.0.post1/mmllib/playlist.py0000644000175000017500000001761613043253040016753 0ustar niknik00000000000000# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016 # mirabilos # Copyright © 2013 # Dominik George # # 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 deal with mmllib playlists, which are lists containing frequency and duration tuples, and integer entries for special tokens such as bar lines. Playlist format A standard MMLlib playlist is an ordered list whose elements are either the integer 1 (denoting a bar line) or a tuple, normally containing (frequency, duration). Other members may be present. The playlist generation code in the mml module can be overridden, in which case a different format is possible; this module does not support such formats. It is not permitted to have a bar line at the beginning of a playlist, nor is it permitted to have two subsequent bar lines. It is recommended a playlist end with a bar line, but not required. A duration is a floating-point number, measured in seconds, and must be strictly greater than 0. """ def estimate_duration(track): """ Calculate playback length of a list of (frequency, duration) tuples This function looks at all the tuples in a playback list and estimates the playback duration by adding up the durations. track - the playlist Returns the estimated duration in seconds. """ # Remove all non-tuples from the list; integer 1 is abused by mml_parse # for syncmarks; extract second entries of tuples and add them up and # return the result, all in one list comprehension -- I ❤ Python! return sum([x[1] for x in track if isinstance(x, tuple)]) def timedlist_duration(timedlist): """ Calculate playback length for a timedlist This function returns the sum of the length of all measures. timedlist - the timedlist Returns the length of the piece in seconds. """ return sum([x[0] for x in timedlist]) def timedlist_ntracks(timedlist): """ Calculate amount of tracks a timedlist uses This function returns the highest track number addressed, and the amount of tracks that actually are addressed. timedlist - the timedlist Returns tuple(highesttrack, ntracks). """ addressed = {} for measure in timedlist: for event in measure[1]: for action in event[1]: addressed[action[0]] = True tracknos = sorted(addressed.keys()) return (tracknos[-1], len(tracknos)) def timedlist_flatten(timedlist): """ Flatten a timedlist Converts a multi-measure timedlist into a timed document, i.e. a timedlist with only one measure. timedlist - the timedlist Returns a new timedlist as flattened document. """ newlen = 0 newev = [] for measure in timedlist: for ev in measure[1]: newev.append((newlen + ev[0], ev[1])) newlen += measure[0] newmeasure = (newlen, newev) return (newmeasure,) def convert_to_timedlist(staves): """ Convert playlist to timedlist Converts a list of playlists (staves) to a (timedlist, errors) tuple, with them being: timedlist: sequence of measures measure: sequence of (length, list of events) length: length of this measure in seconds (float) event: sequence of (offset, list of actions), ascending by offset offset: time relative to the start of this measure, in seconds (float) action: sequence of (track, frequency), ascending by track number track: 1-based track number frequency: 0 means stop playing errors: sequence of (track, barno, delta) barno: 1-based number of problematic bar or 0 to indicate a missing track delta: amount by which the bar in this track is shorter than the maximum in seconds, or 0, if the bar isn’t present in this track This gives: ([(0.8, [(0, [(1, 440), …]), (0.7, [(1, 0), …])]), …], []) on success ([], [(2, 0, 0), (3, 1, 0.0125), (3, 3, 0), …]) on failure Example input for these: • success: t75o2mna…| • failure: track 1 with 3 bars, track 2 with empty playlist, track 3 with only 2 bars, the first of which is 0.0125 seconds shorter than bar 1 in track 0 Note that both can be returned, i.e. an error is not fatal staves - list of playlists (modified to end with a bar line) Returns tuple(timedlist, errors). """ # return values timedlist = [] errors = [] # sanitise input first for (trkno, staff) in enumerate(staves): while len(staff) > 0 and staff[-1] == 1: staff.pop() if len(staff) == 0: errors.append((trkno + 1, 0, 0)) else: staff.append(1) if len(errors) > 0: return (timedlist, errors) # now we have a list of staves with at least one bar each, # a bar line terminating each – collect events and length, # for each staff separately at first stavev = [] nstaves = len(staves) nbars = 0 for trkno in range(0, nstaves): stev = [] # currently open bar curlen = 0. curevt = {} curfreq = 0 for token in staves[trkno]: if token == 1: # bar line stev.append((curlen, curevt)) curlen = 0. curevt = {} continue # note or rest if curfreq != token[0]: curfreq = token[0] curevt[curlen] = curfreq curlen += token[1] # at the end we’re guaranteed to have just finished a bar # deal with any left-over sound if curfreq > 0: stev[-1][1][stev[-1][0]] = 0 # finish the staff if len(stev) > nbars: nbars = len(stev) stavev.append(stev) # now we have a list of events for all staves separately: # stavev = [staff1, staff2, …] # staffN = [bar1, bar2, …] # barN = (len, {offset: frequency, …}) for barno in range(0, nbars): # highest bar length mslen = max([len(stavev[trkno]) > barno and stavev[trkno][barno][0] or 0 for trkno in range(0, nstaves)]) # events msev = {} # merge tracks one by one for trkno in range(0, nstaves): if not len(stavev[trkno]) > barno: errors.append((trkno + 1, barno + 1, 0)) continue trkbar = stavev[trkno][barno] # check length (against an epsilon value) if mslen - trkbar[0] > 0.0001: errors.append((trkno + 1, barno + 1, mslen - trkbar[0])) # merge events for tbev in trkbar[1].keys(): ihatefloatingpoint = int(tbev * 100000) / 100000. if not ihatefloatingpoint in msev: msev[ihatefloatingpoint] = [] msev[ihatefloatingpoint].append((trkno + 1, trkbar[1][tbev])) # get them into a list msevkeys = sorted(msev.keys()) evlist = [] for evkey in msevkeys: evlist.append((evkey, msev[evkey])) # finish this measure timedlist.append((mslen, evlist)) return (timedlist, errors) MMLlib-0.3.0.post1/test/0000755000175000017500000000000013043255733014243 5ustar niknik00000000000000MMLlib-0.3.0.post1/test/data/0000755000175000017500000000000013043255733015154 5ustar niknik00000000000000MMLlib-0.3.0.post1/test/data/loreley.xml0000644000175000017500000011513613043222611017345 0ustar niknik00000000000000LoreleyPh. Friedrich Silcher (1789–1860), 1837Heinrich Heine (1797–1856), 1824Klavier: August Linder; MML: mirabilosMML encoding & arrangement © 2016 mirabilos, published under The MirOS Licence; copyright for text, music, and piano arrangement has expiredmirabilos, 2016MMLlib by Natureshadow, Creeparoo, and mirabilosLinder, August (Hrsg.): Deutsche Weisen : Die beliebtesten Volks- und geistlichen Lieder. Stuttgart: Albert Auer’s Musikverlag, n.d., c. 1900.37Piano, VoiceC MajorGerman164 (5)1/8Andante6/834Track 1Track 2Track 3Track 44G2quarter76G42eighthG43eighthA4116thG42eighthC52eighthB42eighthA42eighthG46quarterF44quarterF42eighthE43eighthE4116thE42eighthD42eighthC42eighthD42eighthE46quarterE42eighth2eighthG42eighthG43eighthA4116thG42eighthC52eighthB42eighthA42eighthG46quarterF44quarterF42eighthE43eighthE4116thE42eighthG42eighthF42eighthD42eighthC46quarterC42eighth2eighthE42eighthD43eighthE4116thD42eighthG42eighthD42eighthD42eighthB46quarterA44quarterA42eighthG44quarterG42eighthF142eighthG42eighthA42eighthG46quarterG44quarterG42eighthG43eighthA4116thG42eighthC52eighthB42eighthA42eighthG44quarterE52eighthD54quarterD52eighthC53eighthC5116thC52eighthB42eighthA42eighthB42eighthC56quarterC52eighth2eighthlight-heavy4G2quarter762eighthE43eighthF4116thE42eighthF42eighthF42eighthF42eighthE46quarterD44quarterD42eighthC43eighthC4116thC42eighthB32eighthA32eighthB32eighthC46quarterC42eighth2eighth2eighthE43eighthF4116thE42eighthF42eighthF42eighthF42eighthE46quarterD44quarterD42eighthC43eighthC4116thC42eighthB32eighthB32eighthB32eighth12halfB33eighthC4116thB32eighthB32eighthB32eighthB32eighthD46quarterC44quarterC42eighthB34quarter2eighthA32eighthB32eighthC42eighthG22eighthB22eighthC32eighthD32eighthC32eighthB22eighthE43eighthF4116thE42eighthF42eighthF42eighthF42eighthE44quarterG42eighthF44quarterF42eighthE43eighthE4116thE42eighthF42eighthF42eighthF42eighthE46quarterE42eighth2eighthlight-heavy4G2quarter762eighth12half12half6quarterG34quarterG32eighthG32eighth2eighth2eighth4quarter2eighth12half12half2eighth2eighth2eighthD32eighthD32eighthF32eighthE32eighth2eighth2eighth4quarter2eighth2eighth2eighth2eighthD42eighth2eighth2eighthG46quarterE44quarterE42eighthD44quarterD42eighthD34quarterD32eighthD36quarterD34quarterF42eighth12half12half2eighth2eighth2eighthG32eighthG32eighthG32eighthG32eighth2eighth2eighth2eighth2eighthlight-heavy4G2quarter762eighthC3116thG3116thC42eighthG32eighthC3116thA3116thD42eighthB32eighthC3116thG3116thC42eighthG32eighthF2116thF3116thA32eighthF32eighthG2116thE3116thG32eighthE32eighthG24quarterG22eighthC32eighthG22eighthE22eighthC24quarter2eighthC3116thG3116thC42eighthG32eighthC3116thA3116thD42eighthB32eighthC3116thG3116thC42eighthG32eighthF2116thF3116thA32eighthF32eighthG22eighthG32eighthC32eighthG22eighthG22eighthG22eighthC32eighthE22eighthG22eighthC24quarter2eighthG22eighthD32eighthG32eighthG22eighthD32eighthG32eighthG22eighthD32eighthG32eighthC22eighthE32eighthA32eighthD22eighthD32eighthB32eighth4quarterF132eighthB32eighthD42eighthE42eighthF42eighthE42eighthD42eighthC3116thG3116thC42eighthG32eighthC3116thA3116thD42eighthB32eighthC3116thG3116thC4116th116th2eighthF2116thA3116thD4116th116th2eighthG2116thG3116thC4116th116th2eighthD42eighthC42eighthD42eighthC32eighthE32eighthG32eighthC32eighth2eighthlight-heavy MMLlib-0.3.0.post1/test/__init__.py0000644000175000017500000000000013043212055016330 0ustar niknik00000000000000MMLlib-0.3.0.post1/test/test_musicxml.py0000644000175000017500000000323213043242051017502 0ustar niknik00000000000000#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ # Copyright © 2017 # Dominik George # # 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. from __future__ import with_statement import os import unittest from mmllib.musicxml import convert_mml_file class MusicXMLTests(unittest.TestCase): def setUp(self): self.datadir = os.path.join(os.path.dirname(__file__), "data") self.examplesdir = os.path.abspath(os.path.join(self.datadir, "..", "..", "examples")) def test_convert_mml_file(self): mmlfile = os.path.join(self.examplesdir, "loreley.mml") xmlfile = os.path.join(self.datadir, "loreley.xml") xml = convert_mml_file(mmlfile) with open(xmlfile, "rb") as f: expected = f.read() self.assertEqual(xml, expected[:-1]) if __name__ == "__main__": unittest.main() MMLlib-0.3.0.post1/test/test_parser.py0000644000175000017500000006755113043242052017154 0ustar niknik00000000000000#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ # Copyright © 2017 # Dominik George # # 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. import os import unittest from mmllib.parser import mml_file, mml_file_meta class ParserTests(unittest.TestCase): def setUp(self): self.datadir = os.path.join(os.path.dirname(__file__), "data") self.examplesdir = os.path.abspath(os.path.join(self.datadir, "..", "..", "examples")) def test_mml_file_meta(self): mmlfile = os.path.join(self.examplesdir, "loreley.mml") res_meta = mml_file_meta(mmlfile) expected_meta = {'arranger': u'Klavier: August Linder; MML: mirabilos', 'composer': u'Ph. Friedrich Silcher (1789\u20131860), 1837', 'copyright': u'MML encoding & arrangement \xa9 2016 mirabilos, published under The MirOS Licence; copyright for text, music, and piano arrangement has expired', 'duration': 37.89473684210526, 'encoder': u'mirabilos, 2016', 'instruments': u'Piano, Voice', 'key signature': u'C Major', 'language': u'German', 'lyrics': u'Heinrich Heine (1797\u20131856), 1824', 'measures': u'16', 'mml tracks': u'4 (5)', 'pickup measure': u'1/8', 'source': u'Linder, August (Hrsg.): Deutsche Weisen : Die beliebtesten Volks- und geistlichen Lieder. Stuttgart: Albert Auer\u2019s Musikverlag, n.d., c. 1900.', 'tempo': u'Andante', 'time signature': u'6/8', 'title': u'Loreley', 'verses': u'3', 'voices': 4} self.assertDictEqual(res_meta, expected_meta) def test_mml_file(self): mmlfile = os.path.join(self.examplesdir, "loreley.mml") res = mml_file(mmlfile) expected = [[(391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 0.5921052631578948), (440.0, 0.17269736842105263), (0, 0.024671052631578955), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (523.2511306011972, 0.39473684210526316), (493.8833012561241, 0.34539473684210525), (0, 0.04934210526315791), (440.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 1.036184210526316), (0, 0.14802631578947367), (349.2282314330039, 0.6907894736842105), (0, 0.09868421052631582), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 0.518092105263158), (0, 0.07401315789473684), (329.6275569128699, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.39473684210526316), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 1.529605263157895), (0, 0.4440789473684211), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 0.5921052631578948), (440.0, 0.17269736842105263), (0, 0.024671052631578955), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (523.2511306011972, 0.39473684210526316), (493.8833012561241, 0.34539473684210525), (0, 0.04934210526315791), (440.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 1.036184210526316), (0, 0.14802631578947367), (349.2282314330039, 0.6907894736842105), (0, 0.09868421052631582), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 0.518092105263158), (0, 0.07401315789473684), (329.6275569128699, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (261.6255653005986, 1.529605263157895), (0, 0.4440789473684211), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), 1, (293.6647679174076, 0.5921052631578948), (329.6275569128699, 0.17269736842105263), (0, 0.024671052631578955), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (493.8833012561241, 1.036184210526316), (0, 0.14802631578947367), (440.0, 0.6907894736842105), (0, 0.09868421052631582), (440.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 0.6907894736842105), (0, 0.09868421052631582), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (369.9944227116344, 0.39473684210526316), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (440.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 1.875), (0, 0.09868421052631582), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 0.518092105263158), (0, 0.07401315789473684), (440.0, 0.17269736842105263), (0, 0.024671052631578955), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (523.2511306011972, 0.39473684210526316), (493.8833012561241, 0.34539473684210525), (0, 0.04934210526315791), (440.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (391.99543598174927, 0.7894736842105263), (659.2551138257398, 0.34539473684210525), (0, 0.04934210526315791), (587.3295358348151, 0.6907894736842105), (0, 0.09868421052631582), (587.3295358348151, 0.34539473684210525), (0, 0.04934210526315791), 1, (523.2511306011972, 0.518092105263158), (0, 0.07401315789473684), (523.2511306011972, 0.17269736842105263), (0, 0.024671052631578955), (523.2511306011972, 0.34539473684210525), (0, 0.04934210526315791), (493.8833012561241, 0.39473684210526316), (440.0, 0.34539473684210525), (0, 0.04934210526315791), (493.8833012561241, 0.34539473684210525), (0, 0.04934210526315791), 1, (523.2511306011972, 1.529605263157895), (0, 0.4440789473684211), 1], [(0, 0.39473684210526316), 1, (329.6275569128699, 0.5921052631578948), (349.2282314330039, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.7401315789473684), (0, 0.04934210526315791), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 1.036184210526316), (0, 0.14802631578947367), (293.6647679174076, 0.6907894736842105), (0, 0.09868421052631582), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (261.6255653005986, 0.518092105263158), (0, 0.07401315789473684), (261.6255653005986, 0.17269736842105263), (0, 0.024671052631578955), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.39473684210526316), (220.0, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (261.6255653005986, 1.529605263157895), (0, 0.8388157894736843), 1, (329.6275569128699, 0.5921052631578948), (349.2282314330039, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.7401315789473684), (0, 0.04934210526315791), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 1.036184210526316), (0, 0.14802631578947367), (293.6647679174076, 0.6907894736842105), (0, 0.09868421052631582), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (261.6255653005986, 0.518092105263158), (0, 0.07401315789473684), (261.6255653005986, 0.17269736842105263), (0, 0.024671052631578955), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (0, 2.368421052631579), 1, (246.94165062806206, 0.5921052631578948), (261.6255653005986, 0.17269736842105263), (0, 0.024671052631578955), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (293.6647679174076, 1.036184210526316), (0, 0.14802631578947367), (261.6255653005986, 0.6907894736842105), (0, 0.09868421052631582), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), 1, (246.94165062806206, 0.6907894736842105), (0, 0.493421052631579), (220.0, 0.39473684210526316), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), 1, (97.99885899543733, 0.39473684210526316), (123.47082531403103, 0.39473684210526316), (130.8127826502993, 0.39473684210526316), (146.8323839587038, 0.39473684210526316), (130.8127826502993, 0.39473684210526316), (123.47082531403103, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 0.518092105263158), (0, 0.07401315789473684), (349.2282314330039, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.7401315789473684), (0, 0.04934210526315791), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 0.7894736842105263), (391.99543598174927, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.6907894736842105), (0, 0.09868421052631582), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 0.518092105263158), (0, 0.07401315789473684), (329.6275569128699, 0.17269736842105263), (0, 0.024671052631578955), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), (349.2282314330039, 0.7401315789473684), (0, 0.04934210526315791), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (329.6275569128699, 1.529605263157895), (0, 0.4440789473684211), 1], [(0, 0.39473684210526316), 1, (0, 2.368421052631579), 1, (0, 2.368421052631579), 1, (0, 1.1842105263157896), (195.99771799087463, 0.6907894736842105), (0, 0.09868421052631582), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), 1, (195.99771799087463, 0.39473684210526316), (0, 1.973684210526316), 1, (0, 2.368421052631579), 1, (0, 2.368421052631579), 1, (0, 1.1842105263157894), (146.8323839587038, 0.34539473684210525), (0, 0.04934210526315791), (146.8323839587038, 0.34539473684210525), (0, 0.04934210526315791), (174.61411571650194, 0.34539473684210525), (0, 0.04934210526315791), 1, (164.81377845643496, 0.39473684210526316), (0, 1.973684210526316), 1, (0, 1.1842105263157894), (293.6647679174076, 0.34539473684210525), (0, 0.8388157894736843), 1, (391.99543598174927, 1.036184210526316), (0, 0.14802631578947367), (329.6275569128699, 0.6907894736842105), (0, 0.09868421052631582), (329.6275569128699, 0.34539473684210525), (0, 0.04934210526315791), 1, (293.6647679174076, 0.6907894736842105), (0, 0.09868421052631582), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), (146.8323839587038, 0.6907894736842105), (0, 0.09868421052631582), (146.8323839587038, 0.34539473684210525), (0, 0.04934210526315791), 1, (146.8323839587038, 1.875), (0, 0.09868421052631582), (349.2282314330039, 0.34539473684210525), (0, 0.04934210526315791), 1, (0, 2.368421052631579), 1, (0, 2.368421052631579), 1, (0, 1.1842105263157894), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), 1, (195.99771799087463, 0.39473684210526316), (0, 1.5789473684210527), 1], [(0, 0.39473684210526316), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (130.8127826502993, 0.19736842105263158), (220.0, 0.19736842105263158), (293.6647679174076, 0.39473684210526316), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (87.30705785825097, 0.19736842105263158), (174.61411571650194, 0.19736842105263158), (220.0, 0.39473684210526316), (174.61411571650194, 0.34539473684210525), (0, 0.04934210526315791), 1, (97.99885899543733, 0.19736842105263158), (164.81377845643496, 0.19736842105263158), (195.99771799087463, 0.39473684210526316), (164.81377845643496, 0.34539473684210525), (0, 0.04934210526315791), (97.99885899543733, 0.6907894736842105), (0, 0.09868421052631582), (97.99885899543733, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.39473684210526316), (97.99885899543733, 0.39473684210526316), (82.4068892282175, 0.39473684210526316), (65.40639132514966, 0.6907894736842105), (0, 0.493421052631579), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (130.8127826502993, 0.19736842105263158), (220.0, 0.19736842105263158), (293.6647679174076, 0.39473684210526316), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (87.30705785825097, 0.19736842105263158), (174.61411571650194, 0.19736842105263158), (220.0, 0.39473684210526316), (174.61411571650194, 0.34539473684210525), (0, 0.04934210526315791), 1, (97.99885899543733, 0.34539473684210525), (0, 0.04934210526315791), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (130.8127826502993, 0.34539473684210525), (0, 0.04934210526315791), (97.99885899543733, 0.34539473684210525), (0, 0.04934210526315791), (97.99885899543733, 0.34539473684210525), (0, 0.04934210526315791), (97.99885899543733, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.39473684210526316), (82.4068892282175, 0.39473684210526316), (97.99885899543733, 0.39473684210526316), (65.40639132514966, 0.6907894736842105), (0, 0.493421052631579), 1, (97.99885899543733, 0.39473684210526316), (146.8323839587038, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (97.99885899543733, 0.39473684210526316), (146.8323839587038, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), 1, (97.99885899543733, 0.39473684210526316), (146.8323839587038, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (65.40639132514966, 0.39473684210526316), (164.81377845643496, 0.39473684210526316), (220.0, 0.34539473684210525), (0, 0.04934210526315791), 1, (73.41619197935188, 0.39473684210526316), (146.8323839587038, 0.39473684210526316), (246.94165062806206, 0.34539473684210525), (0, 0.8388157894736843), (184.9972113558172, 0.34539473684210525), (0, 0.04934210526315791), 1, (246.94165062806206, 0.39473684210526316), (293.6647679174076, 0.39473684210526316), (329.6275569128699, 0.39473684210526316), (349.2282314330039, 0.39473684210526316), (329.6275569128699, 0.39473684210526316), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.39473684210526316), (195.99771799087463, 0.34539473684210525), (0, 0.04934210526315791), (130.8127826502993, 0.19736842105263158), (220.0, 0.19736842105263158), (293.6647679174076, 0.39473684210526316), (246.94165062806206, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.17269736842105263), (0, 0.6167763157894737), (87.30705785825097, 0.19736842105263158), (220.0, 0.19736842105263158), (293.6647679174076, 0.17269736842105263), (0, 0.6167763157894737), 1, (97.99885899543733, 0.19736842105263158), (195.99771799087463, 0.19736842105263158), (261.6255653005986, 0.17269736842105263), (0, 0.6167763157894737), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), (261.6255653005986, 0.34539473684210525), (0, 0.04934210526315791), (293.6647679174076, 0.34539473684210525), (0, 0.04934210526315791), 1, (130.8127826502993, 0.39473684210526316), (164.81377845643496, 0.39473684210526316), (195.99771799087463, 0.39473684210526316), (130.8127826502993, 0.34539473684210525), (0, 0.4440789473684211), 1]] self.assertListEqual(res, expected) if __name__ == "__main__": unittest.main() MMLlib-0.3.0.post1/test/test_playlist.py0000644000175000017500000005067413043242052017517 0ustar niknik00000000000000#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ # Copyright © 2017 # Dominik George # # 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. import os import unittest from .util import round_sequence_r from mmllib.parser import mml_file from mmllib.playlist import convert_to_timedlist class PlaylistTests(unittest.TestCase): def setUp(self): self.datadir = os.path.join(os.path.dirname(__file__), "data") self.examplesdir = os.path.abspath(os.path.join(self.datadir, "..", "..", "examples")) def test_convert_to_timedlist(self): mmlfile = os.path.join(self.examplesdir, "loreley.mml") res, errors = convert_to_timedlist(mml_file(mmlfile)) expected = [(0.39473684210526316, [(0.0, [(1, 391.99543598174927)]), (0.34538999999999997, [(1, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.59209999999999996, [(1, 440.0), (2, 349.22823143300388)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 195.99771799087463)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 523.25113060119725), (2, 349.22823143300388), (4, 130.81278265029931)]), (1.38157, [(4, 220.0)]), (1.57894, [(1, 493.88330125612413), (4, 293.66476791740757)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 440.0), (2, 349.22823143300388), (4, 246.94165062806206)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.78947000000000001, [(4, 195.99771799087463)]), (1.0361800000000001, [(1, 0), (2, 0)]), (1.13486, [(4, 0)]), (1.18421, [(1, 349.22823143300388), (2, 293.66476791740757), (4, 87.307057858250971)]), (1.38157, [(4, 174.61411571650194)]), (1.57894, [(4, 220.0)]), (1.875, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 349.22823143300388), (2, 293.66476791740757), (4, 174.61411571650194)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 329.62755691286992), (2, 261.62556530059862), (4, 97.998858995437331)]), (0.19736000000000001, [(4, 164.81377845643496)]), (0.39473000000000003, [(4, 195.99771799087463)]), (0.51809000000000005, [(1, 0), (2, 0)]), (0.59209999999999996, [(1, 329.62755691286992), (2, 261.62556530059862)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 329.62755691286992), (2, 261.62556530059862), (4, 164.81377845643496)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 293.66476791740757), (2, 246.94165062806206), (3, 195.99771799087463), (4, 97.998858995437331)]), (1.57894, [(1, 261.62556530059862), (2, 220.0)]), (1.875, [(3, 0), (4, 0)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 293.66476791740757), (2, 246.94165062806206), (3, 195.99771799087463), (4, 97.998858995437331)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 329.62755691286992), (2, 261.62556530059862), (3, 195.99771799087463), (4, 130.81278265029931)]), (0.39473000000000003, [(3, 0), (4, 97.998858995437331)]), (0.78947000000000001, [(4, 82.40688922821748)]), (1.18421, [(4, 65.406391325149656)]), (1.5296000000000001, [(1, 0), (2, 0)]), (1.875, [(4, 0)]), (1.9736800000000001, [(1, 391.99543598174927)]), (2.31907, [(1, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.59209999999999996, [(1, 440.0), (2, 349.22823143300388)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 195.99771799087463)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 523.25113060119725), (2, 349.22823143300388), (4, 130.81278265029931)]), (1.38157, [(4, 220.0)]), (1.57894, [(1, 493.88330125612413), (4, 293.66476791740757)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 440.0), (2, 349.22823143300388), (4, 246.94165062806206)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.78947000000000001, [(4, 195.99771799087463)]), (1.0361800000000001, [(1, 0), (2, 0)]), (1.13486, [(4, 0)]), (1.18421, [(1, 349.22823143300388), (2, 293.66476791740757), (4, 87.307057858250971)]), (1.38157, [(4, 174.61411571650194)]), (1.57894, [(4, 220.0)]), (1.875, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 349.22823143300388), (2, 293.66476791740757), (4, 174.61411571650194)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 329.62755691286992), (2, 261.62556530059862), (4, 97.998858995437331)]), (0.34538999999999997, [(4, 0)]), (0.39473000000000003, [(4, 195.99771799087463)]), (0.51809000000000005, [(1, 0), (2, 0)]), (0.59209999999999996, [(1, 329.62755691286992), (2, 261.62556530059862)]), (0.74012999999999995, [(4, 0)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 329.62755691286992), (2, 261.62556530059862), (4, 130.81278265029931)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 391.99543598174927), (2, 246.94165062806206), (3, 146.83238395870379), (4, 97.998858995437331)]), (1.5296000000000001, [(1, 0), (2, 0), (3, 0), (4, 0)]), (1.57894, [(1, 349.22823143300388), (2, 246.94165062806206), (3, 146.83238395870379), (4, 97.998858995437331)]), (1.9243399999999999, [(1, 0), (2, 0), (3, 0), (4, 0)]), (1.9736800000000001, [(1, 293.66476791740757), (2, 246.94165062806206), (3, 174.61411571650194), (4, 97.998858995437331)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 261.62556530059862), (3, 164.81377845643496), (4, 130.81278265029931)]), (0.39473000000000003, [(3, 0), (4, 82.40688922821748)]), (0.78947000000000001, [(4, 97.998858995437331)]), (1.18421, [(4, 65.406391325149656)]), (1.5296000000000001, [(1, 0)]), (1.875, [(4, 0)]), (1.9736800000000001, [(1, 329.62755691286992)]), (2.31907, [(1, 0)])]), (2.3684210526315792, [(0.0, [(1, 293.66476791740757), (2, 246.94165062806206), (4, 97.998858995437331)]), (0.39473000000000003, [(4, 146.83238395870379)]), (0.59209999999999996, [(1, 329.62755691286992), (2, 261.62556530059862)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 293.66476791740757), (2, 246.94165062806206), (4, 195.99771799087463)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 391.99543598174927), (2, 246.94165062806206), (3, 293.66476791740757), (4, 97.998858995437331)]), (1.5296000000000001, [(1, 0), (2, 0), (3, 0)]), (1.57894, [(1, 293.66476791740757), (2, 246.94165062806206), (4, 146.83238395870379)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 293.66476791740757), (2, 246.94165062806206), (4, 195.99771799087463)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 493.88330125612413), (2, 293.66476791740757), (3, 391.99543598174927), (4, 97.998858995437331)]), (0.39473000000000003, [(4, 146.83238395870379)]), (0.78947000000000001, [(4, 195.99771799087463)]), (1.0361800000000001, [(1, 0), (2, 0), (3, 0)]), (1.13486, [(4, 0)]), (1.18421, [(1, 440.0), (2, 261.62556530059862), (3, 329.62755691286992), (4, 65.406391325149656)]), (1.57894, [(4, 164.81377845643496)]), (1.875, [(1, 0), (2, 0), (3, 0)]), (1.9736800000000001, [(1, 440.0), (2, 261.62556530059862), (3, 329.62755691286992), (4, 220.0)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 246.94165062806206), (3, 293.66476791740757), (4, 73.416191979351879)]), (0.39473000000000003, [(4, 146.83238395870379)]), (0.69077999999999995, [(1, 0), (2, 0), (3, 0)]), (0.78947000000000001, [(1, 391.99543598174927), (3, 293.66476791740757), (4, 246.94165062806206)]), (1.13486, [(1, 0), (3, 0), (4, 0)]), (1.18421, [(1, 369.99442271163446), (2, 220.0), (3, 146.83238395870379)]), (1.57894, [(1, 391.99543598174927), (2, 246.94165062806206)]), (1.875, [(3, 0)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 440.0), (2, 261.62556530059862), (3, 146.83238395870379), (4, 184.99721135581723)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 97.998858995437331), (3, 146.83238395870379), (4, 246.94165062806206)]), (0.39473000000000003, [(2, 123.47082531403103), (4, 293.66476791740757)]), (0.78947000000000001, [(2, 130.81278265029931), (4, 329.62755691286992)]), (1.18421, [(2, 146.83238395870379), (4, 349.22823143300388)]), (1.57894, [(2, 130.81278265029931), (4, 329.62755691286992)]), (1.875, [(1, 0), (3, 0)]), (1.9736800000000001, [(1, 391.99543598174927), (2, 123.47082531403103), (3, 349.22823143300388), (4, 293.66476791740757)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.51809000000000005, [(1, 0), (2, 0)]), (0.59209999999999996, [(1, 440.0), (2, 349.22823143300388)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 195.99771799087463)]), (1.13486, [(1, 0), (2, 0), (4, 0)]), (1.18421, [(1, 523.25113060119725), (2, 349.22823143300388), (4, 130.81278265029931)]), (1.38157, [(4, 220.0)]), (1.57894, [(1, 493.88330125612413), (4, 293.66476791740757)]), (1.9243399999999999, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 440.0), (2, 349.22823143300388), (4, 246.94165062806206)]), (2.31907, [(1, 0), (2, 0), (4, 0)])]), (2.3684210526315792, [(0.0, [(1, 391.99543598174927), (2, 329.62755691286992), (4, 130.81278265029931)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.56742999999999999, [(4, 0)]), (0.78947000000000001, [(1, 659.25511382573984), (2, 391.99543598174927)]), (1.13486, [(1, 0), (2, 0)]), (1.18421, [(1, 587.32953583481515), (2, 349.22823143300388), (4, 87.307057858250971)]), (1.38157, [(4, 220.0)]), (1.57894, [(4, 293.66476791740757)]), (1.7516400000000001, [(4, 0)]), (1.875, [(1, 0), (2, 0)]), (1.9736800000000001, [(1, 587.32953583481515), (2, 349.22823143300388)]), (2.31907, [(1, 0), (2, 0)])]), (2.3684210526315792, [(0.0, [(1, 523.25113060119725), (2, 329.62755691286992), (4, 97.998858995437331)]), (0.19736000000000001, [(4, 195.99771799087463)]), (0.39473000000000003, [(4, 261.62556530059862)]), (0.51809000000000005, [(1, 0), (2, 0)]), (0.56742999999999999, [(4, 0)]), (0.59209999999999996, [(1, 523.25113060119725), (2, 329.62755691286992)]), (0.76480000000000004, [(1, 0), (2, 0)]), (0.78947000000000001, [(1, 523.25113060119725), (2, 329.62755691286992)]), (1.13486, [(1, 0), (2, 0)]), (1.18421, [(1, 493.88330125612413), (2, 349.22823143300388), (3, 195.99771799087463), (4, 293.66476791740757)]), (1.5296000000000001, [(3, 0), (4, 0)]), (1.57894, [(1, 440.0), (3, 195.99771799087463), (4, 261.62556530059862)]), (1.9243399999999999, [(1, 0), (2, 0), (3, 0), (4, 0)]), (1.9736800000000001, [(1, 493.88330125612413), (2, 349.22823143300388), (3, 195.99771799087463), (4, 293.66476791740757)]), (2.31907, [(1, 0), (2, 0), (3, 0), (4, 0)])]), (1.9736842105263159, [(0.0, [(1, 523.25113060119725), (2, 329.62755691286992), (3, 195.99771799087463), (4, 130.81278265029931)]), (0.39473000000000003, [(3, 0), (4, 164.81377845643496)]), (0.78947000000000001, [(4, 195.99771799087463)]), (1.18421, [(4, 130.81278265029931)]), (1.5296000000000001, [(1, 0), (2, 0), (4, 0)])])] self.assertListEqual(round_sequence_r(res), round_sequence_r(expected)) self.assertEqual(errors, []) if __name__ == "__main__": unittest.main() MMLlib-0.3.0.post1/test/util.py0000644000175000017500000000227513043242053015567 0ustar niknik00000000000000# ~*~ coding: utf-8 ~*~ # Copyright © 2017 # Dominik George # # 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. def round_sequence_r(seq, digits=7): if isinstance(seq, list) or isinstance(seq, tuple): return type(seq)([round_sequence_r(x) for x in seq]) elif isinstance(seq, float): return round(seq, digits) else: return seq MMLlib-0.3.0.post1/CHANGELOG.rst0000644000175000017500000000042213043225104015270 0ustar niknik00000000000000Changelog for MMLlib ==================== 0.3 --- - Enable Python 3 compatibility. - Add new mmllint script. - Include example MML songs. - Add first parts of test suite. - Some bugfixes. 0.2 --- - Add mml2musicxml script. 0.1 --- - Separate from Floppi-Music. MMLlib-0.3.0.post1/LICENCE0000644000175000017500000000205413043171424014244 0ustar niknik00000000000000MMLlib is published under The MirOS Licence: Copyright © 2013, 2016 mirabilos Copyright © 2013, 2016, 2017 Dominik George Copyright © 2013 Eike Tim Jesinghaus 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. MMLlib-0.3.0.post1/MANIFEST.in0000644000175000017500000000020313043242750015010 0ustar niknik00000000000000include README.rst include CHANGELOG.rst include LICENCE include MMLFILE.rst recursive-include examples * recursive-include test * MMLlib-0.3.0.post1/MMLFILE.rst0000644000175000017500000000556013043255641015107 0ustar niknik00000000000000Description of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and https://www.mirbsd.org/man4/spkr . Symbols of MML -------------- :A-G[#,+,-][length]: A-G are notes. # or + following a note produces a sharp; - produces a flat. :L(n): Sets the length of each note. L4 is a quarter note, L1 is a whole note, and so on. n may be from 1 to 64. Length may also follow the note to change the length for that note only. A16 is equivalent to L16A. Default is L4. :ML: Music legato. Each note plays the full period set by L. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). :MS: Music staccato. Each note plays three-quarters of the time determined by L. :N(n): Play note n. n may range from 0 to 84. In the 7 possible octaves, there are 84 notes. n set to 0 (or omitted) indicates a rest. :O(n): Octave 0 sets the current octave. There are 7 octaves (0 through 6). Default is 4. Middle C is at the beginning of octave 2. :P(n): Pause. n may range from 1-64; the current L value is used if omitted. :T(n): Tempo. T sets the number of L4s in a minute. n may range from 32-255. Default is 120. :. (period): A period after a note increases the playing time of the note by 3/2 times the period determined by L (length of note) times T (tempo). Multiple periods can appear after a note, and the playing time is scaled accordingly. For example, A. will cause the note A to play one and half times the playing time determined by L (length of the note) times T (the tempo); two periods placed after A (A..) will cause the note to be played at 9/4 times its ascribed value; an A with three periods (A...) at 27/8, etc. Periods may also appear after a P (pause), and increase the pause length as described above. :>: A greater-than symbol raises the current octave by one. :<: A less-than symbol lowers the current octave by one. :\|: Optionally used as a synchronisation mark for multi-track music. This is a proprietary extension in the Floppi-Music project. Comments -------- Lines starting with # are comments. At the beginning of the file, comments may be used to encode metadata. This is yet to be specified. The current implementation parses key/value pairs separated by a colon and a space, strips both key and value, lower-cases the key and adds it to a dictionary. The MusicXML export currently specifically recognises these keys: - Title, Copyright, Encoder (person), Source - Composer, Lyrics, Arranger, Translator *xor* Artist (deprecated, only one) Any other key is treated as miscellaneous field. Voices ------ The voices of a song are interleaved. They are grouped per notation system, and the notation systems are seperated by empty lines. MMLlib-0.3.0.post1/README.rst0000644000175000017500000000452013043243431014744 0ustar niknik00000000000000MMLlib - Modern library for handling Music Macro Language ========================================================= About ----- MMLlib is a pure Python implementation of functionality related to the `Music Macro Language `__ as implemented by Microsoft® GW-BASIC® and compatibles, which is its most common form, also implemented by the PC speaker driver in Linux and `BSD `__, with a number of extensions and changes: - ``|`` denotes a bar line, ignored by the Linux/BSD driver - support for multiple (parallel) instrument tracks - a per-file header with work metadata (optional) - lines starting with a ``#`` are comments - the alias ``~`` is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - a duration estimate and the number of tracks is added to the metadata - return a list of tuples (frequency, duration) to play (in a threaded interpreter, such as Floppi-Music) - bar lines are indicated separately as integer ``1`` - return an event-oriented list of measures / bars across all tracks; each measure has a duration and a list of time-fixed tone on/off events; facilitates playing in non-threaded interpreters and, possibly, MIDI export - export (extended) MML to MusicXML - which can be imported by e.g. `MuseScore `__ to… - double-check the MML score for mistakes in a visual representation - play, arrange, etc. the music - beautify the score to print it as sheet music - export into other formats or share on their website - check tracks for synchronisation error - missing tracks, i.e. with 0 bars - missing bars (can only be detected at the end, of course) - missing notes within a bar, relative to other tracks Examples -------- Some example extended MML files are contained within the examples/ directory, in lieu of better documentation for the extended format, in addition to the `MML format documentation `__. Projects using MMLlib --------------------- ::Floppi-Music:: `Floppi-Music `__ has MML as input format for floppy drive music on Raspberry Pi and uses MMLlib for processing. Floppi-Music is also the origin of MMLlib. MMLlib-0.3.0.post1/setup.py0000644000175000017500000000574613043255660015011 0ustar niknik00000000000000#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ # Copyright © 2013, 2017 # Dominik George # # 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. from __future__ import with_statement import codecs import os from setuptools import setup, find_packages # Get some information for the setup MYDIR = os.path.dirname(__file__) long_description = "" for filename in ["README.rst", "MMLFILE.rst", "CHANGELOG.rst"]: with codecs.open(os.path.join(MYDIR, filename), "r", "utf-8") as f: long_description += f.read() + "\n" setup( # Basic information name = 'MMLlib', version = '0.3.0.post1', description = 'Modern library for handling Music Macro Language', long_description = long_description, license = 'MirOS', url = 'http://github.com/Natureshadow/mmllib', # Author information maintainer = 'Dominik George', maintainer_email = 'nik@naturalnet.de', # Included code packages = ["mmllib"], entry_points = { 'console_scripts': [ 'mml2musicxml = mmllib.cmds:mml2musicxml', 'mmllint = mmllib.cmds:mmllint' ] }, # Distribution information zip_safe = False, # install_requires = [ # '' # ], classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Artistic Software', 'Topic :: Multimedia :: Sound/Audio :: Conversion', 'Topic :: Software Development :: Libraries :: Python Modules' ], test_suite = 'test' ) MMLlib-0.3.0.post1/PKG-INFO0000644000175000017500000001705213043255733014366 0ustar niknik00000000000000Metadata-Version: 1.1 Name: MMLlib Version: 0.3.0.post1 Summary: Modern library for handling Music Macro Language Home-page: http://github.com/Natureshadow/mmllib Author: Dominik George Author-email: nik@naturalnet.de License: MirOS Description: MMLlib - Modern library for handling Music Macro Language ========================================================= About ----- MMLlib is a pure Python implementation of functionality related to the `Music Macro Language `__ as implemented by Microsoft® GW-BASIC® and compatibles, which is its most common form, also implemented by the PC speaker driver in Linux and `BSD `__, with a number of extensions and changes: - ``|`` denotes a bar line, ignored by the Linux/BSD driver - support for multiple (parallel) instrument tracks - a per-file header with work metadata (optional) - lines starting with a ``#`` are comments - the alias ``~`` is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - a duration estimate and the number of tracks is added to the metadata - return a list of tuples (frequency, duration) to play (in a threaded interpreter, such as Floppi-Music) - bar lines are indicated separately as integer ``1`` - return an event-oriented list of measures / bars across all tracks; each measure has a duration and a list of time-fixed tone on/off events; facilitates playing in non-threaded interpreters and, possibly, MIDI export - export (extended) MML to MusicXML - which can be imported by e.g. `MuseScore `__ to… - double-check the MML score for mistakes in a visual representation - play, arrange, etc. the music - beautify the score to print it as sheet music - export into other formats or share on their website - check tracks for synchronisation error - missing tracks, i.e. with 0 bars - missing bars (can only be detected at the end, of course) - missing notes within a bar, relative to other tracks Examples -------- Some example extended MML files are contained within the examples/ directory, in lieu of better documentation for the extended format, in addition to the `MML format documentation `__. Projects using MMLlib --------------------- ::Floppi-Music:: `Floppi-Music `__ has MML as input format for floppy drive music on Raspberry Pi and uses MMLlib for processing. Floppi-Music is also the origin of MMLlib. Description of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and https://www.mirbsd.org/man4/spkr . Symbols of MML -------------- :A-G[#,+,-][length]: A-G are notes. # or + following a note produces a sharp; - produces a flat. :L(n): Sets the length of each note. L4 is a quarter note, L1 is a whole note, and so on. n may be from 1 to 64. Length may also follow the note to change the length for that note only. A16 is equivalent to L16A. Default is L4. :ML: Music legato. Each note plays the full period set by L. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). :MS: Music staccato. Each note plays three-quarters of the time determined by L. :N(n): Play note n. n may range from 0 to 84. In the 7 possible octaves, there are 84 notes. n set to 0 (or omitted) indicates a rest. :O(n): Octave 0 sets the current octave. There are 7 octaves (0 through 6). Default is 4. Middle C is at the beginning of octave 2. :P(n): Pause. n may range from 1-64; the current L value is used if omitted. :T(n): Tempo. T sets the number of L4s in a minute. n may range from 32-255. Default is 120. :. (period): A period after a note increases the playing time of the note by 3/2 times the period determined by L (length of note) times T (tempo). Multiple periods can appear after a note, and the playing time is scaled accordingly. For example, A. will cause the note A to play one and half times the playing time determined by L (length of the note) times T (the tempo); two periods placed after A (A..) will cause the note to be played at 9/4 times its ascribed value; an A with three periods (A...) at 27/8, etc. Periods may also appear after a P (pause), and increase the pause length as described above. :>: A greater-than symbol raises the current octave by one. :<: A less-than symbol lowers the current octave by one. :\|: Optionally used as a synchronisation mark for multi-track music. This is a proprietary extension in the Floppi-Music project. Comments -------- Lines starting with # are comments. At the beginning of the file, comments may be used to encode metadata. This is yet to be specified. The current implementation parses key/value pairs separated by a colon and a space, strips both key and value, lower-cases the key and adds it to a dictionary. The MusicXML export currently specifically recognises these keys: - Title, Copyright, Encoder (person), Source - Composer, Lyrics, Arranger, Translator *xor* Artist (deprecated, only one) Any other key is treated as miscellaneous field. Voices ------ The voices of a song are interleaved. They are grouped per notation system, and the notation systems are seperated by empty lines. Changelog for MMLlib ==================== 0.3 --- - Enable Python 3 compatibility. - Add new mmllint script. - Include example MML songs. - Add first parts of test suite. - Some bugfixes. 0.2 --- - Add mml2musicxml script. 0.1 --- - Separate from Floppi-Music. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Artistic Software Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion Classifier: Topic :: Software Development :: Libraries :: Python Modules MMLlib-0.3.0.post1/setup.cfg0000644000175000017500000000007313043255733015105 0ustar niknik00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0