1 /*
2 * Copyright (C) 2018-2020 Oleg Kapitonov
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 * --------------------------------------------------------------------------
18 */
19
20 #include <limits.h>
21 #include <string.h>
22 #include <math.h>
23 #include <map>
24 #include <stdint.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
29 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
30 #include "lv2/lv2plug.in/ns/ext/atom/forge.h"
31 #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
32 #include "lv2/lv2plug.in/ns/ext/options/options.h"
33 #include "lv2/lv2plug.in/ns/ext/patch/patch.h"
34 #include "lv2/lv2plug.in/ns/ext/worker/worker.h"
35 #include "lv2/lv2plug.in/ns/ext/state/state.h"
36 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
37
38 #include "atom_sink.h"
39 #include "profile.h"
40 #include "kpp_tubeamp.h"
41 #include "faust-support.h"
42
43 using namespace std;
44
45 #define DEFAULT_PROFILE "American Clean"
46 #define DEFAULT_PROFILE_PATH ("profiles/" DEFAULT_PROFILE ".tapf")
47
48 // Defines for compatability with
49 // FAUST generated code
50
51 #define VOLUME_CTRL *(ports.volume)
52 #define DRIVE_CTRL *(ports.drive)
53 #define MASTERGAIN_CTRL *(ports.mastergain)
54
55 #define AMP_BIAS_CTRL profile->amp_bias
56 #define AMP_KREG_CTRL profile->amp_Kreg
57 #define AMP_UPOR_CTRL profile->amp_Upor
58
59 #define PREAMP_BIAS_CTRL profile->preamp_bias
60 #define PREAMP_KREG_CTRL profile->preamp_Kreg
61 #define PREAMP_UPOR_CTRL profile->preamp_Upor
62
63 #define LOW_CTRL *(ports.low)
64 #define MIDDLE_CTRL *(ports.middle)
65 #define HIGH_CTRL *(ports.high)
66
67 #define LOW_FREQ_CTRL profile->tonestack_low_freq
68 #define MIDDLE_FREQ_CTRL profile->tonestack_middle_freq
69 #define HIGH_FREQ_CTRL profile->tonestack_high_freq
70
71 #define LOW_BAND_CTRL profile->tonestack_low_band
72 #define MIDDLE_BAND_CTRL profile->tonestack_middle_band
73 #define HIGH_BAND_CTRL profile->tonestack_high_band
74
75 #define PREAMP_LEVEL profile->preamp_level
76 #define AMP_LEVEL profile->amp_level
77
78 #define SAG_TIME profile->sag_time
79 #define SAG_COEFF profile->sag_coeff
80
81 #define OUTPUT_LEVEL profile->output_level
82
83 // LV2 port numbers
84 enum {PORT_LOW, PORT_MIDDLE, PORT_HIGH, PORT_DRIVE, PORT_MASTERGAIN, PORT_VOLUME, PORT_CABINET,
85 PORT_IN0, PORT_IN1, PORT_OUT0, PORT_OUT1, PORT_CONTROL, PORT_NOTIFY};
86
87 // FAUST generated code
88 #include "plugin.h"
89
90 // Check *.tapf file signature
check_profile_file(const char * path)91 int check_profile_file(const char *path)
92 {
93 int status = 0;
94
95 FILE * profile_file = fopen(path, "rb");
96
97 if (profile_file != NULL)
98 {
99 st_profile_header check_profile;
100 if (fread(&check_profile, sizeof(st_profile_header), 1, profile_file) == 1)
101 {
102 if (!strncmp(check_profile.signature, "TaPf", 4))
103 {
104 status = 1;
105 }
106 }
107 else status = 1;
108
109 fclose(profile_file);
110 }
111
112 return status;
113 }
114
set_profile(stPlugin * plugin,stProfile * profile)115 static void set_profile(stPlugin *plugin, stProfile *profile)
116 {
117 plugin->profile = profile;
118 plugin->dsp->profile = &profile->header;
119 plugin->convproc = &profile->convproc;
120 plugin->preamp_convproc = &profile->preamp_convproc;
121 }
122
123 // LV2 Callback functions
124
125 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)126 instantiate(const LV2_Descriptor* descriptor,
127 double rate,
128 const char* bundle_path,
129 const LV2_Feature* const* features)
130 {
131 stPlugin* plugin = new stPlugin((int)rate);
132 const LV2_Options_Option* options = NULL;
133 // Scan host features for URID map.
134 for (int i = 0; features[i]; i++)
135 {
136 if (!strcmp(features[i]->URI, LV2_URID_URI "#map"))
137 {
138 plugin->map = (LV2_URID_Map*)features[i]->data;
139 }
140 else if (!strcmp(features[i]->URI, LV2_URID_URI "#unmap"))
141 {
142 plugin->unmap = (LV2_URID_Unmap*)features[i]->data;
143 }
144 else if (!strcmp(features[i]->URI, LV2_OPTIONS__options))
145 {
146 options = (const LV2_Options_Option*) features[i]->data;
147 }
148 else if (!strcmp(features[i]->URI, LV2_WORKER__schedule))
149 {
150 plugin->schedule = (LV2_Worker_Schedule*)features[i]->data;
151 }
152 }
153 if (!plugin->map)
154 {
155 fprintf(stderr, "%s: host doesn't support urid:map, giving up\n", PLUGIN_URI);
156 delete plugin;
157 return 0;
158 }
159 if (!plugin->schedule)
160 {
161 fprintf(stderr, "%s: host doesn't support woker:schedule, giving up\n", PLUGIN_URI);
162 delete plugin;
163 return 0;
164 }
165
166 int32_t bufsize = 8192;
167 LV2_URID bufsz_max = plugin->map->map(plugin->map->handle, LV2_BUF_SIZE__maxBlockLength);
168 LV2_URID atom_Int = plugin->map->map(plugin->map->handle, LV2_ATOM__Int);
169 for (const LV2_Options_Option* opt = options; opt->key; opt++)
170 {
171 if (opt->context == LV2_OPTIONS_INSTANCE
172 && opt->key == bufsz_max && opt->type == atom_Int) {
173 bufsize = *(const int32_t*) opt->value;
174 }
175 }
176 plugin->set_bufsize(bufsize);
177
178 plugin->uris.patch_Set = plugin->map->map(plugin->map->handle, LV2_PATCH__Set);
179 plugin->uris.patch_Get = plugin->map->map(plugin->map->handle, LV2_PATCH__Get);
180 plugin->uris.patch_property = plugin->map->map(plugin->map->handle, LV2_PATCH__property);
181 plugin->uris.patch_value = plugin->map->map(plugin->map->handle, LV2_PATCH__value);
182 plugin->uris.atom_Path = plugin->map->map(plugin->map->handle, LV2_ATOM__Path);
183 plugin->uris.profile = plugin->map->map(plugin->map->handle, "https://faustlv2.bitbucket.io/kpp_tubeamp#profile");
184 plugin->uris.freeSample = plugin->map->map(plugin->map->handle, "https://faustlv2.bitbucket.io/kpp_tubeamp#freeSample");
185
186 lv2_atom_forge_init(&plugin->forge, plugin->map);
187
188 plugin->bundle_path = bundle_path;
189
190 std::string path = plugin->bundle_path + DEFAULT_PROFILE_PATH;
191 stProfile *profile = stPlugin::load_profile(path.c_str(), plugin->rate);
192 if (!profile)
193 {
194 fprintf(stderr,"Error while loading default profile!\n");
195 }
196 else
197 {
198 set_profile(plugin, profile);
199 }
200 return (LV2_Handle)plugin;
201 }
202
203 static void
cleanup(LV2_Handle instance)204 cleanup(LV2_Handle instance)
205 {
206 stPlugin* plugin = (stPlugin*)instance;
207 delete plugin;
208 }
209
210 // Worker function (works in the separate thread)
211 // Can free old profile structures or load new
212 static LV2_Worker_Status
work(LV2_Handle instance,LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle handle,uint32_t size,const void * data)213 work(LV2_Handle instance,
214 LV2_Worker_Respond_Function respond,
215 LV2_Worker_Respond_Handle handle,
216 uint32_t size,
217 const void* data)
218 {
219 stPlugin* plugin = (stPlugin*)instance;
220 const LV2_Atom* atom = (const LV2_Atom*)data;
221 const char *path = NULL;
222
223 if (atom->type == plugin->uris.freeSample)
224 {
225 // Free old profile
226 stProfileMessage *msg = (stProfileMessage *)data;
227 std::lock_guard<std::mutex> lock(plugin->profile_free_mutex);
228 delete msg->body.profile;
229 }
230 else if (atom->type == plugin->forge.Object)
231 {
232 // Load new profile
233 const LV2_Atom* value = NULL;
234 lv2_atom_object_get((const LV2_Atom_Object*)atom, plugin->uris.patch_value, &value, 0);
235
236 path = (const char*)LV2_ATOM_BODY_CONST(value);
237
238 if (!path)
239 {
240 fprintf(stderr,"Error in path while loading profile!\n");
241 return LV2_WORKER_ERR_UNKNOWN;
242 }
243
244 if (check_profile_file(path))
245 {
246 stProfile *profile = stPlugin::load_profile(path, plugin->rate);
247 if (!profile)
248 {
249 fprintf(stderr,"Error while loading profile!\n");
250 return LV2_WORKER_ERR_UNKNOWN;
251 }
252 stProfileMessage msg = {
253 { sizeof(stProfileMessageBody), plugin->uris.patch_value },
254 { profile }
255 };
256
257 respond(handle, lv2_atom_total_size((LV2_Atom*)&msg), (LV2_Atom*)&msg);
258 }
259 }
260
261 return LV2_WORKER_SUCCESS;
262 }
263
264 // Response after worker function returns
265 static LV2_Worker_Status
work_response(LV2_Handle instance,uint32_t size,const void * data)266 work_response(LV2_Handle instance,
267 uint32_t size,
268 const void* data)
269 {
270 stPlugin* plugin = (stPlugin*)instance;
271
272 // Schedule work to free the old profile
273 stProfileMessage freemsg = {
274 { sizeof(stProfileMessageBody), plugin->uris.freeSample },
275 { plugin->profile }
276 };
277
278 // Swap old and new profile structures
279 stProfileMessage *msg = (stProfileMessage *)data;
280 set_profile(plugin, msg->body.profile);
281 // Schedule deleting of old profile structures
282 plugin->schedule->schedule_work(plugin->schedule->handle, sizeof(freemsg), &freemsg);
283
284 return LV2_WORKER_SUCCESS;
285 }
286
287 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)288 connect_port(LV2_Handle instance,
289 uint32_t port,
290 void* data)
291 {
292 stPlugin* plugin = (stPlugin*)instance;
293 switch (port) {
294 case PORT_LOW:
295 plugin->dsp->ports.low = (float*) data;
296 break;
297 case PORT_MIDDLE:
298 plugin->dsp->ports.middle = (float *) data;
299 break;
300 case PORT_HIGH:
301 plugin->dsp->ports.high = (float *) data;
302 break;
303 case PORT_DRIVE:
304 plugin->dsp->ports.drive = (float *) data;
305 break;
306 case PORT_MASTERGAIN:
307 plugin->dsp->ports.mastergain = (float *) data;
308 break;
309 case PORT_VOLUME:
310 plugin->dsp->ports.volume = (float *) data;
311 break;
312 case PORT_CABINET:
313 plugin->dsp->ports.cabinet = (float *) data;
314 break;
315 case PORT_IN0:
316 plugin->inputs[0] = (float *) data;
317 break;
318 case PORT_IN1:
319 plugin->inputs[1] = (float *) data;
320 break;
321 case PORT_OUT0:
322 plugin->outputs[0] = (float *) data;
323 break;
324 case PORT_OUT1:
325 plugin->outputs[1] = (float *) data;
326 break;
327 case PORT_CONTROL:
328 plugin->dsp->ports.control = (LV2_Atom_Sequence *) data;
329 break;
330 case PORT_NOTIFY:
331 plugin->dsp->ports.notify = (LV2_Atom_Sequence *) data;
332 }
333 }
334
335 static void
run(LV2_Handle instance,uint32_t n_samples)336 run(LV2_Handle instance, uint32_t n_samples)
337 {
338 stPlugin* plugin = (stPlugin*)instance;
339 // Process audio.
340 plugin->process_audio(n_samples);
341
342 const uint32_t notify_capacity = plugin->dsp->ports.notify->atom.size;
343 lv2_atom_forge_set_buffer(&plugin->forge,
344 (uint8_t*)plugin->dsp->ports.notify,
345 notify_capacity);
346
347 lv2_atom_forge_sequence_head(&plugin->forge, &plugin->notify_frame, 0);
348
349 // Read Atom messages
350 LV2_ATOM_SEQUENCE_FOREACH(plugin->dsp->ports.control, ev)
351 {
352 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body;
353
354 if (obj->body.otype == plugin->uris.patch_Set)
355 {
356 // Received patch_Set message
357 const LV2_Atom* property = NULL;
358 lv2_atom_object_get(obj, plugin->uris.patch_property, &property, 0);
359
360 if (((const LV2_Atom_URID*)property)->body == plugin->uris.profile)
361 {
362 // Received profile object
363 const LV2_Atom* value = NULL;
364
365 lv2_atom_object_get(obj, plugin->uris.patch_value, &value, 0);
366
367 if (value->type == plugin->uris.atom_Path)
368 {
369 // Received object path, load new profile in worker
370 plugin->schedule->schedule_work(plugin->schedule->handle,
371 lv2_atom_total_size((LV2_Atom*)obj),
372 (LV2_Atom*)obj);
373 }
374 }
375 }
376 else if (obj->body.otype == plugin->uris.patch_Get)
377 {
378 // Received patch_Get message,
379 // send current profile path back
380 lv2_atom_forge_frame_time(&plugin->forge, 0);
381 LV2_Atom_Forge_Frame frame;
382 lv2_atom_forge_object(&plugin->forge, &frame, 0, plugin->uris.patch_Set);
383
384 lv2_atom_forge_key(&plugin->forge, plugin->uris.patch_property);
385 lv2_atom_forge_urid(&plugin->forge, plugin->uris.profile);
386 lv2_atom_forge_key(&plugin->forge, plugin->uris.patch_value);
387 if (plugin->profile)
388 {
389 lv2_atom_forge_path(&plugin->forge,
390 plugin->profile->path.data(), plugin->profile->path.size());
391 }
392 else
393 {
394 lv2_atom_forge_path(&plugin->forge, NULL, 0);
395 }
396
397 lv2_atom_forge_pop(&plugin->forge, &frame);
398 }
399
400 }
401 }
402
403 static void
activate(LV2_Handle instance)404 activate(LV2_Handle instance)
405 {
406 stPlugin* plugin = (stPlugin*)instance;
407 plugin->resume();
408 }
409
410 static void
deactivate(LV2_Handle instance)411 deactivate(LV2_Handle instance)
412 {
413 stPlugin* plugin = (stPlugin*)instance;
414 plugin->suspend();
415 }
416
417 // Used to save profile path
418 static LV2_State_Status
save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)419 save(LV2_Handle instance,
420 LV2_State_Store_Function store,
421 LV2_State_Handle handle,
422 uint32_t flags,
423 const LV2_Feature* const* features)
424 {
425 stPlugin* plugin = (stPlugin*)instance;
426
427 LV2_State_Map_Path* map_path = NULL;
428
429 for (int i = 0; features[i]; i++) {
430 if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) {
431 map_path = (LV2_State_Map_Path*)features[i]->data;
432 }
433 }
434
435 if (!map_path) {
436 return LV2_STATE_ERR_NO_FEATURE;
437 }
438
439 // Map absolute sample path to an abstract state path
440 char *path = NULL;
441 {
442 std::lock_guard<std::mutex> lock(plugin->profile_free_mutex);
443 if (plugin->profile)
444 {
445 path = map_path->abstract_path(map_path->handle, plugin->profile->path.c_str());
446 }
447 }
448
449 // Store profile = abstract path
450 store(handle,
451 plugin->uris.profile,
452 path,
453 path ? strlen(path) + 1 : 0,
454 plugin->uris.atom_Path,
455 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
456
457 free(path);
458 return LV2_STATE_SUCCESS;
459 }
460
461 // Used to restore saved profile path
462 static LV2_State_Status
restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)463 restore(LV2_Handle instance,
464 LV2_State_Retrieve_Function retrieve,
465 LV2_State_Handle handle,
466 uint32_t flags,
467 const LV2_Feature* const* features)
468 {
469 stPlugin* plugin = (stPlugin*)instance;
470
471 LV2_State_Map_Path* paths = NULL;
472
473 for (int i = 0; features[i]; i++)
474 {
475 if (!strcmp(features[i]->URI, LV2_STATE__mapPath))
476 {
477 paths = (LV2_State_Map_Path*)features[i]->data;
478 }
479 }
480
481 if (!paths)
482 {
483 fprintf(stderr, "%s: host doesn't support state:map_Path\n", PLUGIN_URI);
484 return LV2_STATE_ERR_NO_FEATURE;
485 }
486
487 // Get eg:sample from state
488 size_t size;
489 uint32_t type;
490 uint32_t valflags;
491 const void* value = retrieve(handle, plugin->uris.profile,
492 &size, &type, &valflags);
493 std::string path;
494 if (!value)
495 {
496 path = plugin->bundle_path + DEFAULT_PROFILE_PATH;
497 }
498 else if (type != plugin->uris.atom_Path)
499 {
500 return LV2_STATE_ERR_BAD_TYPE;
501 }
502 else
503 {
504 // Map abstract state path to absolute path
505 char *abspath = paths->absolute_path(paths->handle, (const char *) value);
506 path = abspath;
507 free(abspath);
508 }
509
510 // We should obtain real file path, not symlink
511 // which Ardour creates in project directory,
512 // because user wants to see real profile files
513 // in LV2 bundle, not symlinks in Ardour project
514
515 // Check if profile file is symlink
516 struct stat stat_buf;
517 lstat(path.c_str(), &stat_buf);
518
519 // If so, dereference symlink
520 if (S_ISLNK(stat_buf.st_mode))
521 {
522 std::vector<char> real_profile_file_name(PATH_MAX);
523 FILE *fp;
524 std::string readlink_command;
525
526 readlink_command = "readlink -f \"" + path + "\"" + "\0";
527 fp = popen(readlink_command.c_str(), "r");
528
529 if (fgets(real_profile_file_name.data(), PATH_MAX - 1, fp) != NULL)
530 {
531 real_profile_file_name[strlen(real_profile_file_name.data()) - 1] = '\0';
532 path = real_profile_file_name.data();
533 }
534 pclose(fp);
535 }
536
537 if (check_profile_file(path.c_str()))
538 {
539 // Send Atom message to load this profile
540 LV2_Atom_Forge forge;
541 LV2_Atom* buf = (LV2_Atom*)calloc(1, path.size() + 128);
542 lv2_atom_forge_init(&forge, plugin->map);
543 lv2_atom_forge_set_sink(&forge, atom_sink, atom_sink_deref, buf);
544
545 LV2_Atom_Forge_Frame frame;
546 lv2_atom_forge_object(&forge, &frame, 0, plugin->uris.patch_Set);
547
548 lv2_atom_forge_key(&forge, plugin->uris.patch_property);
549 lv2_atom_forge_urid(&forge, plugin->uris.profile);
550 lv2_atom_forge_key(&forge, plugin->uris.patch_value);
551 lv2_atom_forge_path(&forge, path.data(), path.size());
552
553 lv2_atom_forge_pop(&forge, &frame);
554
555 const uint32_t msg_size = lv2_atom_pad_size(buf->size);
556 plugin->schedule->schedule_work(plugin->schedule->handle, msg_size, buf + 1);
557 free(buf);
558 }
559
560 return LV2_STATE_SUCCESS;
561 }
562
563
564 const void*
extension_data(const char * uri)565 extension_data(const char* uri)
566 {
567 static const LV2_State_Interface state = { save, restore };
568 static const LV2_Worker_Interface worker = { work, work_response, NULL };
569 if (!strcmp(uri, LV2_STATE__interface))
570 {
571 return &state;
572 }
573 else if (!strcmp(uri, LV2_WORKER__interface))
574 {
575 return &worker;
576 }
577 return NULL;
578 }
579
580 static const LV2_Descriptor descriptor = {
581 PLUGIN_URI,
582 instantiate,
583 connect_port,
584 activate,
585 run,
586 deactivate,
587 cleanup,
588 extension_data
589 };
590
591 extern "C"
592 LV2_SYMBOL_EXPORT
593 const LV2_Descriptor*
lv2_descriptor(uint32_t index)594 lv2_descriptor(uint32_t index)
595 {
596 switch (index) {
597 case 0:
598 return &descriptor;
599 default:
600 return NULL;
601 }
602 }
603