class SynthTestWithSClang(SCBaseTest):

    __test__ = True
    start_sclang = True

    def setUp(self) -> None:
        self.assertIsNotNone(SynthTestWithSClang.sc.lang)
        self.custom_nodeid = 42
        self.all_synth_args = {
            "freq": 400,
            "amp": 0.3,
            "num": 4,
            "pan": 0,
            "lg": 0.1
        }
        self.synth = Synth("s2", nodeid=self.custom_nodeid)

    def tearDown(self) -> None:
        self.synth.free()
        time.sleep(0.1)

    def test_synth_desc(self):
        self.assertIsNotNone(self.synth.synth_desc)
        for name, synth_arg in self.synth.synth_desc.items():
            self.assertEqual(name, synth_arg.name)
            self.assertAlmostEqual(self.synth.get(name), synth_arg.default)

    def test_getattr(self):
        for name, value in self.all_synth_args.items():
            self.assertAlmostEqual(self.synth.__getattr__(name), value)
Пример #2
0
 def update_synth(self) -> None:
     """Update volume Synth"""
     amp = 0.0 if self._muted else dbamp(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
Пример #3
0
 def test_node_registry(self):
     copy1 = Synth(nodeid=self.synth.nodeid, new=False)
     copy2 = Synth(nodeid=self.custom_nodeid, new=False)
     self.assertIs(self.synth, copy1)
     self.assertIs(self.synth, copy2)
     self.assertIs(copy1, copy2)
     del copy1, copy2
Пример #4
0
    def test_wait(self):
        duration = 0.15
        tol = 0.05

        def check(synth):
            self.assertEqual(synth.is_playing, None)
            self.assertEqual(synth.started, True)
            self.assertEqual(synth.freed, False)
            self.assertEqual(synth.group, self.sc.server.default_group.nodeid)
            t_wait_for_notification = time.time()
            while not synth.is_playing:
                if time.time() - t_wait_for_notification > 0.15:
                    self.fail("Waiting for /n_go notification took too long.")
            self.assertEqual(synth.is_playing, True)
            self.assertEqual(synth.started, True)
            self.assertEqual(synth.freed, False)
            synth.wait(timeout=1)
            time_played = time.time() - t0
            self.assertEqual(synth.is_playing, False)
            self.assertEqual(synth.started, False)
            self.assertEqual(synth.freed, True)
            self.assertEqual(synth.group, None)
            self.assertLessEqual(time_played, duration + tol)
            self.assertGreaterEqual(time_played, duration - tol)

        t0 = time.time()
        with self.assertWarnsRegex(
            UserWarning, "SynthDesc 's1' is unknown", msg="SynthDesc seems to be known"
        ):
            s1_synth = Synth("s1", controls={"dur": duration, "amp": 0.0})
        check(s1_synth)
        t0 = time.time()
        s1_synth.new()
        check(s1_synth)
 def setUp(self) -> None:
     self.assertIsNotNone(SynthTestWithSClang.sc.lang)
     self.custom_nodeid = 42
     self.all_synth_args = {
         "freq": 400,
         "amp": 0.3,
         "num": 4,
         "pan": 0,
         "lg": 0.1
     }
     self.synth = Synth("s2", nodeid=self.custom_nodeid)
Пример #6
0
 def setUp(self) -> None:
     with self.assertRaises(RuntimeError):
         SynthTest.sc.lang
     self.custom_nodeid = 42
     self.synth_args = {"amp": 0.0, "num": 3}
     warnings.simplefilter("always", UserWarning)
     with self.assertWarnsRegex(UserWarning,
                                "SynthDesc 's2' is unknown",
                                msg="SynthDesc seems to be known"):
         self.synth = Synth("s2",
                            controls=self.synth_args,
                            nodeid=self.custom_nodeid)
     self.assertIsNone(self.synth._synth_desc)
     self.sc.server.sync()
Пример #7
0
class VolumeTest(SCBaseTest):

    __test__ = True
    start_sclang = True

    def setUp(self) -> None:
        self.assertIsNotNone(VolumeTest.sc.lang)
        VolumeTest.sc.server.unmute()
        self.assertFalse(VolumeTest.sc.server.muted)
        vol_synth = VolumeTest.sc.server._volume._synth
        if vol_synth is not None:
            vol_synth.wait(timeout=1)
        del vol_synth
        self.assertIsNone(VolumeTest.sc.server._volume._synth)
        self.custom_nodeid = 42
        self.all_synth_args = {
            "freq": 400,
            "amp": 0.3,
            "num": 4,
            "pan": 0,
            "lg": 0.1
        }
        self.synth = Synth("s2", nodeid=self.custom_nodeid)

    def tearDown(self) -> None:
        self.synth.free()
        time.sleep(0.1)

    def test_synth_desc(self):
        num_channels = VolumeTest.sc.server.options.num_output_buses
        self.assertIsNotNone(
            SynthDef.get_description(f"sc3nb_volumeAmpControl{num_channels}"))

    def test_set_volume(self):
        volume = -10
        VolumeTest.sc.server.volume = volume
        self.assertEqual(volume, VolumeTest.sc.server.volume)
        vol_synth = VolumeTest.sc.server._volume._synth
        self.assertIn(vol_synth, VolumeTest.sc.server.query_tree().children)
        self.assertAlmostEqual(dbamp(volume), vol_synth.get("volumeAmp"))
        VolumeTest.sc.server.muted = True
        VolumeTest.sc.server.volume = 0
        self.assertAlmostEqual(0, vol_synth.get("volumeAmp"))
        VolumeTest.sc.server.muted = False
        vol_synth.wait(timeout=0.2)
        self.assertNotIn(vol_synth, VolumeTest.sc.server.query_tree().children)
        del vol_synth
        self.assertFalse(VolumeTest.sc.server.muted)
        self.assertIsNone(VolumeTest.sc.server._volume._synth)
Пример #8
0
 def setUp(self) -> None:
     self.assertIsNotNone(VolumeTest.sc.lang)
     VolumeTest.sc.server.unmute()
     self.assertFalse(VolumeTest.sc.server.muted)
     vol_synth = VolumeTest.sc.server._volume._synth
     if vol_synth is not None:
         vol_synth.wait(timeout=1)
     del vol_synth
     self.assertIsNone(VolumeTest.sc.server._volume._synth)
     self.custom_nodeid = 42
     self.all_synth_args = {
         "freq": 400,
         "amp": 0.3,
         "num": 4,
         "pan": 0,
         "lg": 0.1
     }
     self.synth = Synth("s2", nodeid=self.custom_nodeid)
Пример #9
0
    def start(
        self,
        timetag: float = 0,
        duration: Optional[float] = None,
        node: Union[Node, int] = 0,
        bus: int = 0,
    ):
        """Start the recording.

        Parameters
        ----------
        timetag : float, by default 0 (immediately)
            Time (or time offset when <1e6) to start
        duration : float, optional
            Length of the recording, by default until stopped.
        node : Union[Node, int], optional
            Node that should be recorded, by default 0
        bus : int, by default 0
            Bus that should be recorded

        Raises
        ------
        RuntimeError
            When trying to start a recording unprepared.
        """
        if self._state != RecorderState.PREPARED:
            raise RuntimeError(
                f"Recorder state must be PREPARED but is {self._state}")
        args = dict(bus=bus,
                    duration=duration or -1,
                    bufnum=self._record_buffer.bufnum)

        with self._server.bundler(timetag=timetag):
            self._record_synth = Synth(
                self._synth_name,
                controls=args,
                server=self._server,
                target=node,
                add_action=AddAction.TO_TAIL,
            )
        self._state = RecorderState.RECORDING
Пример #10
0
    def test_fast_wait(self):
        duration = 0.15
        tol = 0.05

        def check(synth):
            self.assertEqual(synth.is_playing, False)
            self.assertEqual(synth.started, False)
            self.assertEqual(synth.freed, True)
            time_played = time.time() - t0
            self.assertLessEqual(time_played, duration + tol)
            self.assertGreaterEqual(time_played, duration - tol)

        t0 = time.time()
        with self.assertWarnsRegex(
            UserWarning, "SynthDesc 's1' is unknown", msg="SynthDesc seems to be known"
        ):
            s1_synth = Synth("s1", controls={"dur": duration, "amp": 0.0})
        s1_synth.wait(timeout=1)
        check(s1_synth)

        t0 = time.time()
        s1_synth.new()
        s1_synth.wait(timeout=1)
        check(s1_synth)
Пример #11
0
 def test_setattr(self):
     with self.assertWarnsRegex(UserWarning,
                                "SynthDesc 's2' is unknown",
                                msg="SynthDesc seems to be known"):
         synth1 = Synth("s2", controls={"amp": 0.0}, target=self.group)
     with self.assertWarnsRegex(UserWarning,
                                "SynthDesc 's2' is unknown",
                                msg="SynthDesc seems to be known"):
         synth2 = Synth("s2", controls={"amp": 0.0}, target=self.group)
     GroupTest.sc.server.sync()
     cmd_args = {"pan": -1.0, "num": 1}
     for name, value in cmd_args.items():
         self.group.set(name, value)
     GroupTest.sc.server.sync()
     self.assertEqual(synth1.group, self.group.nodeid)
     self.assertEqual(synth2.group, self.group.nodeid)
     for name, value in cmd_args.items():
         self.assertAlmostEqual(synth1.get(name), value)
         self.assertAlmostEqual(synth2.get(name), value)
Пример #12
0
class Recorder:
    """Allows to record audio easily."""

    # TODO rec_header, rec_format with Literal type (py3.8) from Buffer
    def __init__(
        self,
        path: str = "record.wav",
        nr_channels: int = 2,
        rec_header: str = "wav",
        rec_format: str = "int16",
        bufsize: int = 65536,
        server: Optional[SCServer] = None,
    ):
        """Create and prepare a recorder.

        Parameters
        ----------
        path : str, optional
            path of recording file, by default "record.wav"
        nr_channels : int, optional
            Number of channels, by default 2
        rec_header : str, optional
            File format, by default "wav"
        rec_format : str, optional
            Recording resolution, by default "int16"
        bufsize : int, optional
            size of buffer, by default 65536
        server : SCServer, optional
            server used for recording,
            by default use the SC default server
        """
        self._state = RecorderState.UNPREPARED
        self._server = server or SC.get_default().server
        self._record_buffer = Buffer(server=self._server)
        self._record_synth: Optional[Synth] = None
        self.prepare(path, nr_channels, rec_header, rec_format, bufsize)

    def prepare(
        self,
        path: str = "record.wav",
        nr_channels: int = 2,
        rec_header: str = "wav",
        rec_format: str = "int16",
        bufsize: int = 65536,
    ):
        """Pepare the recorder.

        Parameters
        ----------
        path : str, optional
            path of recording file, by default "record.wav"
        nr_channels : int, optional
            Number of channels, by default 2
        rec_header : str, optional
            File format, by default "wav"
        rec_format : str, optional
            Recording resolution, by default "int16"
        bufsize : int, optional
            size of buffer, by default 65536

        Raises
        ------
        RuntimeError
            When Recorder does not needs to be prepared.
        """
        if self._state != RecorderState.UNPREPARED:
            raise RuntimeError(
                f"Recorder state must be UNPREPARED but is {self._state}")
        # prepare buffer
        self._record_buffer.alloc(bufsize, channels=nr_channels)
        self._record_buffer.write(
            path=path,
            header=rec_header,
            sample=rec_format,
            num_frames=0,
            starting_frame=0,
            leave_open=True,
        )
        self._rec_id = self._record_buffer.bufnum
        # TODO we could prepare the synthDef beforehand and just use the right one here.
        # This would allow Recordings without sclang
        self._synth_def = SynthDef(
            f"sc3nb_recording_{self._rec_id}",
            r"""{ |bus, bufnum, duration|
            var tick = Impulse.kr(1);
            var timer = PulseCount.kr(tick) - 1;
            Line.kr(0, 0, duration, doneAction: if(duration <= 0, 0, 2));
            SendReply.kr(tick, '/recordingDuration', timer, ^rec_id);
            DiskOut.ar(bufnum, In.ar(bus, ^nr_channels))
        }""",
        )
        self._synth_name = self._synth_def.add(pyvars={
            "rec_id": self._rec_id,
            "nr_channels": nr_channels
        })
        self._state = RecorderState.PREPARED

    def start(
        self,
        timetag: float = 0,
        duration: Optional[float] = None,
        node: Union[Node, int] = 0,
        bus: int = 0,
    ):
        """Start the recording.

        Parameters
        ----------
        timetag : float, by default 0 (immediately)
            Time (or time offset when <1e6) to start
        duration : float, optional
            Length of the recording, by default until stopped.
        node : Union[Node, int], optional
            Node that should be recorded, by default 0
        bus : int, by default 0
            Bus that should be recorded

        Raises
        ------
        RuntimeError
            When trying to start a recording unprepared.
        """
        if self._state != RecorderState.PREPARED:
            raise RuntimeError(
                f"Recorder state must be PREPARED but is {self._state}")
        args = dict(bus=bus,
                    duration=duration or -1,
                    bufnum=self._record_buffer.bufnum)

        with self._server.bundler(timetag=timetag):
            self._record_synth = Synth(
                self._synth_name,
                controls=args,
                server=self._server,
                target=node,
                add_action=AddAction.TO_TAIL,
            )
        self._state = RecorderState.RECORDING

    def pause(self, timetag: float = 0):
        """Pause the recording.

        Parameters
        ----------
        timetag : float, by default 0 (immediately)
            Time (or time offset when <1e6) to pause

        Raises
        ------
        RuntimeError
            When trying to pause if not recording.
        """
        if self._state != RecorderState.RECORDING or self._record_synth is None:
            raise RuntimeError(
                f"Recorder state must be RECORDING but is {self._state}")
        with self._server.bundler(timetag=timetag):
            self._record_synth.run(False)
        self._state = RecorderState.PAUSED

    def resume(self, timetag: float = 0):
        """Resume the recording

        Parameters
        ----------
        timetag : float, by default 0 (immediately)
            Time (or time offset when <1e6) to resume

        Raises
        ------
        RuntimeError
            When trying to resume if not paused.
        """
        if self._state != RecorderState.PAUSED or self._record_synth is None:
            raise RuntimeError(
                f"Recorder state must be PAUSED but is {self._state}")
        with self._server.bundler(timetag=timetag):
            self._record_synth.run(True)
        self._state = RecorderState.RECORDING

    def stop(self, timetag: float = 0):
        """Stop the recording.

        Parameters
        ----------
        timetag : float, by default 0 (immediately)
            Time (or time offset when <1e6) to stop

        Raises
        ------
        RuntimeError
            When trying to stop if not started.
        """
        if (self._state not in [RecorderState.RECORDING, RecorderState.PAUSED]
                or self._record_synth is None):
            raise RuntimeError(
                f"Recorder state must be RECORDING or PAUSED but is {self._state}"
            )

        with self._server.bundler(timetag=timetag):
            self._record_synth.free()
            self._record_synth = None
            self._record_buffer.close()
        self._state = RecorderState.UNPREPARED

    def __repr__(self) -> str:
        return f"<Recorder [{self._state.value}]>"

    def __del__(self):
        try:
            self.stop()
        except RuntimeError:
            pass
        self._record_buffer.free()
Пример #13
0
 def test_reuse_nodeid(self):
     with self.assertWarnsRegex(
         UserWarning, "SynthDesc 's2' is unknown", msg="SynthDesc seems to be known"
     ):
         synth1 = Synth("s2", {"amp": 0.0})
         nodeid = synth1.nodeid
         synth1.free()
         synth1.wait(timeout=1)
         group = Group(nodeid=nodeid)
         with self.assertRaisesRegex(RuntimeError, "Tried to get "):
             Synth("s2", nodeid=nodeid)
         group.free()
         group.wait(timeout=1)
         synth2 = Synth("s2", nodeid=nodeid)
         synth2.free()
         synth2.wait(timeout=1)
Пример #14
0
 def test_too_many_arguments(self):
     with self.assertRaises(TypeError):
         Synth("s2", {"amp": 0.0}, "this is too much!")
     with self.assertRaises(TypeError):
         Group(101, "this is too much!")
Пример #15
0
class SynthTest(SCBaseTest):

    __test__ = True

    def setUp(self) -> None:
        with self.assertRaises(RuntimeError):
            SynthTest.sc.lang
        self.custom_nodeid = 42
        self.synth_args = {"amp": 0.0, "num": 3}
        warnings.simplefilter("always", UserWarning)
        with self.assertWarnsRegex(UserWarning,
                                   "SynthDesc 's2' is unknown",
                                   msg="SynthDesc seems to be known"):
            self.synth = Synth("s2",
                               controls=self.synth_args,
                               nodeid=self.custom_nodeid)
        self.assertIsNone(self.synth._synth_desc)
        self.sc.server.sync()

    def tearDown(self) -> None:
        nodeid = self.synth.nodeid
        self.assertIn(nodeid, self.sc.server.nodes)
        self.synth.free()
        self.synth.wait()
        del self.synth  # make sure that synth is deleted from registry
        t0 = time.time()
        while nodeid in self.sc.server.nodes:
            time.sleep(0.005)
            if time.time() - t0 > 0.2:
                self.fail("NodeID is still in server.nodes")
        self.assertNotIn(nodeid, self.sc.server.nodes)
        with self.assertRaises(KeyError):
            del self.sc.server.nodes[nodeid]

    def test_node_registry(self):
        copy1 = Synth(nodeid=self.synth.nodeid, new=False)
        copy2 = Synth(nodeid=self.custom_nodeid, new=False)
        self.assertIs(self.synth, copy1)
        self.assertIs(self.synth, copy2)
        self.assertIs(copy1, copy2)
        del copy1, copy2

    def test_set_get(self):
        for name, value in {"amp": 0.0, "num": 1}.items():
            self.synth.__setattr__(name, value)
            self.assertAlmostEqual(self.synth.__getattr__(name), value)

        with self.assertWarnsRegex(UserWarning,
                                   "Setting 'freq' as python attribute"):
            with self.assertRaisesRegex(AttributeError, "no attribute 'freq'"):
                self.synth.__getattribute__(
                    "freq")  # should not have a python attribute named freq
            self.synth.freq = 420  # should warn if setting attribute

        self.assertAlmostEqual(self.synth.get("freq"),
                               400)  # default freq of s2 SynthDef

        with self.assertWarnsRegex(UserWarning,
                                   "recognized as Node Parameter now"):
            self.synth.set("freq", 100)
        self.assertAlmostEqual(self.synth.get("freq"), 100)

        self.synth.freq = 300
        self.assertAlmostEqual(self.synth.get("freq"), 300)

        for name, value in self.synth_args.items():
            self.assertAlmostEqual(self.synth.__getattr__(name), value)

    def test_new_warning(self):
        with self.assertLogs(level="WARNING") as log:
            self.synth.new()
            time.sleep(0.1)
        self.assertTrue("duplicate node ID" in log.output[-1])

    def test_query(self):
        query_result = self.synth.query()
        self.assertIsInstance(query_result, SynthInfo)
        self.assertEqual(query_result.nodeid, self.custom_nodeid)
        self.assertEqual(query_result.group,
                         SynthTest.sc.server.default_group.nodeid)
        self.assertEqual(query_result.prev_nodeid, -1)
        self.assertEqual(query_result.next_nodeid, -1)
Пример #16
0
 def test_duplicate(self):
     self.assertNotIn("/s_new", self.sc.server.fails)
     with self.assertWarnsRegex(
         UserWarning, "SynthDesc 's2' is unknown", msg="SynthDesc seems to be known"
     ):
         synth1 = Synth("s2", controls={"amp": 0.0})
         wait_t0 = time.time()
         synth1.new(controls={"amp": 0.0})
         while not "/s_new" in self.sc.server.fails:
             self.assertLessEqual(time.time() - wait_t0, 0.5)
         self.assertEqual(self.sc.server.fails["/s_new"].get(), "duplicate node ID")
         synth1.free()
         synth1.wait(timeout=1)
         synth1.new({"amp": 0.0})
         synth1.free()
         synth1.wait(timeout=1)
         with self.assertRaises(Empty):
             self.sc.server.fails["/s_new"].get(timeout=0.5)
Пример #17
0
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_synthdef)
        self._server.add_init_hook(self.update_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_synth()

    def mute(self) -> None:
        """Mute audio"""
        self._muted = True
        self.update_synth()

    def unmute(self) -> None:
        """Unmute audio"""
        self._muted = False
        self.update_synth()

    def update_synth(self) -> None:
        """Update volume Synth"""
        amp = 0.0 if self._muted else dbamp(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

    def send_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"