csoundengine 2.22.4__tar.gz → 2.22.7__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.7}/.github/workflows/test2.yml +9 -2
- {csoundengine-2.22.4 → csoundengine-2.22.7}/PKG-INFO +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/abstractrenderer.py +4 -4
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/baseschedevent.py +13 -17
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csd.py +2 -2
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csoundlib.py +92 -40
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/engine.py +1 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/enginebase.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/engineorc.py +3 -2
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/instr.py +19 -28
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/internal.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offline.py +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/schedevent.py +27 -25
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/session.py +49 -46
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/synth.py +15 -17
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/tools.py +24 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/PKG-INFO +1 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/SOURCES.txt +0 -1
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/offlineengine.ipynb +11 -11
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-bus.ipynb +353 -138
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-dealloc-synth.ipynb +21 -20
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-delay.ipynb +12 -15
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-engine-params.ipynb +21 -26
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-libcsound.ipynb +44 -100
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-named-events.ipynb +51 -47
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-new-named-params.ipynb +65 -84
- csoundengine-2.22.7/notebooks/test/test-offline-render.ipynb +704 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-offlinesession.ipynb +28 -28
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-params.ipynb +149 -202
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-playsample.ipynb +50 -53
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-stop.ipynb +32 -39
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-tight-scheduling.ipynb +29 -1047
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-whenfinished.ipynb +88 -28
- {csoundengine-2.22.4 → csoundengine-2.22.7}/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.7}/.cirrus.yml +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/.gitignore +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/LICENSE.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/MANIFEST.in +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/README.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/cmake/csound-custom-macos.cmake +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/__init__.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/_common.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/_palette.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/busproxy.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/config.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/contants.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csounddefs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csoundparse.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/dependencies.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/errors.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/event.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/instrtools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/interact.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/jacktools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/jupytertools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/linuxaudio.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/magic.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offlineengine.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offlineorc.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/paramtable.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/plotting.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/py.typed +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/renderjob.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sessionhandler.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sessioninstrs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sftools.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/state.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/tableproxy.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/dependency_links.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/requires.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/top_level.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Builtin-Opcodes.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Installation.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Introduction.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Makefile +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Reference.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/_static/custom.css +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/_static/offlineengine-example1.mp3 +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/config-edit.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/eventui.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/eventui2.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-config.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-plotTable.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-session-1.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/offlineengine-example1.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/select-preset.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/synthui.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableplot-sine.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableplot-stereo.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableproxy-plot.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableproxy-plotspectrogram.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/ui2.png +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/conf.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/config.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/configkeys.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/csoundengine.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/csoundlib.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/index.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/instr.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/jupyternotebook.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/magics.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/make.bat +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offline.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine_example.inc +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine_example2.inc +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlinerenderer.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/renderjob.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/requirements.txt +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/schedevent.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/session.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/synth.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/tables.rst +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/buses.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/offlineengine-playback.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/colours-german-male.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/piano-pitchtest-2.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/voiceover-fragment-48k.flac +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/plltrack.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/voice-pitch-track.ipynb +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/pyrightconfig.json +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/readthedocs.yml +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/scripts/download-plugins.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/scripts/update-config-docs.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/setup.cfg +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/importtime.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/libcsoundtest.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/simple.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test1.csd +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test1.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test2.py +0 -0
- {csoundengine-2.22.4 → csoundengine-2.22.7}/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
|
|
@@ -371,7 +371,7 @@ class Instr:
|
|
|
371
371
|
self.pfieldIndexToValue: Mapping[int, float | str] = pargsIndexToValue
|
|
372
372
|
"Dict mapping pfield index to its default value"
|
|
373
373
|
|
|
374
|
-
self.aliases = aliases if aliases is not None else EMPTYDICT
|
|
374
|
+
self.aliases: dict[str, str] = aliases if aliases is not None else EMPTYDICT
|
|
375
375
|
"""Maps alias argument names to their real argument names
|
|
376
376
|
|
|
377
377
|
Aliased parameters can be pfields or named controls"""
|
|
@@ -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,28 +599,26 @@ 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
|
|
611
610
|
|
|
612
611
|
Args:
|
|
613
612
|
aliases: include aliases
|
|
614
|
-
aliased: include parameters which have an alias (implies aliases)
|
|
615
613
|
|
|
616
614
|
Returns:
|
|
617
615
|
a set of the dynamic (modifiable) parameters accepted by this Instr
|
|
618
616
|
"""
|
|
619
|
-
dynparams = self.dynamicParams(aliases=aliases
|
|
617
|
+
dynparams = self.dynamicParams(aliases=aliases)
|
|
620
618
|
return frozenset(dynparams.keys())
|
|
621
619
|
|
|
622
620
|
@cache
|
|
623
|
-
def dynamicPfields(self, aliases=
|
|
621
|
+
def dynamicPfields(self, aliases=False) -> dict[str, float]:
|
|
624
622
|
"""
|
|
625
623
|
The dynamic pfields in this instr
|
|
626
624
|
|
|
@@ -629,7 +627,6 @@ class Instr:
|
|
|
629
627
|
|
|
630
628
|
Args:
|
|
631
629
|
aliases: include aliases
|
|
632
|
-
aliased: include parameters which have an alias (implies aliases)
|
|
633
630
|
|
|
634
631
|
Returns:
|
|
635
632
|
a dict mapping pfield name to default value.
|
|
@@ -644,8 +641,7 @@ class Instr:
|
|
|
644
641
|
for alias, realname in self.aliases.items():
|
|
645
642
|
if realname in pfields:
|
|
646
643
|
pfields[alias] = pfields[realname]
|
|
647
|
-
|
|
648
|
-
pfields.pop(realname, None)
|
|
644
|
+
pfields.pop(realname, None)
|
|
649
645
|
|
|
650
646
|
# We know that pfields cannot hold any string since we are selecting by 'k' prefix
|
|
651
647
|
return pfields # type: ignore
|
|
@@ -680,7 +676,7 @@ class Instr:
|
|
|
680
676
|
return frozenset(pfields)
|
|
681
677
|
|
|
682
678
|
@cache
|
|
683
|
-
def dynamicParams(self, aliases=
|
|
679
|
+
def dynamicParams(self, aliases=False
|
|
684
680
|
) -> dict[str, float]:
|
|
685
681
|
"""
|
|
686
682
|
A dict with all dynamic parameters defined in this instr
|
|
@@ -708,27 +704,25 @@ class Instr:
|
|
|
708
704
|
params = params.copy()
|
|
709
705
|
for alias, realname in self.aliases.items():
|
|
710
706
|
params[alias] = params[realname]
|
|
711
|
-
|
|
712
|
-
del params[realname]
|
|
707
|
+
del params[realname]
|
|
713
708
|
return params
|
|
714
709
|
|
|
715
|
-
def paramNames(self, aliases=
|
|
710
|
+
def paramNames(self, aliases=False
|
|
716
711
|
) -> frozenset[str]:
|
|
717
712
|
"""
|
|
718
713
|
All parameter names
|
|
719
714
|
"""
|
|
720
|
-
pfields = self.pfieldNames(aliases=aliases
|
|
715
|
+
pfields = self.pfieldNames(aliases=aliases)
|
|
721
716
|
return frozenset(pfields | self.controlNames()) if self.controls else pfields
|
|
722
717
|
|
|
723
|
-
|
|
724
|
-
def pfieldNames(self, aliases=True, aliased=False
|
|
718
|
+
def pfieldNames(self, aliases=False
|
|
725
719
|
) -> frozenset[str]:
|
|
726
720
|
"""
|
|
727
721
|
The set of named pfields declared in this instrument
|
|
728
722
|
|
|
729
723
|
Args:
|
|
730
724
|
aliases: include aliases
|
|
731
|
-
|
|
725
|
+
|
|
732
726
|
Returns:
|
|
733
727
|
a set with the named pfields defined in this instr
|
|
734
728
|
"""
|
|
@@ -740,20 +734,19 @@ class Instr:
|
|
|
740
734
|
for alias, realname in self.aliases.items():
|
|
741
735
|
if realname in pfields:
|
|
742
736
|
pfields.add(alias)
|
|
743
|
-
|
|
744
|
-
pfields.remove(realname)
|
|
737
|
+
pfields.remove(realname)
|
|
745
738
|
return frozenset(pfields)
|
|
746
739
|
|
|
747
740
|
def paramValue(self, param: str) -> float | str | None:
|
|
748
741
|
param2 = self.unaliasParam(param, param)
|
|
749
|
-
defaults = self.paramDefaultValues(
|
|
742
|
+
defaults = self.paramDefaultValues()
|
|
750
743
|
if param2 not in defaults:
|
|
751
744
|
raise KeyError(f"Unknown parameter '{param}'. "
|
|
752
745
|
f"Possible parameters: {defaults.keys()}")
|
|
753
746
|
return defaults[param2]
|
|
754
747
|
|
|
755
748
|
@cache
|
|
756
|
-
def paramDefaultValues(self, aliases=
|
|
749
|
+
def paramDefaultValues(self, aliases=False) -> dict[str, float]:
|
|
757
750
|
"""
|
|
758
751
|
A dict mapping named parameters to their default values
|
|
759
752
|
|
|
@@ -763,7 +756,6 @@ class Instr:
|
|
|
763
756
|
|
|
764
757
|
Args:
|
|
765
758
|
aliases: included aliases
|
|
766
|
-
aliased: include parameters which have an alias
|
|
767
759
|
|
|
768
760
|
Returns:
|
|
769
761
|
a dict of named dynamic parameters to this instr and their associated
|
|
@@ -793,8 +785,7 @@ class Instr:
|
|
|
793
785
|
if self.aliases and aliases:
|
|
794
786
|
for alias, realname in self.aliases.items():
|
|
795
787
|
params[alias] = params[realname]
|
|
796
|
-
|
|
797
|
-
del params[realname]
|
|
788
|
+
del params[realname]
|
|
798
789
|
|
|
799
790
|
return params
|
|
800
791
|
|
|
@@ -811,8 +802,8 @@ class Instr:
|
|
|
811
802
|
parameter to its given value
|
|
812
803
|
"""
|
|
813
804
|
return instrtools.distributeParams(params=params,
|
|
814
|
-
pfieldNames=self.pfieldNames(
|
|
815
|
-
controlNames=self.controlNames(
|
|
805
|
+
pfieldNames=self.pfieldNames(),
|
|
806
|
+
controlNames=self.controlNames())
|
|
816
807
|
|
|
817
808
|
def pfieldName(self, index: int, alias=True) -> str:
|
|
818
809
|
"""
|