sc-kernel 0.3.2__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sc_kernel/__init__.py +1 -1
- sc_kernel/kernel.py +189 -128
- sc_kernel-0.5.0.dist-info/METADATA +218 -0
- sc_kernel-0.5.0.dist-info/RECORD +14 -0
- {sc_kernel-0.3.2.dist-info → sc_kernel-0.5.0.dist-info}/WHEEL +1 -1
- sc_kernel-0.3.2.dist-info/METADATA +0 -139
- sc_kernel-0.3.2.dist-info/RECORD +0 -14
- {sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/kernel.json +0 -0
- {sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/logo-32x32.png +0 -0
- {sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/logo-64x64.png +0 -0
- {sc_kernel-0.3.2.dist-info → sc_kernel-0.5.0.dist-info/licenses}/LICENSE +0 -0
- {sc_kernel-0.3.2.dist-info → sc_kernel-0.5.0.dist-info}/top_level.txt +0 -0
sc_kernel/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ =
|
1
|
+
__version__ = "0.4.0"
|
sc_kernel/kernel.py
CHANGED
@@ -1,49 +1,128 @@
|
|
1
1
|
import json
|
2
2
|
import platform
|
3
3
|
import sys
|
4
|
-
import
|
4
|
+
from functools import partial
|
5
5
|
from subprocess import check_output
|
6
6
|
import re
|
7
|
-
from typing import
|
7
|
+
from typing import Dict, Optional
|
8
8
|
import os
|
9
9
|
|
10
10
|
import pexpect
|
11
11
|
from IPython.lib.display import Audio
|
12
|
+
from IPython.display import Image
|
12
13
|
from metakernel import ProcessMetaKernel, REPLWrapper
|
13
14
|
|
14
15
|
from . import __version__
|
15
16
|
|
17
|
+
# idea from
|
18
|
+
# https://github.com/supercollider/qpm/blob/d3f72894e289744f01f3c098ab0a474d5190315e/qpm/sclang_process.py#L62
|
19
|
+
|
20
|
+
PLOT_HELPER = """
|
21
|
+
var plot = {|w, fileName, waitTime|
|
22
|
+
var image;
|
23
|
+
var filePath;
|
24
|
+
(waitTime?1.0).wait;
|
25
|
+
fileName = fileName ? "sc_%.png".format({rrand(0, 9)}.dup(10).join(""));
|
26
|
+
filePath = cwd +/+ fileName;
|
27
|
+
if(w.isKindOf(Plotter), {
|
28
|
+
w = w.parent;
|
29
|
+
});
|
30
|
+
if(w.isNil, {"Can not create image of closed window".throw;});
|
31
|
+
image = Image.fromWindow(w).write(filePath);
|
32
|
+
"-----PLOTTED_IMAGE_%-----".format(filePath).postln;
|
33
|
+
image;
|
34
|
+
};
|
35
|
+
"""
|
36
|
+
|
37
|
+
RECORD_HELPER = """
|
38
|
+
var record = {|duration, fileName|
|
39
|
+
var filePath;
|
40
|
+
var recorder;
|
41
|
+
fileName = fileName ? "sc_%.flac".format({rrand(0, 9)}.dup(10).join(""));
|
42
|
+
filePath = cwd +/+ fileName;
|
43
|
+
Server.default.recorder.recHeaderFormat = "flac";
|
44
|
+
Server.default.recorder.recSampleFormat = "int16";
|
45
|
+
recorder = Server.default.record(path: filePath, duration: duration);
|
46
|
+
(duration+1).wait;
|
47
|
+
"-----RECORDED_AUDIO_%-----".format(filePath).postln;
|
48
|
+
recorder;
|
49
|
+
};
|
50
|
+
"""
|
51
|
+
|
52
|
+
EXEC_WRAPPER = partial(
|
53
|
+
"""
|
54
|
+
var cwd = "{cwd}";
|
55
|
+
{plot_helper}
|
56
|
+
{record_helper}
|
57
|
+
|
58
|
+
{{
|
59
|
+
var result;
|
60
|
+
"**** JUPYTER ****".postln;
|
61
|
+
result = {{ try {{
|
62
|
+
{code}
|
63
|
+
}} {{|error| "ERROR %".format(error).postln}} }}.value();
|
64
|
+
postf("-> %%\n", result);
|
65
|
+
"**** /JUPYTER ****".postln;
|
66
|
+
}}.fork(AppClock);
|
67
|
+
""".format, # noqa
|
68
|
+
cwd=os.getcwd(),
|
69
|
+
plot_helper="" if os.environ.get("NO_QT") else PLOT_HELPER,
|
70
|
+
record_helper=RECORD_HELPER,
|
71
|
+
)
|
72
|
+
|
73
|
+
SEARCH_CLASSES = partial(
|
74
|
+
"""
|
75
|
+
var getClasses = {{|t|
|
76
|
+
var res = [];
|
77
|
+
if(t.size>2, {{
|
78
|
+
Class.allClasses.do({{ |class|
|
79
|
+
var name = class.name.asString;
|
80
|
+
if (name.beginsWith(t)) {{
|
81
|
+
res = res.add(name);
|
82
|
+
}};
|
83
|
+
}});
|
84
|
+
}});
|
85
|
+
res.cs;
|
86
|
+
}};
|
87
|
+
getClasses.("{search_term}");
|
88
|
+
""".format # noqa
|
89
|
+
)
|
90
|
+
|
91
|
+
|
92
|
+
class BgColors:
|
93
|
+
# from https://stackoverflow.com/a/287944/3475778
|
94
|
+
FAIL = "\033[91m"
|
95
|
+
END = "\x1b[0m"
|
96
|
+
|
16
97
|
|
17
98
|
def get_kernel_json():
|
18
|
-
"""Get the kernel json for the kernel.
|
19
|
-
"""
|
99
|
+
"""Get the kernel json for the kernel."""
|
20
100
|
here = os.path.dirname(__file__)
|
21
|
-
with open(os.path.join(here,
|
101
|
+
with open(os.path.join(here, "kernel.json")) as fid:
|
22
102
|
data = json.load(fid)
|
23
|
-
data[
|
103
|
+
data["argv"][0] = sys.executable
|
24
104
|
return data
|
25
105
|
|
26
106
|
|
27
107
|
class SCKernel(ProcessMetaKernel):
|
28
|
-
app_name =
|
29
|
-
name =
|
30
|
-
implementation =
|
108
|
+
app_name = "supercollider_kernel"
|
109
|
+
name = "SuperCollider Kernel"
|
110
|
+
implementation = "sc_kernel"
|
31
111
|
implementation_version = __version__
|
32
|
-
language =
|
112
|
+
language = "supercollider"
|
33
113
|
language_info = {
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
114
|
+
"mimetype": "text/x-sclang",
|
115
|
+
"name": "smalltalk", # although supercollider is included in pygments its not working here
|
116
|
+
"file_extension": ".scd",
|
117
|
+
"pygments_lexer": "supercollider",
|
38
118
|
}
|
39
119
|
kernel_json = get_kernel_json()
|
40
120
|
|
41
|
-
|
42
|
-
HTML_HELP_FILE_PATH_REGEX = re.compile(r'-> file:\/\/(.*\.html)')
|
121
|
+
HTML_HELP_FILE_PATH_REGEX = re.compile(r"-> file:\/\/(.*\.html)")
|
43
122
|
SCHELP_HELP_FILE_PATH_REGEX = re.compile(r"<a href='file:\/\/(.*\.schelp)'>")
|
44
|
-
SC_VERSION_REGEX = re.compile(r
|
45
|
-
|
46
|
-
|
123
|
+
SC_VERSION_REGEX = re.compile(r"sclang (\d+(\.\d+)+)")
|
124
|
+
PLOT_REGEX = re.compile(r"-{5}PLOTTED_IMAGE_(?P<ImagePath>[^%].*)-{5}")
|
125
|
+
RECORDING_REGEX = re.compile(r"-{5}RECORDED_AUDIO_(?P<AudioPath>[^%].*)-{5}")
|
47
126
|
|
48
127
|
@property
|
49
128
|
def language_version(self):
|
@@ -51,118 +130,97 @@ class SCKernel(ProcessMetaKernel):
|
|
51
130
|
|
52
131
|
@property
|
53
132
|
def banner(self):
|
54
|
-
return check_output([self._sclang_path,
|
133
|
+
return check_output([self._sclang_path, "-v"]).decode("utf-8")
|
55
134
|
|
56
135
|
def __init__(self, *args, **kwargs):
|
57
136
|
super().__init__(*args, **kwargs)
|
58
137
|
self._sclang_path = self._get_sclang_path()
|
59
138
|
self.__sclang: Optional[REPLWrapper] = None
|
60
|
-
self.__sc_classes: Optional[List[str]] = None
|
61
139
|
self.wrapper = self._sclang
|
62
|
-
self.recording_paths = set()
|
63
140
|
|
64
141
|
@staticmethod
|
65
142
|
def _get_sclang_path() -> str:
|
66
|
-
sclang_path = os.environ.get(
|
143
|
+
sclang_path = os.environ.get("SCLANG_PATH")
|
67
144
|
if not sclang_path:
|
68
145
|
p = platform.system()
|
69
|
-
if p ==
|
70
|
-
sclang_path =
|
71
|
-
if p ==
|
72
|
-
sclang_path =
|
73
|
-
if p ==
|
146
|
+
if p == "Linux":
|
147
|
+
sclang_path = "sclang"
|
148
|
+
if p == "Darwin":
|
149
|
+
sclang_path = "/Applications/SuperCollider.app/Contents/MacOS/sclang"
|
150
|
+
if p == "Windows":
|
74
151
|
raise NotImplementedError
|
75
152
|
return sclang_path # type: ignore
|
76
153
|
|
77
154
|
@property
|
78
|
-
def _sclang(self) ->
|
155
|
+
def _sclang(self) -> "ScREPLWrapper":
|
79
156
|
if self.__sclang:
|
80
157
|
return self.__sclang # type: ignore
|
81
|
-
self.__sclang = ScREPLWrapper(f
|
158
|
+
self.__sclang = ScREPLWrapper(f"{self._sclang_path} -i jupyter")
|
82
159
|
return self.__sclang
|
83
160
|
|
84
161
|
def do_execute_direct(self, code: str, silent=False):
|
85
|
-
if code ==
|
86
|
-
code =
|
87
|
-
for file_recording in self.RECORD_MAGIC_REGEX.findall(code):
|
88
|
-
# check https://en.wikipedia.org/wiki/HTML5_audio#Supported_audio_coding_formats
|
89
|
-
# for available formats in a browser and
|
90
|
-
# http://doc.sccode.org/Classes/SoundFile.html#-headerFormat
|
91
|
-
# for available SC formats
|
92
|
-
_, file_ext = os.path.splitext(file_recording)
|
93
|
-
if file_ext.lower() not in ['.flac', '.wav']:
|
94
|
-
self.log.error('Only FLAC and WAV is supported for browser playback!')
|
95
|
-
file_path = os.path.join(os.getcwd(), file_recording)
|
96
|
-
self.log.info(f'Start recording to {file_path}')
|
97
|
-
recording_code = f"""
|
98
|
-
s.recorder.recHeaderFormat = "{file_ext[1:]}";
|
99
|
-
s.recorder.recSampleFormat = "int24";
|
100
|
-
s.record("{file_path}");
|
101
|
-
"""
|
102
|
-
code = recording_code + code
|
103
|
-
# remove magic from code execution
|
104
|
-
code = self.RECORD_MAGIC_REGEX.sub('', code)
|
105
|
-
|
162
|
+
if code == ".":
|
163
|
+
code = "CmdPeriod.run;"
|
106
164
|
return super().do_execute_direct(
|
107
165
|
code=code,
|
108
166
|
silent=silent,
|
109
167
|
)
|
110
168
|
|
111
169
|
def _check_for_recordings(self, message):
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
finished_recordings: List[str] = self._sclang.RECORDING_STOPPED_REGEX.findall(message)
|
131
|
-
|
132
|
-
displayed_recordings: List[str] = []
|
133
|
-
for finished_recording in finished_recordings:
|
134
|
-
for recording_path in [f for f in self.recording_paths if finished_recording in f]:
|
135
|
-
self.log.info(f'Found finished recording: {recording_path}')
|
136
|
-
time.sleep(1.0) # wait for finished writing, just in case
|
137
|
-
self.Display(Audio(filename=recording_path))
|
138
|
-
displayed_recordings.append(recording_path)
|
139
|
-
self.recording_paths.difference_update(displayed_recordings)
|
170
|
+
audio_path: str
|
171
|
+
for audio_path in self.RECORDING_REGEX.findall(message):
|
172
|
+
try:
|
173
|
+
self.Display(Audio(audio_path))
|
174
|
+
except ValueError as e:
|
175
|
+
message += f"Error displaying {audio_path}: {e}\n"
|
176
|
+
return self.RECORDING_REGEX.sub("", message)
|
177
|
+
|
178
|
+
def _check_for_plot(self, message: str) -> str:
|
179
|
+
image_path: str
|
180
|
+
for image_path in self.PLOT_REGEX.findall(message):
|
181
|
+
try:
|
182
|
+
self.Display(Image(filename=image_path))
|
183
|
+
except (ValueError, FileNotFoundError) as e:
|
184
|
+
message += f"Error displaying {image_path}: {e}\n"
|
185
|
+
|
186
|
+
return self.PLOT_REGEX.sub("", message)
|
140
187
|
|
141
188
|
def Write(self, message):
|
142
|
-
self._check_for_recordings(message)
|
189
|
+
message = self._check_for_recordings(message)
|
190
|
+
message = self._check_for_plot(message)
|
143
191
|
super().Write(message)
|
144
192
|
|
145
|
-
@
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
193
|
+
@staticmethod
|
194
|
+
def extract_class_name(obj: str) -> str:
|
195
|
+
reverse_class_name = ""
|
196
|
+
# not used but maybe later
|
197
|
+
reverse_method_name = None # noqa
|
198
|
+
|
199
|
+
# chars which delimit a new class
|
200
|
+
TERMINAL_CHARS = [" ", "(", ")"]
|
201
|
+
|
202
|
+
for o in obj[::-1]:
|
203
|
+
if o in TERMINAL_CHARS:
|
204
|
+
break
|
205
|
+
if o == ".":
|
206
|
+
# everything after a . is a method - reset our counter
|
207
|
+
reverse_method_name = reverse_class_name # noqa
|
208
|
+
reverse_class_name = ""
|
209
|
+
continue
|
210
|
+
reverse_class_name += o
|
211
|
+
return reverse_class_name[::-1]
|
212
|
+
|
213
|
+
def get_completions(self, info: Dict):
|
214
|
+
class_name = self.extract_class_name(info["obj"])
|
215
|
+
sc_string = self._sclang.run_command(
|
216
|
+
SEARCH_CLASSES(search_term=class_name), timeout=5
|
217
|
+
)
|
218
|
+
# need to remove -> from interpreter
|
219
|
+
return json.loads(sc_string[2:])
|
220
|
+
|
221
|
+
def get_kernel_help_on(self, info: Dict, level=0, none_on_fail=False):
|
222
|
+
code = self.extract_class_name(info["obj"])
|
223
|
+
output = self._sclang.run_command(f'SCDoc.findHelpFile("{code}");')
|
166
224
|
help_file_paths = self.HTML_HELP_FILE_PATH_REGEX.findall(output)
|
167
225
|
if help_file_paths:
|
168
226
|
if os.path.isfile(help_file_paths[0]):
|
@@ -178,24 +236,22 @@ class SCKernel(ProcessMetaKernel):
|
|
178
236
|
|
179
237
|
class ScREPLWrapper(REPLWrapper):
|
180
238
|
def __init__(self, cmd_or_spawn, *args, **kwargs):
|
181
|
-
cmd = pexpect.spawn(
|
182
|
-
cmd_or_spawn,
|
183
|
-
encoding='utf-8'
|
184
|
-
)
|
239
|
+
cmd = pexpect.spawn(cmd_or_spawn, encoding="utf-8")
|
185
240
|
try:
|
186
241
|
# we wait for the Welcome message and throw everything before it away as well
|
187
|
-
cmd.expect(
|
242
|
+
cmd.expect("Welcome to SuperCollider.*", timeout=15)
|
188
243
|
except pexpect.TIMEOUT as e:
|
189
|
-
print(f
|
244
|
+
print(f"Could not start sclang successfully: {e}")
|
190
245
|
raise e
|
191
246
|
|
192
247
|
super().__init__(
|
193
248
|
cmd,
|
194
|
-
prompt_regex=
|
195
|
-
prompt_change_cmd=
|
249
|
+
prompt_regex="",
|
250
|
+
prompt_change_cmd="",
|
196
251
|
new_prompt_regex="",
|
197
252
|
prompt_emit_cmd="",
|
198
|
-
*args,
|
253
|
+
*args,
|
254
|
+
**kwargs,
|
199
255
|
)
|
200
256
|
self.child: pexpect.spawn
|
201
257
|
# increase buffer size so streaming of large data goes much faster
|
@@ -205,9 +261,13 @@ class ScREPLWrapper(REPLWrapper):
|
|
205
261
|
|
206
262
|
BEGIN_TOKEN = "**** JUPYTER ****"
|
207
263
|
END_TOKEN = "**** /JUPYTER ****"
|
208
|
-
COMMAND_REGEX = re.compile(
|
209
|
-
|
210
|
-
|
264
|
+
COMMAND_REGEX = re.compile(
|
265
|
+
r"\*{4} JUPYTER \*{4}(?P<Content>.*)\*{4} /JUPYTER \*{4}", re.DOTALL
|
266
|
+
)
|
267
|
+
ERROR_REGEX = re.compile(
|
268
|
+
r"(\*{4} JUPYTER \*{4})?(?P<Content>.*ERROR: .*\-{35})", re.DOTALL
|
269
|
+
)
|
270
|
+
THROW_REGEX = re.compile(r"(?P<Content>(ERROR: .*)?CALL STACK:)", re.DOTALL)
|
211
271
|
|
212
272
|
def run_command(self, command, timeout=30, *args, **kwargs):
|
213
273
|
"""
|
@@ -223,26 +283,27 @@ class ScREPLWrapper(REPLWrapper):
|
|
223
283
|
:param kwargs:
|
224
284
|
:return: output of command as a string
|
225
285
|
"""
|
226
|
-
# idea from
|
227
|
-
# https://github.com/supercollider/qpm/blob/d3f72894e289744f01f3c098ab0a474d5190315e/qpm/sclang_process.py#L62
|
228
|
-
|
229
|
-
exec_command = '{ var result; "%s".postln; result = {%s}.value(); postf("-> %%\n", result); "%s".postln;}.fork(AppClock);' % ( # noqa
|
230
|
-
self.BEGIN_TOKEN, command, self.END_TOKEN) # noqa
|
231
|
-
|
232
286
|
# 0x1b is the escape key which tells sclang to evaluate any command b/c
|
233
287
|
# we can not use \n as we can have multiple lines in our command
|
234
|
-
|
288
|
+
command = EXEC_WRAPPER(code=command)
|
289
|
+
# print(command)
|
290
|
+
self.child.sendline(f"{command}{chr(0x1b)}")
|
235
291
|
|
236
|
-
self.child.expect(
|
292
|
+
self.child.expect(
|
293
|
+
[self.COMMAND_REGEX, self.ERROR_REGEX, self.THROW_REGEX], timeout=timeout
|
294
|
+
)
|
237
295
|
|
238
296
|
# although \r\n is DOS style it is for some reason used by UNIX, see
|
239
297
|
# https://pexpect.readthedocs.io/en/stable/overview.html#find-the-end-of-line-cr-lf-conventions
|
240
298
|
# we also remove \r\n at start and end of each command with the slicing [2:-2]
|
241
|
-
output = self._clean_output(
|
299
|
+
output = self._clean_output(
|
300
|
+
self.child.match.groupdict().get("Content", "ERROR")
|
301
|
+
)
|
242
302
|
# output = self.child.match.groups()[0][2:-2].replace('\r\n', '\n')
|
243
|
-
if
|
244
|
-
output += self.child.readline().replace(
|
245
|
-
|
303
|
+
if "ERROR: " in output:
|
304
|
+
output += self.child.readline().replace("\r\n", "\n")
|
305
|
+
# add red color for fail
|
306
|
+
output = f"{BgColors.FAIL} {output} {BgColors.END}"
|
246
307
|
return output
|
247
308
|
|
248
309
|
@staticmethod
|
@@ -253,11 +314,11 @@ class ScREPLWrapper(REPLWrapper):
|
|
253
314
|
:param output:
|
254
315
|
:return:
|
255
316
|
"""
|
256
|
-
if output.startswith(
|
317
|
+
if output.startswith("\r\n"):
|
257
318
|
output = output[2:]
|
258
|
-
if output.endswith(
|
319
|
+
if output.endswith("\r\n"):
|
259
320
|
output = output[:-2]
|
260
|
-
return output.replace(
|
321
|
+
return output.replace("\r\n", "\n")
|
261
322
|
|
262
323
|
@property
|
263
324
|
def before_output(self) -> str:
|
@@ -0,0 +1,218 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: sc_kernel
|
3
|
+
Version: 0.5.0
|
4
|
+
Summary: SuperCollider kernel for Jupyter
|
5
|
+
Author: Dennis Scheiba
|
6
|
+
License: BSD
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
8
|
+
Classifier: Operating System :: OS Independent
|
9
|
+
Classifier: Framework :: IPython
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
11
|
+
Classifier: Topic :: System :: Shells
|
12
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Sound Synthesis
|
13
|
+
Requires-Python: >=3.10
|
14
|
+
Description-Content-Type: text/markdown
|
15
|
+
License-File: LICENSE
|
16
|
+
Requires-Dist: metakernel<0.31,>=0.23.0
|
17
|
+
Requires-Dist: ipython<10.0,>=4.0
|
18
|
+
Requires-Dist: pygments<3.0
|
19
|
+
Requires-Dist: jupyterlab<5.0,>=3.0
|
20
|
+
Requires-Dist: jupyter_server<3.0,>2.0
|
21
|
+
Provides-Extra: dev
|
22
|
+
Requires-Dist: coverage==5.2.1; extra == "dev"
|
23
|
+
Requires-Dist: flake8<=6.0.0; extra == "dev"
|
24
|
+
Requires-Dist: unittest-xml-reporting==3.0.4; extra == "dev"
|
25
|
+
Requires-Dist: mypy<=1.0.0; extra == "dev"
|
26
|
+
Requires-Dist: pre-commit==2.17.0; extra == "dev"
|
27
|
+
Requires-Dist: black<=23.1.0; extra == "dev"
|
28
|
+
Dynamic: license-file
|
29
|
+
|
30
|
+
# Supercollider Jupyter Kernel
|
31
|
+
|
32
|
+
This kernel allows running [SuperCollider](https://supercollider.github.io/) Code in a [Jupyter](https://jupyter.org/) environment.
|
33
|
+
|
34
|
+

|
35
|
+
|
36
|
+
## Installation
|
37
|
+
|
38
|
+
Please make sure one has installed [SuperCollider](https://supercollider.github.io/) and
|
39
|
+
[Python 3 with pip](https://realpython.com/installing-python).
|
40
|
+
|
41
|
+
* To install the kernel for Jupyter execute
|
42
|
+
|
43
|
+
```shell
|
44
|
+
pip3 install --upgrade sc-kernel
|
45
|
+
```
|
46
|
+
|
47
|
+
This will also install [Jupyter Lab](https://jupyter.org/) if it is not already installed on the system.
|
48
|
+
|
49
|
+
* Start a new Jupyter Lab instance by executing `jupyter lab` in a console.
|
50
|
+
|
51
|
+
* Click on the SuperCollider icon
|
52
|
+
|
53
|
+
If one has not installed SuperCollider in the default location, one has to set a environment variable
|
54
|
+
called `SCLANG_PATH` which points to the sclang executable.
|
55
|
+
|
56
|
+
To uninstall the kernel execute
|
57
|
+
|
58
|
+
```shell
|
59
|
+
jupyter kernelspec uninstall sc_kernel
|
60
|
+
```
|
61
|
+
|
62
|
+
### As a Docker container
|
63
|
+
|
64
|
+
It is also possible to run sc-kernel in a Docker container, although a sound output is not possible in this case.
|
65
|
+
Assuming you have cloned the repository and opened a terminal in its directory.
|
66
|
+
|
67
|
+
```shell
|
68
|
+
# build container - takes some time b/c we build supercollider
|
69
|
+
docker build -t sc_kernel .
|
70
|
+
# run container
|
71
|
+
# -v mounts the current directory to the container
|
72
|
+
# -p passes the container port to our host
|
73
|
+
docker run -v ${PWD}:/home/sc_kernel -p 8888:8888 sc_kernel
|
74
|
+
```
|
75
|
+
|
76
|
+
## Usage
|
77
|
+
|
78
|
+
Contrary to ScIDE each document will run in its own interpreter and not in a shared one.
|
79
|
+
This is the default behavior of Jupyter but maybe this will be changed at a later point.
|
80
|
+
|
81
|
+
Currently it is only possible to use the default config - if you encounter missing classes
|
82
|
+
it is probably caused that they are not available in the default config.
|
83
|
+
|
84
|
+
### Stop sound
|
85
|
+
|
86
|
+
Currently the `Cmd + .` command is not binded. Instead create a new cell with a single dot
|
87
|
+
|
88
|
+
```supercollider
|
89
|
+
.
|
90
|
+
```
|
91
|
+
|
92
|
+
and execute this cell. This will transform the command to `CommandPeriod.run;` which is what is actually called on the `Cmd + .` press in the IDE.
|
93
|
+
|
94
|
+
### Recording
|
95
|
+
|
96
|
+
`sc_kernel` provides an easy way to record audio to the local directory and store it embedded in the notebook
|
97
|
+
so one can transfer the notebook into a website which has the audio files included.
|
98
|
+
|
99
|
+
The audio is stored in FLAC with 16 bit resolution.
|
100
|
+
|
101
|
+
The provided function `record` takes 2 arguments:
|
102
|
+
|
103
|
+
* Duration in seconds
|
104
|
+
* Filename which will be used for the recording, using the path of the notebook as base path.
|
105
|
+
|
106
|
+
Assuming one has started the server, simply execute
|
107
|
+
|
108
|
+
```supercollider
|
109
|
+
Ndef(\sine, {
|
110
|
+
var sig = SinOsc.ar(LFDNoise0.kr(1.0!2).exprange(100, 400));
|
111
|
+
sig = sig * \amp.kr(0.2);
|
112
|
+
sig;
|
113
|
+
}).play;
|
114
|
+
|
115
|
+
record.(4.0);
|
116
|
+
```
|
117
|
+
|
118
|
+

|
119
|
+
|
120
|
+
### Plotting
|
121
|
+
|
122
|
+
`sc_kernel` also provides a way to embed images of SuperCollider windows into the Jupyter document.
|
123
|
+
First create a window that you want to embed into the document
|
124
|
+
|
125
|
+
```supercollider
|
126
|
+
w = {SinOsc.ar(2.0)}.plot(1.0);
|
127
|
+
```
|
128
|
+
|
129
|
+
After the plotting is finished by the server we can now simply save an image of the window
|
130
|
+
to a file and also embed the image into the document via a SuperCollider helper method which is available.
|
131
|
+
|
132
|
+
```supercollider
|
133
|
+
plot.(w);
|
134
|
+
```
|
135
|
+
|
136
|
+

|
137
|
+
|
138
|
+
The image will be saved relative the directory where `jupyter lab` was executed.
|
139
|
+
The optional second argument can be the filename.
|
140
|
+
|
141
|
+
> Note that `{}.plot` does not return a `Window` but a `Plotter`, but `sc_kernel`
|
142
|
+
> accesses the window of a `Plotter` automatically.
|
143
|
+
>
|
144
|
+
> For plotting e.g. the server meter you need to pass the proper window, so
|
145
|
+
>
|
146
|
+
> ```supercollider
|
147
|
+
> a = s.meter;
|
148
|
+
> // a is a ServerMeter
|
149
|
+
>
|
150
|
+
> // new cell
|
151
|
+
> plot.(a.window, "meter.png");
|
152
|
+
> ```
|
153
|
+
|
154
|
+
### Autocomplete
|
155
|
+
|
156
|
+
Simply push `Tab` to see available autocompletions.
|
157
|
+
This is currently limited to scan for available classes.
|
158
|
+
|
159
|
+
### Documentation
|
160
|
+
|
161
|
+
To display the documentation of a Class, simply prepend a `?` to it and execute it, e.g.
|
162
|
+
|
163
|
+
```supercollider
|
164
|
+
?SinOsc
|
165
|
+
```
|
166
|
+
|
167
|
+
You can also hit `shift <tab>` iff the cursor is behind a class to trigger the inline documentation.
|
168
|
+
|
169
|
+

|
170
|
+
|
171
|
+
### Real Time Collaboration
|
172
|
+
|
173
|
+
Jupyter Lab allows for real time collaboration in which multiple users can write in the same document from different computers by visiting the Jupyter server via their browser.
|
174
|
+
Each user can write and execute sclang statements on your local sclang interpreter and the cursors of each user is shown to everyone.
|
175
|
+
|
176
|
+
This allows for interactive, shared sessions which can be an interesting live coding sessions.
|
177
|
+
|
178
|
+
> Be aware that this can be a security threat as it allows for other people from within the network to execute arbitrary sclang commands on your computer
|
179
|
+
|
180
|
+
To start such a session you can spin Jupyter Lab via
|
181
|
+
|
182
|
+
```shell
|
183
|
+
jupyter lab --ip 0.0.0.0 --collaborative --NotebookApp.token='sclang'
|
184
|
+
```
|
185
|
+
|
186
|
+
where the `NotebookApp.token` is the necessary password to login - set it to `''` if no password is wanted.
|
187
|
+
|
188
|
+
Check out the [documentation on Jupyter Lab](https://jupyterlab.readthedocs.io/en/stable/user/rtc.html) about *Real Time Collaboration*.
|
189
|
+
|
190
|
+
## Development
|
191
|
+
|
192
|
+
Any PR is welcome! Please state the changes in an Issue.
|
193
|
+
To contribute, please
|
194
|
+
|
195
|
+
* Fork the repository and clone it to a local directory
|
196
|
+
|
197
|
+
* Create a virtual environment and install the dev dependencies
|
198
|
+
in it with
|
199
|
+
|
200
|
+
```shell
|
201
|
+
pip3 install -e ".[dev]"
|
202
|
+
```
|
203
|
+
|
204
|
+
* If one wants to add the kernel to an existing Jupyter installation one can execute
|
205
|
+
|
206
|
+
```shell
|
207
|
+
jupyter kernelspec install sc_kernel
|
208
|
+
```
|
209
|
+
|
210
|
+
and run `jupyter lab` from within the cloned directory as
|
211
|
+
we need to have access to `sc_kernel`.
|
212
|
+
|
213
|
+
* Run `./run_tests.sh` and make a PR :)
|
214
|
+
Use `black sc_kernel test` to format the source code.
|
215
|
+
|
216
|
+
## Maintainers
|
217
|
+
|
218
|
+
* [Dennis Scheiba](https://dennis-scheiba.com)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
sc_kernel/__init__.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
|
2
|
+
sc_kernel/__main__.py,sha256=_ZNuw-RZoWvEFNAWCOMRRIZ11fKNY3WeHAGBrcWaTpU,126
|
3
|
+
sc_kernel/kernel.json,sha256=ck5y4eT6sjMINUGTYZBOaDhsoO2Bj8vK0EkYIoSEV4c,106
|
4
|
+
sc_kernel/kernel.py,sha256=_j0Wbx12LMqgPJweQZVAJZMhHxOJnDUnQp7K28Vw5K0,11074
|
5
|
+
sc_kernel/images/logo-32x32.png,sha256=JsjTZpe6UkvbEqPr8x7Op8shzIuAGnVS3sFFjM3JotE,3949
|
6
|
+
sc_kernel/images/logo-64x64.png,sha256=yjFvhw6uio4bx8KOW5RDGGASOyo6JkyE5CaSdh3wbt8,12511
|
7
|
+
sc_kernel-0.5.0.data/data/share/jupyter/kernels/sc_kernel/kernel.json,sha256=ck5y4eT6sjMINUGTYZBOaDhsoO2Bj8vK0EkYIoSEV4c,106
|
8
|
+
sc_kernel-0.5.0.data/data/share/jupyter/kernels/sc_kernel/logo-32x32.png,sha256=JsjTZpe6UkvbEqPr8x7Op8shzIuAGnVS3sFFjM3JotE,3949
|
9
|
+
sc_kernel-0.5.0.data/data/share/jupyter/kernels/sc_kernel/logo-64x64.png,sha256=yjFvhw6uio4bx8KOW5RDGGASOyo6JkyE5CaSdh3wbt8,12511
|
10
|
+
sc_kernel-0.5.0.dist-info/licenses/LICENSE,sha256=SFO9qjqxmDQwmS2w2NkXljrvheZBqzgzg6LANm-UA5I,1459
|
11
|
+
sc_kernel-0.5.0.dist-info/METADATA,sha256=UsNjKm-kUapEBsfhyjDEEZVZTuKG3cAkSLIueABpW0o,6773
|
12
|
+
sc_kernel-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
+
sc_kernel-0.5.0.dist-info/top_level.txt,sha256=REBbQoy_WwYSyTAnZjqKTiwea6PdRv3CVK9My8LKj0g,10
|
14
|
+
sc_kernel-0.5.0.dist-info/RECORD,,
|
@@ -1,139 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: sc-kernel
|
3
|
-
Version: 0.3.2
|
4
|
-
Summary: UNKNOWN
|
5
|
-
Home-page: https://github.com/capital-G/sc_kernel
|
6
|
-
Author: Dennis Scheiba
|
7
|
-
License: BSD
|
8
|
-
Platform: UNKNOWN
|
9
|
-
Classifier: Framework :: IPython
|
10
|
-
Classifier: License :: OSI Approved :: BSD License
|
11
|
-
Classifier: Programming Language :: Python :: 3.6
|
12
|
-
Classifier: Programming Language :: Python :: 3.7
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
14
|
-
Classifier: Topic :: System :: Shells
|
15
|
-
Requires-Python: >=3.6
|
16
|
-
Description-Content-Type: text/markdown
|
17
|
-
Requires-Dist: metakernel (>=0.23.0)
|
18
|
-
Requires-Dist: ipython (>=4.0)
|
19
|
-
Requires-Dist: pygments (>=2.1)
|
20
|
-
Requires-Dist: jupyterlab (>=2.0)
|
21
|
-
Provides-Extra: dev
|
22
|
-
Requires-Dist: coverage (==5.2.1) ; extra == 'dev'
|
23
|
-
Requires-Dist: flake8 (==3.8.3) ; extra == 'dev'
|
24
|
-
Requires-Dist: unittest-xml-reporting (==3.0.4) ; extra == 'dev'
|
25
|
-
Requires-Dist: mypy (==0.770) ; extra == 'dev'
|
26
|
-
|
27
|
-
# Supercollider Jupyter Kernel
|
28
|
-
|
29
|
-
This kernel allows running [SuperCollider](https://supercollider.github.io/) Code in a [Jupyter](https://jupyter.org/) environment.
|
30
|
-
|
31
|
-

|
32
|
-
|
33
|
-
## Installation
|
34
|
-
|
35
|
-
Please make sure one has installed [SuperCollider](https://supercollider.github.io/) and
|
36
|
-
[Python 3 with pip](https://realpython.com/installing-python).
|
37
|
-
|
38
|
-
* To install the kernel for Jupyter execute
|
39
|
-
|
40
|
-
```shell
|
41
|
-
pip3 install sc-kernel
|
42
|
-
```
|
43
|
-
|
44
|
-
This will also install [Jupyter Lab](https://jupyter.org/) if it is not already installed on the system.
|
45
|
-
|
46
|
-
* Start a new Jupyter Lab instance by executing `jupyter lab` in a console.
|
47
|
-
|
48
|
-
* Click on the SuperCollider icon
|
49
|
-
|
50
|
-
If one has not installed SuperCollider in the default location, one has to set a environment variable
|
51
|
-
called `SCLANG_PATH` which points to the sclang executable.
|
52
|
-
|
53
|
-
To uninstall the kernel execute
|
54
|
-
|
55
|
-
```shell
|
56
|
-
jupyter kernelspec uninstall sc_kernel
|
57
|
-
```
|
58
|
-
|
59
|
-
## Usage
|
60
|
-
|
61
|
-
### Stop sound
|
62
|
-
|
63
|
-
Currently the `Cmd + .` command is not binded. Instead create a new cell with a single dot
|
64
|
-
|
65
|
-
```
|
66
|
-
.
|
67
|
-
```
|
68
|
-
|
69
|
-
and execute this cell. This will transform the command to `CommandPeriod.run;` which is what is actually called on the `Cmd + .` press in the IDE.
|
70
|
-
|
71
|
-
|
72
|
-
### Recording
|
73
|
-
|
74
|
-
`sc_kernel` provides an easy way to record audio to the local directory and store it in the notebook
|
75
|
-
so one can later share the notebook with the sound examples embedded.
|
76
|
-
|
77
|
-
Assuming one has started the server, simply execute
|
78
|
-
|
79
|
-
```supercollider
|
80
|
-
%% record "my_file.flac"
|
81
|
-
|
82
|
-
{SinOsc.ar(SinOsc.ar(200)*200)*0.2!2}.play;
|
83
|
-
```
|
84
|
-
|
85
|
-
to start the recording.
|
86
|
-
|
87
|
-
To stop the recording, simply stop the Server recording via
|
88
|
-
|
89
|
-
```supercollider
|
90
|
-
s.stopRecording;
|
91
|
-
```
|
92
|
-
|
93
|
-
If one has chosen FLAC or WAV as file format, one will see a playback menu for the file within the notebook.
|
94
|
-
|
95
|
-

|
96
|
-
|
97
|
-
If an relative path is provided as filename it will be put relative to the folder where `jupyter lab` was executed.
|
98
|
-
If an absolute path is given the output will be directed to the absolute path.
|
99
|
-
|
100
|
-
Keep in mind that **good browser support only exists for FLAC** and with WAV the seeking does not work.
|
101
|
-
The standard recording format of supercollider AIFF is not supported by browsers.
|
102
|
-
|
103
|
-
### Autocomplete
|
104
|
-
|
105
|
-
Simply push `Tab` to see available autocompletions.
|
106
|
-
|
107
|
-
### Documentation
|
108
|
-
|
109
|
-
To display the documentation of a Class, simply prepend a `?` to it and execute it, e.g.
|
110
|
-
|
111
|
-
```supercollider
|
112
|
-
?SinOsc
|
113
|
-
```
|
114
|
-
|
115
|
-
## Development
|
116
|
-
|
117
|
-
Any PR is welcome! Please state the changes in an Issue.
|
118
|
-
To contribute, please
|
119
|
-
|
120
|
-
* Fork the repository and clone it to a local directory
|
121
|
-
|
122
|
-
* If one wants to add the kernel to an existing Jupyter installation one can execute
|
123
|
-
|
124
|
-
```shell
|
125
|
-
jupyter kernelspec install sc_kernel
|
126
|
-
```
|
127
|
-
|
128
|
-
and run `jupyter lab` from within the cloned directory as
|
129
|
-
we need to have access to `sc_kernel`.
|
130
|
-
|
131
|
-
* Run `./run_tests.sh` and make a PR :)
|
132
|
-
The tests often run into a pexpect timeout for some reason.
|
133
|
-
They should pass if they are run alone.
|
134
|
-
|
135
|
-
## Maintainers
|
136
|
-
|
137
|
-
* [Dennis Scheiba](https://dennis-scheiba.com)
|
138
|
-
|
139
|
-
|
sc_kernel-0.3.2.dist-info/RECORD
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
sc_kernel/__init__.py,sha256=bqu4G7Is-8Were-hMi-SthEalL7QW-PAbKWGZ9pVB6U,22
|
2
|
-
sc_kernel/__main__.py,sha256=_ZNuw-RZoWvEFNAWCOMRRIZ11fKNY3WeHAGBrcWaTpU,126
|
3
|
-
sc_kernel/kernel.json,sha256=ck5y4eT6sjMINUGTYZBOaDhsoO2Bj8vK0EkYIoSEV4c,106
|
4
|
-
sc_kernel/kernel.py,sha256=UZte2a7FRM-RZu4zmP_iW49v3je8p2g9jIzWrIDE0sU,10922
|
5
|
-
sc_kernel/images/logo-32x32.png,sha256=JsjTZpe6UkvbEqPr8x7Op8shzIuAGnVS3sFFjM3JotE,3949
|
6
|
-
sc_kernel/images/logo-64x64.png,sha256=yjFvhw6uio4bx8KOW5RDGGASOyo6JkyE5CaSdh3wbt8,12511
|
7
|
-
sc_kernel-0.3.2.data/data/share/jupyter/kernels/sc_kernel/kernel.json,sha256=ck5y4eT6sjMINUGTYZBOaDhsoO2Bj8vK0EkYIoSEV4c,106
|
8
|
-
sc_kernel-0.3.2.data/data/share/jupyter/kernels/sc_kernel/logo-32x32.png,sha256=JsjTZpe6UkvbEqPr8x7Op8shzIuAGnVS3sFFjM3JotE,3949
|
9
|
-
sc_kernel-0.3.2.data/data/share/jupyter/kernels/sc_kernel/logo-64x64.png,sha256=yjFvhw6uio4bx8KOW5RDGGASOyo6JkyE5CaSdh3wbt8,12511
|
10
|
-
sc_kernel-0.3.2.dist-info/LICENSE,sha256=SFO9qjqxmDQwmS2w2NkXljrvheZBqzgzg6LANm-UA5I,1459
|
11
|
-
sc_kernel-0.3.2.dist-info/METADATA,sha256=T1jqGi-2UTRnsAq8b-rBxWxzAxg_Yf6iiHS590pb90o,3878
|
12
|
-
sc_kernel-0.3.2.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92
|
13
|
-
sc_kernel-0.3.2.dist-info/top_level.txt,sha256=REBbQoy_WwYSyTAnZjqKTiwea6PdRv3CVK9My8LKj0g,10
|
14
|
-
sc_kernel-0.3.2.dist-info/RECORD,,
|
{sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/kernel.json
RENAMED
File without changes
|
{sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/logo-32x32.png
RENAMED
File without changes
|
{sc_kernel-0.3.2.data → sc_kernel-0.5.0.data}/data/share/jupyter/kernels/sc_kernel/logo-64x64.png
RENAMED
File without changes
|
File without changes
|
File without changes
|