cl-synthesizer

A Modular Audio Synthesizer Engine implemented in Common Lisp.

cl-synthesizer is an easy to use library which follows the approach of a classical hardware modular synthesizer, where modules are put into a rack and patched together with cables.

The source code of cl-synthesizer can be found here.

Installation

cd ~/quicklisp/local-projects
git clone https://github.com/Frechmatz/cl-wave-file-writer.git
git clone https://github.com/Frechmatz/cl-java-sound-client.git    
git clone https://github.com/Frechmatz/cl-synthesizer.git
(ql:quickload "cl-synthesizer")

Examples

Saw

(defpackage :cl-synthesizer-patches-saw
  (:documentation "Saw")
  (:use :cl))
(in-package :cl-synthesizer-patches-saw)

(defun example ()
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module
     rack
     "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 440.0 :v-peak 5.0)
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("VCO" :output-socket :saw))
     :filename "docs/saw.wav"
     :v-peak 5.0)
    rack))

(defun run-example ()
  (cl-synthesizer:play-rack (example) :duration-seconds 3.0))

;; (run-example)

Frequency modulated Saw

(defpackage :cl-synthesizer-patches-frequency-modulated-saw
  (:use :cl)
  (:documentation "Frequency modulated Saw"))
(in-package :cl-synthesizer-patches-frequency-modulated-saw)

(defun example ()
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module
     rack
     "LFO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 1.0
     :v-peak 5.0)
    (cl-synthesizer:add-module
     rack
     "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 440
     :v-peak 5.0
     :cv-lin-hz-v 20.0)
    (cl-synthesizer:add-patch rack "LFO" :sine "VCO" :cv-lin)
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("VCO" :output-socket :saw))
     :filename "docs/frequency-modulated-saw.wav"
     :v-peak 5.0)
    rack))

(defun run-example ()
  (cl-synthesizer:play-rack (example) :duration-seconds 3.0))

;;(run-example)

Two frequency modulated Saws

(defpackage :cl-synthesizer-patches-two-frequency-modulated-saws
  (:use :cl)
  (:documentation "Two frequency modulated Saws"))
(in-package :cl-synthesizer-patches-two-frequency-modulated-saws)

(defun make-modulated-saw (name environment &key lfo-frequency vco-frequency)
  (declare (ignore name))
  (let ((rack (cl-synthesizer:make-rack :environment environment)))
    (cl-synthesizer:add-module
     rack
     "LFO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency lfo-frequency :v-peak 5.0)
    (cl-synthesizer:add-module
     rack
     "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency vco-frequency :v-peak 5.0 :cv-lin-hz-v 20.0)
    (cl-synthesizer:add-patch rack "LFO" :sine "VCO" :cv-lin)
    (cl-synthesizer:add-rack-output rack :saw "VCO" :saw)
    rack))

(defun example ()
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module
     rack "SAW-1" #'make-modulated-saw :lfo-frequency 1.0 :vco-frequency 440.0)
    (cl-synthesizer:add-module
     rack "SAW-2" #'make-modulated-saw :lfo-frequency 2.0 :vco-frequency 442.0)
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("SAW-1" :output-socket :saw)
       ("SAW-2" :output-socket :saw))
     :filename "docs/two-frequency-modulated-saws.wav"
     :v-peak 5.0)
    rack))

(defun run-example ()
  (cl-synthesizer:play-rack (example) :duration-seconds 3.0))

;;(run-example)

Concepts

Environment

An environment defines properties such as the sample rate and the home directory of the synthesizer.

Module

Modules are creating the sounds of the synthesizer. Modules do have a name, inputs, outputs and a shutdown function. The inputs/outputs are represented by keywords and are so called sockets. The shutdown function can be used to release resources that have been allocated by the module.

Beside the input/output sockets a module can also expose "state sockets". State sockets represent internal states of the module. These sockets are not accessible when connecting modules with each other. Their purpose is to support debugging/analyzation of a module, for example by writing certain states to a CSV file.

A module is represented by a property list. This list provides functions such as to get the input sockets, to get the output sockets, to get the state sockets, to set input values, to retrieve output values, to update the module, to shutdown the module and so on.

A module must provide a factory/instantiation function. The typical name of this function is "make-module". When a module is added to the synthesizer then not the readily instantiated module is passed, but its factory function. This function is called by the synthesizer. The synthesizer passes the module name, the environment and any arbitrary initialization parameters to it.

For each input/output socket that a module exposes, it must provide a corresponding setter/getter function. When processing an update, the synthesizer sets the inputs of the module via successive calls to the input setters. An input setter must not change the current outputs of the module. When all inputs have been set, the synthesizer calls the update function of the module, which has no parameters. The update function sets the module-outputs according to the previously set inputs.

Example: A module

(defpackage :cl-synthesizer-example-1-adder-2
  (:use :cl)
  (:export :make-module))
(in-package :cl-synthesizer-example-1-adder-2)

(defun make-module (name environment)
  "Adder module with 2 inputs"
  (declare (ignore name environment))
  (let ((input-1 nil) (input-2 nil) (cur-output nil))
    (let ((inputs
            (list
             :input-1 (list
                       :set (lambda (value) (setf input-1 value))
                       :get (lambda() input-1))
             :input-2 (list
                       :set (lambda (value) (setf input-2 value))
                       :get (lambda() input-2))))
          (outputs
            (list
             :sum (list :get (lambda() cur-output)))))
      (list
       :inputs (lambda() inputs)
       :outputs (lambda() outputs)
       :update (lambda () (setf cur-output (+ input-1 input-2)))))))

Example: A module based on other modules

(defpackage :cl-synthesizer-example-1-adder-4
  (:use :cl)
  (:export :make-module))
(in-package :cl-synthesizer-example-1-adder-4)

(defun make-module (name environment)
  "Adder module with 4 inputs"
  (declare (ignore name))
  ;; create a rack
  (let ((rack (cl-synthesizer:make-rack :environment environment)))

    ;; add modules
    (cl-synthesizer:add-module rack "ADDER-1" #'cl-synthesizer-example-1-adder-2:make-module)
    (cl-synthesizer:add-module rack "ADDER-2" #'cl-synthesizer-example-1-adder-2:make-module)
    (cl-synthesizer:add-module rack "MAIN-ADDER" #'cl-synthesizer-example-1-adder-2:make-module)

    ;; patch modules
    (cl-synthesizer:add-patch rack "ADDER-1" :sum "MAIN-ADDER" :input-1)
    (cl-synthesizer:add-patch rack "ADDER-2" :sum "MAIN-ADDER" :input-2)
    
    ;; define rack inputs
    (cl-synthesizer:add-rack-input rack :input-1 "ADDER-1" :input-1)
    (cl-synthesizer:add-rack-input rack :input-2 "ADDER-1" :input-2)
    (cl-synthesizer:add-rack-input rack :input-3 "ADDER-2" :input-1)
    (cl-synthesizer:add-rack-input rack :input-4 "ADDER-2" :input-2)

    ;; define rack output
    (cl-synthesizer:add-rack-output rack :sum "MAIN-ADDER" :sum)

    ;; return the rack
    rack))

Rack

Racks are holding modules and their connections with each other. The connections are so called "Patches".

Example

(defpackage :cl-synthesizer-example-1
  (:use :cl))
(in-package :cl-synthesizer-example-1)


(defun make-example-rack ()
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module rack "ADDER" #'cl-synthesizer-example-1-adder-4:make-module)
    ;; add rack inputs
    (cl-synthesizer:add-rack-input rack :input-1 "ADDER" :input-1)
    (cl-synthesizer:add-rack-input rack :input-2 "ADDER" :input-2)
    (cl-synthesizer:add-rack-input rack :input-3 "ADDER" :input-3)
    (cl-synthesizer:add-rack-input rack :input-4 "ADDER" :input-4)
    ;; add rack output
    (cl-synthesizer:add-rack-output rack :sum "ADDER" :sum)
    rack))

(defun run-example ()
  (let ((rack (make-example-rack)))
    (let ((rack-inputs (funcall (getf rack :inputs)))
          (rack-outputs (funcall (getf rack :outputs))))
      ;; set inputs
      (let ((input-setter (getf (getf rack-inputs :input-1) :set)))
        (funcall input-setter 10))
      (let ((input-setter (getf (getf rack-inputs :input-2) :set)))
        (funcall input-setter 20))
      (let ((input-setter (getf (getf rack-inputs :input-3) :set)))
        (funcall input-setter 30))
      (let ((input-setter (getf (getf rack-inputs :input-4) :set)))
        (funcall input-setter 40))
      ;; update
      (cl-synthesizer:update rack)
      ;; print output of rack
      (let ((sum (funcall (getf (getf rack-outputs :sum) :get))))
        (format t "~%Sum:~a~%" sum))
      ;; shutdown
      (cl-synthesizer:shutdown rack))))

;; (run-example)

API

Environment

make-environment

cl-synthesizer:make-environment (&key (sample-rate 44100) (home-directory nil))

Creates an environment. An enviroment is a property list with the following keys:

*home-directory*

cl-synthesizer:*home-directory*

Optional home-directory setting of cl-synthesizer. Overrides the home-directory argument passed to make-environment.

Module

get-module-name

cl-synthesizer:get-module-name (module)

Returns the name of a module.

get-module-rack

cl-synthesizer:get-module-rack (module)

Returns the rack to which a module belongs.

update

cl-synthesizer:update (module)

Calls the update function of a module.

shutdown

cl-synthesizer:shutdown (module)

Calls the (optional) shutdown function of a module.

is-rack

cl-synthesizer:is-rack (module)

Returns t if the given module represents a rack.

Rack

make-rack

cl-synthesizer:make-rack (&key environment)

Creates a rack.

The function has the following parameters:

Returns the rack.

get-environment

cl-synthesizer:get-environment (rack)

Returns the environment of the rack.

add-module

cl-synthesizer:add-module (rack module-name module-fn &rest args)

Adds a module to a rack.

The function has the following parameters:

Returns the module.

get-module

cl-synthesizer:get-module (rack path)

Get a module by its name or path.

The function has the following parameters:

Returns the module or nil.

get-modules

cl-synthesizer:get-modules (rack)

Returns the modules of a rack.

add-patch

cl-synthesizer:add-patch (rack output-module-name output-socket input-module-name input-socket)

Adds a patch to the rack. A patch is an unidirectional connection between an output socket of a source module and an input socket of a destination module.

The function has the following parameters:

The rack signals an assembly-error in cases such as:

get-patches

cl-synthesizer:get-patches (rack)

Returns a list of the patches of a rack. Each patch is represented by a property list with the following keys:

add-rack-input

cl-synthesizer:add-rack-input (rack rack-input-socket input-module-name input-socket)

Exposes an input socket of a module as an input socket of the rack.

The function has the following parameters:

add-rack-output

cl-synthesizer:add-rack-output (rack rack-output-socket output-module-name output-socket)

Exposes an output socket of a module as an output socket of the rack.

The function has the following parameters:

add-hook

cl-synthesizer:add-hook (rack hook)

Adds a hook to a rack.

The function has the following parameters:

Hooks must not add/remove modules or patches.

play-rack

cl-synthesizer:play-rack (rack &key duration-seconds)

A utility function that "plays" the rack by consecutively calling its update function for a given number of "ticks".

The function has the following parameters:

Modules

System

cl-synthesizer-modules-system:make-module (name environment &key)

Creates a module which exposes runtime information of its rack.

The function has the following parameters:

The module has no inputs.

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-system-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-system-example-1)

(defun example ()
  "System example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "SYSTEM"
     #'cl-synthesizer-modules-system:make-module)
    
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("SYSTEM" :output-socket :ticks :name "ticks")
       ("SYSTEM" :output-socket :milliseconds :name "milliseconds")
       ("SYSTEM" :output-socket :seconds :name "seconds")
       ("SYSTEM" :output-socket :sample-rate :name "sample-rate"))
     :filename "cl-synthesizer-examples/system-example-1.csv"
     :add-header t
     :column-separator ",")

    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 2)))

;; (run-example)

VCO

cl-synthesizer-modules-vco:make-module (name environment &key base-frequency v-peak (cv-lin-hz-v 0.0) (duty-cycle 0.5) (phase-offset 0.0) (f-max 12000.0) (wave-forms nil) (sync-threshold 2.5))

Creates a Voltage Controlled Oscillator module with 1V/Octave and linear frequency modulation inputs. The oscillator has through-zero support.

The function has the following parameters:

The module has the following inputs:

The frequency of the oscillator is calculated by adding the frequencies resulting from the :cv-lin and :cv-exp inputs. The frequency is clipped according to the :f-max setting.

The module has the following outputs (depending on the :wave-forms parameter):

The module exposes the following states via the state function:

Example

(defpackage :cl-synthesizer-modules-vco-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-vco-example-1)

(defun example ()
  "Write all wave forms to a Wave and a CSV file"
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module
     rack
     "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 10.0 :v-peak 5.0)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("VCO" :output-socket :sine)
       ("VCO" :output-socket :triangle)
       ("VCO" :output-socket :saw)
       ("VCO" :output-socket :square))
     :filename "cl-synthesizer-examples/vco-example-1.wav"
     :v-peak 5.0)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("VCO" :output-socket :sine :name "Sine")
       ("VCO" :output-socket :triangle :name "Triangle")
       ("VCO" :output-socket :saw :name "Saw")
       ("VCO" :output-socket :square :name "Square"))
     :filename "cl-synthesizer-examples/vco-example-1.csv"
     :add-header t
     :column-separator ",")
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 1)))

;; (run-example)

VCA

cl-synthesizer-modules-vca:make-module (name environment &key cv-max (initial-gain 0.0) (exponential nil))

Creates a Voltage Controlled Amplifier/Attenuator module. The VCA multiplies an incoming signal with a factor of 0..1.

The function has the following parameters:

The module has the following inputs:

The effective amplification voltage is v = :cv + :gain + :initial-gain, where 0.0 <= v <= :cv-max.

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-vca-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-vca-example-1)

(defun example ()
  "Amplification of a 10kHz sine wave with a bipolar triangular signal."
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))

    ;; Set up oscillator modulating the amplification
    (cl-synthesizer:add-module
     rack "LFO-CV"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 0.5
     :v-peak 5.0)

    ;; set up oscillator providing the audio signal
    (cl-synthesizer:add-module
     rack "VCO-AUDIO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 5.0
     :v-peak 5.0)

    ;; Set up VCA
    (cl-synthesizer:add-module
     rack "VCA"
     #'cl-synthesizer-modules-vca:make-module
     :cv-max 5.0
     :exponential t)

    ;; Add patches
    (cl-synthesizer:add-patch rack "VCO-AUDIO" :sine "VCA" :input)
    (cl-synthesizer:add-patch rack "LFO-CV" :triangle "VCA" :cv)

    ;; Write VCA inputs/outputs to a CSV file
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("VCA" :input-socket :cv :name "CV")
       ("VCA" :input-socket :input :name "Input")
       ("VCA" :output-socket :output :name "Output"))
     :filename "cl-synthesizer-examples/vca-example-1.csv")
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 5.0)))

;; (run-example)

ADSR

cl-synthesizer-modules-adsr:make-module (name environment &key attack-time-ms attack-target-output decay-time-ms decay-target-output release-time-ms (gate-threshold 2.5) (backward-coupled nil) (exponential nil))

Creates an envelope generator module with the phases Attack, Decay, Sustain and Release.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-adsr-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-adsr-example-1)

(defun make-voice (name environment &key exponential)
  (declare (ignore name))
  (let ((rack (cl-synthesizer:make-rack :environment environment)))

    (cl-synthesizer:add-module
     rack "ADSR"
     #'cl-synthesizer-modules-adsr:make-module
     :attack-time-ms 500 :attack-target-output 5.0
     :decay-time-ms 250 :decay-target-output -3.0
     :release-time-ms 1000
     :exponential exponential)

    (cl-synthesizer:add-rack-input rack :gate "ADSR" :gate)
    (cl-synthesizer:add-rack-output rack :adsr-out "ADSR" :cv)

    rack))

(defun example ()
  "ADSR example. Linear vs. Exponential"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "MIDI-SEQUENCER"
     #'cl-synthesizer-modules-midi-sequencer:make-module :events
     (list 
      (list :timestamp-milli-seconds 0
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-on-event :channel 1 :note-number 69 :velocity 100)))
      (list :timestamp-milli-seconds 1500
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-off-event :channel 1 :note-number 69 :velocity 100)))))

    (cl-synthesizer:add-module
     rack "MIDI-IFC"
     #'cl-synthesizer-modules-midi-polyphonic-interface:make-module :voice-count 1)

    (cl-synthesizer:add-patch rack "MIDI-SEQUENCER" :midi-events "MIDI-IFC" :midi-events)
    
    (cl-synthesizer:add-module
     rack "GATE-MULTIPLE"
     #'cl-synthesizer-modules-multiple:make-module
     :output-count 2)

    (cl-synthesizer:add-patch rack "MIDI-IFC" :gate-1 "GATE-MULTIPLE" :input)

    (cl-synthesizer:add-module
     rack "LINEAR" #'make-voice :exponential nil)
    (cl-synthesizer:add-patch rack "GATE-MULTIPLE" :output-1 "LINEAR" :gate)
    
    (cl-synthesizer:add-module
     rack "EXPONENTIAL" #'make-voice :exponential t)
    (cl-synthesizer:add-patch rack "GATE-MULTIPLE" :output-2 "EXPONENTIAL" :gate)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("GATE-MULTIPLE" :input-socket :input :name "Gate")
       ("LINEAR" :output-socket :adsr-out :name "ADSR Linear")
       ("EXPONENTIAL" :output-socket :adsr-out :name "ADSR Exponential"))
     :filename "cl-synthesizer-examples/adsr-example-1.csv")
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 3)))

;; (run-example)

Multiple

cl-synthesizer-modules-multiple:make-module (name environment &key output-count)

Creates a Multiple module. A multiple passes the value of exactly one input socket to as many output sockets as defined by output-count.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-multiple-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-multiple-example-1)

(defun example ()
  "Multiple example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "LFO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 1.0 :v-peak 1.0)
    
    (cl-synthesizer:add-module rack "MULTIPLE"
                               #'cl-synthesizer-modules-multiple:make-module :output-count 5)
    (cl-synthesizer:add-patch rack "LFO" :sine "MULTIPLE" :input)
    (cl-synthesizer:add-rack-output rack :line-out-1 "MULTIPLE" :output-1)
    (cl-synthesizer:add-rack-output rack :line-out-2 "MULTIPLE" :output-2)

    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 10)))

;; (run-example)

Fixed Output

cl-synthesizer-modules-fixed-output:make-module (name environment &key value (output-socket out))

Creates a module with a fixed output value.

The function has the following parameters:

The module has no inputs. The module has one output socket according to the :output-socket parameter.

Example

(defpackage :cl-synthesizer-modules-fixed-output-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-fixed-output-example-1)

(defun example ()
  "Fixed-Output example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "FIXED-OUTPUT"
     #'cl-synthesizer-modules-fixed-output:make-module
     :value 3.0
     :output-socket :fixed)
    
    (cl-synthesizer:add-rack-output rack :line-out "FIXED-OUTPUT" :fixed)

    rack))

(defun run-example ()
  (cl-synthesizer:play-rack (example) :duration-seconds 1))

;; (run-example)

Adder

cl-synthesizer-modules-adder:make-module (name environment &key input-count)

Creates a simple voltage adder module.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Mixer

cl-synthesizer-modules-mixer:make-module (name environment &key channel-count channel-cv-max channel-cv-gain main-cv-max main-cv-gain)

Creates a mixer module. The mixer provides an attenuator for each input and a main attenuator for the mixer output. All attenuators have linear amplification characteristic.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-mixer-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-mixer-example-1)

(defun example ()
  "Mixer example."
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))

    ;;
    ;; add modules...
    ;;
    
    (cl-synthesizer:add-module
     rack "MIXER" #'cl-synthesizer-modules-mixer:make-module
     :channel-count 2
     :channel-cv-max 5.0
     :channel-cv-gain 5.0
     :main-cv-max 5.0
     :main-cv-gain 2.5)
    
    (cl-synthesizer:add-patch rack "VOICE-1" :audio "MIXER" :channel-1)
    (cl-synthesizer:add-patch rack "VOICE-2" :audio "MIXER" :channel-2)
    (cl-synthesizer:add-rack-output rack :line-out "MIXER" :output)
    
    rack))

Trigger

cl-synthesizer-modules-trigger:make-module (name environment &key trigger-threshold pulse-voltage)

Creates a Voltage to Trigger Converter module. The module fires a one clock cycle long pulse when input voltage >= trigger-threshold and then waits that the input voltage descends below trigger-threshold before the next pulse can be triggered. The module can for example be used to generate a trigger out of a gate signal.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-trigger-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-trigger-example-1)

(defun example ()
  "Emit trigger signal based on sine input"
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    (cl-synthesizer:add-module
     rack
     "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 5.0 :v-peak 5.0)

    (cl-synthesizer:add-module
     rack
     "TRIGGER"
     #'cl-synthesizer-modules-trigger:make-module
     :trigger-threshold 4.9 :pulse-voltage 3.0)

    (cl-synthesizer:add-patch rack "VCO" :sine "TRIGGER" :input)
    
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("TRIGGER" :input-socket :input)
       ("TRIGGER" :output-socket :output))
     :filename "cl-synthesizer-examples/trigger-example-1.wav"
     :v-peak 5.0)

    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 2)))

;; (run-example)

Ramp

cl-synthesizer-modules-ramp:make-module (name environment &key time-ms target-output (gate-state nil) (trigger-threshold 2.5) (gate-threshold 2.5) (exponential nil))

Creates a module whose output climbs from a given input value to a given output value in a given time. Main purpose of this module is to create envelope generators by chaining multiple ramp and sustain modules.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

When the ramp aborts due to a toggling Gate signal or when its supposed duration has been exceeded due to time modulation then the output value does not jump to the desired target-output but stays at its current value.

This module has been inspired by dhemery

Example

(defpackage :cl-synthesizer-modules-ramp-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-ramp-example-1)

(defun make-voice (name environment &key (exponential nil))
  (declare (ignore name))
  (let ((rack
         (cl-synthesizer:make-rack :environment environment)))

    (cl-synthesizer:add-module
     rack "ATTACK"
     #'cl-synthesizer-modules-ramp:make-module
     :time-ms 700 :target-output 5.0 :gate-state nil :exponential exponential)

    (cl-synthesizer:add-module
     rack "DECAY"
     #'cl-synthesizer-modules-ramp:make-module
     :time-ms 500 :target-output -2.5 :exponential exponential)
    
    (cl-synthesizer:add-rack-input rack :trigger "ATTACK" :trigger)
    (cl-synthesizer:add-patch rack "ATTACK" :busy "DECAY" :pass-through)
    (cl-synthesizer:add-patch rack "ATTACK" :output "DECAY" :input)
    (cl-synthesizer:add-patch rack "ATTACK" :done "DECAY" :trigger)

    (cl-synthesizer:add-rack-output rack :output "DECAY" :output)

    rack))

(defun example ()
  "Linear and Exponential Ramp"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))

    (cl-synthesizer:add-module
     rack "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 0.5 :v-peak 5.0)

    (cl-synthesizer:add-module
     rack "TRIGGER"
     #'cl-synthesizer-modules-trigger:make-module
     :trigger-threshold 4.9 :pulse-voltage 5.0)

    (cl-synthesizer:add-patch rack "VCO" :square "TRIGGER" :input)
    
    (cl-synthesizer:add-module
     rack "TRIGGER-MULTIPLE"
     #'cl-synthesizer-modules-multiple:make-module
     :output-count 2)

    (cl-synthesizer:add-patch rack "TRIGGER" :output "TRIGGER-MULTIPLE" :input)
    
    (cl-synthesizer:add-module
     rack "LINEAR" #'make-voice :exponential nil)
    (cl-synthesizer:add-patch rack "TRIGGER-MULTIPLE" :output-1 "LINEAR" :trigger)
    
    (cl-synthesizer:add-module
     rack "EXPONENTIAL" #'make-voice :exponential t)
    (cl-synthesizer:add-patch rack "TRIGGER-MULTIPLE" :output-2 "EXPONENTIAL" :trigger)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("LINEAR" :output-socket :output :name "Lin Out")
       ("EXPONENTIAL" :output-socket :output :name "Exp Out"))
     :filename "cl-synthesizer-examples/ramp-example-1.csv")
    
    rack))

(defun run-example ()
  (let ((rack (example))) (cl-synthesizer:play-rack rack :duration-seconds 5)))

;; (run-example)

Sustain

cl-synthesizer-modules-sustain:make-module (name environment &key (trigger-threshold 2.5) (gate-threshold 2.5))

Creates a module which holds a given input as long as its gate input is "on". Main purpose of this module is to create envelope generators by chaining multiple ramp and sustain modules.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

This module has been inspired by dhemery

Example

(defpackage :cl-synthesizer-modules-sustain-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-sustain-example-1)

(defun example ()
  "Sustain example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))
    
    ;; Use MIDI sequencer for generation of Gate signals
    (cl-synthesizer:add-module
     rack "MIDI-SEQUENCER"
     #'cl-synthesizer-modules-midi-sequencer:make-module :events
     (list 
      (list :timestamp-milli-seconds 300
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-on-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))
      (list :timestamp-milli-seconds 700
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-off-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))
      (list :timestamp-milli-seconds 1800
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-on-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))
      (list :timestamp-milli-seconds 2100
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-off-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))))

    (cl-synthesizer:add-module
     rack "MIDI-IFC"
     #'cl-synthesizer-modules-midi-polyphonic-interface:make-module :voice-count 1)

    (cl-synthesizer:add-module
     rack "GATE-MULTIPLE"
     #'cl-synthesizer-modules-multiple:make-module :output-count 2)

    (cl-synthesizer:add-module
     rack "TRIGGER"
     #'cl-synthesizer-modules-trigger:make-module
     :trigger-threshold 4.9 :pulse-voltage 5.0)

    (cl-synthesizer:add-module
     rack "VCO"
     #'cl-synthesizer-modules-vco:make-module
     :base-frequency 0.5 :v-peak 5.0)
    
    (cl-synthesizer:add-module
     rack "SUSTAIN"
     #'cl-synthesizer-modules-sustain:make-module)

    (cl-synthesizer:add-patch rack "MIDI-SEQUENCER" :midi-events "MIDI-IFC" :midi-events)
    (cl-synthesizer:add-patch rack "MIDI-IFC" :gate-1 "GATE-MULTIPLE" :input)
    (cl-synthesizer:add-patch rack "GATE-MULTIPLE" :output-1 "TRIGGER" :input)
    (cl-synthesizer:add-patch rack "GATE-MULTIPLE" :output-2 "SUSTAIN" :gate)
    (cl-synthesizer:add-patch rack "TRIGGER" :output "SUSTAIN" :trigger)
    (cl-synthesizer:add-patch rack "VCO" :sine "SUSTAIN" :input)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("MIDI-IFC" :output-socket :gate-1 :name "Gate")
       ("SUSTAIN" :input-socket :trigger :name "Sustain Trigger In")
       ("SUSTAIN" :input-socket :input :name "Sustain In")
       ("SUSTAIN" :output-socket :output :name "Sustain Out")
       ("SUSTAIN" :output-socket :done :name "Sustain Done Out"))
     :filename "cl-synthesizer-examples/sustain-example-1.csv")
    
    rack))

(defun run-example ()
  (let ((rack (example))) (cl-synthesizer:play-rack rack :duration-seconds 3)))

;; (run-example)

Wave File Writer

cl-synthesizer-modules-wave-file-writer:make-module (name environment &key channel-count filename v-peak (sample-width 16bit))

Creates a Wave File Writer module. Writes files in "Waveform Audio File" ("WAV") format.

The function has the following parameters:

The module has the following inputs:

The module has no outputs.

CSV File Writer

cl-synthesizer-modules-csv-file-writer:make-module (name environment &key columns filename (column-separator ,) (add-header t))

Creates a CSV File Writer module.

The function has the following parameters:

The module has the following inputs:

Due to performance/consing considerations all columns are written using the Lisp-Writer. If a value contains the column separator it will not be quoted. The file is opened on the first call of the update function and closed by the shutdown handler.

The module has no outputs.

Monitors

Monitors are hook implementations that register themselves at a rack. They are called after a rack has processed an update. They collect data like output socket values and pass them to a so called "Monitor Backend". A monitor backend can for example be a CSV-File-Writer. A monitor backend is a module. It is instantiated by a so called "Monitor Agent". This agent sits between the monitor and its backend. The agent instantiates the backend and defines the mapping of the values collected by the monitor to input sockets of the backend.

add-monitor

cl-synthesizer-monitor:add-monitor (rack monitor-agent socket-mappings &rest additional-agent-args)

Registers a monitor at a rack.

The function has the following parameters:

wave-file-agent

cl-synthesizer-monitor-wave-file-agent:make-backend (name environment input-socket-settings &rest rest &key filename &allow-other-keys)

Creates a monitor backend which writes to a Wave file.

The function has the following parameters:

The function returns a values object consisting of

Example

(defpackage :cl-synthesizer-monitor-wave-file-example-1
  (:use :cl))

(in-package :cl-synthesizer-monitor-wave-file-example-1)

(defun example ()
  "Wave-File-Agent example"
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "VCO" #'cl-synthesizer-modules-vco:make-module
     :base-frequency 5.0 :v-peak 5.0)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("VCO" :output-socket :sine))
     :filename "cl-synthesizer-examples/monitor-wave-file-example-1.wav"
     :v-peak 5.0)
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 2)))

;; (run-example)

csv-file-agent

cl-synthesizer-monitor-csv-file-agent:make-backend (name environment input-socket-settings &rest rest &key filename &allow-other-keys)

Creates a monitor backend which writes to a CSV file.

The function has the following parameters:

The function returns a values object consisting of

Example

(defpackage :cl-synthesizer-monitor-csv-file-example-1
  (:use :cl))

(in-package :cl-synthesizer-monitor-csv-file-example-1)

(defun example ()
  "CSV-Agent example"
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "VCO" #'cl-synthesizer-modules-vco:make-module :base-frequency 5.0 :v-peak 5.0)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("VCO" :output-socket :sine :name "Sine"))
     :filename "cl-synthesizer-examples/monitor-csv-file-example-1.csv"
     :add-header t
     :column-separator ",")
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 2)))

;; (run-example)

buffer-agent

cl-synthesizer-monitor-buffer-agent:make-backend (name environment input-socket-settings &rest rest &key buffer &allow-other-keys)

Creates a monitor backend which writes to a memory buffer.

The function has the following parameters:

The function returns a values object consisting of

Example

(defpackage :cl-synthesizer-monitor-buffer-example-1
  (:use :cl))

(in-package :cl-synthesizer-monitor-buffer-example-1)

(defun example ()
  "Buffer-Agent example"
  (let ((rack (cl-synthesizer:make-rack :environment (cl-synthesizer:make-environment))))
    
    (cl-synthesizer:add-module
     rack "VCO" #'cl-synthesizer-modules-vco:make-module :base-frequency 5.0 :v-peak 5.0)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-buffer-agent:make-backend
     '(("VCO" :output-socket :sine)
       ("VCO" :output-socket :saw))
     :buffer (make-array 2))
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer:play-rack rack :duration-seconds 2)))

;; (run-example)

MIDI

MIDI Event

cl-synthesizer-midi-event:make-control-change-event (&key channel controller-number control-value)

Creates a MIDI control change event.

cl-synthesizer-midi-event:make-note-on-event (&key channel note-number velocity)

Creates a MIDI Note-On event.

cl-synthesizer-midi-event:make-note-off-event (&key channel note-number velocity)

Creates a MIDI Note-Off event.

cl-synthesizer-midi-event:control-change-eventp (event)

Returns t if the given MIDI event is a Control-Change event.

cl-synthesizer-midi-event:note-on-eventp (event)

Returns t if the given MIDI event is a Note-On event.

cl-synthesizer-midi-event:note-off-eventp (event)

Returns t if the given MIDI event is a Note-Off event.

cl-synthesizer-midi-event:get-channel (event)

Returns the MIDI channel number to which the event belongs.

cl-synthesizer-midi-event:get-controller-number (event)

Returns the controller number of a Control-Change MIDI event.

cl-synthesizer-midi-event:get-control-value (event)

Returns the control value of a Control-Change MIDI event.

cl-synthesizer-midi-event:get-note-number (event)

Returns the note number of Note-On/Off MIDI event.

cl-synthesizer-midi-event:get-velocity (event)

Returns the velocity of a Note-On/Off MIDI event.

MIDI Polyphonic Interface

cl-synthesizer-modules-midi-polyphonic-interface:make-module (name environment &key (voice-count 1) (channel nil) (cv-gate-on 5.0) (cv-gate-off 0.0) (cv-velocity-max 5.0))

Creates a polyphonic MIDI interface module. The module dispatches MIDI-Note events to so called voices where each voice is represented by a pitch-control voltage, a velocity-control voltage and a gate signal.

The function has the following parameters:

Gate transitions are implemented as follows: Each incoming "note on" event causes that the gate signal of the assigned voice switches to On. If the gate signal of the assigned voice is already On (this happens when the available voices are exhausted and a voice is "stolen") then the gate signal switches to Off for the duration of one system tick and then to On again.

The module has the following inputs:

The module has the following outputs:

MIDI Monophonic Interface

cl-synthesizer-modules-midi-monophonic-interface:make-module (name environment &key (channel nil) (cv-gate-on 5.0) (cv-gate-off 0.0) (force-gate-retrigger nil) (stack-depth 5) (cv-velocity-max 5.0) (force-velocity-update nil))

Creates a monophonic MIDI interface module. The module dispatches MIDI-Note events to a single voice. If the voice is already assigned to a note, then the incoming note is pushed on top of the current note.

The function has the following parameters:

Gate transitions are implemented as follows: Incoming notes are stacked. The first ("initiating") "note on" event causes the gate signal to go up. The gate goes down when after a "note off" event no more notes are on the stack.

The module has the following inputs:

The module has the following outputs:

MIDI Relative CC Interface

cl-synthesizer-modules-midi-relative-cc-interface:make-module (name environment &key mappings (channel nil) (initial-output 0) (min-output nil) (max-output nil))

Creates a MIDI CC Event interface module. CC events are interpreted as relative changes to the current output value.

The function has the following parameters:

The module has the following inputs:

The module has the following outputs:

Example

(defpackage :cl-synthesizer-modules-midi-relative-cc-interface-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-midi-relative-cc-interface-example-1)

(defun example ()
  "MIDI Relative CC-Interface Example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))

    (cl-synthesizer:add-module
     rack "MIDI-CC-IFC" #'cl-synthesizer-modules-midi-relative-cc-interface:make-module
     :mappings '((:controller-number 112 :control-value 61 :offset -1.0)
                 (:controller-number 112 :control-value 67 :offset 1.0))
     :initial-output 2.5
     :channel nil)

    (cl-synthesizer:add-module
     rack "MIDI-SEQUENCER"
     #'cl-synthesizer-modules-midi-sequencer:make-module :events
     (list 
      (list :timestamp-milli-seconds 100
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 61)))
      (list :timestamp-milli-seconds 200
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 61)))
      (list :timestamp-milli-seconds 300
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 61)))
      (list :timestamp-milli-seconds 400
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 61)))
      (list :timestamp-milli-seconds 500
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 67)))
      (list :timestamp-milli-seconds 600
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 67)))
      (list :timestamp-milli-seconds 700
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 67)))
      (list :timestamp-milli-seconds 800
            :midi-events (list
                          (cl-synthesizer-midi-event:make-control-change-event :channel 1 :controller-number 112 :control-value 67)))))
    
    (cl-synthesizer:add-patch rack "MIDI-SEQUENCER" :midi-events "MIDI-CC-IFC" :midi-events)

    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-csv-file-agent:make-backend
     '(("MIDI-CC-IFC" :output-socket :output :name "CC-Out"))
     :filename "cl-synthesizer-examples/midi-relative-cc-interface-example-1.csv")

    rack))

(defun run-example ()
  (cl-synthesizer::play-rack (example) :duration-seconds 0.9))

;; (run-example)

MIDI Sequencer

cl-synthesizer-modules-midi-sequencer:make-module (name environment &key events)

Creates a Midi-Sequencer module.

The function has the following parameters:

The module has no inputs. The module has one output socket :midi-events.

Example

(defpackage :cl-synthesizer-modules-midi-sequencer-example-1
  (:use :cl))

(in-package :cl-synthesizer-modules-midi-sequencer-example-1)

(defun example ()
  "Midi-Sequencer example"
  (let ((rack (cl-synthesizer:make-rack
               :environment (cl-synthesizer:make-environment))))

    ;; Add sequencer
    (cl-synthesizer:add-module
     rack
     "MIDI-SEQUENCER"
     #'cl-synthesizer-modules-midi-sequencer:make-module :events
     (list 
      (list :timestamp-milli-seconds 0
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-on-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))
      (list :timestamp-milli-seconds 1000
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-off-event
                           :channel 1
                           :note-number 69
                           :velocity 100)))
      (list :timestamp-milli-seconds 2000
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-on-event
                           :channel 1
                           :note-number 75
                           :velocity 100)))
      (list :timestamp-milli-seconds 2500
            :midi-events (list
                          (cl-synthesizer-midi-event:make-note-off-event
                           :channel 1
                           :note-number 75
                           :velocity 100)))))

    ;; Add MIDI Interface and connect it with the MIDI Sequencer
    (cl-synthesizer:add-module
     rack
     "MIDI-IFC"
     #'cl-synthesizer-modules-midi-polyphonic-interface:make-module :voice-count 1)
    (cl-synthesizer:add-patch rack "MIDI-SEQUENCER" :midi-events "MIDI-IFC" :midi-events)

    ;; Add VCO
    (cl-synthesizer:add-module
     rack "VCO" #'cl-synthesizer-modules-vco:make-module
     :base-frequency (cl-synthesizer-midi:get-note-number-frequency 0)
     :v-peak 5.0)

    ;; Add ADSR
    (cl-synthesizer:add-module
     rack "ADSR"
     #'cl-synthesizer-modules-adsr:make-module
     :attack-time-ms 100 :attack-target-output 5.0
     :decay-time-ms 50 :decay-target-output 3.0
     :release-time-ms 100)
    
    ;; Add VCA
    (cl-synthesizer:add-module rack "VCA" #'cl-synthesizer-modules-vca:make-module :cv-max 5.0 :exponential nil)

    ;; Connect VCA with ADSR and VCO
    (cl-synthesizer:add-patch rack "ADSR" :cv "VCA" :cv)
    (cl-synthesizer:add-patch rack "VCO" :triangle "VCA" :input)
    
    ;; Connect Midi interface with ADSR and VCO
    (cl-synthesizer:add-patch rack "MIDI-IFC" :cv-1 "VCO" :cv-exp)
    (cl-synthesizer:add-patch rack "MIDI-IFC" :gate-1 "ADSR" :gate)

    ;; Write VCA output to a wave file
    (cl-synthesizer-monitor:add-monitor
     rack
     #'cl-synthesizer-monitor-wave-file-agent:make-backend
     '(("VCA" :output-socket :output))
     :filename "cl-synthesizer-examples/midi-sequencer-example-1.wav"
     :v-peak 5.0)
    
    rack))

(defun run-example ()
  (let ((rack (example)))
    (cl-synthesizer::play-rack rack :duration-seconds 5)))

;; (run-example)

MIDI Utilities

cl-synthesizer-midi:get-note-number-frequency (note-number)

Returns the frequency of a given note number. Note number 69 results in a frequency of 440Hz. This function implements a mapping according to midinote2freq

Note numberFrequency
08.175798
18.661958
29.177024
39.722718
410.300861
510.913383
611.5623255
712.249857
812.9782715
955/4
1014.567618
1115.433853
1216.351597
1317.323915
1418.354048
1519.445436
1620.601723
1721.826765
1823.124651
1924.499714
2025.956543
2155/2
2229.135237
2330.867706
2432.703194
2534.64783
2636.708096
2738.890873
2841.203445
2943.65353
3046.249302
3148.999428
3251.913086
3355
3458.270473
3561.735413
3665.40639
3769.29566
3873.41619
3977.781746
4082.40689
4187.30706
4292.498604
4397.998856
44103.82617
45110
46116.54095
47123.470825
48130.81277
49138.59132
50146.83238
51155.56349
52164.81378
53174.61412
54184.99721
55195.99771
56207.65234
57220
58233.0819
59246.94165
60261.62555
61277.18265
62293.66476
63311.12698
64329.62756
65349.22824
66369.99442
67391.99542
68415.3047
69440
70466.1638
71493.8833
72523.2511
73554.3653
74587.3295
75622.25397
76659.2551
77698.4565
78739.98883
79783.99084
80830.6094
81880
82932.3276
83987.7666
841046.5022
851108.7306
861174.659
871244.5079
881318.5103
891396.913
901479.9777
911567.9817
921661.2188
931760
941864.6552
951975.5332
962093.0044
972217.4612
982349.318
992489.0159
1002637.0205
1012793.826
1022959.9553
1033135.9634
1043322.4375
1053520
1063729.3103
1073951.0664
1084186.009
1094434.9224
1104698.636
1114978.0317
1125274.041
1135587.652
1145919.9106
1156271.927
1166644.875
1177040
1187458.6206
1197902.133
1208372.018
1218869.845
1229397.272
1239956.063
12410548.082
12511175.304
12611839.821
12712543.854

Conditions

cl-synthesizer:assembly-error

This condition is signalled in cases where the assembly of a rack fails, because for example a module name is not unique, a module is malformed, a patch is added for a non-existing module, a patch is added to an already patched socket and so on.

Run tests

(asdf:test-system :cl-synthesizer)

Run examples

Run all examples of cl-synthesizer. Generated files are written to ~/cl-synthesizer-examples/

(asdf:load-system :cl-synthesizer/examples)
(cl-synthesizer-examples::run-examples)

Run profiler

Run the profiling suite. Generated files files are written to ~/cl-synthesizer-profiler/

(asdf:load-system :cl-synthesizer/profiling)
(cl-synthesizer-profiling::run-all)

Generate documentation

Depends on :cl-html-readme and :docparser which are available via Quicklisp.

(asdf:load-system :cl-synthesizer/doc)
(cl-synthesizer-make-doc::make-doc)

Acknowledgements

Envelope generation has been inspired by dhemery