Why

The final goal is (or may be) doing something at a very high level, possibly live, typing code inside a REPL or reloading a file.

There are already many bindings for OpenFramework in high level languages, at least two: one in Python and one in Lua. But they are both based on SWIG and merely expose the OF primitives to an other language, so you have basically two drawbacks:

  • dealing with the same (quite low level) constructs: lines, polygons, etc…
  • performances of Lua or Python

Embedding

Most of the scripting languages can interact with something at a lower level, typically written in C or C++.

Three are two main ways to do this: extending and embedding. Extending consists in write and compile an external library with all your functions, and than import it as a module from your script.

With embedding, instead, your application itself became the interpreter and you can expose functions as builtins.

From our point of view should be nice keep the structure of a standard OpenFramewors, define some high level constructs and then call them from a live environment, so the best choice can be embedding.

Lisp

But both Python and Lua can be embedded, in particular Lua is very suited for that, so why using an ancient and strange-looking language like Scheme?

I don’t want to enter in details here, but Scheme, being part of the Lisp family, permits to easily reprogram its own syntax and so create Domain Specific Languages to any kind of application.

One of the most powerful environments for live-coding, Fluxus (now unmaintained) is itself a Scheme DSL build on top of a C++ backend.

Guile

Scheme is ad ancient language, there are a lot of specification, and a lot of different interpreters, few of them maintained. One of them, Guile, is actively developed by the GNU Project (the guys behind gcc, Emacs, GIMP, gdb, a large part of the GNU/Linux OS and many other things) and explicitly designed to be embedded inside applications.

Start embedding

I am starting with Guile in this days, and a tutorial I found useful to start embedding is the Tutorial Introduction to Guile. The main concepts can be reduced to the following snippet of code:

#include <stdio.h>
#include <libguile.h>

SCM hello_world() {
  printf("Hello World!\n");
  return SCM_UNSPECIFIED;
}

void* register_functions(void* data) {
  scm_c_define_gsubr("hello-world", 0, 0, 0, &hello_world);
  return NULL;
}

int main(int argc, char* argv[]) {
  scm_with_guile(&register_functions, NULL);
  scm_shell(0, NULL); 
  return EXIT_SUCCESS;
}

We are defining an hello_world function which take non arguments and returns an “unspecified” SCM value. Then we expose it with scm_c_define_gsubr, binding it to the `“hello-world” name. At the end we run a Scheme shell.

After compiling it with:

gcc $(guile-config compile) $(guile-config link) hello.c -o hello 

(you have to have Guile installed) You can run the program an call the hello-world function.

> (hello-world)
Hello World!

You can notice the unusual way to call functions, in Lisp most of the syntax (various brackets, commas, colons, semicolons…) are replaced with simple parentheses. So function call like f(a, b) became (f a b).

Type (quit) to exit

Readline

GNU Readline Library is a library used in interpreters and shells, such as Python or Bash, to do autocompletion with TAB, or scrolling the history using arrows. To enable it put this lines in the main:

scm_c_eval_string("(use-modules (ice-9 readline))");
scm_c_eval_string("(activate-readline)");

OpenFrameworks

Let’s try now to embed Guile in a OpenFrameworks app. First of all if you build with Make you have to uncomment and set in the config.make file:

PROJECT_LDFLAGS = `guile-config link`

and:

PROJECT_CFLAGS = `guile-config compile`

If you use an other build system or some IDE like Codeblocks, XCode or Visual Studio I’m sorry but you have to find by yourself how to set this variables (and maybe write it in the comments so I can update this post).

Now we can add in ofApp.h:

#include <libguile.h>

And in ofApp.cpp:

SCM hello_world() {
  printf("Hello World!\n");
  return SCM_UNSPECIFIED;
}

void* register_functions(void* data){
  scm_c_define_gsubr("hello-world", 0, 0, 0, (scm_t_subr)(&hello_world));
  return NULL;
}

void ofApp::setup(){
  scm_with_guile(&register_functions, NULL);

  scm_c_eval_string("(use-modules (ice-9 readline))");
  scm_c_eval_string("(activate-readline)");

  scm_shell(0, NULL); 
}

In scm_c_define_gsubr we have to explicitly cast the type of &hello_world (the address of the memory where the hello_world machine code is actually stored) to scm_t_subr to avoid problems with the C++ compiler.

Now we can compile and run it, we can call the hello-worls but it doesn’t display any window. This because the scm_shell function runs its own main loop, blocking OpenFramework to start. We have to run it in a different thread.

Threading

Threads are a way to run instructions “in parallel”, here there is the OF documentation explanation of the concept with some code.

To use them in our app we ave to define a new class in ofApp.h and create a instance of it in our ofApp:

class ReplThread : public ofThread{
  public:
    void threadedFunction();
};

class ofApp : public ofBaseApp{
  private:
    ReplThread replThread;

  public:
    void setup();
    ...
};

In ofApp.cpp we move the code from setup to the threadedFunction method of our new class, and in setup start the thread.

void ReplThread::threadedFunction(){
  scm_with_guile(&register_functions, NULL);

  scm_c_eval_string("(use-modules (ice-9 readline))");
  scm_c_eval_string("(activate-readline)");

  scm_shell(0, NULL);
}

void ofApp::setup(){
  replThread.startThread();
}

Something graphical

Now we can put away the hello_world and do something a bit more interesting, like setting the background.

SCM background(SCM r, SCM g, SCM b){
  ofBackground(255 * scm_to_double(r),
               255 * scm_to_double(g),
               255 * scm_to_double(b));
  return SCM_UNSPECIFIED;
}

void* register_functions(void* data){
  scm_c_define_gsubr("background",  3, 0, 0, (scm_t_subr)(&background));
  return NULL;
}

The function scm_to_double cast the SCM variables to actual numbers and the second argument of scm_c_define_gsubr is the number of required arguments, in this case 3.

Multithreading problem

I you try to go further you will immediately notice something: you can not draw anything form the REPL thread. For example exposing a circle function:

SCM circle(SCM x, SCM y, SCM r){
  ofFill();
  ofSetCircleResolution(100);
  ofDrawCircle(scm_to_double(x),
               scm_to_double(y),
               scm_to_double(r));
  return SCM_UNSPECIFIED;
}
scm_c_define_gsubr("circle", 3, 0, 0, (scm_t_subr)(&circle));

And calling it, nothing appears on the screen.

Probably there is a way to fix it, but there is also a simple workaround: we can avoid completely the need of a second thread. Now we are writing commands in a “live shell” but we can also write a sketch file and ask OF to reload it every time is changed.

The easiest (but inelegant) solution is to put a sketch.scm file in the bin/ folder and reload it each frame:

void ofApp::draw(){
  scm_c_eval_string("(load \"sketch.scm\")");
}

Sample implementation

Here an example with some primitives exposed (the .h can be the default one unchanged):

#include "ofApp.h"
#include <libguile.h>

SCM background(SCM r, SCM g, SCM b){
  ofBackground(255 * scm_to_double(r),
               255 * scm_to_double(g),
               255 * scm_to_double(b));
  return SCM_UNSPECIFIED;
}

SCM set_color(SCM r, SCM g, SCM b){
  ofSetColor(255 * scm_to_double(r),
             255 * scm_to_double(g),
             255 * scm_to_double(b));
  return SCM_UNSPECIFIED;
}

SCM circle(SCM x, SCM y, SCM r){
  ofFill();
  ofSetCircleResolution(100);
  ofDrawCircle(scm_to_double(x),
               scm_to_double(y),
               scm_to_double(r));
  return SCM_UNSPECIFIED;
}

void* register_functions(void* data){
  scm_c_define_gsubr("background", 3, 0, 0, (scm_t_subr)(&background));
  scm_c_define_gsubr("set-color",  3, 0, 0, (scm_t_subr)(&set_color));
  scm_c_define_gsubr("circle",     3, 0, 0, (scm_t_subr)(&circle));
  return NULL;
}

void ofApp::setup(){
  scm_with_guile(&register_functions, NULL);

  // Load and start gnu-readline, library for history and autocompletion
  scm_c_eval_string("(use-modules (ice-9 readline))");
  scm_c_eval_string("(activate-readline)");
}

void ofApp::draw(){
  scm_c_eval_string("(load \"sketch.scm\")");
}

And a simple sketch drawing a black circle on a blue background:

(background 0 0.4 1)
(set-color 0 0 0)
(circle 40 40 20)

What’s next

I have no idea of where we can go exploring and combining this instruments.

My current plan is to start working with my artist collective on a some sort of collaborative DSL, but the project is not even defined yet and now I am just playing with the technology and sharing the know-how. We are open to any kind of suggestion or contribution, so feel free to share feedback, ideas or code.

Happy hacking.