Score

[1]:
import sc3nb as scn
[2]:
sc = scn.startup()
Starting sclang process... [sclang | start reading ]
Done.
Registering OSC /return callback in sclang... Done.
Loading default sc3nb SynthDefs... Done.
Booting SuperCollider Server... [scsynth | start reading ]
Done.
[3]:
from sc3nb import Score, SynthDef

The Score class can be used for non-realtime synthesis.

  • This is done by starting the SuperCollider audio server scsynth in the non-realtime mode.

  • The server will read the provided OSC file and render the sound to the specified sound file.

  • Note that this will require to send all required SynthDefs and Buffers at the beginning. However you can start using the Buffers & SynthDefs immediately after the corresponding OSCMessages as the audio server will handle all messages in the specified order.

The Score.record_nrt class method provides an easy interface that generates a OSC file from a dict with timings as keys and lists of OSCMessages as values.

[4]:
help(Score.record_nrt)
Help on method record_nrt in module sc3nb.sc_objects.score:

record_nrt(messages: Dict[float, List[sc3nb.osc.osc_communication.OSCMessage]], osc_path: str, out_file: str, in_file: Optional[str] = None, sample_rate: int = 44100, header_format: str = 'AIFF', sample_format: str = 'int16', options: Optional[sc3nb.sc_objects.server.ServerOptions] = None) method of builtins.type instance
    Write an OSC file from the messages and wri

    Parameters
    ----------
    messages : Dict[float, List[OSCMessage]]
        Dict with times as key and lists of OSC messages as values.
    osc_path : str
        Path of the binary OSC file.
    out_file : str
        Path of the resulting sound file.
    in_file : Optional[str], optional
        Path of input soundfile, by default None.
    sample_rate : int, optional
        sample rate for synthesis, by default 44100.
    header_format : str, optional
        header format of the output file, by default "AIFF".
    sample_format : str, optional
        sample format of the output file, by default "int16".
    options : Optional[ServerOptions], optional
        instance of server options to specify server options, by default None

    Returns
    -------
    subprocess.CompletedProcess
        Completed scsynth non-realtime process.

Lets create a simple SynthDef for this demonstration

[5]:
synthdef = SynthDef(
    "test",
    r"""{ |out, freq = 440|
            OffsetOut.ar(out,
                SinOsc.ar(freq, 0, 0.2) * Line.kr(1, 0, 0.5, doneAction: Done.freeSelf)
            )
        }""",
)

For creating the messages its recommended to use the Bundler class

[6]:
with sc.server.bundler(send_on_exit=False) as bundler:
    synthdef.add()  # Send the test SynthDef
    bundler.add(0.0, "/s_new", ["test", 1003, 0, 0, "freq", 440])
    bundler.add(0.2, "/s_new", ["test", 1000, 0, 0, "freq", 440])
    bundler.add(0.4, "/s_new", ["test", 1001, 0, 0, "freq", 660])
    bundler.add(0.6, "/s_new", ["test", 1002, 0, 0, "freq", 220])
    bundler.add(1, "/c_set", [0, 0])  # The /c_set [0, 0] will close the audio file

The corresponding messages can be seen with

[7]:
bundler.messages()
[7]:
{0.0: [<OSCMessage("/d_recv", [b'SCgf\x00\x00\x00\x02\x00\x01\x04test\x00\x00\x00\x05\x00\x00\x00\x00>L\xcc\xcd?\x80\x00\x00?\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00C\xdc\x00\x00\x00\x00\x00\x02\x03out\x00\x00\x00\x00\x04freq\x00\x00\x00\x01\x00\x00\x00\x06\x07Control\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x01\x01\x06SinOsc\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x00\x02\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x02\x04Line\x01\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x03\xff\xff\xff\xff\x00\x00\x00\x04\x01\x0cBinaryOpUGen\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x02\tOffsetOut\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00'])>,
  <OSCMessage("/s_new", ['test', 1003, 0, 0, 'freq', 440])>],
 0.2: [<OSCMessage("/s_new", ['test', 1000, 0, 0, 'freq', 440])>],
 0.4: [<OSCMessage("/s_new", ['test', 1001, 0, 0, 'freq', 660])>],
 0.6: [<OSCMessage("/s_new", ['test', 1002, 0, 0, 'freq', 220])>],
 1.0: [<OSCMessage("/c_set", [0, 0])>]}

Lets start the non-realtime synthesis

[8]:
Score.record_nrt(bundler.messages(), "../media/score.osc", "../media/score.wav", header_format="WAV")
start time 0
nextOSCPacket 0.2
nextOSCPacket 0.4
nextOSCPacket 0.6
nextOSCPacket 1


[8]:
CompletedProcess(args=['/Applications/SuperCollider.app/Contents/Resources/scsynth', '-N', '../media/score.osc', '_', '../media/score.wav', '44100', 'WAV', 'int16', '-u', '57110', '-l', '6', '-i', '2', '-o', '2', '-a', '1024', '-c', '4096', '-b', '1024', '-R', '0'], returncode=0, stdout='start time 0\nnextOSCPacket 0.2\nnextOSCPacket 0.4\nnextOSCPacket 0.6\nnextOSCPacket 1\n', stderr='')

Lets listen to the created audio file with the IPython Audio class that allows to read and play audio files

[9]:
from IPython.display import Audio
[10]:
Audio("../media/score.wav")
[10]:
[11]:
sc.exit()
Quitting SCServer... [scsynth | reached EOF ]
Done.
Exiting sclang... [sclang | reached EOF ]
Done.
[ ]: