From 74d7751c37d2c991d244c1c23e1a4cd24451ac41 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 6 Aug 2014 01:26:06 +0000 Subject: Order book chapters in build script rather than by bundle name. --- plugins/eg05-scope.lv2/README.txt | 32 -- plugins/eg05-scope.lv2/examploscope.c | 423 ------------------- plugins/eg05-scope.lv2/examploscope.ttl.in | 130 ------ plugins/eg05-scope.lv2/examploscope_ui.c | 655 ----------------------------- plugins/eg05-scope.lv2/manifest.ttl.in | 21 - plugins/eg05-scope.lv2/uris.h | 73 ---- plugins/eg05-scope.lv2/wscript | 73 ---- 7 files changed, 1407 deletions(-) delete mode 100644 plugins/eg05-scope.lv2/README.txt delete mode 100644 plugins/eg05-scope.lv2/examploscope.c delete mode 100644 plugins/eg05-scope.lv2/examploscope.ttl.in delete mode 100644 plugins/eg05-scope.lv2/examploscope_ui.c delete mode 100644 plugins/eg05-scope.lv2/manifest.ttl.in delete mode 100644 plugins/eg05-scope.lv2/uris.h delete mode 100644 plugins/eg05-scope.lv2/wscript (limited to 'plugins/eg05-scope.lv2') diff --git a/plugins/eg05-scope.lv2/README.txt b/plugins/eg05-scope.lv2/README.txt deleted file mode 100644 index 122794c..0000000 --- a/plugins/eg05-scope.lv2/README.txt +++ /dev/null @@ -1,32 +0,0 @@ -== Simple Oscilloscope == - -This plugin displays the waveform of an incoming audio signal using a simple -GTK+Cairo GUI. - -This plugin illustrates: - -- UI <==> Plugin communication via http://lv2plug.in/ns/ext/atom/[LV2 Atom] events -- Atom vector usage and resize-port extension -- Save/Restore UI state by communicating state to backend -- Saving simple key/value state via the http://lv2plug.in/ns/ext/state/[LV2 State] extension -- Cairo drawing and partial exposure - -This plugin intends to outline the basics for building visualization plugins -that rely on atom communication. The UI looks like an oscilloscope, but is not -a real oscilloscope implementation: - -- There is no display synchronisation, results will depend on LV2 host. -- It displays raw audio samples, which a proper scope must not do. -- The display itself just connects min/max line segments. -- No triggering or synchronization. -- No labels, no scale, no calibration, no markers, no numeric readout, etc. - -Addressing these issues is beyond the scope of this example. - -Please see http://lac.linuxaudio.org/2013/papers/36.pdf for scope design, -https://wiki.xiph.org/Videos/Digital_Show_and_Tell for background information, -and http://lists.lv2plug.in/pipermail/devel-lv2plug.in/2013-November/000545.html -for general LV2 related conceptual criticism regarding real-time visualizations. - -A proper oscilloscope based on this example can be found at -https://github.com/x42/sisco.lv2 diff --git a/plugins/eg05-scope.lv2/examploscope.c b/plugins/eg05-scope.lv2/examploscope.c deleted file mode 100644 index 6d1e953..0000000 --- a/plugins/eg05-scope.lv2/examploscope.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - Copyright 2013 Robin Gareus - - 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. -*/ - -#include -#include -#include - -#include "lv2/lv2plug.in/ns/ext/log/log.h" -#include "lv2/lv2plug.in/ns/ext/log/logger.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#include "./uris.h" - -/** - ==== Private Plugin Instance Structure ==== - - In addition to the usual port buffers and features, this plugin stores the - state of the UI here, so it can be opened and closed without losing the - current settings. The UI state is communicated between the plugin and the - UI using atom messages via a sequence port, similarly to MIDI I/O. -*/ -typedef struct { - // Port buffers - float* input[2]; - float* output[2]; - const LV2_Atom_Sequence* control; - LV2_Atom_Sequence* notify; - - // Atom forge and URI mapping - LV2_URID_Map* map; - ScoLV2URIs uris; - LV2_Atom_Forge forge; - LV2_Atom_Forge_Frame frame; - - // Log feature and convenience API - LV2_Log_Log* log; - LV2_Log_Logger logger; - - // Instantiation settings - uint32_t n_channels; - double rate; - - // UI state - bool ui_active; - bool send_settings_to_ui; - float ui_amp; - uint32_t ui_spp; -} EgScope; - -/** ==== Port Indices ==== */ -typedef enum { - SCO_CONTROL = 0, // Event input - SCO_NOTIFY = 1, // Event output - SCO_INPUT0 = 2, // Audio input 0 - SCO_OUTPUT0 = 3, // Audio output 0 - SCO_INPUT1 = 4, // Audio input 1 (stereo variant) - SCO_OUTPUT1 = 5, // Audio input 2 (stereo variant) -} PortIndex; - -/** ==== Instantiate Method ==== */ -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* bundle_path, - const LV2_Feature* const* features) -{ - (void)descriptor; // Unused variable - (void)bundle_path; // Unused variable - - // Allocate and initialise instance structure. - EgScope* self = (EgScope*)calloc(1, sizeof(EgScope)); - if (!self) { - return NULL; - } - - // Get host features - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID__map)) { - self->map = (LV2_URID_Map*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_LOG__log)) { - self->log = (LV2_Log_Log*)features[i]->data; - } - } - - if (!self->map) { - fprintf(stderr, "EgScope.lv2 error: Host does not support urid:map\n"); - free(self); - return NULL; - } - - // Decide which variant to use depending on the plugin URI - if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) { - self->n_channels = 2; - } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) { - self->n_channels = 1; - } else { - free(self); - return NULL; - } - - // Initialise local variables - self->ui_active = false; - self->send_settings_to_ui = false; - self->rate = rate; - - // Set default UI settings - self->ui_spp = 50; - self->ui_amp = 1.0; - - // Map URIs and initialise forge/logger - map_sco_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - lv2_log_logger_init(&self->logger, self->map, self->log); - - return (LV2_Handle)self; -} - -/** ==== Connect Port Method ==== */ -static void -connect_port(LV2_Handle handle, - uint32_t port, - void* data) -{ - EgScope* self = (EgScope*)handle; - - switch ((PortIndex)port) { - case SCO_CONTROL: - self->control = (const LV2_Atom_Sequence*)data; - break; - case SCO_NOTIFY: - self->notify = (LV2_Atom_Sequence*)data; - break; - case SCO_INPUT0: - self->input[0] = (float*)data; - break; - case SCO_OUTPUT0: - self->output[0] = (float*)data; - break; - case SCO_INPUT1: - self->input[1] = (float*)data; - break; - case SCO_OUTPUT1: - self->output[1] = (float*)data; - break; - } -} - -/** - ==== Utility Function: `tx_rawaudio` ==== - - This function forges a message for sending a vector of raw data. The object - is a http://lv2plug.in/ns/ext/atom#Blank[Blank] with a few properties, like: - [source,n3] - -------- - [] - a sco:RawAudio ; - sco:channelID 0 ; - sco:audioData [ 0.0, 0.0, ... ] . - -------- - - where the value of the `sco:audioData` property, `[ 0.0, 0.0, ... ]`, is a - http://lv2plug.in/ns/ext/atom#Vector[Vector] of - http://lv2plug.in/ns/ext/atom#Float[Float]. -*/ -static void -tx_rawaudio(LV2_Atom_Forge* forge, - ScoLV2URIs* uris, - const int32_t channel, - const size_t n_samples, - const float* data) -{ - LV2_Atom_Forge_Frame frame; - - // Forge container object of type 'RawAudio' - lv2_atom_forge_frame_time(forge, 0); - lv2_atom_forge_object(forge, &frame, 0, uris->RawAudio); - - // Add integer 'channelID' property - lv2_atom_forge_key(forge, uris->channelID); - lv2_atom_forge_int(forge, channel); - - // Add vector of floats 'audioData' property - lv2_atom_forge_key(forge, uris->audioData); - lv2_atom_forge_vector( - forge, sizeof(float), uris->atom_Float, n_samples, data); - - // Close off object - lv2_atom_forge_pop(forge, &frame); -} - -/** ==== Run Method ==== */ -static void -run(LV2_Handle handle, uint32_t n_samples) -{ - EgScope* self = (EgScope*)handle; - - /* Ensure notify port buffer is large enough to hold all audio-samples and - configuration settings. A minimum size was requested in the .ttl file, - but check here just to be sure. - - TODO: Explain these magic numbers. - */ - const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels; - const uint32_t space = self->notify->atom.size; - if (space < size + 128) { - /* Insufficient space, report error and do nothing. Note that a - real-time production plugin mustn't call log functions in run(), but - this can be useful for debugging and example purposes. - */ - lv2_log_error(&self->logger, "Buffer size is insufficient\n"); - return; - } - - // Prepare forge buffer and initialize atom-sequence - lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space); - lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); - - /* Send settings to UI - - The plugin can continue to run while the UI is closed and re-opened. - The state and settings of the UI are kept here and transmitted to the UI - every time it asks for them or if the user initializes a 'load preset'. - */ - if (self->send_settings_to_ui && self->ui_active) { - self->send_settings_to_ui = false; - // Forge container object of type 'ui_state' - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_frame_time(&self->forge, 0); - lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State); - - // Add UI state as properties - lv2_atom_forge_key(&self->forge, self->uris.ui_spp); - lv2_atom_forge_int(&self->forge, self->ui_spp); - lv2_atom_forge_key(&self->forge, self->uris.ui_amp); - lv2_atom_forge_float(&self->forge, self->ui_amp); - lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate); - lv2_atom_forge_float(&self->forge, self->rate); - lv2_atom_forge_pop(&self->forge, &frame); - } - - // Process incoming events from GUI - if (self->control) { - const LV2_Atom_Event* ev = lv2_atom_sequence_begin( - &(self->control)->body); - // For each incoming message... - while (!lv2_atom_sequence_is_end( - &self->control->body, self->control->atom.size, ev)) { - // If the event is an atom:Blank object - if (ev->body.type == self->uris.atom_Blank) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == self->uris.ui_On) { - // If the object is a ui-on, the UI was activated - self->ui_active = true; - self->send_settings_to_ui = true; - } else if (obj->body.otype == self->uris.ui_Off) { - // If the object is a ui-off, the UI was closed - self->ui_active = false; - } else if (obj->body.otype == self->uris.ui_State) { - // If the object is a ui-state, it's the current UI settings - const LV2_Atom* spp = NULL; - const LV2_Atom* amp = NULL; - lv2_atom_object_get(obj, self->uris.ui_spp, &spp, - self->uris.ui_amp, &, - 0); - if (spp) { - self->ui_spp = ((const LV2_Atom_Int*)spp)->body; - } - if (amp) { - self->ui_amp = ((const LV2_Atom_Float*)amp)->body; - } - } - } - ev = lv2_atom_sequence_next(ev); - } - } - - // Process audio data - for (uint32_t c = 0; c < self->n_channels; ++c) { - if (self->ui_active) { - // If UI is active, send raw audio data to UI - tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]); - } - // If not processing audio in-place, forward audio - if (self->input[c] != self->output[c]) { - memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); - } - } - - // Close off sequence - lv2_atom_forge_pop(&self->forge, &self->frame); -} - -static void -cleanup(LV2_Handle handle) -{ - free(handle); -} - - -/** - ==== State Methods ==== - - This plugin's state consists of two basic properties: one `int` and one - `float`. No files are used. Note these values are POD, but not portable, - since different machines may have a different integer endianness or floating - point format. However, since standard Atom types are used, a good host will - be able to save them portably as text anyway. -*/ -static LV2_State_Status -state_save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - EgScope* self = (EgScope*)instance; - if (!self) { - return LV2_STATE_SUCCESS; - } - - store(handle, self->uris.ui_spp, - (void*)&self->ui_spp, sizeof(uint32_t), - self->uris.atom_Int, - LV2_STATE_IS_POD); - - store(handle, self->uris.ui_amp, - (void*)&self->ui_amp, sizeof(float), - self->uris.atom_Float, - LV2_STATE_IS_POD); - - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -state_restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - EgScope* self = (EgScope*)instance; - - size_t size; - uint32_t type; - uint32_t valflags; - - const void* spp = retrieve( - handle, self->uris.ui_spp, &size, &type, &valflags); - if (spp && size == sizeof(uint32_t) && type == self->uris.atom_Int) { - self->ui_spp = *((const uint32_t*)spp); - self->send_settings_to_ui = true; - } - - const void* amp = retrieve( - handle, self->uris.ui_amp, &size, &type, &valflags); - if (amp && size == sizeof(float) && type == self->uris.atom_Float) { - self->ui_amp = *((const float*)amp); - self->send_settings_to_ui = true; - } - - return LV2_STATE_SUCCESS; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2_State_Interface state = { state_save, state_restore }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return NULL; -} - -/** ==== Plugin Descriptors ==== */ -static const LV2_Descriptor descriptor_mono = { - SCO_URI "#Mono", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data -}; - -static const LV2_Descriptor descriptor_stereo = { - SCO_URI "#Stereo", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor_mono; - case 1: - return &descriptor_stereo; - default: - return NULL; - } -} diff --git a/plugins/eg05-scope.lv2/examploscope.ttl.in b/plugins/eg05-scope.lv2/examploscope.ttl.in deleted file mode 100644 index 0b76962..0000000 --- a/plugins/eg05-scope.lv2/examploscope.ttl.in +++ /dev/null @@ -1,130 +0,0 @@ -@prefix atom: . -@prefix bufsz: . -@prefix doap: . -@prefix foaf: . -@prefix lv2: . -@prefix rdfs: . -@prefix ui: . -@prefix urid: . -@prefix rsz: . -@prefix state: . -@prefix egscope: . - - - a foaf:Person ; - foaf:name "Robin Gareus" ; - foaf:mbox ; - foaf:homepage . - - - a doap:Project ; - doap:maintainer ; - doap:name "Example Scope" . - -egscope:Mono - a lv2:Plugin, lv2:AnalyserPlugin ; - doap:name "Example Scope (Mono)" ; - lv2:project ; - doap:license ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData state:interface ; - ui:ui egscope:ui ; - lv2:port [ - a atom:AtomPort , - lv2:InputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a atom:AtomPort , - lv2:OutputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify" ; - # 8192 * sizeof(float) + LV2-Atoms - rsz:minimumSize 32832; - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 2 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 3 ; - lv2:symbol "out" ; - lv2:name "Out" - ] . - - -egscope:Stereo - a lv2:Plugin, lv2:AnalyserPlugin ; - doap:name "Example Scope (Stereo)" ; - lv2:project ; - doap:license ; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData state:interface ; - ui:ui egscope:ui ; - lv2:port [ - a atom:AtomPort , - lv2:InputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 0 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a atom:AtomPort , - lv2:OutputPort ; - atom:bufferType atom:Sequence ; - lv2:designation lv2:control ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify" ; - rsz:minimumSize 65664; - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 2 ; - lv2:symbol "in0" ; - lv2:name "InL" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 3 ; - lv2:symbol "out0" ; - lv2:name "OutL" - ] , [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 4 ; - lv2:symbol "in1" ; - lv2:name "InR" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 5 ; - lv2:symbol "out1" ; - lv2:name "OutR" - ] . - - -egscope:ui - a ui:GtkUI ; - lv2:requiredFeature urid:map ; - ui:portNotification [ - ui:plugin egscope:Mono ; - lv2:symbol "notify" ; - ui:notifyType atom:Blank - ] , [ - ui:plugin egscope:Stereo ; - lv2:symbol "notify" ; - ui:notifyType atom:Blank - ] . diff --git a/plugins/eg05-scope.lv2/examploscope_ui.c b/plugins/eg05-scope.lv2/examploscope_ui.c deleted file mode 100644 index a8c1e2c..0000000 --- a/plugins/eg05-scope.lv2/examploscope_ui.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - Copyright 2013 Robin Gareus - - 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. -*/ - -#include -#include - -#include -#include - -#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" -#include "./uris.h" - -// Drawing area size -#define DAWIDTH (640) -#define DAHEIGHT (200) - -/** - Max continuous points on path. Many short-path segments are - expensive|inefficient long paths are not supported by all surfaces (usually - its a miter - not point - limit, depending on used cairo backend) -*/ -#define MAX_CAIRO_PATH (128) - -/** - Representation of the raw audio-data for display (min | max) values for a - given 'index' position. -*/ -typedef struct { - float data_min[DAWIDTH]; - float data_max[DAWIDTH]; - - uint32_t idx; - uint32_t sub; -} ScoChan; - -typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - ScoLV2URIs uris; - - LV2UI_Write_Function write; - LV2UI_Controller controller; - - GtkWidget* hbox; - GtkWidget* vbox; - GtkWidget* sep[2]; - GtkWidget* darea; - GtkWidget* btn_pause; - GtkWidget* lbl_speed; - GtkWidget* lbl_amp; - GtkWidget* spb_speed; - GtkWidget* spb_amp; - GtkAdjustment* spb_speed_adj; - GtkAdjustment* spb_amp_adj; - - ScoChan chn[2]; - uint32_t stride; - uint32_t n_channels; - bool paused; - float rate; -} EgScopeUI; - - -/** Send current UI settings to backend. */ -static void -send_ui_state(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - - // Use local buffer on the stack to build atom - uint8_t obj_buf[1024]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - // Start a ui:State object - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_State); - - // msg[samples-per-pixel] = integer - lv2_atom_forge_key(&ui->forge, ui->uris.ui_spp); - lv2_atom_forge_int(&ui->forge, ui->stride); - - // msg[amplitude] = float - lv2_atom_forge_key(&ui->forge, ui->uris.ui_amp); - lv2_atom_forge_float(&ui->forge, gain); - - // Finish ui:State object - lv2_atom_forge_pop(&ui->forge, &frame); - - // Send message to plugin port '0' - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** Notify backend that UI is closed. */ -static void -send_ui_disable(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - send_ui_state(handle); - - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_Off); - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** - Notify backend that UI is active. - - The plugin should send state and enable data transmission. -*/ -static void -send_ui_enable(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_On); - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); -} - -/** Gtk widget callback. */ -static gboolean -on_cfg_changed(GtkWidget* widget, gpointer data) -{ - send_ui_state(data); - return TRUE; -} - -/** - Gdk drawing area draw callback. - - Called in Gtk's main thread and uses Cairo to draw the data. -*/ -static gboolean -on_expose_event(GtkWidget* widget, GdkEventExpose* ev, gpointer data) -{ - EgScopeUI* ui = (EgScopeUI*)data; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - - // Get cairo type for the gtk window - cairo_t* cr; - cr = gdk_cairo_create(ui->darea->window); - - // Limit cairo-drawing to exposed area - cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); - cairo_clip(cr); - - // Clear background - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels); - cairo_fill(cr); - - cairo_set_line_width(cr, 1.0); - - const uint32_t start = ev->area.x; - const uint32_t end = ev->area.x + ev->area.width; - - assert(start < DAWIDTH); - assert(end <= DAWIDTH); - assert(start < end); - - for (uint32_t c = 0; c < ui->n_channels; ++c) { - ScoChan* chn = &ui->chn[c]; - - /* Drawing area Y-position of given sample-value. - * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5) - * also the cairo Y-axis points upwards (hence 'minus value') - * - * == ( DAHEIGHT * (CHN) // channel offset - * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0' - * - (DAHEIGHT / 2) * (VAL) * (GAIN) - * ) - */ - const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f; - const float chn_y_scale = DAHEIGHT * 0.5f * gain; - -#define CYPOS(VAL) (chn_y_offset - (VAL) * chn_y_scale) - - cairo_save(cr); - - /* Restrict drawing to current channel area, don't bleed drawing into - neighboring channels. */ - cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT); - cairo_clip(cr); - - // Set color of wave-form - cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); - - /* This is a somewhat 'smart' mechanism to plot audio data using - alternating up/down line-directions. It works well for both cases: - 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but - is not ideal for either. */ - if (start == chn->idx) { - cairo_move_to(cr, start - 0.5, CYPOS(0)); - } else { - cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start])); - } - - uint32_t pathlength = 0; - for (uint32_t i = start; i < end; ++i) { - if (i == chn->idx) { - continue; - } else if (i % 2) { - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - ++pathlength; - } else { - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - ++pathlength; - } - - /** Limit the max cairo path length. This is an optimization trade - off: too short path: high load CPU/GPU load. too-long path: - bad anti-aliasing, or possibly lost points */ - if (pathlength > MAX_CAIRO_PATH) { - pathlength = 0; - cairo_stroke(cr); - if (i % 2) { - cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i])); - } else { - cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i])); - } - } - } - - if (pathlength > 0) { - cairo_stroke(cr); - } - - // Draw current position vertical line if display is slow - if (ui->stride >= ui->rate / 4800.0f || ui->paused) { - cairo_set_source_rgba(cr, .9, .2, .2, .6); - cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c); - cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1)); - cairo_stroke(cr); - } - - // Undo the 'clipping' restriction - cairo_restore(cr); - - // Channel separator - if (c > 0) { - cairo_set_source_rgba(cr, .5, .5, .5, 1.0); - cairo_move_to(cr, 0, DAHEIGHT * c - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * c - .5); - cairo_stroke(cr); - } - - // Zero scale line - cairo_set_source_rgba(cr, .3, .3, .7, .5); - cairo_move_to(cr, 0, DAHEIGHT * (c + .5) - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * (c + .5) - .5); - cairo_stroke(cr); - } - - cairo_destroy(cr); - return TRUE; -} - -/** - Parse raw audio data and prepare for later drawing. - - Note this is a toy example, which is really a waveform display, not an - oscilloscope. A serious scope would not display samples as is. - - Signals above ~ 1/10 of the sampling-rate will not yield a useful visual - display and result in a rather unintuitive representation of the actual - waveform. - - Ideally the audio-data would be buffered and upsampled here and after that - written in a display buffer for later use. - - For more information, see - https://wiki.xiph.org/Videos/Digital_Show_and_Tell - http://lac.linuxaudio.org/2013/papers/36.pdf - and https://github.com/x42/sisco.lv2 -*/ -static int -process_channel(EgScopeUI* ui, - ScoChan* chn, - const size_t n_elem, - float const* data, - uint32_t* idx_start, - uint32_t* idx_end) -{ - int overflow = 0; - *idx_start = chn->idx; - for (size_t i = 0; i < n_elem; ++i) { - if (data[i] < chn->data_min[chn->idx]) { - chn->data_min[chn->idx] = data[i]; - } - if (data[i] > chn->data_max[chn->idx]) { - chn->data_max[chn->idx] = data[i]; - } - if (++chn->sub >= ui->stride) { - chn->sub = 0; - chn->idx = (chn->idx + 1) % DAWIDTH; - if (chn->idx == 0) { - ++overflow; - } - chn->data_min[chn->idx] = 1.0; - chn->data_max[chn->idx] = -1.0; - } - } - *idx_end = chn->idx; - return overflow; -} - -/** - Called via port_event() which is called by the host, typically at a rate of - around 25 FPS. -*/ -static void -update_scope(EgScopeUI* ui, - const int32_t channel, - const size_t n_elem, - float const* data) -{ - // Never trust input data which could lead to application failure. - if (channel < 0 || (uint32_t)channel > ui->n_channels) { - return; - } - - // Update state in sync with 1st channel - if (channel == 0) { - ui->stride = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_speed)); - const bool paused = gtk_toggle_button_get_active( - GTK_TOGGLE_BUTTON(ui->btn_pause)); - - if (paused != ui->paused) { - ui->paused = paused; - gtk_widget_queue_draw(ui->darea); - } - } - if (ui->paused) { - return; - } - - uint32_t idx_start; // Display pixel start - uint32_t idx_end; // Display pixel end - int overflow; // Received more audio-data than display-pixel - - // Process this channel's audio-data for display - ScoChan* chn = &ui->chn[channel]; - overflow = process_channel(ui, chn, n_elem, data, &idx_start, &idx_end); - - // Signal gtk's main thread to redraw the widget after the last channel - if ((uint32_t)channel + 1 == ui->n_channels) { - if (overflow > 1) { - // Redraw complete widget - gtk_widget_queue_draw(ui->darea); - } else if (idx_end > idx_start) { - // Redraw area between start -> end pixel - gtk_widget_queue_draw_area(ui->darea, idx_start - 2, 0, 3 - + idx_end - idx_start, - DAHEIGHT * ui->n_channels); - } else if (idx_end < idx_start) { - // Wrap-around: redraw area between 0->start AND end->right-end - gtk_widget_queue_draw_area( - ui->darea, - idx_start - 2, 0, - 3 + DAWIDTH - idx_start, DAHEIGHT * ui->n_channels); - gtk_widget_queue_draw_area( - ui->darea, - 0, 0, - idx_end + 1, DAHEIGHT * ui->n_channels); - } - } -} - -static LV2UI_Handle -instantiate(const LV2UI_Descriptor* descriptor, - const char* plugin_uri, - const char* bundle_path, - LV2UI_Write_Function write_function, - LV2UI_Controller controller, - LV2UI_Widget* widget, - const LV2_Feature* const* features) -{ - EgScopeUI* ui = (EgScopeUI*)malloc(sizeof(EgScopeUI)); - - if (!ui) { - fprintf(stderr, "EgScope.lv2 UI: out of memory\n"); - return NULL; - } - - ui->map = NULL; - *widget = NULL; - - if (!strcmp(plugin_uri, SCO_URI "#Mono")) { - ui->n_channels = 1; - } else if (!strcmp(plugin_uri, SCO_URI "#Stereo")) { - ui->n_channels = 2; - } else { - free(ui); - return NULL; - } - - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { - ui->map = (LV2_URID_Map*)features[i]->data; - } - } - - if (!ui->map) { - fprintf(stderr, "EgScope.lv2 UI: Host does not support urid:map\n"); - free(ui); - return NULL; - } - - // Initialize private data structure - ui->write = write_function; - ui->controller = controller; - - ui->vbox = NULL; - ui->hbox = NULL; - ui->darea = NULL; - ui->stride = 25; - ui->paused = false; - ui->rate = 48000; - - ui->chn[0].idx = 0; - ui->chn[0].sub = 0; - ui->chn[1].idx = 0; - ui->chn[1].sub = 0; - memset(ui->chn[0].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[0].data_max, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_max, 0, sizeof(float) * DAWIDTH); - - map_sco_uris(ui->map, &ui->uris); - lv2_atom_forge_init(&ui->forge, ui->map); - - // Setup UI - ui->hbox = gtk_hbox_new(FALSE, 0); - ui->vbox = gtk_vbox_new(FALSE, 0); - - ui->darea = gtk_drawing_area_new(); - gtk_widget_set_size_request(ui->darea, DAWIDTH, DAHEIGHT * ui->n_channels); - - ui->lbl_speed = gtk_label_new("Samples/Pixel"); - ui->lbl_amp = gtk_label_new("Amplitude"); - - ui->sep[0] = gtk_hseparator_new(); - ui->sep[1] = gtk_label_new(""); - ui->btn_pause = gtk_toggle_button_new_with_label("Pause"); - - ui->spb_speed_adj = (GtkAdjustment*)gtk_adjustment_new( - 25.0, 1.0, 1000.0, 1.0, 5.0, 0.0); - ui->spb_speed = gtk_spin_button_new(ui->spb_speed_adj, 1.0, 0); - - ui->spb_amp_adj = (GtkAdjustment*)gtk_adjustment_new( - 1.0, 0.1, 6.0, 0.1, 1.0, 0.0); - ui->spb_amp = gtk_spin_button_new(ui->spb_amp_adj, 0.1, 1); - - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->darea, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->vbox, FALSE, FALSE, 4); - - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[0], FALSE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[1], TRUE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_pause, FALSE, FALSE, 2); - - g_signal_connect(G_OBJECT(ui->darea), "expose_event", - G_CALLBACK(on_expose_event), ui); - g_signal_connect(G_OBJECT(ui->spb_amp), "value-changed", - G_CALLBACK(on_cfg_changed), ui); - g_signal_connect(G_OBJECT(ui->spb_speed), "value-changed", - G_CALLBACK(on_cfg_changed), ui); - - *widget = ui->hbox; - - /* Send UIOn message to plugin, which will request state and enable message - transmission. */ - send_ui_enable(ui); - - return ui; -} - -static void -cleanup(LV2UI_Handle handle) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - /* Send UIOff message to plugin, which will save state and disable message - * transmission. */ - send_ui_disable(ui); - gtk_widget_destroy(ui->darea); - free(ui); -} - -static int -recv_raw_audio(EgScopeUI* ui, const LV2_Atom_Object* obj) -{ - const LV2_Atom* chan_val = NULL; - const LV2_Atom* data_val = NULL; - const int n_props = lv2_atom_object_get( - obj, - ui->uris.channelID, &chan_val, - ui->uris.audioData, &data_val, - NULL); - - if (n_props != 2 || - chan_val->type != ui->uris.atom_Int || - data_val->type != ui->uris.atom_Vector) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt audio message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t chn = ((const LV2_Atom_Int*)chan_val)->body; - const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*)data_val; - if (vec->body.child_type != ui->uris.atom_Float) { - return 1; // Vector has incorrect element type - } - - // Number of elements = (total size - header size) / element size - const size_t n_elem = ((data_val->size - sizeof(LV2_Atom_Vector_Body)) - / sizeof(float)); - - // Float elements immediately follow the vector body header - const float* data = (const float*)(&vec->body + 1); - - // Update display - update_scope(ui, chn, n_elem, data); - return 0; -} - -static int -recv_ui_state(EgScopeUI* ui, const LV2_Atom_Object* obj) -{ - const LV2_Atom* spp_val = NULL; - const LV2_Atom* amp_val = NULL; - const LV2_Atom* rate_val = NULL; - const int n_props = lv2_atom_object_get( - obj, - ui->uris.ui_spp, &spp_val, - ui->uris.ui_amp, &_val, - ui->uris.param_sampleRate, &rate_val, - NULL); - - if (n_props != 3 || - spp_val->type != ui->uris.atom_Int || - amp_val->type != ui->uris.atom_Float || - rate_val->type != ui->uris.atom_Float) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt state message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t spp = ((const LV2_Atom_Int*)spp_val)->body; - const float amp = ((const LV2_Atom_Float*)amp_val)->body; - const float rate = ((const LV2_Atom_Float*)rate_val)->body; - - // Update UI - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_speed), spp); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_amp), amp); - ui->rate = rate; - - return 0; -} - -/** - Receive data from the DSP-backend. - - This is called by the host, typically at a rate of around 25 FPS. - - Ideally this happens regularly and with relatively low latency, but there - are no hard guarantees about message delivery. -*/ -static void -port_event(LV2UI_Handle handle, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - EgScopeUI* ui = (EgScopeUI*)handle; - const LV2_Atom* atom = (const LV2_Atom*)buffer; - - /* Check type of data received - * - format == 0: Control port event (float) - * - format > 0: Message (atom) - */ - if (format == ui->uris.atom_eventTransfer && - atom->type == ui->uris.atom_Blank) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; - if (obj->body.otype == ui->uris.RawAudio) { - recv_raw_audio(ui, obj); - } else if (obj->body.otype == ui->uris.ui_State) { - recv_ui_state(ui, obj); - } - } -} - -static const LV2UI_Descriptor descriptor = { - SCO_URI "#ui", - instantiate, - cleanup, - port_event, - NULL -}; - -LV2_SYMBOL_EXPORT -const LV2UI_Descriptor* -lv2ui_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor; - default: - return NULL; - } -} diff --git a/plugins/eg05-scope.lv2/manifest.ttl.in b/plugins/eg05-scope.lv2/manifest.ttl.in deleted file mode 100644 index a64aff1..0000000 --- a/plugins/eg05-scope.lv2/manifest.ttl.in +++ /dev/null @@ -1,21 +0,0 @@ -@prefix lv2: . -@prefix rdfs: . -@prefix ui: . - -# ==== Mono plugin variant ==== - - a lv2:Plugin ; - lv2:binary ; - rdfs:seeAlso . - -# ==== Stereo plugin variant ==== - - a lv2:Plugin ; - lv2:binary ; - rdfs:seeAlso . - -# ==== Gtk 2.0 UI ==== - - a ui:GtkUI ; - ui:binary ; - rdfs:seeAlso . diff --git a/plugins/eg05-scope.lv2/uris.h b/plugins/eg05-scope.lv2/uris.h deleted file mode 100644 index bd57551..0000000 --- a/plugins/eg05-scope.lv2/uris.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright 2013 Robin Gareus - - 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 SCO_URIS_H -#define SCO_URIS_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/parameters/parameters.h" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" - -#define SCO_URI "http://lv2plug.in/plugins/eg-scope" - -typedef struct { - // URIs defined in LV2 specifications - LV2_URID atom_Blank; - LV2_URID atom_Vector; - LV2_URID atom_Float; - LV2_URID atom_Int; - LV2_URID atom_eventTransfer; - LV2_URID param_sampleRate; - - /* URIs defined for this plugin. It is best to re-use existing URIs as - much as possible, but plugins may need more vocabulary specific to their - needs. These are used as types and properties for plugin:UI - communication, as well as for saving state. */ - LV2_URID RawAudio; - LV2_URID channelID; - LV2_URID audioData; - LV2_URID ui_On; - LV2_URID ui_Off; - LV2_URID ui_State; - LV2_URID ui_spp; - LV2_URID ui_amp; -} ScoLV2URIs; - -static inline void -map_sco_uris(LV2_URID_Map* map, ScoLV2URIs* uris) -{ - uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank); - uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->param_sampleRate = map->map(map->handle, LV2_PARAMETERS__sampleRate); - - /* Note the convention that URIs for types are capitalized, and URIs for - everything else (mainly properties) are not, just as in LV2 - specifications. */ - uris->RawAudio = map->map(map->handle, SCO_URI "#RawAudio"); - uris->audioData = map->map(map->handle, SCO_URI "#audioData"); - uris->channelID = map->map(map->handle, SCO_URI "#channelID"); - uris->ui_On = map->map(map->handle, SCO_URI "#UIOn"); - uris->ui_Off = map->map(map->handle, SCO_URI "#UIOff"); - uris->ui_State = map->map(map->handle, SCO_URI "#UIState"); - uris->ui_spp = map->map(map->handle, SCO_URI "#ui-spp"); - uris->ui_amp = map->map(map->handle, SCO_URI "#ui-amp"); -} - -#endif /* SCO_URIS_H */ diff --git a/plugins/eg05-scope.lv2/wscript b/plugins/eg05-scope.lv2/wscript deleted file mode 100644 index 807d15d..0000000 --- a/plugins/eg05-scope.lv2/wscript +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf -import re - -# Variables for 'waf dist' -APPNAME = 'eg-scope.lv2' -VERSION = '1.0.0' - -# Mandatory variables -top = '.' -out = 'build' - -def options(opt): - opt.load('compiler_c') - autowaf.set_options(opt) - -def configure(conf): - conf.load('compiler_c') - autowaf.configure(conf) - autowaf.set_c99_mode(conf) - autowaf.display_header('Scope Configuration') - - if not autowaf.is_child(): - autowaf.check_pkg(conf, 'lv2', atleast_version='1.2.1', uselib_store='LV2') - - autowaf.check_pkg(conf, 'cairo', uselib_store='CAIRO', - atleast_version='1.8.10', mandatory=True) - autowaf.check_pkg(conf, 'gtk+-2.0', uselib_store='GTK2', - atleast_version='2.18.0', mandatory=False) - - autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR) - print('') - -def build(bld): - bundle = 'eg-scope.lv2' - - # Make a pattern for shared objects without the 'lib' prefix - module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN) - module_ext = module_pat[module_pat.rfind('.'):] - - # Build manifest.ttl by substitution (for portable lib extension) - for i in ['manifest.ttl', 'examploscope.ttl']: - bld(features = 'subst', - source = i + '.in', - target = '%s/%s' % (bundle, i), - install_path = '${LV2DIR}/%s' % bundle, - LIB_EXT = module_ext) - - # Use LV2 headers from parent directory if building as a sub-project - includes = ['.'] - if autowaf.is_child: - includes += ['../..'] - - # Build plugin library - obj = bld(features = 'c cshlib', - source = 'examploscope.c', - name = 'examploscope', - target = '%s/examploscope' % bundle, - install_path = '${LV2DIR}/%s' % bundle, - use = 'LV2', - includes = includes) - obj.env.cshlib_PATTERN = module_pat - - # Build UI library - if bld.is_defined('HAVE_GTK2'): - obj = bld(features = 'c cshlib', - source = 'examploscope_ui.c', - name = 'examploscope_ui', - target = '%s/examploscope_ui' % bundle, - install_path = '${LV2DIR}/%s' % bundle, - use = 'GTK2 CAIRO LV2', - includes = includes) - obj.env.cshlib_PATTERN = module_pat -- cgit v1.2.1