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, &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