More examples and some minor fixes

This commit is contained in:
2023-06-29 00:03:45 +03:00
parent 7d6ba407bd
commit 9bd4ec0ff0
14 changed files with 336 additions and 66 deletions

View File

@ -224,6 +224,10 @@ class Pitch(Event):
def get_octave(self):
"""Getter for octave"""
return self.octave
def get_beat(self):
"""Getter for beat"""
return self.beat
def get_pitch_class(self):
"""Getter for pitche"""

View File

@ -555,6 +555,8 @@ class ListOperation(Sequence):
outcome = __chord_operation(first, second, False, options)
elif isinstance(second, Chord):
outcome = __chord_operation(second, first, True, options)
elif isinstance(first, Rest) or isinstance(second, Rest):
outcome = Rest(duration=first.get_duration())
else:
outcome = Pitch(
pitch_class=operation(

View File

@ -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)

View File

@ -231,7 +231,10 @@ def accidentals_from_note_name(name: str) -> int:
Returns:
int: Integer representing number of flats or sharps: -7 flat to 7 sharp.
"""
idx = CIRCLE_OF_FIFTHS.index(name.upper())
if name not in CIRCLE_OF_FIFTHS:
name = midi_to_note_name(note_name_to_midi(name))
idx = CIRCLE_OF_FIFTHS.index(name)
return idx - 6