Skip to content
/ xipd Public

programming language for audio processing that compiles to PureData

License

Notifications You must be signed in to change notification settings

xi/xipd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

xipd is a programming language for audio processing that compiles to PureData.

Example

include "std.xipd"

osc(freq) {
    osc = `osc~`
    freq -> osc
    return osc
}

osc1 = osc(1)
osc2 = osc(osc1 *~ 20 +~ 440)
osc2 -> `dac~`

PureData Primer

PureData is a graphical programming environment for real-time audio processing. It consists of nodes and connections. For example, there could be one oscillator node, one node for audio output, and a connection between them.

There are two kinds of connections: control connections for sporadic messages and signal connections for continuous streams. Nodes that expect signal connections are usually suffixed with "~". For example, the + node can be used to add two numbers, but if you want to mix audio streams you have to use +~ instead.

Nodes can have multiple inlets and outlets. For example, the + node has two inlets. The value of a node is only updated when it receives a message to its first inlet. The first inlet is therefore referred to as hot while the others are called cold.

PureData comes with a large set of built-in nodes for low-level audio processing. There is also a large community that shares more high-level structures.

Goals

I personally had some issues with PureData, so I tried to wrap it in something that feels more familiar to me. The result of that attempt is xipd. The goals are:

  • Text-based instead of visual programming language: This is crucial so authors can use common tools like vim or git. PureData does have a text representation, but it is not really meant for humans.
  • Variables: In its text representation, PureData references nodes by index. I want to use human-readable names instead.
  • Includes: In order to structure code, I want to be able to split it into several files.
  • Functions: With PureData it is common to copy large sets of nodes. Instead I want to have functions so that code can be reused in a structured way.
  • Inlets are hot by default: While I understand that cold inlets are a powerful tool for control flow, I found it to be very unintuitive.
  • Small language, extensive standard library: The language itself should be simple and stable. Over time, a standard library of functions should emerge that provides useful abstractions.
  • Intuitive standard library: I had a hard time adjusting to the built-in nodes. I still don't really understand some of them, while some features that I would have expected are missing. I hope that a standard library of functions can provide a more intuitive set of primitives by wrapping those builtins.
  • No loss of features: xipd should be able to express everything that PureData can express.

Features

Names

Names for functions and variables must consist of upper and lower letters, digits, and underscores. The first character must not be a digit.

Expressions

An expression always represents a node combined with a default inlet/outlet. If no inlet/outlet is provided it always defaults to 0. There are many different types of expressions:

  • String: (e.g. "foo")
  • Integer: (e.g. 1)
  • Float: (e.g. 1.0)
  • Raw: a raw PureData object node (e.g. `osc~ 440`)
  • Reference: A variable with an optional explicit inlet/outlet that overwrites the default one (e.g. foo:1)
  • Function call: The arguments can be arbitrary expressions (e.g. foo(`osc~`, 15))
  • Operator: (e.g. foo:1 * 2 + 15)

Statements

Each file is parsed line by line. Empty lines and lines starting with # are ignored, as is leading and trailing whitespace. All other lines must match one of the following patterns:

  • Assignment: Assign an expression to a name (e.g. foo = 1)
  • Connection: Create a connection between two nodes. (e.g. foo:1 -> `dac~` )
  • Include: Include code from another file (e.g. include "../foo.xipd")
  • Array: Create a named array that can store data, e.g. samples (e.g. array "mysample")
  • Start a function: (e.g. foo(a, b) {)
  • End a function: Each function must end with }
  • Return: Each function must return an expression. This should be the last statement of the function because no further statements are executed. (e.g. return foo:1)
  • Expression: Sometimes it is useful to have expressions (especially function calls) without assigning them to a name, just for their side effects.

Low-level language

By not using functions and operators you can have a low-level language that is quite close to the PureData text representation:

X1 = 1
X2 = `osc~`
X3 = 20
X4 = `*~`
X5 = 440
X6 = `+~`
X7 = `osc~`
X8 = `dac~`
X1:0 -> X2:0
X2:0 -> X4:0
X3:0 -> X4:1
X4:0 -> X6:0
X5:0 -> X6:1
X6:0 -> X7:0
X7:0 -> X8:0

About

programming language for audio processing that compiles to PureData

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published