|
|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
"""Collection of converters"""
|
|
|
|
|
from ziffers import zparse, Ziffers, Pitch, Rest, Chord
|
|
|
|
|
from ziffers import zparse, Ziffers, Pitch, Rest, Chord, accidentals_from_note_name
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from music21 import converter, note, stream, meter, chord, environment
|
|
|
|
|
from music21 import converter, note, stream, meter, chord, environment, tempo, key
|
|
|
|
|
music21_imported: bool = True
|
|
|
|
|
except ImportError:
|
|
|
|
|
music21_imported: bool = False
|
|
|
|
|
@ -13,52 +13,26 @@ try:
|
|
|
|
|
except (ImportError, TypeError) as Error:
|
|
|
|
|
csound_imported: bool = False
|
|
|
|
|
|
|
|
|
|
def freq_to_pch(freq: float) -> str:
|
|
|
|
|
"Format frequency to Csound PCH format"
|
|
|
|
|
return f"{freq:.2f}"
|
|
|
|
|
def ziffers_to_csound_score(ziffers: Ziffers, bpm: int=80, amp: float=1500, instr: (int|str)=1) -> str:
|
|
|
|
|
""" Transform Ziffers object to Csound score in format:
|
|
|
|
|
i {instrument} {start time} {duration} {amplitude} {frequency} """
|
|
|
|
|
|
|
|
|
|
if not csound_imported:
|
|
|
|
|
raise ImportError("Install Csound")
|
|
|
|
|
|
|
|
|
|
# Csound score example: i1 0 1 0.5 8.00
|
|
|
|
|
# 1. The first number is the instrument number. In this case it is instrument 1.
|
|
|
|
|
# 2. The second number is the start time in seconds. In this case it is 0 seconds.
|
|
|
|
|
# 3. The third number is the duration in seconds. In this case it is 1 second.
|
|
|
|
|
# 4. The fourth number is the amplitude. In this case it is 0.5.
|
|
|
|
|
# 5. The fifth number is the frequency. In this case it is 8 Hz.
|
|
|
|
|
|
|
|
|
|
def freqs_and_durations_to_csound_score(pairs: list[list[float|list[float], float|list[float]]], bpm: int=80, amp: float=0.5, instr: (int|str)=1) -> str:
|
|
|
|
|
"""Tranforms list of lists containing frequencies and note lengths to csound score format.
|
|
|
|
|
Note lengths are transformed in seconds with the bpm.
|
|
|
|
|
Start time in seconds is calculated and summed from the note lengths.
|
|
|
|
|
If frequency is None, then it is a rest.
|
|
|
|
|
If frequency is a list, then it is a chord.
|
|
|
|
|
|
|
|
|
|
Example input: [[261.6255653005986, 0.5], [None, 0.25], [440.0, 0.125], [[261.6255653005986, 329.62755691286986, 391.9954359817492], [0.25, 0.125, 0.25]]]"""
|
|
|
|
|
score = ""
|
|
|
|
|
instr = f'"{instr}"' if isinstance(instr, str) else instr
|
|
|
|
|
start_time = 0
|
|
|
|
|
for pair in pairs:
|
|
|
|
|
if isinstance(pair[0], list):
|
|
|
|
|
for freq, dur in zip(pair[0], pair[1]):
|
|
|
|
|
score += f"i {instr} {start_time} {dur * 4 * 60 / bpm} {amp} {freq_to_pch(freq)}\n"
|
|
|
|
|
start_time += max(pair[1]) * 4 * 60 / bpm
|
|
|
|
|
else:
|
|
|
|
|
if pair[0] is None:
|
|
|
|
|
score += f"i {instr} {start_time} {pair[1] * 4 * 60 / bpm} {amp} 0\n"
|
|
|
|
|
else:
|
|
|
|
|
score += f"i {instr} {start_time} {pair[1] * 4 * 60 / bpm} {amp} {freq_to_pch(pair[0])}\n"
|
|
|
|
|
start_time += pair[1] * 4 * 60 / bpm
|
|
|
|
|
return score
|
|
|
|
|
|
|
|
|
|
def to_csound_score(expression: str | Ziffers, bpm: int=80, amp: float=0.5, instr: (int|str)=1) -> str:
|
|
|
|
|
""" Transform Ziffers object to Csound score """
|
|
|
|
|
if not csound_imported:
|
|
|
|
|
raise ImportError("Install Csound library")
|
|
|
|
|
|
|
|
|
|
if isinstance(expression, Ziffers):
|
|
|
|
|
score = freqs_and_durations_to_csound_score(expression.freq_pairs(),bpm,amp,instr)
|
|
|
|
|
else:
|
|
|
|
|
parsed = zparse(expression)
|
|
|
|
|
score = freqs_and_durations_to_csound_score(parsed.freq_pairs(),bpm,amp,instr)
|
|
|
|
|
|
|
|
|
|
for item in ziffers.evaluated_values:
|
|
|
|
|
if isinstance(item, Chord):
|
|
|
|
|
for freq, dur in zip(item.get_freq(), item.get_duration()):
|
|
|
|
|
score += f"i {instr} {start_time} {dur * 4 * 60 / bpm} {amp} {freq:.2f}\n"
|
|
|
|
|
start_time += max(item.get_duration()) * 4 * 60 / bpm
|
|
|
|
|
elif isinstance(item, Rest):
|
|
|
|
|
score += f"i {instr} {start_time} {item.get_duration() * 4 * 60 / bpm} {amp} 0\n"
|
|
|
|
|
elif isinstance(item, Pitch):
|
|
|
|
|
score += f"i {instr} {start_time} {item.get_duration() * 4 * 60 / bpm} {amp} {item.get_freq():.2f}\n"
|
|
|
|
|
start_time += item.get_duration() * 4 * 60 / bpm
|
|
|
|
|
return score
|
|
|
|
|
|
|
|
|
|
def to_music21(expression: str | Ziffers, **options):
|
|
|
|
|
@ -68,7 +42,7 @@ def to_music21(expression: str | Ziffers, **options):
|
|
|
|
|
raise ImportError("Install Music21 library")
|
|
|
|
|
|
|
|
|
|
# Register the ZiffersMusic21 converter
|
|
|
|
|
converter.registerSubconverter(ZiffersMusic21)
|
|
|
|
|
converter.registerSubConverter(ZiffersMusic21)
|
|
|
|
|
|
|
|
|
|
if isinstance(expression, Ziffers):
|
|
|
|
|
if options:
|
|
|
|
|
@ -118,12 +92,23 @@ if music21_imported:
|
|
|
|
|
parsed = zparse(dataString)
|
|
|
|
|
|
|
|
|
|
note_stream = stream.Part()
|
|
|
|
|
|
|
|
|
|
if "time" in options:
|
|
|
|
|
m_item = meter.TimeSignature(options["time"]) # Common time
|
|
|
|
|
else:
|
|
|
|
|
m_item = meter.TimeSignature("c") # Common time
|
|
|
|
|
|
|
|
|
|
note_stream.insert(0, m_item)
|
|
|
|
|
|
|
|
|
|
if "key" in options:
|
|
|
|
|
accidentals = accidentals_from_note_name(options["key"])
|
|
|
|
|
if "scale" in options and options["scale"].upper() == "MINOR":
|
|
|
|
|
accidentals-=3 # If minor, subtract 3 from accidentals
|
|
|
|
|
note_stream.append(key.KeySignature(accidentals))
|
|
|
|
|
|
|
|
|
|
if "bpm" in options:
|
|
|
|
|
note_stream.append(tempo.MetronomeMark(number=options["bpm"]))
|
|
|
|
|
|
|
|
|
|
for item in parsed:
|
|
|
|
|
if isinstance(item, Pitch):
|
|
|
|
|
m_item = note.Note(item.note)
|
|
|
|
|
|