supriya

repository·main·Indexed 19 days ago

https://github.com/supriya-project/supriya

A Python API for SuperCollider (version 26.3b0) that enables realtime synthesis engine communication, nonrealtime score composition, and native Python-based SynthDef compilation. It allows users to define synthesis graphs using Unit Generators (UGens) via the @synthdef decorator or SynthDefBuilder, manage servers (scsynth or supernova), and interact with audio buses and frequency scopes.

Tokens
38.5K
Snippets
185
Records
214
Agent score
64%

What's inside supriya

  1. Using Buses and Buffers for audio data

    main

    Synths interact with audio data through two main mechanisms: Buses and Buffers.

    Buses (supriya.contexts.entities.Bus)

    Buses are placeholders for signals. They are instantiated when the server boots and their count cannot be changed after booting.

    • Audio-rate buses: Manage signals sample-by-sample (similar to mixer channels).
    • Control-rate buses: Manage signals once per sample-block (conceptually similar to control voltage).

    Buffers (supriya.contexts.entities.Buffer)

    Buffers are fixed-size arrays of floating-point values used for wavetables, samples, envelopes, or delay lines.

    • Persistence: Unlike buses, buffers maintain their data until explicitly changed.
    • Allocation: Buffers must be explicitly allocated on the server (an asynchronous action) because they require additional memory allocation.
  2. Use Moments to bundle requests with timestamps

    main

    You can bundle multiple requests together using supriya.contexts.core.Moment context managers via the .at() method. This allows you to specify when a group of commands should occur.

    • In Non-realtime (Score): All requests must happen inside a moment. Scores count time from zero.
    • In Realtime (Server):
      • Requests outside a moment or in a moment without a timestamp are executed "as soon as possible."
      • You can use real timestamps (e.g., from time.time()) to schedule future actions.

    Note: If you are issuing commands inside a moment, any Completion context used must be closed before the moment closes, otherwise the completion message won't be bundled into the original request.

    # Non-realtime: Score counts from 0
    with score.at(0):
        score_group = score.add_group()
    
    # Realtime: Using real timestamps (e.g., 0.1 seconds from now)
    import time
    with server.at(time.time() + 0.1):
        server_group = server.add_group()
    
    # Realtime: Execute ASAP
    with server.at():
        server.add_group()
    
    # Realtime: Omit moment entirely (executes ASAP)
    server.add_group()
  3. Debug Supriya via Server Logging, Status, and Node Trees

    main

    Supriya provides several layers of visibility for debugging audio issues:

    1. Server Logging: Check if the scsynth server booted correctly or reported errors during startup/shutdown. You can adjust the logging level of the supriya.scsynth logger to INFO to see server lifecycle events.
    2. Server Status: Query the server to verify technical parameters like the current sample rate and the counts of active nodes, synths, and synthdefs.
    3. Node Tree Queries: Inspect the hierarchical structure of nodes on the server to ensure the graph matches your intended performance logic.
    4. OSC Transcripts: Monitor the actual OSC messages being sent to and received from the server to verify communication fidelity.
  4. Understand calculation rates in Supriya

    main

    Supriya/SuperCollider uses different calculation rates for Unit Generators (UGens) and buses. The rate determines how often a value is updated:

    • audio rate: A rate where one value is generated for each sample in the sample block.
    • control rate: A rate where one value is generated per sample block.
    • demand rate: A rate where one value is generated each time a connected supriya.ugens.demand.Demand UGen is triggered.
    • scalar rate: Also called "constant" or "initialization" rate; the value is calculated only once regardless of input.
  5. How interpreted documentation works with uqbar.sphinx.book

    main

    Supriya uses the uqbar.sphinx.book extension to execute code examples during the documentation build process. This ensures examples are always up-to-date.

    Key behaviors:

    • Session State: Every document effectively has its own console session (similar to a Jupyter notebook). State is maintained across blocks, including those marked with :hide:.
    • Automatic Imports: All interpreted docs automatically receive import supriya at the start of the session.
    • Docstring Caveat: Code blocks in docstrings should not rely on state from other parts of the Python module, as the execution order in the doctree may differ from the module order. Always use fully qualified imports.
    • Error Handling: If a code block raises an unhandled exception, the build fails. Use the :allow-exceptions: flag to prevent specific exceptions from breaking the build.
    .. book::
        :allow-exceptions:
    
        >>> print(1 / 0)  # This will not break the docs build
  6. Testing philosophy and best practices in Supriya

    main

    When writing tests for Supriya, follow these core principles:

    • Keep tests simple: Follow a pattern of setup (ideally via fixtures) $\rightarrow$ validate pre-conditions $\rightarrow$ perform a single operation $\rightarrow$ validate post-conditions $\rightarrow$ teardown (ideally via fixtures).
    • Test against live servers: Do not mock the SuperCollider server. Instead, use a running instance to test actual communication. Validate the communication rather than trying to validate the internal state of the server.
    • Test all public surfaces: Every public function, class, and method should be tested. Use parameterization for variations in behavior.
    • Test failure modes: Test 'unhappy paths', including expected exceptions, warnings, and explicit log emissions. Failure states are considered part of the API.
    • Prefer unit tests over doctests: While doctests are acceptable if concise, move extended testing logic into the unit test suite and exposition into the documentation.
  7. How Supriya clocks work

    main

    Supriya provides musical-time-aware clocks that allow scheduling callbacks relative to seconds, beats, and measures. They understand tempo, time signatures, and downbeats.

    Clocks are categorized by two dimensions:

    1. Execution Model:

      • Threaded Clocks: Handle callbacks in their own thread. Best for interactive terminal experimentation or simple applications.
      • Asynchronous Clocks: Hook into an asyncio event loop. Best for complex applications integrating with asyncio-aware libraries like aiohttp, python-prompt-toolkit, or pymonome.
    2. Time Mode:

      • Online Clocks: Run in real-time.
      • Offline Clocks: Implement the same interface as online clocks but process callbacks as fast as possible, ignoring real time. Use these for unit-testing or non-realtime rendering of musical patterns.
  8. Understand Supriya Contexts: Realtime vs Non-realtime

    main

    Supriya uses the Context interface to interact with scsynth-compatible execution environments. There are two primary types of contexts:

    1. Write-only Non-realtime Contexts (supriya.Score): Used for scheduling events that will be played back later. These are "write-only" because you cannot query the state of the server (e.g., you cannot ask for the current node tree or buffer contents). Mutations are synchronous.
    2. Read/Write Realtime Contexts (supriya.Server): Used for interacting with a running server. These support both mutations and queries (e.g., checking server status or node information).
      • Sync Servers (supriya.contexts.realtime.Server): Queries block the current thread until a reply arrives.
      • Async Servers (supriya.contexts.realtime.AsyncServer): Support async/await syntax for queries.
    >>> server = supriya.Server().boot()  # realtime
    >>> score = supriya.Score()  # non-realtime
  9. How Synth Definitions and Unit Generators work

    main

    While the node tree is a dynamic graph of entities, a synth's internal structure is a static graph of Unit Generators (UGens).

    • Unit Generators (supriya.ugens.core.UGen): Discrete audio operations (e.g., sine wave generation, filtering, multiplication) that read from/write to buses or buffers.
    • Synth Definitions (supriya.ugens.core.SynthDef): A collection of UGens composed into a graph. A SynthDef acts as a static template for a synth. Once defined, it cannot be changed; you must create a new SynthDef for different configurations.

    Note: Allocating a SynthDef on the server is an asynchronous action.

  10. How Nodes, Groups, and Synths are organized

    main

    The SuperCollider server organizes entities into a rooted acyclic digraph (a tree) of supriya.contexts.entities.Node objects.

    There are two primary types of nodes:

    1. Groups (supriya.contexts.entities.Group): Container nodes that hold other nodes (either groups or synths).
    2. Synths (supriya.contexts.entities.Synth): Nodes that perform actual audio processing.

    Audio Processing Flow:

    • The server traverses the node tree depth-first, starting from the root node.
    • In standard servers (scsynth), processing is deterministic as it visits nodes one after another.
    • In the supernova server, parallel groups allow children to be processed in parallel, which introduces indeterminism and requires careful handling of audio data.
  11. What are Buffers in Supriya

    main

    Buffers are fixed-size arrays of sample data used for storing audio. They consist of one or more channels and one or more frames.

    Key characteristics:

    • They can be read from/written to disk.
    • They can be created empty and populated later, or synthesized by the server (e.g., for window functions or wavetables).
    • They are used for streaming audio when files are too large to load into memory.
    • Unlike buses, buffers can be reconfigured with different channel counts and durations upon allocation.
    • The supriya.contexts.entities.Buffer class acts as a proxy to the buffer in the running scsynth process.
    • The supriya.contexts.entities.BufferGroup class models a contiguous block of buffers.