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
- single inheritance
- typedef (function pointers and type aliases)
- enums
- tagged unions
- templates (both class and functions)
- easy concurrency/async
- lambdas
- memoization
- variadic functions
Try me
There's a gwion repl in the wild. You can try the language there
Overview
This is a basic tutorial that will lead you through the basics of Gwion.
First Steps
This guide will help you install Gwion.
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 thegwion
directory. It is a good idea to install it, however, so that it can be run in any folder on the system.
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
Gwion On Windows
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
Setting up
Make sure that you have the following programs installed, and that they are in your path :
- touch
- a C compiler, in this example we will use gcc
First, we need to define some enviromment variables
set CC=gcc
set BUILD_ON_WINDOWS=1
Next we need to create some directories
mkdir .d
cd util
mkdir .d
cd ../ast
mkdir .d
cd ..
now, look for something like ast/libprettyerr/libprettyerr.a:
, and replace the relevant command with:
@+set CFLAGS_=${CFLAGS} && set CFLAGS=-I<your current dir>/util/libtermcolor/include && ${MAKE} -s -C ast/libprettyerr static && set CFLAGS=CFLAGS_
where <your current dir> is the absolute path to the current working directory
and finally run
make
Declarations
Basics
Declaring a primitive or an object is quite straight forward:
var int i;
var Object o;
<<< i, " ", o >>>;
Declaring a reference
sometimes you just want an object to be instantiated later.
This is done using the late
keyword
late Object object_ref;
if(object_ref)
<<< "We have an object: ", object_ref >>>;
else
<<< "We have no object" >>>;
trying to access, print or pass an non instantiated object will perform NullPtrException
late Object object_ref;
<<< object_ref >>>;
Arrays
array as refs
var int array_ref[];
new int[2] :=> array_ref;
<<< array_ref >>>;
Global Values
- adc
- blackhole
- dac
- pi
- null
- samp
- ms
- second
- hour
- me
- this
- _func_
- _file_
- _line_
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
-
funptr
-
class
- dtor
- extends
-
enum
-
union
-
auto
Special Values
- me
- this
- vararg
- maybe
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
Core Reference
compiler generated documentation
#!+ [builtin]
#!- one type to rule them all.
primitive Class;
#!- this type is infered.
primitive auto;
#!- a void type.
primitive void;
#!- integer type.
primitive int;
#!- character type.
primitive char extends int;
#!- float type.
primitive float;
#!- represent duration.
primitive dur;
#!- represent time.
primitive time;
#!- internal time for `now`.
primitive Now extends time;
specialid const now;
#!- internal predicate representation.
specialid const @predicate;
#!- internal base of all objects and structures.
primitive enum extends int;
primitive Compound;
primitive Object extends Compound;
specialid const this;
specialid const super;
primitive bool extends int;
const bool true;
const bool false;
operator bool ! (int);
operator @implicit (bool, float);
specialid const maybe;
operator bool @conditional (int);
operator bool @unconditional (int);
operator int + (int, int);
operator int - (int, int);
operator int * (int, int);
operator int / (int, int);
operator int (int, int);
operator int > (int, int);
operator int >= (int, int);
operator int < (int, int);
operator int <= (int, int);
operator int >> (int, int);
operator int << (int, int);
operator int & (int, int);
operator int | (int, int);
operator int ^ (int, int);
operator bool && (int, int);
operator bool || (int, int);
operator bool == (int, int);
operator bool != (int, int);
operator int :=> (int, int);
operator int +=> (int, int);
operator int -=> (int, int);
operator int *=> (int, int);
operator int /=> (int, int);
operator int %=> (int, int);
operator int <<=> (int, int);
operator int >>=> (int, int);
operator int &=> (int, int);
operator int |=> (int, int);
operator int ^=> (int, int);
operator int - (int);
operator int ++ (int);
operator int -- (int);
operator int ~ (int);
operator int [:] ();
operator int int ++ ();
operator int int -- ();
primitive u8 1;
primitive u16 2;
primitive u32 4;
primitive u64 8;
operator bool @conditional (float);
operator bool @unconditional (float);
operator float + (float, float);
operator float - (float, float);
operator float * (float, float);
operator float / (float, float);
operator float @implicit (float, float);
operator float :=> (float, float);
operator float +=> (float, float);
operator float -=> (float, float);
operator float *=> (float, float);
operator float /=> (float, float);
operator bool && (float, float);
operator bool || (float, float);
operator bool == (float, float);
operator bool != (float, float);
operator bool > (float, float);
operator bool >= (float, float);
operator bool < (float, float);
operator bool <= (float, float);
operator float - (float);
operator dur :: (int, dur);
operator dur :: (float, dur);
operator bool ! (float);
operator int > (int, float);
operator int >= (int, float);
operator int < (int, float);
operator int <= (int, float);
operator float + (int, float);
operator float * (int, float);
operator float - (int, float);
operator float / (int, float);
operator float :=> (int, float);
operator float +=> (int, float);
operator float -=> (int, float);
operator float *=> (int, float);
operator float /=> (int, float);
operator float $ (int, float);
operator float @implicit (int, float);
operator bool && (int, float);
operator bool || (int, float);
operator bool == (int, float);
operator bool != (int, float);
operator float + (float, int);
operator float - (float, int);
operator float * (float, int);
operator float / (float, int);
operator float :=> (float, int);
operator float +=> (float, int);
operator float -=> (float, int);
operator float *=> (float, int);
operator float /=> (float, int);
operator float $ (float, int);
operator bool && (float, int);
operator bool || (float, int);
operator bool == (float, int);
operator bool != (float, int);
operator bool > (float, int);
operator bool >= (float, int);
operator bool < (float, int);
operator bool <= (float, int);
operator bool @conditional (dur);
operator bool @unconditional (dur);
operator dur :=> (dur, dur);
operator dur +=> (dur, dur);
operator dur -=> (dur, dur);
operator dur *=> (dur, dur);
operator dur /=> (dur, dur);
operator dur + (dur, dur);
operator dur - (dur, dur);
operator dur * (dur, dur);
operator float / (dur, dur);
operator dur / (dur, float);
operator dur *=> (float, dur);
operator dur /=> (float, dur);
operator bool == (dur, dur);
operator bool != (dur, dur);
operator bool > (dur, dur);
operator bool >= (dur, dur);
operator bool < (dur, dur);
operator bool <= (dur, dur);
operator bool @conditional (time);
operator bool @unconditional (time);
operator time :=> (time, time);
operator time + (time, dur);
operator time * (time, dur);
operator time / (time, dur);
operator dur - (time, time);
operator time *=> (float, time);
operator time /=> (float, time);
operator time :=> (dur, time);
operator time + (dur, time);
operator time => (dur, Now);
operator bool => (time, time);
operator bool > (time, time);
operator bool >= (time, time);
operator bool < (time, time);
operator bool <= (time, time);
#!- the base of all functions.
primitive Function;
#!- the base of decayed operators.
primitive Operator extends Function;
#!- the base of function pointers.
class Funptr extends Object {
fun void @ctor();
fun void default();
}
operator Funptr class ();
operator => (@Any, Function);
operator => (@Any, Funptr);
operator :=> (Function, Funptr);
operator @implicit (Function, Funptr);
operator $ (Function, Funptr);
operator @implicit (Operator, Funptr);
operator $ (Operator, Funptr);
operator :=> (Function, Function);
operator Function @partial ();
operator Class @partial ();
primitive @error;
operator :=> (Object, Object);
operator => (@Any, Compound);
operator bool == (Object, Object);
operator bool != (Object, Object);
operator bool $ (Object, Object);
operator bool @unconditional (Object);
operator bool @conditional (Object);
operator bool ! (Object);
operator Compound class ();
const float samplerate;
const float pi;
const dur d_zero;
const dur samp;
const dur ms;
const dur second;
const dur minute;
const dur hour;
const time t_zero;
primitive None;
specialid const None;
operator None :=> (None, None);
const int index;
fun bool is(int member);
operator auto new:[ T ](int size, int id, T value);
operator . (Union, @Any);
union Option :[ A ] {
None none;
A val;
};
class Array:[ T ] extends Object {
funptr static A map_t:[ A ](T elem);
funptr static Option:[ A ] compactmap_t:[ A ](T elem);
funptr static A fold_t:[ A ](T elem, A acc);
funptr static bool filter_t(T elem);
funptr static T new_t(int idx);
fun bool remove(int index);
fun bool insert(int index, T data);
fun int size();
fun int depth();
fun int cap();
fun T random();
fun A[] map:[ A ](map_t:[ A ] data);
fun A[] compactMap:[ A ](compactmap_t:[ A ] data);
fun T[] filter(filter_t data);
fun int count(filter_t data);
fun A foldl:[ A ](fold_t:[ A ] data, A initial);
fun A foldr:[ A ](fold_t:[ A ] data, A initial);
operator auto new(new_t init);
}
operator :=> (Array, Array);
operator @implicit (Array, Array);
operator << (Array, @Any);
operator >> (@Any, Array);
operator $ (Array, Array);
operator int [:] (int, Array);
operator [] (int, Array);
operator Array void @each_init ();
operator Array int @each ();
operator Array @each_val ();
operator Array int @each_idx ();
operator Array class ();
operator @Any bool @array_init ();
class Vector:[ T, const int N ] extends Object {
operator auto new();
var static T N;
}
operator Vector class ();
class Event extends Object {
fun void @ctor();
fun void signal();
fun void broadcast();
}
operator int => (Event, Now);
class UGen extends Object {
fun void @ctor();
fun UGen chan(int arg0);
fun int op();
fun int op(int arg0);
fun float last();
}
operator UGen ~> (UGen, UGen);
operator UGen ~< (UGen, UGen);
operator UGen :~> (UGen, UGen);
operator UGen :~< (UGen, UGen);
operator UGen[] ~> (UGen[], UGen[]);
operator UGen[] ~< (UGen[], UGen[]);
operator UGen[] :~> (UGen[], UGen[]);
operator UGen[] :~< (UGen[], UGen[]);
operator UGen[] ~> (UGen, UGen[]);
operator UGen[] ~< (UGen, UGen[]);
operator UGen[] :~> (UGen, UGen[]);
operator UGen[] :~< (UGen, UGen[]);
operator UGen ~> (UGen[], UGen);
operator UGen ~< (UGen[], UGen);
operator UGen :~> (UGen[], UGen);
operator UGen :~< (UGen[], UGen);
const UGen blackhole;
const UGen dac;
const UGen adc;
operator spork (@Any);
operator fork (@Any);
operator new (@Any);
#!- Ref: take a reference from a variable.
#!- used just as the variable it reference.
#!- can only be used as argument.
#!- and cannot be returned.
struct Ref:[ A ] {
#!- a pointer to the referenced variable.
}
#!- internal `Ref` type creation.
operator Ref class ();
class string extends Object {
fun void @ctor();
fun int size();
fun string upper();
fun string lower();
fun string ltrim();
fun string rtrim();
fun string trim();
fun string insert(int pos, string str);
fun string replace(int pos, string str);
fun string replace(int pos, int n, string str);
fun int find(char c);
fun int find(char c, int pos);
fun int find(string str);
fun int find(string str, int pos);
fun int rfind(char c);
fun int rfind(char c, int pos);
fun int rfind(string str);
fun int rfind(string str, int pos);
fun void erase(int start, int length);
fun void save(string path);
fun static string load(string path);
fun int atoi();
fun int atoi2(&int offset);
fun float atof();
}
operator [] (int, string);
operator bool == (string, string);
operator bool != (string, string);
operator string [:] (int, string);
specialid const __file__;
specialid const __func__;
specialid const __line__;
class Shred extends Object {
const int cancel;
fun void exit();
fun bool running();
fun bool done();
fun int id();
fun static Shred fromId(int xid);
fun void yield();
fun int args();
fun string arg(int n);
fun string name();
fun string path();
fun string dir();
fun string code_name();
fun string code_path();
fun string code_dir();
fun void set_cancel(bool n);
fun void test_cancel();
fun void lock();
fun void unlock();
fun float get_now();
fun UGen get_blackhole();
}
specialid const me;
class Fork extends Shred {
const int is_done;
const Event ev;
fun void join();
fun void test_cancel();
}
class TypedFork:[ A ] extends Fork {
const A retval;
}
class Gain extends UGen {
fun void @ctor();
fun float gain();
fun float gain(float arg0);
}
class Impulse extends UGen {
fun void @ctor();
fun float next();
fun float next(float arg0);
}
class FullRect extends UGen {
fun void @ctor();
}
class HalfRect extends UGen {
fun void @ctor();
}
class Step extends UGen {
fun void @ctor();
fun float next();
fun float next(float arg0);
}
class ZeroX extends UGen {
fun void @ctor();
}
class UsrUGen extends UGen {
fun void @ctor();
fun int default_tick();
}
operator UsrUGen ~=> (Function, UsrUGen);
#!- allow member access.
operator . (Compound, @Any);
operator . (Function, @Any);
#!- Operators class types.
operator bool == (Class, Class);
operator bool != (Class, Class);
operator bool >= (Class, Class);
operator bool > (Class, Class);
operator bool <= (Class, Class);
operator bool < (Class, Class);
#!- Allow binary call to constructors.
operator => (@Any, Class);
#!- internal constructor operator.
operator call_type (@Any);
#!- allow static access.
operator . (Class, @Any);
#!- Deep Equality fallback
operator bool ?= (@Any, @Any);
#!- Deep Inequality fallback
operator bool <> (@Any, @Any);
#!- Deep Equality
operator bool ?= (Compound, Compound);
#!- Deep Inequality
operator bool <> (Compound, Compound);
class Dict:[ Key, Val ] extends Object {
fun void @ctor();
fun void remove(Key key);
}
operator Dict class ();
operator Dict int @each ();
operator Dict void @each_init ();
operator Dict @each_val ();
operator Dict @each_idx ();
fun int hash(int key);
fun int hash(Object key);
fun int hash(float key);
fun int hash(time key);
fun int hash(dur key);
fun int hash(string key);
#!- a type for *pretty print*.
primitive Gack;
#!- @Gack implicit cast
operator @implicit (Gack, @Any);
class Sift extends Shred {
}
#!- This operator expands too
#!- spork {
#!- while(true) {
#!- lhs.last() => rhs;
#!- samp => now;
#!- }
#!- }
operator Sift |> (UGen, Function);
operator Sift |> (Sift, Function);
operator Sift |> (UGen, Funptr);
operator Sift |> (Sift, Funptr);
#!- Definition of the basic locale
fun float BasicLocale(string str);
enum @hidden_enum {
0 :=> @hidden_enum,
}
Control Flow
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 >>>;
}
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, "!" >>>;
}
While Loops
while(true) {
if(maybe)
break;
<<< "running..." >>>;
}
well this may output nothing... lets try
<<< maybe >>>;
do{
if(maybe)
break;
<<< "running..." >>>;
} while(true);
Extending
Giving gwion a new driver
basics
in order to use GWION_CTL ...
concept
upd driver
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)tructorshred
: 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
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 >>>;
Partial Application
According to wikipeda: In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
In gwion, you can use a hole _
to achieve that
fun int test(int i, int j) {
return i + j;
}
test(_, 2) :=> const auto mytest;
<<< 40 => mytest >>>;
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);
Short lambdas
if the lambda consists of only one expression, the result of that expression is implicetely returned. Notice there is no semicolon in the lambda body;
<<< \ { 42 }() >>>;
Use case
Passing to a function pointer
funptr void fptr_t(int);
\ i { <<< "passed '", i, "'" >>>; } :=> var fptr_t fptr;
fptr(4);
As Argument to Functions
funptr void fptr_t(int);
fun void test(fptr_t fptr) {
fptr(5);
}
test(\ i { <<< "passed '", i, "'" >>>; });
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);
Pipes
Motivation
Sometimes when writing code, you have a situation such as this:
fun int incr(int i) {
return i + 1;
}
<<< incr(incr(incr(incr(1)))) >>>;
This code looks rather unappealing due to the nested functions. Instead, you can use pipes!
Usage
Instead of nesting functions over and over again, you can pipe the functions in a nice line.
fun int incr(int i) {
return i + 1;
}
<<< 1 => incr => incr => incr => incr >>>;
As you can see, the 1
is piped into the incr
function, and the result of that is piped into the incr
function, and so on.
Multiple arguments
Piping works a little differently if your function has multiple arguments. If a function has multiple arguments, there are two ways to pipe.
First off, you can pipe all arguments directly.
fun int add(int i, int j) {
return i + j;
}
<<< (1, 2) => add >>>;
Second off, you can pipe arguments one at a time.
fun int add(int i, int j) {
return i + j;
}
<<< 1 => add(_, 2) >>>;
<<< 2 => add(1, _) >>>;
The underscore determines where the piped argument goes. In the first line, 1
goes into the first argument, whereas in the second line, 2
goes into the second argument.
You can also have multiple underscores.
fun int add3(int i, int j, int k) {
return i + j + k;
}
<<< (1, 3) => add3(_, 2, _) >>>;
The arguments go into their respective underscores. In this case, 1
goes into the first argument and 3
goes into the third.
Types
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.
Function Pointers
Primitive types
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 >>>;
Tagged Union
Union store their component in the same memory space, but only one element can be used at a time
union U {
int i;
float f;
Object o;
};
#! create an union with field `i` set to `1`
new U(i, 1) :=> var U u;
<<< u.i >>>;
#! set field f to 2.4
2.4 :=> u.f;
<<< u.f >>>;
#! this will trigger an invalid access error
<<< u.i >>>;
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
Analys
compiler generated documentation
Emoji
compiler generated documentation
K
compiler generated documentation
Lsys
compiler generated documentation
Math
compiler generated documentation
Modules
compiler generated documentation
Std
compiler generated documentation
TinySF
compiler generated documentation
Tuple
compiler generated documentation
Vecx
compiler generated documentation
Contributing
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.
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.
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
useexport 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 themake
recipe makes things simple
Now please submit a pull request.
Contributing Plugins
This is a step by step hands on tutorial that will quide you trought the steps of making a Gwion plugin
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 int
s 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
#import 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
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:
Name | Value | Description |
---|---|---|
GW_OK | 1 | Success |
GW_PASS | 0 | Ignore the result and continue |
GW_ERROR | -1 | An 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.
Name | Value | Description |
---|---|---|
CHECK_BB | if (f < 0) return GW_ERROR; | Takes an expression that evaluates to m_bool . Potentially exits function returning GW_ERROR . |
CHECK_BO | if (f < 0) return NULL; | Takes an expression that evaluates to m_bool . Potentially exits function returning NULL . |
CHECK_OB | if (!f) return GW_ERROR; | Takes an expression that evaluates to a pointer. Potentially exits function returning GW_ERROR . |
CHECK_OO | if (!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
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.
Contributors
Thanks goes to these wonderful people:
Benchmarks
Author: fennecdjay Date: 02/25/24 19:27:30 Commit: 30af8f4
Results
Please note that times are in second (lower is better)
it seems that some chuck test are broken atm. we're investigating this