Skip to content

Commit

Permalink
Added a new --superdirt_verbose flag:
Browse files Browse the repository at this point in the history
- users can toggle on SuperCollider & SuperDirt textual output.
- Sardine is able to monitor SuperDirt textual output.
- rewrote and changed a few function/attributes/variables name for the
  sake of clarity.
  • Loading branch information
Bubobubobubobubo committed Aug 25, 2022
1 parent eae4327 commit fc312fc
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 54 deletions.
1 change: 1 addition & 0 deletions cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def bool_check(thing: Any):
parser.add_argument("--ppqn", type=float, help="ppqn")
parser.add_argument("--parameters", type=str, help="add a custom param")
parser.add_argument("--boot_superdirt", type=str2bool, help="Boot SC && SuperDirt")
parser.add_argument("--verbose_superdirt", type=str2bool, help="Toggle SuperDirt textual output")
parser.add_argument("--deferred", type=str2bool, help="Turn on/off deferred scheduling")
parser.add_argument("--clock", type=str2bool, help="Active or passive Clock")
parser.add_argument("--SCconfig", type=str2bool, help="SuperDirt Configuration Path")
Expand Down
4 changes: 3 additions & 1 deletion sardine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@


if config.boot_superdirt:
SC = SuperColliderProcess(startup_file=config.superdirt_config_path)
SC = SuperColliderProcess(
startup_file=config.superdirt_config_path,
verbose=config.verbose_superdirt)


c = Clock(
Expand Down
4 changes: 4 additions & 0 deletions sardine/io/UserConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"ppqn": 48,
"parameters": [],
"boot_superdirt": True,
"verbose_superdirt": False,
"active_clock": True,
"superdirt_config_path": str(USER_DIR / "default_superdirt.scd"),
"user_config_path": str(USER_DIR / "user_configuration.py"),
Expand All @@ -50,6 +51,7 @@ class Config:
ppqn: int
bpm: int
superdirt_config_path: str
verbose_superdirt: bool
user_config_path: str
boot_superdirt: bool
active_clock: bool
Expand All @@ -65,6 +67,7 @@ def from_dict(cls, data: dict) -> "Config":
ppqn=config["ppqn"],
bpm=config["bpm"],
boot_superdirt=config["boot_superdirt"],
verbose_superdirt=config["verbose_superdirt"],
active_clock=config["active_clock"],
superdirt_config_path=config["superdirt_config_path"],
user_config_path=config["user_config_path"],
Expand All @@ -80,6 +83,7 @@ def to_dict(self) -> dict:
"ppqn": self.ppqn,
"bpm": self.bpm,
"boot_superdirt": self.boot_superdirt,
"verbose_superdirt": self.verbose_superdirt,
"superdirt_config_path": self.superdirt_config_path,
"active_clock": self.active_clock,
"user_config_path": self.user_config_path,
Expand Down
107 changes: 54 additions & 53 deletions sardine/superdirt/AutoBoot.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,38 @@
#!/usr/bin/env python3
from os import walk, path
from pathlib import Path
import platform, subprocess, os, signal
import platform, subprocess
import shutil
from typing import Union
from appdirs import *

import tempfile
import psutil
import asyncio
from rich import print

__all__ = ("SuperColliderProcess",)


class SuperColliderProcess:

"""
Start SCLang process. Allows the execution of SuperCollider
code directly from the Python side.
"""
def __init__(self, startup_file: Union[str, None] = None, preemptive=True, verbose=False):

def __init__(self, startup_file: Union[str, None] = None, preemptive=True):
appname, appauthor = "Sardine", "Bubobubobubo"
self._user_dir = Path(user_data_dir(appname, appauthor))
self._sclang_path = self.find_sclang_path()
self._synth_directory = self._find_synths_directory()
self._startup_file = self._find_startup_file(user_file=startup_file)
self.temp_file = tempfile.NamedTemporaryFile()

# If preemptive, all previously running instances of SuperCollider will be
# killed to prevent more issues...
if preemptive:
self.hard_kill()

self._sclang_proc = subprocess.Popen(
[self._sclang_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
start_new_session=True,
)

self.boot()

if verbose:
asyncio.create_task(self.monitor())

def _find_vanilla_startup_file(self):
"""Find the startup file when booting Sardine"""
cur_path = Path(__file__).parent.resolve()
Expand Down Expand Up @@ -77,26 +69,28 @@ def _find_synths_directory(self) -> Path:

def terminate(self) -> None:
"""Terminate the SCLang process"""

self.send("Server.killAll; 0.exit;")
self._sclang_proc.terminate()

def reset(self) -> None:
"""Restart the SCLang subprocess"""

self._sclang_proc = subprocess.Popen(
[self._sclang_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
start_new_session=True,
)
self.write_stdin("Server.killAll; 0.exit;")
self._sclang.terminate()

async def monitor(self):
"""Monitoring SuperCollider output using an asynchronous function
that runs on a loop and prints to output. Can be quite verbose at
boot time!"""
while self._sclang.poll() is None:
where = self.temp_file.tell()
lines = self.temp_file.read()
if not lines:
await asyncio.sleep(0.1)
self.temp_file.seek(where)
else:
import sys
sys.__stdout__.write(lines.decode())
sys.__stdout__.flush()
sys.__stdout__.write(self.temp_file.read())
sys.__stdout__.flush()

def hard_kill(self) -> None:
"""Look for an instance of SuperCollider, kill it."""

"""Look for all instances of SuperCollider, kill them."""
print("\n[bold red]Preemptive: Killing all SC instances...[/bold red]")
try:
for proc in psutil.process_iter():
Expand All @@ -108,12 +102,8 @@ def hard_kill(self) -> None:
except Exception:
print(f"[yellow]There was no SC process to kill...")

def send(self, message: str):

"""
Pipe strings to SCLang: message: single or multi-line string.
TODO: Fix multiline support.
"""
def write_stdin(self, message: str):
"""Write to sclang stdin using Python strings"""

# Converting messages for multiline-input
message = "".join(message.splitlines())
Expand All @@ -123,20 +113,20 @@ def send(self, message: str):
message += "\n"

# Writing messages
self._sclang_proc.stdin.write(message)
self._sclang_proc.stdin.flush()
self._sclang.stdin.write(message)
self._sclang.stdin.flush()

def meter(self) -> None:
"""Open SuperCollider VUmeter"""
self.send("s.meter()")
self.write_stdin("s.meter()")

def scope(self) -> None:
"""Open SuperCollider frequency scope"""
self.send("s.scope()")
self.write_stdin("s.scope()")

def meterscope(self) -> None:
"""Open SuperCollider frequency scope + VUmeter"""
self.send("s.scope(); s.meter()")
self.write_stdin("s.scope(); s.meter()")

def check_synth_file_extension(self, string: str) -> bool:
return string.endswith(".scd") or string.endswith(".sc")
Expand All @@ -157,7 +147,7 @@ def load_custom_synthdefs(self) -> None:
buffer += line

# sending the string to the interpreter
self.send(buffer)
self.write_stdin(buffer)
print("Loaded SynthDefs:")
for f in files:
print("- {}".format(f))
Expand Down Expand Up @@ -186,14 +176,25 @@ def find_sclang_path(self) -> str:
raise OSError("This OS is not officially supported by Sardine.")

def boot(self) -> None:

"""Booting a background instance of SCLang!"""
print("\n[red]Starting SCLang && SuperDirt[/red]")
self.send(message="""load("{}")""".format(self._startup_file))
# print(f"{self._sclang_proc.communicate()[0]}")
self._sclang = subprocess.Popen(
[self._sclang_path],
stdin=subprocess.PIPE,
stdout=self.temp_file,
stderr=self.temp_file,
bufsize=1,
universal_newlines=True,
start_new_session=True,
)

self.write_stdin(message="""load("{}")""".format(self._startup_file))

if self._synth_directory is not None:
self.load_custom_synthdefs()

def kill(self) -> None:
"""Kill the connexion with the SC Interpreter"""
self.send("Server.killAll")
self.send("0.exit")
self.write_stdin("Server.killAll")
self.write_stdin("0.exit")

0 comments on commit fc312fc

Please sign in to comment.