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.
Files changed (134) hide show
  1. {csoundengine-2.22.4 → csoundengine-2.22.6}/.github/workflows/test2.yml +9 -2
  2. {csoundengine-2.22.4 → csoundengine-2.22.6}/PKG-INFO +1 -1
  3. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/abstractrenderer.py +4 -4
  4. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/baseschedevent.py +13 -17
  5. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csd.py +2 -2
  6. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csoundlib.py +92 -40
  7. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/engine.py +1 -0
  8. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/enginebase.py +1 -1
  9. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/engineorc.py +3 -2
  10. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/instr.py +16 -22
  11. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/internal.py +1 -1
  12. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offline.py +1 -1
  13. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/schedevent.py +20 -19
  14. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/session.py +49 -46
  15. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/synth.py +10 -13
  16. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/tools.py +24 -0
  17. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/PKG-INFO +1 -1
  18. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/SOURCES.txt +0 -1
  19. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/offlineengine.ipynb +11 -11
  20. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-bus.ipynb +353 -138
  21. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-dealloc-synth.ipynb +21 -20
  22. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-delay.ipynb +12 -15
  23. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-engine-params.ipynb +21 -26
  24. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-libcsound.ipynb +44 -100
  25. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-named-events.ipynb +51 -47
  26. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-new-named-params.ipynb +65 -84
  27. csoundengine-2.22.6/notebooks/test/test-offline-render.ipynb +704 -0
  28. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-offlinesession.ipynb +28 -28
  29. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-params.ipynb +149 -202
  30. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-playsample.ipynb +50 -53
  31. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-stop.ipynb +32 -39
  32. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-tight-scheduling.ipynb +29 -1047
  33. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/test-whenfinished.ipynb +88 -28
  34. {csoundengine-2.22.4 → csoundengine-2.22.6}/pyproject.toml +1 -1
  35. csoundengine-2.22.4/csoundengine/__sessionbase.py +0 -38
  36. csoundengine-2.22.4/notebooks/test/test-offline-render.ipynb +0 -738
  37. {csoundengine-2.22.4 → csoundengine-2.22.6}/.cirrus.yml +0 -0
  38. {csoundengine-2.22.4 → csoundengine-2.22.6}/.gitignore +0 -0
  39. {csoundengine-2.22.4 → csoundengine-2.22.6}/LICENSE.txt +0 -0
  40. {csoundengine-2.22.4 → csoundengine-2.22.6}/MANIFEST.in +0 -0
  41. {csoundengine-2.22.4 → csoundengine-2.22.6}/README.rst +0 -0
  42. {csoundengine-2.22.4 → csoundengine-2.22.6}/cmake/csound-custom-macos.cmake +0 -0
  43. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/__init__.py +0 -0
  44. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/_common.py +0 -0
  45. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/_palette.py +0 -0
  46. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/busproxy.py +0 -0
  47. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/config.py +0 -0
  48. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/contants.py +0 -0
  49. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csounddefs.py +0 -0
  50. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/csoundparse.py +0 -0
  51. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/dependencies.py +0 -0
  52. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/errors.py +0 -0
  53. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/event.py +0 -0
  54. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/instrtools.py +0 -0
  55. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/interact.py +0 -0
  56. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/jacktools.py +0 -0
  57. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/jupytertools.py +0 -0
  58. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/linuxaudio.py +0 -0
  59. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/magic.py +0 -0
  60. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offlineengine.py +0 -0
  61. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/offlineorc.py +0 -0
  62. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/paramtable.py +0 -0
  63. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/plotting.py +0 -0
  64. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/py.typed +0 -0
  65. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/renderjob.py +0 -0
  66. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sessionhandler.py +0 -0
  67. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sessioninstrs.py +0 -0
  68. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/sftools.py +0 -0
  69. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/state.py +0 -0
  70. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine/tableproxy.py +0 -0
  71. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/dependency_links.txt +0 -0
  72. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/requires.txt +0 -0
  73. {csoundengine-2.22.4 → csoundengine-2.22.6}/csoundengine.egg-info/top_level.txt +0 -0
  74. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Builtin-Opcodes.rst +0 -0
  75. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Installation.rst +0 -0
  76. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Introduction.rst +0 -0
  77. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Makefile +0 -0
  78. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/Reference.rst +0 -0
  79. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/_static/custom.css +0 -0
  80. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/_static/offlineengine-example1.mp3 +0 -0
  81. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/config-edit.png +0 -0
  82. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/eventui.png +0 -0
  83. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/eventui2.png +0 -0
  84. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-config.png +0 -0
  85. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-plotTable.png +0 -0
  86. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/jupyter-session-1.png +0 -0
  87. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/offlineengine-example1.png +0 -0
  88. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/select-preset.png +0 -0
  89. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/synthui.png +0 -0
  90. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableplot-sine.png +0 -0
  91. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableplot-stereo.png +0 -0
  92. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableproxy-plot.png +0 -0
  93. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/tableproxy-plotspectrogram.png +0 -0
  94. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/assets/ui2.png +0 -0
  95. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/conf.py +0 -0
  96. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/config.rst +0 -0
  97. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/configkeys.rst +0 -0
  98. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/csoundengine.rst +0 -0
  99. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/csoundlib.rst +0 -0
  100. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/index.rst +0 -0
  101. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/instr.rst +0 -0
  102. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/jupyternotebook.rst +0 -0
  103. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/magics.rst +0 -0
  104. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/make.bat +0 -0
  105. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offline.rst +0 -0
  106. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine.rst +0 -0
  107. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine_example.inc +0 -0
  108. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlineengine_example2.inc +0 -0
  109. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/offlinerenderer.rst +0 -0
  110. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/renderjob.rst +0 -0
  111. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/requirements.txt +0 -0
  112. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/schedevent.rst +0 -0
  113. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/session.rst +0 -0
  114. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/synth.rst +0 -0
  115. {csoundengine-2.22.4 → csoundengine-2.22.6}/docs/tables.rst +0 -0
  116. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/buses.ipynb +0 -0
  117. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/offlineengine-playback.ipynb +0 -0
  118. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/colours-german-male.flac +0 -0
  119. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/piano-pitchtest-2.flac +0 -0
  120. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/snd/voiceover-fragment-48k.flac +0 -0
  121. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/test/plltrack.ipynb +0 -0
  122. {csoundengine-2.22.4 → csoundengine-2.22.6}/notebooks/voice-pitch-track.ipynb +0 -0
  123. {csoundengine-2.22.4 → csoundengine-2.22.6}/pyrightconfig.json +0 -0
  124. {csoundengine-2.22.4 → csoundengine-2.22.6}/readthedocs.yml +0 -0
  125. {csoundengine-2.22.4 → csoundengine-2.22.6}/scripts/download-plugins.py +0 -0
  126. {csoundengine-2.22.4 → csoundengine-2.22.6}/scripts/update-config-docs.py +0 -0
  127. {csoundengine-2.22.4 → csoundengine-2.22.6}/setup.cfg +0 -0
  128. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/importtime.py +0 -0
  129. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/libcsoundtest.py +0 -0
  130. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/simple.py +0 -0
  131. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test1.csd +0 -0
  132. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test1.py +0 -0
  133. {csoundengine-2.22.4 → csoundengine-2.22.6}/test/test2.py +0 -0
  134. {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
- Move-Item -Force -Path ".\csound-binaries\build\Release\*" -Destination "C:\Program Files\csound"
44
- ls -r "C:/Program Files/csound"
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csoundengine
3
- Version: 2.22.4
3
+ Version: 2.22.6
4
4
  Summary: An audio synthesis framework using csound
5
5
  Author-email: Eduardo Moguillansky <eduardo.moguillansky@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -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, instrname: str) -> _instr.Instr | None:
137
+ def getInstr(self, name: str) -> _instr.Instr | None:
138
138
  """
139
- Returns the Instr instance corresponding to instrname, or None if no such instr
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(aliased=True)}, "
131
- f"aliases: {self.aliases()}, pfields: {self.pfieldNames(aliases=False)}, "
132
- f"control names: {self.controlNames(aliases=False)}")
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=True, aliased=False) -> frozenset[str]:
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
- if not aliased:
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=True, aliased=False) -> frozenset[str]:
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, included aliases for pfields (if applicable)
278
- aliased: if True, include the aliased names. Otherwise if aliases
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=True, aliased=False) -> frozenset[str]:
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=True, aliased=False) -> frozenset[str]:
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, aliased=aliased)
312
- tableargs = self.controlNames(aliases=aliases, aliased=aliased)
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, aliased=True)
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 = np.zeros((tablength+1,), dtype=float)
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="text") -> list[np.ndarray]:
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
- return _ftsaveReadText(path)
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
@@ -2053,6 +2053,7 @@ class Engine(_EngineBase):
2053
2053
  callback=None,
2054
2054
  ) -> int:
2055
2055
  """
2056
+
2056
2057
  Create a new table and fill it with data.
2057
2058
 
2058
2059
  Args:
@@ -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 ${notifyDeallocOsc}
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=True, aliased=False) -> frozenset[str]:
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
- if not aliased:
603
- names.remove(name)
602
+ names.remove(name)
604
603
  return frozenset(names)
605
604
 
606
605
  @cache
607
- def dynamicParamNames(self, aliases=True, aliased=False
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, aliased=aliased)
618
+ dynparams = self.dynamicParams(aliases=aliases)
620
619
  return frozenset(dynparams.keys())
621
620
 
622
621
  @cache
623
- def dynamicPfields(self, aliases=True, aliased=False) -> dict[str, float]:
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
- if not aliased:
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=True, aliased=False
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
- if not aliased:
712
- del params[realname]
709
+ del params[realname]
713
710
  return params
714
711
 
715
- def paramNames(self, aliases=True, aliased=False
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, aliased=aliased)
717
+ pfields = self.pfieldNames(aliases=aliases)
721
718
  return frozenset(pfields | self.controlNames()) if self.controls else pfields
722
719
 
723
- # @cache
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
- if not aliased:
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=True, aliased=False) -> dict[str, float]:
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
- if not aliased:
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(aliases=True, aliased=True),
815
- controlNames=self.controlNames(aliases=True, aliased=True))
808
+ pfieldNames=self.pfieldNames(),
809
+ controlNames=self.controlNames())
816
810
 
817
811
  def pfieldName(self, index: int, alias=True) -> str:
818
812
  """
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
21
21
  import numpy.typing as npt
22
22
  from matplotlib.figure import Figure
23
23
 
24
- from csoundlib import AudioDevice, MidiDevice
24
+ from csoundengine.csoundlib import AudioDevice, MidiDevice
25
25
 
26
26
  from .instr import Instr
27
27
  T = TypeVar('T')
@@ -517,7 +517,7 @@ class OfflineSession(AbstractRenderer):
517
517
  """
518
518
  return self.instrs
519
519
 
520
- def getInstr(self, name) -> Instr | None:
520
+ def getInstr(self, name: str) -> Instr | None:
521
521
  """
522
522
  Find a registered Instr, by name
523
523
 
@@ -224,24 +224,21 @@ class SchedEvent(BaseSchedEvent):
224
224
  self._instr = instr
225
225
  return instr
226
226
 
227
- def paramNames(self, aliases=True, aliased=False) -> frozenset[str]:
228
- return self.instr.paramNames(aliases=aliases, aliased=aliased)
227
+ def paramNames(self, aliases=False) -> frozenset[str]:
228
+ return self.instr.paramNames(aliases=aliases)
229
229
 
230
- def dynamicParamNames(self, aliases=True, aliased=False) -> frozenset[str]:
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, aliased=aliased) if instr else EMPTYSET
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 (params := self.instr.dynamicParams(aliased=True)):
256
- raise KeyError(f"Unknown parameter '{param}' for {self}. Possible parameters: {params}")
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=True, aliased=False) -> frozenset[str]:
290
- return self.instr.controlNames(aliases=aliases, aliased=aliased)
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=True, aliased=False) -> frozenset[str]:
293
- return self.instr.pfieldNames(aliases=aliases, aliased=aliased)
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(aliased=True):
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, aliased=aliased))
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=True, aliased=False) -> frozenset[str]:
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, aliased=aliased))
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, aliased=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, aliased=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)