Added music21 and csound examples

This commit is contained in:
2023-06-28 00:55:56 +03:00
parent fe65c87ed2
commit 7d6ba407bd
14 changed files with 577 additions and 20 deletions

101
example.musicxml Normal file
View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="4.0">
<movement-title>Music21 Fragment</movement-title>
<identification>
<creator type="composer">Music21</creator>
<encoding>
<encoding-date>2023-06-27</encoding-date>
<software>music21 v.9.1.0</software>
</encoding>
</identification>
<defaults>
<scaling>
<millimeters>7</millimeters>
<tenths>40</tenths>
</scaling>
</defaults>
<part-list>
<score-part id="P0c4c819b93d5335473e23baff277bd9f">
<part-name />
</score-part>
</part-list>
<!--=========================== Part 1 ===========================-->
<part id="P0c4c819b93d5335473e23baff277bd9f">
<!--========================= Measure 1 ==========================-->
<measure implicit="no" number="1">
<attributes>
<divisions>10080</divisions>
<time>
<beats>4</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>D</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>E</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<rest />
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>D</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
</note>
<note>
<chord />
<pitch>
<step>E</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>G</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
</note>
<note print-object="no" print-spacing="yes">
<rest />
<duration>5040</duration>
<type>eighth</type>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
</score-partwise>

View File

@ -0,0 +1,36 @@
try:
import ctcsound
except (ImportError,TypeError):
csound_imported = false
from ziffers import *
if(csound_imported):
# Parse ziffers notation
parsed = zparse("w 0 1 q 0 1 2 3 r e 5 3 9 2 q r 0")
# Convert to csound score
score = to_csound_score(parsed, 180, 1500, "Meep")
# Outputs: i {instrument} {start time} {duration} {amplitude} {pitch}
print(score)
orc = """
instr Meep
out(linen(oscili(p4,p5),0.1,p3,0.1))
endin
"""
c = ctcsound.Csound()
c.setOption("-odac") # Using SetOption() to configure Csound
c.compileOrc(orc)
c.readScore(score)
c.start()
c.perform()
c.stop()
else:
print("Csound not found! First download from https://csound.com/ and add to PATH or PYENV (Windows path: C:\Program Files\Csound6_x64\bin). Then install ctcsound with 'pip install ctcsound'.")

View File

@ -0,0 +1,20 @@
from music21 import *
from ziffers import *
# Create melody string
melody = "q 0 2 4 r e 1 4 3 2 s 0 1 2 6 e 2 8 2 1 h 0"
#bass_line = "(q 0 2 4 6 e 1 4 3 2 s 0 1 2 6 e 2 8 2 1 h 0)-7"
# Parse ziffers notation to melody from string
parsed = zparse(melody)
#parsed_bass = zparse(bass_line)
# Convert to music21 object
s2 = to_music21(parsed, time="4/4")
# Merge melody and bass line
#s2.append(to_music21(parsed_bass, time="4/4"))
# Write to midi file under examples/music21/midi folder
s2.write('midi', fp='examples/music21/output/ziffers_example.mid')

View File

@ -0,0 +1,9 @@
from music21 import *
from ziffers import *
# Parse ziiffers object to music21 object
parsed = zparse("q 1 024 5 235 h 02345678{12} 0", key='C', scale='Zyditonic')
s2 = to_music21(parsed,time="4/4")
# Save to MusicXML file
s2.write('musicxml', fp='examples/music21/output/ziffers_example.xml')

View File

@ -0,0 +1,9 @@
from music21 import *
from ziffers import *
# Parse ziiffers object to music21 object
parsed = zparse('1 2 qr e 124')
s2 = to_music21(parsed,time="4/4")
# Save object as png file
s2.write("musicxml.png", fp="examples/music21/output/example.png")

View File

@ -1,8 +0,0 @@
from music21 import *
from sardine import *
from ziffers import *
s = to_music21('(i v vi vii^dim)@(q0 e2 s1 012)',time="4/4")
s.show('midi')

View File

@ -1,9 +0,0 @@
from music21 import *
from sardine import *
from ziffers import *
parsed = zparse('1 2 qr e 124')
s2 = to_music21(parsed,time="4/4")
s2.show()
s2.show('midi')

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="4.0">
<identification>
<encoding>
<encoding-date>2023-06-27</encoding-date>
<software>music21 v.9.1.0</software>
</encoding>
</identification>
<defaults>
<scaling>
<millimeters>7</millimeters>
<tenths>40</tenths>
</scaling>
</defaults>
<part-list>
<score-part id="P2a5e2bdd0bc8bab63f324063ff62ac90">
<part-name />
</score-part>
</part-list>
<!--=========================== Part 1 ===========================-->
<part id="P2a5e2bdd0bc8bab63f324063ff62ac90">
<!--========================= Measure 1 ==========================-->
<measure implicit="no" number="1">
<attributes>
<divisions>10080</divisions>
<time>
<beats>4</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>D</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>E</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<rest />
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>D</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
</note>
<note>
<chord />
<pitch>
<step>E</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>G</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>5040</duration>
<type>eighth</type>
</note>
<note print-object="no" print-spacing="yes">
<rest />
<duration>5040</duration>
<type>eighth</type>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
</score-partwise>

Binary file not shown.

View File

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="4.0">
<movement-title>Music21 Fragment</movement-title>
<identification>
<creator type="composer">Music21</creator>
<encoding>
<encoding-date>2023-06-27</encoding-date>
<software>music21 v.9.1.0</software>
</encoding>
</identification>
<defaults>
<scaling>
<millimeters>7</millimeters>
<tenths>40</tenths>
</scaling>
</defaults>
<part-list>
<score-part id="P381d8734c7dfe8b1fbfca186c059f010">
<part-name />
</score-part>
</part-list>
<!--=========================== Part 1 ===========================-->
<part id="P381d8734c7dfe8b1fbfca186c059f010">
<!--========================= Measure 1 ==========================-->
<measure implicit="no" number="1">
<attributes>
<divisions>10080</divisions>
<time>
<beats>4</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>C</step>
<alter>1</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
<accidental>sharp</accidental>
</note>
<note>
<pitch>
<step>C</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>F</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<chord />
<pitch>
<step>A</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>C</step>
<alter>0</alter>
<octave>5</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<pitch>
<step>F</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<chord />
<pitch>
<step>G</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
</note>
<note>
<chord />
<pitch>
<step>C</step>
<alter>0</alter>
<octave>5</octave>
</pitch>
<duration>10080</duration>
<type>quarter</type>
<accidental>natural</accidental>
</note>
</measure>
<!--========================= Measure 2 ==========================-->
<measure implicit="no" number="2">
<note>
<pitch>
<step>C</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>F</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>G</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>A</step>
<alter>0</alter>
<octave>4</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>C</step>
<alter>0</alter>
<octave>5</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>D</step>
<alter>-1</alter>
<octave>5</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>flat</accidental>
</note>
<note>
<chord />
<pitch>
<step>F</step>
<alter>0</alter>
<octave>5</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>G</step>
<alter>0</alter>
<octave>5</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note>
<chord />
<pitch>
<step>F</step>
<alter>0</alter>
<octave>6</octave>
</pitch>
<duration>20160</duration>
<type>half</type>
<accidental>natural</accidental>
</note>
<note print-object="no" print-spacing="yes">
<rest />
<duration>20160</duration>
<type>half</type>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
</score-partwise>

View File

@ -0,0 +1,9 @@
from music21 import *
from ziffers import *
# Parse Ziffers string to music21 object
s = to_music21('(i v vi vii^dim)@(q0 e 2 1 q 012)', scale="Lydian", time="4/4")
# See https://web.mit.edu/music21/doc/installing/installAdditional.html
# Attempt to open / show the midi in MuseScore
s.show()

View File

@ -153,9 +153,17 @@ class Ziffers(Sequence):
def pairs(self) -> list[tuple]:
"""Return list of pitches and durations"""
return [
(val.get_pitch_class(), val.get_duration())
[val.get_pitch_class(), val.get_duration()]
for val in self.evaluated_values
if isinstance(val, Pitch)
if isinstance(val, Pitch) or isinstance(val, Chord) or isinstance(val, Rest)
]
def freq_pairs(self) -> list[tuple]:
"""Return list of pitches in freq and durations"""
return [
[val.get_freq(), val.get_duration()]
for val in self.evaluated_values
if isinstance(val, Pitch) or isinstance(val, Chord) or isinstance(val, Rest)
]
def octaves(self) -> list[int]:
@ -194,4 +202,4 @@ class Ziffers(Sequence):
return all_items
if len(all_items) == 1:
return all_items[0]
return None
return None

View File

@ -7,12 +7,67 @@ try:
except ImportError:
music21_imported: bool = False
try:
import ctcsound
csound_imported: bool = True
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}"
# 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)
return score
def to_music21(expression: str | Ziffers, **options):
"""Helper for passing options to the parser"""
if not music21_imported:
raise ImportError("Install Music21 library")
# Register the ZiffersMusic21 converter
converter.registerSubconverter(ZiffersMusic21)
if isinstance(expression, Ziffers):