"""Server Volume controls."""
import logging
import warnings
from typing import TYPE_CHECKING, Optional
from pyamapping import clip, db_to_amp
from sc3nb.sc_objects.node import AddAction, Synth
from sc3nb.sc_objects.synthdef import SynthDef
if TYPE_CHECKING:
from sc3nb.sc_objects.server import SCServer
[docs]_LOGGER = logging.getLogger(__name__)
[docs]class Volume:
"""Server volume controls"""
def __init__(self, server: "SCServer", min_: int = -90, max_: int = 6) -> None:
self._server = server
self._server.add_init_hook(self.send__volume_synthdef)
self._server.add_init_hook(self.update_volume_synth)
self.min = min_
self.max = max_
self._muted = False
self._volume = 0.0
self._lag = 0.1
self._synth_name: Optional[str] = None
self._synth: Optional[Synth] = None
@property
def muted(self):
"""True if muted."""
return self._muted
@muted.setter
def muted(self, muted: bool):
if muted:
self.mute()
else:
self.unmute()
@property
def volume(self):
"""Volume in dB."""
return self._volume
@volume.setter
def volume(self, volume):
self._volume = clip(volume, self.min, self.max)
self.update_volume_synth()
[docs] def mute(self) -> None:
"""Mute audio"""
self._muted = True
self.update_volume_synth()
[docs] def unmute(self) -> None:
"""Unmute audio"""
self._muted = False
self.update_volume_synth()
[docs] def update_volume_synth(self) -> None:
"""Update volume Synth"""
amp = 0.0 if self._muted else db_to_amp(self._volume)
active = amp != 1.0
if active:
if self._server.is_running:
if self._synth is None:
if self._synth_name is None:
warnings.warn(
"Cannot set volume. Volume SynthDef unknown. Is the default sclang running?"
)
return
controls = {
"volumeAmp": amp,
"volumeLag": self._lag,
"bus": self._server.output_bus.idxs[0],
}
self._synth = Synth(
self._synth_name,
add_action=AddAction.AFTER,
target=self._server.default_group,
controls=controls,
server=self._server,
)
else:
self._synth.set("volumeAmp", amp)
else:
if self._synth is not None:
self._synth.release()
self._synth = None
[docs] def send__volume_synthdef(self):
"""Send Volume SynthDef"""
if self._server.is_running:
num_channels = self._server.output_bus.num_channels
synth_def = SynthDef(
f"sc3nb_volumeAmpControl{num_channels}",
r"""{ | volumeAmp = 1, volumeLag = 0.1, gate=1, bus |
XOut.ar(bus,
Linen.kr(gate, releaseTime: 0.05, doneAction:2),
In.ar(bus, ^num_channels) * Lag.kr(volumeAmp, volumeLag)
);
}""",
)
try:
self._server.lookup_receiver("sclang")
except KeyError:
_LOGGER.info(
"Volume SynthDef cannot be send. No sclang receiver known."
)
else:
self._synth_name = synth_def.add(server=self._server)
assert self._synth_name is not None, "Synth name is None"