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
21 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