Welcome to Gwion

gwion is a strongly timed programming language for making music.

It is strongly influenced by chuck, but adds a bunch of high-level features: templating, first-class functions and more.

It aims to be simple, small, fast, extendable and embeddable.

gwion was originally designed and created by Jérémie Astor. He is a musician, who made it to assist in his shows/compositions.

We are currently looking for contributions, you can learn how to make some here.

And now for the hello world

Here is the piece of code you're waiting for:

<<< "Hello, World!" >>>;

(Bag of) Features

Try me

There's a gwion repl in the wild. You can try the language there

built with mdBook
You'll find the source here, Luke!

Overview

This is a basic tutorial that will lead you through the basics of Gwion.

built with mdBook
You'll find the source here, Luke!

First Steps

This guide will help you install Gwion.

built with mdBook
You'll find the source here, Luke!

Installing gwion

Get the sources

The source is accessible on github.

Provided you have git installed, you can get it with:

git clone --recursive https://github.com/Gwion/Gwion

then change to the source directory

cd Gwion

At this point, you may want to configure the build. In this case, have a look at the configuration page.

Build the libraries and program

make

Install the package

make install

You may need root privilege to do this.

This is not strictly necessary, as you can run the program by typing ./gwion in the gwion directory. It is a good idea to install it, however, so that it can be run in any folder on the system.

built with mdBook
You'll find the source here, Luke!

Configuring Gwion

Gwion can be configured to better suit the user's needs. This is mainly done in two files: config.mk and util/config.mk.

config.mk

  • DEBUG_STACK ?= 0
    • Enables the debug stack, which prints some stack info, thus verifying whether a function is balanced.

util/config.mk

  • USE_DOUBLE ?= 0
    • Sets the size of float to double
  • USE_GETTEXT ?= 0
    • Enable internationalization (almost complete)
  • USE_DEBUG ?= 1
    • Enables debug features
  • USE_LTO ?= 0
    • Enables Link-Time Optimization
  • USE_COVERAGE ?= 0
    • Enables code coverage
built with mdBook
You'll find the source here, Luke!

Declarations

Basics

Declaring a primitive or an object is quite straight forward:

var int i;
var Object o;
<<< i, " ", o >>>;

Declaring a reference

late Object object_ref;
<<< "Reference points to no object yet: ", object_ref >>>;
new Object => object_ref;
<<< "But now it does: ", object_ref >>>;

Arrays

array as refs

var int array_ref[];
<<< array_ref >>>;
new int[2] => array_ref;
<<< array_ref >>>;
built with mdBook
You'll find the source here, Luke!

Global Values

  • adc
  • blackhole
  • dac
  • pi
  • null
  • samp
  • ms
  • second
  • hour
  • me
  • this
  • _func_
  • _file_
  • _line_
built with mdBook
You'll find the source here, Luke!

Keywords

  • fun

  • operator

  • return

  • goto

  • switch/case/default

  • if/else

  • break/continue

  • until/do/while/for/repeat

  • global/static

  • private/protect

  • const

  • new

  • spork

  • fork

  • typeof

  • typedef

  • funcdef

  • class

    • dtor
    • extends
  • enum

  • union

  • auto

built with mdBook
You'll find the source here, Luke!

Special Values

  • me
  • this
  • vararg
  • maybe
built with mdBook
You'll find the source here, Luke!

Tests

test.sh requires valgrind there are two kinds of tests:

Gwion tests

those tests are just gwion (.gw) files, handling special comments:

  • #! [skip] (optionally followed by reason to skip)
  • #! [todo] (optionally followed by reason to delay testing)
  • #! [contains] followed by string to match
  • #! [excludes] followed by string not to match

Shell test

those tests are just bash (.sh) files.
they should start with this snippet

#!/bin/bash
# [test] #5
n=0
[ "$1" ] && n="$1"
[ "$n" -eq 0 ] && n=1
source tests/sh/common.sh
built with mdBook
You'll find the source here, Luke!

Reference

built with mdBook
You'll find the source here, Luke!

Control Flow

built with mdBook
You'll find the source here, Luke!

For Loops

For loops in Gwion is pretty similar to classic C syntax

basic loops

for(var int i; i < 3; ++i)
   <<< i >>>;

It also works with a block of code.

for(var int i; i < 3; ++i) {
   i/2 => var float f1;
   i/2. => var float f2;
   <<< i, " " , f1, " ", f2 >>>;
}

Nested Loops

var int array[3][4];

for(var int i; i < 3; ++i) {
  for(var int j; j < 4; ++j) {
    <<< array[i][j] >>>;
  }
}

Auto Loops

Simple auto loop

var int array[2][3];
foreach(a: array) {
  <<< a >>>;
  foreach(b: a)
    <<< b >>>;
}

Auto Pointer loop

If you want to change it the value in the array, you need a pointer

var int array[2][3];
var int i;
foreach(a: array) {
  foreach(b: a)
    <<< ++i => b >>>;
}
foreach(a: array) {
  foreach(b: a)
    <<< b >>>;
}
built with mdBook
You'll find the source here, Luke!

the Repeat keyword

let start simple ;-)
The easiest way to do an action repeatidly in Gwion is, ... the repeat keyword!

Basic example

repeat(3)
   <<< "Hello, world!" >>>;

Block example

of course this also works with a block code.

repeat(3) {
   maybe ? "You" : "Me" => var string s;
   <<< "Hello, ", s, "!" >>>;
}
built with mdBook
You'll find the source here, Luke!

While Loops

while(true) {
  if(maybe)
    break;
  <<< "running..." >>>;
}

well this may output nothing... lets try

<<< maybe >>>;
do{
  if(maybe)
    break;
  <<< "running..." >>>;
} while(true);
built with mdBook
You'll find the source here, Luke!

Extending

built with mdBook
You'll find the source here, Luke!

Giving gwion a new driver

basics

in order to use GWION_CTL ...

concept

upd driver

built with mdBook
You'll find the source here, Luke!

Writing a Gwion plugin

THIS IS OUTDATED. please look at the source code in src/lib/ instead

  • [getting started]

Getting started

use the script

headers

#include "vm.h"
#include "instr.h"
#include "import.h

Class

Define the type:

struct Type_ t_mytype = { "MyType", SZ_INT, &t_object};

every type extending t_object should have SZ_INT

Handling Constructors and Destructors

CTOR

CTOR(mytype_ctor) {
  /* constructor code here */
}

DTOR

DTOR(mytype_dtor) {
  /* destructor code here */
}

those macros provide two variables:

  • o: the M_Object for the (con/des)tructor
  • shred: the VM_Shred for the (con/des)tructor
CHECK_BB(import_class_begin(env, &t_mytpe, env->global_nspc, mytype_ctor, mytype_dtor))

variable

declare a m_int. coding convention require

  • a leading _o
  • a following type
m_int o_mytype_myvaroffset;

function

/* declare a member function */
MFUN(mytype_memberfunction) {
  /* code here */
}

SFUN(mtype_staticfunction) {
  /* code here */
}

operator

Import function

built with mdBook
You'll find the source here, Luke!

Functions

a simple (commented example)

#! declare function 'test_function'
#! with return type int
#! taking an int as argument
fun int test_function(int arg) {
  #! return the argument + 2
  return arg + 2;
}

#! now call the function (and debug print the result)
<<< test_function(0) >>>;
#! or use alternate syntax
<<< 1 => test_function >>>;
built with mdBook
You'll find the source here, Luke!

Lambda

Overview

lambdas are anonymous functions.

The syntax to create them is simple:

\ variable0 variable1 ... {  your code here }

You can even use it to

Call a function just once

\ i { <<< "passed '", i, "'" >>>; }(3);

Use case

Passing to a function pointer

funcdef void fptr_t(int);
\ i { <<< "passed '", i, "'" >>>; } => var fptr_t fptr;
fptr(4);

As Argument to Functions

funcdef void fptr_t(int);
fun void test(fptr_t fptr) {
   fptr(5);
}
test(\ i { <<< "passed '", i, "'" >>>; });
built with mdBook
You'll find the source here, Luke!

Variadic functions

A function whoses arity is not fixed.

Well, a function that takes a fixed number of arguments, and additionnal ones.

a simple example

fun void variadic_test(int i, ...) {
  <<<  "first argument is ", i  >>>;
  varloop vararg {
    <<<  "\tadditionnal argument", vararg $ int  >>>;
  }
}
variadic_test(1);
variadic_test(1, 2);
variadic_test(1, 2, 3);
built with mdBook
You'll find the source here, Luke!

Types

built with mdBook
You'll find the source here, Luke!

Enums

For those who don't know about enumerated types, you can read about those here and here

Enums in gwion

enums require a name and at least one member.

You use them like this:

enum MyEnum {
  zero, one, two
};
<<< "${MyEnum.zero} ${MyEnum.one} ${MyEnum.two} >>>;

Storage and access Specifiers

When inside a class, enums accept private or protect specifiers.

built with mdBook
You'll find the source here, Luke!

Function Pointers

built with mdBook
You'll find the source here, Luke!

Primitive types

built with mdBook
You'll find the source here, Luke!

Type aliases

Create an alias for a previously defined type.

typedef int MyInt;
var MyInt i;
<<< i >>>;
<<< i $ int >>>;

Aliases can also point to an array type

typedef float[3] Point;
var Point p;
foreach(a : p)
  <<< a >>>;

Aliases can be used to refer to a type family

typedef Ptr:[int] IntPtr;
var IntPtr int_ptr;
<<< int_ptr >>>;
built with mdBook
You'll find the source here, Luke!

Union types

Union store their component in the same memory space. For more infomations, see here.

union U {
  int a;
  float f;
  Object o;
};
built with mdBook
You'll find the source here, Luke!

Gwion Preprocessor

Memoization

You can use the memoize pragma to enable memoization on functions:

normal version

fib_recurs.gw

fun int recursive_fib(int n) {
    if (n < 2)
        return n;
    return recursive_fib(n - 2) + recursive_fib(n - 1);
}
<<< 40 => recursive_fib >>>;

memoized version

The syntax of the memoize pragma is as follow:

#pragma memoization <number of results to store>

See the memoized version of previous function:

fib_recurs_memoize.gw

fun int recursive_fib(int n) {
#pragma memoize 2
    if (n < 2)
        return n;
    return recursive_fib(n - 2) + recursive_fib(n - 1);
}
<<< 40 => recursive_fib >>>;

Under circomstance where memoization is applicable, such as this one, you can see a huge speed-up.

normal:

memoized:

Memoization setting will be active until the end of file or until it is changed. Therefore, if you want to disable memoization for subsequent functions, use:

#pragma memoize 0
built with mdBook
You'll find the source here, Luke!

Contributing

built with mdBook
You'll find the source here, Luke!

Contributing Code

this is a stub for now, this place deserves better

You might be here because you found something you could improve in Gwion. Please open an issue describing the problem and how you might address it.

Dependencies

  • C compiler supporting C99 (ex- gcc, clang)
  • GNU Make

Fork the repository

You can do this through the github site, or command-line tools like gh or hub. See Further Reading.

Clone the source

Clone the url of your fork

git clone --recursive https://github.com/<your username>/Gwion

Set up the project

Edit some files

Edit some files with your favorite text editor

cd Gwion
vim src/path/to/file

Build the project

make

Test your changes

make test

Rebuild project (if required)

make -C util clean && make -C ast clean && make clean
make

Add your changes

git add path/to/changed/files

Let the world know

git commit -m "Something meaningful about why we are here"
git push

It is recommended that you follow our styleguide for commit messages

Submit a pull request

You can do this through the github site, or command-line tools like gh or hub. See Further Reading.

It is recommended you submit a PR to a branch other than master;

  • If a branch that directly concerns your specific PR is readily available, PR to that
  • Otherwise PR to dev

You can now sit back and wait for your pull request to be reviewed. If it's accepted, congrats! 🎉 You've made a contribution!

Further reading

For a more advanced info on contributing, check out @Chaser324's GitHub Standard Fork & Pull Request Workflow.

For a guide on making a pull request, there's github's Creating a pull request from a fork.

built with mdBook
You'll find the source here, Luke!

Contributing Documentation

this is a stub for now, this place deserves better

You might be here because you found something you could improve in Gwion's documentation. Please open an issue describing the problem and how you might address it.

Dependencies

Technically none of these are needed, but they are quite useful:

Editing the documentation

Fork the repository

You can do this through the github site, or command-line tools like gh or hub. See Further Reading.

Clone the source

Clone the url of your fork

git clone https://github.com/<your username>/gwion-docs

Edit some files

cd gwion-docs
make watch

Add your changes

git add docs/myfile.mdr

Let the world know

git commit -m "Something meaningful about why we are here"
git push

Submit a pull request

You can do this through the github site, or command-line tools like gh or hub. See Further Reading.

You can now sit back and wait for your pull request to be reviewed. If it's accepted, congrats! 🎉 You've made a contribution!

Further reading

For a more advanced info on contributing, check out @Chaser324's GitHub Standard Fork & Pull Request Workflow.

For a guide on making a pull request, there's github's Creating a pull request from a fork.

built with mdBook
You'll find the source here, Luke!

Contributing translations

First off, thank you for considering translating gwion.

Thanks to the build system, you're gonna get on tracks fast.

Make your live easier use export TRANSLATION_TARGET=xxx so you don't have to repeat it on the command line

Step 1: Init the translation language

You have to make sure there is a directory for your target language (e.g.: fr, en, es_ES ...).

make translation-init TRANSLATION_TARGET=<xxx>

Where <xxx> is your language of choice. It will fail if your language already exists, but this is not a problem.

Step 2: Edit

Next, you should adjust the translations. What about using your favorite editor?

make translation-edit TRANSLATION_TARGET=<xxx>

Step 3: Update

Maybe the sources changed, and there is more messages to translate?

make translation-update

This will update all languages.
You can now get back to step 2.

Step 4: Add to VCS

It's now time to add your changes to the package

make translation-commit TRANSLATION_TARGET=<xxx>

In Gwion's case, the vcs is the well known git,
but the make recipe makes things simple

Now please submit a pull request.

built with mdBook
You'll find the source here, Luke!

Contributing Plugins

This is a step by step hands on tutorial that will quide you trought the steps of making a Gwion plugin

built with mdBook
You'll find the source here, Luke!

Introduction

Plugins are an important aspect of Gwion's flexibility and extensibility. They allow you to customize behavior as well as develop novel functionality within Gwion itself.

Much of Gwion's features come from plugins. Audio generation, file I/O, image editing and more.

What is a Plugin?

Plugins are shared object files typically created from C source code. They consist of a few core functions required for the Gwion runtime to properly initialize and use it.

Simple Plugin

Let's create a simple plugin which provides a single function: add. This will take two ints and return their sum as an int.

First, open up your terminal and run the following shell commands within Gwion/plug:

# Create a new directory for our Adder plugin
mkdir Adder

# Navigate to the newly created directory
cd Adder

# Create a makefile for compilation
printf "include ../config.mk\ninclude ../config_post.mk\n" > Makefile

# Create an adder.c file with your favorite text editor
nano adder.c

We are greeted with any empty file. However, it won't be empty for long.

Let's add the following code to the top of our file:

#include "plugin_dev.h"

This will include all the necessary functions and macros to develop a plugin without much head scratching.

Next, we need the GWION_IMPORT function. This is extremely important since it is what the Gwion runtime uses to set up your plugin. GWION_IMPORT is a macro which takes the name of your plugin: in this case that is "Adder". Underneath the include let's add:

GWION_IMPORT(Adder) {
    // Init code here
}

Within this function we register the public API of our plugin to Gwion. In our case, we want an Adder class with a single static function add that takes two numbers and returns their sum.

Our first step is to create the Adder class. This is done with:

GWION_IMPORT(Adder) {
    // Begin our adder class
    DECL_OB(const Type, t_adder, = gwi_class_ini(gwi, "Adder", "Object"));
}

Now, whenever we ini a class, we must also end it:

GWION_IMPORT(Adder) {
    // Begin our adder class
    DECL_OB(const Type, t_adder, = gwi_class_ini(gwi, "Adder", "Object"));

    // End our adder class
    GWI_BB(gwi_class_end(gwi));
}

We also want to make sure to indicate that everything has gone OK. We do this by returning GW_OK:

GWION_IMPORT(Adder) {
    // Begin our adder class
    DECL_OB(const Type, t_adder, = gwi_class_ini(gwi, "Adder", "Object"));

    // End our adder class
    GWI_BB(gwi_class_end(gwi));

    return GW_OK;
}

Right now our code doesn't really do much; it creates an empty class and then exits. Let's expand this code to add a static function inside the class:

GWION_IMPORT(Adder) {
    // Begin our adder class
    DECL_OB(const Type, t_adder, = gwi_class_ini(gwi, "Adder", "Object"));

    // Create a new function named `add` with a return type of `int`
    // gwi_func_ini(gwi, return_type, name);
    GWI_BB(gwi_func_ini(gwi, "int", "add"));

    // Register our two args `a` and `b` of type `int`
    // gwi_func_arg(gwi, arg_type, name);
    GWI_BB(gwi_func_arg(gwi, "int", "a"));
    GWI_BB(gwi_func_arg(gwi, "int", "b"));

    // Mark the function as completely declared
    GWI_BB(gwi_func_end(gwi, adder_add, ae_flag_static));

    // End our adder class
    GWI_BB(gwi_class_end(gwi));

    return GW_OK;
}

Looks like we are good to go! Let's compile...and:

adder.c: In function 'import':
adder.c:22:28: error: 'adder_add' undeclared (first use in this function)
   22 |   GWI_BB(gwi_func_end(gwi, adder_add, ae_flag_static));
      |                            ^~~~~~~~~

When we marked the function as completely declared with gwi_func_end, we gave the function implementation adder_add as an argument. This tells Gwion that our Adder.add function in Gwion corresponds to the C function adder_add. However we haven't actually defined it! Let's do that. Above our GWION_IMPORT function, add:

// The `SFUN` macro defines a static function
// The `static` here is actually a C keyword, unrelated to Gwion
static SFUN(sfun) {
    // Function body
}

Let's recall what our Adder.add function does. It takes two int parameters and returns an int. Gwion provides facilities for doing this:

// The `SFUN` macro defines a static function
// The `static` here is actually a C keyword, unrelated to Gwion
static SFUN(sfun) {
    // Retrieve the arguments
    // `a` is in memory at offset 0
    const m_int a = *(m_int*)MEM(0);
    // `b` is in memory at offset `SZ_INT`
    // This is because `a` has size `SZ_INT` and `b` is after `a`
    const m_int b = *(m_int*)MEM(SZ_INT);

    // We now set the return value, given as a void pointer with the `RETURN` macro
    // We need to cast it to the pointer type we want to return and then assign the return value
    *(m_int*)RETURN = a + b;
}

Finally, we can test our plugin. After running make (which creates Adder.so) in the directory, create a new file called add.gw and insert the following code:

#! We import our newly created plugin
#require Adder

#! Let's call our add function
<<< Adder.add(1, 2) >>>;

If all goes well, running the following shell command should result in "3" being printed:

gwion -p. add.gw
built with mdBook
You'll find the source here, Luke!

Plugin Reference

There are some general macros essential to interfacing with Gwion in general.

The return codes of functions are of type m_bool and consist of the following values:

NameValueDescription
GW_OK1Success
GW_PASS0Ignore the result and continue
GW_ERROR-1An error occured

The following macros are sugar for handling error codes. They can exit the scope of the function. It is recommended to use these rather than writing:

if (!a) return NULL;

and co.

NameValueDescription
CHECK_BBif (f < 0) return GW_ERROR;Takes an expression that evaluates to m_bool. Potentially exits function returning GW_ERROR.
CHECK_BOif (f < 0) return NULL;Takes an expression that evaluates to m_bool. Potentially exits function returning NULL.
CHECK_OBif (!f) return GW_ERROR;Takes an expression that evaluates to a pointer. Potentially exits function returning GW_ERROR.
CHECK_OOif (!f) return NULL;Takes an expression that evaluates to a pointer. Potentially exits function returning NULL.

Likewise there are the DECL_XX macros which specialize to failing if a declaration fails. Their general syntax is as follows:

DECL_XX(type, var_name, = value);

These macros are specific to creating plugins.

MFUN(name) signature for a member function SFUN(name) signature for a static function CTOR(name) signature for a constructor DTOR(name) signature for a destructor GACK(name) signature for a pretty print function TICK(name) signature for a UGen tick function GWION_IMPORT(name) main function of a plugin/library

in said main function, one should use GWI_XX instead of CHECK_XX since it takes care of position and might optimize some checks away (in gwion's internals)

OP_CHECK(name) define a type checking function. should return correct type in success error type on failure NULL to continue type checking

OP_EMIT(name) define an emitter function. returns an m_bool

built with mdBook
You'll find the source here, Luke!

Credits

fennecdjay was of immeasurable help when I was writing these docs. Thank you to them for sitting down and working me through Gwion plugins from the basics.

built with mdBook
You'll find the source here, Luke!

Contributors


Paul Batchelor

💬 🐛 💻 🤔

Benny

💬 🐛 💻

Andrew Prentice

💬 🐛 💻 🤔

aleserche

💻

Pranav Joglekar

📓 📖

Amber

💬 📖 🤔

Chase

💻

Nithin Pankaj

💻

Enrico Gruner

💻

Muhammad Umer Farooq

💻

Harsh Jain

📖

ry-v1

📖

deekts

💻

Forrest Cahoon

🤔

Connor Hewett

💻

Ethan Uppal

🤔
built with mdBook
You'll find the source here, Luke!

Benchmarks

Author: Jérémie Astor Date: 01/09/22 18:29:17 Commit: f48a3b4

Results

Please note that times are in second (lower is better)

it seems that some chuck test are broken atm. we're investigating this

built with mdBook
You'll find the source here, Luke!