1 /* B.Slizr
2  * Step Sequencer Effect Plugin
3  *
4  * Copyright (C) 2018 - 2021 by Sven Jähnichen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #include "BSlizr.h"
22 
23 #include <cstdio>
24 #include <string>
25 #include <stdexcept>
26 
27 #define LIM(g , min, max) ((g) > (max) ? (max) : ((g) < (min) ? (min) : (g)))
28 
BSlizr(double samplerate,const LV2_Feature * const * features)29 BSlizr::BSlizr (double samplerate, const LV2_Feature* const* features) :
30 	map(NULL),
31 	rate(samplerate), bpm(120.0f), speed(1), position(0),
32 	beatsPerBar (4), beatUnit (4), refFrame(0),
33 	prevStep(NULL), actStep(NULL), nextStep(NULL), pos (0),
34 	audioInput1(NULL), audioInput2(NULL), audioOutput1(NULL), audioOutput2(NULL),
35 	sequencesperbar (4), nrSteps(16), attack(0.2), release (0.2),
36 	controlPort1(NULL), controlPort2(NULL),  notifyPort(NULL),
37 	record_on(false), monitorpos(-1), message ()
38 
39 {
40 	notifications.fill (defaultNotification);
41 	monitor.fill (defaultMonitorData);
42 
43 	//Scan host features for URID map
44 	LV2_URID_Map* m = NULL;
45 	for (int i = 0; features[i]; ++i)
46 	{
47 		if (strcmp(features[i]->URI, LV2_URID__map) == 0)
48 		{
49 			m = (LV2_URID_Map*) features[i]->data;
50 		}
51 	}
52 	if (!m) throw std::invalid_argument ("Host does not support urid:map");
53 
54 	//Map URIS
55 	map = m;
56 	getURIs (m, &uris);
57 
58 	// Initialize forge
59 	lv2_atom_forge_init (&forge,map);
60 
61 }
62 
~BSlizr()63 BSlizr::~BSlizr () {}
64 
connect_port(uint32_t port,void * data)65 void BSlizr::connect_port(uint32_t port, void *data)
66 {
67 	switch (port) {
68 	case Control_1:
69 		controlPort1 = (LV2_Atom_Sequence*) data;
70 		break;
71 	case Control_2:
72 		controlPort2 = (LV2_Atom_Sequence*) data;
73 		break;
74 	case Notify:
75 		notifyPort = (LV2_Atom_Sequence*) data;
76 		break;
77 	case AudioIn_1:
78 		audioInput1 = (float*) data;
79 		break;
80 	case AudioIn_2:
81 		audioInput2 = (float*) data;
82 		break;
83 	case AudioOut_1:
84 		audioOutput1 = (float*) data;
85 		break;
86 	case AudioOut_2:
87 		audioOutput2 = (float*) data;
88 		break;
89 	default:
90 		if ((port >= Controllers) && (port < Controllers + NrControllers)) controllers[port - Controllers] = (float*) data;
91 	}
92 }
93 
run(uint32_t n_samples)94 void BSlizr::run (uint32_t n_samples)
95 {
96 
97 	// Check ports
98 	if ((!controlPort1) || (!controlPort2) || (!notifyPort) || (!audioInput1) || (!audioInput2) || (!audioOutput1) || (!audioOutput2)) return;
99 	for (int i = 0; i < NrControllers; ++i)
100 	{
101 		if (!controllers[i]) return;
102 	}
103 
104 	// Update controller values
105 	attack = LIM (*(controllers[Attack - Controllers]), 0.01, 1.0);
106 	release = LIM (*(controllers[Release - Controllers]), 0.01, 1.0);
107 	sequencesperbar = LIM (round (*(controllers[SequencesPerBar - Controllers])), 1, 8);
108 	nrSteps = LIM (round(*(controllers[NrSteps - Controllers])), 1, 16);
109 	for (int i = 0; i < MAXSTEPS; ++i) step[i] = LIM (*(controllers[i + Step_ - Controllers]), 0.0, 1.0);
110 
111 	// Process GUI data
112 	const LV2_Atom_Event* ev2 = lv2_atom_sequence_begin(&(controlPort2)->body);
113 	while (!lv2_atom_sequence_is_end (&controlPort2->body, controlPort2->atom.size, ev2))
114 	{
115 		if (lv2_atom_forge_is_object_type(&forge, ev2->body.type))
116 		{
117 			const LV2_Atom_Object* obj2 = (const LV2_Atom_Object*)&ev2->body;
118 			if (obj2->body.otype == uris.ui_on) record_on = true;
119 			else if (obj2->body.otype == uris.ui_off) record_on = false;
120 		}
121 		ev2 = lv2_atom_sequence_next(ev2);
122 	}
123 
124 	// Prepare forge buffer and initialize atom sequence
125 	const uint32_t space = notifyPort->atom.size;
126 	lv2_atom_forge_set_buffer(&forge, (uint8_t*) notifyPort, space);
127 	lv2_atom_forge_sequence_head(&forge, &notify_frame, 0);
128 
129 	const LV2_Atom_Sequence* in = controlPort1;
130 	uint32_t last_t =0;
131 
132 	// Process audio data
133 	for (const LV2_Atom_Event* ev1 = lv2_atom_sequence_begin(&in->body);
134 		 !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev1);
135 		 ev1 = lv2_atom_sequence_next(ev1))
136 	{
137 		if ((ev1->body.type == uris.atom_Object) || (ev1->body.type == uris.atom_Blank))
138 		{
139 			//update bpm, speed, position
140 			LV2_Atom *oBbeat = NULL, *oBpm = NULL, *oSpeed = NULL, *oBpb = NULL, *oBu = NULL;
141 			const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev1->body;
142 			lv2_atom_object_get (obj, uris.time_barBeat, &oBbeat,
143 									  uris.time_beatsPerMinute,  &oBpm,
144 									  uris.time_beatsPerBar,  &oBpb,
145 									  uris.time_beatUnit,  &oBu,
146 									  uris.time_speed, &oSpeed,
147 									  NULL);
148 
149 			// BPM changed?
150 			if (oBpm && (oBpm->type == uris.atom_Float))
151 			{
152 				float nbpm = ((LV2_Atom_Float*)oBpm)->body;
153 				if (bpm != nbpm)
154 				{
155 					bpm = nbpm;
156 					if (nbpm < 1.0) message.setMessage (JACK_STOP_MSG);
157 					else message.deleteMessage (JACK_STOP_MSG);
158 				}
159 			}
160 
161 			// Beats per bar changed?
162 			if (oBpb && (oBpb->type == uris.atom_Float) && (((LV2_Atom_Float*)oBpb)->body > 0)) beatsPerBar = ((LV2_Atom_Float*)oBpb)->body;
163 
164 			// BeatUnit changed?
165 			if (oBu && (oBu->type == uris.atom_Int) && (((LV2_Atom_Int*)oBu)->body > 0)) beatUnit = ((LV2_Atom_Int*)oBu)->body;
166 
167 			// Speed changed?
168 			if (oSpeed && (oSpeed->type == uris.atom_Float))
169 			{
170 				float nspeed = ((LV2_Atom_Float*)oSpeed)->body;
171 				if (speed != nspeed)
172 				{
173 					speed = nspeed;
174 					if (nspeed == 0.0) message.setMessage (JACK_STOP_MSG);
175 					else message.deleteMessage (JACK_STOP_MSG);
176 				}
177 			}
178 
179 			// Beat position changed (during playing) ?
180 			if (oBbeat && (oBbeat->type == uris.atom_Float))
181 			{
182 				// Get position within a sequence (0..1)
183 				float barsequencepos = ((LV2_Atom_Float*)oBbeat)->body * sequencesperbar / beatsPerBar; // Position within a bar (0..sequencesperbar)
184 				position = MODFL (barsequencepos);			// Position within a sequence
185 				refFrame = ev1->time.frames;				// Reference frame
186 			}
187 		}
188 
189 		play(last_t, ev1->time.frames);
190 		last_t = ev1->time.frames;
191 	}
192 	if (last_t < n_samples) play(last_t, n_samples);		// Play remaining samples
193 
194 	// Update position in case of no new barBeat submitted on next call
195 	double relpos = (n_samples - refFrame) * speed / (rate / (bpm / 60)) * sequencesperbar / beatsPerBar;	// Position relative to reference frame
196 	position = MODFL (position + relpos);
197 	refFrame = 0;
198 
199 	// Send collected data to GUI
200 	if (record_on)
201 	{
202 		notifyGUI();
203 		if (message.isScheduled ()) notifyMessageToGui ();
204 	}
205 
206 	// Close off sequence
207 	lv2_atom_forge_pop(&forge, &notify_frame);
208 }
209 
notifyGUI()210 void BSlizr::notifyGUI()
211 {
212 	if (record_on)
213 	{
214 		int notificationsCount = 0;
215 		// Scan monitor and build notifications
216 		for (int i = 0; i < MONITORBUFFERSIZE; ++i)
217 		{
218 			if (monitor[i].ready)
219 			{
220 				// Copy data monitor -> notifications
221 				if (notificationsCount < NOTIFYBUFFERSIZE - 1)
222 				{
223 					notifications[notificationsCount].position = i;
224 					notifications[notificationsCount].inputMin = monitor[i].inputMin;
225 					notifications[notificationsCount].inputMax = monitor[i].inputMax;
226 					notifications[notificationsCount].outputMin = monitor[i].outputMin;
227 					notifications[notificationsCount].outputMax = monitor[i].outputMax;
228 					notificationsCount++;
229 				}
230 
231 				// Reset monitor data
232 				monitor[i].ready = false;
233 				monitor[i].inputMin = 0.0;
234 				monitor[i].inputMax = 0.0;
235 				monitor[i].outputMin = 0.0;
236 				monitor[i].outputMax = 0.0;
237 			}
238 		}
239 
240 		// And build one closing notification block for submission of current position (horizon)
241 		notifications[notificationsCount].position = monitorpos;
242 		notifications[notificationsCount].inputMin = monitor[monitorpos].inputMin;
243 		notifications[notificationsCount].inputMax = monitor[monitorpos].inputMax;
244 		notifications[notificationsCount].outputMin = monitor[monitorpos].outputMin;
245 		notifications[notificationsCount].outputMax = monitor[monitorpos].outputMax;
246 
247 		// Send notifications
248 		LV2_Atom_Forge_Frame frame;
249 		lv2_atom_forge_frame_time(&forge, 0);
250 		lv2_atom_forge_object(&forge, &frame, 0, uris.notify_event);
251 		lv2_atom_forge_key(&forge, uris.notify_key);
252 		lv2_atom_forge_vector(&forge, sizeof(float), uris.atom_Float, (uint32_t) (5 * notificationsCount), &notifications);
253 		lv2_atom_forge_pop(&forge, &frame);
254 
255 		notificationsCount = 0;
256 	}
257 }
258 
notifyMessageToGui()259 void BSlizr::notifyMessageToGui()
260 {
261 	uint32_t messageNr = message.loadMessage ();
262 
263 	// Send notifications
264 	LV2_Atom_Forge_Frame frame;
265 	lv2_atom_forge_frame_time(&forge, 0);
266 	lv2_atom_forge_object(&forge, &frame, 0, uris.notify_messageEvent);
267 	lv2_atom_forge_key(&forge, uris.notify_message);
268 	lv2_atom_forge_int(&forge, messageNr);
269 	lv2_atom_forge_pop(&forge, &frame);
270 }
271 
play(uint32_t start,uint32_t end)272 void BSlizr::play(uint32_t start, uint32_t end)
273 {
274 	float vol, relpos, iStepf, iStepFrac, effect1, effect2;
275 	uint32_t steps = (uint32_t) nrSteps;
276 	uint32_t iStep;
277 
278 	for (uint32_t i = start; i < end; ++i)
279 	{
280 		// Interpolate position within the loop
281 		if ((speed != 0.0f) && (bpm >= 1.0f))
282 		{
283 			relpos = (i - refFrame) * speed / (rate / (bpm / 60)) * sequencesperbar / beatsPerBar;	// Position relative to reference frame
284 			pos = MODFL (position + relpos);
285 		}
286 		iStepf = (pos * steps);
287 		iStep = (uint32_t)iStepf;											// Calculate step number
288 		iStepFrac = iStepf - iStep;											// Calculate fraction of active step
289 
290 		// Move to the next step?
291 		if (actStep != &(step[iStep]))
292 		{
293 			prevStep = actStep;
294 			actStep = &(step[iStep]);
295 			if (iStep < (steps-1)) nextStep = &(step[iStep+1]);
296 			else nextStep = &(step[0]);
297 		}
298 		if (!prevStep) prevStep = actStep;									// prevStep not initialized (= on Start)?
299 
300 		// Calculate effect (vol) for the position
301 		vol = *actStep;
302 		if (iStepFrac < attack)									// On attack
303 		{
304 			if (*prevStep < *actStep) vol = *prevStep + (iStepFrac / attack) * (*actStep - *prevStep);
305 
306 		}
307 		if (iStepFrac > (1 - release))									// On release
308 		{
309 			if (*nextStep < *actStep) vol = vol - ((iStepFrac - (1 - release)) / release) * (*actStep - *nextStep);
310 		}
311 
312 		// Apply effect on input
313 		effect1 = audioInput1[i] * vol;
314 		effect2 = audioInput2[i] * vol;
315 
316 		// Analyze input and output data for GUI notification
317 		if (record_on)
318 		{
319 			// Calculate position in monitor
320 			int newmonitorpos = (int) (pos * MONITORBUFFERSIZE);
321 			if (newmonitorpos >= MONITORBUFFERSIZE) newmonitorpos = MONITORBUFFERSIZE;
322 			if (newmonitorpos < 0) newmonitorpos = 0;
323 
324 			// Position changed? => Ready to send
325 			if (newmonitorpos != monitorpos)
326 			{
327 				if (monitorpos >= 0) monitor[monitorpos].ready = true;
328 				monitorpos = newmonitorpos;
329 			}
330 
331 			// Get max input and output values for a block
332 			if (effect1 < monitor[monitorpos].outputMin) monitor[monitorpos].outputMin = effect1;
333 			if (effect1 > monitor[monitorpos].outputMax) monitor[monitorpos].outputMax = effect1;
334 			if (effect2 < monitor[monitorpos].outputMin) monitor[monitorpos].outputMin = effect2;
335 			if (effect2 > monitor[monitorpos].outputMax) monitor[monitorpos].outputMax = effect2;
336 			if (audioInput1[i] < monitor[monitorpos].inputMin) monitor[monitorpos].inputMin = audioInput1[i];
337 			if (audioInput1[i] > monitor[monitorpos].inputMax) monitor[monitorpos].inputMax = audioInput1[i];
338 			if (audioInput2[i] < monitor[monitorpos].inputMin) monitor[monitorpos].inputMin = audioInput2[i];
339 			if (audioInput2[i] > monitor[monitorpos].inputMax) monitor[monitorpos].inputMax = audioInput2[i];
340 
341 			monitor[monitorpos].ready = false;
342 		}
343 
344 		// Send effect to audio output
345 		audioOutput1[i] = effect1;
346 		audioOutput2[i] = effect2;
347 	}
348 }
349 
instantiate(const LV2_Descriptor * descriptor,double samplerate,const char * bundle_path,const LV2_Feature * const * features)350 static LV2_Handle instantiate (const LV2_Descriptor* descriptor, double samplerate, const char* bundle_path, const LV2_Feature* const* features)
351 {
352 	// New instance
353 	BSlizr* instance;
354 	try {instance = new BSlizr(samplerate, features);}
355 	catch (std::exception& exc)
356 	{
357 		fprintf (stderr, "BSlizr.lv2: Plugin instantiation failed. %s\n", exc.what ());
358 		return NULL;
359 	}
360 
361 	if (!instance)
362 	{
363 		fprintf(stderr, "BSlizr.lv2: Plugin instantiation failed.\n");
364 		return NULL;
365 	}
366 
367 	if (!instance->map)
368 	{
369 		fprintf(stderr, "BSlizr.lv2: Host does not support urid:map.\n");
370 		delete (instance);
371 		return NULL;
372 	}
373 
374 	return (LV2_Handle)instance;
375 }
376 
connect_port(LV2_Handle instance,uint32_t port,void * data)377 static void connect_port (LV2_Handle instance, uint32_t port, void *data)
378 {
379 	BSlizr* inst = (BSlizr*) instance;
380 	if (inst) inst->connect_port (port, data);
381 }
382 
run(LV2_Handle instance,uint32_t n_samples)383 static void run (LV2_Handle instance, uint32_t n_samples)
384 {
385 	BSlizr* inst = (BSlizr*) instance;
386 	if (inst) inst->run (n_samples);
387 }
388 
cleanup(LV2_Handle instance)389 static void cleanup (LV2_Handle instance)
390 {
391 	BSlizr* inst = (BSlizr*) instance;
392 	if (inst) delete inst;
393 }
394 
395 static const LV2_Descriptor descriptor =
396 {
397 		BSLIZR_URI,
398 		instantiate,
399 		connect_port,
400 		NULL, //activate,
401 		run,
402 		NULL, //deactivate,
403 		cleanup,
404 		NULL //extension_data
405 };
406 
407 // LV2 Symbol Export
lv2_descriptor(uint32_t index)408 LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index)
409 {
410 	switch (index)
411 	{
412 	case 0: return &descriptor;
413 	default: return NULL;
414 	}
415 }
416