aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md27
-rw-r--r--VERSION2
-rw-r--r--canvas.lv2/COPYING201
-rw-r--r--canvas.lv2/README.md18
-rw-r--r--canvas.lv2/canvas.lv2/canvas.h195
-rw-r--r--canvas.lv2/canvas.lv2/forge.h326
-rw-r--r--canvas.lv2/canvas.lv2/idisp.h166
-rw-r--r--canvas.lv2/canvas.lv2/lv2_extensions.h174
-rw-r--r--canvas.lv2/canvas.lv2/render.h87
-rw-r--r--canvas.lv2/canvas.lv2/render_cairo.h584
-rw-r--r--canvas.lv2/canvas.lv2/render_nanovg.h597
-rw-r--r--canvas.lv2/gitlab-ci/generic.yml106
-rw-r--r--manifest.ttl.in80
-rw-r--r--meson.build70
-rw-r--r--meson_options.txt1
-rw-r--r--monitors.c37
-rw-r--r--monitors.h109
-rw-r--r--monitors.ttl.in361
-rw-r--r--monitors_audio_wave.c671
-rw-r--r--monitors_midi_pianoroll.c729
-rw-r--r--monitors_time_metronom.c475
-rw-r--r--props.lv2/.gitlab-ci.yml2
-rw-r--r--props.lv2/COPYING201
-rw-r--r--props.lv2/README.md20
-rw-r--r--props.lv2/VERSION1
-rw-r--r--props.lv2/gitlab-ci/generic.yml106
-rw-r--r--props.lv2/meson.build82
-rw-r--r--props.lv2/props.h (renamed from props.h)0
-rw-r--r--props.lv2/test/chunk.bin (renamed from test/chunk.bin)bin16 -> 16 bytes
-rw-r--r--props.lv2/test/manifest.ttl.in (renamed from test/manifest.ttl.in)0
-rw-r--r--props.lv2/test/props.c (renamed from test/props.c)0
-rw-r--r--props.lv2/test/props.ttl (renamed from test/props.ttl)0
-rw-r--r--props.lv2/test/props_test.c (renamed from test/props_test.c)0
-rw-r--r--timely.lv2/.gitlab-ci.yml2
-rw-r--r--timely.lv2/COPYING201
-rw-r--r--timely.lv2/README.md20
-rw-r--r--timely.lv2/VERSION1
-rw-r--r--timely.lv2/gitlab-ci/generic.yml106
-rw-r--r--timely.lv2/meson.build67
-rw-r--r--timely.lv2/test/manifest.ttl.in28
-rw-r--r--timely.lv2/test/timely.c218
-rw-r--r--timely.lv2/test/timely.ttl65
-rw-r--r--timely.lv2/timely.h404
43 files changed, 6507 insertions, 33 deletions
diff --git a/README.md b/README.md
index 08466d2..63e4d45 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,31 @@
-# Props.lv2
+# Monitors.lv2
-## Utility header for property based LV2 plugins
+## LV2 monitors plugins
+
+### Webpage
+
+Get more information at: [http://open-music-kontrollers.ch/lv2/monitors](http://open-music-kontrollers.ch/lv2/monitors)
+
+### Build status
+
+[![build status](https://gitlab.com/OpenMusicKontrollers/monitors.lv2/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/monitors.lv2/commits/master)
+
+### Dependencies
+
+* [LV2](http://lv2plug.in) (LV2 Plugin Standard)
+
+### Build / install
+
+ git clone https://git.open-music-kontrollers.ch/lv2/monitors.lv2
+ cd monitors.lv2
+ meson build
+ cd build
+ ninja -j4
+ sudo ninja install
### License
-Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
This is free software: you can redistribute it and/or modify
it under the terms of the Artistic License 2.0 as published by
diff --git a/VERSION b/VERSION
index a63a9fe..b029d33 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.159
+0.1.287
diff --git a/canvas.lv2/COPYING b/canvas.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/canvas.lv2/COPYING
@@ -0,0 +1,201 @@
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/canvas.lv2/README.md b/canvas.lv2/README.md
new file mode 100644
index 0000000..06b87f2
--- /dev/null
+++ b/canvas.lv2/README.md
@@ -0,0 +1,18 @@
+# Canvas LV2 plugin extension
+
+### License
+
+Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+
+This is free software: you can redistribute it and/or modify
+it under the terms of the Artistic License 2.0 as published by
+The Perl Foundation.
+
+This source is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Artistic License 2.0 for more details.
+
+You should have received a copy of the Artistic License 2.0
+along the source as a COPYING file. If not, obtain it from
+<http://www.perlfoundation.org/artistic_license_2_0>.
diff --git a/canvas.lv2/canvas.lv2/canvas.h b/canvas.lv2/canvas.lv2/canvas.h
new file mode 100644
index 0000000..7cca0fa
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/canvas.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_H
+#define _LV2_CANVAS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+
+#define CANVAS_URI "http://open-music-kontrollers.ch/lv2/canvas"
+#define CANVAS_PREFIX CANVAS_URI"#"
+
+#define CANVAS__graph CANVAS_PREFIX"graph"
+#define CANVAS__body CANVAS_PREFIX"body"
+#define CANVAS__aspectRatio CANVAS_PREFIX"aspectRatio"
+
+// Graph properties and attributes
+#define CANVAS__BeginPath CANVAS_PREFIX"BeginPath"
+#define CANVAS__ClosePath CANVAS_PREFIX"ClosePath"
+#define CANVAS__Arc CANVAS_PREFIX"Arc"
+#define CANVAS__CurveTo CANVAS_PREFIX"CurveTo"
+#define CANVAS__LineTo CANVAS_PREFIX"LineTo"
+#define CANVAS__MoveTo CANVAS_PREFIX"MoveTo"
+#define CANVAS__Rectangle CANVAS_PREFIX"Rectangle"
+#define CANVAS__PolyLine CANVAS_PREFIX"PolyLine"
+#define CANVAS__Style CANVAS_PREFIX"Style"
+#define CANVAS__LineWidth CANVAS_PREFIX"LineWidth"
+#define CANVAS__LineDash CANVAS_PREFIX"LineDash"
+#define CANVAS__LineCap CANVAS_PREFIX"LineCap"
+#define CANVAS__LineJoin CANVAS_PREFIX"LineJoin"
+#define CANVAS__MiterLimit CANVAS_PREFIX"MiterLimig"
+#define CANVAS__Stroke CANVAS_PREFIX"Stroke"
+#define CANVAS__Fill CANVAS_PREFIX"Fill"
+#define CANVAS__Clip CANVAS_PREFIX"Clip"
+#define CANVAS__Save CANVAS_PREFIX"Save"
+#define CANVAS__Restore CANVAS_PREFIX"Restore"
+#define CANVAS__Translate CANVAS_PREFIX"Translate"
+#define CANVAS__Scale CANVAS_PREFIX"Scale"
+#define CANVAS__Rotate CANVAS_PREFIX"Rotate"
+#define CANVAS__Transform CANVAS_PREFIX"Transform"
+#define CANVAS__Reset CANVAS_PREFIX"Reset"
+#define CANVAS__FontSize CANVAS_PREFIX"FontSize"
+#define CANVAS__FillText CANVAS_PREFIX"FillText"
+
+#define CANVAS__lineCapButt CANVAS_PREFIX"lineCapButt"
+#define CANVAS__lineCapRound CANVAS_PREFIX"lineCapRound"
+#define CANVAS__lineCapSquare CANVAS_PREFIX"lineCapSquare"
+
+#define CANVAS__lineJoinMiter CANVAS_PREFIX"lineJoinMiter"
+#define CANVAS__lineJoinRound CANVAS_PREFIX"lineJoinRound"
+#define CANVAS__lineJoinBevel CANVAS_PREFIX"lineJoinBevel"
+
+// Input properties and attributes
+
+#define CANVAS__mouseButtonLeft CANVAS_PREFIX"mouseButtonLeft"
+#define CANVAS__mouseButtonMiddle CANVAS_PREFIX"mouseButtonMiddle"
+#define CANVAS__mouseButtonRight CANVAS_PREFIX"mouseButtonRight"
+#define CANVAS__mouseWheelX CANVAS_PREFIX"mouseWheelX"
+#define CANVAS__mouseWheelY CANVAS_PREFIX"mouseWheelY"
+#define CANVAS__mousePositionX CANVAS_PREFIX"mousePositionX"
+#define CANVAS__mousePositionY CANVAS_PREFIX"mousePositionY"
+#define CANVAS__mouseFocus CANVAS_PREFIX"mouseFocus"
+
+typedef struct _LV2_Canvas_URID LV2_Canvas_URID;
+
+struct _LV2_Canvas_URID {
+ LV2_URID Canvas_graph;
+ LV2_URID Canvas_body;
+ LV2_URID Canvas_aspectRatio;
+
+ LV2_URID Canvas_BeginPath;
+ LV2_URID Canvas_ClosePath;
+ LV2_URID Canvas_Arc;
+ LV2_URID Canvas_CurveTo;
+ LV2_URID Canvas_LineTo;
+ LV2_URID Canvas_MoveTo;
+ LV2_URID Canvas_Rectangle;
+ LV2_URID Canvas_PolyLine;
+ LV2_URID Canvas_Style;
+ LV2_URID Canvas_LineWidth;
+ LV2_URID Canvas_LineDash;
+ LV2_URID Canvas_LineCap;
+ LV2_URID Canvas_LineJoin;
+ LV2_URID Canvas_MiterLimit;
+ LV2_URID Canvas_Stroke;
+ LV2_URID Canvas_Fill;
+ LV2_URID Canvas_Clip;
+ LV2_URID Canvas_Save;
+ LV2_URID Canvas_Restore;
+ LV2_URID Canvas_Translate;
+ LV2_URID Canvas_Scale;
+ LV2_URID Canvas_Rotate;
+ LV2_URID Canvas_Transform;
+ LV2_URID Canvas_Reset;
+ LV2_URID Canvas_FontSize;
+ LV2_URID Canvas_FillText;
+
+ LV2_URID Canvas_lineCapButt;
+ LV2_URID Canvas_lineCapRound;
+ LV2_URID Canvas_lineCapSquare;
+
+ LV2_URID Canvas_lineJoinMiter;
+ LV2_URID Canvas_lineJoinRound;
+ LV2_URID Canvas_lineJoinBevel;
+
+ LV2_URID Canvas_mouseButtonLeft;
+ LV2_URID Canvas_mouseButtonMiddle;
+ LV2_URID Canvas_mouseButtonRight;
+ LV2_URID Canvas_mouseWheelX;
+ LV2_URID Canvas_mouseWheelY;
+ LV2_URID Canvas_mousePositionX;
+ LV2_URID Canvas_mousePositionY;
+ LV2_URID Canvas_mouseFocus;
+
+ LV2_Atom_Forge forge;
+};
+
+static inline void
+lv2_canvas_urid_init(LV2_Canvas_URID *urid, LV2_URID_Map *map)
+{
+ urid->Canvas_graph = map->map(map->handle, CANVAS__graph);
+ urid->Canvas_body = map->map(map->handle, CANVAS__body);
+ urid->Canvas_aspectRatio = map->map(map->handle, CANVAS__aspectRatio);
+
+ urid->Canvas_BeginPath = map->map(map->handle, CANVAS__BeginPath);
+ urid->Canvas_ClosePath = map->map(map->handle, CANVAS__ClosePath);
+ urid->Canvas_Arc = map->map(map->handle, CANVAS__Arc);
+ urid->Canvas_CurveTo = map->map(map->handle, CANVAS__CurveTo);
+ urid->Canvas_LineTo = map->map(map->handle, CANVAS__LineTo);
+ urid->Canvas_MoveTo = map->map(map->handle, CANVAS__MoveTo);
+ urid->Canvas_Rectangle = map->map(map->handle, CANVAS__Rectangle);
+ urid->Canvas_PolyLine = map->map(map->handle, CANVAS__PolyLine);
+ urid->Canvas_Style = map->map(map->handle, CANVAS__Style);
+ urid->Canvas_LineWidth = map->map(map->handle, CANVAS__LineWidth);
+ urid->Canvas_LineDash = map->map(map->handle, CANVAS__LineDash);
+ urid->Canvas_LineCap = map->map(map->handle, CANVAS__LineCap);
+ urid->Canvas_LineJoin = map->map(map->handle, CANVAS__LineJoin);
+ urid->Canvas_MiterLimit = map->map(map->handle, CANVAS__MiterLimit);
+ urid->Canvas_Stroke = map->map(map->handle, CANVAS__Stroke);
+ urid->Canvas_Fill = map->map(map->handle, CANVAS__Fill);
+ urid->Canvas_Clip = map->map(map->handle, CANVAS__Clip);
+ urid->Canvas_Save = map->map(map->handle, CANVAS__Save);
+ urid->Canvas_Restore = map->map(map->handle, CANVAS__Restore);
+ urid->Canvas_Translate = map->map(map->handle, CANVAS__Translate);
+ urid->Canvas_Scale = map->map(map->handle, CANVAS__Scale);
+ urid->Canvas_Rotate = map->map(map->handle, CANVAS__Rotate);
+ urid->Canvas_Transform = map->map(map->handle, CANVAS__Transform);
+ urid->Canvas_Reset = map->map(map->handle, CANVAS__Reset);
+ urid->Canvas_FontSize = map->map(map->handle, CANVAS__FontSize);
+ urid->Canvas_FillText = map->map(map->handle, CANVAS__FillText);
+
+ urid->Canvas_lineCapButt = map->map(map->handle, CANVAS__lineCapButt);
+ urid->Canvas_lineCapRound = map->map(map->handle, CANVAS__lineCapRound);
+ urid->Canvas_lineCapSquare = map->map(map->handle, CANVAS__lineCapSquare);
+
+ urid->Canvas_lineJoinMiter = map->map(map->handle, CANVAS__lineJoinMiter);
+ urid->Canvas_lineJoinRound = map->map(map->handle, CANVAS__lineJoinRound);
+ urid->Canvas_lineJoinBevel = map->map(map->handle, CANVAS__lineJoinBevel);
+
+ urid->Canvas_mouseButtonLeft = map->map(map->handle, CANVAS__mouseButtonLeft);
+ urid->Canvas_mouseButtonMiddle = map->map(map->handle, CANVAS__mouseButtonMiddle);
+ urid->Canvas_mouseButtonRight = map->map(map->handle, CANVAS__mouseButtonRight);
+ urid->Canvas_mouseWheelX = map->map(map->handle, CANVAS__mouseWheelX);
+ urid->Canvas_mouseWheelY = map->map(map->handle, CANVAS__mouseWheelY);
+ urid->Canvas_mousePositionX = map->map(map->handle, CANVAS__mousePositionX);
+ urid->Canvas_mousePositionY = map->map(map->handle, CANVAS__mousePositionY);
+ urid->Canvas_mouseFocus = map->map(map->handle, CANVAS__mouseFocus);
+
+ lv2_atom_forge_init(&urid->forge, map);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_H
diff --git a/canvas.lv2/canvas.lv2/forge.h b/canvas.lv2/canvas.lv2/forge.h
new file mode 100644
index 0000000..c5ce5fe
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/forge.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_FORGE_H
+#define _LV2_CANVAS_FORGE_H
+
+#include <canvas.lv2/canvas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_simple(LV2_Atom_Forge *forge, LV2_URID otype)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_vec(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, uint32_t n, const float *vec)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_vector(forge, sizeof(float), forge->Float, n, vec);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_flt(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, float flt)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_float(forge, flt);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_lng(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, int64_t lng)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_long(forge, lng);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_prp(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, LV2_URID prop)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, prop);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_str(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, const char *text)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_string(forge, text, strlen(text));
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_beginPath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_BeginPath);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_closePath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_ClosePath);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_arc(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y, float r, float a1, float a2)
+{
+ const float vec [5] = {x, y, r, a1, a2};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Arc, 5, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_curveTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x1, float y1, float x2, float y2, float x3, float y3)
+{
+ const float vec [6] = {x1, y1, x2, y2, x3, y3};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_CurveTo, 6, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_LineTo, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_moveTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_MoveTo, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_rectangle(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y, float w, float h)
+{
+ const float vec [4] = {x, y, w, h};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Rectangle, 4, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_polyLine(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ uint32_t n, const float *vec)
+{
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_PolyLine, n, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_style(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ uint32_t style)
+{
+ return _lv2_canvas_forge_lng(forge, urid, urid->Canvas_Style, style);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineWidth(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float line_width)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_LineWidth, line_width);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineDash(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float dash_length, float separator_length)
+{
+ const float vec [2] = {dash_length, separator_length};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_LineDash, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineCap(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID line_cap)
+{
+ return _lv2_canvas_forge_prp(forge, urid, urid->Canvas_LineCap, line_cap);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineJoin(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID line_join)
+{
+ return _lv2_canvas_forge_prp(forge, urid, urid->Canvas_LineJoin, line_join);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_miterLimit(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float miter_limit)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_MiterLimit, miter_limit);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_stroke(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Stroke);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fill(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Fill);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_clip(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Clip);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_save(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Save);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_restore(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Restore);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_translate(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Translate, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_scale(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float w, float h)
+{
+ const float vec [2] = {w, h};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Scale, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_rotate(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float a)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_Rotate, a);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_transform(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float xx, float xy, float x0, float yy, float yx, float y0)
+{
+ const float vec [6] = {xx, xy, x0, yy, yx, y0};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Transform, 6, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_reset(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Reset);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fontSize(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float size)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_FontSize, size);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fillText(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ const char *text)
+{
+ return _lv2_canvas_forge_str(forge, urid, urid->Canvas_FillText, text);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_FORGE_H
diff --git a/canvas.lv2/canvas.lv2/idisp.h b/canvas.lv2/canvas.lv2/idisp.h
new file mode 100644
index 0000000..099ab2c
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/idisp.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_IDISP_H
+#define _LV2_CANVAS_IDISP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LV2_CANVAS_RENDER_CAIRO
+#include <canvas.lv2/render.h>
+#include <canvas.lv2/lv2_extensions.h>
+
+typedef struct _LV2_Canvas_Idisp LV2_Canvas_Idisp;
+
+struct _LV2_Canvas_Idisp {
+ LV2_Inline_Display *queue_draw;
+ LV2_Canvas canvas;
+ LV2_Inline_Display_Image_Surface image_surface;
+ struct {
+ cairo_surface_t *surface;
+ cairo_t *ctx;
+ } cairo;
+};
+
+static inline LV2_Inline_Display_Image_Surface *
+_lv2_canvas_idisp_surf_init(LV2_Canvas_Idisp *idisp, int w, int h)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+
+ surf->width = w;
+ surf->height = h;
+ surf->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, surf->width);
+ surf->data = realloc(surf->data, surf->stride * surf->height);
+ if(!surf->data)
+ {
+ return NULL;
+ }
+
+ idisp->cairo.surface = cairo_image_surface_create_for_data(
+ surf->data, CAIRO_FORMAT_ARGB32, surf->width, surf->height, surf->stride);
+
+ if(idisp->cairo.surface)
+ {
+ cairo_surface_set_device_scale(idisp->cairo.surface, surf->width, surf->height);
+
+ idisp->cairo.ctx = cairo_create(idisp->cairo.surface);
+ if(idisp->cairo.ctx)
+ {
+ cairo_select_font_face(idisp->cairo.ctx, "cairo:monospace",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ }
+ }
+
+ return surf;
+}
+
+static inline void
+_lv2_canvas_idisp_surf_deinit(LV2_Canvas_Idisp *idisp)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+
+ if(idisp->cairo.ctx)
+ {
+ cairo_destroy(idisp->cairo.ctx);
+ idisp->cairo.ctx = NULL;
+ }
+
+ if(idisp->cairo.surface)
+ {
+ cairo_surface_finish(idisp->cairo.surface);
+ cairo_surface_destroy(idisp->cairo.surface);
+ idisp->cairo.surface = NULL;
+ }
+
+ if(surf->data)
+ {
+ free(surf->data);
+ surf->data = NULL;
+ }
+}
+
+static inline LV2_Inline_Display_Image_Surface *
+lv2_canvas_idisp_surf_configure(LV2_Canvas_Idisp *idisp,
+ uint32_t w, uint32_t h, float aspect_ratio)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+ int W;
+ int H;
+
+ if(aspect_ratio < 1.f)
+ {
+ W = h * aspect_ratio;
+ H = h;
+ }
+ else if(aspect_ratio > 1.f)
+ {
+ W = w;
+ H = w / aspect_ratio;
+ }
+ else // aspect_ratio == 1.f
+ {
+ W = w;
+ H = h;
+ }
+
+ if( (surf->width != W) || (surf->height != H) || !surf->data)
+ {
+ _lv2_canvas_idisp_surf_deinit(idisp);
+ surf = _lv2_canvas_idisp_surf_init(idisp, W, H);
+ }
+
+ return surf;
+}
+
+static inline void
+lv2_canvas_idisp_init(LV2_Canvas_Idisp *idisp, LV2_Inline_Display *queue_draw,
+ LV2_URID_Map *map)
+{
+ lv2_canvas_init(&idisp->canvas, map);
+ idisp->queue_draw = queue_draw;
+}
+
+static inline void
+lv2_canvas_idisp_deinit(LV2_Canvas_Idisp *idisp)
+{
+ _lv2_canvas_idisp_surf_deinit(idisp);
+}
+
+static inline void
+lv2_canvas_idisp_queue_draw(LV2_Canvas_Idisp *idisp)
+{
+ if(idisp->queue_draw)
+ {
+ idisp->queue_draw->queue_draw(idisp->queue_draw->handle);
+ }
+}
+
+static inline bool
+lv2_canvas_idisp_render_body(LV2_Canvas_Idisp *idisp, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ return lv2_canvas_render_body(&idisp->canvas, idisp->cairo.ctx,
+ type, size, body);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_IDISP_H
diff --git a/canvas.lv2/canvas.lv2/lv2_extensions.h b/canvas.lv2/canvas.lv2/lv2_extensions.h
new file mode 100644
index 0000000..64fc3bc
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/lv2_extensions.h
@@ -0,0 +1,174 @@
+/*
+ Copyright 2016 Robin Gareus <robin@gareus.org>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef _ardour_lv2_extensions_h_
+#define _ardour_lv2_extensions_h_
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+ @defgroup inlinedisplay Inline-Display
+
+ Support for displaying a miniaturized generic view
+ directly in the host's Mixer Window.
+
+ @{
+*/
+
+#define LV2_INLINEDISPLAY_URI "http://harrisonconsoles.com/lv2/inlinedisplay"
+#define LV2_INLINEDISPLAY_PREFIX LV2_INLINEDISPLAY_URI "#"
+#define LV2_INLINEDISPLAY__interface LV2_INLINEDISPLAY_PREFIX "interface"
+#define LV2_INLINEDISPLAY__queue_draw LV2_INLINEDISPLAY_PREFIX "queue_draw"
+
+/** Opaque handle for LV2_Inline_Display::queue_draw() */
+typedef void* LV2_Inline_Display_Handle;
+
+/** raw image pixmap format is ARGB32,
+ * the data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ */
+typedef struct {
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+} LV2_Inline_Display_Image_Surface;
+
+/** a LV2 Feature provided by the Host to the plugin */
+typedef struct {
+ /** Opaque host data */
+ LV2_Inline_Display_Handle handle;
+ /** Request from run() that the host should call render() at a later time
+ * to update the inline display */
+ void (*queue_draw)(LV2_Inline_Display_Handle handle);
+} LV2_Inline_Display;
+
+/**
+ * Plugin Inline-Display Interface.
+ */
+typedef struct {
+ /**
+ * The render method. This is called by the host in a non-realtime context,
+ * usually the main GUI thread.
+ * The data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ *
+ * @param instance The LV2 instance
+ * @param w the max available width
+ * @param h the max available height
+ * @return pointer to a LV2_Inline_Display_Image_Surface or NULL
+ */
+ LV2_Inline_Display_Image_Surface* (*render)(LV2_Handle instance, uint32_t w, uint32_t h);
+} LV2_Inline_Display_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup automate Self-Automation
+
+ Support for plugins to write automation data via Atom Events
+
+ @{
+*/
+
+#define LV2_AUTOMATE_URI "http://ardour.org/lv2/automate"
+#define LV2_AUTOMATE_URI_PREFIX LV2_AUTOMATE_URI "#"
+/** an lv2:optionalFeature */
+#define LV2_AUTOMATE_URI__can_write LV2_AUTOMATE_URI_PREFIX "canWriteAutomatation"
+/** atom:supports */
+#define LV2_AUTOMATE_URI__control LV2_AUTOMATE_URI_PREFIX "automationControl"
+/** lv2:portProperty */
+#define LV2_AUTOMATE_URI__controlled LV2_AUTOMATE_URI_PREFIX "automationControlled"
+#define LV2_AUTOMATE_URI__controller LV2_AUTOMATE_URI_PREFIX "automationController"
+
+/** atom messages */
+#define LV2_AUTOMATE_URI__event LV2_AUTOMATE_URI_PREFIX "event"
+#define LV2_AUTOMATE_URI__setup LV2_AUTOMATE_URI_PREFIX "setup"
+#define LV2_AUTOMATE_URI__finalize LV2_AUTOMATE_URI_PREFIX "finalize"
+#define LV2_AUTOMATE_URI__start LV2_AUTOMATE_URI_PREFIX "start"
+#define LV2_AUTOMATE_URI__end LV2_AUTOMATE_URI_PREFIX "end"
+#define LV2_AUTOMATE_URI__parameter LV2_AUTOMATE_URI_PREFIX "parameter"
+#define LV2_AUTOMATE_URI__value LV2_AUTOMATE_URI_PREFIX "value"
+
+/**
+ @}
+*/
+
+/**
+ @defgroup license License-Report
+
+ Allow for commercial LV2 to report their
+ licensing status.
+
+ @{
+*/
+
+#define LV2_PLUGINLICENSE_URI "http://harrisonconsoles.com/lv2/license"
+#define LV2_PLUGINLICENSE_PREFIX LV2_PLUGINLICENSE_URI "#"
+#define LV2_PLUGINLICENSE__interface LV2_PLUGINLICENSE_PREFIX "interface"
+
+typedef struct _LV2_License_Interface {
+ /* @return -1 if no license is needed; 0 if unlicensed, 1 if licensed */
+ int (*is_licensed)(LV2_Handle instance);
+ /* @return a string copy of the licensee name if licensed, or NULL, the caller needs to free this */
+ char* (*licensee)(LV2_Handle instance);
+ /* @return a URI identifying the plugin-bundle or plugin for which a given license is valid */
+ const char* (*product_uri)(LV2_Handle instance);
+ /* @return human readable product name for the URI */
+ const char* (*product_name)(LV2_Handle instance);
+ /* @return link to website or webstore */
+ const char* (*store_url)(LV2_Handle instance);
+} LV2_License_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup plugin provided bypass
+
+ A port with the designation "processing#enable" must
+ control a plugin's internal bypass mode.
+
+ If the port value is larger than zero the plugin processes
+ normally.
+
+ If the port value is zero, the plugin is expected to bypass
+ all signals unmodified.
+
+ The plugin is responsible for providing a click-free transition
+ between the states.
+
+ (values less than zero are reserved for future use:
+ e.g click-free insert/removal of latent plugins.
+ Generally values <= 0 are to be treated as bypassed.)
+
+ lv2:designation <http://ardour.org/lv2/processing#enable> ;
+
+ @{
+*/
+
+#define LV2_PROCESSING_URI "http://ardour.org/lv2/processing"
+#define LV2_PROCESSING_URI_PREFIX LV2_PROCESSING_URI "#"
+#define LV2_PROCESSING_URI__enable LV2_PROCESSING_URI_PREFIX "enable"
+
+/**
+ @}
+*/
+
+#endif
diff --git a/canvas.lv2/canvas.lv2/render.h b/canvas.lv2/canvas.lv2/render.h
new file mode 100644
index 0000000..b809804
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_H
+#define _LV2_CANVAS_RENDER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <canvas.lv2/canvas.h>
+
+#define LV2_CANVAS_NUM_METHODS 26
+
+typedef struct _LV2_Canvas_Meth LV2_Canvas_Meth;
+typedef struct _LV2_Canvas LV2_Canvas;
+typedef void (*LV2_Canvas_Func)(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body);
+
+struct _LV2_Canvas_Meth {
+ LV2_URID command;
+ LV2_Canvas_Func func;
+};
+
+struct _LV2_Canvas {
+ LV2_Canvas_URID urid;
+ LV2_Canvas_Meth methods [LV2_CANVAS_NUM_METHODS];
+};
+
+static inline const float *
+_lv2_canvas_render_get_float_vecs(LV2_Canvas_URID *urid, const LV2_Atom *body,
+ uint32_t *n)
+{
+ const LV2_Atom_Vector *vec = (const LV2_Atom_Vector *)body;
+ const float *flt = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vec);
+ *n = (vec->atom.type == urid->forge.Vector)
+ && (vec->body.child_type == urid->forge.Float)
+ && (vec->body.child_size == sizeof(float))
+ ? (vec->atom.size - sizeof(LV2_Atom_Vector_Body)) / vec->body.child_size
+ : 0;
+
+ return flt;
+}
+
+static inline const float *
+_lv2_canvas_render_get_float_vec(LV2_Canvas_URID *urid, const LV2_Atom *body,
+ uint32_t n)
+{
+ uint32_t N;
+ const float *flt = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ return n == N ? flt : NULL;
+}
+
+static inline const void *
+_lv2_canvas_render_get_type(const LV2_Atom *body, LV2_URID type)
+{
+ return body->type == type
+ ? LV2_ATOM_BODY_CONST(body)
+ : NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(LV2_CANVAS_RENDER_NANOVG)
+# include <canvas.lv2/render_nanovg.h>
+#else
+# include <canvas.lv2/render_cairo.h>
+#endif
+
+#endif // _LV2_CANVAS_RENDER_H
diff --git a/canvas.lv2/canvas.lv2/render_cairo.h b/canvas.lv2/canvas.lv2/render_cairo.h
new file mode 100644
index 0000000..a41c72c
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render_cairo.h
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_CAIRO_H
+#define _LV2_CANVAS_RENDER_CAIRO_H
+
+#include <assert.h>
+
+#include <canvas.lv2/canvas.h>
+
+#include <cairo/cairo.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+_lv2_canvas_render_beginPath(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_new_sub_path(ctx);
+}
+
+static inline void
+_lv2_canvas_render_closePath(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_close_path(ctx);
+}
+
+static inline void
+_lv2_canvas_render_arc(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
+
+ if(v)
+ {
+ cairo_arc(ctx, v[0], v[1], v[2], v[3], v[4]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_curveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ cairo_curve_to(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_line_to(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_moveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_move_to(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rectangle(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
+
+ if(v)
+ {
+ cairo_rectangle(ctx, v[0], v[1], v[2], v[3]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_polyline(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ uint32_t N;
+ const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ if(v)
+ {
+ for(uint32_t i = 0; i < 2; i += 2)
+ {
+ cairo_move_to(ctx, v[i], v[i+1]);
+ }
+
+ for(uint32_t i = 2; i < N; i += 2)
+ {
+ cairo_line_to(ctx, v[i], v[i+1]);
+ }
+ }
+}
+
+static inline void
+_lv2_canvas_render_style(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
+
+ if(v)
+ {
+ const float r = (float)((*v >> 24) & 0xff) / 0xff;
+ const float g = (float)((*v >> 16) & 0xff) / 0xff;
+ const float b = (float)((*v >> 8) & 0xff) / 0xff;
+ const float a = (float)((*v >> 0) & 0xff) / 0xff;
+
+ cairo_set_source_rgba(ctx, r, g, b, a);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineWidth(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_line_width(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineDash(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ const double d[2] = {v[0], v[1]};
+ cairo_set_dash(ctx, d, 2, 0);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineCap(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
+
+ if(*v == urid->Canvas_lineCapButt)
+ cap = CAIRO_LINE_CAP_BUTT;
+ else if(*v == urid->Canvas_lineCapRound)
+ cap = CAIRO_LINE_CAP_ROUND;
+ else if(*v == urid->Canvas_lineCapSquare)
+ cap = CAIRO_LINE_CAP_SQUARE;
+
+ cairo_set_line_cap(ctx, cap);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineJoin(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
+
+ if(*v == urid->Canvas_lineJoinMiter)
+ join = CAIRO_LINE_JOIN_MITER;
+ else if(*v == urid->Canvas_lineJoinRound)
+ join = CAIRO_LINE_JOIN_ROUND;
+ else if(*v == urid->Canvas_lineJoinBevel)
+ join = CAIRO_LINE_JOIN_BEVEL;
+
+ cairo_set_line_join(ctx, join);
+ }
+}
+
+static inline void
+_lv2_canvas_render_miterLimit(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_miter_limit(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_stroke(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_stroke(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fill(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_fill(ctx);
+}
+
+static inline void
+_lv2_canvas_render_clip(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_clip(ctx);
+}
+
+static inline void
+_lv2_canvas_render_save(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_save(ctx);
+}
+
+static inline void
+_lv2_canvas_render_restore(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_restore(ctx);
+}
+
+static inline void
+_lv2_canvas_render_translate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_translate(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_scale(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_scale(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rotate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_rotate(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_transform(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ const cairo_matrix_t matrix = {
+ .xx = v[0],
+ .xy = v[1],
+ .x0 = v[2],
+ .yy = v[3],
+ .yx = v[4],
+ .y0 = v[5]
+ };
+
+ cairo_transform(ctx, &matrix);
+ }
+}
+
+static inline void
+_lv2_canvas_render_reset(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_identity_matrix(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fontSize(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_font_size(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_fillText(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
+
+ if(v)
+ {
+ cairo_text_extents_t extents;
+ cairo_text_extents(ctx, v, &extents);
+ const float dx = extents.width/2 + extents.x_bearing;
+ const float dy = extents.height/2 + extents.y_bearing;
+ cairo_rel_move_to(ctx, -dx, -dy);
+ cairo_show_text(ctx, v);
+ }
+}
+
+static inline void
+_lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
+{
+ if(n < 2)
+ return;
+
+ LV2_Canvas_Meth *p = A;
+
+ int i = -1;
+ int j = n;
+
+ while(true)
+ {
+ do {
+ i += 1;
+ } while(A[i].command < p->command);
+
+ do {
+ j -= 1;
+ } while(A[j].command > p->command);
+
+ if(i >= j)
+ break;
+
+ const LV2_Canvas_Meth tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+
+ _lv2_canvas_qsort(A, j + 1);
+ _lv2_canvas_qsort(A + j + 1, n - j - 1);
+}
+
+static inline LV2_Canvas_Meth *
+_lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
+{
+ LV2_Canvas_Meth *base = a;
+
+ for(int N = n, half; N > 1; N -= half)
+ {
+ half = N/2;
+ LV2_Canvas_Meth *dst = &base[half];
+ base = (dst->command > p) ? base : dst;
+ }
+
+ return (base->command == p) ? base : NULL;
+}
+
+static inline void
+lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
+{
+ lv2_canvas_urid_init(&canvas->urid, map);
+
+ unsigned ptr = 0;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
+ canvas->methods[ptr++].func = _lv2_canvas_render_arc;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
+ canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Style;
+ canvas->methods[ptr++].func = _lv2_canvas_render_style;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
+ canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
+ canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fill;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
+ canvas->methods[ptr++].func = _lv2_canvas_render_clip;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Save;
+ canvas->methods[ptr++].func = _lv2_canvas_render_save;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
+ canvas->methods[ptr++].func = _lv2_canvas_render_restore;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_translate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
+ canvas->methods[ptr++].func = _lv2_canvas_render_scale;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
+ canvas->methods[ptr++].func = _lv2_canvas_render_transform;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
+ canvas->methods[ptr++].func = _lv2_canvas_render_reset;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
+
+ assert(ptr == LV2_CANVAS_NUM_METHODS);
+
+ _lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
+}
+
+static inline bool
+lv2_canvas_render_body(LV2_Canvas *canvas, cairo_t *ctx, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ LV2_Canvas_URID *urid = &canvas->urid;
+
+ if(!body || (type != urid->forge.Tuple) )
+ return false;
+
+ // save state
+ cairo_save(ctx);
+
+ // clear surface
+ cairo_set_operator(ctx, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(ctx);
+
+ // default attributes
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_font_size(ctx, 0.1);
+ cairo_set_line_width(ctx, 0.01);
+ cairo_set_source_rgba(ctx, 1.0, 1.0, 1.0, 1.0);
+
+ LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
+ {
+ if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
+ const LV2_Atom *body = NULL;
+
+ lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
+
+ LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
+ canvas->methods, LV2_CANVAS_NUM_METHODS);
+
+ if(meth)
+ {
+ meth->func(ctx, urid, body);
+ }
+ }
+ }
+
+ // save state
+ cairo_restore(ctx);
+
+ // flush
+ cairo_surface_t *surface = cairo_get_target(ctx);
+ cairo_surface_flush(surface);
+
+ return true;
+}
+
+static inline bool
+lv2_canvas_render(LV2_Canvas *canvas, cairo_t *ctx, const LV2_Atom_Tuple *tup)
+{
+ return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
+ LV2_ATOM_BODY_CONST(&tup->atom));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LV2_CANVAS_RENDER_CAIRO_H
diff --git a/canvas.lv2/canvas.lv2/render_nanovg.h b/canvas.lv2/canvas.lv2/render_nanovg.h
new file mode 100644
index 0000000..a98bc95
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render_nanovg.h
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_NANOVG_H
+#define _LV2_CANVAS_RENDER_NANOVG_H
+
+#include <assert.h>
+
+#include <nanovg.h>
+
+#if defined(__APPLE__)
+# include <OpenGL/gl.h>
+# include <OpenGL/glext.h>
+#else
+# include <GL/glew.h>
+#endif
+
+#define NANOVG_GL2_IMPLEMENTATION
+#include <nanovg_gl.h>
+
+#if defined(NANOVG_GL2_IMPLEMENTATION)
+# define nvgCreate nvgCreateGL2
+# define nvgDelete nvgDeleteGL2
+#elif defined(NANOVG_GL3_IMPLEMENTATION)
+# define nvgCreate nvgCreateGL3
+# define nvgDelete nvgDeleteGL3
+#elif defined(NANOVG_GLES2_IMPLEMENTATION)
+# define nvgCreate nvgCreateGLES2
+# define nvgDelete nvgDeleteGLES2
+#elif defined(NANOVG_GLES3_IMPLEMENTATION)
+# define nvgCreate nvgCreateGLES3
+# define nvgDelete nvgDeleteGLES3
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+_lv2_canvas_render_beginPath(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgBeginPath(ctx);
+}
+
+static inline void
+_lv2_canvas_render_closePath(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgClosePath(ctx);
+}
+
+static inline void
+_lv2_canvas_render_arc(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
+
+ if(v)
+ {
+ nvgArc(ctx, v[0], v[1], v[2], v[3], v[4], NVG_CCW);
+ }
+}
+
+static inline void
+_lv2_canvas_render_curveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ nvgBezierTo(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgLineTo(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_moveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgMoveTo(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rectangle(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
+
+ if(v)
+ {
+ nvgRect(ctx, v[0], v[1], v[2], v[3]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_polyline(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ uint32_t N;
+ const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ if(v)
+ {
+ for(uint32_t i = 0; i < 2; i += 2)
+ {
+ nvgMoveTo(ctx, v[i], v[i+1]);
+ }
+
+ for(uint32_t i = 2; i < N; i += 2)
+ {
+ nvgLineTo(ctx, v[i], v[i+1]);
+ }
+ }
+}
+
+static inline void
+_lv2_canvas_render_style(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
+
+ if(v)
+ {
+ const uint8_t r = (*v >> 24) & 0xff;
+ const uint8_t g = (*v >> 16) & 0xff;
+ const uint8_t b = (*v >> 8) & 0xff;
+ const uint8_t a = (*v >> 0) & 0xff;
+
+ nvgStrokeColor(ctx, nvgRGBA(r, g, b, a));
+ nvgFillColor(ctx, nvgRGBA(r, g, b, a));
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineWidth(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgStrokeWidth(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineDash(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ //const double d[2] = {v[0], v[1]};
+ //FIXME cairo_set_dash(ctx, d, 2, 0);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineCap(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ int cap = NVG_BUTT;
+
+ if(*v == urid->Canvas_lineCapButt)
+ cap = NVG_BUTT;
+ else if(*v == urid->Canvas_lineCapRound)
+ cap = NVG_ROUND;
+ else if(*v == urid->Canvas_lineCapSquare)
+ cap = NVG_SQUARE;
+
+ nvgLineCap(ctx, cap);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineJoin(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ int join = NVG_MITER;
+
+ if(*v == urid->Canvas_lineJoinMiter)
+ join = NVG_MITER;
+ else if(*v == urid->Canvas_lineJoinRound)
+ join = NVG_ROUND;
+ else if(*v == urid->Canvas_lineJoinBevel)
+ join = NVG_BEVEL;
+
+ nvgLineJoin(ctx, join);
+ }
+}
+
+static inline void
+_lv2_canvas_render_miterLimit(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgMiterLimit(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_stroke(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgStroke(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fill(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgFill(ctx);
+}
+
+static inline void
+_lv2_canvas_render_clip(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ //FIXME cairo_clip(ctx);
+}
+
+static inline void
+_lv2_canvas_render_save(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgSave(ctx);
+}
+
+static inline void
+_lv2_canvas_render_restore(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgRestore(ctx);
+}
+
+static inline void
+_lv2_canvas_render_translate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgTranslate(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_scale(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgScale(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rotate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgRotate(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_transform(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ nvgTransform(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_reset(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgReset(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fontSize(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgFontSize(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_fillText(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
+
+ if(v)
+ {
+ /* FIXME
+ NVGcontextext_extents_t extents;
+ NVGcontextext_extents(ctx, v, &extents);
+ const float dx = extents.width/2 + extents.x_bearing;
+ const float dy = extents.height/2 + extents.y_bearing;
+ cairo_rel_move_to(ctx, -dx, -dy);
+ cairo_show_text(ctx, v);
+ */
+ /*
+ float bounds [4];
+ const float adv_x = nvgTextBounds(ctx, 0.f, 0.f, v, NULL, bounds);
+ nvgText(ctx, float x, float y, const char* string, const char* end);
+ */
+ }
+}
+
+static inline void
+_lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
+{
+ if(n < 2)
+ return;
+
+ LV2_Canvas_Meth *p = A;
+
+ int i = -1;
+ int j = n;
+
+ while(true)
+ {
+ do {
+ i += 1;
+ } while(A[i].command < p->command);
+
+ do {
+ j -= 1;
+ } while(A[j].command > p->command);
+
+ if(i >= j)
+ break;
+
+ const LV2_Canvas_Meth tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+
+ _lv2_canvas_qsort(A, j + 1);
+ _lv2_canvas_qsort(A + j + 1, n - j - 1);
+}
+
+static inline LV2_Canvas_Meth *
+_lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
+{
+ LV2_Canvas_Meth *base = a;
+
+ for(int N = n, half; N > 1; N -= half)
+ {
+ half = N/2;
+ LV2_Canvas_Meth *dst = &base[half];
+ base = (dst->command > p) ? base : dst;
+ }
+
+ return (base->command == p) ? base : NULL;
+}
+
+static inline void
+lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
+{
+ lv2_canvas_urid_init(&canvas->urid, map);
+
+ unsigned ptr = 0;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
+ canvas->methods[ptr++].func = _lv2_canvas_render_arc;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
+ canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Style;
+ canvas->methods[ptr++].func = _lv2_canvas_render_style;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
+ canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
+ canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fill;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
+ canvas->methods[ptr++].func = _lv2_canvas_render_clip;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Save;
+ canvas->methods[ptr++].func = _lv2_canvas_render_save;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
+ canvas->methods[ptr++].func = _lv2_canvas_render_restore;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_translate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
+ canvas->methods[ptr++].func = _lv2_canvas_render_scale;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
+ canvas->methods[ptr++].func = _lv2_canvas_render_transform;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
+ canvas->methods[ptr++].func = _lv2_canvas_render_reset;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
+
+ assert(ptr == LV2_CANVAS_NUM_METHODS);
+
+ _lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
+}
+
+static inline bool
+lv2_canvas_render_body(LV2_Canvas *canvas, NVGcontext *ctx, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ LV2_Canvas_URID *urid = &canvas->urid;
+
+ if(!body || (type != urid->forge.Tuple) )
+ return false;
+
+ // save state
+ nvgSave(ctx);
+
+ // clear surface
+ nvgBeginPath(ctx);
+ nvgRect(ctx, 0, 0, 1.f, 1.f);
+ nvgFillColor(ctx, nvgRGBA(0x1e, 0x1e, 0x1e, 0xff));
+ nvgFill(ctx);
+
+ nvgFontSize(ctx, 0.1);
+ nvgStrokeWidth(ctx, 0.01);
+ nvgStrokeColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
+ nvgFillColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
+
+ LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
+ {
+ if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
+ const LV2_Atom *body = NULL;
+
+ lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
+
+ LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
+ canvas->methods, LV2_CANVAS_NUM_METHODS);
+
+ if(meth)
+ {
+ meth->func(ctx, urid, body);
+ }
+ }
+ }
+
+ // save state
+ nvgRestore(ctx);
+
+ return true;
+}
+
+static inline bool
+lv2_canvas_render(LV2_Canvas *canvas, NVGcontext *ctx, const LV2_Atom_Tuple *tup)
+{
+ return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
+ LV2_ATOM_BODY_CONST(&tup->atom));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LV2_CANVAS_RENDER_NANOVG_H
diff --git a/canvas.lv2/gitlab-ci/generic.yml b/canvas.lv2/gitlab-ci/generic.yml
new file mode 100644
index 0000000..5cd2abc
--- /dev/null
+++ b/canvas.lv2/gitlab-ci/generic.yml
@@ -0,0 +1,106 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS : ""
+
+.native_template: &native_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+
+ - scan-build --status-bugs meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.cross_template: &cross_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+# build
+.universal_linux_template_stretch: &universal_linux_definition_stretch
+ image: ventosus/universal-linux-gnu:stretch
+ <<: *cross_definition
+
+.universal_linux_template_buster: &universal_linux_definition_buster
+ image: ventosus/universal-linux-gnu:buster
+ <<: *native_definition
+
+.universal_linux_template_bullseye: &universal_linux_definition_bullseye
+ image: ventosus/universal-linux-gnu:bullseye
+ <<: *native_definition
+
+.arm_linux_template_stretch: &arm_linux_definition_stretch
+ image: ventosus/arm-linux-gnueabihf:stretch
+ <<: *cross_definition
+
+.arm_linux_template_buster: &arm_linux_definition_buster
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *cross_definition
+
+.arm_linux_template_bullseye: &arm_linux_definition_bullseye
+ image: ventosus/arm-linux-gnueabihf:bullseye
+ <<: *cross_definition
+
+# build
+x86_64-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+x86_64-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+x86_64-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+i686-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+i686-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+i686-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+arm-linux-gnueabihf-stretch:
+ <<: *arm_linux_definition_stretch
+
+arm-linux-gnueabihf-buster:
+ <<: *arm_linux_definition_buster
+
+arm-linux-gnueabihf-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+aarch64-linux-gnu-stretch:
+ <<: *arm_linux_definition_stretch
+
+aarch64-linux-gnu-buster:
+ <<: *arm_linux_definition_buster
+
+aarch64-linux-gnu-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+pack:
+ stage: deploy
+ script:
+ - echo 'packing up'
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/manifest.ttl.in b/manifest.ttl.in
new file mode 100644
index 0000000..9cf24e5
--- /dev/null
+++ b/manifest.ttl.in
@@ -0,0 +1,80 @@
+# Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+#
+# This is free software: you can redistribute it and/or modify
+# it under the terms of the Artistic License 2.0 as published by
+# The Perl Foundation.
+#
+# This source is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Artistic License 2.0 for more details.
+#
+# You should have received a copy of the Artistic License 2.0
+# along the source as a COPYING file. If not, obtain it from
+# http://www.perlfoundation.org/artistic_license_2_0.
+
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+
+@prefix canvas: <http://open-music-kontrollers.ch/lv2/canvas#> .
+@prefix monitors: <http://open-music-kontrollers.ch/lv2/monitors#> .
+
+# Audio Wave Mono
+monitors:audio_wave_mono
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <monitors@MODULE_SUFFIX@> ;
+ ui:ui canvas:display_ui ;
+ rdfs:seeAlso <monitors.ttl> .
+
+# Audio Wave Stereo
+monitors:audio_wave_stereo
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <monitors@MODULE_SUFFIX@> ;
+ ui:ui canvas:display_ui ;
+ rdfs:seeAlso <monitors.ttl> .
+
+# MIDI Pianoroll
+monitors:midi_pianoroll
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <monitors@MODULE_SUFFIX@> ;
+ ui:ui canvas:display_ui ;
+ rdfs:seeAlso <monitors.ttl> .
+
+# Time Metronom
+monitors:time_metronom
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <monitors@MODULE_SUFFIX@> ;
+ ui:ui canvas:display_ui ;
+ rdfs:seeAlso <monitors.ttl> .
+
+# UI
+canvas:display_ui
+ a ui:@UI_TYPE@ ;
+ ui:portNotification [
+ ui:plugin monitors:audio_wave_mono ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer ;
+ ] , [
+ ui:plugin monitors:audio_wave_stereo ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer ;
+ ] , [
+ ui:plugin monitors:midi_pianoroll ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer ;
+ ] , [
+ ui:plugin monitors:time_metronom ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer ;
+ ] .
diff --git a/meson.build b/meson.build
index d354d89..0f354bd 100644
--- a/meson.build
+++ b/meson.build
@@ -1,15 +1,25 @@
-project('props.lv2', 'c', default_options : [
+project('monitors.lv2', 'c', default_options : [
'buildtype=release',
'warning_level=3',
'werror=false',
'b_lto=false',
'c_std=c11'])
+build_root = meson.build_root()
add_project_arguments('-D_GNU_SOURCE', language : 'c')
conf_data = configuration_data()
cc = meson.get_compiler('c')
+build_idisp= get_option('build-inline-display')
+
+if build_idisp
+ add_project_arguments('-DBUILD_IDISP', language: 'c')
+ conf_data.set('BUILD_IDISP', '')
+else
+ conf_data.set('BUILD_IDISP', '#')
+endif
+
cp = find_program('cp')
lv2_validate = find_program('lv2_validate', native : true, required : false)
sord_validate = find_program('sord_validate', native : true, required : false)
@@ -18,21 +28,36 @@ clone = [cp, '@INPUT@', '@OUTPUT@']
m_dep = cc.find_library('m')
lv2_dep = dependency('lv2', version : '>=1.14.0')
+cairo_dep = dependency('cairo', version : '>=1.0.0',
+ static : meson.is_cross_build() and false, #FIXME
+ required: build_idisp)
-inc_dir = []
+canvas_inc = include_directories('canvas.lv2')
+timely_inc = include_directories('timely.lv2')
inst_dir = join_paths(get_option('libdir'), 'lv2', meson.project_name())
-dsp_srcs = [join_paths('test', 'props.c')]
-
+srcs = ['monitors.c',
+ 'monitors_audio_wave.c',
+ 'monitors_midi_pianoroll.c',
+ 'monitors_time_metronom.c']
+
c_args = ['-fvisibility=hidden',
'-ffast-math']
-mod = shared_module('props', dsp_srcs,
+if host_machine.system() == 'windows'
+ conf_data.set('UI_TYPE', 'WindowsUI')
+elif host_machine.system() == 'darwin'
+ conf_data.set('UI_TYPE', 'CocoaUI')
+else
+ conf_data.set('UI_TYPE', 'X11UI')
+endif
+
+mod = shared_module('monitors', srcs,
c_args : c_args,
- include_directories : inc_dir,
name_prefix : '',
- dependencies : [m_dep, lv2_dep],
+ include_directories : [canvas_inc, timely_inc],
+ dependencies : [m_dep, lv2_dep, cairo_dep],
install : true,
install_dir : inst_dir)
@@ -44,32 +69,16 @@ conf_data.set('MICRO_VERSION', version[2])
suffix = mod.full_path().strip().split('.')[-1]
conf_data.set('MODULE_SUFFIX', '.' + suffix)
-manifest_ttl = configure_file(
- input : join_paths('test', 'manifest.ttl.in'), output : 'manifest.ttl',
+manifest_ttl = configure_file(input : 'manifest.ttl.in', output : 'manifest.ttl',
configuration : conf_data,
install : true,
install_dir : inst_dir)
-dsp_ttl = custom_target('props_ttl',
- input : join_paths('test', 'props.ttl'),
- output : 'props.ttl',
- command : clone,
- install : true,
- install_dir : inst_dir)
-custom_target('chunk_bin',
- input : join_paths('test', 'chunk.bin'),
- output : 'chunk.bin',
- command : clone,
+
+dsp_ttl = configure_file(input : 'monitors.ttl.in', output : 'monitors.ttl',
+ configuration : conf_data,
install : true,
install_dir : inst_dir)
-props_test = executable('props_test',
- join_paths('test', 'props_test.c'),
- c_args : c_args,
- install : false)
-
-test('Test', props_test,
- timeout : 240)
-
if lv2_validate.found() and sord_validate.found()
test('LV2 validate', lv2_validate,
args : [manifest_ttl, dsp_ttl])
@@ -77,6 +86,9 @@ endif
if lv2lint.found()
test('LV2 lint', lv2lint,
- args : ['-Ewarn',
- 'http://open-music-kontrollers.ch/lv2/props#test'])
+ args : ['-Ewarn', '-I', join_paths(build_root, ''),
+ 'http://open-music-kontrollers.ch/lv2/monitors#audio_wave_mono',
+ 'http://open-music-kontrollers.ch/lv2/monitors#audio_wave_stereo',
+ 'http://open-music-kontrollers.ch/lv2/monitors#midi_pianoroll',
+ 'http://open-music-kontrollers.ch/lv2/monitors#time_metronom'])
endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..dca36c1
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1 @@
+option('build-inline-display', type : 'boolean', value : false)
diff --git a/monitors.c b/monitors.c
new file mode 100644
index 0000000..160b471
--- /dev/null
+++ b/monitors.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <monitors.h>
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &monitors_audio_wave_mono;
+ case 1:
+ return &monitors_audio_wave_stereo;
+ case 2:
+ return &monitors_midi_pianoroll;
+ case 3:
+ return &monitors_time_metronom;
+
+ default:
+ return NULL;
+ }
+}
diff --git a/monitors.h b/monitors.h
new file mode 100644
index 0000000..320cdd1
--- /dev/null
+++ b/monitors.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MONITORS_LV2_H
+#define _MONITORS_LV2_H
+
+#include <stdint.h>
+#if !defined(_WIN32)
+# include <sys/mman.h>
+#else
+# define mlock(...)
+# define munlock(...)
+#endif
+
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+
+#define MONITORS_URI "http://open-music-kontrollers.ch/lv2/monitors"
+#define MONITORS_PREFIX MONITORS_URI"#"
+
+// plugin uris
+#define MONITORS__audio_wave_mono MONITORS_PREFIX"audio_wave_mono"
+#define MONITORS__audio_wave_stereo MONITORS_PREFIX"audio_wave_stereo"
+#define MONITORS__midi_pianoroll MONITORS_PREFIX"midi_pianoroll"
+#define MONITORS__time_metronom MONITORS_PREFIX"time_metronom"
+
+// paramer uris
+#define MONITORS__window MONITORS_PREFIX"window"
+#define MONITORS__channel MONITORS_PREFIX"channel"
+#define MONITORS__rotation MONITORS_PREFIX"rotation"
+#define MONITORS__horizontalFlip MONITORS_PREFIX"horizontalFlip"
+#define MONITORS__verticalFlip MONITORS_PREFIX"verticalFlip"
+
+extern const LV2_Descriptor monitors_audio_wave_mono;
+extern const LV2_Descriptor monitors_audio_wave_stereo;
+extern const LV2_Descriptor monitors_midi_pianoroll;
+extern const LV2_Descriptor monitors_time_metronom;
+
+typedef struct _craft_t craft_t;
+
+struct _craft_t {
+ union {
+ LV2_Atom_Sequence *seq;
+ uint8_t *buf;
+ };
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge_Ref ref;
+};
+
+static inline void
+_craft_init(craft_t *craft, LV2_URID_Map *map)
+{
+ lv2_atom_forge_init(&craft->forge, map);
+}
+
+static inline void
+_craft_connect(craft_t *craft, LV2_Atom_Sequence *seq)
+{
+ craft->seq = seq;
+}
+
+static inline void
+_craft_in(craft_t *craft)
+{
+ const uint32_t capacity = craft->seq->atom.size;
+ lv2_atom_forge_set_buffer(&craft->forge, craft->buf, capacity);
+ craft->ref = lv2_atom_forge_sequence_head(&craft->forge, &craft->frame, 0);
+}
+
+static inline void
+_craft_clear(craft_t *craft)
+{
+ craft->ref = 0;
+ lv2_atom_sequence_clear(craft->seq);
+}
+
+static inline void
+_craft_out(craft_t *craft)
+{
+ if(craft->ref)
+ lv2_atom_forge_pop(&craft->forge, &craft->frame);
+ else
+ _craft_clear(craft);
+}
+
+#endif // _MONITORS_LV2_H
diff --git a/monitors.ttl.in b/monitors.ttl.in
new file mode 100644
index 0000000..e28a406
--- /dev/null
+++ b/monitors.ttl.in
@@ -0,0 +1,361 @@
+# Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+#
+# This is free software: you can redistribute it and/or modify
+# it under the terms of the Artistic License 2.0 as published by
+# The Perl Foundation.
+#
+# This source is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Artistic License 2.0 for more details.
+#
+# You should have received a copy of the Artistic License 2.0
+# along the source as a COPYING file. If not, obtain it from
+# http://www.perlfoundation.org/artistic_license_2_0.
+
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix log: <http://lv2plug.in/ns/ext/log#> .
+@prefix state: <http://lv2plug.in/ns/ext/state#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix bufsz: <http://lv2plug.in/ns/ext/buf-size#> .
+@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix ui: <http://lv2plug.in/ns/extentions/ui#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix idisp: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
+
+@prefix lic: <http://opensource.org/licenses/> .
+@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix monitors: <http://open-music-kontrollers.ch/lv2/monitors#> .
+@prefix canvas: <http://open-music-kontrollers.ch/lv2/canvas#> .
+
+# to please sord_validate
+idisp:queue_draw
+ a lv2:Feature .
+idisp:interface
+ a lv2:ExtensionData .
+
+ui:updateRate
+ a opts:Option .
+
+# Maintainer
+omk:me
+ a foaf:Person ;
+ foaf:name "Hanspeter Portner" ;
+ foaf:mbox <mailto:dev@open-music-kontrollers.ch> ;
+ foaf:homepage <http://open-music-kontrollers.ch> .
+
+# Project
+proj:monitors
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Router Bundle" .
+
+# Parameters
+canvas:graph
+ a lv2:Parameter ;
+ rdfs:label "Graph" ;
+ rdfs:comment "set canvas graph" ;
+ rdfs:range atom:Tuple .
+canvas:aspectRatio
+ a lv2:Parameter ;
+ rdfs:label "Aspect ratio" ;
+ rdfs:comment "set aspect ratio" ;
+ rdfs:range atom:Float ;
+ lv2:minimum 0.25 ;
+ lv2:maximum 4.0 .
+monitors:rotation
+ a lv2:Parameter ;
+ rdfs:label "Rotation" ;
+ rdfs:comment "set rotation" ;
+ rdfs:range atom:Float ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 360.0 ;
+ units:unit units:degree .
+monitors:horizontalFlip
+ a lv2:Parameter ;
+ rdfs:label "Horizontal flip" ;
+ rdfs:comment "toggle horizontal flip" ;
+ rdfs:range atom:Bool .
+monitors:verticalFlip
+ a lv2:Parameter ;
+ rdfs:label "Vertical flip" ;
+ rdfs:comment "toggle vertical flip" ;
+ rdfs:range atom:Bool .
+monitors:window
+ a lv2:Parameter ;
+ rdfs:label "Window" ;
+ rdfs:comment "set window" ;
+ rdfs:range atom:Int ;
+ lv2:minimum 100 ;
+ lv2:maximum 10000 ;
+ units:unit units:ms .
+monitors:channel
+ a lv2:Parameter ;
+ rdfs:label "Channel" ;
+ rdfs:comment "set channel" ;
+ rdfs:range atom:Int ;
+ lv2:minimum 0 ;
+ lv2:maximum 15 .
+
+# Audio Wave Mono
+monitors:audio_wave_mono
+ a lv2:Plugin ,
+ lv2:AnalyserPlugin ;
+ doap:name "Monitors Audio Wave Mono" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:monitors ;
+ lv2:requiredFeature urid:map, state:loadDefaultState, opts:options ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+ @BUILD_IDISP@lv2:optionalFeature idisp:queue_draw ;
+ @BUILD_IDISP@lv2:extensionData idisp:interface ;
+ opts:requiredOption ui:updateRate ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:AudioPort ;
+ lv2:index 0 ;
+ lv2:symbol "audio_in" ;
+ lv2:name "Audio In" ;
+ ] , [
+ a lv2:OutputPort,
+ lv2:AudioPort ;
+ lv2:index 1 ;
+ lv2:symbol "audio_out" ;
+ lv2:name "Audio Out" ;
+ ] , [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 2 ;
+ lv2:symbol "control" ;
+ lv2:name "Control" ;
+ lv2:designation lv2:control ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 3 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ rsz:minimumSize 131072 ;
+ ] ;
+
+ patch:readable
+ canvas:graph ;
+
+ patch:writable
+ canvas:aspectRatio ,
+ monitors:rotation ,
+ monitors:horizontalFlip ,
+ monitors:verticalFlip ,
+ monitors:window ;
+
+ state:state [
+ canvas:aspectRatio "1.6"^^xsd:float ;
+ monitors:rotation "0.0"^^xsd:float ;
+ monitors:horizontalFlip false ;
+ monitors:verticalFlip false ;
+ monitors:window "1000"^^xsd:int ;
+ ] .
+
+# Audio Wave Stereo
+monitors:audio_wave_stereo
+ a lv2:Plugin ,
+ lv2:AnalyserPlugin ;
+ doap:name "Monitors Audio Wave Stereo" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:monitors ;
+ lv2:requiredFeature urid:map, state:loadDefaultState, opts:options ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+ @BUILD_IDISP@lv2:optionalFeature idisp:queue_draw ;
+ @BUILD_IDISP@lv2:extensionData idisp:interface ;
+ opts:requiredOption ui:updateRate ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:AudioPort ;
+ lv2:index 0 ;
+ lv2:symbol "audio_in_1" ;
+ lv2:name "Audio In 1" ;
+ ] , [
+ a lv2:InputPort ,
+ lv2:AudioPort ;
+ lv2:index 1 ;
+ lv2:symbol "audio_in_2" ;
+ lv2:name "Audio In 2" ;
+ ] , [
+ a lv2:OutputPort,
+ lv2:AudioPort ;
+ lv2:index 2 ;
+ lv2:symbol "audio_out_1" ;
+ lv2:name "Audio Out 1" ;
+ ] , [
+ a lv2:OutputPort,
+ lv2:AudioPort ;
+ lv2:index 3 ;
+ lv2:symbol "audio_out_2" ;
+ lv2:name "Audio Out 2" ;
+ ] , [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 4 ;
+ lv2:symbol "control" ;
+ lv2:name "Control" ;
+ lv2:designation lv2:control ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 5 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ rsz:minimumSize 131072 ;
+ ] ;
+
+ patch:readable
+ canvas:graph ;
+
+ patch:writable
+ canvas:aspectRatio ,
+ monitors:rotation ,
+ monitors:horizontalFlip ,
+ monitors:verticalFlip ,
+ monitors:window ;
+
+ state:state [
+ canvas:aspectRatio "1.6"^^xsd:float ;
+ monitors:rotation "0.0"^^xsd:float ;
+ monitors:horizontalFlip false ;
+ monitors:verticalFlip false ;
+ monitors:window "1000"^^xsd:int ;
+ ] .
+
+# MIDI Pianoroll
+monitors:midi_pianoroll
+ a lv2:Plugin ,
+ lv2:AnalyserPlugin ;
+ doap:name "Monitors MIDI Pianoroll" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:monitors ;
+ lv2:requiredFeature urid:map, state:loadDefaultState, opts:options ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+ @BUILD_IDISP@lv2:optionalFeature idisp:queue_draw ;
+ @BUILD_IDISP@lv2:extensionData idisp:interface ;
+ opts:requiredOption ui:updateRate ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ,
+ midi:MidiEvent ;
+ lv2:index 0 ;
+ lv2:symbol "control" ;
+ lv2:name "Control" ;
+ lv2:designation lv2:control ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ,
+ midi:MidiEvent ;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ rsz:minimumSize 131072 ;
+ ] ;
+
+ patch:readable
+ canvas:graph ;
+
+ patch:writable
+ canvas:aspectRatio ,
+ monitors:rotation ,
+ monitors:horizontalFlip ,
+ monitors:verticalFlip ,
+ monitors:window ,
+ monitors:channel ;
+
+ state:state [
+ canvas:aspectRatio "1.0"^^xsd:float ;
+ monitors:rotation "0.0"^^xsd:float ;
+ monitors:horizontalFlip false ;
+ monitors:verticalFlip false ;
+ monitors:window "1000"^^xsd:int ;
+ monitors:channel 0 ;
+ ] .
+
+# Time Metronom
+monitors:time_metronom
+ a lv2:Plugin ,
+ lv2:AnalyserPlugin ;
+ doap:name "Monitors Time Metronom" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:monitors ;
+ lv2:requiredFeature urid:map, state:loadDefaultState, opts:options ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+ @BUILD_IDISP@lv2:optionalFeature idisp:queue_draw ;
+ @BUILD_IDISP@lv2:extensionData idisp:interface ;
+ opts:requiredOption ui:updateRate ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ,
+ midi:MidiEvent ;
+ lv2:index 0 ;
+ lv2:symbol "control" ;
+ lv2:name "Control" ;
+ lv2:designation lv2:control ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ,
+ midi:MidiEvent ;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ rsz:minimumSize 131072 ;
+ ] ;
+
+ patch:readable
+ canvas:graph ;
+
+ patch:writable
+ canvas:aspectRatio ,
+ monitors:rotation ,
+ monitors:horizontalFlip ,
+ monitors:verticalFlip ;
+
+ state:state [
+ canvas:aspectRatio "1.0"^^xsd:float ;
+ monitors:rotation "0.0"^^xsd:float ;
+ monitors:horizontalFlip false ;
+ monitors:verticalFlip false ;
+ ] .
diff --git a/monitors_audio_wave.c b/monitors_audio_wave.c
new file mode 100644
index 0000000..bb263c9
--- /dev/null
+++ b/monitors_audio_wave.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <monitors.h>
+
+#include <props.lv2/props.h>
+#include <canvas.lv2/canvas.h>
+#include <canvas.lv2/forge.h>
+
+#ifdef BUILD_IDISP
+# include <canvas.lv2/idisp.h>
+#endif
+
+#define MAX_CHANNELS 2
+#define MAX_SAMPLES 512
+#define MAX_GRAPH 0x20000 //FIXME actually measure this
+#define MAX_NPROPS 6
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _plughandle_t plughandle_t;
+typedef struct _point_t point_t;
+typedef struct _item_t item_t;
+typedef struct _wave_t wave_t;
+
+struct _plugstate_t {
+ int32_t window;
+ float rotation;
+ int32_t hflip;
+ int32_t vflip;
+ float aspect_ratio;
+ uint8_t graph [MAX_GRAPH];
+};
+
+struct _point_t {
+ float x;
+ float y;
+} __attribute__((packed));
+
+
+struct _item_t {
+ point_t tail;
+ point_t samples [MAX_SAMPLES];
+ point_t head;
+} __attribute__((packed));
+
+struct _wave_t {
+ item_t upper;
+ item_t lower;
+} __attribute__((packed));
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ const float *audio_in [MAX_CHANNELS];
+ float *audio_out [MAX_CHANNELS];
+ const LV2_Atom_Sequence *control;
+ craft_t notify;
+
+#ifdef BUILD_IDISP
+ LV2_Canvas_Idisp idisp;
+#endif
+ LV2_Canvas_URID urid;
+ double sample_rate;
+ float update_rate;
+ uint32_t graph_size;
+
+ uint32_t max_window;
+ uint32_t spf;
+ uint32_t thresh;
+ uint32_t cnt;
+
+ unsigned nchannels;
+ wave_t wave [MAX_CHANNELS];
+ float min [MAX_CHANNELS];
+ float max [MAX_CHANNELS];
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ PROPS_T(props, MAX_NPROPS);
+};
+
+static void
+_fill(plughandle_t *handle)
+{
+ const float dx = 1.f / MAX_SAMPLES;
+ const float dy = 1.f / handle->nchannels;
+
+ for(unsigned idx = 0; idx < handle->nchannels; idx++)
+ {
+ //FIXME fix for nchannels
+ wave_t *wave = &handle->wave[idx];
+ item_t *upper = &wave->upper;
+ item_t *lower = &wave->lower;
+
+ const float y0 = (0.5f + idx) * dy;
+
+ upper->tail.x = 0.f;
+ upper->tail.y = y0;
+
+ lower->tail.x = 0.f;
+ lower->tail.y = y0;
+
+ for(unsigned i = 0; i < MAX_SAMPLES; i++)
+ {
+ point_t *pu = &upper->samples[i];
+ point_t *pl = &lower->samples[i];
+
+ pu->x = pl->x = i * dx;
+ pu->y = pl->y = y0;
+ }
+
+ upper->head.x = 1.f;
+ upper->head.y = y0;
+
+ lower->head.x = 1.f;
+ lower->head.y = y0;
+ }
+}
+
+static inline void
+_out_of_memory(plughandle_t *handle)
+{
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "out-of-memory\n");
+ }
+
+ _craft_clear(&handle->notify);
+}
+
+static void
+_render(plughandle_t *handle)
+{
+ LV2_Atom_Forge _forge = handle->notify.forge; // clone forge object
+ LV2_Atom_Forge *forge = &_forge;
+ LV2_Canvas_URID *urid = &handle->urid;
+ LV2_Atom_Forge_Frame frame;
+
+ lv2_atom_forge_set_buffer(forge, handle->state.graph, MAX_GRAPH);
+
+ if( !lv2_atom_forge_tuple(forge, &frame)
+ || !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_rectangle(forge, urid, 0.f, 0.f, 1.f, 1.f)
+ || !lv2_canvas_forge_style(forge, urid, 0x0000003f)
+ || !lv2_canvas_forge_fill(forge, urid)
+ || !lv2_canvas_forge_save(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ if(handle->state.hflip || handle->state.vflip)
+ {
+ const float xx = handle->state.hflip ? -1.f : 1.f;
+ const float xy = 0.f;
+ const float x0 = handle->state.hflip ? 1.f : 0.f;
+
+ const float yy = handle->state.vflip ? -1.f : 1.f;
+ const float yx = 0.f;
+ const float y0 = handle->state.vflip ? 1.f : 0.f;
+
+ if( !lv2_canvas_forge_transform(forge, urid, xx, xy, x0, yy, yx, y0) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ if( (fmod(handle->state.rotation, 360.f) != 0.f) )
+ {
+ const float rot = handle->state.rotation / 180.f * M_PI;
+
+ if( !lv2_canvas_forge_translate(forge, urid, 0.5f, 0.5f)
+ || !lv2_canvas_forge_rotate(forge, urid, rot)
+ || !lv2_canvas_forge_translate(forge, urid, -0.5f, -0.5f) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ const float dy = 1.f / handle->nchannels;
+
+ for(unsigned idx = 0; idx < handle->nchannels; idx++)
+ {
+ wave_t *wave = &handle->wave[idx];
+
+ const float y0 = (0.5f + idx) * dy;
+
+ const float base [4] = {
+ 0.f, y0,
+ 1.f, y0
+ };
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_polyLine(forge, urid, sizeof(item_t)/sizeof(float), &wave->upper.tail.x)
+ || !lv2_canvas_forge_style(forge, urid, 0xff7f00ff)
+ || !lv2_canvas_forge_closePath(forge, urid)
+ || !lv2_canvas_forge_fill(forge, urid)
+
+ || !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_polyLine(forge, urid, sizeof(item_t)/sizeof(float), &wave->lower.tail.x)
+ || !lv2_canvas_forge_style(forge, urid, 0xff7f009f)
+ || !lv2_canvas_forge_closePath(forge, urid)
+ || !lv2_canvas_forge_fill(forge, urid)
+
+ || !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_polyLine(forge, urid, 4, base)
+ || !lv2_canvas_forge_lineWidth(forge, urid, 2.f / MAX_SAMPLES)
+ || !lv2_canvas_forge_style(forge, urid, 0xffffffff)
+ || !lv2_canvas_forge_stroke(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ if( !lv2_canvas_forge_restore(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ lv2_atom_forge_pop(forge, &frame);
+
+ props_impl_t *impl = _props_impl_get(&handle->props, handle->urid.Canvas_graph);
+ if(impl)
+ {
+ const LV2_Atom *value= (const LV2_Atom *)handle->state.graph;
+
+ _props_impl_set(&handle->props, impl, value->type, value->size,
+ LV2_ATOM_BODY_CONST(value));
+ }
+
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_queue_draw(&handle->idisp);
+#endif
+}
+
+static void
+_intercept_window(void *data, int64_t frames __attribute__((unused)),
+ props_impl_t *impl __attribute__((unused)))
+{
+ plughandle_t *handle = data;
+
+ const double spw = 1e-3 * handle->sample_rate * handle->state.window;
+ handle->max_window = ceil(spw / MAX_SAMPLES);
+
+ _fill(handle);
+ _render(handle);
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = MONITORS__window,
+ .offset = offsetof(plugstate_t, window),
+ .type = LV2_ATOM__Int,
+ .event_cb = _intercept_window
+ },
+ {
+ .property = MONITORS__rotation,
+ .offset = offsetof(plugstate_t, rotation),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = MONITORS__horizontalFlip,
+ .offset = offsetof(plugstate_t, hflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .property = MONITORS__verticalFlip,
+ .offset = offsetof(plugstate_t, vflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .access = LV2_PATCH__readable,
+ .property = CANVAS__graph,
+ .offset = offsetof(plugstate_t, graph),
+ .type = LV2_ATOM__Tuple,
+ .max_size = MAX_GRAPH
+ },
+ {
+ .property = CANVAS__aspectRatio,
+ .offset = offsetof(plugstate_t, aspect_ratio),
+ .type = LV2_ATOM__Float
+ }
+};
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ return NULL;
+ mlock(handle, sizeof(plughandle_t));
+
+ if(!strcmp(descriptor->URI, MONITORS__audio_wave_mono))
+ handle->nchannels = 1;
+ else if(!strcmp(descriptor->URI, MONITORS__audio_wave_stereo))
+ handle->nchannels = 2;
+
+ LV2_Options_Option *opts = NULL;
+#ifdef BUILD_IDISP
+ LV2_Inline_Display *queue_draw = NULL;
+#endif
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
+ opts = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ handle->log = features[i]->data;
+#ifdef BUILD_IDISP
+ else if(!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw))
+ queue_draw = features[i]->data;
+#endif
+ }
+
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(!opts)
+ {
+ fprintf(stderr,
+ "%s: Host does not support opts:options\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(handle->log)
+ {
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+ }
+
+ _craft_init(&handle->notify, handle->map);
+ lv2_canvas_urid_init(&handle->urid, handle->map);
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_init(&handle->idisp, queue_draw, handle->map);
+#endif
+
+ const LV2_URID ui_update_rate= handle->map->map(handle->map->handle,
+ LV2_UI__updateRate);
+
+ handle->update_rate = 60.f; // fall-back
+ handle->sample_rate = rate;
+
+ for(LV2_Options_Option *opt = opts;
+ opt && (opt->key != 0) && (opt->value != NULL);
+ opt++)
+ {
+ if( (opt->key == ui_update_rate) && (opt->type == handle->notify.forge.Float) )
+ handle->update_rate = *(float*)opt->value;
+ }
+
+ handle->spf = handle->sample_rate / handle->update_rate;
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ _fill(handle);
+ _render(handle);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ switch(handle->nchannels)
+ {
+ case 1:
+ {
+ switch(port)
+ {
+ case 0:
+ handle->audio_in[0] = (const void *)data;
+ break;
+
+ case 1:
+ handle->audio_out[0] = data;
+ break;
+
+ case 2:
+ handle->control = (const void *)data;
+ break;
+ case 3:
+ _craft_connect(&handle->notify, data);
+ break;
+ }
+ } break;
+ case 2:
+ {
+ switch(port)
+ {
+ case 0:
+ handle->audio_in[0] = (const void *)data;
+ break;
+ case 1:
+ handle->audio_in[1] = (const void *)data;
+ break;
+
+ case 2:
+ handle->audio_out[0] = data;
+ break;
+ case 3:
+ handle->audio_out[1] = data;
+ break;
+
+ case 4:
+ handle->control = (const void *)data;
+ break;
+ case 5:
+ _craft_connect(&handle->notify, data);
+ break;
+ }
+ } break;
+ }
+}
+
+static void
+_insert(plughandle_t *handle, unsigned idx, float min, float max)
+{
+ wave_t *wave = &handle->wave[idx];
+
+ for(uint32_t i = 0; i < MAX_SAMPLES - 1; i++)
+ {
+ wave->upper.samples[i].y = wave->upper.samples[i+1].y;
+ wave->lower.samples[i].y = wave->lower.samples[i+1].y;
+ }
+
+ const float dy = 1.f / handle->nchannels;
+
+ wave->upper.samples[MAX_SAMPLES-1].y = (0.5f + idx + min)*dy;
+ wave->lower.samples[MAX_SAMPLES-1].y = (0.5f + idx + max)*dy;
+}
+
+static void
+_run(plughandle_t *handle, uint32_t from, uint32_t to)
+{
+ const uint32_t num = (to - from);
+ const size_t sz = num * sizeof(float);
+
+ for(unsigned idx = 0; idx < handle->nchannels; idx++)
+ {
+ memmove(&handle->audio_out[idx][from], &handle->audio_in[idx][from], sz);
+
+ for(uint32_t i = 0; i < num; i++, handle->cnt++, handle->thresh++)
+ {
+ const float val = handle->audio_in[idx][from + i];
+
+ if(val < handle->min[idx])
+ {
+ handle->min[idx]= val;
+ }
+ else if(val > handle->max[idx])
+ {
+ handle->max[idx] = val;
+ }
+
+ if(handle->cnt >= handle->max_window)
+ {
+ _insert(handle, idx, handle->min[idx], handle->max[idx]);
+
+ handle->min[idx] = 0.f;
+ handle->max[idx] = 0.f;
+ handle->cnt = 0;
+ }
+
+ if(handle->thresh >= handle->spf)
+ {
+ handle->thresh = 0;
+
+ _render(handle);
+ props_set(&handle->props, &handle->notify.forge, from + i,
+ handle->urid.Canvas_graph, &handle->notify.ref);
+ }
+ }
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+
+ _craft_in(&handle->notify);
+
+ props_idle(&handle->props, &handle->notify.forge, 0, &handle->notify.ref);
+
+ int64_t from = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
+ {
+ const LV2_Atom *atom = &ev->body;
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
+
+ props_advance(&handle->props, &handle->notify.forge, ev->time.frames, obj, &handle->notify.ref);
+
+ const int64_t to = ev->time.frames;
+
+ _run(handle, from, to);
+ from = to;
+ }
+
+ {
+ const int64_t to = nsamples;
+
+ _run(handle, from, to);
+ }
+
+ _craft_out(&handle->notify);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+#ifdef BUILD_IDISP
+static LV2_Inline_Display_Image_Surface *
+_idisp_render(LV2_Handle instance, uint32_t w, uint32_t h)
+{
+ plughandle_t *handle = instance;
+
+ float aspect_ratio = 1.f;
+
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_aspectRatio);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ memcpy(&aspect_ratio, impl->stash.body, impl->stash.size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ LV2_Inline_Display_Image_Surface *surf =
+ lv2_canvas_idisp_surf_configure(&handle->idisp, w, h, aspect_ratio);
+
+ if(surf)
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_graph);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ lv2_canvas_idisp_render_body(&handle->idisp, impl->type, impl->stash.size,
+ impl->stash.body);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ return surf;
+}
+
+static const LV2_Inline_Display_Interface idisp_iface = {
+ .render = _idisp_render
+};
+#endif
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ munlock(handle, sizeof(plughandle_t));
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_deinit(&handle->idisp);
+#endif
+ free(handle);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+#ifdef BUILD_IDISP
+ else if(!strcmp(uri, LV2_INLINEDISPLAY__interface))
+ return &idisp_iface;
+#endif
+
+ return NULL;
+}
+
+const LV2_Descriptor monitors_audio_wave_mono = {
+ .URI = MONITORS__audio_wave_mono,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
+
+const LV2_Descriptor monitors_audio_wave_stereo = {
+ .URI = MONITORS__audio_wave_stereo,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
diff --git a/monitors_midi_pianoroll.c b/monitors_midi_pianoroll.c
new file mode 100644
index 0000000..04cd841
--- /dev/null
+++ b/monitors_midi_pianoroll.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <monitors.h>
+
+#include <props.lv2/props.h>
+#include <canvas.lv2/canvas.h>
+#include <canvas.lv2/forge.h>
+
+#ifdef BUILD_IDISP
+# include <canvas.lv2/idisp.h>
+#endif
+
+#define MAX_GRAPH 0x20000 //FIXME actually measure this
+#define MAX_NPROPS 7
+#define MAX_KEYS 0x80
+#define MAX_NOTES 0x200
+#define MAX_VELOCITY 0x80
+#define MASK_KEYS (MAX_KEYS - 1)
+#define MASK_NOTES (MAX_NOTES - 1)
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _plughandle_t plughandle_t;
+typedef struct _active_note_t active_note_t;
+typedef struct _passive_note_t passive_note_t;
+
+struct _active_note_t {
+ int64_t start;
+ uint32_t style;
+};
+
+struct _passive_note_t {
+ int64_t start;
+ int64_t end;
+ uint32_t style;
+};
+
+struct _plugstate_t {
+ int32_t window;
+ float rotation;
+ int32_t hflip;
+ int32_t vflip;
+ float aspect_ratio;
+ uint8_t graph [MAX_GRAPH];
+ int32_t channel;
+};
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ const LV2_Atom_Sequence *control;
+ craft_t notify;
+
+ LV2_URID midi_MidiEvent;
+
+#ifdef BUILD_IDISP
+ LV2_Canvas_Idisp idisp;
+#endif
+ LV2_Canvas_URID urid;
+ double sample_rate;
+ float update_rate;
+ uint32_t graph_size;
+ unsigned range;
+ float range_1;
+
+ unsigned spf;
+ unsigned thresh;
+ int64_t cnt;
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ active_note_t actives [MAX_KEYS];
+ passive_note_t passives [MAX_KEYS][MAX_NOTES];
+ unsigned offsets [MAX_KEYS];
+ uint32_t palette [MAX_VELOCITY];
+
+ PROPS_T(props, MAX_NPROPS);
+};
+
+static const uint32_t palette_colors [] = {
+ 0x00ffffff,
+ 0x00ff00ff,
+ 0xffff00ff,
+ 0xff0000ff
+};
+
+static inline void
+_out_of_memory(plughandle_t *handle)
+{
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "out-of-memory\n");
+ }
+
+ _craft_clear(&handle->notify);
+}
+
+static inline void
+_clear_state(plughandle_t *handle)
+{
+ memset(handle->actives, 0x0, sizeof(handle->actives));
+ memset(handle->passives, 0x0, sizeof(handle->passives));
+}
+
+static void
+_render(plughandle_t *handle, int64_t frames)
+{
+ LV2_Atom_Forge _forge = handle->notify.forge; // clone forge object
+ LV2_Atom_Forge *forge = &_forge;
+ LV2_Canvas_URID *urid = &handle->urid;
+ LV2_Atom_Forge_Frame frame;
+
+ const float dy = 1.f / MASK_KEYS;
+ lv2_atom_forge_set_buffer(forge, handle->state.graph, MAX_GRAPH);
+
+ if( !lv2_atom_forge_tuple(forge, &frame)
+ || !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_rectangle(forge, urid, 0.f, 0.f, 1.f, 1.f)
+ || !lv2_canvas_forge_style(forge, urid, 0x0000003f)
+ || !lv2_canvas_forge_fill(forge, urid)
+ || !lv2_canvas_forge_lineWidth(forge, urid, dy)
+ || !lv2_canvas_forge_save(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ if(handle->state.hflip || handle->state.vflip)
+ {
+ const float xx = handle->state.hflip ? -1.f : 1.f;
+ const float xy = 0.f;
+ const float x0 = handle->state.hflip ? 1.f : 0.f;
+
+ const float yy = handle->state.vflip ? -1.f : 1.f;
+ const float yx = 0.f;
+ const float y0 = handle->state.vflip ? 1.f : 0.f;
+
+ if( !lv2_canvas_forge_transform(forge, urid, xx, xy, x0, yy, yx, y0) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ if( (fmod(handle->state.rotation, 360.f) != 0.f) )
+ {
+ const float rot = handle->state.rotation / 180.f * M_PI;
+
+ if( !lv2_canvas_forge_translate(forge, urid, 0.5f, 0.5f)
+ || !lv2_canvas_forge_rotate(forge, urid, rot)
+ || !lv2_canvas_forge_translate(forge, urid, -0.5f, -0.5f) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ const unsigned range = handle->range;
+ const float range_1 = handle->range_1;
+ const int64_t head = handle->cnt + frames;
+ const int64_t tail = head - range;
+
+ float y = 1.f - dy*0.5f;
+ for(unsigned i = 0; i < MAX_KEYS; i++, y -= dy)
+ {
+ active_note_t *active = &handle->actives[i];
+ passive_note_t *passives = handle->passives[i];
+
+ if(active->start != 0)
+ {
+ const bool start_in_window = (active->start >= tail) && (active->start <= head);
+ const float x0 = start_in_window
+ ? range_1 * (active->start - tail)
+ : 0.f;
+ const float x1 = 1.f;
+ const float line [] = {
+ x0, y,
+ x1, y
+ };
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_polyLine(forge, urid, 4, line)
+ || !lv2_canvas_forge_style(forge, urid, active->style)
+ || !lv2_canvas_forge_stroke(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+
+ for(unsigned j = 0; j < MAX_NOTES; j++)
+ {
+ const unsigned idx = (handle->offsets[i] + j) & MASK_NOTES;
+ passive_note_t *passive = &passives[idx];
+
+ if(passive->start == 0)
+ {
+ break; // skip this key
+ }
+
+ const bool end_in_window = (passive->end >= tail) && (passive->end <= head);
+
+ if(end_in_window)
+ {
+ const bool start_in_window = (passive->start >= tail) && (passive->start <= head);
+ const float x0 = start_in_window
+ ? range_1 * (passive->start - tail)
+ : 0.f;
+ const float x1 = end_in_window
+ ? range_1 * (passive->end - tail)
+ : 1.f;
+ const float line [] = {
+ x0, y,
+ x1, y
+ };
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_polyLine(forge, urid, 4, line)
+ || !lv2_canvas_forge_style(forge, urid, passive->style)
+ || !lv2_canvas_forge_stroke(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+ }
+ else
+ {
+ passive->start = 0; // invalidate
+ }
+ }
+ }
+
+ if( !lv2_canvas_forge_restore(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ lv2_atom_forge_pop(forge, &frame);
+
+ props_impl_t *impl = _props_impl_get(&handle->props, handle->urid.Canvas_graph);
+ if(impl)
+ {
+ const LV2_Atom *value= (const LV2_Atom *)handle->state.graph;
+
+ _props_impl_set(&handle->props, impl, value->type, value->size,
+ LV2_ATOM_BODY_CONST(value));
+ }
+
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_queue_draw(&handle->idisp);
+#endif
+}
+
+static void
+_intercept_window(void *data, int64_t frames,
+ props_impl_t *impl __attribute__((unused)))
+{
+ plughandle_t *handle = data;
+
+ handle->range = 1e-3 * handle->sample_rate * handle->state.window;
+ handle->range_1 = 1.f / handle->range;
+
+ _clear_state(handle);
+ _render(handle, frames);
+}
+
+static void
+_intercept_channel(void *data, int64_t frames,
+ props_impl_t *impl __attribute__((unused)))
+{
+ plughandle_t *handle = data;
+
+ _clear_state(handle);
+ _render(handle, frames);
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = MONITORS__window,
+ .offset = offsetof(plugstate_t, window),
+ .type = LV2_ATOM__Int,
+ .event_cb = _intercept_window
+ },
+ {
+ .property = MONITORS__rotation,
+ .offset = offsetof(plugstate_t, rotation),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = MONITORS__horizontalFlip,
+ .offset = offsetof(plugstate_t, hflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .property = MONITORS__verticalFlip,
+ .offset = offsetof(plugstate_t, vflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .access = LV2_PATCH__readable,
+ .property = CANVAS__graph,
+ .offset = offsetof(plugstate_t, graph),
+ .type = LV2_ATOM__Tuple,
+ .max_size = MAX_GRAPH
+ },
+ {
+ .property = CANVAS__aspectRatio,
+ .offset = offsetof(plugstate_t, aspect_ratio),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = MONITORS__channel,
+ .offset = offsetof(plugstate_t, channel),
+ .type = LV2_ATOM__Int,
+ .event_cb = _intercept_channel
+ }
+};
+
+static void
+_fill_palette(uint32_t *palette)
+{
+ const unsigned n = sizeof(palette_colors) / sizeof(uint32_t);
+ unsigned I = 0;
+ unsigned J = ceilf((float)MAX_VELOCITY / (n-1));
+
+ for(unsigned i = 0; i < (n-1); i++)
+ {
+ const uint32_t c1 = palette_colors[i];
+ const uint32_t c2 = palette_colors[i+1];
+
+ const uint8_t r1 = (c1 >> 24) & 0xff;
+ const uint8_t r2 = (c2 >> 24) & 0xff;
+ const uint8_t g1 = (c1 >> 16) & 0xff;
+ const uint8_t g2 = (c2 >> 16) & 0xff;
+ const uint8_t b1 = (c1 >> 8) & 0xff;
+ const uint8_t b2 = (c2 >> 8) & 0xff;
+ const uint8_t a1 = (c1 >> 0) & 0xff;
+ const uint8_t a2 = (c2 >> 0) & 0xff;
+
+ for(unsigned j = 0; j < J; j++)
+ {
+ const float p = (float)j / J;
+
+ const uint8_t r = r1 + floorf((r2 - r1)*p);
+ const uint8_t g = g1 + floorf((g2 - g1)*p);
+ const uint8_t b = b1 + floorf((b2 - b1)*p);
+ const uint8_t a = a1 + floorf((a2 - a1)*p);
+
+ if(I + j < MAX_VELOCITY)
+ {
+ palette[I + j] = (r << 24) | (g << 16) | (b << 8) | a;
+ }
+ }
+
+ I += J;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ return NULL;
+ mlock(handle, sizeof(plughandle_t));
+
+ LV2_Options_Option *opts = NULL;
+#ifdef BUILD_IDISP
+ LV2_Inline_Display *queue_draw = NULL;
+#endif
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
+ opts = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ handle->log = features[i]->data;
+#ifdef BUILD_IDISP
+ else if(!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw))
+ queue_draw = features[i]->data;
+#endif
+ }
+
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(!opts)
+ {
+ fprintf(stderr,
+ "%s: Host does not support opts:options\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(handle->log)
+ {
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+ }
+
+ _craft_init(&handle->notify, handle->map);
+ lv2_canvas_urid_init(&handle->urid, handle->map);
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_init(&handle->idisp, queue_draw, handle->map);
+#endif
+
+ handle->midi_MidiEvent = handle->map->map(handle->map->handle,
+ LV2_MIDI__MidiEvent);
+
+ const LV2_URID ui_update_rate= handle->map->map(handle->map->handle,
+ LV2_UI__updateRate);
+
+ handle->update_rate = 60.f; // fall-back
+ handle->sample_rate = rate;
+
+ for(LV2_Options_Option *opt = opts;
+ opt && (opt->key != 0) && (opt->value != NULL);
+ opt++)
+ {
+ if( (opt->key == ui_update_rate)
+ && (opt->type == handle->notify.forge.Float) )
+ {
+ handle->update_rate = *(float*)opt->value;
+ }
+ }
+
+ handle->spf = handle->sample_rate / handle->update_rate;
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ handle->cnt = 1;
+
+ _render(handle, 0);
+ _fill_palette(handle->palette);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->control = (const void *)data;
+ break;
+ case 1:
+ _craft_connect(&handle->notify, data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static inline void
+_note_off(plughandle_t *handle, int64_t frames, uint8_t key)
+{
+ active_note_t *active = &handle->actives[key];
+ unsigned *offset = &handle->offsets[key];
+
+ // shift offset left
+ *offset = (*offset - 1) & MASK_NOTES;
+
+ // prepend
+ passive_note_t *passive = &handle->passives[key][*offset];
+ passive->start = active->start;
+ passive->end = handle->cnt + frames;
+ passive->style = active->style;
+
+ active->start = 0; // invalidate this note
+}
+
+static inline void
+_note_on(plughandle_t *handle, int64_t frames, uint8_t key, uint8_t vel)
+{
+ active_note_t *active = &handle->actives[key];
+
+ if(active->start != 0) // found a missing note off
+ {
+ _note_off(handle, frames, key);
+ }
+
+ active->start = handle->cnt + frames;
+ active->style = handle->palette[vel];
+}
+
+static inline void
+_handle_midi(plughandle_t *handle, int64_t frames,
+ uint32_t size __attribute__((unused)), const uint8_t *msg)
+{
+ switch(lv2_midi_message_type(msg))
+ {
+ case LV2_MIDI_MSG_NOTE_ON:
+ {
+ const uint8_t cha = msg[0] & 0x0f;
+
+ if(cha == handle->state.channel)
+ {
+ _note_on(handle, frames, msg[1], msg[2]);
+ }
+
+ } break;
+ case LV2_MIDI_MSG_NOTE_OFF:
+ {
+ const uint8_t cha = msg[0] & 0x0f;
+
+ if(cha == handle->state.channel)
+ {
+ _note_off(handle, frames, msg[1]);
+ }
+
+ } break;
+ default:
+ {
+ // nothing
+ } break;
+ }
+}
+
+static void
+_run(plughandle_t *handle, uint32_t from, uint32_t to)
+{
+ const uint32_t num = (to - from);
+
+ for(uint32_t i = 0; i < num; i++, handle->cnt++, handle->thresh++)
+ {
+ if(handle->thresh >= handle->spf)
+ {
+ handle->thresh = 0;
+
+ _render(handle, from + i);
+ props_set(&handle->props, &handle->notify.forge, from + i,
+ handle->urid.Canvas_graph, &handle->notify.ref);
+ }
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+
+ _craft_in(&handle->notify);
+
+ props_idle(&handle->props, &handle->notify.forge, 0, &handle->notify.ref);
+
+ int64_t from = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
+ {
+ const LV2_Atom *atom = &ev->body;
+
+ if(atom->type == handle->midi_MidiEvent)
+ {
+ const uint8_t *msg = LV2_ATOM_BODY_CONST(atom);
+ _handle_midi(handle, ev->time.frames, atom->size, msg);
+ }
+ else
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
+
+ props_advance(&handle->props, &handle->notify.forge, ev->time.frames, obj,
+ &handle->notify.ref);
+ }
+
+ const int64_t to = ev->time.frames;
+
+ _run(handle, from, to);
+ from = to;
+ }
+
+ {
+ const int64_t to = nsamples;
+
+ _run(handle, from, to);
+ }
+
+ _craft_out(&handle->notify);
+}
+
+#ifdef BUILD_IDISP
+static LV2_Inline_Display_Image_Surface *
+_idisp_render(LV2_Handle instance, uint32_t w, uint32_t h)
+{
+ plughandle_t *handle = instance;
+
+ float aspect_ratio = 1.f;
+
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_aspectRatio);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ memcpy(&aspect_ratio, impl->stash.body, impl->stash.size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ LV2_Inline_Display_Image_Surface *surf =
+ lv2_canvas_idisp_surf_configure(&handle->idisp, w, h, aspect_ratio);
+
+ if(surf)
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_graph);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ lv2_canvas_idisp_render_body(&handle->idisp, impl->type, impl->stash.size,
+ impl->stash.body);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ return surf;
+}
+
+static const LV2_Inline_Display_Interface idisp_iface = {
+ .render = _idisp_render
+};
+#endif
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ munlock(handle, sizeof(plughandle_t));
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_deinit(&handle->idisp);
+#endif
+ free(handle);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+static const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+#ifdef BUILD_IDISP
+ else if(!strcmp(uri, LV2_INLINEDISPLAY__interface))
+ return &idisp_iface;
+#endif
+
+ return NULL;
+}
+
+const LV2_Descriptor monitors_midi_pianoroll = {
+ .URI = MONITORS__midi_pianoroll,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
diff --git a/monitors_time_metronom.c b/monitors_time_metronom.c
new file mode 100644
index 0000000..1c2aec2
--- /dev/null
+++ b/monitors_time_metronom.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include <monitors.h>
+
+#include <timely.lv2/timely.h>
+#include <props.lv2/props.h>
+#include <canvas.lv2/canvas.h>
+#include <canvas.lv2/forge.h>
+
+#ifdef BUILD_IDISP
+# include <canvas.lv2/idisp.h>
+#endif
+
+#define MAX_GRAPH 0x20000 //FIXME actually measure this
+#define MAX_NPROPS 5
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _plughandle_t plughandle_t;
+
+struct _plugstate_t {
+ float rotation;
+ int32_t hflip;
+ int32_t vflip;
+ float aspect_ratio;
+ uint8_t graph [MAX_GRAPH];
+};
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ const LV2_Atom_Sequence *control;
+ craft_t notify;
+
+ timely_t timely;
+
+#ifdef BUILD_IDISP
+ LV2_Canvas_Idisp idisp;
+#endif
+ LV2_Canvas_URID urid;
+ float update_rate;
+ uint32_t graph_size;
+
+ unsigned spf;
+ unsigned thresh;
+ int64_t cnt;
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ PROPS_T(props, MAX_NPROPS);
+};
+
+static inline void
+_out_of_memory(plughandle_t *handle)
+{
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "out-of-memory\n");
+ }
+
+ _craft_clear(&handle->notify);
+}
+
+static void
+_render(plughandle_t *handle, int64_t frames __attribute__((unused)))
+{
+ LV2_Atom_Forge _forge = handle->notify.forge; // clone forge object
+ LV2_Atom_Forge *forge = &_forge;
+ LV2_Canvas_URID *urid = &handle->urid;
+ LV2_Atom_Forge_Frame frame;
+
+ const float bar_beat = TIMELY_BAR_BEAT(&handle->timely);
+ const float beats_per_bar = TIMELY_BEATS_PER_BAR(&handle->timely);
+ const int32_t bar_beat_i = ceilf(bar_beat);
+
+ lv2_atom_forge_set_buffer(forge, handle->state.graph, MAX_GRAPH);
+
+ if( !lv2_atom_forge_tuple(forge, &frame)
+ || !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_rectangle(forge, urid, 0.f, 0.f, 1.f, 1.f)
+ || !lv2_canvas_forge_style(forge, urid, 0x0000003f)
+ || !lv2_canvas_forge_fill(forge, urid)
+ || !lv2_canvas_forge_lineWidth(forge, urid, 0.1f)
+ || !lv2_canvas_forge_save(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ const float alpha = bar_beat_i / beats_per_bar * 2*M_PI - M_PI_2;
+ const float beta = bar_beat / beats_per_bar * 2*M_PI - M_PI_2;
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_arc(forge, urid, 0.5f, 0.5f, 0.2f, -M_PI_2, alpha)
+ || !lv2_canvas_forge_style(forge, urid, 0x7000ffff)
+ || !lv2_canvas_forge_stroke(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_arc(forge, urid, 0.5f, 0.5f, 0.4f, -M_PI_2, beta)
+ || !lv2_canvas_forge_style(forge, urid, 0xff7f00ff)
+ || !lv2_canvas_forge_stroke(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ char label [16];
+ snprintf(label, sizeof(label), "%"PRIi32, bar_beat_i);
+
+ if( !lv2_canvas_forge_beginPath(forge, urid)
+ || !lv2_canvas_forge_moveTo(forge, urid, 0.5f, 0.5f)
+ || !lv2_canvas_forge_fontSize(forge, urid, 0.25f)
+ || !lv2_canvas_forge_style(forge, urid, 0xffffffff)
+ || !lv2_canvas_forge_fillText(forge, urid, label) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ if( !lv2_canvas_forge_restore(forge, urid) )
+ {
+ _out_of_memory(handle);
+ return;
+ }
+
+ lv2_atom_forge_pop(forge, &frame);
+
+ props_impl_t *impl = _props_impl_get(&handle->props, handle->urid.Canvas_graph);
+ if(impl)
+ {
+ const LV2_Atom *value= (const LV2_Atom *)handle->state.graph;
+
+ _props_impl_set(&handle->props, impl, value->type, value->size,
+ LV2_ATOM_BODY_CONST(value));
+ }
+
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_queue_draw(&handle->idisp);
+#endif
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = MONITORS__rotation,
+ .offset = offsetof(plugstate_t, rotation),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = MONITORS__horizontalFlip,
+ .offset = offsetof(plugstate_t, hflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .property = MONITORS__verticalFlip,
+ .offset = offsetof(plugstate_t, vflip),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .access = LV2_PATCH__readable,
+ .property = CANVAS__graph,
+ .offset = offsetof(plugstate_t, graph),
+ .type = LV2_ATOM__Tuple,
+ .max_size = MAX_GRAPH
+ },
+ {
+ .property = CANVAS__aspectRatio,
+ .offset = offsetof(plugstate_t, aspect_ratio),
+ .type = LV2_ATOM__Float
+ }
+};
+
+static void
+_cb(timely_t *timely __attribute__((unused)),
+ int64_t frames __attribute__((unused)),
+ LV2_URID type __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ // do nothing
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ return NULL;
+ mlock(handle, sizeof(plughandle_t));
+
+ LV2_Options_Option *opts = NULL;
+#ifdef BUILD_IDISP
+ LV2_Inline_Display *queue_draw = NULL;
+#endif
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
+ opts = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ handle->log = features[i]->data;
+#ifdef BUILD_IDISP
+ else if(!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw))
+ queue_draw = features[i]->data;
+#endif
+ }
+
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(!opts)
+ {
+ fprintf(stderr,
+ "%s: Host does not support opts:options\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(handle->log)
+ {
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+ }
+
+ _craft_init(&handle->notify, handle->map);
+ lv2_canvas_urid_init(&handle->urid, handle->map);
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_init(&handle->idisp, queue_draw, handle->map);
+#endif
+ timely_init(&handle->timely, handle->map, rate, 0, _cb, handle);
+
+ const LV2_URID ui_update_rate= handle->map->map(handle->map->handle,
+ LV2_UI__updateRate);
+
+ handle->update_rate = 60.f; // fall-back
+
+ for(LV2_Options_Option *opt = opts;
+ opt && (opt->key != 0) && (opt->value != NULL);
+ opt++)
+ {
+ if( (opt->key == ui_update_rate) && (opt->type == handle->notify.forge.Float) )
+ handle->update_rate = *(float*)opt->value;
+ }
+
+ handle->spf = rate / handle->update_rate;
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ handle->cnt = 1;
+
+ _render(handle, 0);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->control = (const void *)data;
+ break;
+ case 1:
+ _craft_connect(&handle->notify, data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+_run(plughandle_t *handle, uint32_t from, uint32_t to)
+{
+ const uint32_t num = (to - from);
+
+ for(uint32_t i = 0; i < num; i++, handle->cnt++, handle->thresh++)
+ {
+ if(handle->thresh >= handle->spf)
+ {
+ handle->thresh = 0;
+
+ _render(handle, from + i);
+ props_set(&handle->props, &handle->notify.forge, from + i,
+ handle->urid.Canvas_graph, &handle->notify.ref);
+ }
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+
+ _craft_in(&handle->notify);
+
+ props_idle(&handle->props, &handle->notify.forge, 0, &handle->notify.ref);
+
+ int64_t from = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
+ {
+ const LV2_Atom_Object *obj= (const LV2_Atom_Object *)&ev->body;
+ const int64_t to = ev->time.frames;
+
+ if(!timely_advance(&handle->timely, obj, from, to))
+ {
+ props_advance(&handle->props, &handle->notify.forge, to, obj, &handle->notify.ref);
+ }
+
+ _run(handle, from, to);
+ from = to;
+ }
+
+ {
+ const int64_t to = nsamples;
+
+ timely_advance(&handle->timely, NULL, from, to);
+ _run(handle, from, to);
+ }
+
+ _craft_out(&handle->notify);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+#ifdef BUILD_IDISP
+static LV2_Inline_Display_Image_Surface *
+_idisp_render(LV2_Handle instance, uint32_t w, uint32_t h)
+{
+ plughandle_t *handle = instance;
+
+ float aspect_ratio = 1.f;
+
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_aspectRatio);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ memcpy(&aspect_ratio, impl->stash.body, impl->stash.size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ LV2_Inline_Display_Image_Surface *surf =
+ lv2_canvas_idisp_surf_configure(&handle->idisp, w, h, aspect_ratio);
+
+ if(surf)
+ {
+ props_impl_t *impl = _props_impl_get(&handle->props,
+ handle->urid.Canvas_graph);
+
+ if(impl)
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ lv2_canvas_idisp_render_body(&handle->idisp, impl->type, impl->stash.size,
+ impl->stash.body);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ }
+
+ return surf;
+}
+
+static const LV2_Inline_Display_Interface idisp_iface = {
+ .render = _idisp_render
+};
+#endif
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ munlock(handle, sizeof(plughandle_t));
+#ifdef BUILD_IDISP
+ lv2_canvas_idisp_deinit(&handle->idisp);
+#endif
+ free(handle);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+#ifdef BUILD_IDISP
+ else if(!strcmp(uri, LV2_INLINEDISPLAY__interface))
+ return &idisp_iface;
+#endif
+
+ return NULL;
+}
+
+const LV2_Descriptor monitors_time_metronom = {
+ .URI = MONITORS__time_metronom,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
diff --git a/props.lv2/.gitlab-ci.yml b/props.lv2/.gitlab-ci.yml
new file mode 100644
index 0000000..979769c
--- /dev/null
+++ b/props.lv2/.gitlab-ci.yml
@@ -0,0 +1,2 @@
+include:
+ - local: 'gitlab-ci/generic.yml'
diff --git a/props.lv2/COPYING b/props.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/props.lv2/COPYING
@@ -0,0 +1,201 @@
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/props.lv2/README.md b/props.lv2/README.md
new file mode 100644
index 0000000..08466d2
--- /dev/null
+++ b/props.lv2/README.md
@@ -0,0 +1,20 @@
+# Props.lv2
+
+## Utility header for property based LV2 plugins
+
+### License
+
+Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+
+This is free software: you can redistribute it and/or modify
+it under the terms of the Artistic License 2.0 as published by
+The Perl Foundation.
+
+This source is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Artistic License 2.0 for more details.
+
+You should have received a copy of the Artistic License 2.0
+along the source as a COPYING file. If not, obtain it from
+<http://www.perlfoundation.org/artistic_license_2_0>.
diff --git a/props.lv2/VERSION b/props.lv2/VERSION
new file mode 100644
index 0000000..a63a9fe
--- /dev/null
+++ b/props.lv2/VERSION
@@ -0,0 +1 @@
+0.1.159
diff --git a/props.lv2/gitlab-ci/generic.yml b/props.lv2/gitlab-ci/generic.yml
new file mode 100644
index 0000000..5cd2abc
--- /dev/null
+++ b/props.lv2/gitlab-ci/generic.yml
@@ -0,0 +1,106 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS : ""
+
+.native_template: &native_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+
+ - scan-build --status-bugs meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.cross_template: &cross_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+# build
+.universal_linux_template_stretch: &universal_linux_definition_stretch
+ image: ventosus/universal-linux-gnu:stretch
+ <<: *cross_definition
+
+.universal_linux_template_buster: &universal_linux_definition_buster
+ image: ventosus/universal-linux-gnu:buster
+ <<: *native_definition
+
+.universal_linux_template_bullseye: &universal_linux_definition_bullseye
+ image: ventosus/universal-linux-gnu:bullseye
+ <<: *native_definition
+
+.arm_linux_template_stretch: &arm_linux_definition_stretch
+ image: ventosus/arm-linux-gnueabihf:stretch
+ <<: *cross_definition
+
+.arm_linux_template_buster: &arm_linux_definition_buster
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *cross_definition
+
+.arm_linux_template_bullseye: &arm_linux_definition_bullseye
+ image: ventosus/arm-linux-gnueabihf:bullseye
+ <<: *cross_definition
+
+# build
+x86_64-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+x86_64-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+x86_64-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+i686-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+i686-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+i686-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+arm-linux-gnueabihf-stretch:
+ <<: *arm_linux_definition_stretch
+
+arm-linux-gnueabihf-buster:
+ <<: *arm_linux_definition_buster
+
+arm-linux-gnueabihf-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+aarch64-linux-gnu-stretch:
+ <<: *arm_linux_definition_stretch
+
+aarch64-linux-gnu-buster:
+ <<: *arm_linux_definition_buster
+
+aarch64-linux-gnu-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+pack:
+ stage: deploy
+ script:
+ - echo 'packing up'
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/props.lv2/meson.build b/props.lv2/meson.build
new file mode 100644
index 0000000..d354d89
--- /dev/null
+++ b/props.lv2/meson.build
@@ -0,0 +1,82 @@
+project('props.lv2', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'werror=false',
+ 'b_lto=false',
+ 'c_std=c11'])
+
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
+
+conf_data = configuration_data()
+cc = meson.get_compiler('c')
+
+cp = find_program('cp')
+lv2_validate = find_program('lv2_validate', native : true, required : false)
+sord_validate = find_program('sord_validate', native : true, required : false)
+lv2lint = find_program('lv2lint', required : false)
+clone = [cp, '@INPUT@', '@OUTPUT@']
+
+m_dep = cc.find_library('m')
+lv2_dep = dependency('lv2', version : '>=1.14.0')
+
+inc_dir = []
+
+inst_dir = join_paths(get_option('libdir'), 'lv2', meson.project_name())
+
+dsp_srcs = [join_paths('test', 'props.c')]
+
+c_args = ['-fvisibility=hidden',
+ '-ffast-math']
+
+mod = shared_module('props', dsp_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : [m_dep, lv2_dep],
+ install : true,
+ install_dir : inst_dir)
+
+version = run_command('cat', 'VERSION').stdout().strip().split('.')
+conf_data.set('MAJOR_VERSION', version[0])
+conf_data.set('MINOR_VERSION', version[1])
+conf_data.set('MICRO_VERSION', version[2])
+
+suffix = mod.full_path().strip().split('.')[-1]
+conf_data.set('MODULE_SUFFIX', '.' + suffix)
+
+manifest_ttl = configure_file(
+ input : join_paths('test', 'manifest.ttl.in'), output : 'manifest.ttl',
+ configuration : conf_data,
+ install : true,
+ install_dir : inst_dir)
+dsp_ttl = custom_target('props_ttl',
+ input : join_paths('test', 'props.ttl'),
+ output : 'props.ttl',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+custom_target('chunk_bin',
+ input : join_paths('test', 'chunk.bin'),
+ output : 'chunk.bin',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+
+props_test = executable('props_test',
+ join_paths('test', 'props_test.c'),
+ c_args : c_args,
+ install : false)
+
+test('Test', props_test,
+ timeout : 240)
+
+if lv2_validate.found() and sord_validate.found()
+ test('LV2 validate', lv2_validate,
+ args : [manifest_ttl, dsp_ttl])
+endif
+
+if lv2lint.found()
+ test('LV2 lint', lv2lint,
+ args : ['-Ewarn',
+ 'http://open-music-kontrollers.ch/lv2/props#test'])
+endif
diff --git a/props.h b/props.lv2/props.h
index 0ea0396..0ea0396 100644
--- a/props.h
+++ b/props.lv2/props.h
diff --git a/test/chunk.bin b/props.lv2/test/chunk.bin
index b66efb8..b66efb8 100644
--- a/test/chunk.bin
+++ b/props.lv2/test/chunk.bin
Binary files differ
diff --git a/test/manifest.ttl.in b/props.lv2/test/manifest.ttl.in
index 0ecc313..0ecc313 100644
--- a/test/manifest.ttl.in
+++ b/props.lv2/test/manifest.ttl.in
diff --git a/test/props.c b/props.lv2/test/props.c
index 590c519..590c519 100644
--- a/test/props.c
+++ b/props.lv2/test/props.c
diff --git a/test/props.ttl b/props.lv2/test/props.ttl
index cd4d107..cd4d107 100644
--- a/test/props.ttl
+++ b/props.lv2/test/props.ttl
diff --git a/test/props_test.c b/props.lv2/test/props_test.c
index 69f3b3f..69f3b3f 100644
--- a/test/props_test.c
+++ b/props.lv2/test/props_test.c
diff --git a/timely.lv2/.gitlab-ci.yml b/timely.lv2/.gitlab-ci.yml
new file mode 100644
index 0000000..979769c
--- /dev/null
+++ b/timely.lv2/.gitlab-ci.yml
@@ -0,0 +1,2 @@
+include:
+ - local: 'gitlab-ci/generic.yml'
diff --git a/timely.lv2/COPYING b/timely.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/timely.lv2/COPYING
@@ -0,0 +1,201 @@
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/timely.lv2/README.md b/timely.lv2/README.md
new file mode 100644
index 0000000..c422cf9
--- /dev/null
+++ b/timely.lv2/README.md
@@ -0,0 +1,20 @@
+# Timely.lv2
+
+## Utility header for time-based LV2 plugins
+
+### License
+
+Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+
+This is free software: you can redistribute it and/or modify
+it under the terms of the Artistic License 2.0 as published by
+The Perl Foundation.
+
+This source is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Artistic License 2.0 for more details.
+
+You should have received a copy of the Artistic License 2.0
+along the source as a COPYING file. If not, obtain it from
+<http://www.perlfoundation.org/artistic_license_2_0>.
diff --git a/timely.lv2/VERSION b/timely.lv2/VERSION
new file mode 100644
index 0000000..a2d633d
--- /dev/null
+++ b/timely.lv2/VERSION
@@ -0,0 +1 @@
+0.1.61
diff --git a/timely.lv2/gitlab-ci/generic.yml b/timely.lv2/gitlab-ci/generic.yml
new file mode 100644
index 0000000..5cd2abc
--- /dev/null
+++ b/timely.lv2/gitlab-ci/generic.yml
@@ -0,0 +1,106 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS : ""
+
+.native_template: &native_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+
+ - scan-build --status-bugs meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.cross_template: &cross_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+# build
+.universal_linux_template_stretch: &universal_linux_definition_stretch
+ image: ventosus/universal-linux-gnu:stretch
+ <<: *cross_definition
+
+.universal_linux_template_buster: &universal_linux_definition_buster
+ image: ventosus/universal-linux-gnu:buster
+ <<: *native_definition
+
+.universal_linux_template_bullseye: &universal_linux_definition_bullseye
+ image: ventosus/universal-linux-gnu:bullseye
+ <<: *native_definition
+
+.arm_linux_template_stretch: &arm_linux_definition_stretch
+ image: ventosus/arm-linux-gnueabihf:stretch
+ <<: *cross_definition
+
+.arm_linux_template_buster: &arm_linux_definition_buster
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *cross_definition
+
+.arm_linux_template_bullseye: &arm_linux_definition_bullseye
+ image: ventosus/arm-linux-gnueabihf:bullseye
+ <<: *cross_definition
+
+# build
+x86_64-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+x86_64-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+x86_64-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+i686-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+i686-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+i686-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+arm-linux-gnueabihf-stretch:
+ <<: *arm_linux_definition_stretch
+
+arm-linux-gnueabihf-buster:
+ <<: *arm_linux_definition_buster
+
+arm-linux-gnueabihf-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+aarch64-linux-gnu-stretch:
+ <<: *arm_linux_definition_stretch
+
+aarch64-linux-gnu-buster:
+ <<: *arm_linux_definition_buster
+
+aarch64-linux-gnu-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+pack:
+ stage: deploy
+ script:
+ - echo 'packing up'
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/timely.lv2/meson.build b/timely.lv2/meson.build
new file mode 100644
index 0000000..df8d684
--- /dev/null
+++ b/timely.lv2/meson.build
@@ -0,0 +1,67 @@
+project('timely.lv2', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'werror=false',
+ 'b_lto=false',
+ 'c_std=c11'])
+
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
+
+conf_data = configuration_data()
+cc = meson.get_compiler('c')
+
+cp = find_program('cp')
+lv2_validate = find_program('lv2_validate', native : true, required : false)
+sord_validate = find_program('sord_validate', native : true, required : false)
+lv2lint = find_program('lv2lint', required : false)
+clone = [cp, '@INPUT@', '@OUTPUT@']
+
+m_dep = cc.find_library('m')
+lv2_dep = dependency('lv2', version : '>=1.14.0')
+
+inc_dir = []
+
+inst_dir = join_paths(get_option('libdir'), 'lv2', meson.project_name())
+
+dsp_srcs = [join_paths('test', 'timely.c')]
+
+c_args = ['-fvisibility=hidden',
+ '-ffast-math']
+
+mod = shared_module('timely', dsp_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : [m_dep, lv2_dep],
+ install : true,
+ install_dir : inst_dir)
+
+version = run_command('cat', 'VERSION').stdout().strip().split('.')
+conf_data.set('MAJOR_VERSION', version[0])
+conf_data.set('MINOR_VERSION', version[1])
+conf_data.set('MICRO_VERSION', version[2])
+
+suffix = mod.full_path().strip().split('.')[-1]
+conf_data.set('MODULE_SUFFIX', '.' + suffix)
+
+manifest_ttl = configure_file(input : join_paths('test', 'manifest.ttl.in'), output : 'manifest.ttl',
+ configuration : conf_data,
+ install : true,
+ install_dir : inst_dir)
+dsp_ttl = custom_target('timely_ttl',
+ input : join_paths('test', 'timely.ttl'),
+ output : 'timely.ttl',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+
+if lv2_validate.found() and sord_validate.found()
+ test('LV2 validate', lv2_validate,
+ args : [manifest_ttl, dsp_ttl])
+endif
+
+if lv2lint.found()
+ test('LV2 lint', lv2lint,
+ args : ['-Ewarn',
+ 'http://open-music-kontrollers.ch/lv2/timely#test'])
+endif
diff --git a/timely.lv2/test/manifest.ttl.in b/timely.lv2/test/manifest.ttl.in
new file mode 100644
index 0000000..e55fd39
--- /dev/null
+++ b/timely.lv2/test/manifest.ttl.in
@@ -0,0 +1,28 @@
+# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+#
+# This is free software: you can redistribute it and/or modify
+# it under the terms of the Artistic License 2.0 as published by
+# The Perl Foundation.
+#
+# This source is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Artistic License 2.0 for more details.
+#
+# You should have received a copy of the Artistic License 2.0
+# along the source as a COPYING file. If not, obtain it from
+# http://www.perlfoundation.org/artistic_license_2_0.
+
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+@prefix timely: <http://open-music-kontrollers.ch/lv2/timely#> .
+
+# Orbit Looper
+timely:test
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <timely@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <timely.ttl> .
diff --git a/timely.lv2/test/timely.c b/timely.lv2/test/timely.c
new file mode 100644
index 0000000..ba2d63a
--- /dev/null
+++ b/timely.lv2/test/timely.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <timely.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+
+#define TIMELY_PREFIX "http://open-music-kontrollers.ch/lv2/timely#"
+#define TIMELY_TEST_URI TIMELY_PREFIX"test"
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+ timely_t timely;
+
+
+ const LV2_Atom_Sequence *event_in;
+};
+
+static void
+_timely_cb(timely_t *timely, int64_t frames, LV2_URID type, void *data)
+{
+ plughandle_t *handle = data;
+
+ const int64_t frame = TIMELY_FRAME(timely);
+
+ if(type == TIMELY_URI_BAR_BEAT(timely))
+ {
+ const float bar_beat = TIMELY_BAR_BEAT_RAW(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:barBeat %f\n",
+ frame, frames, bar_beat);
+ }
+ else if(type == TIMELY_URI_BAR(timely))
+ {
+ const int64_t bar = TIMELY_BAR(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:bar %"PRIi64"\n",
+ frame, frames, bar);
+ }
+ else if(type == TIMELY_URI_BEAT_UNIT(timely))
+ {
+ const int32_t beat_unit = TIMELY_BEAT_UNIT(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatUnit %"PRIi32"\n",
+ frame, frames, beat_unit);
+ }
+ else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
+ {
+ const float bpb = TIMELY_BEATS_PER_BAR(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatsPerBar %f\n",
+ frame, frames, bpb);
+ }
+ else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
+ {
+ const float bpm = TIMELY_BEATS_PER_MINUTE(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatsPerMinute %f\n",
+ frame, frames, bpm);
+ }
+ else if(type == TIMELY_URI_FRAME(timely))
+ {
+ /*
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:frame %"PRIi64"\n",
+ frame, frames, frame);
+ */
+ }
+ else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
+ {
+ const float fps = TIMELY_FRAMES_PER_SECOND(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:framesPerSecond %f\n",
+ frame, frames, fps);
+ }
+ else if(type == TIMELY_URI_SPEED(timely))
+ {
+ const float speed = TIMELY_SPEED(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:speed %f\n",
+ frame, frames, speed);
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ return NULL;
+
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ handle->log = features[i]->data;
+ }
+
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+ if(!handle->log)
+ {
+ fprintf(stderr,
+ "%s: Host does not support log:log\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+
+ timely_mask_t mask = TIMELY_MASK_BAR_BEAT
+ | TIMELY_MASK_BAR
+ | TIMELY_MASK_BEAT_UNIT
+ | TIMELY_MASK_BEATS_PER_BAR
+ | TIMELY_MASK_BEATS_PER_MINUTE
+ //| TIMELY_MASK_FRAME
+ | TIMELY_MASK_FRAMES_PER_SECOND
+ | TIMELY_MASK_SPEED
+ | TIMELY_MASK_BAR_BEAT_WHOLE
+ | TIMELY_MASK_BAR_WHOLE;
+ timely_init(&handle->timely, handle->map, rate, mask, _timely_cb, handle);
+ timely_set_multiplier(&handle->timely, 1.f);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->event_in = (const LV2_Atom_Sequence *)data;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+ int64_t from = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
+ {
+ const int64_t to = ev->time.frames;
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ const int handled = timely_advance(&handle->timely, obj, from, to);
+ (void)handled;
+ from = to;
+ }
+
+ timely_advance(&handle->timely, NULL, from, nsamples);
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ free(handle);
+}
+
+const LV2_Descriptor timely_test = {
+ .URI = TIMELY_TEST_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = NULL
+};
+
+#ifdef _WIN32
+__declspec(dllexport)
+#else
+__attribute__((visibility("default")))
+#endif
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &timely_test;
+ default:
+ return NULL;
+ }
+}
diff --git a/timely.lv2/test/timely.ttl b/timely.lv2/test/timely.ttl
new file mode 100644
index 0000000..6064c9c
--- /dev/null
+++ b/timely.lv2/test/timely.ttl
@@ -0,0 +1,65 @@
+# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+#
+# This is free software: you can redistribute it and/or modify
+# it under the terms of the Artistic License 2.0 as published by
+# The Perl Foundation.
+#
+# This source is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Artistic License 2.0 for more details.
+#
+# You should have received a copy of the Artistic License 2.0
+# along the source as a COPYING file. If not, obtain it from
+# http://www.perlfoundation.org/artistic_license_2_0.
+
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix time: <http://lv2plug.in/ns/ext/time#> .
+@prefix log: <http://lv2plug.in/ns/ext/log#> .
+
+@prefix lic: <http://opensource.org/licenses/> .
+@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix timely: <http://open-music-kontrollers.ch/lv2/timely#> .
+
+# Maintainer
+omk:me
+ a foaf:Person ;
+ foaf:name "Hanspeter Portner" ;
+ foaf:mbox <mailto:dev@open-music-kontrollers.ch> ;
+ foaf:homepage <http://open-music-kontrollers.ch> .
+
+# Project
+proj:timely
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Timely Bundle" .
+
+# Looper Test
+timely:test
+ a lv2:Plugin ,
+ lv2:ConverterPlugin ;
+ doap:name "Timely Test" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:timely ;
+ lv2:requiredFeature urid:map, log:log ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
+
+ lv2:port [
+ # sink event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports time:Position ;
+ lv2:index 0 ;
+ lv2:symbol "event_in" ;
+ lv2:name "Event Input" ;
+ lv2:designation lv2:control ;
+ ] .
diff --git a/timely.lv2/timely.h b/timely.lv2/timely.h
new file mode 100644
index 0000000..ed9497b
--- /dev/null
+++ b/timely.lv2/timely.h
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_TIMELY_H_
+#define _LV2_TIMELY_H_
+
+#include <math.h>
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/time/time.h>
+
+typedef struct _timely_t timely_t;
+typedef void (*timely_cb_t)(timely_t *timely, int64_t frames, LV2_URID type,
+ void *data);
+
+typedef enum _timely_mask_t {
+ TIMELY_MASK_BAR_BEAT = (1 << 0),
+ TIMELY_MASK_BAR = (1 << 1),
+ TIMELY_MASK_BEAT_UNIT = (1 << 2),
+ TIMELY_MASK_BEATS_PER_BAR = (1 << 3),
+ TIMELY_MASK_BEATS_PER_MINUTE = (1 << 4),
+ TIMELY_MASK_FRAME = (1 << 5),
+ TIMELY_MASK_FRAMES_PER_SECOND = (1 << 6),
+ TIMELY_MASK_SPEED = (1 << 7),
+ TIMELY_MASK_BAR_BEAT_WHOLE = (1 << 8),
+ TIMELY_MASK_BAR_WHOLE = (1 << 9)
+} timely_mask_t;
+
+struct _timely_t {
+ struct {
+ LV2_URID atom_object;
+ LV2_URID atom_blank;
+ LV2_URID atom_resource;
+
+ LV2_URID time_position;
+ LV2_URID time_barBeat;
+ LV2_URID time_bar;
+ LV2_URID time_beatUnit;
+ LV2_URID time_beatsPerBar;
+ LV2_URID time_beatsPerMinute;
+ LV2_URID time_frame;
+ LV2_URID time_framesPerSecond;
+ LV2_URID time_speed;
+ } urid;
+
+ struct {
+ float bar_beat;
+ int64_t bar;
+
+ int32_t beat_unit;
+ float beats_per_bar;
+ float beats_per_minute;
+
+ int64_t frame;
+ float frames_per_second;
+
+ float speed;
+ } pos;
+
+ float multiplier;
+
+ double frames_per_beat;
+ double frames_per_bar;
+
+ struct {
+ double beat;
+ double bar;
+ } offset;
+
+ bool first;
+ timely_mask_t mask;
+ timely_cb_t cb;
+ void *data;
+};
+
+#define TIMELY_URI_BAR_BEAT(timely) ((timely)->urid.time_barBeat)
+#define TIMELY_URI_BAR(timely) ((timely)->urid.time_bar)
+#define TIMELY_URI_BEAT_UNIT(timely) ((timely)->urid.time_beatUnit)
+#define TIMELY_URI_BEATS_PER_BAR(timely) ((timely)->urid.time_beatsPerBar)
+#define TIMELY_URI_BEATS_PER_MINUTE(timely) ((timely)->urid.time_beatsPerMinute)
+#define TIMELY_URI_FRAME(timely) ((timely)->urid.time_frame)
+#define TIMELY_URI_FRAMES_PER_SECOND(timely) ((timely)->urid.time_framesPerSecond)
+#define TIMELY_URI_SPEED(timely) ((timely)->urid.time_speed)
+
+#define TIMELY_BAR_BEAT_RAW(timely) ((timely)->pos.bar_beat)
+#define TIMELY_BAR_BEAT(timely) (floor((timely)->pos.bar_beat) \
+ + (timely)->offset.beat / (timely)->frames_per_beat)
+#define TIMELY_BAR(timely) ((timely)->pos.bar)
+#define TIMELY_BEAT_UNIT(timely) ((timely)->pos.beat_unit)
+#define TIMELY_BEATS_PER_BAR(timely) ((timely)->pos.beats_per_bar)
+#define TIMELY_BEATS_PER_MINUTE(timely) ((timely)->pos.beats_per_minute)
+#define TIMELY_FRAME(timely) ((timely)->pos.frame)
+#define TIMELY_FRAMES_PER_SECOND(timely) ((timely)->pos.frames_per_second)
+#define TIMELY_SPEED(timely) ((timely)->pos.speed)
+
+#define TIMELY_FRAMES_PER_BEAT(timely) ((timely)->frames_per_beat)
+#define TIMELY_FRAMES_PER_BAR(timely) ((timely)->frames_per_bar)
+
+static inline void
+_timely_deatomize_body(timely_t *timely, int64_t frames, uint32_t size,
+ const LV2_Atom_Object_Body *body)
+{
+ const LV2_Atom_Float *bar_beat = NULL;
+ const LV2_Atom_Long *bar = NULL;
+ const LV2_Atom_Int *beat_unit = NULL;
+ const LV2_Atom_Float *beats_per_bar = NULL;
+ const LV2_Atom_Float *beats_per_minute = NULL;
+ const LV2_Atom_Long *frame = NULL;
+ const LV2_Atom_Float *frames_per_second = NULL;
+ const LV2_Atom_Float *speed = NULL;
+
+ lv2_atom_object_body_get(size, body,
+ timely->urid.time_barBeat, &bar_beat,
+ timely->urid.time_bar, &bar,
+ timely->urid.time_beatUnit, &beat_unit,
+ timely->urid.time_beatsPerBar, &beats_per_bar,
+ timely->urid.time_beatsPerMinute, &beats_per_minute,
+ timely->urid.time_frame, &frame,
+ timely->urid.time_framesPerSecond, &frames_per_second,
+ timely->urid.time_speed, &speed,
+ 0);
+
+ // send speed first upon transport stop
+ if(speed && (speed->body != timely->pos.speed) && (speed->body == 0.f) )
+ {
+ timely->pos.speed = speed->body;
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, frames, timely->urid.time_speed, timely->data);
+ }
+
+ if(beat_unit)
+ {
+ const int32_t _beat_unit = beat_unit->body * timely->multiplier;
+ if(_beat_unit != timely->pos.beat_unit)
+ {
+ timely->pos.beat_unit = _beat_unit;
+ if(timely->mask & TIMELY_MASK_BEAT_UNIT)
+ timely->cb(timely, frames, timely->urid.time_beatUnit, timely->data);
+ }
+ }
+
+ if(beats_per_bar)
+ {
+ const float _beats_per_bar = beats_per_bar->body * timely->multiplier;
+ if(_beats_per_bar != timely->pos.beats_per_bar)
+ {
+ timely->pos.beats_per_bar = _beats_per_bar;
+ if(timely->mask & TIMELY_MASK_BEATS_PER_BAR)
+ timely->cb(timely, frames, timely->urid.time_beatsPerBar, timely->data);
+ }
+ }
+
+ if(beats_per_minute && (beats_per_minute->body != timely->pos.beats_per_minute) )
+ {
+ timely->pos.beats_per_minute = beats_per_minute->body;
+ if(timely->mask & TIMELY_MASK_BEATS_PER_MINUTE)
+ timely->cb(timely, frames, timely->urid.time_beatsPerMinute, timely->data);
+ }
+
+ if(frame && (frame->body != timely->pos.frame) )
+ {
+ timely->pos.frame = frame->body;
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, frames, timely->urid.time_frame, timely->data);
+ }
+
+ if(frames_per_second && (frames_per_second->body != timely->pos.frames_per_second) )
+ {
+ timely->pos.frames_per_second = frames_per_second->body;
+ if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
+ timely->cb(timely, frames, timely->urid.time_framesPerSecond, timely->data);
+ }
+
+ if(bar && (bar->body != timely->pos.bar) )
+ {
+ timely->pos.bar = bar->body;
+ if(timely->mask & TIMELY_MASK_BAR)
+ timely->cb(timely, frames, timely->urid.time_bar, timely->data);
+ }
+
+ if(bar_beat)
+ {
+ const float _bar_beat = bar_beat->body * timely->multiplier;
+ if(_bar_beat != timely->pos.bar_beat)
+ {
+ timely->pos.bar_beat = _bar_beat;
+ if(timely->mask & TIMELY_MASK_BAR_BEAT)
+ timely->cb(timely, frames, timely->urid.time_barBeat, timely->data);
+ }
+ }
+
+ // send speed last upon transport start
+ if(speed && (speed->body != timely->pos.speed) && (speed->body != 0.f) )
+ {
+ timely->pos.speed = speed->body;
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, frames, timely->urid.time_speed, timely->data);
+ }
+}
+
+static inline void
+_timely_refresh(timely_t *timely)
+{
+ const float speed = (timely->pos.speed != 0.f)
+ ? timely->pos.speed
+ : 1.f; // prevent divisions through zero later on
+
+ timely->frames_per_beat = 240.0 * timely->pos.frames_per_second
+ / (timely->pos.beats_per_minute * timely->pos.beat_unit * speed);
+ timely->frames_per_bar = timely->frames_per_beat * timely->pos.beats_per_bar;
+
+ // bar
+ timely->offset.bar = timely->pos.bar_beat * timely->frames_per_beat;
+
+ // beat
+ double integral;
+ double beat_beat = modf(timely->pos.bar_beat, &integral);
+ (void)integral;
+ timely->offset.beat = beat_beat * timely->frames_per_beat;
+}
+
+static inline void
+timely_init(timely_t *timely, LV2_URID_Map *map, double rate,
+ timely_mask_t mask, timely_cb_t cb, void *data)
+{
+ assert(cb != NULL);
+
+ timely->mask = mask;
+ timely->cb = cb;
+ timely->data = data;
+
+ timely->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
+ timely->urid.atom_blank = map->map(map->handle, LV2_ATOM__Blank);
+ timely->urid.atom_resource = map->map(map->handle, LV2_ATOM__Resource);
+ timely->urid.time_position = map->map(map->handle, LV2_TIME__Position);
+ timely->urid.time_barBeat = map->map(map->handle, LV2_TIME__barBeat);
+ timely->urid.time_bar = map->map(map->handle, LV2_TIME__bar);
+ timely->urid.time_beatUnit = map->map(map->handle, LV2_TIME__beatUnit);
+ timely->urid.time_beatsPerBar = map->map(map->handle, LV2_TIME__beatsPerBar);
+ timely->urid.time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
+ timely->urid.time_frame = map->map(map->handle, LV2_TIME__frame);
+ timely->urid.time_framesPerSecond = map->map(map->handle, LV2_TIME__framesPerSecond);
+ timely->urid.time_speed = map->map(map->handle, LV2_TIME__speed);
+
+ timely->multiplier = 1.f;
+
+ timely->pos.speed = 0.f;
+ timely->pos.bar_beat = 0.f;
+ timely->pos.bar = 0;
+ timely->pos.beat_unit = 4;
+ timely->pos.beats_per_bar = 4.f;
+ timely->pos.beats_per_minute = 120.f;
+ timely->pos.frame = 0;
+ timely->pos.frames_per_second = rate;
+
+ _timely_refresh(timely);
+
+ timely->first = true;
+}
+
+static inline void
+timely_set_multiplier(timely_t *timely, float multiplier)
+{
+ const float mul = multiplier / timely->multiplier;
+
+ timely->pos.bar_beat *= mul;
+ timely->pos.beat_unit *= mul;
+ timely->pos.beats_per_bar *= mul;
+
+ timely->multiplier = multiplier;
+
+ _timely_refresh(timely);
+
+ timely->first = true;
+}
+
+static inline int
+timely_advance_body(timely_t *timely, uint32_t size, uint32_t type,
+ const LV2_Atom_Object_Body *body, uint32_t from, uint32_t to)
+{
+ if(timely->first)
+ {
+ timely->first = false;
+
+ // send initial values
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, 0, timely->urid.time_speed, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEAT_UNIT)
+ timely->cb(timely, 0, timely->urid.time_beatUnit, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEATS_PER_BAR)
+ timely->cb(timely, 0, timely->urid.time_beatsPerBar, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEATS_PER_MINUTE)
+ timely->cb(timely, 0, timely->urid.time_beatsPerMinute, timely->data);
+
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, 0, timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
+ timely->cb(timely, 0, timely->urid.time_framesPerSecond, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR)
+ timely->cb(timely, 0, timely->urid.time_bar, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_BEAT)
+ timely->cb(timely, 0, timely->urid.time_barBeat, timely->data);
+ }
+
+ // are we rolling?
+ if(timely->pos.speed != 0.f)
+ {
+ if( (timely->offset.bar == 0) && (timely->pos.bar == 0) )
+ {
+ if(timely->mask & (TIMELY_MASK_BAR | TIMELY_MASK_BAR_WHOLE) )
+ timely->cb(timely, from, timely->urid.time_bar, timely->data);
+ }
+
+ if( (timely->offset.beat == 0) && (timely->pos.bar_beat == 0) )
+ {
+ if(timely->mask & (TIMELY_MASK_BAR_BEAT | TIMELY_MASK_BAR_BEAT_WHOLE) )
+ timely->cb(timely, from, timely->urid.time_barBeat, timely->data);
+ }
+
+ unsigned update_frame = to;
+ for(unsigned i=from; i<to; i++)
+ {
+ if(timely->offset.bar >= timely->frames_per_bar)
+ {
+ timely->pos.bar += 1;
+ timely->offset.bar -= timely->frames_per_bar;
+
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, (update_frame = i), timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_WHOLE)
+ timely->cb(timely, i, timely->urid.time_bar, timely->data);
+ }
+
+ if( (timely->offset.beat >= timely->frames_per_beat) )
+ {
+ timely->pos.bar_beat = floor(timely->pos.bar_beat) + 1;
+ timely->offset.beat -= timely->frames_per_beat;
+
+ if(timely->pos.bar_beat >= timely->pos.beats_per_bar)
+ timely->pos.bar_beat -= timely->pos.beats_per_bar;
+
+ if( (timely->mask & TIMELY_MASK_FRAME) && (update_frame != i) )
+ timely->cb(timely, (update_frame = i), timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_BEAT_WHOLE)
+ timely->cb(timely, i, timely->urid.time_barBeat, timely->data);
+ }
+
+ timely->offset.bar += 1;
+ timely->offset.beat += 1;
+ timely->pos.frame += 1;
+ }
+ }
+
+ // is this a time position event?
+ if( ( (type == timely->urid.atom_object)
+ || (type == timely->urid.atom_blank)
+ || (type == timely->urid.atom_resource) )
+ && body && (body->otype == timely->urid.time_position) )
+ {
+ _timely_deatomize_body(timely, to, size, body);
+ _timely_refresh(timely);
+
+ return 1; // handled a time position event
+ }
+
+ return 0; // did not handle a time position event
+}
+
+static inline int
+timely_advance(timely_t *timely, const LV2_Atom_Object *obj,
+ uint32_t from, uint32_t to)
+{
+ if(obj)
+ return timely_advance_body(timely, obj->atom.size, obj->atom.type, &obj->body, from, to);
+
+ return timely_advance_body(timely, 0, 0, NULL, from, to);
+}
+
+#endif // _LV2_TIMELY_H_