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.
Files changed (134) hide show
  1. {csoundengine-2.22.4 → csoundengine-2.22.7}/.github/workflows/test2.yml +9 -2
  2. {csoundengine-2.22.4 → csoundengine-2.22.7}/PKG-INFO +1 -1
  3. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/abstractrenderer.py +4 -4
  4. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/baseschedevent.py +13 -17
  5. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csd.py +2 -2
  6. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csoundlib.py +92 -40
  7. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/engine.py +1 -0
  8. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/enginebase.py +1 -1
  9. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/engineorc.py +3 -2
  10. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/instr.py +19 -28
  11. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/internal.py +1 -1
  12. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offline.py +1 -1
  13. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/schedevent.py +27 -25
  14. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/session.py +49 -46
  15. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/synth.py +15 -17
  16. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/tools.py +24 -0
  17. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/PKG-INFO +1 -1
  18. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/SOURCES.txt +0 -1
  19. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/offlineengine.ipynb +11 -11
  20. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-bus.ipynb +353 -138
  21. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-dealloc-synth.ipynb +21 -20
  22. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-delay.ipynb +12 -15
  23. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-engine-params.ipynb +21 -26
  24. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-libcsound.ipynb +44 -100
  25. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-named-events.ipynb +51 -47
  26. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-new-named-params.ipynb +65 -84
  27. csoundengine-2.22.7/notebooks/test/test-offline-render.ipynb +704 -0
  28. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-offlinesession.ipynb +28 -28
  29. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-params.ipynb +149 -202
  30. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-playsample.ipynb +50 -53
  31. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-stop.ipynb +32 -39
  32. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-tight-scheduling.ipynb +29 -1047
  33. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/test-whenfinished.ipynb +88 -28
  34. {csoundengine-2.22.4 → csoundengine-2.22.7}/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.7}/.cirrus.yml +0 -0
  38. {csoundengine-2.22.4 → csoundengine-2.22.7}/.gitignore +0 -0
  39. {csoundengine-2.22.4 → csoundengine-2.22.7}/LICENSE.txt +0 -0
  40. {csoundengine-2.22.4 → csoundengine-2.22.7}/MANIFEST.in +0 -0
  41. {csoundengine-2.22.4 → csoundengine-2.22.7}/README.rst +0 -0
  42. {csoundengine-2.22.4 → csoundengine-2.22.7}/cmake/csound-custom-macos.cmake +0 -0
  43. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/__init__.py +0 -0
  44. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/_common.py +0 -0
  45. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/_palette.py +0 -0
  46. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/busproxy.py +0 -0
  47. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/config.py +0 -0
  48. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/contants.py +0 -0
  49. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csounddefs.py +0 -0
  50. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/csoundparse.py +0 -0
  51. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/dependencies.py +0 -0
  52. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/errors.py +0 -0
  53. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/event.py +0 -0
  54. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/instrtools.py +0 -0
  55. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/interact.py +0 -0
  56. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/jacktools.py +0 -0
  57. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/jupytertools.py +0 -0
  58. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/linuxaudio.py +0 -0
  59. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/magic.py +0 -0
  60. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offlineengine.py +0 -0
  61. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/offlineorc.py +0 -0
  62. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/paramtable.py +0 -0
  63. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/plotting.py +0 -0
  64. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/py.typed +0 -0
  65. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/renderjob.py +0 -0
  66. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sessionhandler.py +0 -0
  67. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sessioninstrs.py +0 -0
  68. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/sftools.py +0 -0
  69. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/state.py +0 -0
  70. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine/tableproxy.py +0 -0
  71. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/dependency_links.txt +0 -0
  72. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/requires.txt +0 -0
  73. {csoundengine-2.22.4 → csoundengine-2.22.7}/csoundengine.egg-info/top_level.txt +0 -0
  74. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Builtin-Opcodes.rst +0 -0
  75. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Installation.rst +0 -0
  76. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Introduction.rst +0 -0
  77. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Makefile +0 -0
  78. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/Reference.rst +0 -0
  79. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/_static/custom.css +0 -0
  80. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/_static/offlineengine-example1.mp3 +0 -0
  81. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/config-edit.png +0 -0
  82. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/eventui.png +0 -0
  83. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/eventui2.png +0 -0
  84. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-config.png +0 -0
  85. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-plotTable.png +0 -0
  86. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/jupyter-session-1.png +0 -0
  87. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/offlineengine-example1.png +0 -0
  88. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/select-preset.png +0 -0
  89. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/synthui.png +0 -0
  90. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableplot-sine.png +0 -0
  91. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableplot-stereo.png +0 -0
  92. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableproxy-plot.png +0 -0
  93. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/tableproxy-plotspectrogram.png +0 -0
  94. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/assets/ui2.png +0 -0
  95. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/conf.py +0 -0
  96. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/config.rst +0 -0
  97. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/configkeys.rst +0 -0
  98. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/csoundengine.rst +0 -0
  99. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/csoundlib.rst +0 -0
  100. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/index.rst +0 -0
  101. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/instr.rst +0 -0
  102. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/jupyternotebook.rst +0 -0
  103. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/magics.rst +0 -0
  104. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/make.bat +0 -0
  105. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offline.rst +0 -0
  106. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine.rst +0 -0
  107. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine_example.inc +0 -0
  108. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlineengine_example2.inc +0 -0
  109. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/offlinerenderer.rst +0 -0
  110. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/renderjob.rst +0 -0
  111. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/requirements.txt +0 -0
  112. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/schedevent.rst +0 -0
  113. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/session.rst +0 -0
  114. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/synth.rst +0 -0
  115. {csoundengine-2.22.4 → csoundengine-2.22.7}/docs/tables.rst +0 -0
  116. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/buses.ipynb +0 -0
  117. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/offlineengine-playback.ipynb +0 -0
  118. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/colours-german-male.flac +0 -0
  119. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/piano-pitchtest-2.flac +0 -0
  120. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/snd/voiceover-fragment-48k.flac +0 -0
  121. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/test/plltrack.ipynb +0 -0
  122. {csoundengine-2.22.4 → csoundengine-2.22.7}/notebooks/voice-pitch-track.ipynb +0 -0
  123. {csoundengine-2.22.4 → csoundengine-2.22.7}/pyrightconfig.json +0 -0
  124. {csoundengine-2.22.4 → csoundengine-2.22.7}/readthedocs.yml +0 -0
  125. {csoundengine-2.22.4 → csoundengine-2.22.7}/scripts/download-plugins.py +0 -0
  126. {csoundengine-2.22.4 → csoundengine-2.22.7}/scripts/update-config-docs.py +0 -0
  127. {csoundengine-2.22.4 → csoundengine-2.22.7}/setup.cfg +0 -0
  128. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/importtime.py +0 -0
  129. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/libcsoundtest.py +0 -0
  130. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/simple.py +0 -0
  131. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test1.csd +0 -0
  132. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test1.py +0 -0
  133. {csoundengine-2.22.4 → csoundengine-2.22.7}/test/test2.py +0 -0
  134. {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
- 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.7
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
@@ -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=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,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
- 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
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, aliased=aliased)
617
+ dynparams = self.dynamicParams(aliases=aliases)
620
618
  return frozenset(dynparams.keys())
621
619
 
622
620
  @cache
623
- def dynamicPfields(self, aliases=True, aliased=False) -> dict[str, float]:
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
- if not aliased:
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=True, aliased=False
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
- if not aliased:
712
- del params[realname]
707
+ del params[realname]
713
708
  return params
714
709
 
715
- def paramNames(self, aliases=True, aliased=False
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, aliased=aliased)
715
+ pfields = self.pfieldNames(aliases=aliases)
721
716
  return frozenset(pfields | self.controlNames()) if self.controls else pfields
722
717
 
723
- # @cache
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
- aliased: include parameters which have an alias (implies aliases)
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
- if not aliased:
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(aliased=True)
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=True, aliased=False) -> dict[str, float]:
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
- if not aliased:
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(aliases=True, aliased=True),
815
- controlNames=self.controlNames(aliases=True, aliased=True))
805
+ pfieldNames=self.pfieldNames(),
806
+ controlNames=self.controlNames())
816
807
 
817
808
  def pfieldName(self, index: int, alias=True) -> str:
818
809
  """
@@ -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