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, ¬ify_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, ¬ify_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), ¬ifications);
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