More examples and some minor fixes
This commit is contained in:
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user