diff --git a/.gitignore b/.gitignore index 6b303ba..9a7f66e 100644 --- a/.gitignore +++ b/.gitignore @@ -165,6 +165,7 @@ cython_debug/ # VSCode .vscode -# Debugging file +# Debugging files debug.py +test.py diff --git a/test.py b/test.py index 28f0a4f..6a1e88c 100644 --- a/test.py +++ b/test.py @@ -1,4 +1,8 @@ from ziffers import * -# a = z1("q e 1 ^ ^ 2 _ 3 4 <3> 3") -a = z1("1 2") -print(a.take(3)) +from music21 import * + +z = z('e (1 2 3)+(4 2 1)*2') +s = to_music21(z,octave=-2,time="3/4") + +s.show() +s.show('midi') diff --git a/ziffers/__init__.py b/ziffers/__init__.py index 313cce0..36bf3c6 100644 --- a/ziffers/__init__.py +++ b/ziffers/__init__.py @@ -4,3 +4,4 @@ from .classes import * from .common import * from .defaults import * from .scale import * +from .converters import * diff --git a/ziffers/classes.py b/ziffers/classes.py index 0c51861..f595bc9 100644 --- a/ziffers/classes.py +++ b/ziffers/classes.py @@ -1,5 +1,5 @@ """ Ziffers classes for the parsed notation """ -from dataclasses import dataclass, field, replace +from dataclasses import dataclass, field, replace, asdict from itertools import product, islice, cycle import operator import random @@ -31,6 +31,9 @@ class Meta: if getattr(self, key) is None: setattr(self, key, value) + def dict(self): + return {k: str(v) for k, v in asdict(self).items()} + @dataclass(kw_only=True) class Item(Meta): diff --git a/ziffers/converters.py b/ziffers/converters.py new file mode 100644 index 0000000..d496d60 --- /dev/null +++ b/ziffers/converters.py @@ -0,0 +1,61 @@ +"""Collection of converters""" +from music21 import converter, note, stream, meter +from ziffers import zparse, Ziffers + +def to_music21(strData: str|Ziffers, **options): + """Helper for passing options to the parser""" + converter.registerSubconverter(ZiffersMusic21) + + if isinstance(strData,Ziffers): + if options: + options["preparsed"] = strData + else: + options = {"preparsed": strData} + options = {"ziffers": options} + return converter.parse("PREPARSED", format="ziffers", keywords=options) + + if options: + options = {"ziffers": options} + return converter.parse(strData, format="ziffers", keywords=options) + else: + test = converter.parse(strData, format="ziffers") + return test + +def set_musescore_path(path: str): + """Helper for setting the Musescore path""" + us = environment.UserSettings() + # Default windows path: + # 'C:\\Program Files\\MuseScore 3\\bin\\MuseScore3.exe' + us['musicxmlPath'] = path + us['musescoreDirectPNGPath'] = path + +class ZiffersMusic21(converter.subConverters.SubConverter): + """Ziffers converter to Music21""" + registerFormats = ("ziffers",) + registerInputExtensions = ("zf",) + + def parseData(self, strData, number=None): + """Parses Ziffers string to Music21 object""" + # Look for options in keywords object + keywords = self.keywords["keywords"] + if "ziffers" in keywords: + options = keywords["ziffers"] + if "preparsed" in options: + parsed = options["preparsed"] + else: + parsed = zparse(strData, **options) + else: + parsed = zparse(strData) + + s = stream.Part() + if "time" in options: + m = meter.TimeSignature(options["time"]) # Common time + else: + m = meter.TimeSignature("c") # Common time + + s.insert(0, m) + for z in parsed: + m_note = note.Note(z.note) + m_note.duration.quarterLength = z.duration * 4 + s.append(m_note) + self.stream = s.makeMeasures()