././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/0000755000175000017500000000000000000000000010617 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074485.0 MMLlib-1.4/CHANGELOG.rst0000644000175000017500000000166500000000000012650 0ustar00niknikChangelog for MMLlib ==================== 1.4 --- - Bump major version to enable semantic versioning - Add octave tracking feature. - Add basic support for MicroPython/ESP PWM - The “mmllint” command can now output “normalised” MML, that is, MML which will play on GW-BASIC without the extensions (such as octave tracking or permitting omission of the default argument values) present - “N” without number is now correctly parsed (as rest) - Update documentation wrt performance expectations, fine detail - ./run.py (in-tree) defaults to python3 as interpreter now - “O” without number now sets the default octave (extension), which was already implied in both documentation and consistency 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/LICENCE0000644000175000017500000000257100000000000011611 0ustar00niknikMMLlib is published under The MirOS Licence: Copyright © 2013, 2016, 2017, 2021 Dominik George Copyright © 2013, 2016, 2017, 2021 mirabilos 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 leans on spkr(4) — even though it does not descend from its codebase — whose authors are credited: • 1990 Eric S. Raymond (esr@snark.thyrsus.com) • 1990 Andrew A. Chernov (ache@astral.msk.su) • 1990 Lennart Augustsson (lennart@augustsson.net) • 2002, 2016, 2020 mirabilos (m@mirbsd.org) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/MANIFEST.in0000644000175000017500000000020300000000000012350 0ustar00niknikinclude README.rst include CHANGELOG.rst include LICENCE include MMLFILE.rst recursive-include examples * recursive-include test * ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/MMLFILE.rst0000644000175000017500000000716600000000000012450 0ustar00niknikDescription of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and http://www.mirbsd.org/man4/spkr which, in turn, are modelled after the IBM BASICA “PLAY” command. 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. While not explicitly specified, most implementations tie sequences of identical pitch where all (but the last) are styled legato. Otherwise, or if the pitch differs, the notes are played with a slur. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). This is the default. :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. The special values L and N control octave tracking. L enables octave tracking, N disables it; with octave tracking enabled, jumps between notes are limited to 6 half-tones, otherwise, the octave is incremented or decremented accordingly. Octave tracking is disabled by default. Octave tracking is suspended for one note after an octave change, including setting the default octave at the beginning of the track, and only applies to letter notes. :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. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.675159 MMLlib-1.4/MMLlib.egg-info/0000755000175000017500000000000000000000000013425 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074544.0 MMLlib-1.4/MMLlib.egg-info/PKG-INFO0000644000175000017500000002003400000000000014521 0ustar00niknikMetadata-Version: 2.1 Name: MMLlib Version: 1.4 Summary: Modern library for handling Music Macro Language Home-page: https://edugit.org/nik/mmllib Maintainer: Dominik George Maintainer-email: nik@naturalnet.de License: MirOS 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 License-File: LICENCE 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 ``~`` (itself a BSD extension) is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - return a normalised form of the MML that does not make use of the extensions and should PLAY on the original BASIC interpreter - a duration estimate and the number of tracks are 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 - export (currently only the first track of) MML to an NXC program - 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 - play a playlist (parsed MML) using PWM on GPIO pins on ESP boards under MicroPython (experimental; may not work for higher pitches) 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 from before it was spun off into a separate project of its own. Description of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and http://www.mirbsd.org/man4/spkr which, in turn, are modelled after the IBM BASICA “PLAY” command. 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. While not explicitly specified, most implementations tie sequences of identical pitch where all (but the last) are styled legato. Otherwise, or if the pitch differs, the notes are played with a slur. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). This is the default. :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. The special values L and N control octave tracking. L enables octave tracking, N disables it; with octave tracking enabled, jumps between notes are limited to 6 half-tones, otherwise, the octave is incremented or decremented accordingly. Octave tracking is disabled by default. Octave tracking is suspended for one note after an octave change, including setting the default octave at the beginning of the track, and only applies to letter notes. :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 ==================== 1.4 --- - Bump major version to enable semantic versioning - Add octave tracking feature. - Add basic support for MicroPython/ESP PWM - The “mmllint” command can now output “normalised” MML, that is, MML which will play on GW-BASIC without the extensions (such as octave tracking or permitting omission of the default argument values) present - “N” without number is now correctly parsed (as rest) - Update documentation wrt performance expectations, fine detail - ./run.py (in-tree) defaults to python3 as interpreter now - “O” without number now sets the default octave (extension), which was already implied in both documentation and consistency 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074544.0 MMLlib-1.4/MMLlib.egg-info/SOURCES.txt0000644000175000017500000000150600000000000015313 0ustar00niknikCHANGELOG.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 examples/pachelbel_canon.mml mmllib/__init__.py mmllib/cmds.py mmllib/esp.py mmllib/mml.py mmllib/musicxml.py mmllib/nxc.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/__pycache__/__init__.cpython-39.pyc test/__pycache__/test_musicxml.cpython-39.pyc test/__pycache__/test_parser.cpython-39.pyc test/__pycache__/test_playlist.cpython-39.pyc test/__pycache__/util.cpython-39.pyc test/data/loreley.mml test/data/loreley.xml././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074544.0 MMLlib-1.4/MMLlib.egg-info/dependency_links.txt0000644000175000017500000000000100000000000017473 0ustar00niknik ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074544.0 MMLlib-1.4/MMLlib.egg-info/entry_points.txt0000644000175000017500000000013100000000000016716 0ustar00niknik[console_scripts] mml2musicxml = mmllib.cmds:mml2musicxml mmllint = mmllib.cmds:mmllint ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074195.0 MMLlib-1.4/MMLlib.egg-info/not-zip-safe0000644000175000017500000000000100000000000015653 0ustar00niknik ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074544.0 MMLlib-1.4/MMLlib.egg-info/top_level.txt0000644000175000017500000000000700000000000016154 0ustar00niknikmmllib ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/PKG-INFO0000644000175000017500000002003400000000000011713 0ustar00niknikMetadata-Version: 2.1 Name: MMLlib Version: 1.4 Summary: Modern library for handling Music Macro Language Home-page: https://edugit.org/nik/mmllib Maintainer: Dominik George Maintainer-email: nik@naturalnet.de License: MirOS 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 License-File: LICENCE 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 ``~`` (itself a BSD extension) is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - return a normalised form of the MML that does not make use of the extensions and should PLAY on the original BASIC interpreter - a duration estimate and the number of tracks are 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 - export (currently only the first track of) MML to an NXC program - 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 - play a playlist (parsed MML) using PWM on GPIO pins on ESP boards under MicroPython (experimental; may not work for higher pitches) 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 from before it was spun off into a separate project of its own. Description of the (extended) music macro language ================================================== Based on http://www.antonis.de/qbebooks/gwbasman/play.html and http://www.mirbsd.org/man4/spkr which, in turn, are modelled after the IBM BASICA “PLAY” command. 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. While not explicitly specified, most implementations tie sequences of identical pitch where all (but the last) are styled legato. Otherwise, or if the pitch differs, the notes are played with a slur. :MN: Music normal. Each note plays seven-eighths of the time determined by L (length). This is the default. :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. The special values L and N control octave tracking. L enables octave tracking, N disables it; with octave tracking enabled, jumps between notes are limited to 6 half-tones, otherwise, the octave is incremented or decremented accordingly. Octave tracking is disabled by default. Octave tracking is suspended for one note after an octave change, including setting the default octave at the beginning of the track, and only applies to letter notes. :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 ==================== 1.4 --- - Bump major version to enable semantic versioning - Add octave tracking feature. - Add basic support for MicroPython/ESP PWM - The “mmllint” command can now output “normalised” MML, that is, MML which will play on GW-BASIC without the extensions (such as octave tracking or permitting omission of the default argument values) present - “N” without number is now correctly parsed (as rest) - Update documentation wrt performance expectations, fine detail - ./run.py (in-tree) defaults to python3 as interpreter now - “O” without number now sets the default octave (extension), which was already implied in both documentation and consistency 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/README.rst0000644000175000017500000000537600000000000012321 0ustar00niknikMMLlib - 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 ``~`` (itself a BSD extension) is not supported The library currently contains functions to: - parse an (extended) MML file into metadata and individual tracks - return a normalised form of the MML that does not make use of the extensions and should PLAY on the original BASIC interpreter - a duration estimate and the number of tracks are 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 - export (currently only the first track of) MML to an NXC program - 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 - play a playlist (parsed MML) using PWM on GPIO pins on ESP boards under MicroPython (experimental; may not work for higher pitches) 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 from before it was spun off into a separate project of its own. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.675159 MMLlib-1.4/examples/0000755000175000017500000000000000000000000012435 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/examples/alle_meine_entchen.mml0000644000175000017500000000034100000000000016740 0ustar00niknik# Title: Alle meine Entchen # Copyright: MML scores (c) 2013, Dominik George , The MirOS Licence # Now Playing: Alle meine Entchen o2 c d e f g2 g2 aaaa g1 aaaa g1 ffff e2 e2 dddd c1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/examples/bruder_jakob.mml0000644000175000017500000000214200000000000015574 0ustar00niknik# 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, 2013; mirabilos, 2016 # Copyright: © 2013 Steven David Tung (FLUTE.NET) 🄯 CC-BY-SA 4.0; MML scores © 2013 Dominik George; © 2016 mirabilos # Instruments: Flute # MML Tracks: 4 # Verses: 1 # Tempo: ♪ = 84 # Time Signature: 4/4 # Key Signature: D Major # Pickup Measure: none # Measures: 57 # Floppi-Music: sped up (from r42) and played 8vb (from o3) # Now Playing: Pachelbel Canon in D, for flute quartet, 2003, by Steven David Tung (CC-BY-SA) # 1 o2t54 p1 | p1 | f#edc# | c# | dc#df#edc# | c# | o2t54 ddddddddagbag | >f#edc# | c# | dc#dagbag | f#de>c#df#add.c#16 | # 11 l16>dc#ddc#c#f#ab | gf#egf#edc#c#df#add.c#16 | l8df#agf#df#e | ddagbag | l4db>c#dc#dc#c#f#ab | gf#egf#edc#c#df#add.c#16 | >df#e4pdf#4 | b4a4b4>c#4 | def#gaeagf#bagagf#e | db>c#dc#dc#c#f#ab | gf#egf#edc#dd4 | d4.ddgea | l8f#>f#e4pdf#4 | b4a4b4>c#4 | def#gaeagf#bagagf#e | db>c#dc#dc#def#gmnf#16mldemnf#16mld4 | l8f#>f#e4pdf#4 | >dmlc#dc#def#ga | d4.ddgea | b4a4b4>c#4 | gdga | # 21 mnf#16mldemnf#16mledec#def#edc#mnd16mlc#mnd16mldc#d | l32a16mlf#gmna16mlf#gmnac#def#gmnf#16mldemnf#16d4 | >dmldc#c#dmnmldc#mnd16mlc#c#dedc#dc# | mng16mlbamng16mlf#ef#edef#gabmng16mlbamnb16ml>c#dc#def#ga | d4.ddgea | gdga | # 23 l8mndpc#ppdp | mnf#16mldemnf#16mledec#def#edc#mnd16mlc#mnd16mldc#d | l32a16mlf#gmna16mlf#gmnac#def#gmnf#16mldemnf#16dmldc#mnc#dmnmldc#mnd16mlc#c#dedc#dc# | mng16mlbamng16mlf#ef#edef#gabmng16mlbamnb16ml>c#dc#def#ga | gdga | # 25 l4>dpdp | mnf#16mldemnf#16mledec#def#edc#mnd16mlc#mnd16mldc#d | l8papapf#pa | # 26 gdga | mldc#mnc#dmnmldc#mnd16mlc#c#dedc#dc# | pgpf#pgp>e | # 27 >de | l8mndpc#ppdp | ef#edc#c#c#c# | # 29 >def#edc#c#c#c# | papapf#pa | pgpf#pgp>e | ddedc#c#dc#c#degf#f#dgf#gedddddddec# | ddedc#c#dc#c#degf#f#dgf#geef#edc#c#c#c# | l4>df#f#f#eeeeddddaaaa | bbbbaaaabbbb>c#dddddddec# | ddedc#c#dc#c#degf#f#dgf#gedmlc#32d32mnec#c#32mndc#mlf#f#f#eeeeddddaaaa | f#aaaaaaaf#f#f#f#f#f#aa | >dc#dddddddec# | gdga | # 37 l4>dmlc#32d32mnec#c#32mndc#mlf#f#f#eeeeddddaaaa | l16d>mld32c#32mndd32e32mnf#df#mlf#32e32mndc# | # 38 gdga | dmlg32f#32mnegf#mld32e32mnf#agmlb32a32mngf#emla32g32mnf#e | bbbbaaaabbbb>c#c#dmlf#32e32mndf#gmld32c#32mndmld32c#32mndd32e32mnf#df#mlf#32e32mndc# | dmlc#32d32mnec#c#32mndc#mlc#dmlf#32e32mndf#gmld32c#32mndmld32c#32mndd32e32mnf#df#mlf#32e32mndc# | d>dl4c#c#dmlf#32e32mndf#gmld32c#32mnf#mlf#mngmlf#mned8.dmldmnemldmnc# | d>dl4c#dd4mldmncmlmncamlamnbmlamngf#8.f#mlf#mngmlf#mne | l16f#8.>f#mlf#mngmlf#mned8.dmldmnemldmnc# | d>dl4c#dmncdc#.c#16 | d4mldmncmlmncamlamnbmlamngf#8.f#mlf#mngmlf#mne | l16f#8.>f#mlf#mngmlf#mned8.dmldmnemldmnc# | >dmncdc#.c#16 | d4mldmncmlmncf#4ed>d4c | l4mld8amlamnbmlamngf#8.f#mlf#mngmlf#mne | mldmncmlmncdc#.c#16 | >dmlf#.mne16 | l4d.d8dc# | f#>f#4ed>d4c | l4mld8dmlf#.mne16 | l4d.d8dc#< | f#>f#4ed>d4c | l4mld8dc#gf#e | # 55 >dd4p4p2 || l8>dc#gf#e | f#4p4p2 || al8mlf#.mne16 | l4d.d8dc# | d4p4p2 || f#edc# | bae16 # Copyright © 2013, 2016, 2017, 2021 # 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 """ __all__ = [ "mml2musicxml", "mml2nxcmono", "mmllint", ] import argparse import sys from pprint import pprint from .musicxml import convert_mml_file as convert_mml_file_to_musicxml from .mml import mml from .nxc import convert_mml_track as convert_mml_track_to_nxc from .parser import mml_file from .playlist import convert_to_timedlist, timedlist_duration, timedlist_ntracks def _filter(func): # 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(func(args.path)) else: sys.stdout.write(func(args.path)) sys.stdout.write("\n") return 0 def mml2musicxml(): """ Entry point for converting MML to MusicXML Returns 0 on success, >0 on error. """ return _filter(convert_mml_file_to_musicxml) mml2musicxml._shortdesc = 'converts MML to MusicXML' def mml2nxcmono(): """ Entry point for converting MML to MusicXML Returns 0 on success, >0 on error. """ return _filter(convert_mml_track_to_nxc) mml2nxcmono._shortdesc = 'converts first MML track to NXC' def mmllint(): """ Entry point for checking MML Returns 0 on success, >0 on error. """ _normml = [] def _addmml(s): _normml.append(s) 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, _mmltrk=_addmml)] else: playlist = mml_file(args.path_or_mml, _mmltrk=_addmml) timedlist, errors = convert_to_timedlist(playlist) if not args.quiet: pprint(timedlist) i = 0 for trk in _normml: i += 1 print("Track #" + str(i) + ": " + trk) 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 mmllint._shortdesc = 'checks MML for some errors' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/esp.py0000644000175000017500000000330000000000000013230 0ustar00niknik# ~*~ coding: utf-8 ~*~ #- # Copyright © 2021 # 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. """ Utility code to play MML on hardware using ESP boards with MicroPython """ from machine import Pin, PWM import time def play(playlist, pin, duty=512): """ Play a playlist on a buzzer on a GPIO pin. playlist - the music to play pin - the number of the pin (which must support PWM) duty - the duty cycle to use (default 512 = 50%) """ beeper = PWM(Pin(pin)) for freq, length in playlist: # Scale down frequency because PWM only supports up to 1 kHz while freq > 1000: freq /= 2 if freq == 0: # Frequency 0 breaks PWM; turn down volume instead beeper.duty(0) else: beeper.duty(duty) beeper.freq(int(freq)) time.sleep(length) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/mml.py0000644000175000017500000002672000000000000013241 0ustar00niknik# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016, 2021 # 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} # Map half-tone steps to valid/normalised MML pitch inputs _MML_STEP2NAME_IS = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] _MML_STEP2NAME_ES = ['C', 'D-', 'D', 'E-', 'E', 'F', 'G-', 'G', 'A-', 'A', 'B-', 'B'] # 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. """ if not (macro and macro[0].isdigit()): return defval num = "" while macro and macro[0].isdigit(): num += macro.pop(0) i = int(num) if i < minval or i > maxval: return defval return i def mml(macro, _nplay=None, _barline=None, _mmltrk=None): """ Parse a string in the "music macro language" The resulting 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) _mmltrk - a function called with a normalised form of the MML (None) 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 octave_tracking = False octave_tracking_onhold = True octave_tracking_last_note = 12 * octave timevalue = 4 # result normalised MML normml = ["O4L4T120MN "] # 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, normml, res, bpm, art, note, length, extra): # parse sustain dots ndots = 0 while macro and macro[0] == ".": macro.pop(0) ndots += 1 normml[0] += "." # play the note or append to a playlist _nplay(res, bpm, art, note, length, ndots, extra) while macro: char = macro.pop(0) if char in _MML_NAME2STEP.keys(): # base note bnote = char note = 12 * octave + _MML_NAME2STEP[char] acc = u'♮' # accidental sign if macro and macro[0] in ("#", "+"): if note < 83: note += 1 acc = u'♯' bnote = _MML_STEP2NAME_IS[note % 12] if bnote == 'C': normml[0] += ">" macro.pop(0) elif macro and macro[0] == "-": if note > 0: note -= 1 acc = u'♭' bnote = _MML_STEP2NAME_ES[note % 12] if bnote == 'B': normml[0] += "<" macro.pop(0) # octave tracking implementation if octave_tracking and not octave_tracking_onhold: if (note < 72) and (abs(note - octave_tracking_last_note) > abs(note + 12 - octave_tracking_last_note)): normml[0] += ">" octave += 1 note += 12 if (note >= 12) and (abs(note - octave_tracking_last_note) > abs((note - 12) - octave_tracking_last_note)): normml[0] += "<" octave -= 1 note -= 12 octave_tracking_onhold = False octave_tracking_last_note = note # final calculated pitch extra = (octave, char, acc) normml[0] += bnote # length _length = _getint(macro, 1, 64, -1) if _length != -1: normml[0] += str(_length) else: _length = timevalue # parse sustain dots, append note to playlist _play(macro, normml, res, bpm, art, note, _length, extra) elif char == "L": timevalue = _getint(macro, 1, 64, 4) normml[0] += "L" + str(timevalue) elif char == "M": if macro: char = macro.pop(0) if char in ("L", "N", "S"): art = char normml[0] += "M" + char elif char == "N": note = _getint(macro, 0, 84, 0) normml[0] += "N" + str(note) note -= 1 _play(macro, normml, res, bpm, art, note, timevalue, note) elif char == "O": if macro and macro[0] == "N": octave_tracking = False octave_tracking_onhold = False macro.pop(0) elif macro and macro[0] == "L": octave_tracking = True macro.pop(0) else: octave = _getint(macro, 0, 6, 4) normml[0] += "O" + str(octave) octave_tracking_onhold = True elif char == ">": if octave < 6: octave += 1 normml[0] += char octave_tracking_onhold = True elif char == "<": if octave > 0: octave -= 1 normml[0] += char octave_tracking_onhold = True elif char == "P": _length = _getint(macro, 1, 64, timevalue) normml[0] += "P" + str(_length) _play(macro, normml, res, bpm, art, -1, _length, -1) elif char == "T": bpm = _getint(macro, 32, 255, 120) normml[0] += "T" + str(bpm) elif char == "|": normml[0] += " " _barline(res) elif char == "X": while macro and macro[0] != ";": macro.pop(0) if _mmltrk is not None: _mmltrk(normml[0].replace('<>', '').replace('><', '').strip()) return res ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/musicxml.py0000644000175000017500000003603300000000000014313 0ustar00niknik# ~*~ 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 try: str = unicode except: pass ## 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['translator'] 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/nxc.py0000644000175000017500000001101600000000000013234 0ustar00niknik# ~*~ coding: utf-8 ~*~ #- # Copyright © 2017 # 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 an NXC program """ import re from copy import deepcopy from .mml import MML_NOTE2PITCH from .parser import mml_file, mml_file_meta def _nxc_meta(meta, fn): s = u"/*-\n * Automatically converted from " + fn + "\n * using MMLlib by Natureshadow, Creeparoo, and mirabilos\n *" # Use deep copies because we change and delete entries meta = deepcopy(meta) # carry over floppi.music-specific metadata if 'title' in meta: s = s + "\n * " + meta['title'] del meta['title'] if 'composer' in meta: s = s + "\n * by " + meta['composer'] del meta['composer'] if 'lyrics' in meta: s = s + "\n * Lyrics: " + meta['lyrics'] del meta['lyrics'] if 'arranger' in meta: s = s + "\n * Arranger: " + meta['arranger'] del meta['arranger'] if 'translator' in meta: s = s + "\n * Translator: " + meta['translator'] del meta['translator'] if 'artist' in meta: s = s + "\n * Artist: " + meta['artist'] del meta['artist'] if 'encoder' in meta: s = s + "\n * Encoder: " + meta['encoder'] del meta['encoder'] if 'source' in meta: s = s + "\n * Source: " + meta['source'] del meta['source'] if 'copyright' in meta: s = s + "\n *\n * " + meta['copyright'] del meta['copyright'] s = s + "\n *\n * +++ Further metadata +++" meta["duration"] = int(meta["duration"]) for tmp in sorted(meta): s = s + (u"\n * %s: %s" % (tmp, meta[tmp])) s = s.replace('*/', '*\\') + "\n */\n\n" return s.encode("UTF-8") def _nxc_track(trklist, name): s = "Tone " + name + "[] = {\n" for ply in trklist: if isinstance(ply, tuple): s = s + "\t{ " + str(int(ply[0] + .5)) + ", " + \ str(int(ply[1] * 1000 + .5)) + " },\n" else: s = s + "\t/* barline */\n" s = s + "};\n" return s.encode("UTF-8") def _create_nxc_mono(meta, staves, fn): hdr = _nxc_meta(meta, fn) trk = _nxc_track(staves[0], "melody") if 'title' in meta: npu = meta['title'] else: npu = fn if 'composer' in meta: npu = npu + ' by ' + meta['composer'] if 'lyrics' in meta: npu = npu + '/' + meta['lyrics'] if 'arranger' in meta: npu = npu + '/' + meta['arranger'] elif 'artist' in meta: npu = npu + ' by ' + meta['artist'] if 'source' in meta: npu = npu + ' from ' + meta['source'] if 'now playing' in meta: npu = meta['now playing'] npe = ''.join([x == '\t' and ' ' or (x if ord(x) >= 32 and ord(x) < 127 else '') for x in list(npu)]).encode('UTF-8') drw = ("\nvoid drawFilename() {\n" + \ '\n'.join(["\tTextOut(0, LCD_LINE%d, \"%s\");" % (n+1, npe[n*16:(n+1)*16].decode('UTF-8').replace('\\', '\\\\').replace('"', '\\"').rstrip()) for n in range(8)]) + \ "\n}\n").encode("UTF-8") return hdr + trk + drw + '\n'.join([''.join([chunk == ' ' and '\t' or chunk for chunk in re.findall(' |.+', line[8:])]) for line in """ task main() { ClearScreen(); drawFilename(); PlayTones(melody); } """.splitlines()[:-1]]).encode("UTF-8") def convert_mml_track(filename): """ Convert first track of MML file to NXC Reads the MML file twice (once for metadata, once for data), converts it to an NXC program. filename - the MML file Returns the NXC program. """ meta = mml_file_meta(filename) staves = mml_file(filename) return _create_nxc_mono(meta, staves, filename) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/parser.py0000644000175000017500000001001600000000000013737 0ustar00niknik# ~*~ coding: utf-8 ~*~ #- # Copyright © 2016, 2021 # 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, _mmltrk=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() _mmltrk - the override mmltrk 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, _mmltrk)) 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/mmllib/playlist.py0000644000175000017500000001761600000000000014321 0ustar00niknik# ~*~ 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) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/setup.cfg0000644000175000017500000000004600000000000012440 0ustar00niknik[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074469.0 MMLlib-1.4/setup.py0000644000175000017500000000626400000000000012341 0ustar00niknik#!/usr/bin/env python3 # ~*~ 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" # see https://stackoverflow.com/a/1178429/2171120 import sys if sys.version_info < (2, 6, 0): reload(sys).setdefaultencoding("UTF-8") setup( # Basic information name = 'MMLlib', version = '1.4', description = 'Modern library for handling Music Macro Language', long_description = long_description, license = 'MirOS', url = 'https://edugit.org/nik/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 = [ # '' # ], # Recommends: argparse (for mmllib.cmds, so not a hard Depends for the library) 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' ) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/test/0000755000175000017500000000000000000000000011576 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/test/__init__.py0000644000175000017500000000000000000000000013675 0ustar00niknik././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/test/__pycache__/0000755000175000017500000000000000000000000014006 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074196.0 MMLlib-1.4/test/__pycache__/__init__.cpython-39.pyc0000644000175000017500000000017400000000000020200 0ustar00niknika za@sdS)Nrrr!/home/nik/mmllib/test/__init__.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074196.0 MMLlib-1.4/test/__pycache__/test_musicxml.cpython-39.pyc0000644000175000017500000000177300000000000021347 0ustar00niknika za@s>ddlmZddlZddlZddlmZGdddejZdS))with_statementN)convert_mml_filec@seZdZddZddZdS) MusicXMLTestscCstjtjtd|_dS)Ndata)ospathjoindirname__file__datadir)selfr &/home/nik/mmllib/test/test_musicxml.pysetUpszMusicXMLTests.setUpcCsrtj|jd}tj|jd}t|}t|d}|}Wdn1sP0Y|||dddS)Nz loreley.mmlz loreley.xmlrb)rrrr ropenread assertEqual)r ZmmlfileZxmlfilexmlfexpectedr r rtest_convert_mml_file s  &z#MusicXMLTests.test_convert_mml_fileN)__name__ __module__ __qualname__rrr r r rrsr) __future__rrunittestZmmllib.musicxmlrTestCaserr r r rs  ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074196.0 MMLlib-1.4/test/__pycache__/test_parser.cpython-39.pyc0000644000175000017500000001410000000000000020766 0ustar00niknika zau@sPddlZddlZddlmZmZddlmZmZddlm Z GdddeZ dS)N)round_sequence_rTestCase)mml_file mml_file_meta)mmlc@s,eZdZddZddZddZddZd S) ParserTestscCstjtjtd|_dS)Ndata)ospathjoindirname__file__datadir)selfr$/home/nik/mmllib/test/test_parser.pysetUp szParserTests.setUpcCsRtj|jd}t|}dddddddd d d d d ddddddd}|||dS)N loreley.mmlz&Klavier: August Linder; MML: mirabilosu)Ph. Friedrich Silcher (1789–1860), 1837uMML encoding & arrangement © 2016 mirabilos, published under The MirOS Licence; copyright for text, music, and piano arrangement has expiredg(B@z&mirabilos, 2016, Python testsuite 2017z Piano, VoicezC MajorZGermanu"Heinrich Heine (1797–1856), 1824Z16z4 (5)z1/8uLinder, August (Hrsg.): Deutsche Weisen : Die beliebtesten Volks- und geistlichen Lieder. Stuttgart: Albert Auer’s Musikverlag, n.d., c. 1900.ZAndantez6/8ZLoreley3)arrangercomposer copyrightdurationencoderZ instrumentsz key signaturelanguagelyricsZmeasuresz mml trackszpickup measuresourcetempoztime signaturetitleZversesvoices)r r r rrassertDictEqual)rmmlfileZres_metaZ expected_metarrrtest_mml_file_meta#s,zParserTests.test_mml_file_metacCsLtj|jd}t|}gdgdgdgdg}|t|t|dS)Nr))GNx@g(?rg8P^C?rr%)?{@篡?rg8P^C?r%篡?rg8P^C?VPZ@6P^C?y"~@r.r/r*r.r/rr%Dy 5?rg(?֦u@篡?r&r:r.r/r{y t@Dy 5?rg(?r>r+r,r>r.r/! Zr@r2VPZp@r.r/rDr.r/rr>P^Cy?rgk?r-r/rr'r)r,r-r/r0r3r/r5r/rr6r8r9r&r<r/rr=r@rAr,rBr/r-r/r<r/rGr/rrFrIrJrBr/r)rDr(rAr,rGr/r-r/rGr/rGr/r)r4r7r8)r*r;r&r5r/r)r%r;r&r-r/)g 'w@r2r-r/r5r/r)r%?r&r-r/r)r%r?r@r)r,r-r/r0r3r/r5r/r)r%6P^C?)g{y @r.r/)! Z@r;r&)rNr.r/r)r1r?r@)r1r+r,)r1r.r/)r4r2r5r/r3r/r)r1rIrJr)rr2rr>r(r:r+r,rBr/r:gk(?r/r<r/rr>r7r8rDr;r&rGr/rrFr?r@rFr+r,rEr/y"n@r2k@r.r/rXr.r/rrKrgz 5?rrPrQr,rBr/rRr/r<r/rrSr8rTr&rGr/rrUr@rVr,rEr/r[r/r[r/r[r/rrg)@r)rXr(rVr,r[r/r[r/r[r/r[r/r)rDr7r8)rFr;r&rEr/r)rXr;rgDy 5?rZr2r[r/rEr/rGNX@r2)y"^@r2VPZ`@r2! Zb@r2rc)rbr.r/rr=r@rQr,rBr/rRr/r<r/r)r>rMr-r/r9r&r<r/rr=r@rAr,rBr/rRr/r<r/rrHrJr)FrOrr]rr]r)rg)?)GNh@r;r&rgr.r/rrgr2rgDy 5?rr]rr]rrg(?rfr.r/rlr/֦e@r.r/r{y d@r2rjrrkrGr\rr6r8)r>r;r&rBr/rrTr&rGr/)rfr;r&rlr/r)rfrLr&r<r/rr]rr]rrkrhr/rhr/rhr/rri)rg6P^C?r)rOrrd6P^C?rgrrrFr2rhr/rqrZrrrCr[r/rrqrsrtrhr/g֦U@rrrnrrr_rmr/rrarr)rprrri)rpr.r/)rar;r&rar.r/rrcr`g|y T@r2VPZP@r;r^rrqrsrtrhr/rqrurCr[r/rrqrsrtrhr/rvrwr_rmr/rryr/rhr/rdr.r/ryr/ryr/ryr/rrcrzr`r{r^rr`rerhr/r`rerhr/rr`rerhr/)r|r2rorYr/r)g ZR@r2rer[r\)g 'g@r.r/rrWrCr>r2)r:r2r~rGr/rrqrsrtrhr/rqrurCr[r/rrqrsrVrgk(?rvru)rDr+rrrxrsrVrrGr/rEr/rGr/rrcrorir}rJr)r r r rrassertListEqualr)rr#resexpectedrrr test_mml_file:sFuzParserTests.test_mml_filecs`d}d}gd}ddgfdd}t||dd<|d||tdt|dS) Nz-ml|ol o bc n cb p bc- p cb+ p o5cbf p o5cn60fz1O4L4T120MN ML O4B>CN0C._getres)_mmltrkr)r assertEqualrr)rinputZ expected_mmlZ expected_lstrrrrtest_mml_details1s zParserTests.test_mml_detailsN)__name__ __module__ __qualname__rr$rrrrrrrszr) r unittestutilrrZ mmllib.parserrrZ mmllib.mmlrrrrrrs  ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074196.0 MMLlib-1.4/test/__pycache__/test_playlist.cpython-39.pyc0000644000175000017500000001176200000000000021346 0ustar00niknika zaQ@sLddlZddlZddlmZmZddlmZddlmZGdddeZ dS)N)round_sequence_rTestCase)mml_file)convert_to_timedlistc@seZdZddZddZdS) PlaylistTestscCstjtjtd|_dS)Ndata)ospathjoindirname__file__datadir)selfr&/home/nik/mmllib/test/test_playlist.pysetUpszPlaylistTests.setUpc!Csvtj|jd}tt|\}}dddgfddgfgfddgdfd d gfd d gfd ddgfdddgfdgdfdgdfdgdfddgfdddgfdddgfdgdfd gdfg fddgdfd d gfd d gfdd gfd!ddgfdd"gfdgd#fdd$gfddgfd%ddgfdgd&fd gdfg fddgd'fd d(gfd d gfd)ddgfd d*d+gfdddgfdgd,fdgdfdgd-fdd.d/gfd%d0d"gfdddgfdgd-fd gd1fgfddgd2fd d0d3gfdd4gfdd5gfd6ddgfd%d"gfddgfd dgfgfddgdfd d gfd d gfd ddgfdddgfdgdfdgdfdgdfddgfdddgfdddgfdgdfd gdfg fddgdfd d gfd d gfdd gfd!ddgfdd"gfdgd#fdd$gfddgfd%ddgfdgd&fd gdfg fddgd'fd7d"gfd d gfd)ddgfd d*d+gfd8d"gfdddgfdgd9fdgdfdgd:fd6gd1fdgd;fdgd1fdgd<fd gd1fgfddgd=fd d0d4gfdd3gfdd5gfd6dgfd%d"gfdd*gfd dgfgfddgd>fd d?gfd d*d+gfdddgfdgd@fdgdfdgdAfd6gdBfdgdCfdddgfdgd@fd gdfg fddgdDfd d?gfdd gfd!gdBfdd"gfdgdEfdd(gfd%gdBfdgdFfd gd1fg fddgdGfd d?gfdHgdBfdgdIfdgdJfdgdKfdddLgfd%d0gfdddgfdgdMfd gd1fg fddgdNfd dOdgfddPdQgfddRdSgfddPdQgfd%dd0gfdgdTfd gd1fgfddgdfd d gfd d gfd)ddgfd ddgfdddgfdgdfdgdfdgdfddgfdddgfdddgfdgdfd gdfgfddgdfd d gfd d gfdUd"gfddVdWgfdddgfdgdXfddgfddgfdYd"gfd%ddgfddZdgfd ddgfg fddgd[fd d gfd d gfd)ddgfdUd"gfd d\d]gfdddgfdd\d]gfdddgfdgd^fd6d0d"gfdgd_fdgd1fdgd^fd gd1fgfd`dgdafd d0d(gfdd gfddbgfd6gdfgfg}|t|t|||gdS)cNz loreley.mmlg6P^C?grGNx@gm{?rrg)@)r{y t@VPZ`@gĬC?rGNh@gP0AC?rVPZp@gm{?rg{@r֦u@gb=y?rrg3VC?)rrrgb(?)rr#rrg߉Y/?)rgVPZ@r!rg'$?rk@g3VC?rgy"~@r! Zr@g|'f?gNz1?)r r!ry"n@gyt@gNz1?r$)rr"rr*rg֦U@r֦e@g?)r-r.r0)rrrrrGNX@r{y d@gNz1?r2r3)r2r3r6)rr*rr,rr4rrrr'r;r)rr#r>r$)r2r3r:rr4)rg{y T@rgVPZP@gb=y?g^c?g:;%?)r2r3r)rr9r;! Zb@r4)r-r9r@r4)r8r9)r;r1r4)r<)r;r7r)r8r9r4rrA)r8r9r)rr9r;r*r4)rr#r>)r8r9rB)r(r.)r;rr4)r r3r;rr?)r r3rDr&)rr9rC)rg ZR@g^c?)rrCr+)rr>r$))rg 'w@r=r@r9)r r3r@)rg 'g@)r)rr5r@r+rgy"^@)rr)rr)rrA)rr")rrE)r;r"r)gb(?)rg{y @)rr)rg! Z@r!r/g%?rF)r%rr4r%r)r(r!r:r))r r:rgDy 5?)r%rr:rr) r r r rrrassertListEqualr assertEqual)rmmlfilereserrorsexpectedrrrtest_convert_to_timedlist!s                                    "                                              fz'PlaylistTests.test_convert_to_timedlistN)__name__ __module__ __qualname__rrMrrrrrsr) r unittestutilrrZ mmllib.parserrZmmllib.playlistrrrrrrs   ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640074196.0 MMLlib-1.4/test/__pycache__/util.cpython-39.pyc0000644000175000017500000001011300000000000017410 0ustar00niknika zaN@shddlZddlZd ddZejdkrRddlZddlZd ddZGd d d ejZnGd d d ejZdS)NcCsFt|tst|tr*t|dd|DSt|tr>t||S|SdS)NcSsg|] }t|qS)round_sequence_r).0xrr/home/nik/mmllib/test/util.py z$round_sequence_r..) isinstancelisttupletypefloatround)seqdigitsrrrrs   r)rrFcCsRd}z t|}Wnty,t|}Yn0|r>t||krB|S|d|dS)NPz [truncated]...)repr Exceptionobject__repr__len)objshort _MAX_LENGTHresultrrr safe_repr)s  rc@s:eZdZd ddZd ddZdddZdd Zd d ZdS)TestCaseNc Cs0t}|j}t||s*|d|t|ft||sJ|d|t|fd}z t|}Wnttfyxd|}Yn0|durz t|}Wnttfyd|}Yn0|dur||krdSt|} t|} t| dkr| ddd} t| dkr| ddd} || | f} d| }t t ||D]} z || } Wn2tt tfyt|d| |f7}YqYn0z || }Wn2tt tfy|d | |f7}YqYn0| |kr2|d | t| t|f7}qq2||kr|durt |t |krdS||krv|d |||f7}z|d |t||f7}Wn*tt tfyr|d ||f7}Yn0nf||kr|d|||f7}z|d |t||f7}Wn*tt tfy|d||f7}Yn0|}dd tt|t|}|||}|||}||dS)NzFirst sequence is not a %s: %szSecond sequence is not a %s: %sz(First %s has no length. Non-sequence?z)Second %s has no length. Non-sequence?z...z%ss differ: %s != %s z( Unable to index element %d of first %s z) Unable to index element %d of second %s z# First differing element %d: %s %s z+ First %s contains %d additional elements. zFirst extra element %d: %s z'Unable to index element %d of first %s z, Second %s contains %d additional elements. z(Unable to index element %d of second %s  )r __name__r failureExceptionrr TypeErrorNotImplementedError capitalizeZxrangemin IndexErrorr joindifflibndiffpprintpformat splitlines_truncateMessage_formatMessagefail)selfseq1seq2msgseq_type seq_type_name differinglen1len2Z seq1_reprZ seq2_reprelementsiitem1item2 standardMsgdiffMsgrrrassertListEqual4s                        zTestCase.assertListEqualc Cs||td||td||krdt|dt|df}ddtt|t|}| ||}| | ||dS)Nz"First argument is not a dictionaryz#Second argument is not a dictionaryz%s != %sTr ) assertIsInstancedictrr(r)r*r+r,r-r.r0r/)r1d1d2r4r>diffrrrassertDictEquals    zTestCase.assertDictEqualcCs0t||s,dt||f}||||dS)Nz%s is not an instance of %r)r rr0r/)r1rclsr4r>rrrrAs zTestCase.assertIsInstancecCs||SNr)r1messagerErrrr.szTestCase._truncateMessagecCs|p|SrHr)r1r4r>rrrr/szTestCase._formatMessage)N)N)N)r! __module__ __qualname__r@rFrAr.r/rrrrr3s  X rc@s eZdZdS)rN)r!rJrKrrrrrs)r)F)sysunittestr version_infor)r+rrrrrrs  q././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1640074544.683159 MMLlib-1.4/test/data/0000755000175000017500000000000000000000000012507 5ustar00niknik././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/test/data/loreley.mml0000644000175000017500000001002000000000000014662 0ustar00niknik# Title: Loreley # Composer: Ph. Friedrich Silcher (1789–1860), 1837 # Lyrics: Heinrich Heine (1797–1856), 1824 # Arranger: Klavier: August Linder; MML: mirabilos # Encoder: mirabilos, 2016, Python testsuite 2017 # Copyright: MML encoding & arrangement © 2016 mirabilos, published under The MirOS Licence; copyright for text, music, and piano arrangement has expired # Source: Linder, August (Hrsg.): Deutsche Weisen : Die beliebtesten Volks- und geistlichen Lieder. Stuttgart: Albert Auer’s Musikverlag, n.d., c. 1900. # Instruments: Piano, Voice # MML Tracks: 4 (5) # Verses: 3 # Language: German # Tempo: Andante # Time Signature: 6/8 # Key Signature: C Major # Pickup Measure: 1/8 # Measures: 16 ## Structure: # Track 1: voice track # Text embedded here, as comments (not exported) – roughly note-aligned # Track 2: usually lowest voice double, except in bar 12 # Track 3: occasional helper # Track 4: bass background o2t76l8msg| mlg. mna16 g ml>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 | mlLoreleyPh. 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, 2016, Python testsuite 2017MMLlib 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/test/test_musicxml.py0000644000175000017500000000276100000000000015056 0ustar00niknik# ~*~ 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") def test_convert_mml_file(self): mmlfile = os.path.join(self.datadir, "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]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/test/test_parser.py0000644000175000017500000007261200000000000014513 0ustar00niknik# ~*~ coding: utf-8 ~*~ # Copyright © 2017 # Dominik George # Copyright © 2021 # 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. import os import unittest from .util import round_sequence_r, TestCase from mmllib.parser import mml_file, mml_file_meta from mmllib.mml import mml class ParserTests(TestCase): def setUp(self): self.datadir = os.path.join(os.path.dirname(__file__), "data") def test_mml_file_meta(self): mmlfile = os.path.join(self.datadir, "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, Python testsuite 2017', '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.datadir, "loreley.mml") res = mml_file(mmlfile) expected = [[(391.99543598174927, 0.29605263157894735), (0, 0.098684210526315819), 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(round_sequence_r(res), round_sequence_r(expected)) def test_mml_details(self): input = 'ml|ol o bc n cb p bc- p cb+ p o5cbf p o5cn60f' expected_mml = "O4L4T120MN ML O4B>CN0CC (1975.533205024496, 0.5), (2093.004522404789, 0.5), (0, 0.5), # C B → C # # 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, TestCase from mmllib.parser import mml_file from mmllib.playlist import convert_to_timedlist class PlaylistTests(TestCase): def setUp(self): self.datadir = os.path.join(os.path.dirname(__file__), "data") def test_convert_to_timedlist(self): mmlfile = os.path.join(self.datadir, "loreley.mml") res, errors = convert_to_timedlist(mml_file(mmlfile)) expected = [(0.39473684210526316, [(0.0, [(1, 391.99543598174927)]), (0.29604999999999998, [(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, []) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640073850.0 MMLlib-1.4/test/util.py0000644000175000017500000001611600000000000013132 0ustar00niknik# ~*~ 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 sys import unittest 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 # The following is a backport of some of Python 2.7’s new # unittest functions, under the Python licence which you # already accepted seeming as you run this under Python. # No rights are claimed for this by the MMLlib developers. if sys.version_info < (2, 7, 0): import difflib import pprint def safe_repr(obj, short=False): _MAX_LENGTH = 80 try: result = repr(obj) except Exception: result = object.__repr__(obj) if not short or len(result) < _MAX_LENGTH: return result return result[:_MAX_LENGTH] + ' [truncated]...' class TestCase(unittest.TestCase): def assertListEqual(self, seq1, seq2, msg=None): seq_type=list seq_type_name = seq_type.__name__ if not isinstance(seq1, seq_type): raise self.failureException('First sequence is not a %s: %s' % (seq_type_name, safe_repr(seq1))) if not isinstance(seq2, seq_type): raise self.failureException('Second sequence is not a %s: %s' % (seq_type_name, safe_repr(seq2))) differing = None try: len1 = len(seq1) except (TypeError, NotImplementedError): differing = 'First %s has no length. Non-sequence?' % ( seq_type_name) if differing is None: try: len2 = len(seq2) except (TypeError, NotImplementedError): differing = 'Second %s has no length. Non-sequence?' % ( seq_type_name) if differing is None: if seq1 == seq2: return seq1_repr = safe_repr(seq1) seq2_repr = safe_repr(seq2) if len(seq1_repr) > 30: seq1_repr = seq1_repr[:30] + '...' if len(seq2_repr) > 30: seq2_repr = seq2_repr[:30] + '...' elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr) differing = '%ss differ: %s != %s\n' % elements for i in xrange(min(len1, len2)): try: item1 = seq1[i] except (TypeError, IndexError, NotImplementedError): differing += ('\nUnable to index element %d of first %s\n' % (i, seq_type_name)) break try: item2 = seq2[i] except (TypeError, IndexError, NotImplementedError): differing += ('\nUnable to index element %d of second %s\n' % (i, seq_type_name)) break if item1 != item2: differing += ('\nFirst differing element %d:\n%s\n%s\n' % (i, safe_repr(item1), safe_repr(item2))) break else: if (len1 == len2 and seq_type is None and type(seq1) != type(seq2)): # The sequences are the same, but have differing types. return if len1 > len2: differing += ('\nFirst %s contains %d additional ' 'elements.\n' % (seq_type_name, len1 - len2)) try: differing += ('First extra element %d:\n%s\n' % (len2, safe_repr(seq1[len2]))) except (TypeError, IndexError, NotImplementedError): differing += ('Unable to index element %d ' 'of first %s\n' % (len2, seq_type_name)) elif len1 < len2: differing += ('\nSecond %s contains %d additional ' 'elements.\n' % (seq_type_name, len2 - len1)) try: differing += ('First extra element %d:\n%s\n' % (len1, safe_repr(seq2[len1]))) except (TypeError, IndexError, NotImplementedError): differing += ('Unable to index element %d ' 'of second %s\n' % (len1, seq_type_name)) standardMsg = differing diffMsg = '\n' + '\n'.join( difflib.ndiff(pprint.pformat(seq1).splitlines(), pprint.pformat(seq2).splitlines())) standardMsg = self._truncateMessage(standardMsg, diffMsg) msg = self._formatMessage(msg, standardMsg) self.fail(msg) def assertDictEqual(self, d1, d2, msg=None): self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') if d1 != d2: standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True)) diff = ('\n' + '\n'.join(difflib.ndiff( pprint.pformat(d1).splitlines(), pprint.pformat(d2).splitlines()))) standardMsg = self._truncateMessage(standardMsg, diff) self.fail(self._formatMessage(msg, standardMsg)) def assertIsInstance(self, obj, cls, msg=None): if not isinstance(obj, cls): standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) self.fail(self._formatMessage(msg, standardMsg)) def _truncateMessage(self, message, diff): return message + diff def _formatMessage(self, msg, standardMsg): return msg or standardMsg else: class TestCase(unittest.TestCase): pass