Skip to content

Common Lisp bindings for libpd, the data-processing layer of PureData.

License

Notifications You must be signed in to change notification settings

aartaka/cl-pure-data

Repository files navigation

cl-pure-data – Common Lisp bindings for libpd.

This is a chaotic set of bindings for libpd, the data-processing interface for PureData. This library does not emit any sound or picture – you have to create images and sounds yourself.

Getting Started

Clone the repository:

git clone https://github.com/aartaka/cl-pure-data

Load the library into your Lisp image:

(asdf:load-asd #p"/path/to/cl-pure-data/cl-pure-data.asd")
(asdf:load-asd :cl-pure-data)

The dependencies are CFFI and Alexandria, they are easy to reach in almost any implementation. Additionally, make sure libpd is in your cffi:*foreign-library-directories*.

After it loads fine, you can start hacking:

;; Initialize hooks to print everything out.
(pd:init-default-hooks)
;; Initialize audio to two input, two outputs and sample rate of 44100.
(pd:init-audio 2 2 44100)
;; Load a patch from somewhere in the filesystem.
(pd:open-patch #p"/nested/directories/and/whatever.pd")
;; Enable Direct Sound Processing (e.g. playback of the data you feed into libpd).
(pd:message "pd" "dsp" 1)
;; Feed the data for three ticks into the image.
;; DOES NOT PLAY ANY AUDIO :(
(pd:process #(3.0 4.3 1.0 ...) 3)

And then do whatever you want with the data you get!

Proxies

cl-pure-data exposes the defproxy macro to define proxies – patch-backed Lisp code seamlessly translating into PD code. It’s somewhat restricted at the moment, but is also working for dynamic patching (pun intended) of PD files and on-the-fly processing.

To see how that works, try (taken from the defproxy documentation)

;; Assuming you initialized cl-pure-data as in previous snippets already.
(pd:init-default-hooks)
(pd:init-audio 0 1 44100)
(pd:message "pd" "dsp" 1)
;;
;; This creates two functions, bloopy and bloopy-off, loading and
;; unloading the generated patch respectively. The contents of the
;; patch are the PD commands generated from the Lisp code in the body.
(pd:defproxy bloopy ()
  (dac~ (osc~ (+~ (*~ (samphold~ (noise~) (phasor~ 6)) 200) 440))))
;; Start bloopy. You won't hear anything, but `ps:process' will start returning different values
(bloopy)
;; Make it emit sound. Ensure you have cl-pure-data/asla or an equivalent loaded!
(loop repeat 1000 do (pd:process #()))
;; Disable bloopy.
(bloopy-off)

;; Create an even simpler proxy, yet having an argument.
(pd:defproxy osc (&optional (freq 440))
  (dac~ (osc~ freq)))
;; Initialize it and make it play sound. Use the default frequency.
(osc)
(loop repeat 500 do (pd:process #()))
;; Now change the frequency to something audibly higher.
(osc 600)
(loop repeat 500 do (pd:process #()))
;; Don't run this -- for the safety of your ears!
(osc 4000)
(loop repeat 1000 do (pd:process #()))

Structure of the library

  • Low-level API:
    libpd.lisp
    low-level bindings to libpd. C functions with lispy names, beware of malloc&free business!
  • High-level API:
    hooks.lisp
    high-level bindings to PD event hooks. Simply push your function into the hook you like, and you’ll see the effect the next time the event fires!
    arr.lisp
    high-level-ish bindings for PD arrays. Exposes arr to reference arrays by name, contents to get subsequences of those (literally the same set of arguments as subseq), and elem to reference just one element (think elt). Both contents and elem are setf-able.
    audio.lisp
    high-level API to PD audio processing capabilities. init-audio sets things up for processing, while process takes an input and produces and output, threading it all through PureData image.
    pure-data.lisp
    generic high-level macros (with-pd, defpdfun), patch handling (open-patch, close-patch, with-patch), message-sending (message), and cleanup (release).
    proxy.lisp
    Proxies (terminology taken from cl-collider) to express PD patches in Lisp code and dynamically modifying those. All with the help of defproxy macro.

Most of the cl-pure-data functions return

  • t (or other meaningful truthy value) on success
  • and nil on failure,
  • but some also signal conditions, so watch out!

Someday this will be more structured. Someday…

Thing that this library can do:

  • Directly interfacing with libpd.
    • libpd package with the function names mirroring the ones from libpd.
  • Initializing PureData instance.
    • happens automatically when you call any function from cl-pure-data package or via libpd:libpd-init.
  • Loading and unloading PureData patches (.pd) into the instance.
  • Binding hooks to PureData events.
    • Binding sensible default hooks via init-default-hooks.
    • Binding to individual hooks (from hooks.lisp) via simple (push #'handler hook).
  • Sending messages to the current instance via message.

Things that this library cannot do (yet?)

  • [X] Subscribing to messages sent by PD.
  • [ ] MIDI interfacing (I’ve been too lazy and MIDI-ignorant to do it…)
  • [ ] Wrapping messages the right way.
  • [ ] Concatenating print messages (although simply printing them to standard output works fine too…)
  • [ ] Conveniently managing instances. Right now instances are only swap-able when using libpd package. Ideally, this package should never be accessed by the end-user. Even though it’s exported.
  • [ ] Binding queued API for thread-safety.
  • [X] Producing sound (although using also-alsa or cl-portaudio should be pretty straightforward in this context).
    • [ ] There’s also-alsa-based backend now, but it only plays well with one output channel :D
    • [ ] Producing visuals is not an option either, until you leverage some image/video library.
  • [X] Making new PureData patches out of Lisp code.
    • [X] Should be relatively easy—take Lisp code, process it into .pd code format, write it into a file, and load it into the current instance.
      • [ ] Now, how do we express self-referential and multiple-output nodes in essentially non-cyclical code…
  • [ ] Signaling recoverable conditions for some exceptional case, like broken messages.

About

Common Lisp bindings for libpd, the data-processing layer of PureData.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published