Packages

An Elixir wrapper for the music live-coding language SuperCollider. Supex communicates with SuperCollider’s `sclang` tool, letting you generate and control sound directly from Elixir.

Current section

Files

Jump to
supex lib supex.ex
Raw

lib/supex.ex

defmodule Supex do
@moduledoc """
Supex = SuperCollider + Elixir
An Elixir wrapper for the music live-coding language SuperCollider.
Supex communicates with SuperCollider’s `sclang` tool, letting you generate and control sound directly from Elixir.
- 🎧 Play basic oscillators with a clean, pipeable syntax
- 🔤 Send raw SuperCollider code when needed
- ⛔ Stop sounds by name
Built for musicians, coders, and live performers who want to use Elixir for audio synthesis.
👉 Requires `sclang` installed (`sudo apt install sclang` on Linux)
## Installation
The package can be installed by adding `supex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:supex, "~> 0.1.0"}
]
end
```
## 🔍 Learn SuperCollider basics
https://doc.sccode.org/Tutorials/Getting-Started/00-Getting-Started-With-SC.html
## ⚙️ Requirements:
SuperCollider's `sclang` must be installed.
Example (Linux): `sudo apt install sclang`
## 🟢 Start the SuperCollider sclang server
```elixir
iex> Supex.Sclang.start_link(:ok)
```
## 💡 Examples
️▶ Play a sine oscillator at 269 Hz and name it "y"; then stop it:
```elixir
iex> import Supex
iex> osc |> freq(269) |> name("y") |> play
iex> osc |> name("y") |> stop
```
▶ Modulate volume of a sine wave using another sine as LFO:
```elixir
iex> import Supex
iex> osc |> mul(osc |> freq(2) |> mul(0.4) |> add(0.5) |> lfo) |> play
iex> osc |> stop
```
▶ Modulate a pulse wave's frequency and width:
```elixir
iex> import Supex
iex> osc(:pulse) |> freq("SinOsc.kr(0.4).range(169, 269)") |> width("SinOsc.kr(6.9).range(0.01, 0.8)")|> mul(0.3) |> name("x") |> play
iex> osc |> name("x") |> stop
```
🔤 Send a raw SuperCollider expression:
```elixir
iex> import Supex
iex> "RLPF.ar(Pulse.ar([100, 250], 0.5, 0.1), XLine.kr(8000, 400, 5), 0.05)" |> play
iex> osc |> name("z") |> stop
```
## ⚠️ Disclaimer
SuperCollider and Supex can produce loud, sudden sounds.
Use volume control and a limiter to protect your ears.
Avoid hearing damage.
"""
alias Supex.Pan
alias Supex.Synth
alias Supex.Sclang
alias Supex.Ugen
@spec osc() :: struct()
@doc """
Create a oscillator.
`type` can be:
`:sin` (sinus wave)
`:saw` (saw wave)
`:pulse` (pulse wave, aka square wave)
Defaults to `:sin`.
"""
@doc since: "0.1.0"
defdelegate osc(type \\ :sin), to: Ugen
@doc """
Transforms a "normal" oscillator to a LFO to use it as a modulator.
It uses `kr` (control rate) instead of `ar`(audio rate),
cf. https://doc.sccode.org/Tutorials/Getting-Started/05-Functions-and-Sound.html
## example
Modulate the volume of a sine wave with another sine wave as an LFO:
iex> import SC
iex> osc |> mul(osc |> freq(3) |> mul(0.5) |> add(0.5) |> lfo) |> play
iex> osc |> stop
or
iex> osc(:pulse) |> mul(osc|>freq(2)|>mul(0.4)|>add(0.5)|>lfo) |> freq(osc(:saw)|>freq(0.2)|>mul(100)|>add(100)|>lfo) |> play
"""
@doc since: "0.1.0"
@spec lfo(%Supex.Ugen{}) :: binary()
def lfo(%Ugen{} = ugen) do
%Ugen{sc_command: sc_command} = ugen |> Ugen.lfo()
sc_command
end
@doc """
Tune the frequency of an oscillator.
`freq` can be an integer or float.
"""
@doc since: "0.1.0"
@spec freq(%Supex.Ugen{}, integer() | float()) :: struct()
defdelegate freq(ugen, freq), to: Ugen
@doc """
The pulse width of an pulse wave oscillator.
`width` should be a value between `0.01` and `0.99`.
"""
@doc since: "0.1.0"
@spec width(%Supex.Ugen{}, integer() | float()) :: struct()
defdelegate width(ugen, width), to: Ugen
@doc """
The phase of a sinus wave.
"""
@doc since: "0.1.0"
@spec phase(%Supex.Ugen{}, integer() | float()) :: struct()
defdelegate phase(ugen, phase), to: Ugen
@doc """
Multiplication of the signal.
Chose values between `0.1` and `1` for not hurting your ears.
"""
@doc since: "0.1.0"
@spec mul(%Supex.Ugen{}, integer() | float()) :: struct()
defdelegate mul(ugen, mul), to: Ugen
@doc """
Add the value to the signal.
Values can be `0.1` or `1` for example.
"""
@doc since: "0.1.0"
@spec add(%Supex.Ugen{}, integer() | float()) :: struct()
defdelegate add(ugen, add), to: Ugen
@doc """
Adds a name for referencing.
e.g. with a name the singnal can be stopped on the SuperCollider server.
"""
@doc since: "0.1.0"
@spec name(%Supex.Ugen{}, binary()) :: struct()
defdelegate name(ugen, name), to: Ugen
@doc """
A synth definition.
"""
@doc since: "0.1.0"
@spec synth(binary()) :: binary()
defdelegate synth(name), to: Synth, as: :define
@doc """
Play the composed oscillator, or a raw SuperCollider's command (as a string).
## example
iex> import SC
iex> osc |> freq(269) |> name("y") |> play
"""
@doc since: "0.1.0"
@spec play(%Supex.Ugen{sc_command: binary(), sc_name: binary()} | binary()) :: :ok
def play(%Ugen{} = ugen), do: ugen |> Pan.center() |> Ugen.play() |> Sclang.execute()
@doc since: "0.1.0"
def play(sc_command) when is_binary(sc_command),
do: sc_command |> Pan.center() |> Ugen.play() |> Sclang.execute()
@doc """
Stop playing a oscillator with a name.
## example
iex> import SC
iex> osc |> name("y") |> stop
"""
@doc since: "0.1.0"
@spec stop(%Supex.Ugen{sc_name: binary()}) :: :ok
def stop(%Ugen{} = ugen) do
ugen |> Ugen.stop() |> Sclang.execute()
end
@doc "Stops all sound playing."
@doc since: "0.1.0"
@spec stop_playing() :: :ok
defdelegate stop_playing(), to: Sclang
@doc """
Executes a raw SuperCollider's command on the `sclang` server.
Must be a string.
"""
@doc since: "0.1.0"
@spec execute(binary()) :: :ok
defdelegate execute(sc_command), to: Sclang
end