Skip to content

Foreign-Call Controller

Description

Allows any Lisp (or Scheme) program to be used as a controller. To take advantage of this function, you should have some knowledge of the Lisp (or Scheme) programming language. The fact that it can replicate Scheme programs is for backwards-compatibility, since old versions of Modalys used a Scheme interface instead of a Lisp interface.

(make-controller 'foreign-call ... )

Lisp Syntax and Default Values

The 'foreign-call controller can be created using the following Lisp syntax:

(make-controller 'foreign-call dimension period foreign_function (list controller1 controller2 ... ))

Parameters

The 'foreign-call controller takes the following arguments:

  • dimension: number of dimensions for the output controller.
  • period: the time between controller updates (in synthesis seconds).
  • foreign_function: a Lisp or Scheme function representing the expression to be evaluated. see details, below.
  • controller1, controller2, etc...: controllers to "plug-in" to the math expression.

The output controller can be optionally re-sampled using the period argument. A period of 0, as always, indicates the controller will be updated every sample.

The Lisp or Scheme lambda function with no arguments, says how to update the "output latch" from the "input latches". See description, below, for details.

A variable number of controllers may be provided as a list.

Discussion

Here is a practical example of how to use the 'foreign-call controller to multiply two controllers (my-ctl1 and my-ctl2):

(make-controller 'foreign-call 1 0
         (foreign-callable (lambda (input output)
                  (vset output 0 (* (vget input 0 0) (vget input 1 0))) ))
         (list my-ctl1 my-ctl2))
That is equivalent to the (obsolete) 'expression controller:

(make-controller 'expression 1 0 "in(1,1)*in(2,1)" my-ctl1 my-ctl2)
Or the 'arithmetic controller:

(make-controller 'arithmetic 1 "*" my-ctl1 my-ctl2)
Naturally, apart from simply accepting input controllers, any Modalys or Lisp programming function may be called within the lambda function. Here is an example of how the speed of an access can be obtained using the 'foreign-call controller:

(make-controller 'foreign-call 1 0
         (foreign-callable (lambda (ins outs) 
                  (vset outs 0 (get-info 'speed access-ref)) ))
         '())
Obtaining the access speed this way is considerably more computationally expensive than using the 'access-speed controller, however, there may be other esoteric uses of the 'foreign-call controller to obtain seldom-accessed Modalys data.

Although we generally recommend using the 'expression, 'arithmetic or other controllers when and where possible (did we mention that they were a lot faster???!), there may be some advanced or expert techniques which require the 'foreign-call controller to do specific tasks, so the function has been retained in the Modalys environment.

Options

Lambda Function Syntax

It's worth studying the update function a bit (actually you need to know how to program a little bit in Lisp, Scheme or a related programming language in order to really understand this well). The update function must begin like this (of course, "inputs" and "output" could be other variable names like "ins" and "out"):

(foreign-callable (lambda (inputs output) . . . ))
This is then followed by programming statements that are used to change the value in the controller's "output box" by using the function (vset ... ). Therefore, to put the value 31.2 in the first dimension of your output box, you would write:

(vset output 0 31.2)
Notice that the output dimensions are numbered starting from zero. Thus, you could change the second dimension using:

(vset output 1 31.2)
and so forth. Naturally, to know how to update its output, the function must have access to all the values in the input controllers. To get the inputs, you use the (vget ... ) function like this:

(vget inputs 0 0)
This gets the first dimension of the first input - both are numbered starting from 0. So, if you type:

(vget inputs 0 1)
you get the second dimension of the first input, or if you type:

(vget inputs 1 0)
you get the first dimension of the second input, and so on. Therefore:

(vget inputs 18 21)
would get the 22nd dimension of the 19th input. So you can see that the following function:

(foreign-callable (lambda (inputs output) (vset output 0 (* (vget inputs 0 0) (vget inputs 1 0)))))
means that the update rule is: "get the first dimension of the first input controller; then, get the first dimension of the second input controller. Multiply them together, and stick the result in the first dimension of my own output box."

This is a useful general-programming-like way of using a controller, however for most basic math operations you can use the 'arithmetic controller or for more complex expressions, the 'expression controller.

Retro-Compatibility

This controller was formerly called (make-controller 'scheme ... ) and had a slightly different syntax where the number of input controllers had to be explicitly given before the list containing them:

(make-controller 'scheme dimension period foreign_function num_input_ctlrs (list controller1 controller2 ... ))
Additionally, the scheme lambda function syntax was a tiny bit different. The simple example shown in the discussion, above, would have looked like this:

(make-controller 'scheme 1 0
         (lambda ()
                  (vset  0 (* (vget  0 0) (vget  1 0))) )
         2 (list my-ctl1 my-ctl2))
Any very old Modalys scripts which still use scheme controllers will need to be updated to use the 'foreign-call instead.