From 5a7363654fad6db2e945d10051beac01d8549e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9phine=20Wolf=20Oberholtzer?= Date: Sun, 14 Apr 2024 10:49:25 -0400 Subject: [PATCH] Re-implement graphdefs --- supriya/assets/synthdefs/default.py | 2 +- supriya/assets/synthdefs/system_synthdefs.py | 29 +- supriya/assets/synthdefs/test.py | 4 +- supriya/contexts/core.py | 32 +- supriya/contexts/entities.py | 10 + supriya/contexts/requests.py | 22 +- supriya/enums.py | 15 +- supriya/ext/mypy.py | 66 +- supriya/patterns/structure.py | 4 +- supriya/ugens/basic.py | 32 +- supriya/ugens/bufio.py | 24 +- supriya/ugens/core.py | 10011 +++++++++-------- supriya/ugens/demand.py | 10 +- supriya/ugens/diskio.py | 4 +- supriya/ugens/dynamics.py | 2 +- supriya/ugens/envelopes.py | 77 +- supriya/ugens/factories.py | 677 +- supriya/ugens/ffsinosc.py | 57 +- supriya/ugens/granular.py | 4 +- supriya/ugens/hilbert.py | 2 +- supriya/ugens/inout.py | 8 +- supriya/ugens/ml.py | 8 +- supriya/ugens/panning.py | 80 +- supriya/ugens/pv.py | 7 +- supriya/ugens/triggers.py | 38 +- tests/book/test_ext_book.py | 22 +- tests/contexts/test_Score_nodes.py | 6 +- tests/contexts/test_Score_render.py | 16 +- tests/contexts/test_Score_synthdefs.py | 14 +- tests/contexts/test_Server_misc.py | 6 +- tests/contexts/test_Server_nodes.py | 8 +- tests/contexts/test_Server_synthdefs.py | 16 +- tests/ugens/test_SynthDefFactory.py | 52 +- tests/ugens/test_SynthDef___str__.py | 6 +- tests/ugens/test_SynthDef_parameters.py | 236 +- tests/ugens/test_SynthDef_shared.py | 7 +- tests/ugens/test_SynthDef_splay.py | 18 +- tests/ugens/test_SynthDef_system.py | 46 +- 38 files changed, 5987 insertions(+), 5691 deletions(-) diff --git a/supriya/assets/synthdefs/default.py b/supriya/assets/synthdefs/default.py index 3bc258f5d..83bc06ed3 100644 --- a/supriya/assets/synthdefs/default.py +++ b/supriya/assets/synthdefs/default.py @@ -19,7 +19,7 @@ def _build_default_synthdef() -> SynthDef: amplitude=0.1, frequency=440, gate=1, - out=Parameter(parameter_rate=ParameterRate.SCALAR, value=0), + out=Parameter(rate=ParameterRate.SCALAR, value=0), pan=0.5, ) as builder: low_pass = LPF.ar( diff --git a/supriya/assets/synthdefs/system_synthdefs.py b/supriya/assets/synthdefs/system_synthdefs.py index 519d661ce..4d5099899 100644 --- a/supriya/assets/synthdefs/system_synthdefs.py +++ b/supriya/assets/synthdefs/system_synthdefs.py @@ -1,5 +1,13 @@ -from ...enums import EnvelopeShape -from ...ugens import EnvGen, Envelope, In, InFeedback, Out, SynthDef, SynthDefBuilder +from supriya.enums import EnvelopeShape +from supriya.ugens import ( + EnvGen, + Envelope, + In, + InFeedback, + Out, + SynthDef, + SynthDefBuilder, +) def _build_link_audio_synthdef(channel_count: int) -> SynthDef: @@ -7,19 +15,20 @@ def _build_link_audio_synthdef(channel_count: int) -> SynthDef: out=0, in_=16, gate=1, fade_time=0.02, done_action=2 ) as builder: start_value = builder["fade_time"] <= 0 - envelope = EnvGen.kr( + envelope = Envelope( + amplitudes=[start_value, 1.0, 0.0], + durations=[1.0, 1.0], + curves=[EnvelopeShape.SINE], + release_node=1, + ) + envgen = EnvGen.kr( done_action=builder["done_action"], - envelope=Envelope( - amplitudes=[start_value, 1.0, 0.0], - durations=[1.0, 1.0], - curves=[EnvelopeShape.SINE], - release_node=1, - ), + envelope=envelope, gate=builder["gate"], time_scale=builder["fade_time"], ) source = InFeedback.ar(bus=builder["in_"], channel_count=channel_count) - Out.ar(bus=builder["out"], source=source * envelope) + Out.ar(bus=builder["out"], source=source * envgen) return builder.build(name=f"system_link_audio_{channel_count}") diff --git a/supriya/assets/synthdefs/test.py b/supriya/assets/synthdefs/test.py index be1c2eee9..1bcd02189 100644 --- a/supriya/assets/synthdefs/test.py +++ b/supriya/assets/synthdefs/test.py @@ -5,7 +5,7 @@ def _build_test_synthdef() -> SynthDef: with SynthDefBuilder( frequency=440, - amplitude=Parameter(value=1.0, parameter_rate=ParameterRate.AUDIO), + amplitude=Parameter(value=1.0, rate=ParameterRate.AUDIO), ) as builder: sin_osc = SinOsc.ar(frequency=builder["frequency"]) enveloped_sin = sin_osc * builder["amplitude"] @@ -16,7 +16,7 @@ def _build_test_synthdef() -> SynthDef: def _build_test_two_voice_synthdef() -> SynthDef: with SynthDefBuilder( frequencies=(220, 440), - amplitude=Parameter(value=1.0, parameter_rate=ParameterRate.AUDIO), + amplitude=Parameter(value=1.0, rate=ParameterRate.AUDIO), ) as builder: sin_osc = SinOsc.ar(frequency=builder["frequencies"]) enveloped_sin = sin_osc * builder["amplitude"] diff --git a/supriya/contexts/core.py b/supriya/contexts/core.py index 0bf2d571c..e288338f4 100644 --- a/supriya/contexts/core.py +++ b/supriya/contexts/core.py @@ -606,26 +606,34 @@ def add_synth( raise ValueError(add_action_) target_node_id = self._resolve_node(target_node) synthdef_kwargs: Dict[ - Union[int, str], Union[SupportsFloat, str, Tuple[float, ...]] + Union[int, str], Union[float, str, Tuple[Union[float, str], ...]] ] = {} for _, parameter in synthdef.indexed_parameters: if parameter.name not in settings: continue value = settings[parameter.name] + if not isinstance(value, Sequence) or isinstance(value, str): + value = (value,) if value == parameter.value: continue - if parameter.parameter_rate is ParameterRate.SCALAR: - synthdef_kwargs[parameter.name] = float(value) - elif parameter.name in ("in_", "out"): - synthdef_kwargs[parameter.name] = float(value) - elif isinstance(value, Bus): - synthdef_kwargs[parameter.name] = value.map_symbol() - elif isinstance(value, str): - synthdef_kwargs[parameter.name] = value - elif isinstance(value, tuple): - synthdef_kwargs[parameter.name] = tuple((float(v) for v in value)) + if parameter.rate is ParameterRate.SCALAR or parameter.name in ( + "in_", + "out", + ): + synthdef_kwargs[parameter.name] = tuple(float(v) for v in value) else: - synthdef_kwargs[parameter.name] = float(value) + processed_values: List[Union[float, str]] = [] + for v in value: + if isinstance(v, Bus): + processed_values.append(v.map_symbol()) + elif isinstance(v, str): + processed_values.append(v) + else: + processed_values.append(float(v)) + if len(processed_values) == 1: + synthdef_kwargs[parameter.name] = processed_values[0] + else: + synthdef_kwargs[parameter.name] = tuple(processed_values) id_ = self._allocate_id(Node, permanent=permanent) self._add_requests( NewSynth( diff --git a/supriya/contexts/entities.py b/supriya/contexts/entities.py index 3a61a135a..8dfc08241 100644 --- a/supriya/contexts/entities.py +++ b/supriya/contexts/entities.py @@ -568,6 +568,16 @@ def free(self): """ self.context.free_bus_group(self) + def map_symbol(self) -> str: + """ + Get the bus group's map symbol. + """ + if self.calculation_rate is CalculationRate.AUDIO: + return f"a{self.id_}" + elif self.calculation_rate is CalculationRate.CONTROL: + return f"c{self.id_}" + raise InvalidCalculationRate + @dataclasses.dataclass(frozen=True) class Node(ContextObject): diff --git a/supriya/contexts/requests.py b/supriya/contexts/requests.py index 097399059..635aa0b9a 100644 --- a/supriya/contexts/requests.py +++ b/supriya/contexts/requests.py @@ -14,7 +14,6 @@ List, Optional, Sequence, - SupportsFloat, SupportsInt, Tuple, Union, @@ -1309,11 +1308,11 @@ class NewSynth(Request): add_action: AddActionLike target_node_id: SupportsInt controls: Optional[ - Dict[Union[int, str], Union[SupportsFloat, str, Tuple[float, ...]]] + Dict[Union[int, str], Union[float, str, Tuple[Union[float, str], ...]]] ] = None def to_osc(self) -> OscMessage: - contents: List[Union[float, str, Tuple[float, ...]]] = [ + contents: List[Union[float, str, Tuple[Union[float, str], ...]]] = [ ( self.synthdef.actual_name if isinstance(self.synthdef, SynthDef) @@ -1325,12 +1324,13 @@ def to_osc(self) -> OscMessage: ] for key, value in sorted((self.controls or {}).items()): contents.append(key if isinstance(key, str) else int(key)) - if isinstance(value, str): + if isinstance(value, tuple): + if len(value) == 1: + contents.append(value[0]) + else: + contents.append(value) + elif isinstance(value, (float, str)): contents.append(value) - elif isinstance(value, tuple): - contents.append(tuple((float(v) for v in value))) - else: - contents.append(float(value)) return OscMessage(RequestName.SYNTH_NEW, *contents) @@ -1642,14 +1642,16 @@ class ReceiveSynthDefs(Request): ... ), ... ) >>> request.to_osc() - OscMessage('/d_recv', b'SCgf\x00\x00\x00\x02\x00\x01\x07default\x00\x00\x00\x0c\x00\x00\x00\x00>\x99\x99\x9a<#\xd7\n?333@\x00\x00\x00\xbe\xcc\xcc\xcd>\xcc\xcc\xcdEz\x00\x00E\x9c@\x00E\x1c@\x00EH\x00\x00?\x80\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00=\xcc\xcc\xcdC\xdc\x00\x00?\x80\x00\x00?\x00\x00\x00\x00\x00\x00\x05\tamplitude\x00\x00\x00\x01\tfrequency\x00\x00\x00\x02\x04gate\x00\x00\x00\x03\x03out\x00\x00\x00\x00\x03pan\x00\x00\x00\x04\x00\x00\x00\x14\x07Control\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07Control\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x01\x01\x01\x01\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x05Linen\x01\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x03\xff\xff\xff\xff\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x04\x01\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x05\xff\xff\xff\xff\x00\x00\x00\x00\x00\x0cBinaryOpUGen\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x06\x00\x0cBinaryOpUGen\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Sum3\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\n\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x07\xff\xff\xff\xff\x00\x00\x00\x08\x00\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\t\xff\xff\xff\xff\x00\x00\x00\n\x00\x05XLine\x01\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x0b\xff\xff\xff\xff\x00\x00\x00\x00\x01\x03LPF\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x04Pan2\x02\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\xff\xff\xff\xff\x00\x00\x00\x0b\x02\x02\tOffsetOut\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x01\x00\x00', OscMessage('/s_new', 'default', 1000, 1, 1)) + OscMessage('/d_recv', b'SCgf\x00\x00\x00\x02\x00\x01\x07default\x00\x00\x00\x0c\x00\x00\x00\x00>\x99\x99\x9a<#\xd7\n?333@\x00\x00\x00\xbe\xcc\xcc\xcd>\xcc\xcc\xcdEz\x00\x00E\x9c@\x00E\x1c@\x00EH\x00\x00?\x80\x00\x00\x00\x00\x00\x05=\xcc\xcc\xcdC\xdc\x00\x00?\x80\x00\x00?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\tamplitude\x00\x00\x00\x00\tfrequency\x00\x00\x00\x01\x04gate\x00\x00\x00\x02\x03pan\x00\x00\x00\x03\x03out\x00\x00\x00\x04\x00\x00\x00\x14\x07Control\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\x01\x01\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x05Linen\x01\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x03\xff\xff\xff\xff\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x04\x01\x07Control\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x04\x00\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x05\xff\xff\xff\xff\x00\x00\x00\x00\x00\x0cBinaryOpUGen\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x06\x00\x0cBinaryOpUGen\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x01\x06VarSaw\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Sum3\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\n\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x07\xff\xff\xff\xff\x00\x00\x00\x08\x00\x04Rand\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\t\xff\xff\xff\xff\x00\x00\x00\n\x00\x05XLine\x01\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x0b\xff\xff\xff\xff\x00\x00\x00\x00\x01\x03LPF\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x04Pan2\x02\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xff\xff\xff\xff\x00\x00\x00\x0b\x02\x02\tOffsetOut\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x01\x00\x00', OscMessage('/s_new', 'default', 1000, 1, 1)) """ synthdefs: Sequence[SynthDef] on_completion: Optional[Requestable] = None def to_osc(self) -> OscMessage: - contents = [compile_synthdefs(self.synthdefs)] + contents: List[Union[OscBundle, OscMessage, bytes]] = [ + compile_synthdefs(*self.synthdefs) + ] if self.on_completion: contents.append(self.on_completion.to_osc()) return OscMessage(RequestName.SYNTHDEF_RECEIVE, *contents) diff --git a/supriya/enums.py b/supriya/enums.py index db58f01ae..962aa6c0c 100644 --- a/supriya/enums.py +++ b/supriya/enums.py @@ -3,7 +3,7 @@ """ from collections.abc import Sequence -from typing import cast +from typing import SupportsFloat, cast from uqbar.enums import IntEnumeration, StrictEnumeration @@ -128,10 +128,19 @@ def from_expr(cls, expr) -> "CalculationRate": if hasattr(expr, "calculation_rate"): return expr.calculation_rate - elif isinstance(expr, (int, float)) and not isinstance(expr, cls): + elif isinstance(expr, ParameterRate): + return { + ParameterRate.AUDIO: CalculationRate.AUDIO, + ParameterRate.CONTROL: CalculationRate.CONTROL, + ParameterRate.SCALAR: CalculationRate.SCALAR, + ParameterRate.TRIGGER: CalculationRate.CONTROL, + }[expr] + elif isinstance(expr, (int, float, SupportsFloat)) and not isinstance( + expr, cls + ): return cast(CalculationRate, CalculationRate.SCALAR) elif isinstance(expr, Parameter): - name = expr.parameter_rate.name + name = expr.rate.name if name == "TRIGGER": return cast(CalculationRate, CalculationRate.CONTROL) return CalculationRate.from_expr(name) diff --git a/supriya/ext/mypy.py b/supriya/ext/mypy.py index e2a1d276a..0c7d3d0ab 100644 --- a/supriya/ext/mypy.py +++ b/supriya/ext/mypy.py @@ -40,27 +40,28 @@ def transform(self) -> bool: info = self._ctx.cls.info SupportsIntType = api.named_type("typing.SupportsInt") - UGenOperableType = api.named_type( - "supriya.ugens.core.UGenOperable", - ) + CalculationRateType = api.named_type("supriya.enums.CalculationRate") + UGenOperableType = api.named_type("supriya.ugens.core.UGenOperable") + UGenScalarType = api.named_type("supriya.ugens.core.UGenScalar") + UGenVectorType = api.named_type("supriya.ugens.core.UGenVector") # api.named_type() breaks for these... why? - UGenInitScalarParamTypeSym = api.lookup_fully_qualified( - "supriya.ugens.core.UGenInitScalarParam" + UGenRecursiveInputTypeSym = api.lookup_fully_qualified( + "supriya.ugens.core.UGenRecursiveInput" ) - UGenInitVectorParamTypeSym = api.lookup_fully_qualified( - "supriya.ugens.core.UGenInitVectorParam" + UGenScalarInputTypeSym = api.lookup_fully_qualified( + "supriya.ugens.core.UGenScalarInput" ) - UGenRateVectorParamTypeSym = api.lookup_fully_qualified( - "supriya.ugens.core.UGenRateVectorParam" + UGenVectorInputTypeSym = api.lookup_fully_qualified( + "supriya.ugens.core.UGenVectorInput" ) - assert UGenInitScalarParamTypeSym.node is not None - assert UGenInitVectorParamTypeSym.node is not None - assert UGenRateVectorParamTypeSym.node is not None + assert UGenRecursiveInputTypeSym.node is not None + assert UGenScalarInputTypeSym.node is not None + assert UGenVectorInputTypeSym.node is not None - UGenInitScalarParamType = getattr(UGenInitScalarParamTypeSym.node, "target") - UGenInitVectorParamType = getattr(UGenInitVectorParamTypeSym.node, "target") - UGenRateVectorParamType = getattr(UGenRateVectorParamTypeSym.node, "target") + UGenRecursiveInputType = getattr(UGenRecursiveInputTypeSym.node, "target") + UGenScalarInputType = getattr(UGenScalarInputTypeSym.node, "target") + UGenVectorInputType = getattr(UGenVectorInputTypeSym.node, "target") decorator_arguments = { "ar": _get_decorator_bool_argument(self._ctx, "ar", False), @@ -75,12 +76,23 @@ def transform(self) -> bool: self._ctx, "fixed_channel_count", False ), } - init_args = [] + init_args = [ + Argument( + variable=Var("calculation_rate", CalculationRateType), + type_annotation=CalculationRateType, + initializer=None, + kind=ARG_OPT, + ), + Argument( + variable=Var("special_index", SupportsIntType), + type_annotation=SupportsIntType, + initializer=None, + kind=ARG_OPT, + ), + ] rate_args = [] for name, unexpanded in self.collect_params(): - init_type = ( - UGenInitVectorParamType if unexpanded else UGenInitScalarParamType - ) + init_type = UGenVectorInputType if unexpanded else UGenScalarInputType init_args.append( Argument( variable=Var(name, init_type), @@ -91,8 +103,8 @@ def transform(self) -> bool: ) rate_args.append( Argument( - variable=Var(name, UGenRateVectorParamType), - type_annotation=UGenRateVectorParamType, + variable=Var(name, UGenRecursiveInputType), + type_annotation=UGenRecursiveInputType, initializer=None, kind=ARG_OPT, ) @@ -101,7 +113,7 @@ def transform(self) -> bool: api=api, cls=cls, name=name, - typ=UGenInitVectorParamType if unexpanded else UGenInitScalarParamType, + typ=UGenVectorType if unexpanded else UGenScalarType, override_allow_incompatible=True, ) if ( @@ -135,17 +147,17 @@ def transform(self) -> bool: args=init_args, return_type=NoneType(), ) - return False + return True -def _ugen_hook(ctx: ClassDefContext) -> None: - UGenTransformer(ctx).transform() +def _ugen_hook(ctx: ClassDefContext) -> bool: + return UGenTransformer(ctx).transform() class SupriyaPlugin(Plugin): - def get_class_decorator_hook( + def get_class_decorator_hook_2( self, fullname: str - ) -> Optional[Callable[[ClassDefContext], None]]: + ) -> Optional[Callable[[ClassDefContext], bool]]: if fullname == "supriya.ugens.core.ugen": return _ugen_hook return None diff --git a/supriya/patterns/structure.py b/supriya/patterns/structure.py index 6fd57451a..e741bd2ee 100644 --- a/supriya/patterns/structure.py +++ b/supriya/patterns/structure.py @@ -56,7 +56,7 @@ def _adjust(self, expr: Event, state: Optional[UUIDDict] = None) -> Event: updates["target_node"] = state["group"] if hasattr(expr, "synthdef"): synthdef = getattr(expr, "synthdef") or synthdefs.default - parameter_names = synthdef.parameter_names + parameter_names = synthdef.parameters for name in ("in_", "out"): if name in parameter_names and kwargs.get(name) is None: updates[name] = state["bus"] @@ -296,7 +296,7 @@ def _adjust(self, expr: Event, state: Optional[UUIDDict] = None) -> Event: updates["target_node"] = expr.target_node or self._target_node if self._target_bus is not None and hasattr(expr, "synthdef"): synthdef = getattr(expr, "synthdef") or synthdefs.default - parameter_names = synthdef.parameter_names + parameter_names = synthdef.parameters for name in ("in_", "out"): if name in parameter_names and kwargs.get(name) is None: updates[name] = self._target_bus diff --git a/supriya/ugens/basic.py b/supriya/ugens/basic.py index cede9e769..256466ccc 100644 --- a/supriya/ugens/basic.py +++ b/supriya/ugens/basic.py @@ -1,7 +1,8 @@ -from typing import Any, Dict, Tuple +from typing import Any, Dict, Sequence, Tuple from .. import utils from ..enums import CalculationRate +from ..utils import flatten from .core import PseudoUGen, UGen, UGenVector, param, ugen @@ -124,21 +125,13 @@ class Mix(PseudoUGen): ### PRIVATE METHODS ### - @classmethod - def _flatten_sources(cls, sources): - flattened_sources = [] - for source in sources: - if isinstance(source, UGenVector): - flattened_sources.extend(source) - else: - flattened_sources.append(source) - return UGenVector(*flattened_sources) - ### PUBLIC METHODS ### @classmethod def new(cls, sources): - sources = cls._flatten_sources(sources) + if not isinstance(sources, Sequence): + sources = [sources] + sources = list(flatten(sources)) summed_sources = [] for part in utils.group_by_count(sources, 4): if len(part) == 4: @@ -311,7 +304,7 @@ def multichannel(cls, sources, channel_count): bus: 0.0 source[0]: Sum3.ar[0] """ - sources = cls._flatten_sources(sources) + sources = list(flatten(sources)) mixes, parts = [], [] for i in range(0, len(sources), channel_count): parts.append(sources[i : i + channel_count]) @@ -334,7 +327,7 @@ class MulAdd(UGen): ... source=source, ... ) >>> mul_add - + """ ### CLASS VARIABLES ### @@ -347,7 +340,12 @@ class MulAdd(UGen): @classmethod def _new_single( - cls, addend=None, multiplier=None, calculation_rate=None, source=None + cls, + addend=None, + multiplier=None, + calculation_rate=None, + source=None, + special_index=None, ): def _inputs_are_valid(source, multiplier, addend): if CalculationRate.from_expr(source) == CalculationRate.AUDIO: @@ -415,7 +413,7 @@ class Sum3(UGen): ... input_two=input_two, ... input_three=input_three, ... ) - + """ input_one = param() @@ -473,7 +471,7 @@ class Sum4(UGen): ... input_three=input_three, ... input_four=input_four, ... ) - + """ input_one = param() diff --git a/supriya/ugens/bufio.py b/supriya/ugens/bufio.py index 2411b0fa3..693894d02 100644 --- a/supriya/ugens/bufio.py +++ b/supriya/ugens/bufio.py @@ -25,7 +25,7 @@ class BufRd(UGen): ... phase=phase, ... ) >>> buf_rd - , ])> + """ buffer_id = param() @@ -148,26 +148,6 @@ class MaxLocalBufs(UGen): maximum = param(0) - def increment(self): - """ - Increments maximum local buffer count. - - :: - - >>> max_local_bufs = supriya.ugens.MaxLocalBufs.ir(maximum=1).source - >>> max_local_bufs.inputs - (1.0,) - - :: - - >>> max_local_bufs.increment() - >>> max_local_bufs.inputs - (2.0,) - - Returns none. - """ - self._inputs[0] += 1 - @ugen(ar=True, kr=True, is_multichannel=True) class PlayBuf(UGen): @@ -187,7 +167,7 @@ class PlayBuf(UGen): ... trigger=1, ... ) >>> play_buf - , ])> + """ buffer_id = param() diff --git a/supriya/ugens/core.py b/supriya/ugens/core.py index b6358f75e..6fbd8412c 100644 --- a/supriya/ugens/core.py +++ b/supriya/ugens/core.py @@ -1,44 +1,47 @@ import abc -import collections import copy -import dataclasses import hashlib import inspect -import pathlib +import math +import operator import struct import subprocess import tempfile import threading import uuid from enum import Enum +from pathlib import Path +from types import MappingProxyType from typing import ( - Any, Callable, Dict, + FrozenSet, Iterable, List, + Literal, + Mapping, NamedTuple, Optional, Protocol, Sequence, SupportsFloat, + SupportsInt, Tuple, Type, Union, cast, + overload, + runtime_checkable, ) -from ..typing import CalculationRateLike, Default, Missing - try: from typing import TypeAlias except ImportError: from typing_extensions import TypeAlias # noqa -import uqbar.graphs -from uqbar.objects import new +from uqbar.graphs import Edge, Graph, Node, RecordField, RecordGroup -from .. import sclang, utils +from .. import sclang from ..enums import ( BinaryOperator, CalculationRate, @@ -47,15 +50,25 @@ SignalRange, UnaryOperator, ) +from ..typing import CalculationRateLike, Default, Missing, ParameterRateLike +from ..utils import flatten, iterate_nwise class Check(Enum): + """ + A UGen input rate check configuration. + """ + NONE = 0 SAME_AS_FIRST = 1 SAME_OR_SLOWER = 2 class Param(NamedTuple): + """ + A UGen input configuration. + """ + default: Optional[Union[Default, Missing, float]] = None check: Check = Check.NONE unexpanded: bool = False @@ -85,7 +98,7 @@ def _add_init( ) for key, param in params.items(): value_repr = _format_value(param.default) - type_ = "UGenInitVectorParam" if param.unexpanded else "UGenInitScalarParam" + type_ = "UGenVectorInput" if param.unexpanded else "UGenScalarInput" prefix = f"{key}: {type_}" args.append( f"{prefix} = {value_repr}" @@ -112,14 +125,14 @@ def _add_param_fn(cls, name: str, index: int, unexpanded: bool) -> None: name=name, args=["self"], body=( - [f"return UGenVector(*self._inputs[{index}:])"] + [f"return self._inputs[{index}:]"] if unexpanded - else [f"return UGenVector(*self._inputs[{index}:{index} + 1])"] + else [f"return self._inputs[{index}]"] ), decorator=property, globals_=_get_fn_globals(), override=True, - return_type=UGenVector, + return_type=UGenVector if unexpanded else UGenScalar, ) @@ -136,15 +149,14 @@ def _add_rate_fn( args.append("*") for key, param in params.items(): value_repr = _format_value(param.default) - prefix = f"{key}: UGenRateVectorParam" + prefix = f"{key}: UGenRecursiveInput" args.append( f"{prefix} = {value_repr}" if not isinstance(param.default, Missing) else prefix ) body = ["return cls._new_expanded("] - if rate is not None: - body.append(f" calculation_rate={rate!r},") + body.append(f" calculation_rate={rate!r},") if is_multichannel and not fixed_channel_count: args.append(f"channel_count: int = {channel_count or 1}") body.append(" channel_count=channel_count,") @@ -157,7 +169,7 @@ def _add_rate_fn( body=body, decorator=classmethod, globals_=_get_fn_globals(), - return_type=cls, + return_type=UGenOperable, ) @@ -211,15 +223,13 @@ def _get_fn_globals(): "Default": Default, "DoneAction": DoneAction, "Missing": Missing, - "OutputProxy": OutputProxy, - "Sequence": Sequence, "SupportsFloat": SupportsFloat, - "UGenVector": UGenVector, - "UGenInitScalarParam": UGenInitScalarParam, - "UGenInitVectorParam": UGenInitVectorParam, - "UGenOperable": UGenOperable, - "UGenRateVectorParam": UGenRateVectorParam, + "UGenRecursiveInput": UGenRecursiveInput, + "UGenScalar": UGenScalar, "UGenSerializable": UGenSerializable, + "UGenVector": UGenVector, + "UGenScalarInput": UGenScalarInput, + "UGenVectorInput": UGenVectorInput, "Union": Union, } @@ -243,14 +253,14 @@ def _process_class( signal_range: Optional[int] = None, ) -> Type["UGen"]: params: Dict[str, Param] = {} - unexpanded_input_names = [] + unexpanded_keys = [] valid_calculation_rates = [] for name, value in cls.__dict__.items(): if not isinstance(value, Param): continue params[name] = value if value.unexpanded: - unexpanded_input_names.append(name) + unexpanded_keys.append(name) _add_param_fn(cls, name, len(params) - 1, value.unexpanded) _add_init(cls, params, is_multichannel, channel_count, fixed_channel_count) for should_add, rate in [ @@ -272,8 +282,8 @@ def _process_class( cls._is_output = bool(is_output) cls._is_pure = bool(is_pure) cls._is_width_first = bool(is_width_first) - cls._ordered_input_names = {key: param.default for key, param in params.items()} - cls._unexpanded_input_names = tuple(unexpanded_input_names) + cls._ordered_keys = tuple(params.keys()) + cls._unexpanded_keys = frozenset(unexpanded_keys) cls._valid_calculation_rates = tuple(valid_calculation_rates) if signal_range is not None: cls._signal_range = SignalRange.from_expr(signal_range) @@ -315,6 +325,8 @@ def ugen( Decorate a UGen class. Akin to dataclasses.dataclass. + + Collects parameter descriptors and generates initializer and rate class methods. """ def wrap(cls: Type[UGen]) -> Type[UGen]: @@ -341,3692 +353,4802 @@ def wrap(cls: Type[UGen]) -> Type[UGen]: return wrap -class UGenOperable: - - ### SPECIAL METHODS ### - - def __abs__(self) -> "UGenOperable": - """ - Gets absolute value of ugen graph. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = abs(ugen_graph) - >>> result - - - :: +def _get_method_for_rate(cls, calculation_rate: CalculationRate) -> Callable: + if calculation_rate == CalculationRate.AUDIO: + return cls.ar + elif calculation_rate == CalculationRate.CONTROL: + return cls.kr + elif calculation_rate == CalculationRate.SCALAR: + if hasattr(cls, "ir"): + return cls.ir + return cls.kr + return cls.new + + +def _compute_binary_op( + left: "UGenRecursiveInput", + right: "UGenRecursiveInput", + special_index: BinaryOperator, + float_operator: Optional[Callable] = None, +) -> "UGenOperable": + def recurse( + all_expanded_params: UGenRecursiveParams, + ) -> "UGenOperable": + if not isinstance(all_expanded_params, dict) and len(all_expanded_params) == 1: + all_expanded_params = all_expanded_params[0] + if isinstance(all_expanded_params, dict): + if ( + isinstance(left, SupportsFloat) + and isinstance(right, SupportsFloat) + and float_operator is not None + ): + return ConstantProxy(float_operator(float(left), float(right))) + return BinaryOpUGen._new_single( + calculation_rate=max( + [ + CalculationRate.from_expr(left), + CalculationRate.from_expr(right), + ] + ), + special_index=special_index, + **all_expanded_params, + ) + return UGenVector( + *(recurse(expanded_params) for expanded_params in all_expanded_params) + ) - >>> supriya.graph(result) # doctest: +SKIP + return recurse(UGen._expand_params({"left": left, "right": right})) - :: - >>> print(result) - synthdef: - name: f21696d155a2686700992f0e9a04a79c - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(ABSOLUTE_VALUE).ar: - source: WhiteNoise.ar[0] +def _compute_unary_op( + source: "UGenRecursiveInput", + special_index: UnaryOperator, + float_operator: Optional[Callable] = None, +) -> "UGenOperable": + def recurse( + all_expanded_params: UGenRecursiveParams, + ) -> "UGenOperable": + if not isinstance(all_expanded_params, dict) and len(all_expanded_params) == 1: + all_expanded_params = all_expanded_params[0] + if isinstance(all_expanded_params, dict): + if isinstance(source, SupportsFloat) and float_operator is not None: + return ConstantProxy(float_operator(float(source))) + return UnaryOpUGen._new_single( + calculation_rate=max( + [ + CalculationRate.from_expr(source), + ] + ), + special_index=special_index, + **all_expanded_params, + ) + return UGenVector( + *(recurse(expanded_params) for expanded_params in all_expanded_params) + ) - .. container:: example + return recurse(UGen._expand_params({"source": source})) - **Example 2:** - :: +def _compute_ugen_map( + source: "UGenRecursiveInput", ugen: Type["UGen"], **kwargs: "UGenRecursiveInput" +) -> "UGenOperable": + if isinstance(source, UGenSerializable): + source = source.serialize() + if not isinstance(source, Sequence): + source = UGenVector(source) + outputs: List[UGenOperable] = [] + for input_ in source: + calculation_rate = CalculationRate.from_expr(input_) + method = _get_method_for_rate(ugen, calculation_rate) + outputs.append(method(source=input_, **kwargs)) + if len(outputs) == 1: + return outputs[0] + return UGenVector(*outputs) - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=(440, 442, 443), - ... ) - >>> result = abs(ugen_graph) - >>> result - , , ])> - :: +class UGenOperable: + """ + Mixin for UGen arithmetic operations. + """ - >>> supriya.graph(result) # doctest: +SKIP + def __abs__(self) -> "UGenOperable": + """ + Compute absolute value of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 1d45df2f3d33d1b0641d2c464498f6c4 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - UnaryOpUGen(ABSOLUTE_VALUE).ar/0: - source: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - UnaryOpUGen(ABSOLUTE_VALUE).ar/1: - source: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - UnaryOpUGen(ABSOLUTE_VALUE).ar/2: - source: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = abs(ugen_graph) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(ABSOLUTE_VALUE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(ABSOLUTE_VALUE).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_unary_op(self, UnaryOperator.ABSOLUTE_VALUE) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.ABSOLUTE_VALUE, + float_operator=operator.abs, + ) - def __add__(self, expr: "UGenOperand") -> "UGenOperable": + def __add__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Adds `expr` to ugen graph. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph + expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + Add ``expr`` to UGen graph. - >>> print(result) - synthdef: - name: 6bf4339326d015532b7604cd7af9ad3b - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] - - .. container:: example - - **Example 2:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph + expr - >>> result - , , ])> - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: f4a3c1ed35cc5f6fe66b70a3bc520b10 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] - - .. container:: example - - **Example 3:** - - :: - - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph + expr - >>> result - + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph + expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(ADDITION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ADDITION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> supriya.graph(result) # doctest: +SKIP + Supports short-circuiting: - :: + :: - >>> print(result) - synthdef: - name: f79088cc154ef2b65c72a0f8de8336ce - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(ADDITION).ar: - left: Dust.ar[0] - right: 4.0 + >>> result = ugen_graph + 0 + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.ADDITION) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.ADDITION, + float_operator=operator.add, + ) - def __and__(self, expr: "UGenOperand") -> "UGenOperable": + def __and__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Computes the bitwise AND of the UGen graph and `expr`. + Compute bitwise AND of UGen graph and ``expr``. - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph & expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph & expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(BITWISE_AND).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_AND).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: 9a5b4d1212b6b7fe299c21a8b1e401cc - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(BITWISE_AND).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.BITWISE_AND) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.BITWISE_AND, + float_operator=operator.and_, + ) def __ceil__(self) -> "UGenOperable": """ - Calculates the ceiling of ugen graph. + Calculate ceiling of ugen graph. :: >>> import math - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = math.ceil(source) - >>> print(operation) + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = math.ceil(ugen_graph) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: c7b1855219f3364f731bdd2e4599b1d1 + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(CEILING).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(CEILING).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(CEILING).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.CEILING) + return _compute_unary_op( + source=self, special_index=UnaryOperator.CEILING, float_operator=math.ceil + ) - def __div__(self, expr: "UGenOperand") -> "UGenOperable": + def __floor__(self) -> "UGenOperable": """ - Divides ugen graph by `expr`. + Calculate floor of ugen graph. - .. container:: example + :: - **Example 1:** + >>> import math + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = math.floor(ugen_graph) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(FLOOR).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(FLOOR).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, special_index=UnaryOperator.FLOOR, float_operator=math.floor + ) - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph / expr - >>> result - + def __floordiv__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute integer division of UGen graph by ``expr``. - :: + :: - >>> supriya.graph(result) # doctest: +SKIP + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph // expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(INTEGER_DIVISION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(INTEGER_DIVISION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.INTEGER_DIVISION, + float_operator=operator.floordiv, + ) - >>> print(result) - synthdef: - name: 6da024a346859242c441fe03326d2adc - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + def __ge__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Test if UGen graph is greater than or equal to ``expr``. - .. container:: example + :: - **Example 2:** + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph >= expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.GREATER_THAN_OR_EQUAL, + float_operator=operator.ge, + ) - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph / expr - >>> result - , , ])> + def __graph__(self) -> Graph: + """ + Generate Graphviz graph of UGen graph. + """ + return self.__synthdef__().__graph__() - :: + def __gt__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Test if UGen graph is greater than ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph > expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(GREATER_THAN).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(GREATER_THAN).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: be20d589dfccb721f56da8b002d86763 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.GREATER_THAN, + float_operator=operator.gt, + ) - .. container:: example + def __invert__(self) -> "UGenOperable": + """ + Compute bitwise inversion of UGen graph. - **Example 3:** + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ~ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(BIT_NOT).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(BIT_NOT).ar/1: + source: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph / expr - >>> result - + """ - :: + return _compute_unary_op( + source=self, + special_index=UnaryOperator.BIT_NOT, + float_operator=operator.not_, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __le__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Test if UGen graph is less than or equal to ``expr``. - :: + :: - >>> print(result) - synthdef: - name: 672765c596fcaa083186b2f2b996ba1d - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(FLOAT_DIVISION).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph <= expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - self, expr, BinaryOperator.FLOAT_DIVISION + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.LESS_THAN_OR_EQUAL, + float_operator=operator.le, ) - def __floor__(self) -> "UGenOperable": + def __lshift__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates the floor of ugen graph. + Bitshift UGen graph to the left by ``expr``. :: - >>> import math - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = math.floor(source) - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph << expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: 407228cfdb74bdd79b51c425fb8a7f77 + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(FLOOR).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SHIFT_LEFT).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_LEFT).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.FLOOR) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SHIFT_LEFT, + float_operator=operator.lshift, + ) - def __graph__(self): + def __lt__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Gets Graphviz representation of ugen graph. + Test if UGen graph is less than ``expr``. - Returns GraphvizGraph instance. - """ - synthdef = self._clone() - result = synthdef.__graph__() - return result - - def __ge__(self, expr: "UGenOperand") -> "UGenOperable": - """ - Tests if ugen graph if greater than or equal to `expr`. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph >= expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 9db96233abf1f610d027ff285691482d - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + :: - .. container:: example + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph < expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(LESS_THAN).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(LESS_THAN).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - **Example 2:** + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.LESS_THAN, + float_operator=operator.lt, + ) - :: + def __mod__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute modulo of UGen graph and ``expr``: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph >= expr - >>> result - , , ])> + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph % expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MODULO).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(MODULO).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> supriya.graph(result) # doctest: +SKIP + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.MODULO, + float_operator=operator.mod, + ) - :: + def __mul__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Multiply UGen graph and ``expr``. - >>> print(result) - synthdef: - name: 6d43342b3787aa11a46cea54412407e1 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + :: - .. container:: example + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph * expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MULTIPLICATION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(MULTIPLICATION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - **Example 3:** + Supports short-circuiting: - :: + :: - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph >= expr - >>> result - + >>> ugen_graph * 0 + , <0.0>])> - :: + :: - >>> supriya.graph(result) # doctest: +SKIP + >>> result = ugen_graph * 1 + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 - :: + :: - >>> print(result) - synthdef: - name: b06931195bab8e6f6ca2e3a857e71a95 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(GREATER_THAN_OR_EQUAL).ar: - left: Dust.ar[0] - right: 4.0 + >>> result = ugen_graph * -1 + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - self, expr, BinaryOperator.GREATER_THAN_OR_EQUAL + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.MULTIPLICATION, + float_operator=operator.mul, ) - def __gt__(self, expr: "UGenOperand") -> "UGenOperable": + def __neg__(self) -> "UGenOperable": """ - Tests if ugen graph if greater than `expr`. - - .. container:: example - - **Example 1:** + Compute negative of UGen graph. - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph > expr - >>> result - + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = -ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.NEGATIVE, + float_operator=operator.neg, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __or__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute bitwise OR of UGen graph and ``expr``. - :: + :: - >>> print(result) - synthdef: - name: 01bebf935112af62ffdd282a99581904 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph | expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(BITWISE_OR).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_OR).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - .. container:: example + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.BITWISE_OR, + float_operator=operator.or_, + ) - **Example 2:** + def __pow__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Raise UGen graph to the power of ``expr``. - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph > expr - >>> result - , , ])> + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph ** expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(POWER).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(POWER).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.POWER, + float_operator=operator.pow, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __radd__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Add UGen graph to ``expr`` (reflected). - :: + :: - >>> print(result) - synthdef: - name: 55642179864ad927e9d5cf6358367677 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(GREATER_THAN).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr + ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(ADDITION).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(ADDITION).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ADDITION).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - .. container:: example + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.ADDITION, + float_operator=operator.add, + ) - **Example 3:** + def __rand__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute reflected bitwise AND of ``expr`` and UGen graph (reflected). - :: + :: - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph > expr - >>> result - + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr & ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_AND).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(BITWISE_AND).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_AND).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - :: + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.BITWISE_AND, + float_operator=operator.and_, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __rfloordiv__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute integer division of ``expr`` by UGen graph (reflected). - :: + :: - >>> print(result) - synthdef: - name: 5177e03443ad31ee2664aae2201fb979 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(GREATER_THAN).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr // ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(INTEGER_DIVISION).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(INTEGER_DIVISION).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(INTEGER_DIVISION).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.GREATER_THAN) + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.INTEGER_DIVISION, + float_operator=operator.floordiv, + ) - def __le__(self, expr: "UGenOperand") -> "UGenOperable": + def __rmod__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Tests if ugen graph if less than or equal to `expr`. - - .. container:: example - - **Example 1:** + Compute reflected modulo of ``expr`` and UGen graph (reflected). - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph <= expr - >>> result - + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr % ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(MODULO).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(MODULO).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(MODULO).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - :: + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.MODULO, + float_operator=operator.mod, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __rmul__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Multiple ``expr`` by UGen graph (reflected). - :: + :: - >>> print(result) - synthdef: - name: fefc06cbbc3babb35046306c6d41e3c5 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr - ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(SUBTRACTION).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(SUBTRACTION).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SUBTRACTION).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - .. container:: example + Supports short-circuiting: - **Example 2:** + :: - :: + >>> result = 0 - ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/1: + source: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph <= expr - >>> result - , , ])> + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.MULTIPLICATION, + float_operator=operator.mul, + ) - :: + def __ror__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute bitwise OR of ``expr`` and UGen graph (reflected). - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr | ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_OR).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(BITWISE_OR).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_OR).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - >>> print(result) - synthdef: - name: 53f29d793fd676fbca1d541e938b66ca - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.BITWISE_OR, + float_operator=operator.or_, + ) - .. container:: example + def __rpow__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Raise ``expr`` to the power of UGen graph (reflected). - **Example 3:** + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr ** ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(POWER).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(POWER).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(POWER).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph <= expr - >>> result - + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.POWER, + float_operator=operator.pow, + ) - :: + def __rlshift__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Bitshift ``expr`` to the left by UGen graph (reflected). - >>> supriya.graph(result) # doctest: +SKIP + :: - :: - - >>> print(result) - synthdef: - name: 3cf0414af96d130edf2e1b839f73036c - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(LESS_THAN_OR_EQUAL).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr << ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_LEFT).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(SHIFT_LEFT).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_LEFT).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - self, expr, BinaryOperator.LESS_THAN_OR_EQUAL + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.SHIFT_LEFT, + float_operator=operator.lshift, ) - def __len__(self) -> int: - raise NotImplementedError - - def __lt__(self, expr: "UGenOperand") -> "UGenOperable": + def __rrshift__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Tests if ugen graph if less than `expr`. + Bitshift ``expr`` to the right by UGen graph (reflected). - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph < expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 844f34c0ffb28ecc24bd5cf0bae20b43 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + :: - .. container:: example + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr >> ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_RIGHT).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(SHIFT_RIGHT).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_RIGHT).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - **Example 2:** + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.SHIFT_RIGHT, + float_operator=operator.rshift, + ) - :: + def __rshift__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Bitshift UGen graph to the right by ``expr``. - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph < expr - >>> result - , , ])> + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph >> expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SHIFT_RIGHT).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SHIFT_RIGHT).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> supriya.graph(result) # doctest: +SKIP + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SHIFT_RIGHT, + float_operator=operator.rshift, + ) - :: + def __rsub__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Subtract UGen graph from ``expr`` (reflected). - >>> print(result) - synthdef: - name: 14c1494fe4e153e690a8ef0a42e5834f - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(LESS_THAN).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + :: - .. container:: example + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr - ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(SUBTRACTION).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(SUBTRACTION).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SUBTRACTION).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - **Example 3:** + Supports short-circuiting: - :: + :: - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph < expr - >>> result - + >>> result = 0 - ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(NEGATIVE).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.SUBTRACTION, + float_operator=operator.sub, + ) - >>> supriya.graph(result) # doctest: +SKIP + def __rtruediv__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute true division of ``expr`` by UGen graph (reflected). - :: + :: - >>> print(result) - synthdef: - name: e87d41791847aa80d8a3e56318e506e4 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(LESS_THAN).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr / ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(FLOAT_DIVISION).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(FLOAT_DIVISION).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(FLOAT_DIVISION).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.LESS_THAN) + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.FLOAT_DIVISION, + float_operator=operator.truediv, + ) - def __mod__(self, expr: "UGenOperand") -> "UGenOperable": + def __rxor__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Gets modulo of ugen graph and `expr`. + Compute bitwise XOR of ``expr`` and UGen graph (reflected). - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph % expr - >>> result - + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = [1, 2, 3] + >>> result = expr ^ ugen_graph + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_XOR).ar/0: + left: 1.0 + right: SinOsc.ar/0[0] + - BinaryOpUGen(BITWISE_XOR).ar/1: + left: 3.0 + right: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_XOR).ar/2: + left: 2.0 + right: SinOsc.ar/1[0] - >>> supriya.graph(result) # doctest: +SKIP + """ + return _compute_binary_op( + left=expr, + right=self, + special_index=BinaryOperator.BITWISE_XOR, + float_operator=operator.xor, + ) - :: + def __str__(self) -> str: + return str(self.__synthdef__()) - >>> print(result) - synthdef: - name: e4a06e157474f8d1ae213916f3cf585a - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MODULO).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + def __sub__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Subtract ``expr`` from UGen graph. - .. container:: example + :: - **Example 2:** + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph - expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SUBTRACTION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SUBTRACTION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + Supports short-circuiting: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph % expr - >>> result - , , ])> + :: - :: + >>> result = ugen_graph - 0 + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 - >>> supriya.graph(result) # doctest: +SKIP + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SUBTRACTION, + float_operator=operator.sub, + ) - :: + def __synthdef__(self) -> "SynthDef": + """ + Generate a SynthDef from UGen graph. - >>> print(result) - synthdef: - name: 90badce1cf8fc1752b5eb99b29122a14 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MODULO).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(MODULO).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(MODULO).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + Typically used for rendering debug information about the UGen graph, or + for generating Graphviz graphs. + """ - .. container:: example + def recurse(operable) -> None: + if isinstance(operable, UGenVector): + for x in operable: + recurse(x) + elif isinstance(operable, OutputProxy): + recurse(operable.ugen) + elif isinstance(operable, UGen): + if operable in ugens: + return + ugens.append(operable) + for input_ in operable.inputs: + recurse(input_) - **Example 3:** + builder = SynthDefBuilder() + ugens: List[UGen] = [] + recurse(copy.deepcopy(self)) + for ugen in ugens: + ugen._uuid = builder._uuid + builder._add_ugen(ugen) + return builder.build(optimize=False) - :: + def __truediv__(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute true division of UGen graph by ``expr``. - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph % expr - >>> result - + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph / expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(FLOAT_DIVISION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(FLOAT_DIVISION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> supriya.graph(result) # doctest: +SKIP + Supports short-circuiting: - :: + :: - >>> print(result) - synthdef: - name: bfa60877061daf112516cc3ec8c7ff69 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(MODULO).ar: - left: Dust.ar[0] - right: 4.0 + >>> result = ugen_graph / 1 + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.MODULO) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.FLOAT_DIVISION, + float_operator=operator.truediv, + ) - def __mul__(self, expr: "UGenOperand") -> "UGenOperable": + def __xor__(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Multiplies ugen graph by `expr`. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph * expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + Compute bitwise XOR of UGen graph and ``expr``. - >>> print(result) - synthdef: - name: ea2b5e5cec4e2d5a1bef0a8dda522bd3 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] - - .. container:: example - - **Example 2:** + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph ^ expr + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(BITWISE_XOR).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(BITWISE_XOR).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph * expr - >>> result - , , ])> + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.BITWISE_XOR, + float_operator=operator.xor, + ) - :: + def absdiff(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute absolute difference between UGen graph and ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.absdiff(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(ABSOLUTE_DIFFERENCE).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ABSOLUTE_DIFFERENCE).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: 9d353c198344b6be3635244197bc2a4b - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.ABSOLUTE_DIFFERENCE, + float_operator=lambda a, b: abs(a - b), + ) - .. container:: example + def am_clip(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute two quadrant multiplication between UGen graph and ``expr``. - **Example 3:** + - 0 when b <= 0 + - a * b when b > 0 - :: + :: - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph * expr - >>> result - + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.am_clip(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(AMCLIP).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(AMCLIP).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.AMCLIP, + ) - >>> supriya.graph(result) # doctest: +SKIP + def amplitude_to_db(self) -> "UGenOperable": + """ + Convert UGen graph from amplitude to decibels. - :: + :: - >>> print(result) - synthdef: - name: 1735acd4add428d8ab317d00236b0fe7 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(MULTIPLICATION).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.amplitude_to_db() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(AMPLITUDE_TO_DB).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(AMPLITUDE_TO_DB).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - self, expr, BinaryOperator.MULTIPLICATION + return _compute_unary_op( + source=self, special_index=UnaryOperator.AMPLITUDE_TO_DB ) - def __neg__(self) -> "UGenOperable": + def acos(self) -> "UGenOperable": """ - Negates ugen graph. + Compute the arccosine of UGen graph. - .. container:: example - - **Example 1:** + :: - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = -ugen_graph - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: a987a13f0593e4e4e070acffb11d5c3e - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(NEGATIVE).ar: - source: WhiteNoise.ar[0] - - .. container:: example - - **Example 2:** - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=(440, 442, 443), - ... ) - >>> result = -ugen_graph - >>> result - , , ])> + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.acos() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(ARCCOS).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(ARCCOS).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.ARCCOS, + float_operator=math.acos, + ) - >>> supriya.graph(result) # doctest: +SKIP + def asin(self) -> "UGenOperable": + """ + Compute the arcsine of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: e5dfc1d4ecb11ed8170aaf11469a6443 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - UnaryOpUGen(NEGATIVE).ar/0: - source: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - UnaryOpUGen(NEGATIVE).ar/1: - source: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - UnaryOpUGen(NEGATIVE).ar/2: - source: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.asin() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(ARCSIN).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(ARCSIN).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_unary_op(self, UnaryOperator.NEGATIVE) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.ARCSIN, + float_operator=math.asin, + ) - def __or__(self, expr: "UGenOperand") -> "UGenOperable": + def atan(self) -> "UGenOperable": """ - Computes the bitwise OR of the UGen graph and `expr`. + Compute the arctangent of UGen graph. - .. container:: example + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.atan() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(ARCTAN).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(ARCTAN).ar/1: + source: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph | expr - >>> result - + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.ARCTAN, + float_operator=math.atan, + ) - :: + def atan2(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the arctangen of UGen graph divided by ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.atan2(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(ATAN2).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ATAN2).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: 333e2e7362f86138866f3f2a160f77dd - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(BITWISE_OR).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.BITWISE_OR) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.ATAN2, + float_operator=math.atan2, + ) - def __pow__(self, expr: "UGenOperand") -> "UGenOperable": + def bi_lin_rand(self) -> "UGenOperable": """ - Raises ugen graph to the power of `expr`. - - .. container:: example + Compute a bilateral linearly distributed random number from - UGen graph to + UGen graph. - **Example 1:** - - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph ** expr - >>> result - + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.bi_lin_rand() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(BILINRAND).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(BILINRAND).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.BILINRAND, + ) - >>> supriya.graph(result) # doctest: +SKIP + def bi_rand(self) -> "UGenOperable": + """ + Compute a random number between - UGen graph and + UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 3498b370c0575fb2c2ed45143ba2da4f - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.bi_rand() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(RAND2).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(RAND2).ar/1: + source: SinOsc.ar/1[0] - .. container:: example + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.RAND2, + ) - **Example 2:** + def clip( + self, + minimum: "UGenRecursiveInput", + maximum: "UGenRecursiveInput", + ) -> "UGenOperable": + """ + Clip UGen graph between ``maximum`` and ``minimum``. - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph ** expr - >>> result - , , ])> + >>> from supriya.ugens import LFNoise1, SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.clip( + ... maximum=LFNoise1.kr(), + ... minimum=LFNoise1.kr(), + ... ) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - LFNoise1.kr/0: + frequency: 500.0 + - LFNoise1.kr/1: + frequency: 500.0 + - Clip.ar/0: + source: SinOsc.ar/0[0] + minimum: LFNoise1.kr/0[0] + maximum: LFNoise1.kr/1[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Clip.ar/1: + source: SinOsc.ar/1[0] + minimum: LFNoise1.kr/0[0] + maximum: LFNoise1.kr/1[0] - :: + """ + from . import Clip - >>> supriya.graph(result) # doctest: +SKIP + return _compute_ugen_map( + self, cast(Type[UGen], Clip), minimum=minimum, maximum=maximum + ) - :: + def clip2(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute bilateral clipping of UGen graph by ``expr``. - >>> print(result) - synthdef: - name: 04e78034682f9ffd6628fbfd09a28c13 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] + - clip a to +/- b - .. container:: example + :: - **Example 3:** + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.clip2(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(CLIP2).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(CLIP2).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: - - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph ** expr - >>> result - - - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.CLIP2, + ) - >>> supriya.graph(result) # doctest: +SKIP + def cos(self) -> "UGenOperable": + """ + Compute cosine of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 50b8e3b154bc85c98d76ced493a32731 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(POWER).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.cos() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(COS).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(COS).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.POWER) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.COS, + float_operator=math.cos, + ) - def __rpow__(self, expr: "UGenOperand") -> "UGenOperable": + def cosh(self) -> "UGenOperable": """ - Raises `expr` to the power of ugen graph. - - .. container:: example + Compute the hyperbolic cosine of UGen graph. - **Example 1:** + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.cosh() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(COSH).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(COSH).ar/1: + source: SinOsc.ar/1[0] - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr ** ugen_graph - >>> result - + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.COSH, + float_operator=math.cosh, + ) - :: + def cubed(self) -> "UGenOperable": + """ + Compute the cube of UGen graph. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.cubed() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(CUBED).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(CUBED).ar/1: + source: SinOsc.ar/1[0] - >>> print(result) - synthdef: - name: c450618c9e0fe5213629275da4e5e354 - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar: - left: 1.5 - right: SinOsc.ar[0] + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.CUBED, + float_operator=lambda x: x**3, + ) - .. container:: example + def db_to_amplitude(self) -> "UGenOperable": + """ + Convert UGen graph from decibels to amplitude. - **Example 2:** + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.db_to_amplitude() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(DB_TO_AMPLITUDE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(DB_TO_AMPLITUDE).ar/1: + source: SinOsc.ar/1[0] - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr ** ugen_graph - >>> result - , , ])> + """ + return _compute_unary_op( + source=self, special_index=UnaryOperator.DB_TO_AMPLITUDE + ) - :: + def difference_of_squares(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the difference of squares of UGen graph and ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + - (a * a) - (b * b) - :: + :: - >>> print(result) - synthdef: - name: a614dc68313ee7ca2677e63fd499de0d - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(POWER).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.difference_of_squares(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(DIFFERENCE_OF_SQUARES).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(DIFFERENCE_OF_SQUARES).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(expr, self, BinaryOperator.POWER) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.DIFFERENCE_OF_SQUARES, + float_operator=lambda a, b: (a * a) - (b * b), + ) - def __radd__(self, expr: "UGenOperand") -> "UGenOperable": + def digit_value(self) -> "UGenOperable": """ - Adds ugen graph to `expr`. + Compute the digit value of UGen graph. - .. container:: example + :: - **Example 1:** + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.digit_value() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(AS_INT).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(AS_INT).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.AS_INT, + float_operator=lambda x: divmod(x, 1.0)[0], + ) - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr + ugen_graph - >>> result - + def distort(self) -> "UGenOperable": + """ + Compute non-linear distortion of UGen graph. - :: + - x / (1 + abs(x)) - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.softclip() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SOFTCLIP).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SOFTCLIP).ar/1: + source: SinOsc.ar/1[0] - >>> print(result) - synthdef: - name: bb0592fad58b0bfa1a403c7ff6a400f3 - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar: - left: 1.5 - right: SinOsc.ar[0] + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.DISTORT, + float_operator=lambda x: x / (1 + abs(x)), + ) - .. container:: example + def exceeds(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Threshold UGen graph by ``expr``. - **Example 2:** + - 0 when a < b, otherwise a - :: + Equivalent to sclang's ``threshold`` method, but renamed due to name + conflicts with many UGen parameters. - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr + ugen_graph - >>> result - , , ])> + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.exceeds(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(THRESHOLD).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(THRESHOLD).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> supriya.graph(result) # doctest: +SKIP + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.THRESHOLD, + float_operator=lambda a, b: 0.0 if a < b else a, + ) - :: + def exponential_rand_range(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute an exponentially-distributed random number in the interval of UGen graph to ``expr``. - >>> print(result) - synthdef: - name: 0ad0a3d4b7ddf8bb56807813efc62202 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + :: - Returns ugen graph. - """ - return UGenOperable._compute_binary_op(expr, self, BinaryOperator.ADDITION) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.exponential_rand_range(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(EXPRANDRANGE).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(EXPRANDRANGE).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - def __rdiv__(self, expr: "UGenOperand") -> "UGenOperable": """ - Divides `expr` by ugen graph. - - .. container:: example - - **Example 1:** + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.EXPRANDRANGE, + ) - :: + def excess(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the residual of clipping UGen graph by ``expr`` - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr / ugen_graph - >>> result - + - a - clip2(a,b)) - :: + :: - >>> supriya.graph(result) # doctest: +SKIP + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.excess(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(EXCESS).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(EXCESS).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.EXCESS, + ) - >>> print(result) - synthdef: - name: d79490206a430281b186b188d617f679 - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar: - left: 1.5 - right: SinOsc.ar[0] + def exponential(self) -> "UGenOperable": + """ + Compute the exponential of UGen graph. - .. container:: example + :: - **Example 2:** + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.exponential() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(EXPONENTIAL).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(EXPONENTIAL).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.EXPONENTIAL) - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr / ugen_graph - >>> result - , , ])> + def fill(self, expr: "UGenRecursiveInput") -> "UGenOperable": + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.FILL, + ) - :: + def fold2(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute bilateral folding of UGen graph by ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + - fold a to +/- b - :: + :: - >>> print(result) - synthdef: - name: d71b3081490f800d5136c87f5fef46d1 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.fold2(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(FOLD2).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(FOLD2).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - expr, self, BinaryOperator.FLOAT_DIVISION + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.FOLD2, ) - def __rmod__(self, expr: "UGenOperand") -> "UGenOperable": + def fractional_part(self) -> "UGenOperable": """ - Gets modulo of `expr` and ugen graph. - - .. container:: example - - **Example 1:** + Compute the fractional part of UGen graph. - :: + :: - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr % ugen_graph - >>> result - + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.fractional_part() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(FRACTIONAL_PART).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(FRACTIONAL_PART).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.FRACTIONAL_PART, + float_operator=lambda x: divmod(x, 1.0)[1], + ) - >>> supriya.graph(result) # doctest: +SKIP + def gcd(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the greatest common divisor of UGen graph and ``expr``. - :: + :: - >>> print(result) - synthdef: - name: d79490206a430281b186b188d617f679 - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar: - left: 1.5 - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.gcd(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(GREATEST_COMMON_DIVISOR).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(GREATEST_COMMON_DIVISOR).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - .. container:: example + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.GREATEST_COMMON_DIVISOR, + float_operator=lambda a, b: math.gcd(a, b), + ) - **Example 2:** + def hanning_window(self) -> "UGenOperable": + """ + Compute Hanning window from UGen graph. - :: + :: - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr % ugen_graph - >>> result - , , ])> + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.hanning_window() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(HANNING_WINDOW).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(HANNING_WINDOW).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, special_index=UnaryOperator.HANNING_WINDOW + ) - >>> supriya.graph(result) # doctest: +SKIP + def hypot(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the hypotenuse of UGen graph and ``expr``. - :: + :: - >>> print(result) - synthdef: - name: d71b3081490f800d5136c87f5fef46d1 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(FLOAT_DIVISION).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.hypot(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(HYPOT).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(HYPOT).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - expr, self, BinaryOperator.FLOAT_DIVISION + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.HYPOT, + float_operator=math.hypot, ) - def __rmul__(self, expr: "UGenOperand") -> "UGenOperable": + def hypotx(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Multiplies `expr` by ugen graph. - - .. container:: example + Compute the hypotenuse approximation of UGen graph and ``expr``. - **Example 1:** + - abs(x) + abs(y) - ((sqrt(2) - 1) * min(abs(x), abs(y))) - :: + :: - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr * ugen_graph - >>> result - + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.hypotx(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(HYPOTX).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(HYPOTX).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.HYPOTX, + ) - >>> supriya.graph(result) # doctest: +SKIP + def hz_to_midi(self) -> "UGenOperable": + """ + Convert UGen graph from Hertz to MIDI note number. - :: + :: - >>> print(result) - synthdef: - name: f60bbe0480298a7ae8b54de5a4c0260f - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar: - left: 1.5 - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.hz_to_midi() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_MIDI).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_MIDI).ar/1: + source: SinOsc.ar/1[0] - .. container:: example + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.HZ_TO_MIDI) - **Example 2:** + def hz_to_octave(self) -> "UGenOperable": + """ + Convert UGen graph from Hertz to octave number. - :: + :: - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr * ugen_graph - >>> result - , , ])> + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.hz_to_octave() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_OCTAVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_OCTAVE).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.HZ_TO_OCTAVE) - >>> supriya.graph(result) # doctest: +SKIP + def is_equal_to(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute equality of UGen graph and ``expr``. - :: + :: - >>> print(result) - synthdef: - name: 0295153106bff55a2bf6db3b7184d301 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.is_equal_to(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(EQUAL).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(EQUAL).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op( - expr, self, BinaryOperator.MULTIPLICATION + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.EQUAL, + float_operator=lambda a, b: float(a == b), ) - def __rsub__(self, expr: "UGenOperand") -> "UGenOperable": + def is_not_equal_to(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Subtracts ugen graph from `expr`. + Compute inequality of UGen graph and ``expr``. - .. container:: example + :: - **Example 1:** + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.is_not_equal_to(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(NOT_EQUAL).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(NOT_EQUAL).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.NOT_EQUAL, + float_operator=lambda a, b: float(a != b), + ) - >>> expr = 1.5 - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> result = expr - ugen_graph - >>> result - + def lagged( + self, + lag_time_up: "UGenRecursiveInput" = 0.5, + lag_time_down: Optional["UGenRecursiveInput"] = None, + factor: Literal[1, 2, 3] = 1, + ) -> "UGenOperable": + """ + Lag UGen graph. - :: + If ``lag_time_down`` is non-null, use an "up/down" variant. - >>> supriya.graph(result) # doctest: +SKIP + If ``factor`` is 2 or 3, use the Lag2 or Lag3 variant. - :: + :: - >>> print(result) - synthdef: - name: 74e331121aa41f4d49a6d38a38ca4a9a - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar: - left: 1.5 - right: SinOsc.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.lagged(0.25) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Lag.ar/0: + source: SinOsc.ar/0[0] + lag_time: 0.25 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Lag.ar/1: + source: SinOsc.ar/1[0] + lag_time: 0.25 - .. container:: example + :: - **Example 2:** + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.lagged(lag_time_up=0.25, lag_time_down=1.5, factor=3) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Lag3UD.ar/0: + source: SinOsc.ar/0[0] + lag_time_up: 0.25 + lag_time_down: 1.5 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Lag3UD.ar/1: + source: SinOsc.ar/1[0] + lag_time_up: 0.25 + lag_time_down: 1.5 - :: + """ + from . import Lag, Lag2, Lag2UD, Lag3, Lag3UD, LagUD - >>> expr = [220, 330] - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = expr - ugen_graph - >>> result - , , ])> + if factor not in [1, 2, 3]: + raise ValueError(factor) - :: + if lag_time_down is None: + ugen: Type[UGen] = [Lag, Lag2, Lag3][factor - 1] + return _compute_ugen_map(self, cast(Type[UGen], ugen), lag_time=lag_time_up) + ugen = [LagUD, Lag2UD, Lag3UD][factor - 1] + return _compute_ugen_map( + self, + cast(Type[UGen], ugen), + lag_time_up=lag_time_up, + lag_time_down=lag_time_down, + ) - >>> supriya.graph(result) # doctest: +SKIP + def lin_rand(self) -> "UGenOperable": + """ + Compute a linearly-distributed random number between 0 and UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 1ca2e8f3f541b9365413a0dbf9028e95 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/0: - left: 220.0 - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/1: - left: 330.0 - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/2: - left: 220.0 - right: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.lin_rand() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(LINRAND).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(LINRAND).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(expr, self, BinaryOperator.SUBTRACTION) - - def __str__(self): - """ - Gets string representation of ugen graph. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> print(str(ugen_graph)) - synthdef: - name: c9b0ed62d4e0666b74166ff5ec09abe4 - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar(frequency=[1, 2, 3]) - >>> print(str(ugen_graph)) - synthdef: - name: 4015dac116b25c54b4a6f02bcb5859cb - ugens: - - SinOsc.ar/0: - frequency: 1.0 - phase: 0.0 - - SinOsc.ar/1: - frequency: 2.0 - phase: 0.0 - - SinOsc.ar/2: - frequency: 3.0 - phase: 0.0 - - Returns string. - """ - synthdef = self._clone() - result = str(synthdef) - return result + return _compute_unary_op( + source=self, + special_index=UnaryOperator.LINRAND, + ) - def __sub__(self, expr: "UGenOperand") -> "UGenOperable": + def lcm(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Subtracts `expr` from ugen graph. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph - expr - >>> result - - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: cd62fff8ff3ad7758d0f7ad82f39c7ce - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] - - .. container:: example - - **Example 2:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph - expr - >>> result - , , ])> - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 9a8355f84507908cadf3cc63187ddab4 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/0: - left: WhiteNoise.kr[0] - right: SinOsc.ar/0[0] - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/1: - left: WhiteNoise.kr[0] - right: SinOsc.ar/1[0] - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(SUBTRACTION).ar/2: - left: WhiteNoise.kr[0] - right: SinOsc.ar/2[0] - - .. container:: example - - **Example 3:** - - :: - - >>> ugen_graph = supriya.ugens.Dust.ar( - ... density=11.5, - ... ) - >>> expr = 4 - >>> result = ugen_graph - expr - >>> result - + Compute the least common multiple of UGen graph and ``expr``. - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: 48ca704043ed00a2b6a55fd4b6b72cf1 - ugens: - - Dust.ar: - density: 11.5 - - BinaryOpUGen(SUBTRACTION).ar: - left: Dust.ar[0] - right: 4.0 + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.lcm(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(LEAST_COMMON_MULTIPLE).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(LEAST_COMMON_MULTIPLE).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.SUBTRACTION) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.LEAST_COMMON_MULTIPLE, + float_operator=lambda a, b: math.lcm(a, b), + ) - def __xor__(self, expr: "UGenOperand") -> "UGenOperable": + def log(self) -> "UGenOperable": """ - Computes the bitwise XOR of the UGen graph and `expr`. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.kr() - >>> expr = supriya.ugens.SinOsc.ar() - >>> result = ugen_graph ^ expr - >>> result - - - :: + Compute the logarithm of UGen graph. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.log() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(LOG).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(LOG).ar/1: + source: SinOsc.ar/1[0] - >>> print(result) - synthdef: - name: 355f2c7fa510863b921bb8c28bc4a682 - ugens: - - WhiteNoise.kr: null - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - BinaryOpUGen(BITWISE_XOR).ar: - left: WhiteNoise.kr[0] - right: SinOsc.ar[0] """ - return UGenOperable._compute_binary_op(self, expr, BinaryOperator.BITWISE_XOR) - - __truediv__ = __div__ - __rtruediv__ = __rdiv__ - - ### PRIVATE METHODS ### - - def _clone(self): - def recurse(uuid, ugen, all_ugens): - if hasattr(ugen, "inputs"): - for input_ in ugen.inputs: - if not isinstance(input_, OutputProxy): - continue - input_ = input_.source - input_._uuid = uuid - recurse(uuid, input_, all_ugens) - ugen._uuid = uuid - if ugen not in all_ugens: - all_ugens.append(ugen) - - from . import SynthDefBuilder - - builder = SynthDefBuilder() - ugens = copy.deepcopy(self) - if not isinstance(ugens, UGenVector): - ugens = [ugens] - all_ugens = [] - for u in ugens: - if isinstance(u, OutputProxy): - u = u.source - recurse(builder._uuid, u, all_ugens) - for u in all_ugens: - if isinstance(u, UGen): - builder._add_ugens(u) - else: - builder._add_parameter(u) - return builder.build(optimize=False) - - @staticmethod - def _compute_binary_op(left, right, operator) -> "UGenOperable": - result: List[Union[OutputProxy, float]] = [] - if not isinstance(left, Sequence): - left = (left,) - if not isinstance(right, Sequence): - right = (right,) - dictionary = {"left": left, "right": right} - operator = BinaryOperator.from_expr(operator) - special_index = operator.value - for expanded_dict in UGen._expand_dictionary(dictionary): - left = expanded_dict["left"] - right = expanded_dict["right"] - ugen = BinaryOpUGen._new_single( - calculation_rate=max( - [ - CalculationRate.from_expr(left), - CalculationRate.from_expr(right), - ] - ), - left=left, - right=right, - special_index=special_index, - ) - result.extend(ugen if not isinstance(ugen, (float, int)) else [ugen]) - if len(result) == 1: - # TODO: remove cast(...) - return cast(UGenOperable, result[0]) - return UGenVector(*result) - - def _compute_ugen_map(self, map_ugen, **kwargs): - sources = [] - ugens = [] - if len(self) == 1: - sources = [self] - else: - sources = self - for source in sources: - method = UGen._get_method_for_rate(map_ugen, source) - ugen = method(source=source, **kwargs) - ugens.extend(ugen) - if 1 < len(ugens): - return UGenVector(*ugens) - elif len(ugens) == 1: - return ugens[0].source - return [] - - @staticmethod - def _compute_unary_op(source, operator) -> "UGenOperable": - result: List[Union[OutputProxy, float]] = [] - if not isinstance(source, Sequence): - source = (source,) - operator = UnaryOperator.from_expr(operator) - special_index = operator.value - for single_source in source: - calculation_rate = CalculationRate.from_expr(single_source) - ugen = UnaryOpUGen._new_single( - calculation_rate=calculation_rate, - source=single_source, - special_index=special_index, - ) - result.extend(ugen if not isinstance(ugen, (float, int)) else [ugen]) - if len(result) == 1: - # TODO: remove cast(...) - return cast(UGenOperable, result[0]) - return UGenVector(*result) - - def _get_output_proxy(self, i): - if isinstance(i, int): - if not (0 <= i < len(self)): - raise IndexError(i, len(self)) - return OutputProxy(source=self, output_index=i) - indices = i.indices(len(self)) - if not (0 <= indices[0] <= indices[1] <= len(self)): - raise IndexError(i, indices, len(self)) - output_proxies = ( - OutputProxy(source=self, output_index=i) for i in range(*indices) + return _compute_unary_op( + source=self, special_index=UnaryOperator.LOG, float_operator=math.log ) - return UGenVector(*output_proxies) - ### PUBLIC METHODS ### - - def absdiff(self, expr: "UGenOperand") -> "UGenOperable": + def log2(self) -> "UGenOperable": """ - Calculates absolute difference between ugen graph and `expr`. + Compute the base 2 logarithm of UGen graph. - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar() - >>> expr = supriya.ugens.WhiteNoise.kr() - >>> result = ugen_graph.absdiff(expr) - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: a6b274b5f30e1dfa86ac1d00ef1c169b - ugens: - - SinOsc.ar: - frequency: 440.0 - phase: 0.0 - - WhiteNoise.kr: null - - BinaryOpUGen(ABSOLUTE_DIFFERENCE).ar: - left: SinOsc.ar[0] - right: WhiteNoise.kr[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.log2() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(LOG2).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(LOG2).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_binary_op(self, expr, BinaryOperator.ABSOLUTE_DIFFERENCE) + return _compute_unary_op( + source=self, special_index=UnaryOperator.LOG2, float_operator=math.log2 + ) - def amplitude_to_db(self) -> "UGenOperable": + def log10(self) -> "UGenOperable": """ - Converts ugen graph from amplitude to decibels. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.amplitude_to_db() - - :: + Compute the base 10 logarithm of UGen graph. - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: 73daa5fd8db0d28c03c3872c845fd3ed - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(AMPLITUDE_TO_DB).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.log10() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(LOG10).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(LOG10).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.AMPLITUDE_TO_DB) + return _compute_unary_op( + source=self, special_index=UnaryOperator.LOG10, float_operator=math.log10 + ) - def clip( - self, - minimum: Union[SupportsFloat, "UGenOperable"], - maximum: Union[SupportsFloat, "UGenOperable"], - ) -> "UGenOperable": + def max(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Clips ugen graph. - - .. container:: example - - **Example 1:** - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.clip(-0.25, 0.25) - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + Compute the maximum of UGen graph and ``expr``. - >>> print(result) - synthdef: - name: e710843b0e0fbc5e6185afc6cdf90149 - ugens: - - WhiteNoise.ar: null - - Clip.ar: - source: WhiteNoise.ar[0] - minimum: -0.25 - maximum: 0.25 - - .. container:: example - - **Example 2:** - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph.clip(-0.25, 0.25) - - :: - - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.max(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MAXIMUM).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(MAXIMUM).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: 000e997ea0d7e8637c9f9040547baa50 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - Clip.ar/0: - source: SinOsc.ar/0[0] - minimum: -0.25 - maximum: 0.25 - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - Clip.ar/1: - source: SinOsc.ar/1[0] - minimum: -0.25 - maximum: 0.25 - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - Clip.ar/2: - source: SinOsc.ar/2[0] - minimum: -0.25 - maximum: 0.25 """ - from . import Clip - - return self._compute_ugen_map(Clip, minimum=minimum, maximum=maximum) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.MAXIMUM, + float_operator=lambda a, b: max((a, b)), + ) - def cubed(self) -> "UGenOperable": + def max(self, expr: "UGenOperand") -> "UGenOperable": """ - Calculates the cube of ugen graph. + Calculates maximum between ugen graph and `expr`. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.cubed() + >>> left = supriya.ugens.SinOsc.ar() + >>> right = supriya.ugens.WhiteNoise.kr() + >>> operation = left.max(right) >>> print(operation) synthdef: - name: ad344666e7f3f60edac95b1ea40c412d + name: dcdca07fb0439c8b4321f42803d18c32 ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(CUBED).ar: - source: DC.ar[0] + - SinOsc.ar: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MAXIMUM).ar: + left: SinOsc.ar[0] + right: WhiteNoise.kr[0] Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.CUBED) + return self._compute_binary_op(self, expr, BinaryOperator.MAXIMUM) - def db_to_amplitude(self) -> "UGenOperable": + def midi_to_hz(self) -> "UGenOperable": """ - Converts ugen graph from decibels to amplitude. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.db_to_amplitude() + Convert UGen graph from MIDI note number to Hertz. - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: fe82aae42b01b2b43d427cafd77c1c22 - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(DB_TO_AMPLITUDE).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.midi_to_hz() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(MIDI_TO_HZ).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(MIDI_TO_HZ).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.DB_TO_AMPLITUDE) - - def digit_value(self) -> "UGenOperable": - return self._compute_unary_op(self, UnaryOperator.DIGIT_VALUE) + return _compute_unary_op(source=self, special_index=UnaryOperator.MIDI_TO_HZ) - def distort(self) -> "UGenOperable": + def min(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Distorts ugen graph non-linearly. + Compute the minimum of UGen graph and ``expr``. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.distort() - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.min(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: bb632e15f448820d93b3880ad943617b + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(DISTORT).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MINIMUM).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(MINIMUM).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.DISTORT) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.MINIMUM, + float_operator=lambda a, b: min((a, b)), + ) - def exponential(self) -> "UGenOperable": + def min(self, expr: "UGenOperand") -> "UGenOperable": """ - Calculates the natural exponential function of ugen graph. + Calculates minimum between ugen graph and `expr`. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.exponential() + >>> left = supriya.ugens.SinOsc.ar() + >>> right = supriya.ugens.WhiteNoise.kr() + >>> operation = left.min(right) >>> print(operation) synthdef: - name: f3b8b1036b3cceddf116c3f6a3c5a9a0 + name: f80c0a7b300911e9eff0e8760f5fab18 ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(EXPONENTIAL).ar: - source: DC.ar[0] + - SinOsc.ar: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(MINIMUM).ar: + left: SinOsc.ar[0] + right: WhiteNoise.kr[0] Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.EXPONENTIAL) + return self._compute_binary_op(self, expr, BinaryOperator.MINIMUM) - def fractional_part(self) -> "UGenOperable": + def octave_to_hz(self) -> "UGenOperable": """ - Calculates the fraction part of ugen graph. + Convert UGen graph from octave number to Hertz. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.fractional_part() - >>> print(operation) + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.octave_to_hz() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: c663d5ee6c7c5347c043727c628af658 + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(FRACTIONAL_PART).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(OCTAVE_TO_HZ).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(OCTAVE_TO_HZ).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.FRACTIONAL_PART) + return _compute_unary_op(source=self, special_index=UnaryOperator.OCTAVE_TO_HZ) - def hanning_window(self) -> "UGenOperable": + def rand_range(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates Hanning-window of ugen graph. + Compute a random number in the interval of UGen graph to ``expr``. - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.hanning_window() - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 18cb43db42ae3499f2c233e83df877fd - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(HANNING_WINDOW).ar: - source: LFNoise2.ar[0] + :: - Returns ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.HANNING_WINDOW) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.rand_range(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(RANDRANGE).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(RANDRANGE).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - def hz_to_midi(self) -> "UGenOperable": """ - Converts ugen graph from Hertz to midi note number. + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.RANDRANGE, + ) - .. container:: example + def rand(self) -> "UGenOperable": + """ + Compute a random number between 0 and UGen graph, exclusive. - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.hz_to_midi() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.rand() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(RAND).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(RAND).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.RAND, + ) - >>> supriya.graph(result) # doctest: +SKIP + def ratio_to_semitones(self) -> "UGenOperable": + """ + Converts UGen graph from frequency ratio to semitone distance. - :: + :: - >>> print(result) - synthdef: - name: 227a6ae85bc89b3af939cff32f54e36a - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(HZ_TO_MIDI).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.ratio_to_semitones() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(RATIO_TO_SEMITONES).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(RATIO_TO_SEMITONES).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.HZ_TO_MIDI) + return _compute_unary_op( + source=self, special_index=UnaryOperator.RATIO_TO_SEMITONES + ) - def hz_to_octave(self) -> "UGenOperable": + def rectangle_window(self) -> "UGenOperable": """ - Converts ugen graph from Hertz to octave number. + Compute rectangle window from UGen graph. - .. container:: example - - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.hz_to_octave() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.rectangle_window() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(RECTANGLE_WINDOW).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(RECTANGLE_WINDOW).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, special_index=UnaryOperator.RECTANGLE_WINDOW + ) - >>> supriya.graph(result) # doctest: +SKIP + def reciprocal(self) -> "UGenOperable": + """ + Compute the reciprocal of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: e4fd4ca786d453fc5dfb955c63b6fbf6 - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(HZ_TO_OCTAVE).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.reciprocal() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(RECIPROCAL).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(RECIPROCAL).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.HZ_TO_OCTAVE) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.RECIPROCAL, + float_operator=lambda x: 1 / x, + ) - def is_equal_to(self, expr: "UGenOperand") -> "UGenOperable": + def ring1(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates equality between ugen graph and `expr`. + Compute ring modulation of UGen graph and ``expr`` plus UGen graph. + + - (a * b) + a :: - >>> left = supriya.ugens.SinOsc.ar() - >>> right = supriya.ugens.WhiteNoise.kr() - >>> operation = left.is_equal_to(right) - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.ring1(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: 8287d890708ce26adff4968d63d494a0 + name: ... ugens: - - SinOsc.ar: + - SinOsc.ar/0: frequency: 440.0 phase: 0.0 - WhiteNoise.kr: null - - BinaryOpUGen(EQUAL).ar: - left: SinOsc.ar[0] + - BinaryOpUGen(RING1).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(RING1).ar/1: + left: SinOsc.ar/1[0] right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_binary_op(self, expr, BinaryOperator.EQUAL) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.RING1, + float_operator=lambda a, b: ((a * b) + a), + ) - def is_not_equal_to(self, expr: "UGenOperand") -> "UGenOperable": + def ring2(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates inequality between ugen graph and `expr`. + Compute ring modulation of UGen graph and ``expr`` plus both sources. + + - (a * b) + a + b :: - >>> left = supriya.ugens.SinOsc.ar() - >>> right = supriya.ugens.WhiteNoise.kr() - >>> operation = left.is_not_equal_to(right) - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.ring2(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: b9f77aa86bc08a3b023d8f664afef05d + name: ... ugens: - - SinOsc.ar: + - SinOsc.ar/0: frequency: 440.0 phase: 0.0 - WhiteNoise.kr: null - - BinaryOpUGen(NOT_EQUAL).ar: - left: SinOsc.ar[0] + - BinaryOpUGen(RING2).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(RING2).ar/1: + left: SinOsc.ar/1[0] right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_binary_op(self, expr, BinaryOperator.NOT_EQUAL) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.RING2, + float_operator=lambda a, b: (a * b) + a + b, + ) - def lagged(self, lag_time=0.5) -> "UGenOperable": + def ring3(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Lags ugen graph. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.lagged(0.5) + Compute ring modulation of UGen graph and ``expr`` multiplied by UGen graph. - :: + - a * b * a - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 6c3e2cc1a3d54ecfaa49d567a84eae77 - ugens: - - WhiteNoise.ar: null - - Lag.ar: - source: WhiteNoise.ar[0] - lag_time: 0.5 - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph.lagged(0.5) - - :: - - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.ring3(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(RING3).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(RING3).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - >>> print(result) - synthdef: - name: 67098a4ddab35f6e1333a80a226bf559 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - Lag.ar/0: - source: SinOsc.ar/0[0] - lag_time: 0.5 - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - Lag.ar/1: - source: SinOsc.ar/1[0] - lag_time: 0.5 - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - Lag.ar/2: - source: SinOsc.ar/2[0] - lag_time: 0.5 """ - from . import Lag - - return self._compute_ugen_map(Lag, lag_time=lag_time) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.RING3, + float_operator=lambda a, b: a * a * b, + ) - def log(self) -> "UGenOperable": + def ring4(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates the natural logarithm of ugen graph. + Compute ring modulation variant of UGen graph and ``expr``. + + - (a * a * b) - (a * b * b) :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.log() - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.ring4(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: 4da44dab9d935efd1cf098b4d7cec420 + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(LOG).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(RING4).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(RING4).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.LOG) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.RING4, + float_operator=lambda a, b: (a * a * b) - (a * b * b), + ) - def log2(self) -> "UGenOperable": + def round(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates the base-2 logarithm of ugen graph. + Round UGen graph by ``expr``. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.log2() - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.round(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: f956f79a387ffbeb409326046397b4dd + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(LOG2).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(ROUND).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ROUND).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.LOG2) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.ROUND, + ) - def log10(self) -> "UGenOperable": + def round_up(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates the base-10 logarithm of ugen graph. + Round UGen graph _up_ by ``expr``. :: - >>> source = supriya.ugens.DC.ar(source=0.5) - >>> operation = source.log10() - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.round_up(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: 122d9333b8ac76164782d00707d3386a + name: ... ugens: - - DC.ar: - source: 0.5 - - UnaryOpUGen(LOG10).ar: - source: DC.ar[0] + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(ROUND_UP).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(ROUND_UP).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.LOG10) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.ROUND_UP, + ) - def max(self, expr: "UGenOperand") -> "UGenOperable": + def s_curve(self) -> "UGenOperable": """ - Calculates maximum between ugen graph and `expr`. + Compute the S-curve of UGen graph. :: - >>> left = supriya.ugens.SinOsc.ar() - >>> right = supriya.ugens.WhiteNoise.kr() - >>> operation = left.max(right) - >>> print(operation) + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.s_curve() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: dcdca07fb0439c8b4321f42803d18c32 + name: ... ugens: - - SinOsc.ar: + - SinOsc.ar/0: frequency: 440.0 phase: 0.0 - - WhiteNoise.kr: null - - BinaryOpUGen(MAXIMUM).ar: - left: SinOsc.ar[0] - right: WhiteNoise.kr[0] + - UnaryOpUGen(S_CURVE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(S_CURVE).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_binary_op(self, expr, BinaryOperator.MAXIMUM) + return _compute_unary_op(source=self, special_index=UnaryOperator.S_CURVE) - def midi_to_hz(self) -> "UGenOperable": + def scale( + self, + input_minimum: "UGenRecursiveInput", + input_maximum: "UGenRecursiveInput", + output_minimum: "UGenRecursiveInput", + output_maximum: "UGenRecursiveInput", + exponential: bool = False, + ) -> "UGenOperable": """ - Converts ugen graph from midi note number to Hertz. - - .. container:: example - - :: + Scale UGen graph. - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.midi_to_hz() - - :: + :: - >>> supriya.graph(result) # doctest: +SKIP + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.scale(-1.0, 1.0, 0.5, 0.75) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - MulAdd.ar/0: + source: SinOsc.ar/0[0] + multiplier: 0.125 + addend: 0.625 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - MulAdd.ar/1: + source: SinOsc.ar/1[0] + multiplier: 0.125 + addend: 0.625 - :: + """ + from . import LinExp, LinLin - >>> print(result) - synthdef: - name: 5faaa2c74715175625d774b20952f263 - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(MIDI_TO_HZ).ar: - source: WhiteNoise.ar[0] + return _compute_ugen_map( + self, + cast(Type[UGen], LinExp if exponential else LinLin), + input_minimum=input_minimum, + input_maximum=input_maximum, + output_minimum=output_minimum, + output_maximum=output_maximum, + ) - Returns ugen graph. + def scale_negative(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - return self._compute_unary_op(self, UnaryOperator.MIDI_TO_HZ) + Scale negative portion of UGen graph by ``expr``. - def min(self, expr: "UGenOperand") -> "UGenOperable": - """ - Calculates minimum between ugen graph and `expr`. + - a * b when a < 0 + - otherwise a :: - >>> left = supriya.ugens.SinOsc.ar() - >>> right = supriya.ugens.WhiteNoise.kr() - >>> operation = left.min(right) - >>> print(operation) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.scale_negative(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) synthdef: - name: f80c0a7b300911e9eff0e8760f5fab18 + name: ... ugens: - - SinOsc.ar: + - SinOsc.ar/0: frequency: 440.0 phase: 0.0 - WhiteNoise.kr: null - - BinaryOpUGen(MINIMUM).ar: - left: SinOsc.ar[0] + - BinaryOpUGen(SCALE_NEG).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SCALE_NEG).ar/1: + left: SinOsc.ar/1[0] right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_binary_op(self, expr, BinaryOperator.MINIMUM) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SCALE_NEG, + ) - def octave_to_hz(self) -> "UGenOperable": + def semitones_to_ratio(self) -> "UGenOperable": """ - Converts ugen graph from octave number to Hertz. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.octave_to_hz() + Converts UGen graph from semitone distance to frequency ratio. - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: 04c00b0f32088eb5e4cef0549aed6d96 - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(OCTAVE_TO_HZ).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.semitones_to_ratio() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SEMITONES_TO_RATIO).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SEMITONES_TO_RATIO).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.OCTAVE_TO_HZ) + return _compute_unary_op( + source=self, special_index=UnaryOperator.SEMITONES_TO_RATIO + ) - def ratio_to_semitones(self) -> "UGenOperable": + def sign(self) -> "UGenOperable": """ - Converts ugen graph from frequency ratio to semitone distance. - - .. container:: example + Compute the sign of UGen graph. - :: + :: - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.ratio_to_semitones() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.sign() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SIGN).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SIGN).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.SIGN) - >>> supriya.graph(result) # doctest: +SKIP + def silence(self) -> "UGenOperable": + """ + Silence (zero-out) UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 2e23630ade4fab35fc821c190b7f33db - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(RATIO_TO_SEMITONES).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.silence() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SILENCE).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SILENCE).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.RATIO_TO_SEMITONES) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SILENCE, + float_operator=lambda x: 0.0, + ) - def rectangle_window(self) -> "UGenOperable": + def sin(self) -> "UGenOperable": """ - Calculates rectangle-window of ugen graph. - - .. container:: example + Compute the sine of UGen graph. - :: + :: - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.rectangle_window() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.sin() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SIN).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SIN).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SIN, + float_operator=math.sin, + ) - >>> supriya.graph(result) # doctest: +SKIP + def sinh(self) -> "UGenOperable": + """ + Compute the hyperbolic sine of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 0d296187bbdb205f3a283f301a5fad61 - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(RECTANGLE_WINDOW).ar: - source: LFNoise2.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.sinh() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SINH).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SINH).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.RECTANGLE_WINDOW) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SINH, + float_operator=math.sinh, + ) - def reciprocal(self) -> "UGenOperable": + def softclip(self) -> "UGenOperable": """ - Calculates reciprocal of ugen graph. - - .. container:: example + Compute non-linear distortion of UGen graph. - :: + :: - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.reciprocal() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.softclip() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SOFTCLIP).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SOFTCLIP).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.SOFTCLIP) - >>> supriya.graph(result) # doctest: +SKIP + def sqrt(self) -> "UGenOperable": + """ + Compute the square root of UGen graph. - :: + :: - >>> print(result) - synthdef: - name: 2e1c714d0def9d5c310197861d725559 - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(RECIPROCAL).ar: - source: LFNoise2.ar[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.sqrt() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SQUARE_ROOT).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SQUARE_ROOT).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.RECIPROCAL) + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SQUARE_ROOT, + float_operator=math.sqrt, + ) - def s_curve(self) -> "UGenOperable": + def squared(self) -> "UGenOperable": """ - Calculates S-curve of ugen graph. - - .. container:: example + Compute the square of UGen graph. - :: + :: - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.s_curve() + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.squared() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SQUARED).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SQUARED).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SQUARED, + float_operator=lambda x: x**2, + ) - >>> supriya.graph(result) # doctest: +SKIP + def square_of_difference(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute the square of difference between UGen graph and ``expr``. - :: + - (a - b) ** 2 - >>> print(result) - synthdef: - name: 21bcaf49922e2c4124d4cadba85c00ac - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(S_CURVE).ar: - source: LFNoise2.ar[0] + :: - Returns ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.S_CURVE) + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.square_of_difference(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SQUARE_OF_DIFFERENCE).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SQUARE_OF_DIFFERENCE).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - def scale( - self, - input_minimum, - input_maximum, - output_minimum, - output_maximum, - exponential=False, - ) -> "UGenOperable": """ - Scales ugen graph from `input_minimum` and `input_maximum` to `output_minimum` and `output_maximum`. - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.scale(-1, 1, 0.5, 0.75) - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: e2295e64ed7b9c949ec22ccdc82520e3 - ugens: - - WhiteNoise.ar: null - - MulAdd.ar: - source: WhiteNoise.ar[0] - multiplier: 0.125 - addend: 0.625 - - .. container:: example - - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar( - ... frequency=[440, 442, 443], - ... ) - >>> result = ugen_graph.scale(-1, 1, 0.5, 0.75, exponential=True) - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: - - >>> print(result) - synthdef: - name: 88dca305143542bd40a82d8a6a337306 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - LinExp.ar/0: - source: SinOsc.ar/0[0] - input_minimum: -1.0 - input_maximum: 1.0 - output_minimum: 0.5 - output_maximum: 0.75 - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - LinExp.ar/1: - source: SinOsc.ar/1[0] - input_minimum: -1.0 - input_maximum: 1.0 - output_minimum: 0.5 - output_maximum: 0.75 - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - LinExp.ar/2: - source: SinOsc.ar/2[0] - input_minimum: -1.0 - input_maximum: 1.0 - output_minimum: 0.5 - output_maximum: 0.75 - """ - from . import LinExp, LinLin - - return self._compute_ugen_map( - LinExp if exponential else LinLin, - input_minimum=input_minimum, - input_maximum=input_maximum, - output_minimum=output_minimum, - output_maximum=output_maximum, + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SQUARE_OF_DIFFERENCE, + float_operator=lambda a, b: (a - b) ** 2, ) - def semitones_to_ratio(self) -> "UGenOperable": + def square_of_sum(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Converts ugen graph from semitone distance to frequency ratio. + Compute square of sum of UGen graph and ``expr``. - .. container:: example + - (a + b) ** 2 - :: - - >>> ugen_graph = supriya.ugens.WhiteNoise.ar() - >>> result = ugen_graph.semitones_to_ratio() - - :: - - >>> supriya.graph(result) # doctest: +SKIP - - :: + :: - >>> print(result) - synthdef: - name: f77ac2c24b06f8e620817f14285c2877 - ugens: - - WhiteNoise.ar: null - - UnaryOpUGen(SEMITONES_TO_RATIO).ar: - source: WhiteNoise.ar[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.square_of_sum(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SQUARE_OF_SUM).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SQUARE_OF_SUM).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.SEMITONES_TO_RATIO) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SQUARE_OF_SUM, + float_operator=lambda a, b: (a + b) ** 2, + ) - def sign(self) -> "UGenOperable": + def sum3_rand(self) -> "UGenOperable": """ - Calculates sign of ugen graph. + Compute a random number in the interval of - UGen graph to + UGen graph, calculated by averaging three such numbers. - .. container:: example + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.sum3_rand() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(SUM3RAND).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(SUM3RAND).ar/1: + source: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.sign() + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.SUM3RAND, + ) - :: + def sum_of_squares(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute sum of squares of UGen graph and ``expr``. - >>> supriya.graph(result) # doctest: +SKIP + - (a * a) + (b * b) - :: + :: - >>> print(result) - synthdef: - name: 6f62abd8306dbf1aae66c09dd98203b5 - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(SIGN).ar: - source: LFNoise2.ar[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.sum_of_squares(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(SUM_OF_SQUARES).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(SUM_OF_SQUARES).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.SIGN) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.SUM_OF_SQUARES, + float_operator=lambda a, b: (a * a) + (b * b), + ) - def softclip(self) -> "UGenOperable": + def tan(self) -> "UGenOperable": """ - Distorts ugen graph non-linearly. - """ - return self._compute_unary_op(self, UnaryOperator.SOFTCLIP) + Compute the tangent of UGen graph. - def sqrt(self) -> "UGenOperable": - """ - Calculates square root of ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.SQUARE_ROOT) + :: - def squared(self) -> "UGenOperable": - """ - Calculates square of ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.SQUARED) + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.tan() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(TAN).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(TAN).ar/1: + source: SinOsc.ar/1[0] - def sum(self) -> "UGenOperable": """ - Sums ugen graph. + return _compute_unary_op( + source=self, + special_index=UnaryOperator.TAN, + float_operator=math.tan, + ) - .. container:: example + def tanh(self) -> "UGenOperable": + """ + Compute the hyperbolic tangent of UGen graph. - **Example 1:** + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.tanh() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(TANH).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(TANH).ar/1: + source: SinOsc.ar/1[0] - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.sum() + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.TANH, + float_operator=math.tanh, + ) - :: + def through(self) -> "UGenOperable": + """ + Pass through UGen graph. - >>> supriya.graph(result) # doctest: +SKIP + :: - :: + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.through() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(THRU).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(THRU).ar/1: + source: SinOsc.ar/1[0] - >>> print(result) - synthdef: - name: 350f2065d4edc69244399dcaff5a1ceb - ugens: - - LFNoise2.ar: - frequency: 500.0 + """ + return _compute_unary_op( + source=self, + special_index=UnaryOperator.THRU, + float_operator=lambda x: x, + ) - .. container:: example + def transpose(self, semitones: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute transposition of UGen graph by ``expr`` in semitones. - **Example 2:** + :: - :: - - >>> ugen_graph = supriya.ugens.SinOsc.ar(frequency=[440, 442, 443]) - >>> result = ugen_graph.sum() + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.transpose(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_MIDI).ar/0: + source: SinOsc.ar/0[0] + - WhiteNoise.kr: null + - BinaryOpUGen(ADDITION).ar/0: + left: UnaryOpUGen(HZ_TO_MIDI).ar/0[0] + right: WhiteNoise.kr[0] + - UnaryOpUGen(MIDI_TO_HZ).ar/0: + source: BinaryOpUGen(ADDITION).ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(HZ_TO_MIDI).ar/1: + source: SinOsc.ar/1[0] + - BinaryOpUGen(ADDITION).ar/1: + left: UnaryOpUGen(HZ_TO_MIDI).ar/1[0] + right: WhiteNoise.kr[0] + - UnaryOpUGen(MIDI_TO_HZ).ar/1: + source: BinaryOpUGen(ADDITION).ar/1[0] - :: + """ + return (self.hz_to_midi() + semitones).midi_to_hz() - >>> supriya.graph(result) # doctest: +SKIP + def triangle_window(self) -> "UGenOperable": + """ + Compute triangle window from UGen graph. - :: + :: - >>> print(result) - synthdef: - name: a1d26283f87b8b445db982ff0e831fb7 - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - SinOsc.ar/1: - frequency: 442.0 - phase: 0.0 - - SinOsc.ar/2: - frequency: 443.0 - phase: 0.0 - - Sum3.ar: - input_one: SinOsc.ar/0[0] - input_two: SinOsc.ar/1[0] - input_three: SinOsc.ar/2[0] + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.triangle_window() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(TRIANGLE_WINDOW).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(TRIANGLE_WINDOW).ar/1: + source: SinOsc.ar/1[0] - Returns ugen graph. """ - from . import Mix - - return Mix.new(self) + return _compute_unary_op( + source=self, special_index=UnaryOperator.TRIANGLE_WINDOW + ) - def tanh(self) -> "UGenOperable": + def truncate(self, expr: "UGenRecursiveInput") -> "UGenOperable": """ - Calculates hyperbolic tangent of ugen graph. - - .. container:: example + Truncate UGen graph by ``expr``. - :: + :: - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.tanh() + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.truncate(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(TRUNCATION).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(TRUNCATION).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - :: + """ + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.TRUNCATION, + ) - >>> supriya.graph(result) # doctest: +SKIP + def unsigned_shift(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute unsigned right shift of UGen graph by ``expr``. - :: + :: - >>> print(result) - synthdef: - name: e74aa9abf6e389d8ca39d2c9828d81be - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(TANH).ar: - source: LFNoise2.ar[0] + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.unsigned_shift(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(UNSIGNED_SHIFT).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(UNSIGNED_SHIFT).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - Returns ugen graph. """ - return self._compute_unary_op(self, UnaryOperator.TANH) + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.UNSIGNED_SHIFT, + ) - def transpose(self, semitones) -> "UGenOperable": + def welch_window(self) -> "UGenOperable": """ - Transposes ugen graph by `semitones`. + Compute Welch window from UGen graph. - .. container:: example - - :: + :: - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.transpose([0, 3, 7]) + >>> from supriya.ugens import SinOsc + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> result = ugen_graph.welch_window() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - UnaryOpUGen(WELCH_WINDOW).ar/0: + source: SinOsc.ar/0[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - UnaryOpUGen(WELCH_WINDOW).ar/1: + source: SinOsc.ar/1[0] - :: + """ + return _compute_unary_op(source=self, special_index=UnaryOperator.WELCH_WINDOW) - >>> supriya.graph(result) # doctest: +SKIP + def wrap2(self, expr: "UGenRecursiveInput") -> "UGenOperable": + """ + Compute bilateral wrapping of UGen graph by ``expr``. - :: + - wrap a to +/- b - >>> print(result) - synthdef: - name: c481c3d42e3cfcee0267250247dab51f - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(HZ_TO_MIDI).ar: - source: LFNoise2.ar[0] - - UnaryOpUGen(MIDI_TO_HZ).ar/0: - source: UnaryOpUGen(HZ_TO_MIDI).ar[0] - - BinaryOpUGen(ADDITION).ar/0: - left: UnaryOpUGen(HZ_TO_MIDI).ar[0] - right: 3.0 - - UnaryOpUGen(MIDI_TO_HZ).ar/1: - source: BinaryOpUGen(ADDITION).ar/0[0] - - BinaryOpUGen(ADDITION).ar/1: - left: UnaryOpUGen(HZ_TO_MIDI).ar[0] - right: 7.0 - - UnaryOpUGen(MIDI_TO_HZ).ar/2: - source: BinaryOpUGen(ADDITION).ar/1[0] + :: - Returns ugen graph. - """ - return (self.hz_to_midi() + semitones).midi_to_hz() + >>> from supriya.ugens import SinOsc, WhiteNoise + >>> ugen_graph = SinOsc.ar(frequency=[440, 443]) + >>> expr = WhiteNoise.kr() + >>> result = ugen_graph.wrap2(expr) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - WhiteNoise.kr: null + - BinaryOpUGen(WRAP2).ar/0: + left: SinOsc.ar/0[0] + right: WhiteNoise.kr[0] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - BinaryOpUGen(WRAP2).ar/1: + left: SinOsc.ar/1[0] + right: WhiteNoise.kr[0] - def triangle_window(self) -> "UGenOperable": """ - Calculates triangle-window of ugen graph. + return _compute_binary_op( + left=self, + right=expr, + special_index=BinaryOperator.WRAP2, + ) - .. container:: example - :: +class UGenScalar(UGenOperable): + """ + A UGen scalar. + """ - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.triangle_window() + pass - :: - >>> supriya.graph(result) # doctest: +SKIP +class OutputProxy(UGenScalar): + """ + A UGen output proxy. - :: + Encodes a reference to a specific output of a UGen, as a scalar. + """ - >>> print(result) - synthdef: - name: ebb1820b9d08a639565b5090b53681db - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(TRIANGLE_WINDOW).ar: - source: LFNoise2.ar[0] + def __init__(self, ugen: "UGen", index: int) -> None: + self.ugen = ugen + self.index = index - Returns ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.TRIANGLE_WINDOW) + def __eq__(self, expr) -> bool: + return ( + isinstance(expr, type(self)) + and self.ugen is expr.ugen + and self.index == expr.index + ) - def welch_window(self) -> "UGenOperable": - """ - Calculates Welch-window of ugen graph. + def __hash__(self) -> int: + return hash((type(self), self.ugen, self.index)) - .. container:: example + def __repr__(self) -> str: + return repr(self.ugen).replace(">", f"[{self.index}]>") - :: + def __str__(self) -> str: + return str(self.ugen) - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.welch_window() + @property + def calculation_rate(self) -> CalculationRate: + return self.ugen.calculation_rate - :: - >>> supriya.graph(result) # doctest: +SKIP +class ConstantProxy(UGenScalar): + """ + A floating point constant proxy. - :: + Wraps a float and exposes all UGenOperable methods against it. + """ - >>> print(result) - synthdef: - name: ... - ugens: - - LFNoise2.ar: - frequency: 500.0 - - UnaryOpUGen(WELCH_WINDOW).ar: - source: LFNoise2.ar[0] + def __init__(self, value: SupportsFloat) -> None: + self.value = float(value) - Returns ugen graph. - """ - return self._compute_unary_op(self, UnaryOperator.WELCH_WINDOW) + def __eq__(self, expr) -> bool: + if isinstance(expr, SupportsFloat): + return float(self) == float(expr) + return False - @property - def signal_range(self): - raise NotImplementedError + def __float__(self) -> float: + return self.value + def __repr__(self) -> str: + return f"<{self.value}>" -class UGenSerializable(Protocol): - def serialize(self) -> Sequence[Union[SupportsFloat, "OutputProxy"]]: - pass + def __str__(self) -> str: + return str(self.value) -class UGenVector(UGenOperable, Sequence): +class UGenVector(UGenOperable, Sequence[UGenOperable]): + """ + A sequence of UGenOperables. + """ - ### INITIALIZER ### + def __init__(self, *values: Union[SupportsFloat, UGenOperable]) -> None: + values_: List[Union[UGen, UGenScalar, UGenVector]] = [] + for x in values: + if isinstance(x, (UGen, UGenScalar, UGenVector)): + values_.append(x) + elif isinstance(x, UGenSerializable): + values_.append(UGenVector(*x.serialize())) + elif isinstance(x, SupportsFloat): + values_.append(ConstantProxy(float(x))) + else: + raise ValueError(x) + self._values = tuple(values_) - def __init__(self, *ugens): - self._ugens = tuple(ugens) + @overload + def __getitem__(self, i: int) -> UGenOperable: + pass - ### SPECIAL METHODS ### + @overload + def __getitem__(self, i: slice) -> "UGenVector": + pass def __getitem__(self, i): - return self.ugens[i] + if isinstance(i, int): + return self._values[i] + return UGenVector(*self._values[i]) - def __len__(self): - return len(self.ugens) + def __len__(self) -> int: + return len(self._values) - def __repr__(self): + def __repr__(self) -> str: return f"<{type(self).__name__}([{', '.join(repr(x) for x in self)}])>" - ### PUBLIC PROPERTIES ### - - @property - def signal_range(self): - return max(_.signal_range for _ in self) + def flatten(self) -> UGenOperable: + """ + Flatten UGen vector. - @property - def ugens(self): - return self._ugens + :: + >>> from supriya.ugens import Pan2, SinOsc + >>> vector = Pan2.ar(source=Pan2.ar(source=SinOsc.ar(frequency=[440, 443]))) + >>> supriya.graph(vector) # doctest: +SKIP + >>> print(repr(vector)) + , ])>, , ])>])> -class OutputProxy(UGenOperable): - ### INITIALIZER ### + :: - def __init__(self, *, source: "UGen", output_index: int) -> None: - self._output_index = output_index - self._source = source + >>> result = vector.flatten() + >>> print(repr(result)) + , , , , , , , ])> - ### SPECIAL METHODS ### + """ + if len(vector := UGenVector(*flatten(self))) == 1: + return vector[0] + return vector - def __eq__(self, expr): - if not isinstance(expr, type(self)): - return False - if self._output_index != expr._output_index: - return False - if self._source != expr._source: - return False - return True + def mix(self, channel_count: int = 1) -> UGenOperable: + """ + Mix UGen vector down to ``channel_count`` outputs. - def __hash__(self) -> int: - hash_values = (type(self), self._output_index, self._source) - return hash(hash_values) + :: - def __iter__(self): - yield self + >>> from supriya.ugens import Pan2, SinOsc + >>> vector = Pan2.ar(source=Pan2.ar(source=SinOsc.ar(frequency=[440, 443]))) + >>> supriya.graph(vector) # doctest: +SKIP + >>> print(vector) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Pan2.ar/0: + source: SinOsc.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/1: + source: Pan2.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/2: + source: Pan2.ar/0[1] + position: 0.0 + level: 1.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Pan2.ar/3: + source: SinOsc.ar/1[0] + position: 0.0 + level: 1.0 + - Pan2.ar/4: + source: Pan2.ar/3[0] + position: 0.0 + level: 1.0 + - Pan2.ar/5: + source: Pan2.ar/3[1] + position: 0.0 + level: 1.0 - def __len__(self) -> int: - return 1 + :: - def __repr__(self) -> str: - return repr(self.source).replace(">", f"[{self.output_index}]>") + >>> result = vector.mix(2) + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Pan2.ar/0: + source: SinOsc.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/1: + source: Pan2.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/2: + source: Pan2.ar/0[1] + position: 0.0 + level: 1.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Pan2.ar/3: + source: SinOsc.ar/1[0] + position: 0.0 + level: 1.0 + - Pan2.ar/4: + source: Pan2.ar/3[0] + position: 0.0 + level: 1.0 + - Pan2.ar/5: + source: Pan2.ar/3[1] + position: 0.0 + level: 1.0 + - Sum4.ar/0: + input_one: Pan2.ar/1[0] + input_two: Pan2.ar/2[0] + input_three: Pan2.ar/4[0] + input_four: Pan2.ar/5[0] + - Sum4.ar/1: + input_one: Pan2.ar/1[1] + input_two: Pan2.ar/2[1] + input_three: Pan2.ar/4[1] + input_four: Pan2.ar/5[1] - ### PRIVATE METHODS ### + """ + from . import Mix - def _get_output_number(self): - return self._output_index + return Mix.multichannel(self, channel_count=channel_count) - def _get_source(self): - return self._source + def sum(self) -> UGenOperable: + """ + Sum UGen vector down to a single output. - ### PUBLIC PROPERTIES ### + :: - @property - def calculation_rate(self): - return self.source.calculation_rate + >>> from supriya.ugens import Pan2, SinOsc + >>> vector = Pan2.ar(source=Pan2.ar(source=SinOsc.ar(frequency=[440, 443]))) + >>> supriya.graph(vector) # doctest: +SKIP + >>> print(vector) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Pan2.ar/0: + source: SinOsc.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/1: + source: Pan2.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/2: + source: Pan2.ar/0[1] + position: 0.0 + level: 1.0 + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Pan2.ar/3: + source: SinOsc.ar/1[0] + position: 0.0 + level: 1.0 + - Pan2.ar/4: + source: Pan2.ar/3[0] + position: 0.0 + level: 1.0 + - Pan2.ar/5: + source: Pan2.ar/3[1] + position: 0.0 + level: 1.0 - @property - def has_done_flag(self): - return self.source.has_done_flag + :: - @property - def output_index(self) -> int: - return self._output_index + >>> result = vector.sum() + >>> supriya.graph(result) # doctest: +SKIP + >>> print(result) + synthdef: + name: ... + ugens: + - SinOsc.ar/0: + frequency: 440.0 + phase: 0.0 + - Pan2.ar/0: + source: SinOsc.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/1: + source: Pan2.ar/0[0] + position: 0.0 + level: 1.0 + - Pan2.ar/2: + source: Pan2.ar/0[1] + position: 0.0 + level: 1.0 + - Sum4.ar/0: + input_one: Pan2.ar/1[0] + input_two: Pan2.ar/1[1] + input_three: Pan2.ar/2[0] + input_four: Pan2.ar/2[1] + - SinOsc.ar/1: + frequency: 443.0 + phase: 0.0 + - Pan2.ar/3: + source: SinOsc.ar/1[0] + position: 0.0 + level: 1.0 + - Pan2.ar/4: + source: Pan2.ar/3[0] + position: 0.0 + level: 1.0 + - Pan2.ar/5: + source: Pan2.ar/3[1] + position: 0.0 + level: 1.0 + - Sum4.ar/1: + input_one: Pan2.ar/4[0] + input_two: Pan2.ar/4[1] + input_three: Pan2.ar/5[0] + input_four: Pan2.ar/5[1] + - BinaryOpUGen(ADDITION).ar: + left: Sum4.ar/0[0] + right: Sum4.ar/1[0] - @property - def signal_range(self) -> SignalRange: - return self.source.signal_range + """ + from . import Mix - @property - def source(self): - return self._source + return Mix.new(self) -class UGen(UGenOperable): +@runtime_checkable +class UGenSerializable(Protocol): """ - A UGen. + Protocol for classes serializable as UGenVectors. """ - ### CLASS VARIABLES ### + def serialize(self, **kwargs) -> UGenVector: + raise NotImplementedError - _default_channel_count = 1 - _has_settable_channel_count = False +class UGen(UGenOperable, Sequence): + """ + A UGen: a "unit generator". + """ + _channel_count = 1 _has_done_flag = False - + _has_settable_channel_count = False _is_input = False - _is_output = False - _is_pure = False - _is_width_first = False - - _ordered_input_names: Dict[ - str, Union[Default, Missing, SupportsFloat, str, None] - ] = {} - - _signal_range: int = SignalRange.BIPOLAR - - _unexpanded_input_names: Tuple[str, ...] = () - - _valid_calculation_rates: Tuple[int, ...] = () - - ### INITIALIZER ### + _ordered_keys: Tuple[str, ...] = () + _signal_range: SignalRange = SignalRange.BIPOLAR + _unexpanded_keys: FrozenSet[str] = frozenset() + _valid_calculation_rates: Tuple[CalculationRate, ...] = () def __init__( self, *, - calculation_rate: CalculationRateLike = None, - special_index: int = 0, - **kwargs, + calculation_rate: CalculationRate = CalculationRate.SCALAR, + special_index: SupportsInt = 0, + **kwargs: Union["UGenScalarInput", "UGenVectorInput"], ) -> None: - calculation_rate_ = CalculationRate.from_expr(calculation_rate) if self._valid_calculation_rates: - assert calculation_rate_ in self._valid_calculation_rates - calculation_rate_, kwargs = self._postprocess_kwargs( - calculation_rate=calculation_rate_, **kwargs + assert calculation_rate in self._valid_calculation_rates + calculation_rate, kwargs = self._postprocess_kwargs( + calculation_rate=calculation_rate, **kwargs ) - self._calculation_rate = calculation_rate_ - self._inputs: List[SupportsFloat] = [] - self._input_names: List[str] = [] - self._special_index = special_index - ugenlike_prototype = (UGen, Parameter) - for input_name in self._ordered_input_names: - input_value = None - if input_name in kwargs: - input_value = kwargs.pop(input_name) - # print(type(self).__name__, input_name, type(input_value)) - if isinstance(input_value, ugenlike_prototype): - assert len(input_value) == 1 - input_value = input_value[0] + self._calculation_rate = calculation_rate + self._special_index = int(special_index) + input_keys: List[Union[str, Tuple[str, int]]] = [] + inputs: List[Union[OutputProxy, float]] = [] + for key in self._ordered_keys: + if (value := kwargs.pop(key)) is None: + raise ValueError(key) + if isinstance(value, UGenSerializable): + serialized = value.serialize() + if any(isinstance(x, UGenVector) for x in serialized): + raise ValueError(key, serialized) + value = cast(Sequence[Union[SupportsFloat, UGenScalar]], serialized) + if isinstance(value, Sequence): + if key not in self._unexpanded_keys: + raise ValueError(key, value) + iterator: Iterable[ + Tuple[Optional[int], Union[SupportsFloat, UGenScalar]] + ] = ((i, v) for i, v in enumerate(value)) else: - try: - input_value = float(input_value) # type: ignore - except TypeError: - pass - if self._is_unexpanded_input_name(input_name): - if not isinstance(input_value, Sequence): - input_value = (input_value,) - if isinstance(input_value, Sequence): - input_value = tuple(input_value) - elif not self._is_valid_input(input_value): - raise ValueError(input_name, input_value) - elif not self._is_valid_input(input_value): - raise ValueError(input_name, input_value) - self._configure_input(input_name, input_value) + iterator = ((None, v) for v in [value]) + i: Optional[int] + for i, x in iterator: + if isinstance(x, SupportsFloat): + inputs.append(float(x)) + elif isinstance(x, ConstantProxy): + inputs.append(float(x.value)) + elif isinstance(x, OutputProxy): + inputs.append(x) + else: + raise ValueError(key, x) + input_keys.append((key, i) if i is not None else key) if kwargs: - raise ValueError(kwargs) - assert all(isinstance(_, (OutputProxy, float)) for _ in self.inputs) - self._validate_inputs() - self._uuid = None + raise ValueError(type(self).__name__, kwargs) + self._inputs = tuple(inputs) + self._input_keys = tuple(input_keys) + self._uuid: Optional[uuid.UUID] = None if SynthDefBuilder._active_builders: builder = SynthDefBuilder._active_builders[-1] self._uuid = builder._uuid - builder._add_ugens(self) - self._check_inputs_share_same_uuid() - - ### SPECIAL METHODS ### - - def __getitem__(self, i): - """ - Gets output proxy at index `i`. - - :: + builder._add_ugen(self) + for input_ in self._inputs: + if isinstance(input_, OutputProxy) and input_.ugen._uuid != self._uuid: + raise SynthDefError("UGen input in different scope") + self._values = tuple( + OutputProxy(ugen=self, index=i) + for i in range(getattr(self, "_channel_count", 1)) + ) - >>> ugen = supriya.ugens.SinOsc.ar().source - >>> ugen[0] - + @overload + def __getitem__(self, i: int) -> OutputProxy: + pass - Returns output proxy. - """ - return self._get_output_proxy(i) + @overload + def __getitem__(self, i: slice) -> UGenVector: + pass - def __len__(self): - """ - Gets number of ugen outputs. + def __getitem__(self, i): + if isinstance(i, int): + return self._values[i] + return UGenVector(*self._values[i]) - Returns integer. - """ - return getattr(self, "_channel_count", self._default_channel_count) + def __len__(self) -> int: + return self._channel_count def __repr__(self): - """ - Gets interpreter representation of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar().source - >>> repr(ugen) - '' - - :: - - >>> ugen = supriya.ugens.WhiteNoise.kr().source - >>> repr(ugen) - '' - - :: - - >>> ugen = supriya.ugens.Rand.ir().source - >>> repr(ugen) - '' - - Returns string. - """ return f"<{type(self).__name__}.{self.calculation_rate.token}()>" - ### PRIVATE METHODS ### - - @staticmethod - def _as_audio_rate_input(expr): - from . import DC, K2A, Silence - - if isinstance(expr, (int, float)): - if expr == 0: - return Silence.ar() - return DC.ar(expr) - elif isinstance(expr, (UGen, OutputProxy)): - if expr.calculation_rate == CalculationRate.AUDIO: - return expr - return K2A.ar(source=expr) - elif isinstance(expr, Iterable): - return UGenVector(*(UGen._as_audio_rate_input(x) for x in expr)) - raise ValueError(expr) - - def _add_constant_input(self, name, value): - self._inputs.append(float(value)) - self._input_names.append(name) - - def _add_ugen_input(self, name, ugen, output_index=None): - if isinstance(ugen, OutputProxy): - output_proxy = ugen - else: - output_proxy = OutputProxy(source=ugen, output_index=output_index) - self._inputs.append(output_proxy) - self._input_names.append(name) - - def _check_inputs_share_same_uuid(self): - for input_ in self.inputs: - if not isinstance(input_, OutputProxy): + def _eliminate( + self, sort_bundles: Dict["UGen", "SynthDefBuilder.SortBundle"] + ) -> None: + if not (sort_bundle := sort_bundles.get(self)) or sort_bundle.descendants: + return + del sort_bundles[self] + for antecedent in tuple(sort_bundle.antecedents): + if not (antecedent_bundle := sort_bundles.get(antecedent)): continue - if input_.source._uuid != self._uuid: - message = "UGen input in different scope: {!r}" - message = message.format(input_.source) - raise ValueError(message) - - def _check_rate_same_as_first_input_rate(self): - first_input_rate = CalculationRate.from_expr(self.inputs[0]) - return self.calculation_rate == first_input_rate - - def _check_range_of_inputs_at_audio_rate(self, start=None, stop=None): - if self.calculation_rate != CalculationRate.AUDIO: - return True - for input_ in self.inputs[start:stop]: - calculation_rate = CalculationRate.from_expr(input_) - if calculation_rate != CalculationRate.AUDIO: - return False - return True - - def _configure_input(self, name, value): - ugen_prototype = (OutputProxy, Parameter, UGen) - if hasattr(value, "__float__"): - self._add_constant_input(name, float(value)) - elif isinstance(value, ugen_prototype): - self._add_ugen_input(name, value._get_source(), value._get_output_number()) - elif isinstance(value, Sequence): - if name not in self._unexpanded_input_names: - raise ValueError(name, self._unexpanded_input_names) - for i, x in enumerate(value): - if hasattr(x, "__float__"): - self._add_constant_input((name, i), float(x)) - elif isinstance(x, ugen_prototype): - self._add_ugen_input( - (name, i), x._get_source(), x._get_output_number() - ) - else: - raise Exception("{!r} {!r}".format(value, x)) - else: - raise ValueError(f"Invalid input: {value!r}") + antecedent_bundle.descendants.remove(self) + antecedent._optimize(sort_bundles) @classmethod - def _expand_dictionary(cls, kwargs, unexpanded_input_names=()): - """ - Expands a dictionary into multichannel dictionaries. - - :: - - >>> dictionary = {"foo": 0, "bar": [1, 2], "baz": [3, 4, 5]} - >>> result = UGen._expand_dictionary(dictionary) - >>> for x in result: - ... sorted(x.items()) - ... - [('bar', 1), ('baz', 3), ('foo', 0)] - [('bar', 2), ('baz', 4), ('foo', 0)] - [('bar', 1), ('baz', 5), ('foo', 0)] - - :: - - >>> dictionary = {"bus": [8, 9], "source": [1, 2, 3]} - >>> result = UGen._expand_dictionary( - ... dictionary, - ... unexpanded_input_names=("source",), - ... ) - >>> for x in result: - ... sorted(x.items()) - ... - [('bus', 8), ('source', [1, 2, 3])] - [('bus', 9), ('source', [1, 2, 3])] - """ + def _expand_params( + cls, + params: Dict[str, "UGenRecursiveInput"], + unexpanded_keys: Optional[Iterable[str]] = None, + ) -> "UGenRecursiveParams": + unexpanded_keys_ = set(unexpanded_keys or ()) size = 0 - for k, v in kwargs.items(): - if isinstance(v, (OutputProxy, str)) or not hasattr(v, "__len__"): + for key, value in params.items(): + if isinstance(value, UGenSerializable): + params[key] = value = value.serialize() + # Scalars + if isinstance(value, (SupportsFloat, UGenScalar)): continue - elif k in unexpanded_input_names: - if all( - hasattr(x, "__len__") and not isinstance(x, OutputProxy) for x in v - ): - size = max(size, len(v)) + elif isinstance(value, Sequence): + # Unexpanded, but need to reach bottom layer + if key in unexpanded_keys_: + if isinstance(value, Sequence) and any( + isinstance(x, Sequence) for x in value + ): + size = max(size, len(value)) + else: + # Reached the bottom layer + continue + # Expanded else: - continue - else: - size = max(size, len(v)) + size = max(size, len(value)) if not size: - return [kwargs] + return cast(Dict[str, Union[UGenScalarInput, UGenVectorInput]], params) results = [] for i in range(size): - new_kwargs = {} - for k, v in kwargs.items(): - if isinstance(v, (OutputProxy, str)) or not hasattr(v, "__len__"): - new_kwargs[k] = v - elif k in unexpanded_input_names: - if all( - hasattr(x, "__len__") and not isinstance(x, OutputProxy) - for x in v - ): - new_kwargs[k] = v[i % len(v)] + new_params: Dict[str, UGenRecursiveInput] = {} + for key, value in params.items(): + # Redundant but satiates MyPy + if isinstance(value, UGenSerializable): + value = value.serialize() + if isinstance(value, (SupportsFloat, UGenScalar)): + new_params[key] = value + elif isinstance(value, Sequence): + if key in unexpanded_keys_: + if isinstance(value, Sequence) and all( + isinstance(x, (SupportsFloat, UGenScalar)) for x in value + ): + new_params[key] = value + else: + new_params[key] = value[i % len(value)] else: - new_kwargs[k] = v - else: - try: - new_kwargs[k] = v[i % len(v)] - except TypeError: - new_kwargs[k] = v - results.extend(cls._expand_dictionary(new_kwargs, unexpanded_input_names)) + new_params[key] = value[i % len(value)] + results.append( + cls._expand_params(new_params, unexpanded_keys=unexpanded_keys) + ) return results - def _get_done_action(self): - if "done_action" not in self._ordered_input_names: - return None - return DoneAction.from_expr(int(self.done_action)) - - @staticmethod - def _get_method_for_rate(cls, calculation_rate): - calculation_rate = CalculationRate.from_expr(calculation_rate) - if calculation_rate == CalculationRate.AUDIO: - return cls.ar - elif calculation_rate == CalculationRate.CONTROL: - return cls.kr - elif calculation_rate == CalculationRate.SCALAR: - if hasattr(cls, "ir"): - return cls.ir - return cls.kr - return cls.new - - def _get_output_number(self): - return 0 - - def _get_outputs(self): - return [self.calculation_rate] * len(self) - - def _get_source(self): - return self - - def _is_unexpanded_input_name(self, input_name): - if self._unexpanded_input_names: - if input_name in self._unexpanded_input_names: - return True - return False - - def _is_valid_input(self, input_value): - if isinstance(input_value, OutputProxy): - return True - elif hasattr(input_value, "__float__"): - return True - return False - @classmethod - def _new_expanded(cls, **kwargs) -> Union[OutputProxy, UGenVector]: - output_proxies = [] - dictionaries = UGen._expand_dictionary( - kwargs, - unexpanded_input_names=cls._unexpanded_input_names, + def _new_expanded( + cls, + *, + calculation_rate: CalculationRateLike, + special_index: int = 0, + **kwargs: "UGenRecursiveInput", + ) -> UGenOperable: + """ + ( + var a, b, c, d; + a = SinOsc.ar([1, 2, 3]); + b = Pan2.ar(a); + c = Pan2.ar(b); + d = (Pan2.ar(c) * 2); + a.postln; + b.postln; + c.postln; + d.postln; ) - for input_dict in dictionaries: - if isinstance(ugen := cls._new_single(**input_dict), OutputProxy): - output_proxies.append(ugen) - elif len(ugen): - output_proxies.extend(ugen[:]) - else: - output_proxies.append(ugen) - if len(output_proxies) == 1: - return output_proxies[0] - return UGenVector(*output_proxies) + [ a SinOsc, a SinOsc, a SinOsc ] + [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ] + [ [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ], [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ], [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ] ] + [ [ [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ], [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ] ], [ [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ], [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ] ], [ [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ], [ [ a BinaryOpUGen, a BinaryOpUGen ], [ a BinaryOpUGen, a BinaryOpUGen ] ] ] ] + """ + + def recurse( + all_expanded_params: UGenRecursiveParams, + ) -> UGenOperable: + if ( + not isinstance(all_expanded_params, dict) + and len(all_expanded_params) == 1 + ): + all_expanded_params = all_expanded_params[0] + if isinstance(all_expanded_params, dict): + return cls._new_single( + calculation_rate=calculation_rate, + special_index=special_index, + **all_expanded_params, + ) + return UGenVector( + *(recurse(expanded_params) for expanded_params in all_expanded_params) + ) - @classmethod - def _new_single(cls, **kwargs): - return cls(**kwargs) + return recurse(cls._expand_params(kwargs, unexpanded_keys=cls._unexpanded_keys)) - def _optimize_graph(self, sort_bundles): - if self._is_pure: - self._perform_dead_code_elimination(sort_bundles) + @classmethod + def _new_single( + cls, + *, + calculation_rate: CalculationRateLike = None, + special_index: SupportsInt = 0, + **kwargs: Union["UGenScalarInput", "UGenVectorInput"], + ) -> UGenOperable: + if ( + len( + ugen := cls( + calculation_rate=CalculationRate.from_expr(calculation_rate), + special_index=special_index, + **kwargs, + ) + ) + == 1 + ): + return ugen[0] + return ugen - def _perform_dead_code_elimination(self, sort_bundles): - sort_bundle = sort_bundles.get(self, None) - if not sort_bundle or sort_bundle.descendants: + def _optimize( + self, sort_bundles: Dict["UGen", "SynthDefBuilder.SortBundle"] + ) -> None: + if not self._is_pure: return - del sort_bundles[self] - for antecedent in tuple(sort_bundle.antecedents): - antecedent_bundle = sort_bundles.get(antecedent, None) - if not antecedent_bundle: - continue - antecedent_bundle.descendants.remove(self) - antecedent._optimize_graph(sort_bundles) + self._eliminate(sort_bundles) def _postprocess_kwargs( - self, *, calculation_rate: CalculationRate, **kwargs - ) -> Tuple[CalculationRate, Dict[str, Any]]: + self, + *, + calculation_rate: CalculationRate, + **kwargs: Union["UGenScalarInput", "UGenVectorInput"], + ) -> Tuple[CalculationRate, Dict[str, Union["UGenScalarInput", "UGenVectorInput"]]]: return calculation_rate, kwargs - def _validate_inputs(self): - pass - - ### PRIVATE PROPERTIES ### - @property - def _has_done_action(self) -> bool: - return "done_action" in self._ordered_input_names - - ### PUBLIC PROPERTIES ### - - @property - def calculation_rate(self): - """ - Gets calculation-rate of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar( - ... frequency=supriya.ugens.WhiteNoise.kr(), - ... phase=0.5, - ... ) - >>> ugen.calculation_rate - CalculationRate.AUDIO - - Returns calculation-rate. - """ + def calculation_rate(self) -> CalculationRate: return self._calculation_rate @property @@ -4034,24 +5156,7 @@ def has_done_flag(self) -> bool: return self._has_done_flag @property - def inputs(self): - """ - Gets inputs of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar( - ... frequency=supriya.ugens.WhiteNoise.kr(), - ... phase=0.5, - ... ).source - >>> for input_ in ugen.inputs: - ... input_ - ... - - 0.5 - - Returns tuple. - """ + def inputs(self) -> Tuple[Union[OutputProxy, float], ...]: return tuple(self._inputs) @property @@ -4063,151 +5168,62 @@ def is_output_ugen(self) -> bool: return self._is_output @property - def outputs(self) -> Tuple[OutputProxy, ...]: - """ - Gets outputs of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar( - ... frequency=supriya.ugens.WhiteNoise.kr(), - ... phase=0.5, - ... ).source - >>> ugen.outputs - (CalculationRate.AUDIO,) - - Returns tuple. - """ - return tuple(self._get_outputs()) - - @property - def signal_range(self): - """ - Gets signal range of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar() - >>> ugen.signal_range - SignalRange.BIPOLAR - - A bipolar signal range indicates that the ugen generates signals above and below - zero. - - A unipolar signal range indicates that the ugen only generates signals of 0 or - greater. - - Returns signal range. - """ + def signal_range(self) -> SignalRange: return self._signal_range @property - def special_index(self): - """ - Gets special index of ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar( - ... frequency=supriya.ugens.WhiteNoise.kr(), - ... phase=0.5, - ... ).source - >>> ugen.special_index - 0 - - The `special index` of most ugens will be 0. SuperColliders's synth definition - file format uses the special index to store the operator id for binary and unary - operator ugens, and the parameter index of controls. - - Returns integer. - """ + def special_index(self) -> int: return self._special_index -UGenOperand: TypeAlias = Union[ - SupportsFloat, UGenOperable, Sequence[Union[SupportsFloat, UGenOperable]] -] - -UGenInitScalarParam: TypeAlias = Union[SupportsFloat, OutputProxy] - -UGenInitVectorParam: TypeAlias = Union[Sequence[UGenInitScalarParam], UGenSerializable] - -UGenRateScalarParam: TypeAlias = Union[SupportsFloat, UGenOperable, UGenSerializable] +UGenScalarInput: TypeAlias = Union[SupportsFloat, UGenScalar] +UGenVectorInput: TypeAlias = Union[UGenSerializable, Sequence[UGenScalarInput]] -UGenRateVectorParam: TypeAlias = Union[ - UGenRateScalarParam, Sequence[UGenRateScalarParam] +UGenRecursiveInput: TypeAlias = Union[ + SupportsFloat, UGenOperable, UGenSerializable, Sequence["UGenRecursiveInput"] ] +UGenParams: TypeAlias = Dict[str, Union[UGenScalarInput, UGenVectorInput]] +UGenRecursiveParams: TypeAlias = Union[UGenParams, List["UGenRecursiveParams"]] + @ugen(is_pure=True) class UnaryOpUGen(UGen): - """ - A unary operator ugen, created by applying a unary operator to a ugen. - - :: - - >>> ugen = supriya.ugens.SinOsc.ar() - >>> unary_op_ugen = abs(ugen) - >>> unary_op_ugen - - - :: - - >>> unary_op_ugen.source.operator - UnaryOperator.ABSOLUTE_VALUE - """ - source = param() + def __init__( + self, + *, + calculation_rate: CalculationRate, + source: UGenScalarInput, + special_index: SupportsInt = 0, + ) -> None: + super().__init__( + calculation_rate=calculation_rate, + source=source, + special_index=special_index, + ) + def __repr__(self) -> str: return f"<{type(self).__name__}.{self.calculation_rate.token}({self.operator.name})>" @property def operator(self) -> UnaryOperator: - """ - Gets operator of UnaryOpUgen. - - :: - - >>> source = supriya.ugens.SinOsc.ar() - >>> unary_op_ugen = -source - >>> unary_op_ugen.source.operator - UnaryOperator.NEGATIVE - - Returns unary operator. - """ return UnaryOperator(self.special_index) @ugen(is_pure=True) class BinaryOpUGen(UGen): - """ - A binary operator ugen, created by applying a binary operator to two ugens. - - :: - - >>> left_operand = supriya.ugens.SinOsc.ar() - >>> right_operand = supriya.ugens.WhiteNoise.kr() - >>> binary_op_ugen = left_operand * right_operand - >>> binary_op_ugen - - - :: - - >>> binary_op_ugen.source.operator - BinaryOperator.MULTIPLICATION - """ - left = param() right = param() def __init__( self, *, - calculation_rate: CalculationRateLike, - left: UGenInitScalarParam, - right: UGenInitScalarParam, - special_index: int, + calculation_rate: CalculationRate, + left: UGenScalarInput, + right: UGenScalarInput, + special_index: SupportsInt = 0, ) -> None: super().__init__( calculation_rate=calculation_rate, @@ -4221,1304 +5237,843 @@ def __repr__(self) -> str: @classmethod def _new_single( - cls, calculation_rate=None, special_index=None, left=None, right=None, **kwargs - ): - a = left - b = right - if special_index == BinaryOperator.MULTIPLICATION: - if a == 0: - return 0 - if b == 0: - return 0 - if a == 1: - return b - if a == -1: - return -b - if b == 1: - return a - if b == -1: - return -a - if special_index == BinaryOperator.ADDITION: - if a == 0: - return b - if b == 0: - return a - if special_index == BinaryOperator.SUBTRACTION: - if a == 0: - return -b - if b == 0: - return a - if special_index == BinaryOperator.FLOAT_DIVISION: - if b == 1: - return a - if b == -1: - return -a - return cls( - calculation_rate=calculation_rate, - special_index=special_index, - left=a, - right=b, - ) - - @property - def operator(self) -> BinaryOperator: - """ - Gets operator of BinaryOpUgen. - - :: - - >>> left = supriya.ugens.SinOsc.ar() - >>> right = supriya.ugens.WhiteNoise.kr() - >>> binary_op_ugen = left / right - >>> binary_op_ugen.source.operator - BinaryOperator.FLOAT_DIVISION - - Returns binary operator. - """ - return BinaryOperator(self.special_index) - - -class PseudoUGen: - @abc.abstractmethod - def __init__(self): - raise NotImplementedError - - -@dataclasses.dataclass(unsafe_hash=True) -class Parameter(UGenOperable): - lag: Optional[float] = None - name: Optional[str] = None - parameter_rate: ParameterRate = cast(ParameterRate, ParameterRate.CONTROL) - value: Union[float, Tuple[float, ...]] = 0.0 - - def __post_init__(self): - try: - self.value = float(self.value) - except TypeError: - self.value = tuple(float(_) for _ in self.value) - self.parameter_rate = ParameterRate.from_expr(self.parameter_rate) - self._uuid = None - - ### SPECIAL METHODS ### - - def __getitem__(self, i): - return self._get_output_proxy(i) - - def __len__(self): - if isinstance(self.value, float): - return 1 - return len(self.value) - - ### PRIVATE METHODS ### - - def _get_source(self): - return self - - def _get_output_number(self): - return 0 - - def _optimize_graph(self, sort_bundles): - pass - - ### PUBLIC PROPERTIES ### - - @property - def calculation_rate(self): - if (name := self.parameter_rate.name) == "TRIGGER": - return cast(CalculationRate, CalculationRate.CONTROL) - return CalculationRate.from_expr(name) - - @property - def has_done_flag(self): - return False - - @property - def inputs(self): - return () - - @property - def signal_range(self): - SignalRange.BIPOLAR - - -class Control(UGen): - """ - A control-rate control ugen. - - Control ugens can be set and routed externally to interact with a running synth. - Controls are created from the parameters of a synthesizer definition, and typically - do not need to be created by hand. - """ - - ### INITIALIZER ### - - def __init__(self, parameters, calculation_rate=None, starting_control_index=0): - coerced_parameters = [] - for parameter in parameters: - if not isinstance(parameter, Parameter): - parameter = Parameter(name=parameter, value=0) - coerced_parameters.append(parameter) - self._parameters = tuple(coerced_parameters) - self._channel_count = len(self) - UGen.__init__( - self, - calculation_rate=calculation_rate, - special_index=starting_control_index, - ) - - ### SPECIAL METHODS ### - - def __getitem__(self, i): - """ - Gets output proxy at `i`, via index or control name. - - Returns output proxy. - """ - if isinstance(i, int): - if len(self) == 1: - return OutputProxy(source=self, output_index=0) - return OutputProxy(source=self, output_index=i) - else: - return self[self._get_control_index(i)] - - def __len__(self): - """ - Gets number of ugen outputs. - - Equal to the number of control names. - - Returns integer. - """ - return sum(len(_) for _ in self.parameters) - - ### PRIVATE METHODS ### - - def _get_control_index(self, control_name): - for i, parameter in enumerate(self._parameters): - if parameter.name == control_name: - return i - raise ValueError - - def _get_outputs(self): - return [self.calculation_rate] * len(self) - - def _get_parameter_output_proxies(self): - output_proxies = [] - for parameter in self.parameters: - output_proxies.extend(parameter) - return output_proxies - - ### PUBLIC PROPERTIES ### - - @property - def controls(self): - """ - Gets controls of control ugen. - - Returns ugen graph. - """ - if len(self.parameters) == 1: - result = self - else: - result = [OutputProxy(self, i) for i in range(len(self.parameters))] - return result - - @property - def parameters(self): - """ - Gets control names associated with control. - - Returns tuple. - """ - return self._parameters - - @property - def starting_control_index(self): - """ - Gets starting control index of control ugen. - - Equivalent to the control ugen's special index. - - Returns integer. - """ - return self._special_index - - -class AudioControl(Control): - """ - A trigger-rate control ugen. - """ - - def __init__(self, parameters, calculation_rate=None, starting_control_index=0): - Control.__init__( - self, - parameters, - calculation_rate=CalculationRate.AUDIO, - starting_control_index=starting_control_index, - ) - - -class LagControl(Control): - """ - A lagged control-rate control ugen. - """ - - ### CLASS VARIABLES ### - - _ordered_input_names = collections.OrderedDict([("lags", None)]) - - _unexpanded_input_names = ("lags",) - - ### INITIALIZER ### - - def __init__(self, parameters, calculation_rate=None, starting_control_index=0): - coerced_parameters = [] - for parameter in parameters: - if not isinstance(parameter, Parameter): - parameter = Parameter(name=parameter, value=0) - coerced_parameters.append(parameter) - self._parameters = tuple(coerced_parameters) - lags = [] - for parameter in self._parameters: - lag = parameter.lag or 0.0 - lags.extend([lag] * len(parameter)) - self._channel_count = len(self) - UGen.__init__( - self, - calculation_rate=calculation_rate, - special_index=starting_control_index, - lags=lags, - ) - - -class TrigControl(Control): - """ - A trigger-rate control ugen. - """ - - ### CLASS VARIABLES ### - - ### INITIALIZER ## - - def __init__(self, parameters, calculation_rate=None, starting_control_index=0): - Control.__init__( - self, - parameters, - calculation_rate=CalculationRate.CONTROL, - starting_control_index=starting_control_index, - ) - - -class SynthDef: - """ - A synth definition. - - :: - - >>> from supriya import SynthDefBuilder, ugens - >>> with SynthDefBuilder(frequency=440) as builder: - ... sin_osc = ugens.SinOsc.ar(frequency=builder["frequency"]) - ... out = ugens.Out.ar(bus=0, source=sin_osc) - ... - >>> synthdef = builder.build() - - :: - - >>> supriya.graph(synthdef) # doctest: +SKIP - - :: - - >>> from supriya import Server - >>> server = Server().boot() - - :: - - >>> _ = server.add_synthdefs(synthdef) - - :: - - >>> _ = server.free_synthdefs(synthdef) - - :: - - >>> _ = server.quit() - """ - - ### INITIALIZER ### - - def __init__( - self, - ugens: Sequence[Union[Parameter, UGen, OutputProxy]], - name: Optional[str] = None, - optimize: bool = True, - ) -> None: - self._name = name - ugens_: List[UGen] = list( - ugen.source if isinstance(ugen, OutputProxy) else ugen - for ugen in copy.deepcopy(ugens) - ) - assert all(isinstance(_, UGen) for _ in ugens_) - ugens_ = self._cleanup_pv_chains(ugens_) - ugens_ = self._cleanup_local_bufs(ugens_) - if optimize: - ugens_ = self._optimize_ugen_graph(ugens_) - ugens_ = self._sort_ugens_topologically(ugens_) - self._ugens = tuple(ugens_) - self._constants = self._collect_constants(self._ugens) - self._control_ugens = self._collect_control_ugens(self._ugens) - self._indexed_parameters = self._collect_indexed_parameters(self._control_ugens) - self._compiled_ugen_graph = SynthDefCompiler.compile_ugen_graph(self) - - ### SPECIAL METHODS ### - - def __eq__(self, expr) -> bool: - if not isinstance(expr, type(self)): - return False - if expr.name != self.name: - return False - if expr._compiled_ugen_graph != self._compiled_ugen_graph: - return False - return True - - def __graph__(self): - r""" - Graphs SynthDef. - - :: - - >>> from supriya.ugens import Out, SinOsc, SynthDefBuilder - - :: - - >>> with SynthDefBuilder(frequency=440) as builder: - ... sin_osc = SinOsc.ar(frequency=builder["frequency"]) - ... out = Out.ar(bus=0, source=sin_osc) - ... - >>> synthdef = builder.build() - >>> print(format(synthdef.__graph__(), "graphviz")) - digraph synthdef_... { - graph [bgcolor=transparent, - color=lightslategrey, - dpi=72, - fontname=Arial, - outputorder=edgesfirst, - overlap=prism, - penwidth=2, - rankdir=LR, - ranksep=1, - splines=spline, - style="dotted, rounded"]; - node [fontname=Arial, - fontsize=12, - penwidth=2, - shape=Mrecord, - style="filled, rounded"]; - edge [penwidth=2]; - ugen_0 [fillcolor=lightgoldenrod2, - label=" Control\n(control) | { { frequency:\n440.0 } }"]; - ugen_1 [fillcolor=lightsteelblue2, - label=" SinOsc\n(audio) | { { frequency | phase:\n0.0 } | { 0 } }"]; - ugen_2 [fillcolor=lightsteelblue2, - label=" Out\n(audio) | { { bus:\n0.0 | source } }"]; - ugen_0:f_1_0_0:e -> ugen_1:f_1_0_0:w [color=goldenrod]; - ugen_1:f_1_1_0:e -> ugen_2:f_1_0_1:w [color=steelblue]; - } - - Returns Graphviz graph. - """ - return SynthDefGrapher.graph(self) - - def __hash__(self) -> int: - hash_values = (type(self), self._name, self._compiled_ugen_graph) - return hash(hash_values) - - def __repr__(self) -> str: - return "<{}: {}>".format(type(self).__name__, self.actual_name) - - def __str__(self) -> str: - """ - Gets string representation of synth definition. - - :: - - >>> from supriya.ugens import Out, SinOsc, SynthDefBuilder - - :: - - >>> with supriya.SynthDefBuilder() as builder: - ... sin_one = supriya.ugens.SinOsc.ar() - ... sin_two = supriya.ugens.SinOsc.ar(frequency=443) - ... source = sin_one + sin_two - ... out = supriya.ugens.Out.ar(bus=0, source=source) - ... - >>> synthdef = builder.build(name="test") - - :: - - >>> supriya.graph(synthdef) # doctest: +SKIP - - :: - - >>> print(synthdef) - synthdef: - name: test - ugens: - - SinOsc.ar/0: - frequency: 440.0 - phase: 0.0 - - SinOsc.ar/1: - frequency: 443.0 - phase: 0.0 - - BinaryOpUGen(ADDITION).ar: - left: SinOsc.ar/0[0] - right: SinOsc.ar/1[0] - - Out.ar: - bus: 0.0 - source[0]: BinaryOpUGen(ADDITION).ar[0] - - Returns string. - """ - - def get_ugen_names() -> Dict[UGen, str]: - grouped_ugens: Dict[Tuple[Type[UGen], CalculationRate, int], List[UGen]] = ( - {} - ) - named_ugens: Dict[UGen, str] = {} - for ugen in self._ugens: - key = (type(ugen), ugen.calculation_rate, ugen.special_index) - grouped_ugens.setdefault(key, []).append(ugen) - for ugen in self._ugens: - parts = [type(ugen).__name__] - if isinstance(ugen, BinaryOpUGen): - ugen_op = BinaryOperator.from_expr(ugen.special_index) - parts.append("(" + ugen_op.name + ")") - elif isinstance(ugen, UnaryOpUGen): - ugen_op = UnaryOperator.from_expr(ugen.special_index) - parts.append("(" + ugen_op.name + ")") - parts.append("." + ugen.calculation_rate.token) - key = (type(ugen), ugen.calculation_rate, ugen.special_index) - related_ugens = grouped_ugens[key] - if len(related_ugens) > 1: - parts.append("/{}".format(related_ugens.index(ugen))) - named_ugens[ugen] = "".join(parts) - return named_ugens - - def get_parameter_name( - input_: Union[Control, Parameter], output_index: int = 0 - ) -> str: - if isinstance(input_, Parameter): - return ":{}".format(input_.name) - elif isinstance(input_, Control): - # Handle array-like parameters - value_index = 0 - for parameter in input_.parameters: - values = parameter.value - if isinstance(values, float): - values = [values] - for i in range(len(values)): - if value_index != output_index: - value_index += 1 - continue - elif len(values) == 1: - return ":{}".format(parameter.name) - else: - return ":{}[{}]".format(parameter.name, i) - return "" - - result = [ - "synthdef:", - f" name: {self.actual_name}", - " ugens:", - ] - ugen_dicts: List[Dict[str, Dict[str, Union[float, str]]]] = [] - named_ugens = get_ugen_names() - for ugen in self._ugens: - parameter_dict: Dict[str, Union[float, str]] = {} - ugen_name = named_ugens[ugen] - for input_name, input_ in zip(ugen._input_names, ugen._inputs): - if isinstance(input_name, str): - argument_name = input_name - else: - argument_name = f"{input_name[0]}[{input_name[1]}]" - if isinstance(input_, SupportsFloat): - value: Union[float, str] = float(input_) - else: - output_index = 0 - if isinstance(input_, OutputProxy): - output_index = input_.output_index - input_ = input_.source - input_name = named_ugens[input_] - value = "{}[{}{}]".format( - input_name, - output_index, - get_parameter_name(input_, output_index), - ) - parameter_dict[argument_name] = value - ugen_dicts.append({ugen_name: parameter_dict}) - for ugen_dict in ugen_dicts: - for ugen_name, parameter_dict in ugen_dict.items(): - if not parameter_dict: - result.append(f" - {ugen_name}: null") - continue - result.append(f" - {ugen_name}:") - for parameter_name, parameter_value in parameter_dict.items(): - result.append(f" {parameter_name}: {parameter_value}") - return "\n".join(result) - - ### PRIVATE METHODS ### - - @staticmethod - def _build_control_mapping(parameters): - control_mapping = {} - scalar_parameters = [] - trigger_parameters = [] - audio_parameters = [] - control_parameters = [] - mapping = { - ParameterRate.AUDIO: audio_parameters, - ParameterRate.CONTROL: control_parameters, - ParameterRate.SCALAR: scalar_parameters, - ParameterRate.TRIGGER: trigger_parameters, - } - for parameter in parameters: - mapping[parameter.parameter_rate].append(parameter) - for filtered_parameters in mapping.values(): - filtered_parameters.sort(key=lambda x: x.name) - control_ugens = [] - starting_control_index = 0 - if scalar_parameters: - control = Control( - parameters=scalar_parameters, - calculation_rate=CalculationRate.SCALAR, - starting_control_index=starting_control_index, - ) - control_ugens.append(control) - for parameter in scalar_parameters: - starting_control_index += len(parameter) - for i, output_proxy in enumerate(control._get_parameter_output_proxies()): - control_mapping[output_proxy] = control[i] - if trigger_parameters: - control = TrigControl( - parameters=trigger_parameters, - starting_control_index=starting_control_index, - ) - control_ugens.append(control) - for parameter in trigger_parameters: - starting_control_index += len(parameter) - for i, output_proxy in enumerate(control._get_parameter_output_proxies()): - control_mapping[output_proxy] = control[i] - if audio_parameters: - control = AudioControl( - parameters=audio_parameters, - starting_control_index=starting_control_index, - ) - control_ugens.append(control) - for parameter in audio_parameters: - starting_control_index += len(parameter) - for i, output_proxy in enumerate(control._get_parameter_output_proxies()): - control_mapping[output_proxy] = control[i] - if control_parameters: - if any(_.lag for _ in control_parameters): - control = LagControl( - parameters=control_parameters, - calculation_rate=CalculationRate.CONTROL, - starting_control_index=starting_control_index, - ) - else: - control = Control( - parameters=control_parameters, - calculation_rate=CalculationRate.CONTROL, - starting_control_index=starting_control_index, - ) - control_ugens.append(control) - for parameter in control_parameters: - starting_control_index += len(parameter) - for i, output_proxy in enumerate(control._get_parameter_output_proxies()): - control_mapping[output_proxy] = control[i] - control_ugens = tuple(control_ugens) - return control_ugens, control_mapping - - @staticmethod - def _build_input_mapping(ugens): - from . import PV_ChainUGen, PV_Copy - - input_mapping = {} - for ugen in ugens: - if not isinstance(ugen, PV_ChainUGen): - continue - if isinstance(ugen, PV_Copy): - continue - for i, input_ in enumerate(ugen.inputs): - if not isinstance(input_, OutputProxy): - continue - source = input_.source - if not isinstance(source, PV_ChainUGen): - continue - if source not in input_mapping: - input_mapping[source] = [] - input_mapping[source].append((ugen, i)) - return input_mapping - - @staticmethod - def _cleanup_local_bufs(ugens): - from . import LocalBuf, MaxLocalBufs - - local_bufs = [] - processed_ugens = [] - for ugen in ugens: - if isinstance(ugen, OutputProxy): - ugen = ugen.source - if isinstance(ugen, MaxLocalBufs): - continue - if isinstance(ugen, LocalBuf): - local_bufs.append(ugen) - processed_ugens.append(ugen) - if local_bufs: - max_local_bufs = MaxLocalBufs.ir(maximum=len(local_bufs)) - for local_buf in local_bufs: - inputs = list(local_buf.inputs[:2]) - inputs.append(max_local_bufs) - local_buf._inputs = tuple(inputs) - index = processed_ugens.index(local_bufs[0]) - processed_ugens[index:index] = [max_local_bufs.source] - return tuple(processed_ugens) - - @staticmethod - def _cleanup_pv_chains(ugens): - from . import LocalBuf, PV_Copy - - input_mapping = SynthDef._build_input_mapping(ugens) - for antecedent, descendants in input_mapping.items(): - if len(descendants) == 1: - continue - for descendant, input_index in descendants[:-1]: - fft_size = antecedent.fft_size - new_buffer = LocalBuf.ir(frame_count=fft_size) - pv_copy = PV_Copy.kr(pv_chain_a=antecedent, pv_chain_b=new_buffer) - inputs = list(descendant._inputs) - inputs[input_index] = pv_copy - descendant._inputs = tuple(inputs) - index = ugens.index(descendant) - replacement = [] - if isinstance(fft_size, UGenOperable): - replacement.append(fft_size) - replacement.extend([new_buffer.source, pv_copy.source]) - ugens[index:index] = replacement - return ugens - - @staticmethod - def _collect_constants(ugens) -> Tuple[float, ...]: - constants = [] - for ugen in ugens: - for input_ in ugen._inputs: - if not isinstance(input_, float): - continue - if input_ not in constants: - constants.append(input_) - return tuple(constants) - - @staticmethod - def _collect_control_ugens(ugens): - control_ugens = tuple(_ for _ in ugens if isinstance(_, Control)) - return control_ugens - - @staticmethod - def _collect_indexed_parameters(control_ugens) -> Sequence[Tuple[int, Parameter]]: - indexed_parameters = [] - parameters = {} - for control_ugen in control_ugens: - index = control_ugen.starting_control_index - for parameter in control_ugen.parameters: - parameters[parameter.name] = (index, parameter) - index += len(parameter) - for parameter_name in sorted(parameters): - indexed_parameters.append(parameters[parameter_name]) - return tuple(indexed_parameters) - - @staticmethod - def _extract_parameters(ugens): - parameters = set() - for ugen in ugens: - if isinstance(ugen, Parameter): - parameters.add(ugen) - ugens = tuple(ugen for ugen in ugens if ugen not in parameters) - parameters = tuple(sorted(parameters, key=lambda x: x.name)) - return ugens, parameters - - @staticmethod - def _initialize_topological_sort(ugens): - ugens = list(ugens) - sort_bundles = collections.OrderedDict() - width_first_antecedents = [] - # The UGens are in the order they were added to the SynthDef - # and that order already mostly places inputs before outputs. - # In sclang, the per-UGen width-first antecedents list is - # updated at the moment the UGen is added to the SynthDef. - # Because we don't store that state on UGens in supriya, we'll - # do it here. - for ugen in ugens: - sort_bundles[ugen] = UGenSortBundle(ugen, width_first_antecedents) - if ugen._is_width_first: - width_first_antecedents.append(ugen) - for ugen in ugens: - sort_bundle = sort_bundles[ugen] - sort_bundle._initialize_topological_sort(sort_bundles) - sort_bundle.descendants[:] = sorted( - sort_bundles[ugen].descendants, key=lambda x: ugens.index(ugen) - ) - return sort_bundles - - @staticmethod - def _optimize_ugen_graph(ugens): - sort_bundles = SynthDef._initialize_topological_sort(ugens) - for ugen in ugens: - ugen._optimize_graph(sort_bundles) - return tuple(sort_bundles) - - @staticmethod - def _remap_controls(ugens, control_mapping): - for ugen in ugens: - inputs = list(ugen.inputs) - for i, input_ in enumerate(inputs): - if input_ in control_mapping: - output_proxy = control_mapping[input_] - inputs[i] = output_proxy - ugen._inputs = tuple(inputs) - - @staticmethod - def _sort_ugens_topologically(ugens): - sort_bundles = SynthDef._initialize_topological_sort(ugens) - available_ugens = [] - for ugen in reversed(ugens): - sort_bundles[ugen]._make_available(available_ugens) - out_stack = [] - while available_ugens: - available_ugen = available_ugens.pop() - sort_bundles[available_ugen]._schedule( - available_ugens, out_stack, sort_bundles - ) - return out_stack - - ### PUBLIC METHODS ### - - def compile(self, use_anonymous_name=False) -> bytes: - synthdefs = [self] - result = compile_synthdefs(synthdefs, use_anonymous_names=use_anonymous_name) - return result - - ### PUBLIC PROPERTIES ### - - @property - def actual_name(self) -> str: - return self.name or self.anonymous_name - - @property - def anonymous_name(self) -> str: - md5 = hashlib.md5() - md5.update(self._compiled_ugen_graph) - anonymous_name = md5.hexdigest() - return anonymous_name + cls, + *, + calculation_rate: CalculationRateLike = None, + special_index: SupportsInt = 0, + **kwargs: Union[UGenScalarInput, UGenVectorInput], + ) -> UGenOperable: + def process( + left: Union[UGenScalar, float], + right: Union[UGenScalar, float], + ) -> Union[UGenOperable, float]: + if special_index == BinaryOperator.MULTIPLICATION: + if left == 0 or right == 0: + return ConstantProxy(0) + if left == 1: + return right + if left == -1: + return -right + if right == 1: + return left + if right == -1: + return -left + if special_index == BinaryOperator.ADDITION: + if left == 0: + return right + if right == 0: + return left + if special_index == BinaryOperator.SUBTRACTION: + if left == 0: + return -right + if right == 0: + return left + if special_index == BinaryOperator.FLOAT_DIVISION: + if right == 1: + return left + if right == -1: + return -left + return cls( + calculation_rate=max( + [ + CalculationRate.from_expr(left), + CalculationRate.from_expr(right), + ] + ), + special_index=special_index, + left=left, + right=right, + )[0] + + left = kwargs["left"] + right = kwargs["right"] + if not isinstance(left, (SupportsFloat, UGenScalar)): + raise ValueError(left) + if not isinstance(right, (SupportsFloat, UGenScalar)): + raise ValueError(right) + if isinstance( + result := process( + float(left) if isinstance(left, SupportsFloat) else left, + float(right) if isinstance(right, SupportsFloat) else right, + ), + SupportsFloat, + ): + return ConstantProxy(result) + return result @property - def audio_channel_count(self) -> int: - return max(self.audio_input_channel_count, self.audio_output_channel_count) + def operator(self) -> BinaryOperator: + return BinaryOperator(self.special_index) - @property - def audio_input_channel_count(self) -> int: - """ - Gets audio input channel count of synthdef. - :: +class PseudoUGen: + @abc.abstractmethod + def __init__(self): + raise NotImplementedError - >>> with supriya.SynthDefBuilder() as builder: - ... audio_in = supriya.ugens.In.ar(channel_count=1) - ... control_in = supriya.ugens.In.kr(channel_count=2) - ... sin = supriya.ugens.SinOsc.ar( - ... frequency=audio_in, - ... ) - ... source = audio_in * control_in[1] - ... audio_out = supriya.ugens.Out.ar(source=[source] * 4) - ... - >>> synthdef = builder.build() - :: +class Parameter(UGen): + def __init__( + self, + *, + name: Optional[str] = None, + value: Union[float, Sequence[float]], + rate: ParameterRate = ParameterRate.CONTROL, + lag: Optional[float] = None, + ) -> None: + if isinstance(value, SupportsFloat): + self.value: Tuple[float, ...] = (float(value),) + else: + self.value = tuple(float(x) for x in value) + self.name = name + self.lag = lag + self.rate = ParameterRate.from_expr(rate) + self._channel_count = len(self.value) + super().__init__(calculation_rate=CalculationRate.from_expr(self.rate)) + + def __eq__(self, other) -> bool: + return (type(self), self.name, self.value, self.rate, self.lag) == ( + type(other), + other.name, + other.value, + other.rate, + other.lag, + ) - >>> supriya.graph(synthdef) # doctest: +SKIP + def __hash__(self) -> int: + return hash((type(self), self.name, self.value, self.rate, self.lag)) - :: + def __repr__(self) -> str: + return f"<{type(self).__name__}.{self.calculation_rate.token}({self.name})>" - >>> synthdef.audio_input_channel_count - 1 - Returns integer. - """ - ugens = tuple( - _ for _ in self.input_ugens if _.calculation_rate == CalculationRate.AUDIO +class Control(UGen): + def __init__( + self, + *, + parameters: Sequence[Parameter], + calculation_rate: CalculationRate, + special_index: int = 0, + ) -> None: + self._parameters = tuple(parameters) + self._channel_count = sum(len(parameter) for parameter in self._parameters) + super().__init__( + calculation_rate=calculation_rate, + special_index=special_index, ) - if len(ugens) == 1: - return len(ugens[0]) - elif not ugens: - return 0 - raise ValueError @property - def audio_output_channel_count(self) -> int: - """ - Gets audio output channel count of synthdef. - - :: - - >>> with supriya.SynthDefBuilder() as builder: - ... audio_in = supriya.ugens.In.ar(channel_count=1) - ... control_in = supriya.ugens.In.kr(channel_count=2) - ... sin = supriya.ugens.SinOsc.ar( - ... frequency=audio_in, - ... ) - ... source = sin * control_in[0] - ... audio_out = supriya.ugens.Out.ar(source=[source] * 4) - ... - >>> synthdef = builder.build() - >>> print(synthdef) - synthdef: - name: ... - ugens: - - In.ar: - bus: 0.0 - - SinOsc.ar: - frequency: In.ar[0] - phase: 0.0 - - In.kr: - bus: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar: - left: SinOsc.ar[0] - right: In.kr[0] - - Out.ar: - bus: 0.0 - source[0]: BinaryOpUGen(MULTIPLICATION).ar[0] - source[1]: BinaryOpUGen(MULTIPLICATION).ar[0] - source[2]: BinaryOpUGen(MULTIPLICATION).ar[0] - source[3]: BinaryOpUGen(MULTIPLICATION).ar[0] + def parameters(self) -> Sequence[Parameter]: + return self._parameters - :: - >>> supriya.graph(synthdef) # doctest: +SKIP +class AudioControl(Control): + pass - :: - >>> synthdef.audio_output_channel_count - 4 +class LagControl(Control): + _ordered_keys = ("lags",) + _unexpanded_keys = frozenset(["lags"]) - Returns integer. - """ - ugens = tuple( - _ for _ in self.output_ugens if _.calculation_rate == CalculationRate.AUDIO + def __init__( + self, + *, + parameters: Sequence[Parameter], + calculation_rate: CalculationRate, + special_index: int = 0, + ) -> None: + self._parameters = tuple(parameters) + self._channel_count = sum(len(parameter) for parameter in self._parameters) + lags = [] + for parameter in parameters: + lags.extend([parameter.lag or 0.0] * len(parameter)) + UGen.__init__( + self, + calculation_rate=calculation_rate, + lags=lags, + special_index=special_index, ) - if len(ugens) == 1: - return len(ugens[0].source) - elif not ugens: - return 0 - raise ValueError - - @property - def constants(self) -> Tuple[float, ...]: - return self._constants - - @property - def control_ugens(self) -> List[UGen]: - return self._control_ugens - - @property - def control_channel_count(self) -> int: - return max(self.control_input_channel_count, self.control_output_channel_count) - - @property - def control_input_channel_count(self) -> int: - """ - Gets control input channel count of synthdef. - :: - >>> with supriya.SynthDefBuilder() as builder: - ... audio_in = supriya.ugens.In.ar(channel_count=1) - ... control_in = supriya.ugens.In.kr(channel_count=2) - ... sin = supriya.ugens.SinOsc.ar( - ... frequency=audio_in, - ... ) - ... source = audio_in * control_in[1] - ... audio_out = supriya.ugens.Out.ar(source=[source] * 4) - ... - >>> synthdef = builder.build() +class TrigControl(Control): + pass - :: - >>> supriya.graph(synthdef) # doctest: +SKIP +class SynthDefError(Exception): + pass - :: - >>> synthdef.control_input_channel_count - 2 +class SynthDef: - Returns integer. - """ - ugens = tuple( - _ for _ in self.input_ugens if _.calculation_rate == CalculationRate.CONTROL + def __init__(self, ugens: Sequence[UGen], name: Optional[str] = None) -> None: + if not ugens: + raise SynthDefError("No UGens provided") + self._ugens = tuple(ugens) + self._name = name + constants: List[float] = [] + for ugen in ugens: + for input_ in ugen.inputs: + if isinstance(input_, float) and input_ not in constants: + constants.append(input_) + self._constants = tuple(constants) + self._controls: Tuple[Control, ...] = tuple( + ugen for ugen in ugens if isinstance(ugen, Control) ) - if len(ugens) == 1: - return len(ugens[0]) - elif not ugens: - return 0 - raise ValueError + self._parameters: Dict[str, Tuple[Parameter, int]] = ( + self._collect_indexed_parameters(self._controls) + ) + self._compiled_graph = _compile_ugen_graph(self) - @property - def control_output_channel_count(self) -> int: - """ - Gets control output channel count of synthdef. + def __graph__(self) -> Graph: + r""" + Graph a SynthDef. :: - >>> with supriya.SynthDefBuilder() as builder: - ... audio_in = supriya.ugens.In.ar(channel_count=1) - ... control_in = supriya.ugens.In.kr(channel_count=2) - ... sin = supriya.ugens.SinOsc.ar( - ... frequency=audio_in, - ... ) - ... source = audio_in * control_in[1] - ... audio_out = supriya.ugens.Out.ar(source=[source] * 4) + >>> from supriya.ugens import Out, SinOsc, SynthDefBuilder + >>> with SynthDefBuilder(amplitude=1.0, bus=0, frequency=[440, 443]) as builder: + ... source = SinOsc.ar(frequency=builder["frequency"]) + ... out = Out.ar(bus=builder["bus"], source=source * builder["amplitude"]) ... - >>> synthdef = builder.build() :: - >>> supriya.graph(synthdef) # doctest: +SKIP + >>> synthdef = builder.build() :: - >>> synthdef.control_output_channel_count - 0 + >>> graph = synthdef.__graph__() + >>> print(format(graph, "graphviz")) + digraph synthdef_4e5d18af62c02b10252a62def13fb402 { + graph [bgcolor=transparent, + color=lightslategrey, + dpi=72, + fontname=Arial, + outputorder=edgesfirst, + overlap=prism, + penwidth=2, + rankdir=LR, + ranksep=1, + splines=spline, + style="dotted, rounded"]; + node [fontname=Arial, + fontsize=12, + penwidth=2, + shape=Mrecord, + style="filled, rounded"]; + edge [penwidth=2]; + ugen_0 [fillcolor=lightgoldenrod2, + label=" Control\n(control) | { { amplitude:\n1.0 | bus:\n0.0 | frequency[0]:\n440.0 | frequency[1]:\n443.0 } }"]; + ugen_1 [fillcolor=lightsteelblue2, + label=" SinOsc\n(audio) | { { frequency | phase:\n0.0 } | { 0 } }"]; + ugen_2 [fillcolor=lightsteelblue2, + label=" BinaryOpUGen\n[MULTIPLICATION]\n(audio) | { { left | right } | { 0 } }"]; + ugen_3 [fillcolor=lightsteelblue2, + label=" SinOsc\n(audio) | { { frequency | phase:\n0.0 } | { 0 } }"]; + ugen_4 [fillcolor=lightsteelblue2, + label=" BinaryOpUGen\n[MULTIPLICATION]\n(audio) | { { left | right } | { 0 } }"]; + ugen_5 [fillcolor=lightsteelblue2, + label=" Out\n(audio) | { { bus | source[0] | source[1] } }"]; + ugen_0:f_1_0_0:e -> ugen_2:f_1_0_1:w [color=goldenrod]; + ugen_0:f_1_0_0:e -> ugen_4:f_1_0_1:w [color=goldenrod]; + ugen_0:f_1_0_1:e -> ugen_5:f_1_0_0:w [color=goldenrod]; + ugen_0:f_1_0_2:e -> ugen_1:f_1_0_0:w [color=goldenrod]; + ugen_0:f_1_0_3:e -> ugen_3:f_1_0_0:w [color=goldenrod]; + ugen_1:f_1_1_0:e -> ugen_2:f_1_0_0:w [color=steelblue]; + ugen_2:f_1_1_0:e -> ugen_5:f_1_0_1:w [color=steelblue]; + ugen_3:f_1_1_0:e -> ugen_4:f_1_0_0:w [color=steelblue]; + ugen_4:f_1_1_0:e -> ugen_5:f_1_0_2:w [color=steelblue]; + } - Returns integer. """ - ugens = tuple( - _ - for _ in self.output_ugens - if _.calculation_rate == CalculationRate.CONTROL - ) - if len(ugens) == 1: - return len(ugens[0].source) - elif not ugens: - return 0 - raise ValueError - @property - def done_actions(self) -> List[DoneAction]: - done_actions = set() + def connect_nodes(ugen_node_mapping: Dict[UGen, Node]) -> None: + for ugen in self.ugens: + tail_node = ugen_node_mapping[ugen] + for i, input_ in enumerate(ugen.inputs): + if not isinstance(input_, OutputProxy): + continue + tail_field = tail_node["inputs"][i] + head_node = ugen_node_mapping[input_.ugen] + head_field = head_node["outputs"][input_.index] + edge = Edge(head_port_position="w", tail_port_position="e") + edge.attach(head_field, tail_field) + if input_.calculation_rate == CalculationRate.CONTROL: + edge.attributes["color"] = "goldenrod" + elif input_.calculation_rate == CalculationRate.AUDIO: + edge.attributes["color"] = "steelblue" + else: + edge.attributes["color"] = "salmon" + + def create_ugen_input_group( + ugen: UGen, ugen_index: int + ) -> Optional[RecordGroup]: + if not ugen.inputs: + return None + input_group = RecordGroup(name="inputs") + for i, (input_key, input_) in enumerate( + zip(ugen._input_keys, ugen._inputs) + ): + input_name = ( + input_key + if isinstance(input_key, str) + else f"{input_key[0]}[{input_key[1]}]" + ) + if isinstance(input_, float): + label = f"{input_name}:\\n{input_}" + else: + label = input_name + input_group.append( + RecordField( + label=label or None, name=f"ugen_{ugen_index}_input_{i}" + ) + ) + return input_group + + def create_ugen_node_mapping() -> Dict[UGen, Node]: + mapping = {} + for i, ugen in enumerate(self.ugens): + node = Node(name=f"ugen_{i}") + if ugen.calculation_rate == CalculationRate.CONTROL: + node.attributes["fillcolor"] = "lightgoldenrod2" + elif ugen.calculation_rate == CalculationRate.AUDIO: + node.attributes["fillcolor"] = "lightsteelblue2" + else: + node.attributes["fillcolor"] = "lightsalmon2" + node.append(create_ugen_title_field(ugen)) + group = RecordGroup() + if (input_group := create_ugen_input_group(ugen, i)) is not None: + group.append(input_group) + if (output_group := create_ugen_output_group(ugen, i)) is not None: + group.append(output_group) + node.append(group) + mapping[ugen] = node + return mapping + + def create_ugen_output_group( + ugen: UGen, ugen_index: int + ) -> Optional[RecordGroup]: + if not len(ugen): + return None + output_group = RecordGroup(name="outputs") + if isinstance(ugen, Control): + i = 0 + for parameter in ugen.parameters: + for j, value in enumerate(parameter.value): + if len(parameter.value) == 1: + label = f"{parameter.name}:\\n{value}" + else: + label = f"{parameter.name}[{j}]:\\n{value}" + output_group.append( + RecordField( + label=label, name=f"ugen_{ugen_index}_output_{i}" + ) + ) + i += 1 + else: + for i, output in enumerate(ugen): + output_group.append( + RecordField(label=str(i), name=f"ugen_{ugen_index}_output_{i}") + ) + return output_group + + def create_ugen_title_field(ugen: UGen) -> RecordField: + name = type(ugen).__name__ + calculation_rate = ugen.calculation_rate.name.lower() + if isinstance(ugen, BinaryOpUGen): + label = f"{name}\\n[{BinaryOperator(ugen.special_index).name}]\\n({calculation_rate})" + elif isinstance(ugen, UnaryOpUGen): + label = f"{name}\\n[{UnaryOperator(ugen.special_index).name}]\\n({calculation_rate})" + else: + label = f"{name}\\n({calculation_rate})" + return RecordField(label=label) + + def style_graph(graph: Graph) -> None: + graph.attributes.update( + { + "bgcolor": "transparent", + "color": "lightslategrey", + "dpi": 72, + "fontname": "Arial", + "outputorder": "edgesfirst", + "overlap": "prism", + "penwidth": 2, + "rankdir": "LR", + "ranksep": 1, + "splines": "spline", + "style": ("dotted", "rounded"), + } + ) + graph.edge_attributes.update({"penwidth": 2}) + graph.node_attributes.update( + { + "fontname": "Arial", + "fontsize": 12, + "penwidth": 2, + "shape": "Mrecord", + "style": ("filled", "rounded"), + } + ) + + graph = Graph(name=f"synthdef_{self.actual_name}") + for node in sorted( + (ugen_node_mapping := create_ugen_node_mapping()).values(), + key=lambda x: x.name, + ): + graph.append(node) + connect_nodes(ugen_node_mapping) + style_graph(graph) + return graph + + def __hash__(self) -> int: + return hash((type(self), self._name, self._compiled_graph)) + + def __repr__(self) -> str: + return "<{}: {}>".format(type(self).__name__, self.actual_name) + + def __str__(self) -> str: + result = [ + "synthdef:", + f" name: {self.actual_name}", + " ugens:", + ] + grouped_ugens: Dict[Tuple[Type[UGen], CalculationRate, int], List[UGen]] = {} for ugen in self.ugens: - done_action = ugen._get_done_action() - if done_action is not None: - done_actions.add(done_action) - return sorted(done_actions) + key = (type(ugen), ugen.calculation_rate, ugen.special_index) + grouped_ugens.setdefault(key, []).append(ugen) + ugen_names: Dict[UGen, str] = {} + for ugen in self.ugens: + name = type(ugen).__name__ + if isinstance(ugen, BinaryOpUGen): + name += f"({BinaryOperator.from_expr(ugen.special_index).name})" + elif isinstance(ugen, UnaryOpUGen): + name += f"({UnaryOperator.from_expr(ugen.special_index).name})" + name += f".{ugen.calculation_rate.token}" + key = (type(ugen), ugen.calculation_rate, ugen.special_index) + if len(related_ugens := grouped_ugens[key]) > 1: + name += f"/{related_ugens.index(ugen)}" + ugen_names[ugen] = name + for ugen in self.ugens: + inputs: Dict[str, str] = {} + if isinstance(ugen, Control): + for parameter in ugen.parameters: + assert parameter.name is not None + if len(parameter.value) == 1: + inputs[parameter.name] = str(parameter.value[0]) + else: + for i, value in enumerate(parameter.value): + inputs[f"{parameter.name}[{i}]"] = str(value) + for input_key, input_ in zip(ugen._input_keys, ugen._inputs): + if isinstance(input_key, str): + input_name = input_key + if input_key in ugen._unexpanded_keys: + input_name += "[0]" + else: + input_name = f"{input_key[0]}[{input_key[1]}]" + if isinstance(input_, float): + input_value = str(input_) + else: + input_value = ugen_names[input_.ugen] + input_value += "[" + input_value += str(input_.index) + if isinstance(input_.ugen, Control): + value_index = 0 + for parameter in input_.ugen.parameters: + if input_.index < value_index + len(parameter): + break + else: + value_index += len(parameter) + input_value += f":{parameter.name}" + if len(parameter) > 1: + input_value += f"[{input_.index - value_index}]" + input_value += "]" + inputs[input_name] = input_value + if inputs: + result.append(f" - {ugen_names[ugen]}:") + for input_name, input_value in inputs.items(): + result.append(f" {input_name}: {input_value}") + else: + result.append(f" - {ugen_names[ugen]}: null") + return "\n".join(result) - @property - def has_gate(self) -> bool: - return "gate" in self.parameter_names + def _collect_indexed_parameters( + self, controls: Sequence[Control] + ) -> Dict[str, Tuple[Parameter, int]]: + mapping: Dict[str, Tuple[Parameter, int]] = {} + for control in controls: + index = control.special_index + for parameter in control.parameters: + assert parameter.name is not None + mapping[parameter.name] = (parameter, index) + index += len(parameter) + return mapping - @property - def indexed_parameters(self) -> Sequence[Tuple[int, Parameter]]: - return self._indexed_parameters + def compile(self, use_anonymous_name: bool = False) -> bytes: + return compile_synthdefs(self, use_anonymous_names=use_anonymous_name) @property - def input_ugens(self) -> Tuple[UGen, ...]: - return tuple(_ for _ in self.ugens if _.is_input_ugen) + def actual_name(self) -> str: + return self.name or self.anonymous_name @property - def name(self) -> Optional[str]: - return self._name + def anonymous_name(self) -> str: + return hashlib.md5(self._compiled_graph).hexdigest() @property - def output_ugens(self) -> Tuple[UGen, ...]: - return tuple(_ for _ in self.ugens if _.is_output_ugen) + def constants(self) -> Sequence[float]: + return self._constants @property - def parameters(self) -> Dict[Optional[str], Parameter]: - return { - parameter.name: parameter for index, parameter in self.indexed_parameters - } + def controls(self) -> Sequence[Control]: + return self._controls @property - def parameter_names(self) -> List[Optional[str]]: - return [parameter.name for index, parameter in self.indexed_parameters] + def has_gate(self) -> bool: + return "gate" in self.parameters @property - def ugens(self) -> Tuple[UGen, ...]: - return self._ugens - - -class UGenSortBundle: - ### INITIALIZER ### - - def __init__(self, ugen, width_first_antecedents): - self.antecedents = [] - self.descendants = [] - self.ugen = ugen - self.width_first_antecedents = tuple(width_first_antecedents) - - ### PRIVATE METHODS ### - - def _initialize_topological_sort(self, sort_bundles): - for input_ in self.ugen.inputs: - if isinstance(input_, OutputProxy): - input_ = input_.source - elif not isinstance(input_, UGen): - continue - input_sort_bundle = sort_bundles[input_] - if input_ not in self.antecedents: - self.antecedents.append(input_) - if self.ugen not in input_sort_bundle.descendants: - input_sort_bundle.descendants.append(self.ugen) - for input_ in self.width_first_antecedents: - input_sort_bundle = sort_bundles[input_] - if input_ not in self.antecedents: - self.antecedents.append(input_) - if self.ugen not in input_sort_bundle.descendants: - input_sort_bundle.descendants.append(self.ugen) - - def _make_available(self, available_ugens): - if not self.antecedents: - if self.ugen not in available_ugens: - available_ugens.append(self.ugen) - - def _schedule(self, available_ugens, out_stack, sort_bundles): - for ugen in reversed(self.descendants): - sort_bundle = sort_bundles[ugen] - sort_bundle.antecedents.remove(self.ugen) - sort_bundle._make_available(available_ugens) - out_stack.append(self.ugen) - - ### PUBLIC METHODS ### - - def clear(self) -> None: - self.antecedents[:] = [] - self.descendants[:] = [] - self.width_first_antecedents[:] = [] - - -class SuperColliderSynthDef: - ### INITIALIZER ### - - def __init__(self, name, body, rates=None): - self._name = name - self._body = body - self._rates = rates - - ### PRIVATE METHODS ### - - def _build_sc_input(self, directory_path): - input_ = [] - input_.append("a = SynthDef(") - input_.append(" \\{}, {{".format(self.name)) - for line in self.body.splitlines(): - input_.append(" " + line) - if self.rates: - input_.append("}}, {});".format(self.rates)) - else: - input_.append("});") - input_.append('"Defined SynthDef".postln;') - input_.append('a.writeDefFile("{}");'.format(directory_path)) - input_.append('"Wrote SynthDef".postln;') - input_.append("0.exit;") - input_ = "\n".join(input_) - return input_ - - ### PUBLIC METHODS ### - - def compile(self): - sclang_path = sclang.find() - with tempfile.TemporaryDirectory() as directory: - directory_path = pathlib.Path(directory) - sc_input = self._build_sc_input(directory_path) - sc_file_path = directory_path / f"{self.name}.sc" - sc_file_path.write_text(sc_input) - command = [str(sclang_path), "-D", str(sc_file_path)] - subprocess.run(command, timeout=10) - result = (directory_path / f"{self.name}.scsyndef").read_bytes() - return bytes(result) - - ### PUBLIC PROPERTIES ### + def indexed_parameters(self) -> Sequence[Tuple[int, Parameter]]: + return sorted((value[1], value[0]) for value in self.parameters.values()) @property - def body(self): - return self._body + def name(self) -> Optional[str]: + return self._name @property - def rates(self): - return self._rates + def parameters(self) -> Mapping[str, Tuple[Parameter, int]]: + return MappingProxyType(self._parameters) @property - def name(self): - return self._name + def ugens(self) -> Sequence[UGen]: + return self._ugens +# TODO: Convert to ContextVar instead _local = threading.local() _local._active_builders = [] class SynthDefBuilder: - """ - A SynthDef builder. - - :: - >>> from supriya import ParameterRate - >>> from supriya.ugens import Decay, Out, Parameter, SinOsc, SynthDefBuilder + class SortBundle(NamedTuple): + ugen: UGen + width_first_antecedents: Tuple[UGen, ...] + antecedents: List[UGen] + descendants: List[UGen] - :: + _active_builders: List["SynthDefBuilder"] = _local._active_builders - >>> builder = SynthDefBuilder( - ... frequency=440, - ... trigger=Parameter( - ... value=0, - ... parameter_rate=ParameterRate.TRIGGER, - ... ), - ... ) + def __init__(self, **kwargs: Union[Parameter, float, Sequence[float]]) -> None: + self._building = False + self._parameters: Dict[str, Parameter] = {} + self._ugens: List[UGen] = [] + self._uuid = uuid.uuid4() + for key, value in kwargs.items(): + if isinstance(value, Parameter): + self.add_parameter( + lag=value.lag, name=key, value=value.value, rate=value.rate + ) + else: + self.add_parameter(name=key, value=value) - :: + def __enter__(self) -> "SynthDefBuilder": + self._active_builders.append(self) + return self - >>> with builder: - ... sin_osc = SinOsc.ar( - ... frequency=builder["frequency"], - ... ) - ... decay = Decay.kr( - ... decay_time=0.5, - ... source=builder["trigger"], - ... ) - ... enveloped_sin = sin_osc * decay - ... out = Out.ar(bus=0, source=enveloped_sin) - ... + def __exit__(self, exc_type, exc_value, traceback): + self._active_builders.pop() - :: + def __getitem__(self, item: str) -> Parameter: + return self._parameters[item] - >>> synthdef = builder.build() - >>> supriya.graph(synthdef) # doctest: +SKIP - """ + def _add_ugen(self, ugen: UGen): + if ugen._uuid != self._uuid: + raise SynthDefError("UGen input in different scope") + if not self._building: + self._ugens.append(ugen) - ### CLASS VARIABLES ### + def _build_control_mapping(self, parameters: Sequence[Parameter]) -> Tuple[ + List[Control], + Dict[OutputProxy, OutputProxy], + ]: + parameter_mapping: Dict[ParameterRate, List[Parameter]] = {} + for parameter in parameters: + parameter_mapping.setdefault(parameter.rate, []).append(parameter) + for filtered_parameters in parameter_mapping.values(): + filtered_parameters.sort(key=lambda x: x.name or "") + controls: List[Control] = [] + control_mapping: Dict[OutputProxy, OutputProxy] = {} + starting_control_index = 0 + for parameter_rate in ParameterRate: + if not (filtered_parameters := parameter_mapping.get(parameter_rate, [])): + continue + if parameter_rate == ParameterRate.SCALAR: + control = Control( + calculation_rate=CalculationRate.SCALAR, + parameters=filtered_parameters, + special_index=starting_control_index, + ) + elif parameter_rate == ParameterRate.TRIGGER: + control = TrigControl( + calculation_rate=CalculationRate.CONTROL, + parameters=filtered_parameters, + special_index=starting_control_index, + ) + elif parameter_rate == ParameterRate.AUDIO: + control = AudioControl( + calculation_rate=CalculationRate.AUDIO, + parameters=filtered_parameters, + special_index=starting_control_index, + ) + elif any(parameter.lag for parameter in filtered_parameters): + control = LagControl( + calculation_rate=CalculationRate.CONTROL, + parameters=filtered_parameters, + special_index=starting_control_index, + ) + else: + control = Control( + calculation_rate=CalculationRate.CONTROL, + parameters=filtered_parameters, + special_index=starting_control_index, + ) + controls.append(control) + output_index = 0 + for parameter in filtered_parameters: + for output in parameter: + control_mapping[cast(OutputProxy, output)] = cast( + OutputProxy, control[output_index] + ) + output_index += 1 + starting_control_index += 1 + return controls, control_mapping - _active_builders: List["SynthDefBuilder"] = _local._active_builders + def _cleanup_local_bufs(self, ugens: List[UGen]) -> List[UGen]: + from . import LocalBuf, MaxLocalBufs - __slots__ = ("_name", "_parameters", "_ugens", "_uuid") + filtered_ugens: List[UGen] = [] + local_bufs: List[UGen] = [] + for ugen in ugens: + if isinstance(ugen, MaxLocalBufs): + continue # purging MaxLocalBufs from the graph so we can rebuild + if isinstance(ugen, LocalBuf): + local_bufs.append(ugen) + filtered_ugens.append(ugen) + if local_bufs: + max_local_bufs = cast(OutputProxy, MaxLocalBufs.ir(maximum=len(local_bufs))) + for local_buf in local_bufs: + inputs: List[Union[OutputProxy, float]] = list(local_buf.inputs[:2]) + inputs.append(max_local_bufs) + local_buf._inputs = tuple(inputs) + # Insert the MaxLocalBufs just before the first LocalBuf + index = filtered_ugens.index(local_bufs[0]) + filtered_ugens[index:index] = [max_local_bufs.ugen] + return filtered_ugens - ### INITIALIZER ### + def _cleanup_pv_chains(self, ugens: List[UGen]) -> List[UGen]: + from . import LocalBuf, PV_ChainUGen, PV_Copy - def __init__(self, name: Optional[str] = None, **kwargs) -> None: - self._name = name - self._uuid = uuid.uuid4() - self._parameters: Dict[Optional[str], Parameter] = collections.OrderedDict() - self._ugens: List[Union[Parameter, UGen]] = [] - for key, value in kwargs.items(): - self._add_parameter(key, value) + mapping: Dict[UGen, List[Tuple[UGen, int]]] = {} + for ugen in ugens: + if isinstance(ugen, PV_Copy) or not isinstance(ugen, PV_ChainUGen): + continue + for i, input_ in enumerate(ugen.inputs): + if not isinstance(input_, OutputProxy) or not isinstance( + input_.ugen, PV_ChainUGen + ): + continue + mapping.setdefault(input_.ugen, []).append((ugen, i)) - ### SPECIAL METHODS ### + for antecedent, descendant_pairs in mapping.items(): + if len(descendant_pairs) < 2: + continue + for descendant, input_index in descendant_pairs[:-1]: + fft_size = getattr(antecedent, "fft_size") + # Create a new LocalBuf and PV_Copy + new_buffer = cast(OutputProxy, LocalBuf.ir(frame_count=fft_size)) + pv_copy = cast( + OutputProxy, + PV_Copy.kr(pv_chain_a=antecedent, pv_chain_b=new_buffer), + ) + # Patch the PV_Copy into the descendant's inputs + inputs = list(descendant.inputs) + inputs[input_index] = pv_copy + descendant._inputs = tuple(inputs) + # Insert the new Localbuf and PV_Copy into the graph + index = ugens.index(descendant) + replacement = [] + if isinstance(fft_size, OutputProxy): + replacement.append(fft_size.ugen) + replacement.extend([new_buffer.ugen, pv_copy.ugen]) + ugens[index:index] = replacement + return ugens - def __enter__(self) -> "SynthDefBuilder": - SynthDefBuilder._active_builders.append(self) - return self + def _initiate_topological_sort(self, ugens: List[UGen]) -> Dict[UGen, SortBundle]: + sort_bundles: Dict[UGen, SynthDefBuilder.SortBundle] = {} + width_first_antecedents: List[UGen] = [] + # The UGens are in the order they were added to the SynthDef and that + # order already mostly places inputs before outputs. In sclang, the + # per-UGen width-first antecedents list is updated at the moment the + # UGen is added to the SynthDef. Because we don't store that state + # directly on UGens in Supriya, we'll do it here. + for ugen in ugens: + sort_bundles[ugen] = self.SortBundle( + antecedents=[], + descendants=[], + ugen=ugen, + width_first_antecedents=tuple(width_first_antecedents), + ) + if ugen._is_width_first: + width_first_antecedents.append(ugen) + for ugen, sort_bundle in sort_bundles.items(): + for input_ in ugen.inputs: + if not isinstance(input_, OutputProxy): + continue + if input_.ugen not in sort_bundle.antecedents: + sort_bundle.antecedents.append(input_.ugen) + if ( + ugen + not in (input_sort_bundle := sort_bundles[input_.ugen]).descendants + ): + input_sort_bundle.descendants.append(ugen) + for antecedent in sort_bundle.width_first_antecedents: + if antecedent not in sort_bundle.antecedents: + sort_bundle.antecedents.append(antecedent) + if ( + ugen + not in (input_sort_bundle := sort_bundles[antecedent]).descendants + ): + input_sort_bundle.descendants.append(ugen) + sort_bundle.descendants[:] = sorted( + sort_bundles[ugen].descendants, key=lambda x: ugens.index(ugen) + ) + return sort_bundles - def __exit__(self, exc_type, exc_value, traceback): - SynthDefBuilder._active_builders.pop() + def _optimize(self, ugens: List[UGen]) -> List[UGen]: + sort_bundles = self._initiate_topological_sort(ugens) + for ugen in ugens: + ugen._optimize(sort_bundles) + return list(sort_bundles) - def __getitem__(self, item: str) -> Parameter: - return self._parameters[item] + def _remap_controls( + self, ugens: List[UGen], control_mapping: Dict[OutputProxy, OutputProxy] + ) -> List[UGen]: + for ugen in ugens: + ugen._inputs = tuple( + ( + control_mapping.get(input_, input_) + if isinstance(input_, OutputProxy) + else input_ + ) + for input_ in ugen._inputs + ) + return ugens - ### PRIVATE METHODS ### + def _sort_topologically(self, ugens: List[UGen]) -> List[UGen]: + try: + sort_bundles = self._initiate_topological_sort(ugens) + except Exception: + print(ugens) + raise + available_ugens: List[UGen] = [] + output_stack: List[UGen] = [] + for ugen in reversed(ugens): + if not sort_bundles[ugen].antecedents and ugen not in available_ugens: + available_ugens.append(ugen) + while available_ugens: + available_ugen = available_ugens.pop() + for descendant in reversed(sort_bundles[available_ugen].descendants): + (descendant_sort_bundle := sort_bundles[descendant]).antecedents.remove( + available_ugen + ) + if ( + not descendant_sort_bundle.antecedents + and descendant_sort_bundle.ugen not in available_ugens + ): + available_ugens.append(descendant_sort_bundle.ugen) + output_stack.append(available_ugen) + return output_stack - def _add_ugens(self, ugen: Union[OutputProxy, Parameter, UGen]): - if isinstance(ugen, OutputProxy): - source = ugen.source - else: - source = ugen - if source._uuid != self._uuid: - raise ValueError - self._ugens.append(source) - - def _add_parameter(self, *args) -> Parameter: - # TODO: Refactor without *args for clarity - if 3 < len(args): - raise ValueError(args) - if len(args) == 1: - assert isinstance(args[0], Parameter) - name, value, parameter_rate = args[0].name, args[0], args[0].parameter_rate - elif len(args) == 2: - name, value = args - if isinstance(value, Parameter): - parameter_rate = value.parameter_rate - else: - parameter_rate = ParameterRate.CONTROL - if name.startswith("a_"): - parameter_rate = ParameterRate.AUDIO - elif name.startswith("i_"): - parameter_rate = ParameterRate.SCALAR - elif name.startswith("t_"): - parameter_rate = ParameterRate.TRIGGER - elif len(args) == 3: - name, value, parameter_rate = args - parameter_rate = ParameterRate.from_expr(parameter_rate) - else: - raise ValueError(args) - if not isinstance(value, Parameter): - parameter = Parameter(name=name, parameter_rate=parameter_rate, value=value) - else: - parameter = new(value, parameter_rate=parameter_rate, name=name) - assert parameter._uuid is None - parameter._uuid = self._uuid + def add_parameter( + self, + *, + name: str, + value: Union[float, Sequence[float]], + rate: Optional[ParameterRateLike] = ParameterRate.CONTROL, + lag: Optional[float] = None, + ) -> Parameter: + if name in self._parameters: + raise ValueError(name, value) + with self: + parameter = Parameter( + lag=lag, name=name, rate=ParameterRate.from_expr(rate), value=value + ) self._parameters[name] = parameter return parameter - ### PUBLIC METHODS ### - def build(self, name: Optional[str] = None, optimize: bool = True) -> SynthDef: - # Calling build() creates controls each time, so strip out - # previously created ones. This could be made cleaner by preventing - # Control subclasses from being aggregated into SynthDefBuilders in - # the first place. - self._ugens[:] = [ugen for ugen in self._ugens if not isinstance(ugen, Control)] - name = self.name or name - with self: - ugens: List[Union[Parameter, UGen]] = [] - ugens.extend(self._parameters.values()) - ugens.extend(self._ugens) - ugens = copy.deepcopy(ugens) - ugens, parameters = SynthDef._extract_parameters(ugens) - ( - control_ugens, - control_mapping, - ) = SynthDef._build_control_mapping(parameters) - SynthDef._remap_controls(ugens, control_mapping) - ugens = control_ugens + ugens - synthdef = SynthDef(ugens, name=name, optimize=optimize) - return synthdef - - def poll_ugen( - self, - ugen: UGen, - label: Optional[str] = None, - trigger: Optional[UGen] = None, - trigger_id: int = -1, - ) -> None: - from . import Impulse, Poll + """ + Build. - poll = Poll.new( - source=ugen, - label=label, - trigger=trigger or Impulse.kr(frequency=1), - trigger_id=trigger_id, - ) - self._add_ugens(poll) + :: + + >>> from supriya.ugens import Out, SinOsc, SynthDefBuilder + >>> with SynthDefBuilder(amplitude=1.0, bus=0, frequency=[440, 443]) as builder: + ... source = SinOsc.ar(frequency=builder["frequency"]) + ... source *= builder["amplitude"] + ... _ = Out.ar(bus=builder["bus"], source=source) + ... - ### PUBLIC PROPERTIES ### + :: - @property - def name(self) -> Optional[str]: - return self._name + >>> synthdef = builder.build() + >>> print(synthdef) + synthdef: + name: 4e5d18af62c02b10252a62def13fb402 + ugens: + - Control.kr: + amplitude: 1.0 + bus: 0.0 + frequency[0]: 440.0 + frequency[1]: 443.0 + - SinOsc.ar/0: + frequency: Control.kr[2:frequency[0]] + phase: 0.0 + - BinaryOpUGen(MULTIPLICATION).ar/0: + left: SinOsc.ar/0[0] + right: Control.kr[0:amplitude] + - SinOsc.ar/1: + frequency: Control.kr[3:frequency[1]] + phase: 0.0 + - BinaryOpUGen(MULTIPLICATION).ar/1: + left: SinOsc.ar/1[0] + right: Control.kr[0:amplitude] + - Out.ar: + bus: Control.kr[1:bus] + source[0]: BinaryOpUGen(MULTIPLICATION).ar/0[0] + source[1]: BinaryOpUGen(MULTIPLICATION).ar/1[0] + + """ + try: + self._building = True + with self: + ugens: List[UGen] = copy.deepcopy(self._ugens) + parameters: List[Parameter] = sorted( + [x for x in ugens if isinstance(x, Parameter)], + key=lambda x: x.name or "", + ) + ugens = [x for x in ugens if not isinstance(x, Parameter)] + controls, control_mapping = self._build_control_mapping(parameters) + ugens = controls + ugens + ugens = self._remap_controls(ugens, control_mapping) + ugens = self._cleanup_pv_chains(ugens) + ugens = self._cleanup_local_bufs(ugens) + ugens = self._sort_topologically(ugens) + if optimize: + ugens = self._optimize(ugens) + finally: + self._building = False + return SynthDef(ugens, name=name) def synthdef(*args: Union[str, Tuple[str, float]]) -> Callable[[Callable], SynthDef]: @@ -5545,7 +6100,10 @@ def synthdef(*args: Union[str, Tuple[str, float]]) -> Callable[[Callable], Synth synthdef: name: sine ugens: - - Control.kr: null + - Control.kr: + amp: 0.1 + freq: 440.0 + gate: 1.0 - SinOsc.ar: frequency: Control.kr[1:freq] phase: 0.0 @@ -5597,11 +6155,14 @@ def synthdef(*args: Union[str, Tuple[str, float]]) -> Callable[[Callable], Synth synthdef: name: sine ugens: - - AudioControl.ar: null + - AudioControl.ar: + freq: 440.0 - SinOsc.ar: frequency: AudioControl.ar[0:freq] phase: 0.0 - LagControl.kr: + amp: 0.1 + gate: 1.0 lags[0]: 0.5 lags[1]: 0.0 - BinaryOpUGen(MULTIPLICATION).ar/0: @@ -5656,8 +6217,9 @@ def inner(func): value = parameter.default if value is inspect._empty: value = 0.0 - parameter = Parameter(lag=lag, name=name, parameter_rate=rate, value=value) - kwargs[name] = builder._add_parameter(parameter) + kwargs[name] = builder.add_parameter( + name=name, lag=lag, rate=rate, value=value + ) with builder: func(**kwargs) return builder.build(name=func.__name__) @@ -5665,656 +6227,353 @@ def inner(func): return inner -class SynthDefGrapher: - r""" - Graphs SynthDefs. +def _compile_constants(synthdef: SynthDef) -> bytes: + return b"".join( + [ + _encode_unsigned_int_32bit(len(synthdef.constants)), + *(_encode_float(constant) for constant in synthdef.constants), + ] + ) - .. container:: example - :: +def _compile_parameters(synthdef: SynthDef) -> bytes: + result = [ + _encode_unsigned_int_32bit(sum(len(control) for control in synthdef.controls)) + ] + for control in synthdef.controls: + for parameter in control.parameters: + for value in parameter.value: + result.append(_encode_float(value)) + result.append(_encode_unsigned_int_32bit(len(synthdef.parameters))) + for name, (_, index) in synthdef.parameters.items(): + result.append(_encode_string(name) + _encode_unsigned_int_32bit(index)) + return b"".join(result) - >>> ugen_graph = supriya.ugens.LFNoise2.ar() - >>> result = ugen_graph.transpose([0, 3, 7]) - :: +def _compile_synthdef(synthdef: SynthDef, name: str) -> bytes: + return b"".join( + [ + _encode_string(name), + _compile_ugen_graph(synthdef), + ] + ) - >>> supriya.graph(result) # doctest: +SKIP - :: +def _compile_ugen(ugen: UGen, synthdef: SynthDef) -> bytes: + return b"".join( + [ + _encode_string(type(ugen).__name__), + _encode_unsigned_int_8bit(ugen.calculation_rate), + _encode_unsigned_int_32bit(len(ugen.inputs)), + _encode_unsigned_int_32bit(len(ugen)), + _encode_unsigned_int_16bit(int(ugen.special_index)), + *(_compile_ugen_input_spec(input_, synthdef) for input_ in ugen.inputs), + *( + _encode_unsigned_int_8bit(ugen.calculation_rate) + for _ in range(len(ugen)) + ), + ] + ) - >>> print(format(result.__graph__(), "graphviz")) - digraph synthdef_c481c3d42e3cfcee0267250247dab51f { - graph [bgcolor=transparent, - color=lightslategrey, - dpi=72, - fontname=Arial, - outputorder=edgesfirst, - overlap=prism, - penwidth=2, - rankdir=LR, - ranksep=1, - splines=spline, - style="dotted, rounded"]; - node [fontname=Arial, - fontsize=12, - penwidth=2, - shape=Mrecord, - style="filled, rounded"]; - edge [penwidth=2]; - ugen_0 [fillcolor=lightsteelblue2, - label=" LFNoise2\n(audio) | { { frequency:\n500.0 } | { 0 } }"]; - ugen_1 [fillcolor=lightsteelblue2, - label=" UnaryOpUGen\n[HZ_TO_MIDI]\n(audio) | { { source } | { 0 } }"]; - ugen_2 [fillcolor=lightsteelblue2, - label=" UnaryOpUGen\n[MIDI_TO_HZ]\n(audio) | { { source } | { 0 } }"]; - ugen_3 [fillcolor=lightsteelblue2, - label=" BinaryOpUGen\n[ADDITION]\n(audio) | { { left | right:\n3.0 } | { 0 } }"]; - ugen_4 [fillcolor=lightsteelblue2, - label=" UnaryOpUGen\n[MIDI_TO_HZ]\n(audio) | { { source } | { 0 } }"]; - ugen_5 [fillcolor=lightsteelblue2, - label=" BinaryOpUGen\n[ADDITION]\n(audio) | { { left | right:\n7.0 } | { 0 } }"]; - ugen_6 [fillcolor=lightsteelblue2, - label=" UnaryOpUGen\n[MIDI_TO_HZ]\n(audio) | { { source } | { 0 } }"]; - ugen_0:f_1_1_0:e -> ugen_1:f_1_0_0:w [color=steelblue]; - ugen_1:f_1_1_0:e -> ugen_2:f_1_0_0:w [color=steelblue]; - ugen_1:f_1_1_0:e -> ugen_3:f_1_0_0:w [color=steelblue]; - ugen_1:f_1_1_0:e -> ugen_5:f_1_0_0:w [color=steelblue]; - ugen_3:f_1_1_0:e -> ugen_4:f_1_0_0:w [color=steelblue]; - ugen_5:f_1_1_0:e -> ugen_6:f_1_0_0:w [color=steelblue]; - } - """ - ### PRIVATE METHODS ### +def _compile_ugens(synthdef: SynthDef) -> bytes: + return b"".join( + [ + _encode_unsigned_int_32bit(len(synthdef.ugens)), + *(_compile_ugen(ugen, synthdef) for ugen in synthdef.ugens), + ] + ) - @staticmethod - def _connect_nodes(synthdef, ugen_node_mapping): - for ugen in synthdef.ugens: - tail_node = ugen_node_mapping[ugen] - for i, input_ in enumerate(ugen.inputs): - if not isinstance(input_, OutputProxy): - continue - tail_field = tail_node["inputs"][i] - source = input_.source - head_node = ugen_node_mapping[source] - head_field = head_node["outputs"][input_.output_index] - edge = uqbar.graphs.Edge(head_port_position="w", tail_port_position="e") - edge.attach(head_field, tail_field) - if source.calculation_rate == CalculationRate.CONTROL: - edge.attributes["color"] = "goldenrod" - elif source.calculation_rate == CalculationRate.AUDIO: - edge.attributes["color"] = "steelblue" - else: - edge.attributes["color"] = "salmon" - - @staticmethod - def _create_ugen_input_group(ugen, ugen_index): - if not ugen.inputs: - return None - input_group = uqbar.graphs.RecordGroup(name="inputs") - for i, input_ in enumerate(ugen.inputs): - label = "" - input_name = None - if i < len(ugen._ordered_input_names): - input_name = tuple(ugen._ordered_input_names)[i] - if input_name: - # input_name = r'\n'.join(input_name.split('_')) - if isinstance(input_, float): - label = r"{}:\n{}".format(input_name, input_) - else: - label = input_name - elif isinstance(input_, float): - label = str(input_) - label = label or None - field = uqbar.graphs.RecordField( - label=label, name="ugen_{}_input_{}".format(ugen_index, i) - ) - input_group.append(field) - return input_group - - @staticmethod - def _create_ugen_node_mapping(synthdef): - ugen_node_mapping = {} - for ugen in synthdef.ugens: - ugen_index = synthdef.ugens.index(ugen) - node = uqbar.graphs.Node(name="ugen_{}".format(ugen_index)) - if ugen.calculation_rate == CalculationRate.CONTROL: - node.attributes["fillcolor"] = "lightgoldenrod2" - elif ugen.calculation_rate == CalculationRate.AUDIO: - node.attributes["fillcolor"] = "lightsteelblue2" - else: - node.attributes["fillcolor"] = "lightsalmon2" - title_field = SynthDefGrapher._create_ugen_title_field(ugen) - node.append(title_field) - group = uqbar.graphs.RecordGroup() - input_group = SynthDefGrapher._create_ugen_input_group(ugen, ugen_index) - if input_group is not None: - group.append(input_group) - output_group = SynthDefGrapher._create_ugen_output_group( - synthdef, ugen, ugen_index - ) - if output_group is not None: - group.append(output_group) - node.append(group) - ugen_node_mapping[ugen] = node - return ugen_node_mapping - - @staticmethod - def _create_ugen_output_group(synthdef, ugen, ugen_index): - if not ugen.outputs: - return None - output_group = uqbar.graphs.RecordGroup(name="outputs") - for i, output in enumerate(ugen.outputs): - label = str(i) - if isinstance(ugen, Control): - parameter_index = ugen.special_index + i - parameter = dict(synthdef.indexed_parameters)[parameter_index] - parameter_name = parameter.name - # parameter_name = r'\n'.join(parameter.name.split('_')) - label = r"{}:\n{}".format(parameter_name, parameter.value) - field = uqbar.graphs.RecordField( - label=label, name="ugen_{}_output_{}".format(ugen_index, i) - ) - output_group.append(field) - return output_group - - @staticmethod - def _create_ugen_title_field(ugen): - name = type(ugen).__name__ - calculation_rate = ugen.calculation_rate.name.lower() - label_template = r"{name}\n({calculation_rate})" - operator = None - if isinstance(ugen, BinaryOpUGen): - operator = BinaryOperator(ugen.special_index).name - label_template = r"{name}\n[{operator}]\n({calculation_rate})" - elif isinstance(ugen, UnaryOpUGen): - operator = UnaryOperator(ugen.special_index).name - label_template = r"{name}\n[{operator}]\n({calculation_rate})" - title_field = uqbar.graphs.RecordField( - label=label_template.format( - name=name, operator=operator, calculation_rate=calculation_rate - ) - ) - return title_field - - @staticmethod - def _style_graph(graph): - graph.attributes.update( - { - "bgcolor": "transparent", - "color": "lightslategrey", - "dpi": 72, - "fontname": "Arial", - "outputorder": "edgesfirst", - "overlap": "prism", - "penwidth": 2, - "rankdir": "LR", - "ranksep": 1, - "splines": "spline", - "style": ("dotted", "rounded"), - } - ) - graph.edge_attributes.update({"penwidth": 2}) - graph.node_attributes.update( - { - "fontname": "Arial", - "fontsize": 12, - "penwidth": 2, - "shape": "Mrecord", - "style": ("filled", "rounded"), - } - ) - ### PUBLIC METHODS ### +def _compile_ugen_graph(synthdef): + return b"".join( + [ + _compile_constants(synthdef), + _compile_parameters(synthdef), + _compile_ugens(synthdef), + _encode_unsigned_int_16bit(0), # no variants, please + ] + ) - @staticmethod - def graph(synthdef): - assert isinstance(synthdef, SynthDef) - graph = uqbar.graphs.Graph(name="synthdef_{}".format(synthdef.actual_name)) - ugen_node_mapping = SynthDefGrapher._create_ugen_node_mapping(synthdef) - for node in sorted(ugen_node_mapping.values(), key=lambda x: x.name): - graph.append(node) - SynthDefGrapher._connect_nodes(synthdef, ugen_node_mapping) - SynthDefGrapher._style_graph(graph) - return graph +def _compile_ugen_input_spec( + input_: Union[OutputProxy, float], synthdef: SynthDef +) -> bytes: + if isinstance(input_, float): + return _encode_unsigned_int_32bit(0xFFFFFFFF) + _encode_unsigned_int_32bit( + synthdef._constants.index(input_) + ) + else: + return _encode_unsigned_int_32bit( + synthdef._ugens.index(input_.ugen) + ) + _encode_unsigned_int_32bit(input_.index) -def compile_synthdef(synthdef, use_anonymous_names=False): - return SynthDefCompiler.compile_synthdef(synthdef, use_anonymous_names) +def _encode_string(value: str) -> bytes: + return struct.pack(">B", len(value)) + value.encode("ascii") -def compile_synthdefs(synthdefs, use_anonymous_names=False): - return SynthDefCompiler.compile_synthdefs(synthdefs, use_anonymous_names) +def _encode_float(value: float) -> bytes: + return struct.pack(">f", value) -def decompile_synthdef(value): - return SynthDefDecompiler.decompile_synthdef(value) +def _encode_unsigned_int_8bit(value: int) -> bytes: + return struct.pack(">B", value) -def decompile_synthdefs(value): - return SynthDefDecompiler.decompile_synthdefs(value) +def _encode_unsigned_int_16bit(value: int) -> bytes: + return struct.pack(">H", value) -class SynthDefCompiler: - @staticmethod - def compile_synthdef(synthdef, name): - result = SynthDefCompiler.encode_string(name) - result += synthdef._compiled_ugen_graph - return result - @staticmethod - def compile_parameters(synthdef): - result = [] - result.append( - SynthDefCompiler.encode_unsigned_int_32bit( - sum(len(_[1]) for _ in synthdef.indexed_parameters) - ) - ) - for control_ugen in synthdef.control_ugens: - for parameter in control_ugen.parameters: - value = parameter.value - if not isinstance(value, tuple): - value = (value,) - for x in value: - result.append(SynthDefCompiler.encode_float(x)) - result.append( - SynthDefCompiler.encode_unsigned_int_32bit(len(synthdef.indexed_parameters)) - ) - for index, parameter in synthdef.indexed_parameters: - name = parameter.name - result.append(SynthDefCompiler.encode_string(name)) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(index)) - return bytes().join(result) - - @staticmethod - def compile_synthdefs(synthdefs, use_anonymous_names=False): - def flatten(value): - if isinstance(value, Sequence) and not isinstance( - value, (bytes, bytearray) - ): - return bytes().join(flatten(x) for x in value) - return value - - result = [] - encoded_file_type_id = b"SCgf" - result.append(encoded_file_type_id) - encoded_file_version = SynthDefCompiler.encode_unsigned_int_32bit(2) - result.append(encoded_file_version) - encoded_synthdef_count = SynthDefCompiler.encode_unsigned_int_16bit( - len(synthdefs) - ) - result.append(encoded_synthdef_count) - for synthdef in synthdefs: - name = synthdef.name - if not name or use_anonymous_names: - name = synthdef.anonymous_name - result.append(SynthDefCompiler.compile_synthdef(synthdef, name)) - result = flatten(result) - result = bytes(result) - return result +def _encode_unsigned_int_32bit(value: int) -> bytes: + return struct.pack(">I", value) - @staticmethod - def compile_ugen(ugen, synthdef): - outputs = ugen._get_outputs() - result = [] - result.append(SynthDefCompiler.encode_string(type(ugen).__name__)) - result.append(SynthDefCompiler.encode_unsigned_int_8bit(ugen.calculation_rate)) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(len(ugen.inputs))) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(len(outputs))) - result.append( - SynthDefCompiler.encode_unsigned_int_16bit(int(ugen.special_index)) - ) - for input_ in ugen.inputs: - result.append(SynthDefCompiler.compile_ugen_input_spec(input_, synthdef)) - for output in outputs: - result.append(SynthDefCompiler.encode_unsigned_int_8bit(output)) - result = bytes().join(result) - return result - @staticmethod - def compile_ugen_graph(synthdef): - result = [] - result.append( - SynthDefCompiler.encode_unsigned_int_32bit(len(synthdef.constants)) - ) - for constant in synthdef.constants: - result.append(SynthDefCompiler.encode_float(constant)) - result.append(SynthDefCompiler.compile_parameters(synthdef)) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(len(synthdef.ugens))) - for ugen_index, ugen in enumerate(synthdef.ugens): - result.append(SynthDefCompiler.compile_ugen(ugen, synthdef)) - result.append(SynthDefCompiler.encode_unsigned_int_16bit(0)) - result = bytes().join(result) - return result +def compile_synthdefs( + synthdef: SynthDef, *synthdefs: SynthDef, use_anonymous_names: bool = False +) -> bytes: + synthdefs_ = (synthdef,) + synthdefs + return b"".join( + [ + b"SCgf", + _encode_unsigned_int_32bit(2), + _encode_unsigned_int_16bit(len(synthdefs_)), + *( + _compile_synthdef( + synthdef, + ( + synthdef.anonymous_name + if not synthdef.name or use_anonymous_names + else synthdef.name + ), + ) + for synthdef in synthdefs_ + ), + ] + ) - @staticmethod - def compile_ugen_input_spec(input_, synthdef): - result = [] - if isinstance(input_, float): - result.append(SynthDefCompiler.encode_unsigned_int_32bit(0xFFFFFFFF)) - constant_index = synthdef._constants.index(input_) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(constant_index)) - elif isinstance(input_, OutputProxy): - ugen = input_.source - output_index = input_.output_index - ugen_index = synthdef._ugens.index(ugen) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(ugen_index)) - result.append(SynthDefCompiler.encode_unsigned_int_32bit(output_index)) - else: - raise Exception("Unhandled input spec: {}".format(input_)) - return bytes().join(result) - @staticmethod - def encode_string(value): - result = bytes(struct.pack(">B", len(value))) - result += bytes(bytearray(value, encoding="ascii")) - return result +def _decode_string(value: bytes, index: int) -> Tuple[str, int]: + length, index = struct.unpack(">B", value[index : index + 1])[0], index + 1 + return value[index : index + length].decode("ascii"), index + length - @staticmethod - def encode_float(value): - return bytes(struct.pack(">f", float(value))) - @staticmethod - def encode_unsigned_int_8bit(value): - return bytes(struct.pack(">B", int(value))) +def _decode_float(value: bytes, index: int) -> Tuple[float, int]: + return struct.unpack(">f", value[index : index + 4])[0], index + 4 - @staticmethod - def encode_unsigned_int_16bit(value): - return bytes(struct.pack(">H", int(value))) - @staticmethod - def encode_unsigned_int_32bit(value): - return bytes(struct.pack(">I", int(value))) +def _decode_int_8bit(value: bytes, index: int) -> Tuple[int, int]: + return struct.unpack(">B", value[index : index + 1])[0], index + 1 -class SynthDefDecompiler: - """ - SynthDef decompiler. +def _decode_int_16bit(value: bytes, index: int) -> Tuple[int, int]: + return struct.unpack(">H", value[index : index + 2])[0], index + 2 - :: - >>> from supriya import ParameterRate - >>> from supriya.ugens import Decay, Out, Parameter, SinOsc, SynthDefBuilder, decompile_synthdefs +def _decode_int_32bit(value: bytes, index: int) -> Tuple[int, int]: + return struct.unpack(">I", value[index : index + 4])[0], index + 4 - :: - >>> with SynthDefBuilder( - ... frequency=440, - ... trigger=Parameter( - ... value=0.0, - ... parameter_rate=ParameterRate.TRIGGER, - ... ), - ... ) as builder: - ... sin_osc = SinOsc.ar(frequency=builder["frequency"]) - ... decay = Decay.kr( - ... decay_time=0.5, - ... source=builder["trigger"], - ... ) - ... enveloped_sin = sin_osc * decay - ... out = Out.ar(bus=0, source=enveloped_sin) - ... - >>> synthdef = builder.build() - >>> supriya.graph(synthdef) # doctest: +SKIP +def _decode_constants(value: bytes, index: int) -> Tuple[Sequence[float], int]: + constants = [] + constants_count, index = _decode_int_32bit(value, index) + for _ in range(constants_count): + constant, index = _decode_float(value, index) + constants.append(constant) + return constants, index - :: - >>> print(synthdef) - synthdef: - name: 001520731aee5371fefab6b505cf64dd - ugens: - - TrigControl.kr: null - - Decay.kr: - source: TrigControl.kr[0:trigger] - decay_time: 0.5 - - Control.kr: null - - SinOsc.ar: - frequency: Control.kr[0:frequency] - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar: - left: SinOsc.ar[0] - right: Decay.kr[0] - - Out.ar: - bus: 0.0 - source[0]: BinaryOpUGen(MULTIPLICATION).ar[0] +def _decode_parameters(value: bytes, index: int) -> Tuple[Dict[int, Parameter], int]: + parameter_values = [] + parameter_count, index = _decode_int_32bit(value, index) + for _ in range(parameter_count): + parameter_value, index = _decode_float(value, index) + parameter_values.append(parameter_value) + parameter_count, index = _decode_int_32bit(value, index) + parameter_names = [] + parameter_indices = [] + for _ in range(parameter_count): + parameter_name, index = _decode_string(value, index) + parameter_index, index = _decode_int_32bit(value, index) + parameter_names.append(parameter_name) + parameter_indices.append(parameter_index) + indexed_parameters = [] + if parameter_count: + for (index_one, name_one), (index_two, name_two) in iterate_nwise( + sorted( + zip(parameter_indices, parameter_names), + key=lambda x: x[0], + ) + + [(len(parameter_values), "")] + ): + indexed_parameters.append( + ( + index_one, + Parameter( + name=name_one, value=parameter_values[index_one:index_two] + ), + ) + ) + indexed_parameters.sort(key=lambda x: parameter_names.index(x[1].name or "")) + return dict(indexed_parameters), index + + +def _decompile_control_parameters( + calculation_rate: CalculationRate, + indexed_parameters: Dict[int, Parameter], + inputs: Sequence[Union[OutputProxy, float]], + output_count: int, + special_index: int, + ugen_class: Type[UGen], +) -> Sequence[Parameter]: + parameter_rate = ParameterRate.CONTROL + if issubclass(ugen_class, TrigControl): + parameter_rate = ParameterRate.TRIGGER + elif calculation_rate == CalculationRate.SCALAR: + parameter_rate = ParameterRate.SCALAR + elif calculation_rate == CalculationRate.AUDIO: + parameter_rate = ParameterRate.AUDIO + parameters = [] + collected_output_count = 0 + lag = 0.0 + while collected_output_count < output_count: + if inputs: + lag = cast(float, inputs[collected_output_count]) + parameter = indexed_parameters[special_index + collected_output_count] + parameter.rate = parameter_rate + if lag: + parameter.lag = lag + parameters.append(parameter) + collected_output_count += len(parameter) + return parameters + + +def _decompile_synthdef(value: bytes, index: int) -> Tuple[SynthDef, int]: + from supriya import ugens + + name, index = _decode_string(value, index) + constants, index = _decode_constants(value, index) + indexed_parameters, index = _decode_parameters(value, index) + decompiled_ugens: List[UGen] = [] + ugen_count, index = _decode_int_32bit(value, index) + for i in range(ugen_count): + ugen_name, index = _decode_string(value, index) + calculation_rate, index = _decode_int_8bit(value, index) + calculation_rate = CalculationRate(calculation_rate) + input_count, index = _decode_int_32bit(value, index) + output_count, index = _decode_int_32bit(value, index) + special_index, index = _decode_int_16bit(value, index) + inputs: List[Union[OutputProxy, float]] = [] + for _ in range(input_count): + ugen_index, index = _decode_int_32bit(value, index) + if ugen_index == 0xFFFFFFFF: + constant_index, index = _decode_int_32bit(value, index) + inputs.append(constants[constant_index]) + else: + ugen = decompiled_ugens[ugen_index] + ugen_output_index, index = _decode_int_32bit(value, index) + output_proxy = ugen[ugen_output_index] + inputs.append(output_proxy) + for _ in range(output_count): + output_rate, index = _decode_int_8bit(value, index) + ugen_class = cast(Type[UGen], getattr(ugens, ugen_name, None)) + ugen = UGen.__new__(ugen_class) + if issubclass(ugen_class, Control): + parameters = _decompile_control_parameters( + calculation_rate, + indexed_parameters, + inputs, + output_count, + special_index, + ugen_class, + ) + ugen_class.__init__( + cast(Control, ugen), + parameters=parameters, + special_index=special_index, + calculation_rate=calculation_rate, + ) + else: + kwargs: UGenParams = {} + if not ugen._unexpanded_keys: + for i, input_name in enumerate(ugen._ordered_keys): + kwargs[input_name] = inputs[i] + else: + for i, input_name in enumerate(ugen._ordered_keys): + if input_name not in ugen._unexpanded_keys: + kwargs[input_name] = inputs[i] + else: + kwargs[input_name] = tuple(inputs[i:]) + ugen._channel_count = output_count + UGen.__init__( + ugen, + calculation_rate=calculation_rate, + special_index=special_index, + **kwargs, + ) + decompiled_ugens.append(ugen) + variants_count, index = _decode_int_16bit(value, index) + synthdef = SynthDef(ugens=decompiled_ugens, name=name) + if synthdef.name == synthdef.anonymous_name: + synthdef._name = None + return synthdef, index - :: - >>> compiled_synthdef = synthdef.compile() - >>> decompiled_synthdef = decompile_synthdefs(compiled_synthdef)[0] - >>> supriya.graph(decompiled_synthdef) # doctest: +SKIP +def decompile_synthdef(value: bytes) -> SynthDef: + if len(synthdefs := decompile_synthdefs(value)) != 1: + raise ValueError(bytes) + return synthdefs[0] - :: - >>> print(decompiled_synthdef) - synthdef: - name: 001520731aee5371fefab6b505cf64dd - ugens: - - TrigControl.kr: null - - Decay.kr: - source: TrigControl.kr[0:trigger] - decay_time: 0.5 - - Control.kr: null - - SinOsc.ar: - frequency: Control.kr[0:frequency] - phase: 0.0 - - BinaryOpUGen(MULTIPLICATION).ar: - left: SinOsc.ar[0] - right: Decay.kr[0] - - Out.ar: - bus: 0.0 - source[0]: BinaryOpUGen(MULTIPLICATION).ar[0] +def decompile_synthdefs(value: bytes) -> List[SynthDef]: + synthdefs: List[SynthDef] = [] + index = 4 + if value[:index] != b"SCgf": + raise ValueError(value) + file_version, index = _decode_int_32bit(value, index) + synthdef_count, index = _decode_int_16bit(value, index) + for _ in range(synthdef_count): + synthdef, index = _decompile_synthdef(value, index) + synthdefs.append(synthdef) + return synthdefs - :: - >>> str(synthdef) == str(decompiled_synthdef) - True - """ +class SuperColliderSynthDef: - ### PRIVATE METHODS ### - - @staticmethod - def _decode_constants(value, index): - sdd = SynthDefDecompiler - constants = [] - constants_count, index = sdd._decode_int_32bit(value, index) - for _ in range(constants_count): - constant, index = sdd._decode_float(value, index) - constants.append(constant) - return constants, index - - @staticmethod - def _decode_parameters(value, index): - sdd = SynthDefDecompiler - parameter_values = [] - parameter_count, index = sdd._decode_int_32bit(value, index) - for _ in range(parameter_count): - parameter_value, index = sdd._decode_float(value, index) - parameter_values.append(parameter_value) - parameter_count, index = sdd._decode_int_32bit(value, index) - parameter_names = [] - parameter_indices = [] - for _ in range(parameter_count): - parameter_name, index = sdd._decode_string(value, index) - parameter_index, index = sdd._decode_int_32bit(value, index) - parameter_names.append(parameter_name) - parameter_indices.append(parameter_index) - indexed_parameters = [] - if parameter_count: - pairs = tuple(zip(parameter_indices, parameter_names)) - pairs = sorted(pairs, key=lambda x: x[0]) - iterator = utils.iterate_nwise(pairs) - for (index_one, name_one), (index_two, name_two) in iterator: - value = parameter_values[index_one:index_two] - if len(value) == 1: - value = value[0] - parameter = Parameter(name=name_one, value=value) - indexed_parameters.append((index_one, parameter)) - index_one, name_one = pairs[-1] - value = parameter_values[index_one:] - if len(value) == 1: - value = value[0] - parameter = Parameter(name=name_one, value=value) - indexed_parameters.append((index_one, parameter)) - indexed_parameters.sort(key=lambda x: parameter_names.index(x[1].name)) - indexed_parameters = collections.OrderedDict(indexed_parameters) - return indexed_parameters, index - - @staticmethod - def _decompile_synthdef(value, index): - from .. import ugens - - sdd = SynthDefDecompiler - synthdef = None - name, index = sdd._decode_string(value, index) - constants, index = sdd._decode_constants(value, index) - indexed_parameters, index = sdd._decode_parameters(value, index) - decompiled_ugens = [] - ugen_count, index = sdd._decode_int_32bit(value, index) - for i in range(ugen_count): - ugen_name, index = sdd._decode_string(value, index) - calculation_rate, index = sdd._decode_int_8bit(value, index) - calculation_rate = CalculationRate(calculation_rate) - input_count, index = sdd._decode_int_32bit(value, index) - output_count, index = sdd._decode_int_32bit(value, index) - special_index, index = sdd._decode_int_16bit(value, index) - inputs = [] - for _ in range(input_count): - ugen_index, index = sdd._decode_int_32bit(value, index) - if ugen_index == 0xFFFFFFFF: - constant_index, index = sdd._decode_int_32bit(value, index) - constant_index = int(constant_index) - inputs.append(constants[constant_index]) - else: - ugen = decompiled_ugens[ugen_index] - ugen_output_index, index = sdd._decode_int_32bit(value, index) - output_proxy = ugen[ugen_output_index] - inputs.append(output_proxy) - for _ in range(output_count): - output_rate, index = sdd._decode_int_8bit(value, index) - ugen_class = getattr(ugens, ugen_name, None) - ugen = UGen.__new__(ugen_class) - if issubclass(ugen_class, Control): - starting_control_index = special_index - parameters = sdd._collect_parameters_for_control( - calculation_rate, - indexed_parameters, - inputs, - output_count, - starting_control_index, - ugen_class, - ) - ugen_class.__init__( - ugen, - parameters=parameters, - starting_control_index=starting_control_index, - calculation_rate=calculation_rate, - ) - else: - kwargs = {} - if not ugen._unexpanded_input_names: - for i, input_name in enumerate(ugen._ordered_input_names): - kwargs[input_name] = inputs[i] - else: - for i, input_name in enumerate(ugen._ordered_input_names): - if input_name not in ugen._unexpanded_input_names: - kwargs[input_name] = inputs[i] - else: - kwargs[input_name] = tuple(inputs[i:]) - ugen._channel_count = output_count - UGen.__init__( - ugen, - calculation_rate=calculation_rate, - special_index=special_index, - **kwargs, - ) - decompiled_ugens.append(ugen) - variants_count, index = sdd._decode_int_16bit(value, index) - synthdef = SynthDef(ugens=decompiled_ugens, name=name) - if synthdef.name == synthdef.anonymous_name: - synthdef._name = None - return synthdef, index - - @staticmethod - def _decode_string(value, index): - length = struct.unpack(">B", value[index : index + 1])[0] - index += 1 - result = value[index : index + length] - result = result.decode("ascii") - index += length - return result, index - - @staticmethod - def _decode_float(value, index): - result = struct.unpack(">f", value[index : index + 4])[0] - index += 4 - return result, index - - @staticmethod - def _decode_int_8bit(value, index): - result = struct.unpack(">B", value[index : index + 1])[0] - index += 1 - return result, index - - @staticmethod - def _decode_int_16bit(value, index): - result = struct.unpack(">H", value[index : index + 2])[0] - index += 2 - return result, index - - @staticmethod - def _decode_int_32bit(value, index): - result = struct.unpack(">I", value[index : index + 4])[0] - index += 4 - return result, index - - @staticmethod - def _collect_parameters_for_control( - calculation_rate, - indexed_parameters, - inputs, - output_count, - starting_control_index, - ugen_class, - ): - parameter_rate = ParameterRate.CONTROL - if issubclass(ugen_class, TrigControl): - parameter_rate = ParameterRate.TRIGGER - elif calculation_rate == CalculationRate.SCALAR: - parameter_rate = ParameterRate.SCALAR - elif calculation_rate == CalculationRate.AUDIO: - parameter_rate = ParameterRate.AUDIO - parameters = [] - collected_output_count = 0 - lag = 0.0 - while collected_output_count < output_count: - if inputs: - lag = inputs[collected_output_count] - parameter = indexed_parameters[ - starting_control_index + collected_output_count + def __init__(self, name: str, body: str, rates: Optional[str] = None): + self.name = name + self.body = body + self.rates = rates + + def _build_sc_input(self, directory_path: Path) -> str: + input_ = [ + "a = SynthDef(", + " \\{}, {{".format(self.name), + ] + for line in self.body.splitlines(): + input_.append(" " + line) + if self.rates: + input_.append("}}, {});".format(self.rates)) + else: + input_.append("});") + input_.extend( + [ + '"Defined SynthDef".postln;', + 'a.writeDefFile("{}");'.format(directory_path), + '"Wrote SynthDef".postln;', + "0.exit;", ] - parameter.parameter_rate = parameter_rate - if lag: - parameter.lag = lag - parameters.append(parameter) - collected_output_count += len(parameter) - return parameters - - ### PUBLIC METHODS ### - - @staticmethod - def decompile_synthdef(value): - synthdefs = SynthDefDecompiler.decompile_synthdefs(value) - assert len(synthdefs) == 1 - return synthdefs[0] - - @staticmethod - def decompile_synthdefs(value): - synthdefs = [] - sdd = SynthDefDecompiler - index = 4 - assert value[:index] == b"SCgf" - file_version, index = sdd._decode_int_32bit(value, index) - synthdef_count, index = sdd._decode_int_16bit(value, index) - for _ in range(synthdef_count): - synthdef, index = sdd._decompile_synthdef(value, index) - synthdefs.append(synthdef) - return synthdefs + ) + return "\n".join(input_) + + def compile(self) -> bytes: + sclang_path = sclang.find() + with tempfile.TemporaryDirectory() as directory: + directory_path = Path(directory) + sc_input = self._build_sc_input(directory_path) + sc_file_path = directory_path / f"{self.name}.sc" + sc_file_path.write_text(sc_input) + command = [str(sclang_path), "-D", str(sc_file_path)] + subprocess.run(command, timeout=10) + result = (directory_path / f"{self.name}.scsyndef").read_bytes() + return bytes(result) diff --git a/supriya/ugens/demand.py b/supriya/ugens/demand.py index 860b291e5..94e6f5039 100644 --- a/supriya/ugens/demand.py +++ b/supriya/ugens/demand.py @@ -90,7 +90,7 @@ class Demand(UGen): ... trigger=trigger, ... ) >>> demand - , ])> + """ trigger = param(0) @@ -360,14 +360,14 @@ class Dswitch(UGen): >>> index = supriya.ugens.Dseq.dr(sequence=[0, 1, 2, 1, 0]) >>> sequence = (1.0, 2.0, 3.0) >>> dswitch = supriya.ugens.Dswitch.dr( - ... index=index, + ... index_=index, ... sequence=sequence, ... ) >>> dswitch """ - index = param() + index_ = param() sequence = param(unexpanded=True) @@ -381,14 +381,14 @@ class Dswitch1(UGen): >>> index = supriya.ugens.Dseq.dr(sequence=[0, 1, 2, 1, 0]) >>> sequence = (1.0, 2.0, 3.0) >>> dswitch_1 = supriya.ugens.Dswitch1.dr( - ... index=index, + ... index_=index, ... sequence=sequence, ... ) >>> dswitch_1 """ - index = param() + index_ = param() sequence = param(unexpanded=True) diff --git a/supriya/ugens/diskio.py b/supriya/ugens/diskio.py index 1ab215be9..b25b4cd8a 100644 --- a/supriya/ugens/diskio.py +++ b/supriya/ugens/diskio.py @@ -15,7 +15,7 @@ class DiskIn(UGen): ... loop=0, ... ) >>> disk_in - , ])> + """ buffer_id = param() @@ -59,7 +59,7 @@ class VDiskIn(UGen): ... send_id=0, ... ) >>> vdisk_in - , ])> + """ buffer_id = param() diff --git a/supriya/ugens/dynamics.py b/supriya/ugens/dynamics.py index 9ad7fc617..000c49811 100644 --- a/supriya/ugens/dynamics.py +++ b/supriya/ugens/dynamics.py @@ -74,7 +74,7 @@ def ar( >>> print(compander_d) synthdef: - name: d4e7b88df56af5070a88f09b0f8c633e + name: ... ugens: - In.ar: bus: 0.0 diff --git a/supriya/ugens/envelopes.py b/supriya/ugens/envelopes.py index f6cf6c4d0..22bb4f8ca 100644 --- a/supriya/ugens/envelopes.py +++ b/supriya/ugens/envelopes.py @@ -19,7 +19,7 @@ class Envelope: :: >>> list(envelope.serialize()) - [0, 2, -99, -99, 1, 1, 1, 0.0, 0, 1, 1, 0.0] + [<0.0>, <2.0>, <-99.0>, <-99.0>, <1.0>, <1.0>, <1.0>, <0.0>, <0.0>, <1.0>, <1.0>, <0.0>] """ def __init__( @@ -150,7 +150,7 @@ def percussive( :: >>> list(envelope.serialize()) - [0, 2, -99, -99, 1.0, 0.01, 5, -4.0, 0, 1.0, 5, -4.0] + [<0.0>, <2.0>, <-99.0>, <-99.0>, <1.0>, <0.01>, <5.0>, <-4.0>, <0.0>, <1.0>, <5.0>, <-4.0>] """ amplitudes = [0, amplitude, 0] durations = [attack_time, release_time] @@ -166,38 +166,40 @@ def linen( curves = [curve] return Envelope(amplitudes=amplitudes, durations=durations, curves=curves) - def serialize(self, for_interpolation=False) -> UGenVector: + def serialize(self, **kwargs) -> UGenVector: result: List[Union[UGenOperable, float]] = [] - if for_interpolation: - result.append(self.offset or 0.0) - result.append(self.initial_amplitude) - result.append(len(self.envelope_segments)) - result.append(self.duration) - for amplitude, duration, curve in self._envelope_segments: - result.append(duration) - if isinstance(curve, EnvelopeShape): - shape = int(curve) - curve = 0.0 - else: - shape = 5 - result.append(shape) - result.append(curve) - result.append(amplitude) - else: - result.append(self.initial_amplitude) - result.append(len(self.envelope_segments)) - result.append(-99 if self.release_node is None else self.release_node) - result.append(-99 if self.loop_node is None else self.loop_node) - for amplitude, duration, curve in self._envelope_segments: - result.append(amplitude) - result.append(duration) - if isinstance(curve, EnvelopeShape): - shape = int(curve) - curve = 0.0 - else: - shape = 5 - result.append(shape) - result.append(curve) + result.append(self.initial_amplitude) + result.append(len(self.envelope_segments)) + result.append(-99 if self.release_node is None else self.release_node) + result.append(-99 if self.loop_node is None else self.loop_node) + for amplitude, duration, curve in self._envelope_segments: + result.append(amplitude) + result.append(duration) + if isinstance(curve, EnvelopeShape): + shape = int(curve) + curve = 0.0 + else: + shape = 5 + result.append(shape) + result.append(curve) + return UGenVector(*result) + + def serialize_interpolated(self) -> UGenVector: + result: List[Union[UGenOperable, float]] = [] + result.append(self.offset or 0.0) + result.append(self.initial_amplitude) + result.append(len(self.envelope_segments)) + result.append(self.duration) + for amplitude, duration, curve in self._envelope_segments: + result.append(duration) + if isinstance(curve, EnvelopeShape): + shape = int(curve) + curve = 0.0 + else: + shape = 5 + result.append(shape) + result.append(curve) + result.append(amplitude) return UGenVector(*result) @classmethod @@ -217,7 +219,7 @@ def triangle(cls, duration=1.0, amplitude=1.0) -> "Envelope": :: >>> list(envelope.serialize()) - [0, 2, -99, -99, 1.0, 0.5, 1, 0.0, 0, 0.5, 1, 0.0] + [<0.0>, <2.0>, <-99.0>, <-99.0>, <1.0>, <0.5>, <1.0>, <0.0>, <0.0>, <0.5>, <1.0>, <0.0>] """ amplitudes = [0, amplitude, 0] duration = duration / 2.0 @@ -303,6 +305,7 @@ class EnvGen(UGen): @classmethod def _new_expanded( cls, + *, calculation_rate=None, done_action=None, envelope=None, @@ -313,14 +316,10 @@ def _new_expanded( ): if not isinstance(done_action, Parameter): done_action = DoneAction.from_expr(done_action) - if envelope is None: - envelope = Envelope() - assert isinstance(envelope, Envelope) - envelope = envelope.serialize() return super(EnvGen, cls)._new_expanded( calculation_rate=calculation_rate, done_action=done_action, - envelope=envelope, + envelope=(envelope or Envelope()).serialize(), gate=gate, level_bias=level_bias, level_scale=level_scale, diff --git a/supriya/ugens/factories.py b/supriya/ugens/factories.py index 39e14382b..13e08845c 100644 --- a/supriya/ugens/factories.py +++ b/supriya/ugens/factories.py @@ -1,5 +1,6 @@ import copy import types +from typing import Sequence from ..enums import DoneAction from . import ( @@ -16,7 +17,6 @@ RandID, ReplaceOut, SynthDefBuilder, - UGenOperable, UGenVector, XOut, ) @@ -60,7 +60,8 @@ class SynthDefFactory: synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -102,7 +103,8 @@ class SynthDefFactory: synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -166,7 +168,8 @@ class SynthDefFactory: synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -248,10 +251,10 @@ def _setup_parameters_and_state(self, builder, state, kwargs): for parameter_block in self._parameter_blocks: parameter_block(builder, state) if self._rand_id is not None: - builder._add_parameter("rand_id", self._rand_id, "SCALAR") + builder.add_parameter(name="rand_id", value=self._rand_id, rate="SCALAR") RandID.ir(rand_id=builder["rand_id"]) if self._gate: - builder._add_parameter("gate", 1, "CONTROL") + builder.add_parameter(name="gate", value=1, rate="CONTROL") state["gate"] = Linen.kr( attack_time=self._gate["attack_time"], done_action=DoneAction.FREE_SYNTH, @@ -259,21 +262,21 @@ def _setup_parameters_and_state(self, builder, state, kwargs): release_time=self._gate["release_time"], ) if self._output or self._input: - builder._add_parameter("out", 0, "SCALAR") + builder.add_parameter(name="out", value=0, rate="SCALAR") if self._input.get("private"): - builder._add_parameter("in_", 0, "SCALAR") + builder.add_parameter(name="in_", value=0, rate="SCALAR") if self._output.get("windowed") or self._input.get("windowed"): - builder._add_parameter("duration", 1, "SCALAR") + builder.add_parameter(name="duration", value=1, rate="SCALAR") state["line"] = Line.kr( done_action=DoneAction.FREE_SYNTH, duration=builder["duration"] ) state["window"] = state["line"].hanning_window() if not self._output.get("windowed") and self._output.get("crossfaded"): - builder._add_parameter("mix", 0, "CONTROL") + builder.add_parameter(name="mix", value=0, rate="CONTROL") if self._output.get("leveled"): - builder._add_parameter("level", 1, "CONTROL") + builder.add_parameter(name="level", value=1, rate="CONTROL") for key, value in self._parameters: - builder._add_parameter(key, value) + builder.add_parameter(name=key, value=value) def _build_input(self, builder, state): if not self._input: @@ -287,6 +290,8 @@ def _build_input(self, builder, state): source = input_class.ar(bus=parameter, channel_count=state["channel_count"]) if self._input.get("windowed"): source *= state["window"] + if source is not None and not isinstance(source, Sequence): + source = UGenVector(source) return source def _build_feedback_loop_input(self, builder, source, state): @@ -296,6 +301,8 @@ def _build_feedback_loop_input(self, builder, source, state): source = local_in else: source += local_in + if source is not None and not isinstance(source, Sequence): + source = UGenVector(source) return source def _build_feedback_loop_output(self, builder, source, state): @@ -365,7 +372,7 @@ def build(self, name=None, **kwargs): source = self._build_feedback_loop_input(builder, source, state) for signal_block in self._signal_blocks: source = signal_block(builder, source, state) - if not isinstance(source, UGenOperable): + if source is not None and not isinstance(source, Sequence): source = UGenVector(source) self._build_output(builder, source, state) self._build_feedback_loop_output(builder, source, state) @@ -418,7 +425,8 @@ def with_channel_count(self, channel_count): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -495,7 +503,8 @@ def with_channel_count(self, channel_count): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -601,7 +610,8 @@ def with_feedback_loop(self, block_function=None): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - LocalIn.ar: @@ -659,7 +669,8 @@ def with_feedback_loop(self, block_function=None): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - LocalIn.ar: @@ -754,16 +765,18 @@ def with_gate(self, attack_time=0.02, release_time=0.02): synthdef: name: ... ugens: - - Control.ir: null - - In.ar: - bus: Control.ir[0:out] - - Control.kr: null + - Control.kr: + gate: 1.0 - Linen.kr: gate: Control.kr[0:gate] attack_time: 0.02 sustain_level: 1.0 release_time: 0.02 done_action: 2.0 + - Control.ir: + out: 0.0 + - In.ar: + bus: Control.ir[0:out] - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 @@ -847,7 +860,8 @@ def with_initial_state(self, **state): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -947,7 +961,8 @@ def with_input(self, feedback=False, private=False, windowed=False): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -992,7 +1007,9 @@ def with_input(self, feedback=False, private=False, windowed=False): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + in_: 0.0 + out: 0.0 - In.ar: bus: Control.ir[0:in_] - ExpRand.ir/0: @@ -1037,7 +1054,9 @@ def with_input(self, feedback=False, private=False, windowed=False): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1094,7 +1113,9 @@ def with_input(self, feedback=False, private=False, windowed=False): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1189,7 +1210,8 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -1234,7 +1256,9 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1289,10 +1313,12 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + mix: 0.0 + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - - Control.kr: null - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 @@ -1337,10 +1363,12 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + level: 1.0 + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - - Control.kr: null - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 @@ -1390,7 +1418,9 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1447,7 +1477,11 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + level: 1.0 + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1455,12 +1489,11 @@ def with_output( done_action: 2.0 - UnaryOpUGen(HANNING_WINDOW).kr: source: Line.kr[0] - - In.ar: - bus: Control.ir[1:out] - - Control.kr: null - BinaryOpUGen(MULTIPLICATION).kr: left: UnaryOpUGen(HANNING_WINDOW).kr[0] right: Control.kr[0:level] + - In.ar: + bus: Control.ir[1:out] - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 @@ -1505,7 +1538,11 @@ def with_output( synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + level: 1.0 + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -1513,15 +1550,14 @@ def with_output( done_action: 2.0 - UnaryOpUGen(HANNING_WINDOW).kr: source: Line.kr[0] + - BinaryOpUGen(MULTIPLICATION).kr: + left: UnaryOpUGen(HANNING_WINDOW).kr[0] + right: Control.kr[0:level] - In.ar: bus: Control.ir[1:out] - BinaryOpUGen(MULTIPLICATION).ar: left: In.ar[0] right: UnaryOpUGen(HANNING_WINDOW).kr[0] - - Control.kr: null - - BinaryOpUGen(MULTIPLICATION).kr: - left: UnaryOpUGen(HANNING_WINDOW).kr[0] - right: Control.kr[0:level] - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 @@ -1560,7 +1596,7 @@ def with_output( return clone def with_parameter(self, name, value, rate=None): - parameter = Parameter(name=name, value=value, parameter_rate=rate) + parameter = Parameter(name=name, value=value, rate=rate) return self.with_parameters(**{name: parameter}) def with_parameters(self, **kwargs): @@ -1597,6 +1633,11 @@ def with_parameter_block(self, block_function): A factory configured to build multi-band compressor SynthDefs, using frequency bands split at ``frequencies``: + :: + + >>> from typing import Sequence + >>> from supriya.ugens import CompanderD, LPF, Mix + :: >>> def parameter_block(builder, state): @@ -1604,13 +1645,14 @@ def with_parameter_block(self, block_function): ... band_count = len(frequencies) + 1 ... for i in range(band_count): ... band_name = "band_{}_".format(i + 1) - ... builder._add_parameter(band_name + "pregain", 0) - ... builder._add_parameter(band_name + "clamp_time", 0.01) - ... builder._add_parameter(band_name + "relax_time", 0.1) - ... builder._add_parameter(band_name + "threshold", -6) - ... builder._add_parameter(band_name + "slope_above", 0.5) - ... builder._add_parameter(band_name + "slope_below", 1.0) - ... builder._add_parameter(band_name + "postgain", 0) + ... builder.add_parameter(name=band_name + "pregain", value=0) + ... builder.add_parameter(name=band_name + "clamp_time", value=0.01) + ... builder.add_parameter(name=band_name + "relax_time", value=0.1) + ... builder.add_parameter(name=band_name + "threshold", value=-6) + ... builder.add_parameter(name=band_name + "slope_above", value=0.5) + ... builder.add_parameter(name=band_name + "slope_below", value=1.0) + ... builder.add_parameter(name=band_name + "postgain", value=0) + ... :: @@ -1618,7 +1660,7 @@ def with_parameter_block(self, block_function): ... bands = [] ... frequencies = state["frequencies"] ... for frequency in frequencies: - ... band = supriya.ugens.LPF.ar(source=source, frequency=frequency) + ... band = LPF.ar(source=source, frequency=frequency) ... bands.append(band) ... source -= band ... bands.append(source) @@ -1626,7 +1668,7 @@ def with_parameter_block(self, block_function): ... for i, band in enumerate(bands): ... band_name = "band_{}_".format(i + 1) ... band *= builder[band_name + "pregain"].db_to_amplitude() - ... band = supriya.ugens.CompanderD.ar( + ... band = CompanderD.ar( ... source=band, ... clamp_time=builder[band_name + "clamp_time"], ... relax_time=builder[band_name + "relax_time"], @@ -1635,12 +1677,13 @@ def with_parameter_block(self, block_function): ... threshold=builder[band_name + "threshold"].db_to_amplitude(), ... ) ... band *= builder[band_name + "postgain"].db_to_amplitude() - ... compressors.extend(band) - ... source = supriya.ugens.Mix.multichannel( + ... compressors.extend(band if isinstance(band, Sequence) else [band]) + ... source = Mix.multichannel( ... compressors, ... state["channel_count"], ... ) ... return source + ... :: @@ -1660,7 +1703,62 @@ def with_parameter_block(self, block_function): synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + band_1_clamp_time: 0.01 + band_1_postgain: 0.0 + band_1_pregain: 0.0 + band_1_relax_time: 0.1 + band_1_slope_above: 0.5 + band_1_slope_below: 1.0 + band_1_threshold: -6.0 + band_2_clamp_time: 0.01 + band_2_postgain: 0.0 + band_2_pregain: 0.0 + band_2_relax_time: 0.1 + band_2_slope_above: 0.5 + band_2_slope_below: 1.0 + band_2_threshold: -6.0 + band_3_clamp_time: 0.01 + band_3_postgain: 0.0 + band_3_pregain: 0.0 + band_3_relax_time: 0.1 + band_3_slope_above: 0.5 + band_3_slope_below: 1.0 + band_3_threshold: -6.0 + band_4_clamp_time: 0.01 + band_4_postgain: 0.0 + band_4_pregain: 0.0 + band_4_relax_time: 0.1 + band_4_slope_above: 0.5 + band_4_slope_below: 1.0 + band_4_threshold: -6.0 + mix: 0.0 + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/0: + source: Control.kr[2:band_1_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/1: + source: Control.kr[6:band_1_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/2: + source: Control.kr[1:band_1_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/3: + source: Control.kr[9:band_2_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/4: + source: Control.kr[13:band_2_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/5: + source: Control.kr[8:band_2_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/6: + source: Control.kr[16:band_3_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/7: + source: Control.kr[20:band_3_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/8: + source: Control.kr[15:band_3_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/9: + source: Control.kr[23:band_4_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/10: + source: Control.kr[27:band_4_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/11: + source: Control.kr[22:band_4_postgain] + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - LPF.ar/0: @@ -1681,108 +1779,83 @@ def with_parameter_block(self, block_function): - BinaryOpUGen(SUBTRACTION).ar/2: left: BinaryOpUGen(SUBTRACTION).ar/1[0] right: LPF.ar/2[0] - - Control.kr: null - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/0: - source: Control.kr[2:band_1_pregain] - BinaryOpUGen(MULTIPLICATION).ar/0: - left: LPF.ar/0[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/0[0] + left: BinaryOpUGen(SUBTRACTION).ar/2[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/9[0] - DelayN.ar/0: source: BinaryOpUGen(MULTIPLICATION).ar/0[0] - maximum_delay_time: Control.kr[0:band_1_clamp_time] - delay_time: Control.kr[0:band_1_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/1: - source: Control.kr[6:band_1_threshold] + maximum_delay_time: Control.kr[21:band_4_clamp_time] + delay_time: Control.kr[21:band_4_clamp_time] - Compander.ar/0: source: BinaryOpUGen(MULTIPLICATION).ar/0[0] control: DelayN.ar/0[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/1[0] - slope_below: Control.kr[5:band_1_slope_below] - slope_above: Control.kr[4:band_1_slope_above] - clamp_time: Control.kr[0:band_1_clamp_time] - relax_time: Control.kr[3:band_1_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/2: - source: Control.kr[1:band_1_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/10[0] + slope_below: Control.kr[26:band_4_slope_below] + slope_above: Control.kr[25:band_4_slope_above] + clamp_time: Control.kr[21:band_4_clamp_time] + relax_time: Control.kr[24:band_4_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/1: left: Compander.ar/0[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/2[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/3: - source: Control.kr[9:band_2_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/11[0] - BinaryOpUGen(MULTIPLICATION).ar/2: - left: LPF.ar/1[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/3[0] + left: LPF.ar/2[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/6[0] - DelayN.ar/1: source: BinaryOpUGen(MULTIPLICATION).ar/2[0] - maximum_delay_time: Control.kr[7:band_2_clamp_time] - delay_time: Control.kr[7:band_2_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/4: - source: Control.kr[13:band_2_threshold] + maximum_delay_time: Control.kr[14:band_3_clamp_time] + delay_time: Control.kr[14:band_3_clamp_time] - Compander.ar/1: source: BinaryOpUGen(MULTIPLICATION).ar/2[0] control: DelayN.ar/1[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/4[0] - slope_below: Control.kr[12:band_2_slope_below] - slope_above: Control.kr[11:band_2_slope_above] - clamp_time: Control.kr[7:band_2_clamp_time] - relax_time: Control.kr[10:band_2_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/5: - source: Control.kr[8:band_2_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/7[0] + slope_below: Control.kr[19:band_3_slope_below] + slope_above: Control.kr[18:band_3_slope_above] + clamp_time: Control.kr[14:band_3_clamp_time] + relax_time: Control.kr[17:band_3_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/3: left: Compander.ar/1[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/5[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/6: - source: Control.kr[16:band_3_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/8[0] - BinaryOpUGen(MULTIPLICATION).ar/4: - left: LPF.ar/2[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/6[0] + left: LPF.ar/1[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/3[0] - DelayN.ar/2: source: BinaryOpUGen(MULTIPLICATION).ar/4[0] - maximum_delay_time: Control.kr[14:band_3_clamp_time] - delay_time: Control.kr[14:band_3_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/7: - source: Control.kr[20:band_3_threshold] + maximum_delay_time: Control.kr[7:band_2_clamp_time] + delay_time: Control.kr[7:band_2_clamp_time] - Compander.ar/2: source: BinaryOpUGen(MULTIPLICATION).ar/4[0] control: DelayN.ar/2[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/7[0] - slope_below: Control.kr[19:band_3_slope_below] - slope_above: Control.kr[18:band_3_slope_above] - clamp_time: Control.kr[14:band_3_clamp_time] - relax_time: Control.kr[17:band_3_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/8: - source: Control.kr[15:band_3_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/4[0] + slope_below: Control.kr[12:band_2_slope_below] + slope_above: Control.kr[11:band_2_slope_above] + clamp_time: Control.kr[7:band_2_clamp_time] + relax_time: Control.kr[10:band_2_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/5: left: Compander.ar/2[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/8[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/9: - source: Control.kr[23:band_4_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/5[0] - BinaryOpUGen(MULTIPLICATION).ar/6: - left: BinaryOpUGen(SUBTRACTION).ar/2[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/9[0] + left: LPF.ar/0[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/0[0] - DelayN.ar/3: source: BinaryOpUGen(MULTIPLICATION).ar/6[0] - maximum_delay_time: Control.kr[21:band_4_clamp_time] - delay_time: Control.kr[21:band_4_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/10: - source: Control.kr[27:band_4_threshold] + maximum_delay_time: Control.kr[0:band_1_clamp_time] + delay_time: Control.kr[0:band_1_clamp_time] - Compander.ar/3: source: BinaryOpUGen(MULTIPLICATION).ar/6[0] control: DelayN.ar/3[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/10[0] - slope_below: Control.kr[26:band_4_slope_below] - slope_above: Control.kr[25:band_4_slope_above] - clamp_time: Control.kr[21:band_4_clamp_time] - relax_time: Control.kr[24:band_4_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/11: - source: Control.kr[22:band_4_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/1[0] + slope_below: Control.kr[5:band_1_slope_below] + slope_above: Control.kr[4:band_1_slope_above] + clamp_time: Control.kr[0:band_1_clamp_time] + relax_time: Control.kr[3:band_1_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/7: left: Compander.ar/3[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/11[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/2[0] - Sum4.ar: - input_one: BinaryOpUGen(MULTIPLICATION).ar/1[0] - input_two: BinaryOpUGen(MULTIPLICATION).ar/3[0] - input_three: BinaryOpUGen(MULTIPLICATION).ar/5[0] - input_four: BinaryOpUGen(MULTIPLICATION).ar/7[0] + input_one: BinaryOpUGen(MULTIPLICATION).ar/7[0] + input_two: BinaryOpUGen(MULTIPLICATION).ar/5[0] + input_three: BinaryOpUGen(MULTIPLICATION).ar/3[0] + input_four: BinaryOpUGen(MULTIPLICATION).ar/1[0] - XOut.ar: bus: Control.ir[0:out] crossfade: Control.kr[28:mix] @@ -1805,7 +1878,114 @@ def with_parameter_block(self, block_function): synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + band_1_clamp_time: 0.01 + band_1_postgain: 0.0 + band_1_pregain: 0.0 + band_1_relax_time: 0.1 + band_1_slope_above: 0.5 + band_1_slope_below: 1.0 + band_1_threshold: -6.0 + band_2_clamp_time: 0.01 + band_2_postgain: 0.0 + band_2_pregain: 0.0 + band_2_relax_time: 0.1 + band_2_slope_above: 0.5 + band_2_slope_below: 1.0 + band_2_threshold: -6.0 + band_3_clamp_time: 0.01 + band_3_postgain: 0.0 + band_3_pregain: 0.0 + band_3_relax_time: 0.1 + band_3_slope_above: 0.5 + band_3_slope_below: 1.0 + band_3_threshold: -6.0 + band_4_clamp_time: 0.01 + band_4_postgain: 0.0 + band_4_pregain: 0.0 + band_4_relax_time: 0.1 + band_4_slope_above: 0.5 + band_4_slope_below: 1.0 + band_4_threshold: -6.0 + band_5_clamp_time: 0.01 + band_5_postgain: 0.0 + band_5_pregain: 0.0 + band_5_relax_time: 0.1 + band_5_slope_above: 0.5 + band_5_slope_below: 1.0 + band_5_threshold: -6.0 + band_6_clamp_time: 0.01 + band_6_postgain: 0.0 + band_6_pregain: 0.0 + band_6_relax_time: 0.1 + band_6_slope_above: 0.5 + band_6_slope_below: 1.0 + band_6_threshold: -6.0 + band_7_clamp_time: 0.01 + band_7_postgain: 0.0 + band_7_pregain: 0.0 + band_7_relax_time: 0.1 + band_7_slope_above: 0.5 + band_7_slope_below: 1.0 + band_7_threshold: -6.0 + band_8_clamp_time: 0.01 + band_8_postgain: 0.0 + band_8_pregain: 0.0 + band_8_relax_time: 0.1 + band_8_slope_above: 0.5 + band_8_slope_below: 1.0 + band_8_threshold: -6.0 + mix: 0.0 + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/0: + source: Control.kr[2:band_1_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/1: + source: Control.kr[6:band_1_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/2: + source: Control.kr[1:band_1_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/3: + source: Control.kr[9:band_2_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/4: + source: Control.kr[13:band_2_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/5: + source: Control.kr[8:band_2_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/6: + source: Control.kr[16:band_3_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/7: + source: Control.kr[20:band_3_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/8: + source: Control.kr[15:band_3_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/9: + source: Control.kr[23:band_4_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/10: + source: Control.kr[27:band_4_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/11: + source: Control.kr[22:band_4_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/12: + source: Control.kr[30:band_5_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/13: + source: Control.kr[34:band_5_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/14: + source: Control.kr[29:band_5_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/15: + source: Control.kr[37:band_6_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/16: + source: Control.kr[41:band_6_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/17: + source: Control.kr[36:band_6_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/18: + source: Control.kr[44:band_7_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/19: + source: Control.kr[48:band_7_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/20: + source: Control.kr[43:band_7_postgain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/21: + source: Control.kr[51:band_8_pregain] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/22: + source: Control.kr[55:band_8_threshold] + - UnaryOpUGen(DB_TO_AMPLITUDE).kr/23: + source: Control.kr[50:band_8_postgain] + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - LPF.ar/0: @@ -1850,216 +2030,168 @@ def with_parameter_block(self, block_function): - BinaryOpUGen(SUBTRACTION).ar/6: left: BinaryOpUGen(SUBTRACTION).ar/5[0] right: LPF.ar/6[0] - - Control.kr: null - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/0: - source: Control.kr[2:band_1_pregain] - BinaryOpUGen(MULTIPLICATION).ar/0: - left: LPF.ar/0[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/0[0] + left: BinaryOpUGen(SUBTRACTION).ar/6[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/21[0] - DelayN.ar/0: source: BinaryOpUGen(MULTIPLICATION).ar/0[0] - maximum_delay_time: Control.kr[0:band_1_clamp_time] - delay_time: Control.kr[0:band_1_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/1: - source: Control.kr[6:band_1_threshold] + maximum_delay_time: Control.kr[49:band_8_clamp_time] + delay_time: Control.kr[49:band_8_clamp_time] - Compander.ar/0: source: BinaryOpUGen(MULTIPLICATION).ar/0[0] control: DelayN.ar/0[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/1[0] - slope_below: Control.kr[5:band_1_slope_below] - slope_above: Control.kr[4:band_1_slope_above] - clamp_time: Control.kr[0:band_1_clamp_time] - relax_time: Control.kr[3:band_1_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/2: - source: Control.kr[1:band_1_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/22[0] + slope_below: Control.kr[54:band_8_slope_below] + slope_above: Control.kr[53:band_8_slope_above] + clamp_time: Control.kr[49:band_8_clamp_time] + relax_time: Control.kr[52:band_8_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/1: left: Compander.ar/0[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/2[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/3: - source: Control.kr[9:band_2_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/23[0] - BinaryOpUGen(MULTIPLICATION).ar/2: - left: LPF.ar/1[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/3[0] + left: LPF.ar/6[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/18[0] - DelayN.ar/1: source: BinaryOpUGen(MULTIPLICATION).ar/2[0] - maximum_delay_time: Control.kr[7:band_2_clamp_time] - delay_time: Control.kr[7:band_2_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/4: - source: Control.kr[13:band_2_threshold] + maximum_delay_time: Control.kr[42:band_7_clamp_time] + delay_time: Control.kr[42:band_7_clamp_time] - Compander.ar/1: source: BinaryOpUGen(MULTIPLICATION).ar/2[0] control: DelayN.ar/1[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/4[0] - slope_below: Control.kr[12:band_2_slope_below] - slope_above: Control.kr[11:band_2_slope_above] - clamp_time: Control.kr[7:band_2_clamp_time] - relax_time: Control.kr[10:band_2_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/5: - source: Control.kr[8:band_2_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/19[0] + slope_below: Control.kr[47:band_7_slope_below] + slope_above: Control.kr[46:band_7_slope_above] + clamp_time: Control.kr[42:band_7_clamp_time] + relax_time: Control.kr[45:band_7_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/3: left: Compander.ar/1[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/5[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/6: - source: Control.kr[16:band_3_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/20[0] - BinaryOpUGen(MULTIPLICATION).ar/4: - left: LPF.ar/2[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/6[0] + left: LPF.ar/5[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/15[0] - DelayN.ar/2: source: BinaryOpUGen(MULTIPLICATION).ar/4[0] - maximum_delay_time: Control.kr[14:band_3_clamp_time] - delay_time: Control.kr[14:band_3_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/7: - source: Control.kr[20:band_3_threshold] + maximum_delay_time: Control.kr[35:band_6_clamp_time] + delay_time: Control.kr[35:band_6_clamp_time] - Compander.ar/2: source: BinaryOpUGen(MULTIPLICATION).ar/4[0] control: DelayN.ar/2[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/7[0] - slope_below: Control.kr[19:band_3_slope_below] - slope_above: Control.kr[18:band_3_slope_above] - clamp_time: Control.kr[14:band_3_clamp_time] - relax_time: Control.kr[17:band_3_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/8: - source: Control.kr[15:band_3_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/16[0] + slope_below: Control.kr[40:band_6_slope_below] + slope_above: Control.kr[39:band_6_slope_above] + clamp_time: Control.kr[35:band_6_clamp_time] + relax_time: Control.kr[38:band_6_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/5: left: Compander.ar/2[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/8[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/9: - source: Control.kr[23:band_4_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/17[0] - BinaryOpUGen(MULTIPLICATION).ar/6: - left: LPF.ar/3[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/9[0] + left: LPF.ar/4[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/12[0] - DelayN.ar/3: source: BinaryOpUGen(MULTIPLICATION).ar/6[0] - maximum_delay_time: Control.kr[21:band_4_clamp_time] - delay_time: Control.kr[21:band_4_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/10: - source: Control.kr[27:band_4_threshold] + maximum_delay_time: Control.kr[28:band_5_clamp_time] + delay_time: Control.kr[28:band_5_clamp_time] - Compander.ar/3: source: BinaryOpUGen(MULTIPLICATION).ar/6[0] control: DelayN.ar/3[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/10[0] - slope_below: Control.kr[26:band_4_slope_below] - slope_above: Control.kr[25:band_4_slope_above] - clamp_time: Control.kr[21:band_4_clamp_time] - relax_time: Control.kr[24:band_4_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/11: - source: Control.kr[22:band_4_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/13[0] + slope_below: Control.kr[33:band_5_slope_below] + slope_above: Control.kr[32:band_5_slope_above] + clamp_time: Control.kr[28:band_5_clamp_time] + relax_time: Control.kr[31:band_5_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/7: left: Compander.ar/3[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/11[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/14[0] - Sum4.ar/0: - input_one: BinaryOpUGen(MULTIPLICATION).ar/1[0] - input_two: BinaryOpUGen(MULTIPLICATION).ar/3[0] - input_three: BinaryOpUGen(MULTIPLICATION).ar/5[0] - input_four: BinaryOpUGen(MULTIPLICATION).ar/7[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/12: - source: Control.kr[30:band_5_pregain] + input_one: BinaryOpUGen(MULTIPLICATION).ar/7[0] + input_two: BinaryOpUGen(MULTIPLICATION).ar/5[0] + input_three: BinaryOpUGen(MULTIPLICATION).ar/3[0] + input_four: BinaryOpUGen(MULTIPLICATION).ar/1[0] - BinaryOpUGen(MULTIPLICATION).ar/8: - left: LPF.ar/4[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/12[0] + left: LPF.ar/3[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/9[0] - DelayN.ar/4: source: BinaryOpUGen(MULTIPLICATION).ar/8[0] - maximum_delay_time: Control.kr[28:band_5_clamp_time] - delay_time: Control.kr[28:band_5_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/13: - source: Control.kr[34:band_5_threshold] + maximum_delay_time: Control.kr[21:band_4_clamp_time] + delay_time: Control.kr[21:band_4_clamp_time] - Compander.ar/4: source: BinaryOpUGen(MULTIPLICATION).ar/8[0] control: DelayN.ar/4[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/13[0] - slope_below: Control.kr[33:band_5_slope_below] - slope_above: Control.kr[32:band_5_slope_above] - clamp_time: Control.kr[28:band_5_clamp_time] - relax_time: Control.kr[31:band_5_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/14: - source: Control.kr[29:band_5_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/10[0] + slope_below: Control.kr[26:band_4_slope_below] + slope_above: Control.kr[25:band_4_slope_above] + clamp_time: Control.kr[21:band_4_clamp_time] + relax_time: Control.kr[24:band_4_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/9: left: Compander.ar/4[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/14[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/15: - source: Control.kr[37:band_6_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/11[0] - BinaryOpUGen(MULTIPLICATION).ar/10: - left: LPF.ar/5[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/15[0] + left: LPF.ar/2[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/6[0] - DelayN.ar/5: source: BinaryOpUGen(MULTIPLICATION).ar/10[0] - maximum_delay_time: Control.kr[35:band_6_clamp_time] - delay_time: Control.kr[35:band_6_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/16: - source: Control.kr[41:band_6_threshold] + maximum_delay_time: Control.kr[14:band_3_clamp_time] + delay_time: Control.kr[14:band_3_clamp_time] - Compander.ar/5: source: BinaryOpUGen(MULTIPLICATION).ar/10[0] control: DelayN.ar/5[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/16[0] - slope_below: Control.kr[40:band_6_slope_below] - slope_above: Control.kr[39:band_6_slope_above] - clamp_time: Control.kr[35:band_6_clamp_time] - relax_time: Control.kr[38:band_6_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/17: - source: Control.kr[36:band_6_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/7[0] + slope_below: Control.kr[19:band_3_slope_below] + slope_above: Control.kr[18:band_3_slope_above] + clamp_time: Control.kr[14:band_3_clamp_time] + relax_time: Control.kr[17:band_3_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/11: left: Compander.ar/5[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/17[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/18: - source: Control.kr[44:band_7_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/8[0] - BinaryOpUGen(MULTIPLICATION).ar/12: - left: LPF.ar/6[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/18[0] + left: LPF.ar/1[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/3[0] - DelayN.ar/6: source: BinaryOpUGen(MULTIPLICATION).ar/12[0] - maximum_delay_time: Control.kr[42:band_7_clamp_time] - delay_time: Control.kr[42:band_7_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/19: - source: Control.kr[48:band_7_threshold] + maximum_delay_time: Control.kr[7:band_2_clamp_time] + delay_time: Control.kr[7:band_2_clamp_time] - Compander.ar/6: source: BinaryOpUGen(MULTIPLICATION).ar/12[0] control: DelayN.ar/6[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/19[0] - slope_below: Control.kr[47:band_7_slope_below] - slope_above: Control.kr[46:band_7_slope_above] - clamp_time: Control.kr[42:band_7_clamp_time] - relax_time: Control.kr[45:band_7_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/20: - source: Control.kr[43:band_7_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/4[0] + slope_below: Control.kr[12:band_2_slope_below] + slope_above: Control.kr[11:band_2_slope_above] + clamp_time: Control.kr[7:band_2_clamp_time] + relax_time: Control.kr[10:band_2_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/13: left: Compander.ar/6[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/20[0] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/21: - source: Control.kr[51:band_8_pregain] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/5[0] - BinaryOpUGen(MULTIPLICATION).ar/14: - left: BinaryOpUGen(SUBTRACTION).ar/6[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/21[0] + left: LPF.ar/0[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/0[0] - DelayN.ar/7: source: BinaryOpUGen(MULTIPLICATION).ar/14[0] - maximum_delay_time: Control.kr[49:band_8_clamp_time] - delay_time: Control.kr[49:band_8_clamp_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/22: - source: Control.kr[55:band_8_threshold] + maximum_delay_time: Control.kr[0:band_1_clamp_time] + delay_time: Control.kr[0:band_1_clamp_time] - Compander.ar/7: source: BinaryOpUGen(MULTIPLICATION).ar/14[0] control: DelayN.ar/7[0] - threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/22[0] - slope_below: Control.kr[54:band_8_slope_below] - slope_above: Control.kr[53:band_8_slope_above] - clamp_time: Control.kr[49:band_8_clamp_time] - relax_time: Control.kr[52:band_8_relax_time] - - UnaryOpUGen(DB_TO_AMPLITUDE).kr/23: - source: Control.kr[50:band_8_postgain] + threshold: UnaryOpUGen(DB_TO_AMPLITUDE).kr/1[0] + slope_below: Control.kr[5:band_1_slope_below] + slope_above: Control.kr[4:band_1_slope_above] + clamp_time: Control.kr[0:band_1_clamp_time] + relax_time: Control.kr[3:band_1_relax_time] - BinaryOpUGen(MULTIPLICATION).ar/15: left: Compander.ar/7[0] - right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/23[0] + right: UnaryOpUGen(DB_TO_AMPLITUDE).kr/2[0] - Sum4.ar/1: - input_one: BinaryOpUGen(MULTIPLICATION).ar/9[0] - input_two: BinaryOpUGen(MULTIPLICATION).ar/11[0] - input_three: BinaryOpUGen(MULTIPLICATION).ar/13[0] - input_four: BinaryOpUGen(MULTIPLICATION).ar/15[0] + input_one: BinaryOpUGen(MULTIPLICATION).ar/15[0] + input_two: BinaryOpUGen(MULTIPLICATION).ar/13[0] + input_three: BinaryOpUGen(MULTIPLICATION).ar/11[0] + input_four: BinaryOpUGen(MULTIPLICATION).ar/9[0] - BinaryOpUGen(ADDITION).ar: - left: Sum4.ar/0[0] - right: Sum4.ar/1[0] + left: Sum4.ar/1[0] + right: Sum4.ar/0[0] - XOut.ar: bus: Control.ir[0:out] crossfade: Control.kr[56:mix] source[0]: BinaryOpUGen(ADDITION).ar[0] + """ clone = self._clone() clone._parameter_blocks.append(block_function) @@ -2111,7 +2243,9 @@ def with_rand_id(self, rand_id=0): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 + rand_id: 23.0 - RandID.ir: rand_id: Control.ir[1:rand_id] - In.ar: @@ -2219,7 +2353,8 @@ def with_signal_block(self, block_function): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -2308,7 +2443,8 @@ def with_silence_detection(self): synthdef: name: ... ugens: - - Control.ir: null + - Control.ir: + out: 0.0 - In.ar: bus: Control.ir[0:out] - ExpRand.ir/0: @@ -2361,7 +2497,11 @@ def with_silence_detection(self): synthdef: name: ... ugens: - - Control.ir: null + - Control.kr: + level: 1.0 + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -2371,7 +2511,6 @@ def with_silence_detection(self): source: Line.kr[0] - In.ar: bus: Control.ir[1:out] - - Control.kr: null - ExpRand.ir/0: minimum: 0.01 maximum: 0.1 diff --git a/supriya/ugens/ffsinosc.py b/supriya/ugens/ffsinosc.py index 944556581..7a5147e27 100644 --- a/supriya/ugens/ffsinosc.py +++ b/supriya/ugens/ffsinosc.py @@ -1,8 +1,9 @@ -from collections.abc import Sequence +from typing import Optional, Sequence from supriya import utils -from .core import UGen, param, ugen +from ..enums import CalculationRate +from .core import UGen, UGenOperable, UGenRecursiveInput, UGenScalarInput, param, ugen @ugen(ar=True, kr=True) @@ -50,17 +51,14 @@ class Klank(UGen): :: - >>> frequencies = [200, 671, 1153, 1723] - >>> amplitudes = None - >>> decay_times = [1, 1, 1, 1] - >>> specifications = [frequencies, amplitudes, decay_times] - >>> source = supriya.ugens.BrownNoise.ar() * 0.001 >>> klank = supriya.ugens.Klank.ar( + ... amplitudes=None, ... decay_scale=1, + ... decay_times=[1, 1, 1, 1], + ... frequencies=[200, 671, 1153, 1723], ... frequency_offset=0, ... frequency_scale=1, - ... source=source, - ... specifications=specifications, + ... source=supriya.ugens.BrownNoise.ar() * 0.001, ... ) >>> klank @@ -72,32 +70,29 @@ class Klank(UGen): decay_scale = param(1) specifications = param(unexpanded=True) - def __init__( - self, - calculation_rate=None, - decay_scale=1, - frequency_offset=0, - frequency_scale=1, - source=None, - specifications=None, - ): - # TODO: Refactor this to not override __init__? - frequencies, amplitudes, decay_times = specifications - assert len(frequencies) + @classmethod + def ar( + cls, + *, + amplitudes: Optional[Sequence[UGenScalarInput]] = None, + decay_scale: UGenRecursiveInput = 1, + decay_times: Optional[Sequence[UGenScalarInput]] = None, + frequencies: Sequence[UGenScalarInput], + frequency_offset: UGenRecursiveInput = 0, + frequency_scale: UGenRecursiveInput = 1, + source: UGenRecursiveInput, + ) -> UGenOperable: + if not frequencies: + raise ValueError(frequencies) if not amplitudes: amplitudes = [1.0] * len(frequencies) - elif not isinstance(amplitudes, Sequence): - amplitudes = [amplitudes] * len(frequencies) if not decay_times: decay_times = [1.0] * len(frequencies) - elif not isinstance(decay_times, Sequence): - decay_times = [decay_times] * len(frequencies) - specifications = utils.zip_cycled(frequencies, amplitudes, decay_times) - specifications = utils.flatten(specifications) - specifications = tuple(specifications) - UGen.__init__( - self, - calculation_rate=calculation_rate, + specifications: Sequence[UGenScalarInput] = list( + utils.flatten(utils.zip_cycled(frequencies, amplitudes, decay_times)) + ) + return cls._new_expanded( + calculation_rate=CalculationRate.AUDIO, decay_scale=decay_scale, frequency_offset=frequency_offset, frequency_scale=frequency_scale, diff --git a/supriya/ugens/granular.py b/supriya/ugens/granular.py index ea8c834d1..58d32ac01 100644 --- a/supriya/ugens/granular.py +++ b/supriya/ugens/granular.py @@ -19,7 +19,7 @@ class GrainBuf(UGen): ... trigger=0, ... ) >>> grain_buf - , ])> + """ trigger = param(0) @@ -49,7 +49,7 @@ class GrainIn(UGen): ... trigger=0, ... ) >>> grain_in - , ])> + """ trigger = param(0) diff --git a/supriya/ugens/hilbert.py b/supriya/ugens/hilbert.py index dec0bb593..c0ab2311b 100644 --- a/supriya/ugens/hilbert.py +++ b/supriya/ugens/hilbert.py @@ -33,7 +33,7 @@ class Hilbert(UGen): ... source=source, ... ) >>> hilbert - , ])> + """ source = param() diff --git a/supriya/ugens/inout.py b/supriya/ugens/inout.py index 360263452..ee5ad9d6c 100644 --- a/supriya/ugens/inout.py +++ b/supriya/ugens/inout.py @@ -13,7 +13,7 @@ class In(UGen): :: >>> supriya.ugens.In.ar(bus=0, channel_count=4) - , , , ])> + """ bus = param(0.0) @@ -33,7 +33,7 @@ class InFeedback(UGen): ... channel_count=2, ... ) >>> in_feedback - , ])> + """ bus = param(0.0) @@ -47,7 +47,7 @@ class LocalIn(UGen): :: >>> supriya.ugens.LocalIn.ar(channel_count=2) - , ])> + """ default = param(0.0, unexpanded=True) @@ -62,7 +62,7 @@ def _postprocess_kwargs( if not isinstance(default, Sequence): default = [default] kwargs["default"] = list( - repeat_to_length([float(x) for x in default], len(self)) + repeat_to_length([float(x) for x in default], self._channel_count) ) return calculation_rate, kwargs diff --git a/supriya/ugens/ml.py b/supriya/ugens/ml.py index 69fefcb13..30ef2f8ba 100644 --- a/supriya/ugens/ml.py +++ b/supriya/ugens/ml.py @@ -20,7 +20,7 @@ class BeatTrack(UGen): ... lock=0, ... ) >>> beat_track - , , , ])> + """ pv_chain = param() @@ -43,7 +43,7 @@ class BeatTrack2(UGen): ... window_size=2, ... ) >>> beat_track_2 - , , , , , ])> + """ bus_index = param(0.0) @@ -113,7 +113,7 @@ class MFCC(UGen): ... pv_chain=pv_chain, ... ) >>> mfcc - , , , , , , , , , , , , ])> + """ pv_chain = param() @@ -183,7 +183,7 @@ class Pitch(UGen): >>> source = supriya.ugens.In.ar(bus=0) >>> pitch = supriya.ugens.Pitch.kr(source=source) >>> pitch - , ])> + """ source = param() diff --git a/supriya/ugens/panning.py b/supriya/ugens/panning.py index 98a2ff4bd..e53b4356f 100644 --- a/supriya/ugens/panning.py +++ b/supriya/ugens/panning.py @@ -1,9 +1,17 @@ -import collections import math from ..enums import CalculationRate from .basic import Mix -from .core import PseudoUGen, UGen, param, ugen +from .core import ( + PseudoUGen, + UGen, + UGenOperable, + UGenRecursiveParams, + UGenVector, + _get_method_for_rate, + param, + ugen, +) @ugen(ar=True, kr=True, channel_count=2, fixed_channel_count=True) @@ -22,7 +30,7 @@ class Balance2(UGen): ... right=right, ... ) >>> balance_2 - , ])> + """ left = param() @@ -47,7 +55,7 @@ class BiPanB2(UGen): ... in_b=in_b, ... ) >>> bi_pan_b_2 - , , ])> + :: @@ -80,7 +88,7 @@ class DecodeB2(UGen): ... y=y, ... ) >>> decode_b_2 - , , , ])> + """ w = param() @@ -101,7 +109,7 @@ class Pan2(UGen): ... source=source, ... ) >>> pan_2 - , ])> + """ source = param() @@ -124,7 +132,7 @@ class Pan4(UGen): ... y_position=0, ... ) >>> pan_4 - , , , ])> + """ source = param() @@ -150,7 +158,7 @@ class PanAz(UGen): ... width=2, ... ) >>> pan_az - , , , , , , , ])> + """ source = param() @@ -175,7 +183,7 @@ class PanB(UGen): ... source=source, ... ) >>> pan_b - , , ])> + """ source = param() @@ -198,7 +206,7 @@ class PanB2(UGen): ... source=source, ... ) >>> pan_b_2 - , , ])> + """ source = param() @@ -223,7 +231,7 @@ class Rotate2(UGen): ... position=position, ... ) >>> rotate_2 - , ])> + Returns an array of the rotator's left and right outputs. """ @@ -311,39 +319,51 @@ class Splay(PseudoUGen): ### CLASS VARIABLES ### - _ordered_input_names = collections.OrderedDict( - [ - ("spread", 1), - ("level", 1), - ("center", 0), - ("normalize", True), - ("source", None), - ] + _ordered_keys = ( + "spread", + "level", + "center", + "normalize", + "source", ) - _unexpanded_input_names = ("source",) + _unexpanded_keys = ("source",) @classmethod def _new_expanded(cls, calculation_rate=None, **kwargs): - dictionaries = UGen._expand_dictionary( - kwargs, unexpanded_input_names=["source"] + def recurse( + all_expanded_params: UGenRecursiveParams, + ) -> UGenOperable: + if ( + not isinstance(all_expanded_params, dict) + and len(all_expanded_params) == 1 + ): + all_expanded_params = all_expanded_params[0] + if isinstance(all_expanded_params, dict): + return cls._new_single( + calculation_rate=calculation_rate, + special_index=0, + **all_expanded_params, + ) + return UGenVector( + *(recurse(expanded_params) for expanded_params in all_expanded_params) + ) + + return Mix.multichannel( + recurse(UGen._expand_params(kwargs, unexpanded_keys=cls._unexpanded_keys)), + 2, ) - ugens = [ - cls._new_single(calculation_rate=calculation_rate, **dictionary) - for dictionary in dictionaries - ] - return Mix.multichannel(ugens, 2) - - ### CLASS METHODS ### @classmethod def _new_single( cls, + *, calculation_rate=None, center=0, level=1, normalize=True, source=None, spread=1, + **kwargs, ): positions = [ (i * (2 / (len(source) - 1)) - 1) * spread + center @@ -354,7 +374,7 @@ def _new_single( level = level * math.sqrt(1 / len(source)) else: level = level / len(source) - panners = UGen._get_method_for_rate(Pan2, calculation_rate)( + panners = _get_method_for_rate(Pan2, calculation_rate)( source=source, position=positions ) return Mix.multichannel(panners, 2) * level diff --git a/supriya/ugens/pv.py b/supriya/ugens/pv.py index 834a4512a..bdb20adb0 100644 --- a/supriya/ugens/pv.py +++ b/supriya/ugens/pv.py @@ -3,7 +3,7 @@ from ..enums import CalculationRate from ..typing import Default from .bufio import LocalBuf -from .core import UGen, UGenOperable, param, ugen +from .core import OutputProxy, UGen, UGenOperable, param, ugen from .info import BufFrames @@ -20,7 +20,10 @@ def fft_size(self) -> UGenOperable: Returns ugen input. """ - return self.inputs[0].fft_size + input_ = self.inputs[0] + assert isinstance(input_, OutputProxy) + assert isinstance(input_.ugen, PV_ChainUGen) + return input_.ugen.fft_size @ugen(kr=True, is_width_first=True) diff --git a/supriya/ugens/triggers.py b/supriya/ugens/triggers.py index 15bf7dec7..962726e25 100644 --- a/supriya/ugens/triggers.py +++ b/supriya/ugens/triggers.py @@ -282,6 +282,7 @@ class Poll(UGen): trigger = param() source = param() trigger_id = param(-1) + label = param(unexpanded=True) ### INITIALIZER ### @@ -290,6 +291,7 @@ def __init__( calculation_rate=None, label=None, source=None, + special_index=None, trigger=None, trigger_id=-1, ): @@ -297,18 +299,16 @@ def __init__( if isinstance(source, UGen): label = type(source).__name__ elif isinstance(source, OutputProxy): - label = type(source.source).__name__ + label = type(source.ugen).__name__ + label = str(label) UGen.__init__( self, calculation_rate=calculation_rate, + label=[len(label), *(ord(c) for c in label)], source=source, trigger=trigger, trigger_id=trigger_id, ) - label = str(label) - self._configure_input("label", len(label)) - for character in label: - self._configure_input("label", ord(character)) ### PUBLIC METHODS ### @@ -342,34 +342,6 @@ def new(cls, label=None, source=None, trigger=None, trigger_id=-1): trigger_id=trigger_id, ) - ### PUBLIC PROPERTIES ### - - @property - def label(self): - """ - Gets `label` input of Poll. - - :: - - >>> sine = supriya.ugens.SinOsc.ar() - >>> trigger = supriya.ugens.Impulse.kr(frequency=1) - >>> poll = supriya.ugens.Poll.ar( - ... label="Foo", - ... source=sine, - ... trigger=trigger, - ... trigger_id=1234, - ... ).source - >>> poll.label - 'Foo' - - Returns ugen input. - """ - index = tuple(self._ordered_input_names).index("trigger_id") + 2 - characters = self._inputs[index:] - characters = [chr(int(_)) for _ in characters] - label = "".join(characters) - return label - @ugen(ar=True, kr=True) class RunningMax(UGen): diff --git a/tests/book/test_ext_book.py b/tests/book/test_ext_book.py index 5122a155a..5b006f55b 100644 --- a/tests/book/test_ext_book.py +++ b/tests/book/test_ext_book.py @@ -28,11 +28,8 @@ def test_sphinx_book_html(caplog, app, status, warning, rm_dirs): assert not warning.getvalue().strip() image_path = pathlib.Path(app.outdir) / "_images" expected_file_names = [ - "audio-4f0fd44621b74146c936fab67a7544438ddb60abe59b506082268778ec2e285f.mp3", - "audio-4f0fd44621b74146c936fab67a7544438ddb60abe59b506082268778ec2e285f.wav", - "plot-6e8cafcdb775004ffba3051b743f9d5a539d541c6601f723ecfcc3145f0217b4.svg", - "score-759292a876c9e866721fda737f939bcc7e84e04108d6adb207480e887de6b24a.aiff", - "score-759292a876c9e866721fda737f939bcc7e84e04108d6adb207480e887de6b24a.osc", + "score-c073aea37cd922f2fe26dbd47dfecee37e1cb2e6cdd06e86908b090add6e2c7e.aiff", + "score-c073aea37cd922f2fe26dbd47dfecee37e1cb2e6cdd06e86908b090add6e2c7e.osc", ] if platform.system() != "Windows": expected_file_names.extend( @@ -41,9 +38,22 @@ def test_sphinx_book_html(caplog, app, status, warning, rm_dirs): "say-3d7f815142f4edadfca73bd01c264223de1e4f5fd5e50a4c9e6f917eddeddba6.mp3", ] ) + # Haven't found a /simple/ cross-platforn solution for detecting audio device SR + expected_44100_file_names = expected_file_names + [ + "audio-08abe38d842cbaa19789618fe4675f1cf64de0eb6f9ab7ebd2165c078ce31429.mp3", + "audio-08abe38d842cbaa19789618fe4675f1cf64de0eb6f9ab7ebd2165c078ce31429.wav", + "plot-397eb6446e5486ac9137cb98affdda8577148ae41ef7857807f53be0793bc74a.svg", + ] + expected_48000_file_names = expected_file_names + [ + "audio-4f0fd44621b74146c936fab67a7544438ddb60abe59b506082268778ec2e285f.mp3", + "audio-4f0fd44621b74146c936fab67a7544438ddb60abe59b506082268778ec2e285f.wav", + "plot-6e8cafcdb775004ffba3051b743f9d5a539d541c6601f723ecfcc3145f0217b4.svg", + ] actual_file_names = sorted(path.name for path in image_path.iterdir()) print(actual_file_names) - assert all(file_name in actual_file_names for file_name in expected_file_names) + assert all( + file_name in actual_file_names for file_name in expected_44100_file_names + ) or all(file_name in actual_file_names for file_name in expected_48000_file_names) # audio and plot names are not stable across platforms audio_mp3_paths = list(image_path.glob("audio-*.mp3")) audio_wav_paths = list(image_path.glob("audio-*.wav")) diff --git a/tests/contexts/test_Score_nodes.py b/tests/contexts/test_Score_nodes.py index 6e5fecb44..47c4acc14 100644 --- a/tests/contexts/test_Score_nodes.py +++ b/tests/contexts/test_Score_nodes.py @@ -49,8 +49,8 @@ def test_add_group(context): def test_add_synth(context): - def compiled(x): - return compile_synthdefs(x) + def compiled(*synthdefs): + return compile_synthdefs(*synthdefs) with context.at(0): context.add_synthdefs(default) @@ -65,7 +65,7 @@ def compiled(x): assert list(context.iterate_osc_bundles()) == [ OscBundle( contents=( - OscMessage("/d_recv", compiled([default])), + OscMessage("/d_recv", compiled(default)), OscMessage("/s_new", "default", 1000, 0, 0), OscMessage( "/s_new", diff --git a/tests/contexts/test_Score_render.py b/tests/contexts/test_Score_render.py index 59f73a13e..07d8448b2 100644 --- a/tests/contexts/test_Score_render.py +++ b/tests/contexts/test_Score_render.py @@ -43,7 +43,7 @@ def context(): ( lambda path: dict(), lambda path: output_path - / "score-e6beae761b2c8f192cfcd48948f0b51239699355b4450e640ffc97ec7243c4a4.aiff", + / "score-1f2fad799464ced28d0af160e819d261488c4361ca8a32288f742f6e8e0fb01a.aiff", 8, 3.0, 44100, @@ -51,7 +51,7 @@ def context(): ( lambda path: dict(duration=1.5), lambda path: output_path - / "score-fbd0f324690ab445771bfa1932339f2cd4fc023e5e79986854091e79f1139f94.aiff", + / "score-d50ec5c18a801a6f0b2a2e61e65b1f1e320256272c90cef2293ef60cd7a5e839.aiff", 8, 1.5, 44100, @@ -59,7 +59,7 @@ def context(): ( lambda path: dict(output_bus_channel_count=2, duration=2.5), lambda path: output_path - / "score-31d5cda432402e03a4fdb507b479408d26eec2879f342e790d6c3ee825cab67d.aiff", + / "score-387cf3edc49f67f6ebbc144cd23306b960926dc673f2817c68543400493fb5e5.aiff", 2, 2.5, 44100, @@ -67,7 +67,7 @@ def context(): ( lambda path: dict(output_bus_channel_count=2, duration=3.0), lambda path: output_path - / "score-b7b44173bb96829b38b0792ac209e2dc96e0d9022c322f4a011b533f32579743.aiff", + / "score-846a0d8cfdcc83340a4cf7d181fab89e1f630637ff93c1354e9addb3ee2837b0.aiff", 2, 3.0, 44100, @@ -75,7 +75,7 @@ def context(): ( lambda path: dict(output_bus_channel_count=2, duration=3.5), lambda path: output_path - / "score-c8561182c6faabda4ad0b10278fe1edb62306f1beca0eaeba82311d7e5020368.aiff", + / "score-8581bbe2f384d3748d7cc156957483430da6b549662599b5b14d6fecc6e3f78a.aiff", 2, 3.5, 44100, @@ -83,7 +83,7 @@ def context(): ( lambda path: dict(render_directory_path=path), lambda path: output_path - / "score-e6beae761b2c8f192cfcd48948f0b51239699355b4450e640ffc97ec7243c4a4.aiff", + / "score-1f2fad799464ced28d0af160e819d261488c4361ca8a32288f742f6e8e0fb01a.aiff", 8, 3.0, 44100, @@ -98,7 +98,7 @@ def context(): ( lambda path: dict(sample_rate=48000), lambda path: output_path - / "score-2140a1b747033ecdbeac8c656586ba444abf60a6e1c60362c6aa40d9481612b7.aiff", + / "score-274be7ac8705739c0e67baf6a220a486f1e361aba9f7e92d536f666a8b43b28f.aiff", 8, 3.0, 48000, @@ -142,7 +142,7 @@ async def test_render( def test___render__(context): expected_path = ( output_path - / "score-e6beae761b2c8f192cfcd48948f0b51239699355b4450e640ffc97ec7243c4a4.aiff" + / "score-1f2fad799464ced28d0af160e819d261488c4361ca8a32288f742f6e8e0fb01a.aiff" ) if expected_path.exists(): expected_path.unlink() diff --git a/tests/contexts/test_Score_synthdefs.py b/tests/contexts/test_Score_synthdefs.py index 1525861e2..bbd370f43 100644 --- a/tests/contexts/test_Score_synthdefs.py +++ b/tests/contexts/test_Score_synthdefs.py @@ -29,8 +29,8 @@ def synthdefs(): def test_add_synthdefs(context, synthdefs): - def compiled(x): - return compile_synthdefs(x) + def compiled(*x): + return compile_synthdefs(*x) with context.at(0): # no synthdefs provided @@ -54,14 +54,14 @@ def compiled(x): assert list(context.iterate_osc_bundles()) == [ OscBundle( contents=( - OscMessage("/d_recv", compiled([synthdefs[0]])), - OscMessage("/d_recv", compiled(synthdefs)), + OscMessage("/d_recv", compiled(synthdefs[0])), + OscMessage("/d_recv", compiled(*synthdefs)), OscMessage( "/d_recv", - compiled([synthdefs[1]]), + compiled(synthdefs[1]), OscMessage("/g_new", 1000, 0, 0), ), - OscMessage("/d_recv", compiled([synthdefs[2]])), + OscMessage("/d_recv", compiled(synthdefs[2])), ), timestamp=0.0, ), @@ -69,7 +69,7 @@ def compiled(x): contents=[ OscMessage( "/d_recv", - compiled([synthdefs[2]]), + compiled(synthdefs[2]), OscMessage("/g_new", 1001, 0, 0), ) ], diff --git a/tests/contexts/test_Server_misc.py b/tests/contexts/test_Server_misc.py index 5b58eae7b..45de897f7 100644 --- a/tests/contexts/test_Server_misc.py +++ b/tests/contexts/test_Server_misc.py @@ -81,13 +81,13 @@ async def test_query_tree(context): 1 group 1001 group 1003 default - out: 0.0, amplitude: 0.1, frequency: 222.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 222.0, gate: 1.0, pan: 0.5, out: 0.0 1004 default - out: 0.0, amplitude: 0.1, frequency: 333.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 333.0, gate: 1.0, pan: 0.5, out: 0.0 1005 group 1000 group 1002 default - out: 0.0, amplitude: 0.1, frequency: 111.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 111.0, gate: 1.0, pan: 0.5, out: 0.0 """ ) # unsync diff --git a/tests/contexts/test_Server_nodes.py b/tests/contexts/test_Server_nodes.py index a529ee281..0a5087060 100644 --- a/tests/contexts/test_Server_nodes.py +++ b/tests/contexts/test_Server_nodes.py @@ -230,10 +230,10 @@ async def test_free_group_children(context): 1 group 1000 group 1002 default - out: 0.0, amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5, out: 0.0 1001 group 1003 default - out: 0.0, amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5, out: 0.0 """ ) # /g_freeAll @@ -259,10 +259,10 @@ async def test_free_group_children(context): 1 group 1000 group 1005 default - out: 0.0, amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5, out: 0.0 1004 group 1006 default - out: 0.0, amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5 + amplitude: 0.1, frequency: 440.0, gate: 1.0, pan: 0.5, out: 0.0 """ ) # /g_deepFree diff --git a/tests/contexts/test_Server_synthdefs.py b/tests/contexts/test_Server_synthdefs.py index ce827c4cf..218d6c7e4 100644 --- a/tests/contexts/test_Server_synthdefs.py +++ b/tests/contexts/test_Server_synthdefs.py @@ -40,8 +40,8 @@ def synthdefs(): @pytest.mark.asyncio async def test_add_synthdefs(context, synthdefs): - def compiled(x): - return compile_synthdefs(x) + def compiled(*x): + return compile_synthdefs(*x) with context.osc_protocol.capture() as transcript: # no synthdefs provided @@ -62,17 +62,15 @@ def compiled(x): with context.add_synthdefs(synthdefs[2]): context.add_group() assert transcript.filtered(received=False, status=False) == [ - OscMessage("/d_recv", compiled([synthdefs[0]])), - OscMessage("/d_recv", compiled(synthdefs)), - OscMessage( - "/d_recv", compiled([synthdefs[1]]), OscMessage("/g_new", 1000, 0, 1) - ), - OscMessage("/d_recv", compiled([synthdefs[2]])), + OscMessage("/d_recv", compiled(synthdefs[0])), + OscMessage("/d_recv", compiled(*synthdefs)), + OscMessage("/d_recv", compiled(synthdefs[1]), OscMessage("/g_new", 1000, 0, 1)), + OscMessage("/d_recv", compiled(synthdefs[2])), OscBundle( contents=[ OscMessage( "/d_recv", - compiled([synthdefs[2]]), + compiled(synthdefs[2]), OscMessage("/g_new", 1001, 0, 1), ) ], diff --git a/tests/ugens/test_SynthDefFactory.py b/tests/ugens/test_SynthDefFactory.py index a8ecf609e..fbd24b37d 100644 --- a/tests/ugens/test_SynthDefFactory.py +++ b/tests/ugens/test_SynthDefFactory.py @@ -17,14 +17,16 @@ def signal_block(builder, source, state): synthdef: name: test ugens: - - Control.ir: null - - Control.kr: null + - Control.kr: + gate: 1.0 - Linen.kr: gate: Control.kr[0:gate] attack_time: 0.02 sustain_level: 1.0 release_time: 0.02 done_action: 2.0 + - Control.ir: + out: 0.0 - SinOsc.ar: frequency: 440.0 phase: 0.0 @@ -51,8 +53,9 @@ def signal_block(builder, source, state): synthdef: name: test ugens: - - Control.ir: null - - Control.kr: null + - Control.kr: + gate: 1.0 + mix: 0.0 - Linen.kr: gate: Control.kr[0:gate] attack_time: 0.02 @@ -62,6 +65,8 @@ def signal_block(builder, source, state): - BinaryOpUGen(MULTIPLICATION).kr: left: Control.kr[1:mix] right: Linen.kr[0] + - Control.ir: + out: 0.0 - SinOsc.ar: frequency: 440.0 phase: 0.0 @@ -86,7 +91,17 @@ def signal_block(builder, source, state): synthdef: name: test ugens: - - Control.ir: null + - Control.kr: + gate: 1.0 + - Linen.kr: + gate: Control.kr[0:gate] + attack_time: 0.02 + sustain_level: 1.0 + release_time: 0.02 + done_action: 2.0 + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -94,13 +109,6 @@ def signal_block(builder, source, state): done_action: 2.0 - UnaryOpUGen(HANNING_WINDOW).kr: source: Line.kr[0] - - Control.kr: null - - Linen.kr: - gate: Control.kr[0:gate] - attack_time: 0.02 - sustain_level: 1.0 - release_time: 0.02 - done_action: 2.0 - BinaryOpUGen(MULTIPLICATION).kr: left: UnaryOpUGen(HANNING_WINDOW).kr[0] right: Linen.kr[0] @@ -128,7 +136,18 @@ def signal_block(builder, source, state): synthdef: name: test ugens: - - Control.ir: null + - Control.kr: + gate: 1.0 + level: 1.0 + - Linen.kr: + gate: Control.kr[0:gate] + attack_time: 0.02 + sustain_level: 1.0 + release_time: 0.02 + done_action: 2.0 + - Control.ir: + duration: 1.0 + out: 0.0 - Line.kr: start: 0.0 stop: 1.0 @@ -136,13 +155,6 @@ def signal_block(builder, source, state): done_action: 2.0 - UnaryOpUGen(HANNING_WINDOW).kr: source: Line.kr[0] - - Control.kr: null - - Linen.kr: - gate: Control.kr[0:gate] - attack_time: 0.02 - sustain_level: 1.0 - release_time: 0.02 - done_action: 2.0 - BinaryOpUGen(MULTIPLICATION).kr/0: left: UnaryOpUGen(HANNING_WINDOW).kr[0] right: Control.kr[1:level] diff --git a/tests/ugens/test_SynthDef___str__.py b/tests/ugens/test_SynthDef___str__.py index 1f533d0e7..169b6cd8d 100644 --- a/tests/ugens/test_SynthDef___str__.py +++ b/tests/ugens/test_SynthDef___str__.py @@ -15,7 +15,11 @@ def test_multi_value_parameters(): synthdef: name: 58528261cb129f5bee634d41a34e082c ugens: - - Control.kr: null + - Control.kr: + amp: 0.1 + freqs[0]: 300.0 + freqs[1]: 400.0 + out: 0.0 - SinOsc.ar/0: frequency: Control.kr[1:freqs[0]] phase: 0.0 diff --git a/tests/ugens/test_SynthDef_parameters.py b/tests/ugens/test_SynthDef_parameters.py index b83d06513..e4845288c 100644 --- a/tests/ugens/test_SynthDef_parameters.py +++ b/tests/ugens/test_SynthDef_parameters.py @@ -29,12 +29,12 @@ def py_synthdef_01(): def test_parameters_01_parameters(py_synthdef_01): - assert py_synthdef_01.indexed_parameters == ( + assert py_synthdef_01.indexed_parameters == [ ( 0, - Parameter(name="freq", parameter_rate=ParameterRate.CONTROL, value=440.0), + Parameter(name="freq", rate=ParameterRate.CONTROL, value=440.0), ), - ) + ] @pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") @@ -112,16 +112,16 @@ def py_synthdef_02(): def test_parameters_02_parameters(py_synthdef_02): - assert py_synthdef_02.indexed_parameters == ( + assert py_synthdef_02.indexed_parameters == [ ( 0, - Parameter(name="freq", parameter_rate=ParameterRate.CONTROL, value=1200.0), + Parameter(name="freq", rate=ParameterRate.CONTROL, value=1200.0), ), ( 1, - Parameter(name="out", parameter_rate=ParameterRate.CONTROL, value=23.0), + Parameter(name="out", rate=ParameterRate.CONTROL, value=23.0), ), - ) + ] @pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") @@ -210,24 +210,20 @@ def test_parameters_03_parameters(py_synthdef_03): """ Multiple parameters, including unused parameters. """ - assert py_synthdef_03.indexed_parameters == ( + assert py_synthdef_03.indexed_parameters == [ ( 0, - Parameter(name="damping", parameter_rate=ParameterRate.CONTROL, value=0.1), + Parameter(name="damping", rate=ParameterRate.CONTROL, value=0.1), ), ( 1, - Parameter( - name="delay_time", parameter_rate=ParameterRate.CONTROL, value=1.0 - ), + Parameter(name="delay_time", rate=ParameterRate.CONTROL, value=1.0), ), ( 2, - Parameter( - name="room_size", parameter_rate=ParameterRate.CONTROL, value=0.9 - ), + Parameter(name="room_size", rate=ParameterRate.CONTROL, value=0.9), ), - ) + ] @pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") @@ -332,186 +328,6 @@ def py_synthdef_04(): return py_synthdef -def test_parameters_04_parameters(py_synthdef_04): - """ - Different calculation rates. - """ - assert py_synthdef_04.indexed_parameters == ( - ( - 3, - Parameter(name="a_phase", parameter_rate=ParameterRate.AUDIO, value=0.0), - ), - ( - 4, - Parameter(name="freq", parameter_rate=ParameterRate.CONTROL, value=440.0), - ), - ( - 0, - Parameter( - name="i_decay_time", parameter_rate=ParameterRate.SCALAR, value=1.0 - ), - ), - ( - 1, - Parameter(name="t_trig_a", parameter_rate=ParameterRate.TRIGGER, value=0.0), - ), - ( - 2, - Parameter(name="t_trig_b", parameter_rate=ParameterRate.TRIGGER, value=0.0), - ), - ) - - -@pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") -@pytest.mark.skipif( - platform.system() == "Darwin" and os.environ.get("CI") == "true", - reason="sclang hangs without QT", -) -def test_parameters_04_supriya_vs_sclang(py_synthdef_04): - sc_synthdef = SuperColliderSynthDef( - "trigTest", - r""" - | - a_phase = 0.0, - freq = 440, - i_decay_time = 1.0, - t_trig_a = 0, - t_trig_b = 0 - | - var decay = Decay2.kr([t_trig_a, t_trig_b], 0.005, i_decay_time); -     Out.ar(0, SinOsc.ar(freq, a_phase) * decay); - """, - ) - sc_compiled_synthdef = bytes(sc_synthdef.compile()) - py_compiled_synthdef = py_synthdef_04.compile() - assert py_compiled_synthdef == sc_compiled_synthdef - - -def test_parameters_04_supriya_vs_bytes(py_synthdef_04): - # fmt: off - test_compiled_synthdef = bytes( - b'SCgf' - b'\x00\x00\x00\x02' - b'\x00\x01' - b'\x08trigTest' - b'\x00\x00\x00\x02' - b';\xa3\xd7\n' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x05' - b'?\x80\x00\x00' # i_decay_time - b'\x00\x00\x00\x00' # t_trig_a - b'\x00\x00\x00\x00' # t_trig_b - b'\x00\x00\x00\x00' # a_phase - b'C\xdc\x00\x00' # freq - b'\x00\x00\x00\x05' - b'\x07a_phase' - b'\x00\x00\x00\x03' - b'\x04freq' - b'\x00\x00\x00\x04' - b'\x0ci_decay_time' - b'\x00\x00\x00\x00' - b'\x08t_trig_a' - b'\x00\x00\x00\x01' - b'\x08t_trig_b' - b'\x00\x00\x00\x02' - b'\x00\x00\x00\n' - b'\x07Control' - b'\x00' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x01' - b'\x00\x00' - b'\x00' - b'\x0bTrigControl' - b'\x01' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x02' - b'\x00\x01' - b'\x01' - b'\x01' - b'\x06Decay2' - b'\x01' - b'\x00\x00\x00\x03' - b'\x00\x00\x00\x01' - b'\x00\x00' - b'\x00\x00\x00\x01' - b'\x00\x00\x00\x00' - b'\xff\xff\xff\xff' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x00' - b'\x01' - b'\x06Decay2' - b'\x01' - b'\x00\x00\x00\x03' - b'\x00\x00\x00\x01' - b'\x00\x00' - b'\x00\x00\x00\x01' - b'\x00\x00\x00\x01' - b'\xff\xff\xff\xff' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x00' - b'\x01' - b'\x0cAudioControl' - b'\x02' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x01' - b'\x00\x03' - b'\x02' - b'\x07Control' - b'\x01' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x01' - b'\x00\x04' - b'\x01' - b'\x06SinOsc' - b'\x02' - b'\x00\x00\x00\x02' - b'\x00\x00\x00\x01' - b'\x00\x00' - b'\x00\x00\x00\x05' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x04' - b'\x00\x00\x00\x00' - b'\x02' - b'\x0cBinaryOpUGen' - b'\x02' - b'\x00\x00\x00\x02' - b'\x00\x00\x00\x01' - b'\x00\x02' - b'\x00\x00\x00\x06' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x02' - b'\x00\x00\x00\x00' - b'\x02' - b'\x0cBinaryOpUGen' - b'\x02' - b'\x00\x00\x00\x02' - b'\x00\x00\x00\x01' - b'\x00\x02' - b'\x00\x00\x00\x06' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x03' - b'\x00\x00\x00\x00' - b'\x02' - b'\x03Out' - b'\x02' - b'\x00\x00\x00\x03' - b'\x00\x00\x00\x00' - b'\x00\x00' - b'\xff\xff\xff\xff' - b'\x00\x00\x00\x01' - b'\x00\x00\x00\x07' - b'\x00\x00\x00\x00' - b'\x00\x00\x00\x08' - b'\x00\x00\x00\x00' - b'\x00\x00' - ) - # fmt: on - py_compiled_synthdef = py_synthdef_04.compile() - assert py_compiled_synthdef == test_compiled_synthdef - - @pytest.fixture def py_synthdef_05(): builder = SynthDefBuilder(amp=0.1, freqs=[300, 400]) @@ -528,18 +344,16 @@ def test_parameters_05_parameters(py_synthdef_05): """ Literal array arguments. """ - assert py_synthdef_05.indexed_parameters == ( + assert py_synthdef_05.indexed_parameters == [ ( 0, - Parameter(name="amp", parameter_rate=ParameterRate.CONTROL, value=0.1), + Parameter(name="amp", rate=ParameterRate.CONTROL, value=0.1), ), ( 1, - Parameter( - name="freqs", parameter_rate=ParameterRate.CONTROL, value=(300.0, 400.0) - ), + Parameter(name="freqs", rate=ParameterRate.CONTROL, value=(300.0, 400.0)), ), - ) + ] @pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") @@ -664,21 +478,21 @@ def test_parameters_06_parameters(py_synthdef_06): """ Literal array arguments. """ - assert py_synthdef_06.indexed_parameters == ( + assert py_synthdef_06.indexed_parameters == [ ( 0, - Parameter(name="amp", parameter_rate=ParameterRate.CONTROL, value=0.1), + Parameter(name="amp", rate=ParameterRate.CONTROL, value=0.1), ), ( 1, Parameter( lag=0.5, name="freqs", - parameter_rate=ParameterRate.CONTROL, + rate=ParameterRate.CONTROL, value=(300.0, 400.0), ), ), - ) + ] @pytest.mark.skipif(platform.system() == "Windows", reason="hangs on Windows") @@ -807,7 +621,10 @@ def test_parameters_07(): synthdef: name: simple_sine ugens: - - Control.kr: null + - Control.kr: + amplitude: 0.0 + bus: 0.0 + frequency: 440.0 - SinOsc.ar: frequency: Control.kr[2:frequency] phase: 0.0 @@ -835,7 +652,10 @@ def test_building_is_idempotent(): synthdef: name: 937772273a43d21bcd7b9f096f42648a ugens: - - Control.kr: null + - Control.kr: + amplitude: 0.0 + bus: 0.0 + frequency: 440.0 - SinOsc.ar: frequency: Control.kr[2:frequency] phase: 0.0 diff --git a/tests/ugens/test_SynthDef_shared.py b/tests/ugens/test_SynthDef_shared.py index 9715833e8..923c38c5b 100644 --- a/tests/ugens/test_SynthDef_shared.py +++ b/tests/ugens/test_SynthDef_shared.py @@ -2,6 +2,7 @@ import supriya.ugens from supriya import SynthDefBuilder +from supriya.ugens.core import SynthDefError def test_01(): @@ -15,7 +16,7 @@ def test_01(): with SynthDefBuilder(): sine_two = supriya.ugens.SinOsc.ar() - with pytest.raises(ValueError) as exception_info: + with pytest.raises(SynthDefError) as exception_info: sine_two * sine_one assert "UGen input in different scope" in str(exception_info.value) @@ -32,7 +33,7 @@ def test_02(): with SynthDefBuilder(): sine_two = supriya.ugens.SinOsc.ar() - with pytest.raises(ValueError) as exception_info: + with pytest.raises(SynthDefError) as exception_info: supriya.ugens.Out.ar(bus=synth_one_bus, source=sine_two) assert "UGen input in different scope" in str(exception_info.value) @@ -46,6 +47,6 @@ def test_03(): supriya.ugens.Out.ar(bus=0, source=[right, left]) with SynthDefBuilder(): - with pytest.raises(ValueError) as exception_info: + with pytest.raises(SynthDefError) as exception_info: left * right assert "UGen input in different scope" in str(exception_info.value) diff --git a/tests/ugens/test_SynthDef_splay.py b/tests/ugens/test_SynthDef_splay.py index 21fa1ce5d..0b3281e3b 100644 --- a/tests/ugens/test_SynthDef_splay.py +++ b/tests/ugens/test_SynthDef_splay.py @@ -34,7 +34,10 @@ def test_Splay_01_sclang(server): synthdef: name: test ugens: - - Control.kr: null + - Control.kr: + spread: 1.0 + level: 0.20000000298023224 + center: 0.0 - BinaryOpUGen(SUBTRACTION).kr: left: Control.kr[2:center] right: Control.kr[0:spread] @@ -119,7 +122,10 @@ def test_Splay_01_supriya(server): synthdef: name: test ugens: - - Control.kr: null + - Control.kr: + center: 0.0 + level: 0.2 + spread: 1.0 - UnaryOpUGen(NEGATIVE).kr: source: Control.kr[2:spread] - BinaryOpUGen(ADDITION).kr/0: @@ -216,7 +222,9 @@ def test_Splay_02_sclang(server): synthdef: name: test ugens: - - Control.kr: null + - Control.kr: + spread: 1.0 + level: 0.20000000298023224 - BinaryOpUGen(SUBTRACTION).kr/0: left: -0.25 right: Control.kr[0:spread] @@ -364,7 +372,9 @@ def test_Splay_02_supriya(server): synthdef: name: test ugens: - - Control.kr: null + - Control.kr: + level: 0.2 + spread: 1.0 - UnaryOpUGen(NEGATIVE).kr/0: source: Control.kr[1:spread] - BinaryOpUGen(ADDITION).kr/0: diff --git a/tests/ugens/test_SynthDef_system.py b/tests/ugens/test_SynthDef_system.py index fead91ba8..824756e40 100644 --- a/tests/ugens/test_SynthDef_system.py +++ b/tests/ugens/test_SynthDef_system.py @@ -14,7 +14,12 @@ def test_system_link_audio_1_supriya(): synthdef: name: system_link_audio_1 ugens: - - Control.kr: null + - Control.kr: + done_action: 2.0 + fade_time: 0.02 + gate: 1.0 + in_: 16.0 + out: 0.0 - BinaryOpUGen(LESS_THAN_OR_EQUAL).kr: left: Control.kr[1:fade_time] right: 0.0 @@ -99,8 +104,14 @@ def test_system_link_audio_1_sclang(): synthdef: name: system_link_audio_1 ugens: - - Control.ir: null - - Control.kr: null + - Control.ir: + doneAction: 2.0 + - Control.kr: + out: 0.0 + in: 16.0 + vol: 1.0 + level: 1.0 + lag: 0.05000000074505806 - BinaryOpUGen(MULTIPLICATION).kr/0: left: Control.kr[2:vol] right: Control.kr[3:level] @@ -109,8 +120,10 @@ def test_system_link_audio_1_sclang(): lag_time: Control.kr[4:lag] - InFeedback.ar: bus: Control.kr[1:in] - - Control.kr: null - - Control.kr: null + - Control.kr: + gate: 1.0 + - Control.kr: + fadeTime: 0.019999999552965164 - EnvGen.kr: gate: Control.kr[0:gate] level_scale: 1.0 @@ -148,7 +161,12 @@ def test_system_link_audio_2_supriya(): synthdef: name: system_link_audio_2 ugens: - - Control.kr: null + - Control.kr: + done_action: 2.0 + fade_time: 0.02 + gate: 1.0 + in_: 16.0 + out: 0.0 - BinaryOpUGen(LESS_THAN_OR_EQUAL).kr: left: Control.kr[1:fade_time] right: 0.0 @@ -240,8 +258,14 @@ def test_system_link_audio_2_sclang(): synthdef: name: system_link_audio_2 ugens: - - Control.ir: null - - Control.kr: null + - Control.ir: + doneAction: 2.0 + - Control.kr: + out: 0.0 + in: 16.0 + vol: 1.0 + level: 1.0 + lag: 0.05000000074505806 - BinaryOpUGen(MULTIPLICATION).kr/0: left: Control.kr[2:vol] right: Control.kr[3:level] @@ -250,8 +274,10 @@ def test_system_link_audio_2_sclang(): lag_time: Control.kr[4:lag] - InFeedback.ar: bus: Control.kr[1:in] - - Control.kr: null - - Control.kr: null + - Control.kr: + gate: 1.0 + - Control.kr: + fadeTime: 0.019999999552965164 - EnvGen.kr: gate: Control.kr[0:gate] level_scale: 1.0