Llama, an OCaml library for declaratively building software-defined modular synthesizers

llama is a library for building audio synthesizers using a declarative EDSL. It contains a collection of combinators which consume and produce streams of values (usually floats for audio signals and bools for control signals) which can be composed much in the same way as one would patch a modular synthesizer. The library also contains a player which can play a stream of floats (treated as audio samples) through your sound card.

An additional library llama_interactive can be used to connect synthesizers to input events and to render oscilloscope visualizations:

Some demos (from the examples directory):

There are more demo videos linked from the readme.

Get llama with opam install llama or opam install llama_interactive. A third package llama_core contains just the core type definitions and combinators but not the player. Use this if you just want to make more effects, filters, etc without depending on additional packages needed to play audio.

Note that llama (and llama_interactive) depends on conf-rust-2021 as interacting with the sound card is done using the cpal rust library. If you don’t want to install rust system-wide (e.g. because you use rustup) then run opam install conf-rust-2021 --assume-depexts before installing llama.

Code Example

This will play repeating pulses of a 440Hz sine wave.

open Llama
open Dsl

(* [osc] represents a signal whose value varies between -1 and 1 according
   to a 440Hz sine wave. *)
let osc : float Signal.t = oscillator (const Sine) (const 440.0)

(* [note_clock] represents a signal whose value is either [true] or [false]
   which changes from [false] to [true] twice per second, and spends 30% of the
   time on. This is often used to communicate the fact that a key is pressed to
   a module that responds to such events. *)
let note_clock : bool Signal.t =
  pulse ~frequency_hz:(const 2.0) ~duty_01:(const 0.3)

(* [envelope] is a signal which is 0 while its [gate] argument is producing
   [false] values, but which raises to 1 over the course of [attack_s] seconds
   when [gate] transitions to [true], and transitions back to [false] when
   [gate] transitions to [false]. Note that even though it is also a [float
   Signal.t] like [osc] is, it doesn't contain audio data. Instead an envelope
   is typically used to modulate a signal in response to a key press, which we
   are simulating here with [note_clock]. *)
let envelope : float Signal.t =
  asr_linear ~gate:note_clock ~attack_s:(const 0.01) ~release_s:(const 0.2)

(* Multiply the oscillator with the envelope to produce a repeating
   burst of volume which gradually tapers off twice per second. *)
let output : float Signal.t = osc *.. envelope

(* Play the sound! *)
let () = play_signal output
24 Likes

A bit unfortunate name, given the hype over LLMs these days and llama being the biggest open source one.

1 Like

Haha whoopsie! Well I must commend llama.cpp on an excellent choice of name. I wonder if llama.cpp can generate an OCaml program which uses llama to play a song.

3 Likes

Names are one of the hard things, of course, but IMO a name clash with something that happens to be popular but has no connection with this PL community seems pretty insignificant :wink:

Hi, @gridbugs! :wave:

I missed this library when it was announced last year, but I’m glad to have learned of it last week. I’ve wanted to play around with sound generation via OCaml for a long while, and this will be on my radar for whenever I actually manage to carve out time :smiley:

Do you happen to recall any of the reasons that led you to use Rust bindings to cpal rather than going with Savonet’s binding lib to ao? (This is an earnest and naive question, as I have no prior knowledge of this kind of stuff :slight_smile: )

Hey @shonfeder! I was unaware of ao and its ocaml bindings. Thanks for the heads up! I started working on llama during a 2-day hackathon (Tarides hacking days) and didn’t want to spend a lot of time getting low-level audio working. I’d used cpal in the past and found it to work well on macos, linux, windows, and wasm, with no additional system libraries needed (except libasound on linux but most systems should already have that). The trade-off of course is that you have to install rust in order to compile my library.

1 Like

Oh cool! Thanks for sharing :slight_smile: For future reference, it’s nice to know that it wasn’t due to any particular shortcoming in ao. It makes sense to stick close to a stack that you know works well. Also I’m quite optimistic about the future of using the rust ecosystem for ovaml.