csoundengine 2.22.4__tar.gz → 2.22.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {csoundengine-2.22.4 → csoundengine-2.22.6}/.github/workflows/test2.yml +9 -2
- {csoundengine-2.22.4 → csoundengine-2.22.6}/PKG-INFO +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/abstractrenderer.py +4 -4
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/baseschedevent.py +13 -17
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csd.py +2 -2
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csoundlib.py +92 -40
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/engine.py +1 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/enginebase.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/engineorc.py +3 -2
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/instr.py +16 -22
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/internal.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offline.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/schedevent.py +20 -19
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/session.py +49 -46
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/synth.py +10 -13
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/tools.py +24 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/PKG-INFO +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/SOURCES.txt +0 -1
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/offlineengine.ipynb +11 -11
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-bus.ipynb +353 -138
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-dealloc-synth.ipynb +21 -20
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-delay.ipynb +12 -15
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-engine-params.ipynb +21 -26
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-libcsound.ipynb +44 -100
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-named-events.ipynb +51 -47
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-new-named-params.ipynb +65 -84
- csoundengine-2.22.6/notebooks/test/test-offline-render.ipynb +704 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-offlinesession.ipynb +28 -28
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-params.ipynb +149 -202
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-playsample.ipynb +50 -53
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-stop.ipynb +32 -39
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-tight-scheduling.ipynb +29 -1047
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-whenfinished.ipynb +88 -28
- {csoundengine-2.22.4 → csoundengine-2.22.6}/pyproject.toml +1 -1
- csoundengine-2.22.4/csoundengine/__sessionbase.py +0 -38
- csoundengine-2.22.4/notebooks/test/test-offline-render.ipynb +0 -738
- {csoundengine-2.22.4 → csoundengine-2.22.6}/.cirrus.yml +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/.gitignore +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/LICENSE.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/MANIFEST.in +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/README.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/cmake/csound-custom-macos.cmake +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/__init__.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/_common.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/_palette.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/busproxy.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/config.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/contants.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csounddefs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csoundparse.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/dependencies.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/errors.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/event.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/instrtools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/interact.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/jacktools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/jupytertools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/linuxaudio.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/magic.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offlineengine.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offlineorc.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/paramtable.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/plotting.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/py.typed +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/renderjob.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sessionhandler.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sessioninstrs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sftools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/state.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/tableproxy.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/dependency_links.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/requires.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/top_level.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Builtin-Opcodes.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Installation.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Introduction.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Makefile +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Reference.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/_static/custom.css +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/_static/offlineengine-example1.mp3 +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/config-edit.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/eventui.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/eventui2.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-config.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-plotTable.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-session-1.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/offlineengine-example1.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/select-preset.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/synthui.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableplot-sine.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableplot-stereo.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableproxy-plot.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableproxy-plotspectrogram.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/ui2.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/conf.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/config.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/configkeys.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/csoundengine.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/csoundlib.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/index.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/instr.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/jupyternotebook.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/magics.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/make.bat +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offline.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine_example.inc +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine_example2.inc +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlinerenderer.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/renderjob.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/requirements.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/schedevent.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/session.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/synth.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/tables.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/buses.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/offlineengine-playback.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/colours-german-male.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/piano-pitchtest-2.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/voiceover-fragment-48k.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/plltrack.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/voice-pitch-track.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/pyrightconfig.json +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/readthedocs.yml +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/scripts/download-plugins.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/scripts/update-config-docs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/setup.cfg +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/importtime.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/libcsoundtest.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/simple.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test1.csd +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test1.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test2.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.6}/test/testargs.py +0 -0
|
@@ -40,8 +40,15 @@ jobs:
|
|
|
40
40
|
curl -L -o csound-binaries.zip https://github.com/csound/csound/releases/download/6.18.1/Csound-6.18.1-windows-x64-binaries.zip
|
|
41
41
|
Expand-Archive csound-binaries.zip -DestinationPath "csound-binaries"
|
|
42
42
|
ls -r
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
$csoundpath = "C:/Program Files/csound"
|
|
44
|
+
|
|
45
|
+
Move-Item -Force -Path ".\csound-binaries\build\Release\*" -Destination $csoundpath
|
|
46
|
+
if (-Not (Test-Path $csoundpath)) {
|
|
47
|
+
Write-Error "The path '$csoundpath' does not exist."
|
|
48
|
+
exit 1
|
|
49
|
+
} else {
|
|
50
|
+
ls -Recurse $csoundpath
|
|
51
|
+
}
|
|
45
52
|
|
|
46
53
|
"C:/Program Files/csound" | Out-File -FilePath $env:GITHUB_PATH -Append
|
|
47
54
|
# [Environment]::SetEnvironmentVariable("PATH", $Env:PATH + ";C:/Program Files/csound", [EnvironmentVariableTarget]::Machine)
|
|
@@ -70,7 +70,7 @@ class AbstractRenderer(ABC):
|
|
|
70
70
|
includes: list[str] | None = None,
|
|
71
71
|
aliases: dict[str, str] | None = None,
|
|
72
72
|
useDynamicPfields: bool | None = None,
|
|
73
|
-
initCallback: Callable[[AbstractRenderer], None] = None,
|
|
73
|
+
initCallback: Callable[[AbstractRenderer], None] | None = None,
|
|
74
74
|
**kws) -> _instr.Instr:
|
|
75
75
|
raise NotImplementedError
|
|
76
76
|
|
|
@@ -134,9 +134,9 @@ class AbstractRenderer(ABC):
|
|
|
134
134
|
raise NotImplementedError
|
|
135
135
|
|
|
136
136
|
@abstractmethod
|
|
137
|
-
def getInstr(self,
|
|
137
|
+
def getInstr(self, name: str) -> _instr.Instr | None:
|
|
138
138
|
"""
|
|
139
|
-
Returns the Instr instance corresponding to
|
|
139
|
+
Returns the Instr instance corresponding to name, or None if no such instr
|
|
140
140
|
"""
|
|
141
141
|
raise NotImplementedError
|
|
142
142
|
|
|
@@ -221,7 +221,7 @@ class AbstractRenderer(ABC):
|
|
|
221
221
|
raise NotImplementedError
|
|
222
222
|
|
|
223
223
|
@abstractmethod
|
|
224
|
-
def _registerTable(self, tabproxy: tableproxy.TableProxy):
|
|
224
|
+
def _registerTable(self, tabproxy: tableproxy.TableProxy) -> None:
|
|
225
225
|
raise NotImplementedError
|
|
226
226
|
|
|
227
227
|
@abstractmethod
|
|
@@ -127,9 +127,9 @@ class BaseSchedEvent(ABC):
|
|
|
127
127
|
self._setTable(param=param, value=value, delay=delay)
|
|
128
128
|
else:
|
|
129
129
|
raise KeyError(f"Unknown parameter: '{param}'. "
|
|
130
|
-
f"Possible parameters for this event: {self.dynamicParamNames(
|
|
131
|
-
f"aliases: {self.aliases()}, pfields: {self.pfieldNames(
|
|
132
|
-
f"control names: {self.controlNames(
|
|
130
|
+
f"Possible parameters for this event: {self.dynamicParamNames()}, "
|
|
131
|
+
f"aliases: {self.aliases()}, pfields: {self.pfieldNames()}, "
|
|
132
|
+
f"control names: {self.controlNames()}")
|
|
133
133
|
|
|
134
134
|
def _automatePfield(self,
|
|
135
135
|
param: int | str,
|
|
@@ -237,7 +237,7 @@ class BaseSchedEvent(ABC):
|
|
|
237
237
|
else:
|
|
238
238
|
raise KeyError(f"Unknown parameter '{param}', supported parameters: {self.dynamicParamNames()}")
|
|
239
239
|
|
|
240
|
-
def dynamicParamNames(self, aliases=
|
|
240
|
+
def dynamicParamNames(self, aliases=False) -> frozenset[str]:
|
|
241
241
|
"""
|
|
242
242
|
The set of all dynamic parameters accepted by this event
|
|
243
243
|
|
|
@@ -248,8 +248,7 @@ class BaseSchedEvent(ABC):
|
|
|
248
248
|
dynparams = set(p for p in params if p.startswith('k'))
|
|
249
249
|
if aliases and (_aliases := self.aliases()):
|
|
250
250
|
dynparams |= _aliases.keys()
|
|
251
|
-
|
|
252
|
-
dynparams.difference_update(_aliases.values())
|
|
251
|
+
dynparams.difference_update(_aliases.values())
|
|
253
252
|
return frozenset(dynparams)
|
|
254
253
|
|
|
255
254
|
def aliases(self) -> dict[str, str]:
|
|
@@ -269,15 +268,13 @@ class BaseSchedEvent(ABC):
|
|
|
269
268
|
orig = self.aliases().get(param)
|
|
270
269
|
return orig if orig is not None else default
|
|
271
270
|
|
|
272
|
-
def pfieldNames(self, aliases=
|
|
271
|
+
def pfieldNames(self, aliases=False) -> frozenset[str]:
|
|
273
272
|
"""
|
|
274
273
|
Returns a set of all named pfields
|
|
275
274
|
|
|
276
275
|
Args:
|
|
277
|
-
aliases: if True,
|
|
278
|
-
|
|
279
|
-
are defined and included (aliases=True), the original names
|
|
280
|
-
will be excluded
|
|
276
|
+
aliases: if True, use aliases for pfields (if applicable). An
|
|
277
|
+
alias shadows the original param
|
|
281
278
|
|
|
282
279
|
Returns:
|
|
283
280
|
a set with the all pfield names.
|
|
@@ -285,13 +282,12 @@ class BaseSchedEvent(ABC):
|
|
|
285
282
|
"""
|
|
286
283
|
raise NotImplementedError
|
|
287
284
|
|
|
288
|
-
def controlNames(self, aliases=
|
|
285
|
+
def controlNames(self, aliases=False) -> frozenset[str]:
|
|
289
286
|
"""
|
|
290
287
|
The names of all controls
|
|
291
288
|
|
|
292
289
|
Args:
|
|
293
290
|
aliases: if True, included control names aliases (if applicable)
|
|
294
|
-
aliased: if True, include the aliased names
|
|
295
291
|
|
|
296
292
|
Returns:
|
|
297
293
|
the names of all controls. Returns an empty set if this event
|
|
@@ -299,7 +295,7 @@ class BaseSchedEvent(ABC):
|
|
|
299
295
|
"""
|
|
300
296
|
raise NotImplementedError
|
|
301
297
|
|
|
302
|
-
def paramNames(self, aliases=
|
|
298
|
+
def paramNames(self, aliases=False) -> frozenset[str]:
|
|
303
299
|
"""
|
|
304
300
|
Set of all named parameters
|
|
305
301
|
|
|
@@ -308,8 +304,8 @@ class BaseSchedEvent(ABC):
|
|
|
308
304
|
:meth:`~BaseEvent.dynamicParams`, :meth:`~BaseEvent.set`,
|
|
309
305
|
:meth:`~BaseEvent.automate`
|
|
310
306
|
"""
|
|
311
|
-
pargs = self.pfieldNames(aliases=aliases
|
|
312
|
-
tableargs = self.controlNames(aliases=aliases
|
|
307
|
+
pargs = self.pfieldNames(aliases=aliases)
|
|
308
|
+
tableargs = self.controlNames(aliases=aliases)
|
|
313
309
|
if pargs and tableargs:
|
|
314
310
|
return pargs | tableargs
|
|
315
311
|
elif pargs:
|
|
@@ -355,7 +351,7 @@ class BaseSchedEvent(ABC):
|
|
|
355
351
|
instrspecs = self.paramSpecs()
|
|
356
352
|
if instrspecs:
|
|
357
353
|
specdict.update(instrspecs)
|
|
358
|
-
dynparams = self.dynamicParamNames(aliases=False
|
|
354
|
+
dynparams = self.dynamicParamNames(aliases=False)
|
|
359
355
|
for param in dynparams:
|
|
360
356
|
value = self.paramValue(param)
|
|
361
357
|
if spec := specdict.get(param):
|
|
@@ -346,7 +346,7 @@ class Csd:
|
|
|
346
346
|
|
|
347
347
|
self._outfileFormat = ''
|
|
348
348
|
self._outfileEncoding = ''
|
|
349
|
-
self._compressionQuality =
|
|
349
|
+
self._compressionQuality = 0.
|
|
350
350
|
|
|
351
351
|
self._definedTables: Set[int] = set()
|
|
352
352
|
self._minTableIndex = 1
|
|
@@ -568,8 +568,8 @@ class Csd:
|
|
|
568
568
|
sizeThreshold = config['offline_score_table_size_limit']
|
|
569
569
|
|
|
570
570
|
if isinstance(data, np.ndarray) and sr:
|
|
571
|
-
sndfile = _tempfile.mktemp(suffix=".wav")
|
|
572
571
|
import sndfileio
|
|
572
|
+
sndfile = _tempfile.mktemp(suffix=".wav")
|
|
573
573
|
sndfileio.sndwrite(sndfile, samples=data, sr=sr, encoding='float32')
|
|
574
574
|
tabnum = self.addSndfile(sndfile, tabnum=tabnum, asProjectFile=True,
|
|
575
575
|
start=start)
|
|
@@ -23,6 +23,7 @@ import shutil as _shutil
|
|
|
23
23
|
import subprocess as _subprocess
|
|
24
24
|
import tempfile as _tempfile
|
|
25
25
|
import textwrap as _textwrap
|
|
26
|
+
import struct
|
|
26
27
|
|
|
27
28
|
import cachetools as _cachetools
|
|
28
29
|
import numpy as np
|
|
@@ -331,7 +332,6 @@ class _PortaudioBackend(AudioBackend):
|
|
|
331
332
|
return info.sr if info is not None else 44100
|
|
332
333
|
return super().getSystemSr()
|
|
333
334
|
|
|
334
|
-
@_functools.cache
|
|
335
335
|
def defaultAudioDevices(self) -> tuple[AudioDevice | None, AudioDevice | None]:
|
|
336
336
|
logger.debug("Querying default device via portaudio/sounddevice")
|
|
337
337
|
try:
|
|
@@ -1251,18 +1251,6 @@ def getAudioBackendNames() -> list[str]:
|
|
|
1251
1251
|
return [b.name for b in audioBackends()]
|
|
1252
1252
|
|
|
1253
1253
|
|
|
1254
|
-
# @_functools.cache
|
|
1255
|
-
# def _instrReplacer() -> Callable[[str], str]:
|
|
1256
|
-
# return emlib.textlib.makeReplacer({".":"_", ":":"_", " ":"_"})
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
# def normalizeInstrumentName(name: str) -> str:
|
|
1260
|
-
# """
|
|
1261
|
-
# Transform name so that it can be accepted as an instrument name
|
|
1262
|
-
# """
|
|
1263
|
-
# return _instrReplacer()(name)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
1254
|
def mincer(sndfile: str,
|
|
1267
1255
|
outfile: str,
|
|
1268
1256
|
timecurve: float | Callable[[float], float] = 1.,
|
|
@@ -1489,6 +1477,77 @@ def recInstr(body: str, events: list, init='', outfile='',
|
|
|
1489
1477
|
return outfile, renderjob.process
|
|
1490
1478
|
|
|
1491
1479
|
|
|
1480
|
+
def _ftsaveReadBinary(path: str) -> list[tuple[dict, np.ndarray]]:
|
|
1481
|
+
"""
|
|
1482
|
+
Header: sizeof(FUNC) - sizeof(MYFLT) - SSTRSIZ = 152
|
|
1483
|
+
|
|
1484
|
+
where:
|
|
1485
|
+
|
|
1486
|
+
sizeof(FUNC) = 1184
|
|
1487
|
+
MYFLT = double (8 bytes)
|
|
1488
|
+
SSTRSIZ = 1024
|
|
1489
|
+
"""
|
|
1490
|
+
# Format string for the 152-byte cropped FUNC struct
|
|
1491
|
+
# Assuming little-endian, 64-bit platform, MYFLT = double
|
|
1492
|
+
FUNC_FORMAT = ('<'
|
|
1493
|
+
'I' # flen uint32_t 4
|
|
1494
|
+
'i' # lenmask int32 4
|
|
1495
|
+
'i' # lobits int32 4
|
|
1496
|
+
'i' # lomask int32 4
|
|
1497
|
+
'd' # lodiv MYFLT 8
|
|
1498
|
+
'd' # cvtbas MYFLT 8
|
|
1499
|
+
'd' # cpscvt MYFLT 8
|
|
1500
|
+
'hh' # loopmode1, loopmode2 int16 x2 4
|
|
1501
|
+
'xxxx' # padding 4
|
|
1502
|
+
'iiii' # begin1, end1, begin2, end2 16
|
|
1503
|
+
'ii' # soundend, flenfrms 8
|
|
1504
|
+
'ii' # nchanls, fno 8
|
|
1505
|
+
'd' # sr MYFLT 8
|
|
1506
|
+
'Q' # *args pointer 8
|
|
1507
|
+
'i' # argcnt int32_t 4
|
|
1508
|
+
'xxxx') # padding 4
|
|
1509
|
+
|
|
1510
|
+
# Verify size
|
|
1511
|
+
assert struct.calcsize(FUNC_FORMAT) == 152, \
|
|
1512
|
+
f"Expected 152 bytes, got {struct.calcsize(FUNC_FORMAT)}"
|
|
1513
|
+
|
|
1514
|
+
"""Read a cropped FUNC struct (152 bytes) from a binary file."""
|
|
1515
|
+
with open(path, 'rb') as f:
|
|
1516
|
+
header = f.read(152)
|
|
1517
|
+
|
|
1518
|
+
if len(header) < 152:
|
|
1519
|
+
raise ValueError(f"File too short: expected 152 bytes, got {len(data)}")
|
|
1520
|
+
|
|
1521
|
+
fields = struct.unpack(FUNC_FORMAT, header)
|
|
1522
|
+
tablelen = fields[0]
|
|
1523
|
+
data = f.read((tablelen + 1) * 8)
|
|
1524
|
+
|
|
1525
|
+
tabinfo = {
|
|
1526
|
+
'flen': fields[0],
|
|
1527
|
+
'lenmask': fields[1],
|
|
1528
|
+
'lobits': fields[2],
|
|
1529
|
+
'lomask': fields[3],
|
|
1530
|
+
'lodiv': fields[4],
|
|
1531
|
+
'cvtbas': fields[5],
|
|
1532
|
+
'cpscvt': fields[6],
|
|
1533
|
+
'loopmode1': fields[7],
|
|
1534
|
+
'loopmode2': fields[8],
|
|
1535
|
+
'begin1': fields[9],
|
|
1536
|
+
'end1': fields[10],
|
|
1537
|
+
'begin2': fields[11],
|
|
1538
|
+
'end2': fields[12],
|
|
1539
|
+
'soundend': fields[13],
|
|
1540
|
+
'flenfrms': fields[14],
|
|
1541
|
+
'nchanls': fields[15],
|
|
1542
|
+
'fno': fields[16],
|
|
1543
|
+
'sr': fields[17],
|
|
1544
|
+
'args': fields[18], # raw pointer value (not dereferenceable)
|
|
1545
|
+
'argcnt': fields[19],
|
|
1546
|
+
}
|
|
1547
|
+
arr = np.frombuffer(data, dtype=np.float64)
|
|
1548
|
+
return [(tabinfo, arr)]
|
|
1549
|
+
|
|
1550
|
+
|
|
1492
1551
|
def _ftsaveReadText(path: str) -> list[np.ndarray]:
|
|
1493
1552
|
# a file can have multiple tables saved
|
|
1494
1553
|
lines = iter(open(path))
|
|
@@ -1510,7 +1569,7 @@ def _ftsaveReadText(path: str) -> list[np.ndarray]:
|
|
|
1510
1569
|
break
|
|
1511
1570
|
if tablength < 0:
|
|
1512
1571
|
raise IOError("Could not read table length")
|
|
1513
|
-
values
|
|
1572
|
+
values = np.zeros((tablength+1,), dtype=float)
|
|
1514
1573
|
# Read data
|
|
1515
1574
|
for i, line in enumerate(lines):
|
|
1516
1575
|
if line.startswith("---"):
|
|
@@ -1520,12 +1579,28 @@ def _ftsaveReadText(path: str) -> list[np.ndarray]:
|
|
|
1520
1579
|
return tables
|
|
1521
1580
|
|
|
1522
1581
|
|
|
1523
|
-
def ftsaveRead(path, mode="
|
|
1582
|
+
def ftsaveRead(path: str, mode="") -> list[tuple[dict, np.ndarray]]:
|
|
1524
1583
|
"""
|
|
1525
1584
|
Read a file saved by ftsave, returns a list of tables
|
|
1526
|
-
|
|
1585
|
+
|
|
1586
|
+
Args:
|
|
1587
|
+
path: the path to the saved table/tables
|
|
1588
|
+
mode: one of "text", "binary" or empty to auto-detect the format
|
|
1589
|
+
|
|
1590
|
+
Returns:
|
|
1591
|
+
a list of (tableinfo: dict, tabledata: np.ndarray), where
|
|
1592
|
+
tableinfo is a dict containing information about the table. This
|
|
1593
|
+
dict is only populated if the table was saved in binary format,
|
|
1594
|
+
for text tables this dict will be empty
|
|
1595
|
+
"""
|
|
1596
|
+
if not mode:
|
|
1597
|
+
from . import tools
|
|
1598
|
+
mode = "binary" if tools.isFileBinary(path) else "text"
|
|
1527
1599
|
if mode == "text":
|
|
1528
|
-
|
|
1600
|
+
arrays = _ftsaveReadText(path)
|
|
1601
|
+
return [({}, arr) for arr in arrays]
|
|
1602
|
+
elif mode == "binary" or mode == "bin":
|
|
1603
|
+
return _ftsaveReadBinary(path)
|
|
1529
1604
|
else:
|
|
1530
1605
|
raise ValueError(f"mode {mode} not supported")
|
|
1531
1606
|
|
|
@@ -1671,29 +1746,6 @@ def dumpAudioDevices(backend=''):
|
|
|
1671
1746
|
print("-- No output devices")
|
|
1672
1747
|
|
|
1673
1748
|
|
|
1674
|
-
# def _hashdict(d: dict) -> int:
|
|
1675
|
-
# return hash((frozenset(d.keys()), frozenset(d.values())))
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
# def _parsePresetSflistprograms(line: str) -> tuple[str, int, int] | None:
|
|
1679
|
-
# # 012345678
|
|
1680
|
-
# # xxx:yyy zzzzzzzzzz
|
|
1681
|
-
# bank = int(line[:3])
|
|
1682
|
-
# num = int(line[4:7])
|
|
1683
|
-
# name = line[8:].strip()
|
|
1684
|
-
# return (name, bank, num)
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
# def _parsePreset(line: str) -> tuple[str, int, int] | None:
|
|
1688
|
-
# match = _re.search(r">> Bank: (\d+)\s+Preset:\s+(\d+)\s+Name:\s*(.+)", line)
|
|
1689
|
-
# if not match:
|
|
1690
|
-
# return None
|
|
1691
|
-
# name = match.group(3).strip()
|
|
1692
|
-
# bank = int(match.group(1))
|
|
1693
|
-
# presetnum = int(match.group(2))
|
|
1694
|
-
# return (name, bank, presetnum)
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
1749
|
def channelTypeFromValue(value: int | float | str | np.ndarray) -> str:
|
|
1698
1750
|
"""
|
|
1699
1751
|
Channel type (k, S, a) from value
|
|
@@ -158,7 +158,7 @@ class _EngineBase(ABC):
|
|
|
158
158
|
...
|
|
159
159
|
|
|
160
160
|
@abstractmethod
|
|
161
|
-
def setChannel(self, channel: str, value: float | str | np.ndarray, delay=0
|
|
161
|
+
def setChannel(self, channel: str, value: float | str | np.ndarray, delay=0., method=''): ...
|
|
162
162
|
|
|
163
163
|
@abstractmethod
|
|
164
164
|
def channelPointer(self, channel: str, kind='control', mode='rw') -> np.ndarray:
|
|
@@ -18,7 +18,7 @@ gi__responses ftgen ${responses}, 0, ${numTokens}, -2, 0
|
|
|
18
18
|
gi__tokenToInstrnum ftgen ${tokenToInstrnum}, 0, ${maxNumInstrs}, -2, 0
|
|
19
19
|
gi__soundfontIndexes dict_new "str:float"
|
|
20
20
|
gi__soundfontIndexCounter init 1000
|
|
21
|
-
gi__builtinInstrs dict_new "str:float", "notifyDealloc", ${notifyDealloc}
|
|
21
|
+
gi__builtinInstrs dict_new "str:float", "notifyDealloc", ${notifyDealloc}, "notifyDeallocOSC", ${notifyDeallocOSC}
|
|
22
22
|
|
|
23
23
|
chn_k "_soundfontPresetCount", 3
|
|
24
24
|
|
|
@@ -87,11 +87,12 @@ opcode sendsync, 0, ii
|
|
|
87
87
|
endop
|
|
88
88
|
|
|
89
89
|
instr ${notifyDealloc}
|
|
90
|
+
iunused = p5
|
|
90
91
|
outvalue "__dealloc__", p4
|
|
91
92
|
turnoff
|
|
92
93
|
endin
|
|
93
94
|
|
|
94
|
-
instr ${
|
|
95
|
+
instr ${notifyDeallocOSC}
|
|
95
96
|
; p4=p1, p5=port
|
|
96
97
|
OSCsend 1, "127.0.0.1", p5, "/dealloc", "d", p4
|
|
97
98
|
turnoff
|
|
@@ -586,7 +586,7 @@ class Instr:
|
|
|
586
586
|
return orig if orig is not None else default
|
|
587
587
|
|
|
588
588
|
@cache
|
|
589
|
-
def controlNames(self, aliases=
|
|
589
|
+
def controlNames(self, aliases=False) -> frozenset[str]:
|
|
590
590
|
"""
|
|
591
591
|
Set of names of the controls in this instr
|
|
592
592
|
|
|
@@ -599,12 +599,11 @@ class Instr:
|
|
|
599
599
|
for name in self.controls.keys():
|
|
600
600
|
if alias := self._argToAlias.get(name):
|
|
601
601
|
names.add(alias)
|
|
602
|
-
|
|
603
|
-
names.remove(name)
|
|
602
|
+
names.remove(name)
|
|
604
603
|
return frozenset(names)
|
|
605
604
|
|
|
606
605
|
@cache
|
|
607
|
-
def dynamicParamNames(self, aliases=
|
|
606
|
+
def dynamicParamNames(self, aliases=False
|
|
608
607
|
) -> frozenset[str]:
|
|
609
608
|
"""
|
|
610
609
|
Set of all dynamic parameters accepted by this Instr
|
|
@@ -616,11 +615,11 @@ class Instr:
|
|
|
616
615
|
Returns:
|
|
617
616
|
a set of the dynamic (modifiable) parameters accepted by this Instr
|
|
618
617
|
"""
|
|
619
|
-
dynparams = self.dynamicParams(aliases=aliases
|
|
618
|
+
dynparams = self.dynamicParams(aliases=aliases)
|
|
620
619
|
return frozenset(dynparams.keys())
|
|
621
620
|
|
|
622
621
|
@cache
|
|
623
|
-
def dynamicPfields(self, aliases=
|
|
622
|
+
def dynamicPfields(self, aliases=False) -> dict[str, float]:
|
|
624
623
|
"""
|
|
625
624
|
The dynamic pfields in this instr
|
|
626
625
|
|
|
@@ -644,8 +643,7 @@ class Instr:
|
|
|
644
643
|
for alias, realname in self.aliases.items():
|
|
645
644
|
if realname in pfields:
|
|
646
645
|
pfields[alias] = pfields[realname]
|
|
647
|
-
|
|
648
|
-
pfields.pop(realname, None)
|
|
646
|
+
pfields.pop(realname, None)
|
|
649
647
|
|
|
650
648
|
# We know that pfields cannot hold any string since we are selecting by 'k' prefix
|
|
651
649
|
return pfields # type: ignore
|
|
@@ -680,7 +678,7 @@ class Instr:
|
|
|
680
678
|
return frozenset(pfields)
|
|
681
679
|
|
|
682
680
|
@cache
|
|
683
|
-
def dynamicParams(self, aliases=
|
|
681
|
+
def dynamicParams(self, aliases=False
|
|
684
682
|
) -> dict[str, float]:
|
|
685
683
|
"""
|
|
686
684
|
A dict with all dynamic parameters defined in this instr
|
|
@@ -708,20 +706,18 @@ class Instr:
|
|
|
708
706
|
params = params.copy()
|
|
709
707
|
for alias, realname in self.aliases.items():
|
|
710
708
|
params[alias] = params[realname]
|
|
711
|
-
|
|
712
|
-
del params[realname]
|
|
709
|
+
del params[realname]
|
|
713
710
|
return params
|
|
714
711
|
|
|
715
|
-
def paramNames(self, aliases=
|
|
712
|
+
def paramNames(self, aliases=False
|
|
716
713
|
) -> frozenset[str]:
|
|
717
714
|
"""
|
|
718
715
|
All parameter names
|
|
719
716
|
"""
|
|
720
|
-
pfields = self.pfieldNames(aliases=aliases
|
|
717
|
+
pfields = self.pfieldNames(aliases=aliases)
|
|
721
718
|
return frozenset(pfields | self.controlNames()) if self.controls else pfields
|
|
722
719
|
|
|
723
|
-
|
|
724
|
-
def pfieldNames(self, aliases=True, aliased=False
|
|
720
|
+
def pfieldNames(self, aliases=False
|
|
725
721
|
) -> frozenset[str]:
|
|
726
722
|
"""
|
|
727
723
|
The set of named pfields declared in this instrument
|
|
@@ -740,8 +736,7 @@ class Instr:
|
|
|
740
736
|
for alias, realname in self.aliases.items():
|
|
741
737
|
if realname in pfields:
|
|
742
738
|
pfields.add(alias)
|
|
743
|
-
|
|
744
|
-
pfields.remove(realname)
|
|
739
|
+
pfields.remove(realname)
|
|
745
740
|
return frozenset(pfields)
|
|
746
741
|
|
|
747
742
|
def paramValue(self, param: str) -> float | str | None:
|
|
@@ -753,7 +748,7 @@ class Instr:
|
|
|
753
748
|
return defaults[param2]
|
|
754
749
|
|
|
755
750
|
@cache
|
|
756
|
-
def paramDefaultValues(self, aliases=
|
|
751
|
+
def paramDefaultValues(self, aliases=False) -> dict[str, float]:
|
|
757
752
|
"""
|
|
758
753
|
A dict mapping named parameters to their default values
|
|
759
754
|
|
|
@@ -793,8 +788,7 @@ class Instr:
|
|
|
793
788
|
if self.aliases and aliases:
|
|
794
789
|
for alias, realname in self.aliases.items():
|
|
795
790
|
params[alias] = params[realname]
|
|
796
|
-
|
|
797
|
-
del params[realname]
|
|
791
|
+
del params[realname]
|
|
798
792
|
|
|
799
793
|
return params
|
|
800
794
|
|
|
@@ -811,8 +805,8 @@ class Instr:
|
|
|
811
805
|
parameter to its given value
|
|
812
806
|
"""
|
|
813
807
|
return instrtools.distributeParams(params=params,
|
|
814
|
-
pfieldNames=self.pfieldNames(
|
|
815
|
-
controlNames=self.controlNames(
|
|
808
|
+
pfieldNames=self.pfieldNames(),
|
|
809
|
+
controlNames=self.controlNames())
|
|
816
810
|
|
|
817
811
|
def pfieldName(self, index: int, alias=True) -> str:
|
|
818
812
|
"""
|
|
@@ -224,24 +224,21 @@ class SchedEvent(BaseSchedEvent):
|
|
|
224
224
|
self._instr = instr
|
|
225
225
|
return instr
|
|
226
226
|
|
|
227
|
-
def paramNames(self, aliases=
|
|
228
|
-
return self.instr.paramNames(aliases=aliases
|
|
227
|
+
def paramNames(self, aliases=False) -> frozenset[str]:
|
|
228
|
+
return self.instr.paramNames(aliases=aliases)
|
|
229
229
|
|
|
230
|
-
def dynamicParamNames(self, aliases=
|
|
230
|
+
def dynamicParamNames(self, aliases=False) -> frozenset[str]:
|
|
231
231
|
"""
|
|
232
232
|
The set of all dynamic parameters accepted by this Synth
|
|
233
233
|
|
|
234
234
|
Args:
|
|
235
235
|
aliases: if True, include aliases
|
|
236
|
-
aliased: include the original names of parameters which have an alias. If both
|
|
237
|
-
aliases and aliased params are included, there are two names to access
|
|
238
|
-
the same parameter
|
|
239
236
|
|
|
240
237
|
Returns:
|
|
241
238
|
a set of the dynamic (modifiable) parameters accepted by this synth
|
|
242
239
|
"""
|
|
243
240
|
instr = self.instr
|
|
244
|
-
return instr.dynamicParamNames(aliases=aliases
|
|
241
|
+
return instr.dynamicParamNames(aliases=aliases) if instr else EMPTYSET
|
|
245
242
|
|
|
246
243
|
def automate(self,
|
|
247
244
|
param: str,
|
|
@@ -252,8 +249,8 @@ class SchedEvent(BaseSchedEvent):
|
|
|
252
249
|
) -> float:
|
|
253
250
|
param = self.unaliasParam(param, param)
|
|
254
251
|
if self.parent is None:
|
|
255
|
-
if param not in
|
|
256
|
-
raise KeyError(f"Unknown parameter '{param}' for {self}. Possible parameters: {
|
|
252
|
+
if param not in self.instr.aliases and param not in self.instr.paramNames():
|
|
253
|
+
raise KeyError(f"Unknown parameter '{param}' for {self}. Possible parameters: {self.instr.paramNames()}")
|
|
257
254
|
if isinstance(pairs, tuple) and len(pairs) == 2 and isinstance(pairs[0], np.ndarray):
|
|
258
255
|
ts, values = pairs
|
|
259
256
|
assert isinstance(values, np.ndarray) and isinstance(ts, np.ndarray)
|
|
@@ -286,11 +283,15 @@ class SchedEvent(BaseSchedEvent):
|
|
|
286
283
|
else:
|
|
287
284
|
self.parent.unsched(self, delay=delay)
|
|
288
285
|
|
|
289
|
-
def controlNames(self, aliases=
|
|
290
|
-
return self.instr.controlNames(aliases=aliases
|
|
286
|
+
def controlNames(self, aliases=False) -> frozenset[str]:
|
|
287
|
+
return self.instr.controlNames(aliases=aliases)
|
|
288
|
+
|
|
289
|
+
def isPfield(self, param: str) -> bool:
|
|
290
|
+
pfields = self.instr.pfieldNames()
|
|
291
|
+
return param in pfields and (param2 := self.aliases().get(param)) is not None and param2 in pfiels
|
|
291
292
|
|
|
292
|
-
def pfieldNames(self, aliases=
|
|
293
|
-
return self.instr.pfieldNames(aliases=aliases
|
|
293
|
+
def pfieldNames(self, aliases=False) -> frozenset[str]:
|
|
294
|
+
return self.instr.pfieldNames(aliases=aliases)
|
|
294
295
|
|
|
295
296
|
def paramValue(self, param: str) -> float | str | None:
|
|
296
297
|
param = self.unaliasParam(param, param)
|
|
@@ -343,7 +344,7 @@ class SchedEventGroup(BaseSchedEvent):
|
|
|
343
344
|
def _setPfield(self, param: str, value: float, delay=0.) -> None:
|
|
344
345
|
count = 0
|
|
345
346
|
for ev in self:
|
|
346
|
-
if param in ev.pfieldNames(
|
|
347
|
+
if param in ev.pfieldNames(aliases=True):
|
|
347
348
|
ev._setPfield(delay=delay, param=param, value=value)
|
|
348
349
|
count += 1
|
|
349
350
|
if count == 0:
|
|
@@ -363,7 +364,7 @@ class SchedEventGroup(BaseSchedEvent):
|
|
|
363
364
|
def paramNames(self, aliases=True, aliased=False) -> frozenset[str]:
|
|
364
365
|
allparams = set()
|
|
365
366
|
for ev in self:
|
|
366
|
-
allparams.update(ev.paramNames(aliases=aliases
|
|
367
|
+
allparams.update(ev.paramNames(aliases=aliases))
|
|
367
368
|
return frozenset(allparams)
|
|
368
369
|
|
|
369
370
|
def paramValue(self, param: str) -> float | str | None:
|
|
@@ -383,10 +384,10 @@ class SchedEventGroup(BaseSchedEvent):
|
|
|
383
384
|
return None
|
|
384
385
|
|
|
385
386
|
@cache
|
|
386
|
-
def dynamicParamNames(self, aliases=
|
|
387
|
+
def dynamicParamNames(self, aliases=False) -> frozenset[str]:
|
|
387
388
|
params = set()
|
|
388
389
|
for ev in self:
|
|
389
|
-
params.update(ev.dynamicParamNames(aliases=aliases
|
|
390
|
+
params.update(ev.dynamicParamNames(aliases=aliases))
|
|
390
391
|
return frozenset(params)
|
|
391
392
|
|
|
392
393
|
def __hash__(self):
|
|
@@ -413,7 +414,7 @@ class SchedEventGroup(BaseSchedEvent):
|
|
|
413
414
|
count = 0
|
|
414
415
|
allparams = set()
|
|
415
416
|
for ev in self:
|
|
416
|
-
evparams = ev.dynamicParamNames(aliases=True
|
|
417
|
+
evparams = ev.dynamicParamNames(aliases=True)
|
|
417
418
|
allparams.update(evparams)
|
|
418
419
|
if param in evparams:
|
|
419
420
|
count += 1
|
|
@@ -437,7 +438,7 @@ class SchedEventGroup(BaseSchedEvent):
|
|
|
437
438
|
) -> float:
|
|
438
439
|
count = 0
|
|
439
440
|
for ev in self:
|
|
440
|
-
if param in ev.dynamicParamNames(aliases=True
|
|
441
|
+
if param in ev.dynamicParamNames(aliases=True):
|
|
441
442
|
count += 1
|
|
442
443
|
ev.automate(param=param, pairs=pairs, mode=mode,
|
|
443
444
|
delay=delay, overtake=overtake)
|