1 /*
2 Copyright 2016 David Robillard <d@drobilla.net>
3 Copyright 2013 Robin Gareus <robin@gareus.org>
4
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8
9 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "./uris.h"
19
20 #include "lv2/atom/atom.h"
21 #include "lv2/atom/forge.h"
22 #include "lv2/atom/util.h"
23 #include "lv2/core/lv2.h"
24 #include "lv2/core/lv2_util.h"
25 #include "lv2/log/log.h"
26 #include "lv2/log/logger.h"
27 #include "lv2/state/state.h"
28 #include "lv2/urid/urid.h"
29
30 #include <stdbool.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 /**
37 ==== Private Plugin Instance Structure ====
38
39 In addition to the usual port buffers and features, this plugin stores the
40 state of the UI here, so it can be opened and closed without losing the
41 current settings. The UI state is communicated between the plugin and the
42 UI using atom messages via a sequence port, similarly to MIDI I/O.
43 */
44 typedef struct {
45 // Port buffers
46 float* input[2];
47 float* output[2];
48 const LV2_Atom_Sequence* control;
49 LV2_Atom_Sequence* notify;
50
51 // Atom forge and URI mapping
52 LV2_URID_Map* map;
53 ScoLV2URIs uris;
54 LV2_Atom_Forge forge;
55 LV2_Atom_Forge_Frame frame;
56
57 // Log feature and convenience API
58 LV2_Log_Logger logger;
59
60 // Instantiation settings
61 uint32_t n_channels;
62 double rate;
63
64 // UI state
65 bool ui_active;
66 bool send_settings_to_ui;
67 float ui_amp;
68 uint32_t ui_spp;
69 } EgScope;
70
71 /** ==== Port Indices ==== */
72 typedef enum {
73 SCO_CONTROL = 0, // Event input
74 SCO_NOTIFY = 1, // Event output
75 SCO_INPUT0 = 2, // Audio input 0
76 SCO_OUTPUT0 = 3, // Audio output 0
77 SCO_INPUT1 = 4, // Audio input 1 (stereo variant)
78 SCO_OUTPUT1 = 5, // Audio input 2 (stereo variant)
79 } PortIndex;
80
81 /** ==== Instantiate Method ==== */
82 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)83 instantiate(const LV2_Descriptor* descriptor,
84 double rate,
85 const char* bundle_path,
86 const LV2_Feature* const* features)
87 {
88 (void)descriptor; // Unused variable
89 (void)bundle_path; // Unused variable
90
91 // Allocate and initialise instance structure.
92 EgScope* self = (EgScope*)calloc(1, sizeof(EgScope));
93 if (!self) {
94 return NULL;
95 }
96
97 // Get host features
98 const char* missing = lv2_features_query(
99 features,
100 LV2_LOG__log, &self->logger.log, false,
101 LV2_URID__map, &self->map, true,
102 NULL);
103 lv2_log_logger_set_map(&self->logger, self->map);
104 if (missing) {
105 lv2_log_error(&self->logger, "Missing feature <%s>\n", missing);
106 free(self);
107 return NULL;
108 }
109
110 // Decide which variant to use depending on the plugin URI
111 if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) {
112 self->n_channels = 2;
113 } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) {
114 self->n_channels = 1;
115 } else {
116 free(self);
117 return NULL;
118 }
119
120 // Initialise local variables
121 self->ui_active = false;
122 self->send_settings_to_ui = false;
123 self->rate = rate;
124
125 // Set default UI settings
126 self->ui_spp = 50;
127 self->ui_amp = 1.0;
128
129 // Map URIs and initialise forge/logger
130 map_sco_uris(self->map, &self->uris);
131 lv2_atom_forge_init(&self->forge, self->map);
132
133 return (LV2_Handle)self;
134 }
135
136 /** ==== Connect Port Method ==== */
137 static void
connect_port(LV2_Handle handle,uint32_t port,void * data)138 connect_port(LV2_Handle handle,
139 uint32_t port,
140 void* data)
141 {
142 EgScope* self = (EgScope*)handle;
143
144 switch ((PortIndex)port) {
145 case SCO_CONTROL:
146 self->control = (const LV2_Atom_Sequence*)data;
147 break;
148 case SCO_NOTIFY:
149 self->notify = (LV2_Atom_Sequence*)data;
150 break;
151 case SCO_INPUT0:
152 self->input[0] = (float*)data;
153 break;
154 case SCO_OUTPUT0:
155 self->output[0] = (float*)data;
156 break;
157 case SCO_INPUT1:
158 self->input[1] = (float*)data;
159 break;
160 case SCO_OUTPUT1:
161 self->output[1] = (float*)data;
162 break;
163 }
164 }
165
166 /**
167 ==== Utility Function: `tx_rawaudio` ====
168
169 This function forges a message for sending a vector of raw data. The object
170 is a http://lv2plug.in/ns/ext/atom#Blank[Blank] with a few properties, like:
171 [source,n3]
172 --------
173 []
174 a sco:RawAudio ;
175 sco:channelID 0 ;
176 sco:audioData [ 0.0, 0.0, ... ] .
177 --------
178
179 where the value of the `sco:audioData` property, `[ 0.0, 0.0, ... ]`, is a
180 http://lv2plug.in/ns/ext/atom#Vector[Vector] of
181 http://lv2plug.in/ns/ext/atom#Float[Float].
182 */
183 static void
tx_rawaudio(LV2_Atom_Forge * forge,ScoLV2URIs * uris,const int32_t channel,const size_t n_samples,const float * data)184 tx_rawaudio(LV2_Atom_Forge* forge,
185 ScoLV2URIs* uris,
186 const int32_t channel,
187 const size_t n_samples,
188 const float* data)
189 {
190 LV2_Atom_Forge_Frame frame;
191
192 // Forge container object of type 'RawAudio'
193 lv2_atom_forge_frame_time(forge, 0);
194 lv2_atom_forge_object(forge, &frame, 0, uris->RawAudio);
195
196 // Add integer 'channelID' property
197 lv2_atom_forge_key(forge, uris->channelID);
198 lv2_atom_forge_int(forge, channel);
199
200 // Add vector of floats 'audioData' property
201 lv2_atom_forge_key(forge, uris->audioData);
202 lv2_atom_forge_vector(
203 forge, sizeof(float), uris->atom_Float, n_samples, data);
204
205 // Close off object
206 lv2_atom_forge_pop(forge, &frame);
207 }
208
209 /** ==== Run Method ==== */
210 static void
run(LV2_Handle handle,uint32_t n_samples)211 run(LV2_Handle handle, uint32_t n_samples)
212 {
213 EgScope* self = (EgScope*)handle;
214
215 /* Ensure notify port buffer is large enough to hold all audio-samples and
216 configuration settings. A minimum size was requested in the .ttl file,
217 but check here just to be sure.
218
219 TODO: Explain these magic numbers.
220 */
221 const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels;
222 const uint32_t space = self->notify->atom.size;
223 if (space < size + 128) {
224 /* Insufficient space, report error and do nothing. Note that a
225 real-time production plugin mustn't call log functions in run(), but
226 this can be useful for debugging and example purposes.
227 */
228 lv2_log_error(&self->logger, "Buffer size is insufficient\n");
229 return;
230 }
231
232 // Prepare forge buffer and initialize atom-sequence
233 lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space);
234 lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0);
235
236 /* Send settings to UI
237
238 The plugin can continue to run while the UI is closed and re-opened.
239 The state and settings of the UI are kept here and transmitted to the UI
240 every time it asks for them or if the user initializes a 'load preset'.
241 */
242 if (self->send_settings_to_ui && self->ui_active) {
243 self->send_settings_to_ui = false;
244 // Forge container object of type 'ui_state'
245 LV2_Atom_Forge_Frame frame;
246 lv2_atom_forge_frame_time(&self->forge, 0);
247 lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State);
248
249 // Add UI state as properties
250 lv2_atom_forge_key(&self->forge, self->uris.ui_spp);
251 lv2_atom_forge_int(&self->forge, self->ui_spp);
252 lv2_atom_forge_key(&self->forge, self->uris.ui_amp);
253 lv2_atom_forge_float(&self->forge, self->ui_amp);
254 lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate);
255 lv2_atom_forge_float(&self->forge, self->rate);
256 lv2_atom_forge_pop(&self->forge, &frame);
257 }
258
259 // Process incoming events from GUI
260 if (self->control) {
261 const LV2_Atom_Event* ev = lv2_atom_sequence_begin(
262 &(self->control)->body);
263 // For each incoming message...
264 while (!lv2_atom_sequence_is_end(
265 &self->control->body, self->control->atom.size, ev)) {
266 // If the event is an atom:Blank object
267 if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) {
268 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body;
269 if (obj->body.otype == self->uris.ui_On) {
270 // If the object is a ui-on, the UI was activated
271 self->ui_active = true;
272 self->send_settings_to_ui = true;
273 } else if (obj->body.otype == self->uris.ui_Off) {
274 // If the object is a ui-off, the UI was closed
275 self->ui_active = false;
276 } else if (obj->body.otype == self->uris.ui_State) {
277 // If the object is a ui-state, it's the current UI settings
278 const LV2_Atom* spp = NULL;
279 const LV2_Atom* amp = NULL;
280 lv2_atom_object_get(obj, self->uris.ui_spp, &spp,
281 self->uris.ui_amp, &,
282 0);
283 if (spp) {
284 self->ui_spp = ((const LV2_Atom_Int*)spp)->body;
285 }
286 if (amp) {
287 self->ui_amp = ((const LV2_Atom_Float*)amp)->body;
288 }
289 }
290 }
291 ev = lv2_atom_sequence_next(ev);
292 }
293 }
294
295 // Process audio data
296 for (uint32_t c = 0; c < self->n_channels; ++c) {
297 if (self->ui_active) {
298 // If UI is active, send raw audio data to UI
299 tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]);
300 }
301 // If not processing audio in-place, forward audio
302 if (self->input[c] != self->output[c]) {
303 memcpy(self->output[c], self->input[c], sizeof(float) * n_samples);
304 }
305 }
306
307 // Close off sequence
308 lv2_atom_forge_pop(&self->forge, &self->frame);
309 }
310
311 static void
cleanup(LV2_Handle handle)312 cleanup(LV2_Handle handle)
313 {
314 free(handle);
315 }
316
317
318 /**
319 ==== State Methods ====
320
321 This plugin's state consists of two basic properties: one `int` and one
322 `float`. No files are used. Note these values are POD, but not portable,
323 since different machines may have a different integer endianness or floating
324 point format. However, since standard Atom types are used, a good host will
325 be able to save them portably as text anyway.
326 */
327 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)328 state_save(LV2_Handle instance,
329 LV2_State_Store_Function store,
330 LV2_State_Handle handle,
331 uint32_t flags,
332 const LV2_Feature* const* features)
333 {
334 EgScope* self = (EgScope*)instance;
335 if (!self) {
336 return LV2_STATE_SUCCESS;
337 }
338
339 store(handle, self->uris.ui_spp,
340 (void*)&self->ui_spp, sizeof(uint32_t),
341 self->uris.atom_Int,
342 LV2_STATE_IS_POD);
343
344 store(handle, self->uris.ui_amp,
345 (void*)&self->ui_amp, sizeof(float),
346 self->uris.atom_Float,
347 LV2_STATE_IS_POD);
348
349 return LV2_STATE_SUCCESS;
350 }
351
352 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)353 state_restore(LV2_Handle instance,
354 LV2_State_Retrieve_Function retrieve,
355 LV2_State_Handle handle,
356 uint32_t flags,
357 const LV2_Feature* const* features)
358 {
359 EgScope* self = (EgScope*)instance;
360
361 size_t size;
362 uint32_t type;
363 uint32_t valflags;
364
365 const void* spp = retrieve(
366 handle, self->uris.ui_spp, &size, &type, &valflags);
367 if (spp && size == sizeof(uint32_t) && type == self->uris.atom_Int) {
368 self->ui_spp = *((const uint32_t*)spp);
369 self->send_settings_to_ui = true;
370 }
371
372 const void* amp = retrieve(
373 handle, self->uris.ui_amp, &size, &type, &valflags);
374 if (amp && size == sizeof(float) && type == self->uris.atom_Float) {
375 self->ui_amp = *((const float*)amp);
376 self->send_settings_to_ui = true;
377 }
378
379 return LV2_STATE_SUCCESS;
380 }
381
382 static const void*
extension_data(const char * uri)383 extension_data(const char* uri)
384 {
385 static const LV2_State_Interface state = { state_save, state_restore };
386 if (!strcmp(uri, LV2_STATE__interface)) {
387 return &state;
388 }
389 return NULL;
390 }
391
392 /** ==== Plugin Descriptors ==== */
393 static const LV2_Descriptor descriptor_mono = {
394 SCO_URI "#Mono",
395 instantiate,
396 connect_port,
397 NULL,
398 run,
399 NULL,
400 cleanup,
401 extension_data
402 };
403
404 static const LV2_Descriptor descriptor_stereo = {
405 SCO_URI "#Stereo",
406 instantiate,
407 connect_port,
408 NULL,
409 run,
410 NULL,
411 cleanup,
412 extension_data
413 };
414
415 LV2_SYMBOL_EXPORT
416 const LV2_Descriptor*
lv2_descriptor(uint32_t index)417 lv2_descriptor(uint32_t index)
418 {
419 switch (index) {
420 case 0:
421 return &descriptor_mono;
422 case 1:
423 return &descriptor_stereo;
424 default:
425 return NULL;
426 }
427 }
428