Skip to main content

Minimal embedded SuperCollider synthesis engine (libscsynth) wrapper

Project description

nanosynth

nanosynth is a Python package that embeds SuperCollider's libscsynth and supernova synthesis engines in-process using nanobind. It makes it possible to define SynthDefs in Python, compile them to SuperCollider's SCgf binary format, boot the embedded audio engine, and control it via OSC -- all without leaving Python.

Features

  • Self-contained with embedded synthesis engines -- both libscsynth and supernova run in-process as Python extensions (vendored and built from source), no separate process required. supernova is SuperCollider's parallel DSP engine -- it distributes independent synth nodes across CPU cores via ParGroups, while scsynth runs everything on a single audio thread

  • High-level Server class -- boot/quit lifecycle, node ID allocation, SynthDef dispatch, buffer management, OSC reply handling, and convenience methods (synth, group, par_group, free, set). Context manager support and managed_synth()/managed_group()/managed_par_group()/managed_buffer() for automatic resource cleanup. Same API for both engines -- just pass protocol=EmbeddedSupernovaProtocol() to use supernova

  • Pythonic SynthDef builder -- define UGen graphs using a context manager and operator overloading, compiled to SuperCollider's SCgf binary format

  • 340+ UGens -- oscillators, filters, delays, noise, chaos, granular, demand, dynamics, panning, physical modeling, reverb, phase vocoder, machine listening, stochastic synthesis, and more

  • Rich operator algebra -- 43 binary and 34 unary operators on all UGen signals, including arithmetic, comparison, bitwise, power, trig, pitch conversion (midicps/cpsmidi), clipping (clip2/fold2/wrap2), and more. Compile-time constant folding and algebraic optimizations

  • Non-real-time (NRT) rendering -- Score class for offline audio rendering to WAV/AIFF files without audio hardware. Timestamped OSC commands are serialized and rendered by the embedded engine

  • Bus allocation -- Bus proxy class with Server.audio_bus(), Server.control_bus(), managed_audio_bus(), managed_control_bus(). Eliminates hardcoded magic bus numbers in effect chains. int() compatible for passing as synth parameters

  • Pattern sequencing -- Pbind, Pseq, Prand, Pwhite, Pseries, Pgeom, Pchoose, Pn, Pconst, Rest, Clock, and Player for musical event scheduling. Patterns are reusable iterables; Pbind produces event streams that drive synth creation with automatic gate release. Clock provides tempo-driven playback with drift-free scheduling

  • MIDI input -- MidiIn class for receiving MIDI from hardware controllers (via embedded RtMidi: CoreMIDI on macOS, ALSA on Linux, WinMM on Windows). Parsed message types (NoteOn, NoteOff, ControlChange, PitchBend) with handler registration. High-level helpers: midi_note_map() for polyphonic note-to-synth mapping, midi_cc_map() for CC-to-parameter control

  • NodeProxy / Ndef -- live coding with hot-swappable synth definitions. NodeProxy owns a private audio bus, a source synth (with ASR envelope for crossfade), and a monitor synth. Swap the source seamlessly while audio plays. Ndef is a global named proxy registry for concise live-coding workflows

  • Server recording -- Server.record(path) captures real-time audio output to WAV/AIFF via DiskOut. stop_recording() finalizes the file. Configurable channel count, bus, and format

  • Buffer management -- alloc_buffer, read_buffer, write_buffer, free_buffer, zero_buffer, close_buffer, and context managers for automatic cleanup

  • Reply handling -- bidirectional OSC communication with the engine: persistent handlers (on/off), blocking one-shot waits (wait_for_reply), and send-and-wait (send_msg_sync)

  • SynthDef graph introspection -- SynthDef.graph() returns a structured DAG of UGenNode/UGenInput NamedTuples for programmatic traversal. SynthDef.to_dot() exports to Graphviz DOT format

  • Envelope system -- Envelope class with factory methods (adsr, asr, linen, percussive, triangle) and the EnvGen UGen

  • OSC codec -- pure-Python OscMessage/OscBundle encode/decode with optional C++ acceleration via nanobind

  • @synthdef decorator -- shorthand for defining SynthDefs as plain functions with parameter rate/lag annotations

  • Full type safety -- passes mypy --strict, complete type annotations throughout

Requirements

  • Python 3.10+

  • uv (package manager)

  • For embedded engines: SuperCollider 3.14.1 (both scsynth and supernova), libsndfile, and PortAudio are vendored and built from source automatically. Audio backend: CoreAudio on macOS, PortAudio (ALSA) on Linux, PortAudio (WASAPI) on Windows -- no system-level audio dependencies beyond the compiler toolchain.

Installation

pip install nanosynth

Or build from source:

# Editable install with embedded scsynth + supernova
uv pip install -e .

# Build wheel (incremental -- reuses cmake build cache in build/)
make build

# Install without supernova (scsynth only)
uv pip install -e . -C cmake.define.NANOSYNTH_EMBED_SUPERNOVA=OFF

# Install without any audio engine (OSC codec + SynthDef compiler only)
uv pip install -e . -C cmake.define.NANOSYNTH_EMBED_SCSYNTH=OFF -C cmake.define.NANOSYNTH_EMBED_SUPERNOVA=OFF

Quick Start

Run the Audio Demos

make demos            # scsynth demos
make demos-supernova  # supernova demos

Define a SynthDef and Play It

The Server class manages the embedded engine lifecycle. Define a SynthDef, boot the server, and play:

import time
from nanosynth import Options, Server
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.synthdef import DoneAction, SynthDefBuilder
from nanosynth.ugens import Out, Pan2, SinOsc

# Define a SynthDef
with SynthDefBuilder(frequency=440.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"])
    sig = sig * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.1, sustain_time=1.8, release_time=0.1),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env
    Out.ar(bus=0, source=Pan2.ar(source=sig))

synthdef = builder.build(name="sine")

# Boot the server, send the SynthDef, create a synth
with Server(Options(verbosity=0)) as server:
    synthdef.send(server)
    time.sleep(0.1)

    node = server.synth("sine", frequency=440.0, amplitude=0.3)
    print(f"Playing 440 Hz sine (node {node}) for 2 seconds...")
    time.sleep(2.0)
    server.free(node)

# Engine shuts down automatically on context exit

Or use SynthDef.play() to send and create a synth in one call:

with Server() as server:
    node = synthdef.play(server, frequency=880.0, amplitude=0.2)
    time.sleep(2.0)

Managed Nodes (Automatic Cleanup)

managed_synth() and managed_group() create nodes that are automatically freed on context exit, even if an exception occurs:

import time
from nanosynth import Server

with Server() as server:
    synthdef.send(server)
    time.sleep(0.1)

    with server.managed_synth("sine", frequency=440.0, amplitude=0.3) as node:
        print(f"Playing node {node}...")
        time.sleep(2.0)
    # node freed automatically here

    # Group multiple voices and free them together
    with server.managed_group(target=1) as group:
        server.synth("sine", target=group, frequency=261.63, amplitude=0.2)
        server.synth("sine", target=group, frequency=329.63, amplitude=0.2)
        server.synth("sine", target=group, frequency=392.00, amplitude=0.2)
        time.sleep(2.0)
    # entire group freed here

Effect Chains with Bus Allocation

Use AddAction to control node execution order and audio_bus() to allocate private buses for effect routing -- no more hardcoded magic bus numbers:

import time
from nanosynth import AddAction, Options, Server

with Server(Options(verbosity=0)) as server:
    src_def.send(server)
    delay_def.send(server)
    time.sleep(0.1)

    # Allocate a private bus for routing source -> effect
    with server.managed_audio_bus(2) as fx_bus:
        # Source group executes first, effect group after
        src_group = server.group(target=1, action=AddAction.ADD_TO_HEAD)
        fx_group = server.group(target=int(src_group), action=AddAction.ADD_AFTER)

        # Effect reads from the allocated bus, writes to hardware output
        server.synth("comb_delay", target=int(fx_group),
                     in_bus=float(int(fx_bus)), delay_time=0.375, mix=0.4)

        # Source writes to the allocated bus
        server.synth("perc_src", target=int(src_group),
                     out_bus=float(int(fx_bus)), frequency=440.0)
        time.sleep(2.0)
    # bus freed automatically

Supernova (Parallel DSP)

Use supernova instead of scsynth to distribute independent synth nodes across CPU cores. The API is identical -- just pass a different protocol:

import time
from nanosynth import AddAction, EmbeddedSupernovaProtocol, Options, Server
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.synthdef import DoneAction, SynthDefBuilder
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=440.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"]) * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.5, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * env))

voice = builder.build(name="voice")

# Boot supernova instead of scsynth
with Server(
    Options(verbosity=0, load_synthdefs=False),
    protocol=EmbeddedSupernovaProtocol(),
) as server:
    voice.send(server)
    time.sleep(0.1)

    # ParGroup: children execute in parallel across CPU cores
    par = server.par_group(target=1, action=AddAction.ADD_TO_HEAD)

    for freq in [261.63, 329.63, 392.00, 523.25]:
        server.synth("voice", target=par, frequency=freq, amplitude=0.15)

    time.sleep(3.0)

With scsynth, all voices execute sequentially on one audio thread. With supernova, voices inside a ParGroup are distributed across cores -- providing a measurable speedup for dense polyphony and independent effect chains.

Recording

Capture real-time audio output to a file:

import time
from nanosynth import Server

with Server() as server:
    synthdef.send(server)
    time.sleep(0.1)

    # Start recording to WAV
    server.record("output.wav", header_format="wav", sample_format="int16")

    # Play some audio
    node = server.synth("sine", frequency=440.0, amplitude=0.3)
    time.sleep(2.0)
    server.free(node)

    # Stop recording -- finalizes the file
    server.stop_recording()

Recording options include num_channels (defaults to output bus count), bus (which bus to record from), header_format ("wav" or "aiff"), and sample_format ("int16", "int24", "float").

Offline (NRT) Rendering

Render audio to a file without real-time audio hardware -- useful for batch processing, testing, and CI pipelines:

from nanosynth import Score, SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

# Define a SynthDef
with SynthDefBuilder(freq=440.0, amp=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["freq"]) * builder["amp"]
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * env))
sd = builder.build(name="sine")

# Build a Score -- a sequence of timestamped OSC commands
score = Score()
score.add_synthdef(0.0, sd)
score.add_synth(0.0, "sine", freq=440.0, amp=0.3)
score.add_synth(0.5, "sine", freq=554.37, amp=0.2)
score.add_synth(1.0, "sine", freq=659.26, amp=0.2)

# Render to a WAV file (no audio hardware needed)
score.render("output.wav", sample_rate=44100, header_format="WAV", sample_format="int16")

Pattern Sequencing

Replace manual time.sleep() loops with musical patterns. Pbind binds keys to patterns or scalars to produce event streams; Clock drives playback at a given tempo:

import time
from nanosynth import Options, Server
from nanosynth.patterns import Clock, Pbind, Prand, Pseq, Pwhite, Rest

with Server(Options(verbosity=0)) as server:
    # (assume a "default" SynthDef with freq, amp, gate params is loaded)
    clock = Clock(bpm=140)

    # Ascending melody
    melody = Pbind(
        instrument="default",
        freq=Pseq([261.63, 293.66, 329.63, 392.00, 440.00]),
        dur=Pseq([0.5, 0.5, 0.5, 0.5, 1.0]),
        amp=0.2,
    )
    melody.play(clock, server)
    time.sleep(3.0)

    # Randomized melody with rests and dynamic amplitude
    rand_melody = Pbind(
        instrument="default",
        freq=Prand([261.63, 329.63, 392.00, 440.00], repeats=8),
        dur=Pseq([0.25, 0.25, Rest(0.5), 0.5], repeats=2),
        amp=Pwhite(0.1, 0.25, repeats=8),
    )
    player = rand_melody.play(clock, server)
    time.sleep(4.0)

    player.stop()
    clock.stop()

Available patterns: Pseq (sequential), Prand (random choice), Pwhite (uniform random float), Pseries (arithmetic series), Pgeom (geometric series), Pchoose (weighted random), Pn (repeat N times), Pconst (yield until sum reaches total). Patterns support chaining with | and preview with .take(n).

MIDI Input

Connect hardware MIDI controllers. Requires the _midi C extension (built by default with NANOSYNTH_EMBED_MIDI=ON):

import time
from nanosynth import Options, Server
from nanosynth.midi import MidiIn, midi_note_map, midi_cc_map

# List available MIDI ports
print(MidiIn.list_ports())

with Server(Options(verbosity=0)) as server:
    # (assume a gated SynthDef "synth" is loaded)

    with MidiIn(port=0) as midi:
        # Polyphonic note mapping: note-on creates synth, note-off sends gate=0
        cleanup_notes = midi_note_map(midi, server, "synth")

        # Map CC1 (mod wheel) to a parameter on an existing synth
        # cleanup_cc = midi_cc_map(midi, server, some_synth,
        #                          cc_map={1: "cutoff"}, range_min=200.0, range_max=8000.0)

        # Or register handlers directly
        midi.on_note_on(lambda msg: print(f"Note {msg.note} vel {msg.velocity}"))
        midi.on_cc(lambda msg: print(f"CC {msg.control} = {msg.value}"))

        input("Press Enter to quit...")
        cleanup_notes()

NodeProxy / Ndef (Live Coding)

Hot-swap synth definitions while audio plays. NodeProxy manages a private audio bus, source synth, and monitor synth -- swapping replaces only the source with a crossfade:

import time
from nanosynth import Options, Server
from nanosynth.proxy import Ndef, NodeProxy
from nanosynth.ugens import LFNoise1, LPF, Saw, SinOsc

with Server(Options(verbosity=0)) as server:
    # NodeProxy: manual usage
    proxy = NodeProxy(server)
    proxy.source = lambda: SinOsc.ar(frequency=440) * 0.2
    proxy.play()
    time.sleep(2.0)

    # Hot-swap to saw wave (crossfades automatically)
    proxy.source = lambda: Saw.ar(frequency=330) * 0.15
    time.sleep(2.0)

    proxy.clear()

    # Ndef: concise named proxy registry
    Ndef(server, "pad", lambda: SinOsc.ar(frequency=220) * 0.2)
    Ndef(server, "pad").play()
    time.sleep(1.5)

    # Hot-swap via Ndef
    Ndef(server, "pad", lambda: Saw.ar(frequency=165) * 0.15)
    time.sleep(1.5)

    Ndef.clear_all(server)

Synthesis Techniques

The following examples show SynthDef definitions for various synthesis techniques. Each can be played using the Server class as shown above.

Using the @synthdef Decorator

For simpler definitions, use the decorator to skip the builder boilerplate. Parameter rates and lags are specified positionally:

from nanosynth import synthdef, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

@synthdef("kr", ("kr", 0.5))  # freq: control rate, amp: control rate with 0.5s lag
def my_sine(freq=440.0, amp=0.3):
    sig = SinOsc.ar(frequency=freq)
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=1.0),
        done_action=DoneAction.FREE_SYNTH,
    )
    Out.ar(bus=0, source=Pan2.ar(source=sig * amp * env))

scgf_bytes = my_sine.compile()  # my_sine is a SynthDef instance

Subtractive Synthesis

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LFNoise1, LPF, Out, Pan2, RLPF, Saw, WhiteNoise, XLine

# Saw wave through a sweeping low-pass filter
with SynthDefBuilder(frequency=110.0, amplitude=0.4) as builder:
    sig = Saw.ar(frequency=builder["frequency"])
    cutoff = XLine.kr(start=8000.0, stop=200.0, duration=3.0,
                      done_action=DoneAction.FREE_SYNTH)
    sig = LPF.ar(source=sig, frequency=cutoff)
    sig = sig * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

filtered_saw = builder.build(name="filtered_saw")

# White noise through a resonant LPF with LFO-modulated cutoff
with SynthDefBuilder(amplitude=0.15) as builder:
    sig = WhiteNoise.ar()
    lfo = LFNoise1.kr(frequency=4.0)
    cutoff = lfo * 1900.0 + 2100.0  # map [-1,1] to [200, 4000]
    sig = RLPF.ar(source=sig, frequency=cutoff, reciprocal_of_q=0.1)
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.5, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

resonant_noise = builder.build(name="resonant_noise")

FM Synthesis

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(
    carrier_freq=440.0, mod_ratio=2.0, mod_index=3.0,
    amplitude=0.3, gate=1.0,
) as builder:
    mod_freq = builder["carrier_freq"] * builder["mod_ratio"]
    modulator = SinOsc.ar(frequency=mod_freq) * builder["mod_index"] * mod_freq
    carrier = SinOsc.ar(frequency=builder["carrier_freq"] + modulator)
    env = EnvGen.kr(
        envelope=Envelope.adsr(
            attack_time=0.01, decay_time=0.1, sustain=0.7, release_time=0.3,
        ),
        gate=builder["gate"],
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = carrier * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

fm_synth = builder.build(name="fm_synth")

Additive Synthesis

Sum harmonics with decreasing amplitude to build a rich tone from pure sine partials:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=200.0, amplitude=0.3) as builder:
    sig = SinOsc.ar(frequency=builder["frequency"]) * 1.0
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 2.0) * 0.5
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 3.0) * 0.33
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 4.0) * 0.25
    sig = sig + SinOsc.ar(frequency=builder["frequency"] * 5.0) * 0.2
    sig = sig * 0.3  # normalize
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.01, release_time=2.0),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

additive = builder.build(name="additive")

Plucked String (Physical Modeling)

Karplus-Strong style plucked string using the Pluck UGen:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Dust, Out, Pan2, Pluck, WhiteNoise

with SynthDefBuilder(frequency=440.0, amplitude=0.5, decay=5.0) as builder:
    trig = Dust.ar(density=1.0)
    sig = Pluck.ar(
        source=WhiteNoise.ar(),
        trigger=trig,
        maximum_delay_time=1.0 / 100.0,
        delay_time=1.0 / builder["frequency"],
        decay_time=builder["decay"],
        coefficient=0.3,
    )
    sig = sig * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

pluck = builder.build(name="plucked_string")

Delay and Reverb Effects

Process a dry signal through comb delay and FreeVerb:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import CombC, FreeVerb, Out, Pan2, Saw, LPF

with SynthDefBuilder(frequency=220.0, amplitude=0.3) as builder:
    # Dry signal: filtered saw
    dry = Saw.ar(frequency=builder["frequency"])
    dry = LPF.ar(source=dry, frequency=2000.0)
    env = EnvGen.kr(
        envelope=Envelope.percussive(attack_time=0.005, release_time=0.3),
        done_action=DoneAction.FREE_SYNTH,
    )
    dry = dry * env * builder["amplitude"]

    # Comb delay for metallic echo
    sig = CombC.ar(
        source=dry,
        maximum_delay_time=0.2,
        delay_time=0.15,
        decay_time=2.0,
    )

    # Reverb
    sig = FreeVerb.ar(source=dry + sig, mix=0.4, room_size=0.8, damping=0.3)
    Out.ar(bus=0, source=Pan2.ar(source=sig))

delay_reverb = builder.build(name="delay_reverb")

Demand-Rate Sequencing

Use demand UGens to sequence pitches without host-side scheduling:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Duty, Dseq, Out, Pan2, SinOsc

with SynthDefBuilder(amplitude=0.3) as builder:
    # Dseq loops a sequence of MIDI-note frequencies at demand rate
    freq_pattern = Dseq.dr(
        repeats=4,
        sequence=[261.63, 293.66, 329.63, 392.00, 440.00, 392.00, 329.63, 293.66],
    )
    # Duty reads from the demand pattern every 0.25 seconds
    freq = Duty.kr(duration=0.25, level=freq_pattern)
    sig = SinOsc.ar(frequency=freq) * builder["amplitude"]
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.01, sustain_time=7.9, release_time=0.1),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env
    Out.ar(bus=0, source=Pan2.ar(source=sig))

sequencer = builder.build(name="sequencer")

Ring Modulation

Multiply two signals together for classic ring modulation:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LFTri, Out, Pan2, SinOsc

with SynthDefBuilder(
    carrier_freq=440.0, mod_freq=60.0, amplitude=0.3,
) as builder:
    carrier = SinOsc.ar(frequency=builder["carrier_freq"])
    modulator = LFTri.ar(frequency=builder["mod_freq"])
    sig = carrier * modulator  # ring mod = simple multiplication
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.05, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

ring_mod = builder.build(name="ring_mod")

Stereo Width with Detuning

Fatten a sound by panning two slightly detuned oscillators:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import LPF, Out, Saw

with SynthDefBuilder(frequency=110.0, detune=0.5, amplitude=0.4) as builder:
    left = Saw.ar(frequency=builder["frequency"] - builder["detune"])
    right = Saw.ar(frequency=builder["frequency"] + builder["detune"])
    left = LPF.ar(source=left, frequency=3000.0)
    right = LPF.ar(source=right, frequency=3000.0)
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.1, sustain_time=2.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    left = left * env * builder["amplitude"]
    right = right * env * builder["amplitude"]
    Out.ar(bus=0, source=[left, right])  # direct stereo output

stereo_saw = builder.build(name="stereo_saw")

Dynamics Processing

Apply compression to a signal using Compander:

from nanosynth import SynthDefBuilder, DoneAction
from nanosynth.envelopes import EnvGen, Envelope
from nanosynth.ugens import Compander, Dust, Out, Pan2, Ringz

with SynthDefBuilder(amplitude=0.5) as builder:
    # Sparse impulses through a resonant filter -- wide dynamic range
    sig = Ringz.ar(
        source=Dust.ar(density=3.0),
        frequency=2000.0,
        decay_time=0.2,
    )
    # Compress: bring quiet parts up, loud parts down
    sig = Compander.ar(
        source=sig,
        control=sig,
        threshold=0.3,
        slope_below=2.0,   # expand below threshold
        slope_above=0.5,   # compress above threshold
        clamp_time=0.01,
        relax_time=0.1,
    )
    env = EnvGen.kr(
        envelope=Envelope.linen(attack_time=0.01, sustain_time=3.0, release_time=0.5),
        done_action=DoneAction.FREE_SYNTH,
    )
    sig = sig * env * builder["amplitude"]
    Out.ar(bus=0, source=Pan2.ar(source=sig))

compressed = builder.build(name="compressed")

Advanced Features

SynthDef Compilation (No Engine Required)

SynthDef graphs can be compiled to SuperCollider's SCgf binary format without booting the audio engine -- useful for generating SynthDefs for any SuperCollider server:

from nanosynth import SynthDefBuilder, compile_synthdefs
from nanosynth.ugens import Out, Pan2, SinOsc

with SynthDefBuilder(frequency=440.0) as builder:
    Out.ar(bus=0, source=Pan2.ar(source=SinOsc.ar(frequency=builder["frequency"])))

synthdef = builder.build(name="sine")
scgf_bytes = synthdef.compile()

# Or compile multiple SynthDefs into a single SCgf blob
blob = compile_synthdefs(synthdef1, synthdef2, synthdef3)

Debugging SynthDef Graphs

SynthDef.dump_ugens() prints a human-readable UGen graph (like SuperCollider's SynthDef.dumpUGens):

print(synthdef.dump_ugens())
# SynthDef: sine
#   0: Control.kr - frequency, amplitude
#   1: SinOsc.ar(frequency: Control[0], phase: 0.0)
#   2: BinaryOpUGen.ar(MULTIPLICATION, a: SinOsc[0], b: Control[1])
#   ...

SynthDef Graph Introspection

Walk the UGen graph programmatically or export to Graphviz DOT format:

# Structured graph -- returns UGenNode/UGenInput NamedTuples
graph = sd.graph()
for node in graph.nodes:
    print(f"{node.node_index}: {node.type_name}.{node.rate}")
    for inp in node.inputs:
        if inp.source is not None:
            print(f"  {inp.name} <- {inp.source.type_name}[{inp.output_index}]")
        else:
            print(f"  {inp.name} = {inp.value}")

# Export to Graphviz DOT
dot = sd.to_dot(rankdir="LR")
print(dot)  # pipe to `dot -Tpng -o graph.png`

OSC Codec

The OSC module works standalone for any OSC communication needs:

from nanosynth import OscMessage, OscBundle

# Encode
msg = OscMessage("/s_new", "sine", 1000, 0, 1, "frequency", 440.0)
datagram = msg.to_datagram()

# Decode
decoded = OscMessage.from_datagram(datagram)
assert decoded == msg

# Bundles
bundle = OscBundle(
    timestamp=None,  # immediately
    contents=[
        OscMessage("/s_new", "sine", 1000, 0, 1),
        OscMessage("/n_set", 1000, "frequency", 880.0),
    ],
)
bundle_bytes = bundle.to_datagram()

Available UGens

Organized by category:

Category UGens
Oscillators SinOsc, Saw, Pulse, Blip, Klank, LFSaw, LFPulse, LFTri, LFCub, LFPar, VarSaw, SyncSaw, Impulse, FSinOsc, LFGauss, Vibrato, Osc, OscN, COsc, VOsc, VOsc3
Filters LPF, HPF, BPF, BRF, RLPF, RHPF, MoogFF, Lag, Lag2, Lag3, LagUD, Lag2UD, Lag3UD, Ramp, Decay, Decay2, Ringz, Formlet, Median, LeakDC, OnePole, OneZero, TwoPole, TwoZero, APF, FOS, SOS, MidEQ, Slew, Slope, Integrator, DetectSilence, Changed
BEQ Filters BLowPass, BHiPass, BBandPass, BBandStop, BAllPass, BLowShelf, BHiShelf, BPeakEQ, BLowCut, BHiCut
Noise WhiteNoise, PinkNoise, BrownNoise, GrayNoise, ClipNoise, Dust, Dust2, Crackle, LFNoise0, LFNoise1, LFNoise2, LFDNoise0, LFDNoise1, LFDNoise3, LFClipNoise, LFDClipNoise, Logistic
Stochastic Gendy1, Gendy2, Gendy3
Delays DelayN, DelayL, DelayC, Delay1, Delay2, CombN, CombL, CombC, AllpassN, AllpassL, AllpassC, BufDelayN, BufDelayL, BufDelayC, BufCombN, BufCombL, BufCombC, BufAllpassN, BufAllpassL, BufAllpassC, DelTapRd, DelTapWr
Envelopes EnvGen, Linen, Done, Free, FreeSelf, FreeSelfWhenDone, Pause, PauseSelf, PauseSelfWhenDone
Panning Pan2, Pan4, PanAz, PanB, PanB2, BiPanB2, Balance2, Rotate2, DecodeB2, XFade2, Splay
Demand Dseq, Dser, Dseries, Drand, Dxrand, Dshuf, Dwrand, Dwhite, Dbrown, Diwhite, Dibrown, Dgeom, Demand, Duty, DemandEnvGen, Dbufrd, Dbufwr, Dstutter, Dreset, Dswitch, Dswitch1, Dunique
Dynamics Compander, CompanderD, Limiter, Normalizer, Amplitude
Chaos LorenzL, HenonN/L/C, GbmanN/L, LatoocarfianN/L/C, LinCongN/L/C, CuspN/L, QuadN/L/C, StandardN/L, FBSineN/L/C
Granular GrainBuf, GrainIn, PitchShift, Warp1
Buffer I/O PlayBuf, RecordBuf, BufRd, BufWr, ClearBuf, LocalBuf, MaxLocalBufs, ScopeOut, ScopeOut2
Disk I/O DiskIn, DiskOut, VDiskIn
Physical Modeling Pluck, Ball, TBall, Spring
Reverb FreeVerb
Convolution Convolution, Convolution2, Convolution2L, Convolution3
Phase Vocoder FFT, IFFT, PV_Add, PV_BinScramble, PV_BinShift, PV_BinWipe, PV_BrickWall, PV_ConformalMap, PV_Conj, PV_Copy, PV_CopyPhase, PV_Diffuser, PV_Div, PV_HainsworthFoote, PV_JensenAndersen, PV_LocalMax, PV_MagAbove, PV_MagBelow, PV_MagClip, PV_MagDiv, PV_MagFreeze, PV_MagMul, PV_MagNoise, PV_MagShift, PV_MagSmear, PV_MagSquared, PV_Max, PV_Min, PV_Mul, PV_PhaseShift, PV_PhaseShift90, PV_PhaseShift270, PV_RandComb, PV_RandWipe, PV_RectComb, PV_RectComb2, RunningSum
Machine Listening BeatTrack, BeatTrack2, KeyTrack, Loudness, MFCC, Onsets, Pitch, SpecCentroid, SpecFlatness, SpecPcile
Hilbert FreqShift, Hilbert, HilbertFIR
I/O In, Out, InFeedback, LocalIn, LocalOut, OffsetOut, ReplaceOut, XOut
Lines Line, XLine, LinExp, LinLin, DC, K2A, A2K, AmpComp, AmpCompA, Silence
Triggers Trig, Trig1, Latch, Gate, Schmidt, Sweep, Phasor, Peak, PeakFollower, RunningMax, RunningMin, SendTrig, Poll, SendReply, SendPeakRMS, ToggleFF, TDelay, ZeroCrossing, LeastChange, MostChange, Clip, Fold, Wrap, InRange
Mouse/Keyboard KeyState, MouseButton, MouseX, MouseY
Info SampleRate, SampleDur, BlockSize, ControlRate, ControlDur, SubsampleOffset, RadiansPerSample, NumRunningSynths, BufFrames, BufSamples, BufSampleRate, BufRateScale, BufChannels, BufDur, NumOutputBuses, NumInputBuses, NumAudioBuses, NumControlBuses, NumBuffers, NodeID
Random Rand, IRand, ExpRand, LinRand, NRand, TRand, TIRand, TExpRand, CoinGate, TWindex, RandID, RandSeed, Hasher, MantissaMask
Utility MulAdd, Sum3, Sum4, Mix
Safety CheckBadValues, Sanitize

Envelope Types

from nanosynth import Envelope

Envelope.adsr(attack_time=0.01, decay_time=0.3, sustain=0.5, release_time=1.0)
Envelope.asr(attack_time=0.01, sustain=1.0, release_time=1.0)
Envelope.linen(attack_time=0.01, sustain_time=1.0, release_time=1.0)
Envelope.percussive(attack_time=0.01, release_time=1.0)
Envelope.triangle(duration=1.0, amplitude=1.0)

# Custom envelope
Envelope(amplitudes=[0, 1, 0.5, 0], durations=[0.1, 0.3, 0.6], curves=[-4])

Documentation

API reference docs are auto-generated from docstrings using mkdocs-material and mkdocstrings.

make docs        # build static site to site/
make docs-serve  # serve locally at http://127.0.0.1:8000 with live reload
make docs-deploy # deploy to GitHub Pages

Browse the docs at shakfu.github.io/nanosynth.

Development

make dev              # uv sync + editable install
make build            # build wheel (incremental via build cache)
make sdist            # build source distribution
make test             # run tests
make lint             # ruff check --fix
make format           # ruff format
make typecheck        # mypy --strict
make qa               # all of the above
make demos            # run scsynth demo scripts
make demos-supernova  # run supernova demo scripts
make clean            # remove transitory files (preserves build cache)
make reset            # clean everything including build cache

CI

The GitHub Actions workflow (.github/workflows/build.yml) builds wheels for CPython 3.10--3.14 on macOS ARM64, Linux x86_64, and Windows x86_64 using cibuildwheel. A qa job runs lint, format check, typecheck, and tests on every push. A source distribution ('sdist') is built separately and all artifacts are aggregated into a single downloadable archive.

A separate release workflow (.github/workflows/release.yml) publishes to PyPI on tag push via trusted publisher, with manual dispatch for TestPyPI.

Attributions

  • SuperCollider -- the audio synthesis engine and programming language that nanosynth embeds.
  • supriya -- the inspiration for nanosynth; its UGen system and SynthDef compiler were the basis for this project's graph compilation pipeline.
  • sc3 - Another SuperCollider library for Python with less features than supriya.
  • TidalCycles -- live coding pattern language for music, built on SuperCollider.
  • Strudel -- JavaScript port of TidalCycles for browser-based live coding.
  • Sonic Pi -- live coding music synth built on SuperCollider.
  • RtMidi -- cross-platform MIDI I/O library, vendored for the _midi extension.
  • nanobind -- the C++/Python binding library used to embed libscsynth, RtiMidi and the OSC codec.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nanosynth-0.1.6.tar.gz (7.2 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

nanosynth-0.1.6-cp314-cp314-win_amd64.whl (2.7 MB view details)

Uploaded CPython 3.14Windows x86-64

nanosynth-0.1.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.1.6-cp314-cp314-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

nanosynth-0.1.6-cp313-cp313-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.13Windows x86-64

nanosynth-0.1.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.1.6-cp313-cp313-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

nanosynth-0.1.6-cp312-cp312-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.12Windows x86-64

nanosynth-0.1.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.1.6-cp312-cp312-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

nanosynth-0.1.6-cp311-cp311-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.11Windows x86-64

nanosynth-0.1.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.1.6-cp311-cp311-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

nanosynth-0.1.6-cp310-cp310-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.10Windows x86-64

nanosynth-0.1.6-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

nanosynth-0.1.6-cp310-cp310-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file nanosynth-0.1.6.tar.gz.

File metadata

  • Download URL: nanosynth-0.1.6.tar.gz
  • Upload date:
  • Size: 7.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6.tar.gz
Algorithm Hash digest
SHA256 736cf9570d8fd1816636c71052cf5c069907e70c380e17433839431b12dbc15e
MD5 e4247bc5c70d1bc03d65c65b975bbb6f
BLAKE2b-256 2047f3bc69cbc7cb35005621e6fc23e1b970e1c58c7bef4b599a6727222d9e17

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.1.6-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 f9db6c5d0c490095db05dd15fc6dcd6ba398dc769850fe13cd3a1d365a0588d6
MD5 3e3e700daf5c0a968cd8fc9a6a967dc2
BLAKE2b-256 b92aeaa941b67178b7f39b70830d51d7597a74932e4a44228a4c256a3e242fdc

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 62369f625dc2beea27a1e9bb9750602889f53d129e1b3b79fec1ce2e8d5b9fe8
MD5 53a98302fa419e6ec3e973afbf8bf4a3
BLAKE2b-256 cc78bc2f1ee7ad162106e82edb35cd6ea25ef81e4ee5f3e761eb04ca153d2f4a

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 14c977ca079231c62a37fc6b0c63b8b1afbaa4b7ba749079c5b0aaa75bd57bc0
MD5 b2194c6be1b7e50009439a2bcb6e0048
BLAKE2b-256 8c9abfced98d586d0d42b785338bfbd51ea2452f61ab96d96dceb7e77393b460

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.1.6-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 94e3ee488e4a26ed29848b4f358b30473962fae1538caa53eeb6fe7eb7e03a34
MD5 2a9518a479ca07fb0564d0fc2adfc837
BLAKE2b-256 4888866e538dec5cc96143602cd826591f4b4df587ecfa9e82f5c912de999efe

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c03cb8bc69ce61c12841adec4c3733e99812380e4b865d4d8faed41e375fb15c
MD5 8f6e3039169fb79f91af0b074bab77cf
BLAKE2b-256 6585d3db01af0e96b8fcc69b5ec1feb91dbe5c091bca5bccd9a77885ba171d46

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ebf0ff38475bf396c77c0244d06d8d3a66ea84d6793483cc2dd85ebe4ce986e0
MD5 a97a7749f6470eb2626baf87ddaf2973
BLAKE2b-256 568188f3ebba8db50081db5a2813435bf59cbc6054376405bd4a10ec8f146e93

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.1.6-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 8ea7955424f01dce19d1892381cdc9c38ba363ff8dad027d4524ceb060bde57c
MD5 5724cc5068f028b290f045c0e15be2e3
BLAKE2b-256 11cb10206171ad329b0e18e1110e6a0d4a25b8c2945a620753dc50d7b9e244e0

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 df28bb251464ffa13fd6afea02ac1f6834c603be0c5a434c5d7afd97b4fc0f72
MD5 fb4bbbf229af495a9ae5899021e69749
BLAKE2b-256 fac566c31fe909ab31be0a7efe42d28d531669fa6eda3fe7b60e97c8489c42bb

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 777059ab90b5992e6ce4490ff6babbef8fd1730a8fd8e86dec518e9e5d618103
MD5 75bcc1a0224d488fecd3e669a6360895
BLAKE2b-256 907253a0e3f3be0a21ffdc247d7bd779f33838573b4965eaa550e82af43bb803

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.1.6-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 a2517590416676dbbe238f03f23c1c76bf2aab71402d4d6cf33d1104ad3d98bf
MD5 7216b8bd880a1d9fd57cf1f035b78f77
BLAKE2b-256 8c4aeffb7cf23680a0c9e26e2f12b010992863ccb752d0d9486f1f87a36905ec

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 246ab38800a13ce7d550eb3f9e6d1e582376656c4084ff460767957142c97e5c
MD5 297650930e041e13f2156d9aa609dacd
BLAKE2b-256 fe75795c259ea47e4f8d2c728728341713524e6b2e69c41602d82485c4f0f65f

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dbffb9cca8557f5e31883bc22dda095f0378b9883788c11af368f668d8a48529
MD5 d1c790c1e4fd9b8961034b36555a6600
BLAKE2b-256 b9448d0039ea293aee63370c265aa0acbd8c948bbcdd0f54db3a4adbae420a16

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: nanosynth-0.1.6-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for nanosynth-0.1.6-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 e9aa13b8ed2c9a689e475fbdd08325a5fb47cdb1fa43c99cce7f4981c9c8f6c8
MD5 87404caddfedc9b298dfe958842dcaf9
BLAKE2b-256 e4da8d078bd64ab4f92899a0da9abcf5359d356cba9b603fcec5a5219782a586

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 87ccaf04bd0397f5cbedfdea7004bc80e816add12158fd8f6fa6d43e84666855
MD5 14ff4a491512a0da4313ea7409a18954
BLAKE2b-256 d2d26ba46a35aa607c910ec4200765199e56fe3d1fe3bc3a02e37fb430fcf2c5

See more details on using hashes here.

File details

Details for the file nanosynth-0.1.6-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nanosynth-0.1.6-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f33a851bd236a580402acedc86a9d3158266a2643b724f5bf86488edef29638f
MD5 9834ed53b6f99d1c4f94b57651aa0908
BLAKE2b-256 be7469e69d0eaf6fd52041a1908e2a15af45db599948c07040549b2ee990cd06

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page