From 3e0abfb52c7796fb94fb8f82da1753efbbaaf86a Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Astor?= Date: Sun, 25 Apr 2021 21:22:43 +0200 Subject: [PATCH] Add plugin introduction --- docs/Contributing/ContributingPlugins.mdr | 3 + docs/Contributing/Plugins/README.md | 1 + docs/Contributing/Plugins/introduction.mdr | 155 +++++++++++++++++++++ docs/Contributing/Plugins/list | 2 + docs/Contributing/list | 4 + 5 files changed, 165 insertions(+) create mode 100644 docs/Contributing/ContributingPlugins.mdr create mode 100644 docs/Contributing/Plugins/README.md create mode 100644 docs/Contributing/Plugins/introduction.mdr create mode 100644 docs/Contributing/Plugins/list diff --git a/docs/Contributing/ContributingPlugins.mdr b/docs/Contributing/ContributingPlugins.mdr new file mode 100644 index 00000000..a5e8906f --- /dev/null +++ b/docs/Contributing/ContributingPlugins.mdr @@ -0,0 +1,3 @@ +# Contributing Plugins + +This is a step by step hands on tutorial that will quide you trought the steps of making a Gwion plugin diff --git a/docs/Contributing/Plugins/README.md b/docs/Contributing/Plugins/README.md new file mode 100644 index 00000000..779d515c --- /dev/null +++ b/docs/Contributing/Plugins/README.md @@ -0,0 +1 @@ +# Plugins diff --git a/docs/Contributing/Plugins/introduction.mdr b/docs/Contributing/Plugins/introduction.mdr new file mode 100644 index 00000000..0cf35c23 --- /dev/null +++ b/docs/Contributing/Plugins/introduction.mdr @@ -0,0 +1,155 @@ +# 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`: +```bash +# 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: +```c +#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: +```c +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: +```c +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: + +```c +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`: +```c +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: +```c +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: + +```c +// 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: + +```c +// 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: +```gwion +#! 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: +```bash +gwion -p. add.gw +``` diff --git a/docs/Contributing/Plugins/list b/docs/Contributing/Plugins/list new file mode 100644 index 00000000..9d7dc46a --- /dev/null +++ b/docs/Contributing/Plugins/list @@ -0,0 +1,2 @@ +README.md +introduction.md diff --git a/docs/Contributing/list b/docs/Contributing/list index 6636e846..67c0d72e 100644 --- a/docs/Contributing/list +++ b/docs/Contributing/list @@ -2,4 +2,8 @@ README.md ContributingCode.md ContributingDocumentation.md ContributingTranslation.md + +ContributingPlugins.md +Plugins + Contributors.md -- 2.43.0