Skip to content

Commit

Permalink
* Re-implemented PlayArgs to allow clearer cascading. Now events inside
Browse files Browse the repository at this point in the history
  a Chain/Voice can set instr arguments at the event level via setPlay
  and these are not overwritten when the parent also defines
  instr arguments; instead the args are merged, with events having
  higher priority (for this we follow the lay of most specificity)

Misc
----

* better handling for weakrefs in reprObj
* fix search&replace mistake

Docs
----

* new notebook showing input/export for musicxml
  • Loading branch information
gesellkammer committed Jul 29, 2023
1 parent a5f58e5 commit cccf75a
Show file tree
Hide file tree
Showing 30 changed files with 1,168 additions and 1,258 deletions.
13 changes: 13 additions & 0 deletions docs/Tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Tutorial (Jupyter Notebooks)
Workspace <notebooks/maelzel-core-workspace>
Score Structure <notebooks/maelzel-core-scorestruct>
Notation Refinements <notebooks/maelzel-core-symbols>
Input / Output <notebooks/musicxmlio>


-------------------
Expand Down Expand Up @@ -93,3 +94,15 @@ Tutorial (Jupyter Notebooks)
(slurs, brackets, lines), articulations, dynamics and many other symbols to notes and chords. Also color,
size, text style, etc. can be customized.

.. grid:: 1 1 2 2

.. grid-item-card:: Input / Output
:link: notebooks/musicxmlio
:link-type: doc
:class-title: cardtitle

.. figure:: assets/notebook-musicxmlio.png
:height: 300px

Lorem ipsum

50 changes: 30 additions & 20 deletions docs/notebooks/musicxmlio.ipynb

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions maelzel/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import emlib.misc
import warnings
import sys
import weakref


def reprObj(obj,
Expand All @@ -11,7 +12,7 @@ def reprObj(obj,
hideFalse = False,
hideEmptyStr = False,
hideFalsy = False,
quoteStrings=False
quoteStrings=False,
) -> str:
"""
Given an object, generate its repr
Expand Down Expand Up @@ -43,8 +44,9 @@ def reprObj(obj,
continue
elif (filterfunc := filter.get(attr)) and not filterfunc(value):
continue
#elif isinstance(value, weakref.ref):
# value = f"ref({value()})"
elif isinstance(value, weakref.ref):
refobj = value()
value = f'ref({type(refobj).__name__})'
elif quoteStrings and isinstance(value, str):
value = f'"{value}"'
info.append(f'{attr}={value}')
Expand All @@ -61,14 +63,14 @@ def fuzzymatch(query: str, choices: Sequence[str], limit=5) -> list[tuple[str, i
return thefuzz.process.extract(query, choices=choices, limit=limit)


def checkChoice(name: str, s: str, choices: Sequence[str], threshold=4):
def checkChoice(name: str, s: str, choices: Sequence[str], threshold=8):
if s not in choices:
if len(choices) > threshold:
matches = fuzzymatch(s, choices, limit=20)
raise ValueError(f'Invalid value "{s}" for {name}, maybe you meant "{matches[0][0]}"? '
f'Other possible choices: {[m[0] for m in matches]}')
else:
raise ValueError(f'Invalid value "{s}" for {name}, it should be one of {choices}')
raise ValueError(f'Invalid value "{s}" for {name}, it should be one of {list(choices)}')


def humanReadableTime(t: float) -> str:
Expand Down
23 changes: 11 additions & 12 deletions maelzel/core/_mobjtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def splitNotesOnce(notes: Chord | Sequence[Note], splitpoint: float, deviation=N
notes: a seq. of Notes
splitpoint: the pitch to split the notes
deviation: an acceptable deviation to fit all notes
in one tree (config: 'splitAcceptableDeviation')
in one part (config: 'splitAcceptableDeviation')
Returns:
notes above and below
Expand All @@ -43,7 +43,7 @@ def splitNotesOnce(notes: Chord | Sequence[Note], splitpoint: float, deviation=N
def splitNotesIfNecessary(notes: list[Note], splitpoint: float, deviation=None
) -> list[list[Note]]:
"""
Like splitNotesOnce, but returns only tree which have notes in them
Like splitNotesOnce, but returns only parts which have notes in them
This can be used to split in more than one staves, which should not overlap
Expand Down Expand Up @@ -173,13 +173,12 @@ def groupLinkedEvents(items: list[MEvent],
groups = [[lastitem]]
for item in items[1:]:
assert item.offset is not None
assert lastitem.end is not None
gap = item.offset - lastitem.end
if gap < 0:
raise ValueError(f"Events supperpose: {lastitem=}, {item=}")
elif gap > mingap:
groups.append([item])
elif not lastitem.canBeLinkedTo(item) or (lastitem.playargs and lastitem.playargs.get('linkednext') is False):
elif not lastitem._canBeLinkedTo(item):
groups.append([item])
else:
groups[-1].append(item)
Expand All @@ -190,7 +189,7 @@ def groupLinkedEvents(items: list[MEvent],
def splitLinkedGroupIntoLines(objs: list[MEvent]
) -> list[list[Note]]:
"""
Given a tree as a list of Notes/Chords, split it in subgroups matching
Given a group as a list of Notes/Chords, split it in subgroups matching
each note with its continuation.
When one chords is followed by another chord and the first chord
Expand Down Expand Up @@ -235,11 +234,11 @@ def splitLinkedGroupIntoLines(objs: list[MEvent]
usednotes = set()
assert all(n.offset is not None for n in notes)
if not started:
# No started tree, so all notes here will start tree
# No started group, so all notes here will start group
for note in notes:
started.append([note])
else:
# there are started tree, so iterate through started tree and
# there are started groups, so iterate through started groups and
# find if there are matches.
for groupidx, group in enumerate(started):
last = group[-1]
Expand All @@ -261,17 +260,17 @@ def splitLinkedGroupIntoLines(objs: list[MEvent]
group.append(notes[matchidx])
usednotes.add(notes[matchidx])
else:
# This tree's last note is not tied and has no gliss: this is the
# end of this tree, so add it to finished
# This group's last note is not tied and has no gliss: this is the
# end of this group, so add it to finished
finished.append(group)
started.pop(groupidx)
# Are there notes left? If yes, this notes did not match any started tree,
# so they must start a tree themselves
# Are there notes left? If yes, this notes did not match any started group,
# so they must start a group themselves
for note in notes:
if note not in usednotes:
started.append([note])

# We finished iterating, are there any started tree? Finish them
# We finished iterating, are there any started groups? Finish them
finished.extend(started)
return finished

Expand Down
16 changes: 9 additions & 7 deletions maelzel/core/builtinpresets.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,20 @@
reshapearray gi__formantBws__, 5, 5
""",
audiogen = r"""
|kx=0, ky=0, kvibamount=1|
|kx=0, ky=0, kvibamount=1, ivibstart=0.5, ivibfreq=4.5, ivibrange=0.25, ipitchlag=0.2|
; Simple vowel singing simulation
; Args:
; kx: x coordinate, from 0 to 1
; ky: y coordinate, from 0 to 1
; kvibamount: vibrato amount, 0 to 1
kvibfreq = linseg:k(0, 0.1, 0, 0.5, 4.5) * randomi:k(0.9, 1.1, 2)
kvibsemi = linseg:k(0, 0.4, 0, 2.1, 0.25) * randomi:k(0.9, 1.1, 10)
knoVib = trighold(changed2(kpitch), ivibstart*0.25)
kvibfreq = linseg:k(0, ivibstart*0.25, 0, ivibstart*0.75, ivibfreq) * randomi:k(0.9, 1.1, 2) * (1 - knoVib)
kvibsemi = linseg:k(0, ivibstart*0.2, 0, ivibstart*0.8, ivibrange) * randomi:k(0.9, 1.1, 10)
kvib = oscil:k(kvibsemi/2, kvibfreq) - kvibsemi/2
kpitch = lag:k(kpitch, 0.2) + kvib*kvibamount
asource = butterlp:a(vco2:a(kamp, mtof(kpitch)), 4000)
kpitch2 = lag:k(kpitch, ipitchlag) + kvib*kvibamount
asource = butterlp:a(vco2:a(kamp, mtof(kpitch2)), 5000)
kcoords[] fillarray 0, 0, 1, \ ; A
0.5, 0.5, 0.3, \ ; E
1, 0, 1, \ ; I
Expand All @@ -239,8 +241,8 @@
kformantFreqs[] weightedsum gi__formantFreqs__, kweights
kformantBws[] weightedsum gi__formantBws__, kweights
kformantAmps[] weightedsum gi__formantAmps__, kweights
kformantFreqs poly 5, "lag", kformantFreqs, 0.2
kformantAmps poly 5, "lag", kformantAmps, 0.2
kformantFreqs poly 5, "lag", kformantFreqs, ipitchlag
kformantAmps poly 5, "lag", kformantAmps, ipitchlag
aformants[] poly 5, "resonx", asource, kformantFreqs, kformantBws, 2, 1
aformants *= kformantAmps
aout1 = sumarray(aformants) * 0.1
Expand Down
Loading

0 comments on commit cccf75a

Please sign in to comment.