diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e5845f2..12d01df 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: test: strategy: matrix: - python: ["3.8", "3.9", "3.10"] + python: ["3.8", "3.9", "3.10", "3.11", "3.12"] platform: [ubuntu-latest, macos-latest] fail-fast: false runs-on: ${{ matrix.platform }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ed351a..88e92c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v5.0.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace @@ -11,12 +11,12 @@ repos: - id: check-builtin-literals - id: check-merge-conflict - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 24.10.0 hooks: - id: black language_version: python3 - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort args: ["--profile", "black", "--filter-files"] @@ -25,7 +25,7 @@ repos: # hooks: # - id: check-manifest - repo: https://github.com/kynan/nbstripout - rev: 0.6.1 + rev: 0.8.1 hooks: - id: nbstripout args: [--extra-keys=metadata.kernelspec metadata.language_info] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f3a8c8f..9a9a6ce 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,21 +7,20 @@ version: 2 sphinx: configuration: docs/source/conf.py +build: + os: ubuntu-lts-latest + apt_packages: + - portaudio19-dev + - supercollider + tools: + python: "3.12" + # Build all formats formats: all python: - version: 3.7 install: - method: pip path: . extra_requirements: - docs - -# TODO currently readthedocs uses ubuntu18.04, this is old -# it also does not support starting supercollider as it lacks X server support. -# https://docs.readthedocs.io/en/stable/config-file/v2.html#build-apt-packages -build: - image: latest - apt_packages: - - portaudio19-dev diff --git a/CHANGELOG.md b/CHANGELOG.md index 934ee3c..8bcfddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Version 1.1.1 + +- Fix startup [issue #18](https://github.com/interactive-sonification/sc3nb/issues/18) +- Other minor improvements + +[See all changes](https://github.com/interactive-sonification/sc3nb/compare/v1.1.0...v1.1.0) + + ## Version 1.1.0 - Bundler Improvements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7672479..d837732 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,10 +43,11 @@ Additional dependencies for sc3nb can be installed via the following extras: | Install | Purpose | |:---------------|:-----------------------------------------------------------------------------| +| `[all]` | install all extras | | `[test]` | running tox for tests and other things | | `[dev]` | using the pre-commit hooks and installing other useful tools for development | | `[docs]` | building the docs directly, without tox (used by tox) | -| `[localtest]` | running pytest directly, without tox (used by tox) | +| `[localtest]` | running pytest directly, without tox (used by tox) | Normally you should only need `[test]` and `[dev]` for contributing. @@ -73,6 +74,12 @@ The following tests should all be successful. ``` tox -e docs ``` + Note that you also need [pandoc](https://pandoc.org/) installed. + If you use conda you can use + ``` + conda install pandoc + ``` + Controll the output in `build/docs/html/` ## How to prepare a release diff --git a/docs/source/conf.py b/docs/source/conf.py index acb9e20..a1b11c2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -86,7 +86,7 @@ autosummary_generate = True -numpydoc_validation_checks = {"all", "GL01", "GL02", "GL05"} +numpydoc_validation_checks = {"all", "GL08"} intersphinx_mapping = {"python": ("https://docs.python.org/dev", None)} diff --git a/examples/supercollider-objects/score-examples.ipynb b/examples/supercollider-objects/score-examples.ipynb index a18259a..910bf5a 100644 --- a/examples/supercollider-objects/score-examples.ipynb +++ b/examples/supercollider-objects/score-examples.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "d94cc915", + "id": "0", "metadata": {}, "source": [ "# Score" @@ -11,7 +11,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8a02aaad", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -21,7 +21,7 @@ { "cell_type": "code", "execution_count": null, - "id": "639cfa3c", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -31,7 +31,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75ae0551", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -40,7 +40,7 @@ }, { "cell_type": "markdown", - "id": "c4a54ee3", + "id": "4", "metadata": {}, "source": [ "The Score class can be used for non-realtime synthesis. \n", @@ -56,7 +56,7 @@ { "cell_type": "code", "execution_count": null, - "id": "09f49299", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +65,7 @@ }, { "cell_type": "markdown", - "id": "a70f0bbf", + "id": "6", "metadata": {}, "source": [ "Lets create a simple SynthDef for this demonstration" @@ -74,7 +74,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2946df64", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "e5831fd0", + "id": "8", "metadata": {}, "source": [ "For creating the messages its recommended to use the Bundler class" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b809a28d", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "90cd5332", + "id": "10", "metadata": {}, "source": [ "The corresponding messages can be seen with" @@ -123,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "91e17fe6", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -132,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "41c1ce0f", + "id": "12", "metadata": {}, "source": [ "Lets start the non-realtime synthesis" @@ -141,7 +141,7 @@ { "cell_type": "code", "execution_count": null, - "id": "027fc513", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -150,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "2f73cc4c", + "id": "14", "metadata": {}, "source": [ "Lets listen to the created audio file with the IPython Audio class that allows to read and play audio files " @@ -159,7 +159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0a9a54c5", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -169,7 +169,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b53bc871", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -179,7 +179,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f5a6e0b7", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -189,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "809080b1", + "id": "18", "metadata": {}, "outputs": [], "source": [] diff --git a/src/sc3nb/__init__.py b/src/sc3nb/__init__.py index befa58f..026aff9 100644 --- a/src/sc3nb/__init__.py +++ b/src/sc3nb/__init__.py @@ -9,7 +9,6 @@ For example usage please refer to the user guide. """ - from sc3nb.sc import startup, SC from sc3nb.sc_objects.server import SCServer, ServerOptions diff --git a/src/sc3nb/magics.py b/src/sc3nb/magics.py index 635cae1..00e2ee7 100644 --- a/src/sc3nb/magics.py +++ b/src/sc3nb/magics.py @@ -1,4 +1,5 @@ """This module adds the Jupyter specialties such as Magics and Keyboard Shortcuts""" + import re import sys import warnings diff --git a/src/sc3nb/osc/parsing.py b/src/sc3nb/osc/parsing.py index ccc0747..15cefed 100644 --- a/src/sc3nb/osc/parsing.py +++ b/src/sc3nb/osc/parsing.py @@ -8,6 +8,7 @@ as this list or when nested as bundles with inner list """ + import logging import math from typing import Any, Sequence, Tuple, Union diff --git a/src/sc3nb/sc.py b/src/sc3nb/sc.py index 6f7ccdc..a469d86 100644 --- a/src/sc3nb/sc.py +++ b/src/sc3nb/sc.py @@ -24,6 +24,7 @@ def startup( scsynth_options: Optional[ServerOptions] = None, with_blip: bool = True, console_logging: bool = False, + kill_others: bool = True, allowed_parents: Sequence[str] = ALLOWED_PARENTS, timeout: float = 10, ) -> "SC": @@ -72,6 +73,7 @@ def startup( scsynth_options=scsynth_options, with_blip=with_blip, console_logging=console_logging, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -80,6 +82,7 @@ def startup( if start_sclang: SC.get_default().start_sclang( sclang_path=sclang_path, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -87,6 +90,7 @@ def startup( SC.get_default().start_server( scsynth_options=scsynth_options, scsynth_path=scsynth_path, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -157,6 +161,7 @@ def __init__( scsynth_options: Optional[ServerOptions] = None, with_blip: bool = True, console_logging: bool = True, + kill_others: bool = True, allowed_parents: Sequence[str] = ALLOWED_PARENTS, timeout: float = 5, ): @@ -170,6 +175,7 @@ def __init__( self.start_sclang( sclang_path=sclang_path, console_logging=self._console_logging, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -179,6 +185,7 @@ def __init__( scsynth_options=scsynth_options, console_logging=self._console_logging, with_blip=with_blip, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -191,6 +198,7 @@ def start_sclang( self, sclang_path: Optional[str] = None, console_logging: bool = True, + kill_others: bool = True, allowed_parents: Sequence[str] = ALLOWED_PARENTS, timeout: float = 5, ): @@ -214,6 +222,7 @@ def start_sclang( self._sclang.start( sclang_path=sclang_path, console_logging=console_logging, + kill_others=kill_others, allowed_parents=allowed_parents, timeout=timeout, ) @@ -233,6 +242,7 @@ def start_server( scsynth_path: Optional[str] = None, console_logging: bool = True, with_blip: bool = True, + kill_others: bool = True, allowed_parents: Sequence[str] = ALLOWED_PARENTS, timeout: float = 5, ): @@ -260,6 +270,7 @@ def start_server( self._server.boot( scsynth_path=scsynth_path, console_logging=console_logging, + kill_others=kill_others, allowed_parents=allowed_parents, with_blip=with_blip, timeout=timeout, diff --git a/src/sc3nb/sc_objects/score.py b/src/sc3nb/sc_objects/score.py index 5fa59aa..ea410eb 100644 --- a/src/sc3nb/sc_objects/score.py +++ b/src/sc3nb/sc_objects/score.py @@ -3,6 +3,7 @@ `SuperCollider Guide - Non-Realtime Synthesis `_ """ + import os import platform import subprocess diff --git a/src/sc3nb/sc_objects/server.py b/src/sc3nb/sc_objects/server.py index 120038f..c4ccc29 100644 --- a/src/sc3nb/sc_objects/server.py +++ b/src/sc3nb/sc_objects/server.py @@ -1,4 +1,5 @@ """Module for managing Server related stuff.""" + import atexit import logging import time @@ -36,6 +37,8 @@ _LOGGER = logging.getLogger(__name__) +# TODO py3.11++ ==> Class(str,Enum) -> StrEnum - see issue #18 + @unique class MasterControlReply(str, Enum): @@ -680,7 +683,7 @@ def remote(self, address: str, port: int, with_blip: bool = True) -> None: self.init(with_blip=with_blip) self._has_booted = True - def reboot(self) -> None: + def reboot(self, with_blip: bool = True) -> None: """Reboot this server Raises @@ -692,7 +695,7 @@ def reboot(self) -> None: raise RuntimeError("Can't reboot a remote Server") receivers = self._receivers # save known receivers and restore them after boot self.quit() - self.boot() + self.boot(with_blip=with_blip) receivers.update( self._receivers ) # update old receivers with possible new values @@ -870,9 +873,7 @@ def send_default_groups(self) -> None: client_ids = range(self._max_logins) def create_default_group(client_id) -> Group: - return Group( - nodeid=2**26 * client_id + 1, target=0, server=self, new=True - ) + return Group(nodeid=2**26 * client_id + 1, target=0, server=self, new=True) self._default_groups = { client: create_default_group(client) for client in client_ids diff --git a/src/sc3nb/sc_objects/synthdef.py b/src/sc3nb/sc_objects/synthdef.py index 3b72732..4611b38 100644 --- a/src/sc3nb/sc_objects/synthdef.py +++ b/src/sc3nb/sc_objects/synthdef.py @@ -307,17 +307,42 @@ def add( # Create new SynthDef add it to SynthDescLib and get bytes if self.sc is None: self.sc = sc3nb.SC.get_default() - synth_def_blob, output = self.sc.lang.cmd( + synth_def_blob_size, output = self.sc.lang.cmd( f""" "sc3nb - Creating SynthDef {self.name}".postln; r.tmpSynthDef = SynthDef("{self.name}", {self.current_def}); SynthDescLib.global.add(r.tmpSynthDef.asSynthDesc); - r.tmpSynthDef.asBytes();""", + r.tmpBlob = r.tmpSynthDef.asBytes(); + r.tmpBlob.size""", pyvars=pyvars, verbose=False, get_result=True, get_output=True, ) + # fetch r.tmpBlob chunk by chunk + chunk_size = 8000 + if synth_def_blob_size < 8160: + synth_def_blob = self.sc.lang.cmd( + f"""r.tmpBlob""", + verbose=False, + get_result=True, + get_output=False, + ) + else: + nr_chunks = int(synth_def_blob_size / chunk_size + 1) + position = 0 + chunk_list = [] + for k in range(nr_chunks): + chunk = self.sc.lang.cmd( + f"""r.tmpBlob[{position}..{position+chunk_size-1}]""", + verbose=False, + get_result=True, + get_output=False, + ) + position += chunk_size + chunk_list.append(chunk) + synth_def_blob = b"".join(chunk_list) + if synth_def_blob == 0: print(output) raise RuntimeError(f"Adding SynthDef failed. - {output}") diff --git a/src/sc3nb/sc_objects/volume.py b/src/sc3nb/sc_objects/volume.py index a881c1c..b22562f 100644 --- a/src/sc3nb/sc_objects/volume.py +++ b/src/sc3nb/sc_objects/volume.py @@ -1,6 +1,5 @@ """Server Volume controls.""" - import logging import warnings from typing import TYPE_CHECKING, Optional diff --git a/src/sc3nb/sclang.py b/src/sc3nb/sclang.py index 13f9522..f0e6856 100644 --- a/src/sc3nb/sclang.py +++ b/src/sc3nb/sclang.py @@ -1,4 +1,5 @@ """Module for handling a SuperCollider language (sclang) process.""" + import logging import re import sys @@ -69,6 +70,7 @@ def start( self, sclang_path: Optional[str] = None, console_logging: bool = True, + kill_others: bool = True, allowed_parents: Sequence[str] = ALLOWED_PARENTS, timeout: float = 10, ) -> None: @@ -100,6 +102,7 @@ def start( executable="sclang", executable_path=sclang_path, console_logging=console_logging, + kill_others=kill_others, allowed_parents=allowed_parents, ) try: @@ -147,7 +150,7 @@ def init(self): addr.sendMsg(^replyAddress, msgContent); result; // result should be returned };""", - pyvars={"replyAddress": ReplyAddress.RETURN_ADDR}, + pyvars={"replyAddress": ReplyAddress.RETURN_ADDR.value}, ) print("Done.") diff --git a/src/sc3nb/util.py b/src/sc3nb/util.py index 21bae89..3c29a88 100644 --- a/src/sc3nb/util.py +++ b/src/sc3nb/util.py @@ -1,4 +1,5 @@ """Module with utlilty functions - especially for handling code snippets""" + import inspect import re import socket