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