diff --git a/csoundengine/baseevent.py b/csoundengine/baseevent.py index 5e0cc2e..292e570 100644 --- a/csoundengine/baseevent.py +++ b/csoundengine/baseevent.py @@ -104,7 +104,7 @@ def set(self, param='', value: float = 0., delay=0., **kws) -> None: self._setp(param=param, value=value, delay=delay) else: raise KeyError(f"Unknown parameter: '{param}'. " - f"Possible parameters for this event: {self.dynamicParams(aliased=True)}") + f"Possible parameters for this event: {self.dynamicParamNames(aliased=True)}") def _automatePfield(self, param: int | str, @@ -197,9 +197,9 @@ def automate(self, elif param.startswith('p') or ((pargs := self.pfieldNames(aliases=False)) and param in pargs): return self._automatePfield(param=param, pairs=pairs, mode=mode, delay=delay, overtake=overtake) else: - raise KeyError(f"Unknown parameter '{param}', supported parameters: {self.dynamicParams()}") + raise KeyError(f"Unknown parameter '{param}', supported parameters: {self.dynamicParamNames()}") - def dynamicParams(self, aliases=True, aliased=False) -> set[str]: + def dynamicParamNames(self, aliases=True, aliased=False) -> set[str]: """ The set of all dynamic parameters accepted by this event diff --git a/csoundengine/engine.py b/csoundengine/engine.py index 000eb11..e179a44 100755 --- a/csoundengine/engine.py +++ b/csoundengine/engine.py @@ -3936,6 +3936,26 @@ def assignBus(self, kind='', value: float = None, persist=False self._perfThread.scoreEvent(0, "i", pfields) return bustoken + def busSystemStatus(self) -> dict: + """ + Get debugging nformation about the status of the bus system + + This is only provided for debugging + + Returns: + a dict containing information about the status of the bus system + (used buses, free buses, etc) + """ + if not self.hasBusSupport(): + raise RuntimeError("This Engine has no bus support") + + audioBusesFree = self.evalCode('pool_size(gi__buspool)') + controlBusesFree = self.evalCode('pool_size(gi__buspoolk)') + return {'audioBusesFree': audioBusesFree, + 'controlBusesFree': controlBusesFree, + 'numAudioBuses': self.numAudioBuses, + 'numControlBuses': self.numControlBuses} + def hasBusSupport(self) -> bool: """ Returns True if this Engine was started with bus support diff --git a/csoundengine/instr.py b/csoundengine/instr.py index 4efe6dd..b3a1e16 100644 --- a/csoundengine/instr.py +++ b/csoundengine/instr.py @@ -512,7 +512,7 @@ def dynamicParamNames(self, aliases=True, aliased=False return frozenset(dynparams.keys()) @cache - def dynamicPfields(self, aliases=True, aliased=True) -> dict[str, float]: + def dynamicPfields(self, aliases=True, aliased=False) -> dict[str, float]: """ The dynamic pfields in this instr @@ -526,11 +526,10 @@ def dynamicPfields(self, aliases=True, aliased=True) -> dict[str, float]: Returns: a dict mapping pfield name to default value. """ - if not self.pfieldNameToIndex: + if not self.pfields: return EMPTYDICT - pfields = {name: self.pfieldIndexToValue.get(idx, 0.) - for name, idx in self.pfieldNameToIndex.items() + pfields = {name: value for name, value in self.pfields.items() if name.startswith('k')} if aliases and self.aliases: @@ -538,7 +537,7 @@ def dynamicPfields(self, aliases=True, aliased=True) -> dict[str, float]: if realname in pfields: pfields[alias] = pfields[realname] if not aliased: - del pfields[realname] + pfields.pop(realname, None) return pfields @@ -592,11 +591,12 @@ def dynamicParams(self, aliases=True, aliased=False if not self.pfields and not self.controls: return EMPTYDICT - params = self.dynamicPfields() + params = self.dynamicPfields(aliases=False) if self.controls: params = params | self.controls if aliases and self.aliases: + params = params.copy() for alias, realname in self.aliases.items(): params[alias] = params[realname] if not aliased: @@ -611,7 +611,7 @@ def paramNames(self, aliases=True, aliased=False pfields = self.pfieldNames(aliases=aliases, aliased=aliased) return frozenset(pfields | self.controlNames()) if self.controls else pfields - @cache + # @cache def pfieldNames(self, aliases=True, aliased=False ) -> frozenset[str]: """ @@ -629,7 +629,7 @@ def pfieldNames(self, aliases=True, aliased=False pfields = set(self.pfieldNameToIndex.keys()) if aliases and self.aliases: for alias, realname in self.aliases.items(): - if alias in pfields: + if realname in pfields: pfields.add(alias) if not aliased: pfields.remove(realname) diff --git a/csoundengine/interact.py b/csoundengine/interact.py index ee3fd5d..acb1a35 100644 --- a/csoundengine/interact.py +++ b/csoundengine/interact.py @@ -182,7 +182,7 @@ def interactSynth(synth: _synth.AbstrSynth, specs: a list of ParamSpec """ if not specs: - dynparams = synth.dynamicParams(aliases=False) + dynparams = synth.dynamicParamNames(aliases=False) params = {param: synth.paramValue(param) for param in sorted(dynparams)} specs = guessParamSpecs(params=params) diff --git a/csoundengine/offline.py b/csoundengine/offline.py index 4a88503..7d1cbc3 100644 --- a/csoundengine/offline.py +++ b/csoundengine/offline.py @@ -242,7 +242,7 @@ def instr(self) -> Instr | None: def paramNames(self, aliases=True, aliased=False) -> frozenset[str]: return self.instr.paramNames(aliases=aliases, aliased=aliased) - def dynamicParams(self, aliases=True, aliased=False) -> frozenset[str]: + def dynamicParamNames(self, aliases=True, aliased=False) -> frozenset[str]: instr = self.instr return instr.dynamicParamNames(aliases=aliases, ) if instr else set() @@ -301,7 +301,7 @@ def _setp(self, param: str, value: float, delay=0.) -> None: ev._setp(delay=delay, param=param, value=value) count += 1 if count == 0: - raise KeyError(f"Parameter '{param}' unknown. Possible paramters: {self.dynamicParams(aliased=True)}") + raise KeyError(f"Parameter '{param}' unknown. Possible paramters: {self.dynamicParamNames(aliased=True)}") def _setTable(self, param: str, value: float, delay=0.) -> None: count = 0 @@ -311,7 +311,7 @@ def _setTable(self, param: str, value: float, delay=0.) -> None: count += 1 if count == 0: raise KeyError(f"Parameter '{param}' unknown. " - f"Possible parameters: {self.dynamicParams(aliased=True)}") + f"Possible parameters: {self.dynamicParamNames(aliased=True)}") @cache def paramNames(self, aliases=True, aliased=False) -> frozenset[str]: @@ -321,10 +321,10 @@ def paramNames(self, aliases=True, aliased=False) -> frozenset[str]: return frozenset(allparams) @cache - def dynamicParams(self, aliases=True, aliased=False) -> frozenset[str]: + def dynamicParamNames(self, aliases=True, aliased=False) -> frozenset[str]: params = set() for ev in self.events: - params.update(ev.dynamicParams(aliases=aliases, aliased=aliased)) + params.update(ev.dynamicParamNames(aliases=aliases, aliased=aliased)) return frozenset(params) def __hash__(self): @@ -339,13 +339,13 @@ def automate(self, ) -> None: count = 0 for ev in self.events: - if param in ev.dynamicParams(aliases=True, aliased=True): + if param in ev.dynamicParamNames(aliases=True, aliased=True): count += 1 ev.automate(param=param, pairs=pairs, mode=mode, delay=delay, overtake=overtake) if count == 0: raise KeyError(f"Param '{param}' not known by any events in this group. " - f"Possible parameters: {self.dynamicParams(aliased=True)}") + f"Possible parameters: {self.dynamicParamNames(aliased=True)}") @dataclass diff --git a/csoundengine/synth.py b/csoundengine/synth.py index 83ceb47..2932035 100644 --- a/csoundengine/synth.py +++ b/csoundengine/synth.py @@ -138,7 +138,7 @@ def ui(self, **specs: tuple[float, float]) -> None: """ from . import interact - dynparams = self.dynamicParams(aliases=True, aliased=False) + dynparams = self.dynamicParamNames(aliases=True, aliased=False) if not dynparams: logger.error(f"No named parameters for {self}") return @@ -366,7 +366,7 @@ def playing(self) -> bool: def finished(self) -> bool: return self.playStatus() == 'stopped' - def dynamicParams(self, aliases=True, aliased=False) -> frozenset[str]: + def dynamicParamNames(self, aliases=True, aliased=False) -> frozenset[str]: """ The set of all dynamic parameters accepted by this Synth @@ -382,7 +382,7 @@ def dynamicParams(self, aliases=True, aliased=False) -> frozenset[str]: """ return self.instr.dynamicParamNames(aliases=aliases, aliased=aliased) - def pfieldNames(self, aliases=True, aliased=False) -> set[str]: + def pfieldNames(self, aliases=True, aliased=False) -> frozenset[str]: return self.instr.pfieldNames(aliases=aliases, aliased=aliased) def _sliceStart(self) -> int: @@ -432,7 +432,7 @@ def _setp(self, param: str, value: float, delay=0.) -> None: idx = self.instr.pfieldIndex(param, default=0) if idx == 0: raise KeyError(f"Unknown parameter {param} for synth {self}. " - f"Possible parameters: {self.dynamicParams()}") + f"Possible parameters: {self.dynamicParamNames()}") self.engine.setp(self.p1, idx, value, delay=delay) @@ -571,7 +571,7 @@ def automate(self, overtake=overtake) param = self.unaliasParam(param, param) - params = self.dynamicParams(aliases=False) + params = self.instr.dynamicParams(aliases=False) if param not in params: raise KeyError(f"Unknown parameter '{param}' for {self}. Possible parameters: {params}") @@ -580,7 +580,7 @@ def automate(self, elif (pargs := self.pfieldNames(aliases=False)) and param in pargs: return self._automatePfield(param=param, pairs=pairs, mode=mode, delay=delay, overtake=overtake) else: - raise KeyError(f"Unknown parameter '{param}', supported parameters: {self.dynamicParams()}") + raise KeyError(f"Unknown parameter '{param}', supported parameters: {self.dynamicParamNames()}") def _automatePfield(self, param: int | str, pairs: list[float] | np.ndarray, mode="linear", delay=0., overtake=False) -> float: @@ -728,26 +728,26 @@ def _automateTable(self, param: str, pairs, mode="linear", delay=0., count = 0 for synth in self.synths: if isinstance(synth, Synth): - controls = synth.dynamicParams() + controls = synth.dynamicParamNames() if controls and param in controls: synth._automateTable(param, pairs, mode=mode, delay=delay, overtake=overtake) count += 1 elif isinstance(synth, SynthGroup): - controls = synth.dynamicParams() + controls = synth.dynamicParamNames() if controls and param in controls: synth._automateTable(param=param, pairs=pairs, mode=mode, delay=delay, overtake=overtake) count += 1 if count == 0: raise KeyError(f"Parameter '{param}' not known. " - f"Possible parameters: {self.dynamicParams()}") + f"Possible parameters: {self.dynamicParamNames()}") @cache - def dynamicParams(self, aliases=True, aliased=False) -> set[str]: + def dynamicParamNames(self, aliases=True, aliased=False) -> set[str]: out: set[str] = set() for synth in self.synths: - dynamicParams = synth.dynamicParams(aliases=aliases, aliased=aliased) + dynamicParams = synth.dynamicParamNames(aliases=aliases, aliased=aliased) out.update(dynamicParams) return out @@ -797,8 +797,6 @@ def automate(self, value of the given parameter is used. The same effect is achieved if the first value is given as 'nan', in this case also the current value of the synth is overtaken. - strict: if True, raise an Exception if the parameter does not match any - synth in this group Returns: a list of synthids. There will be one synthid per synth in this group. @@ -808,22 +806,15 @@ def automate(self, synth """ synthids = [] - if strict: - allparams = self.dynamicParams() - if param not in allparams: - lines = [f"Parameter '{param}' unknown. Known parameters: {allparams}", - f"Synths in this group:"] - for synth in self.synths: - lines.append(" " + repr(synth)) - logger.error("\n".join(lines)) - raise KeyError(f"Parameter {param} not supported by any synth in this group. " - f"Possible parameters: {allparams}") for synth in self.synths: if param in synth.instr.dynamicParams(): synthid = synth.automate(param=param, pairs=pairs, mode=mode, delay=delay, overtake=overtake) else: synthid = 0 synthids.append(synthid) + if all(synthid == 0 for synthid in synthids): + raise KeyError(f"Parameter '{param}' not known. Possible parameters: " + f"{self.dynamicParamNames(aliases=True, aliased=True)}") return synthids def _automatePfield(self, @@ -837,7 +828,7 @@ def _automatePfield(self, if param in synth.instr.dynamicPfieldNames()] if not eventids: raise ValueError(f"Parameter '{param}' unknown for group, possible " - f"parameters: {self.dynamicParams()}") + f"parameters: {self.dynamicParamNames()}") return eventids def _htmlTable(self) -> str: @@ -905,11 +896,11 @@ def __iter__(self): def _setTable(self, param: str, value: float, delay=0) -> None: count = 0 for synth in self.synths: - if param in synth.dynamicParams(aliases=True, aliased=True): + if param in synth.dynamicParamNames(aliases=True, aliased=True): synth._setTable(param=param, value=value, delay=delay) count += 1 if count == 0: - params = list(self.dynamicParams(aliases=False)) + params = list(self.dynamicParamNames(aliases=False)) if aliases := self.aliases(): params.extend(f'{alias}>{orig}' for alias, orig in aliases.items()) raise KeyError(f"Parameter '{param}' unknown. " @@ -939,7 +930,7 @@ def _setp(self, param: str, value: float, delay=0.) -> None: count += 1 if count == 0: raise KeyError(f"Parameter {param} unknown. " - f"Possible parameters: {self.dynamicParams()}") + f"Possible parameters: {self.dynamicParamNames()}") def controlNames(self, aliases=True, aliased=False) -> set[str]: """ diff --git a/setup.py b/setup.py index 2879ebc..3407688 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup import os -version = (2, 2, 1) +version = (2, 2, 2) from os import path this_directory = path.abspath(path.dirname(__file__))