fix a few dangling uses of Ardour as program name, in favor of PROGRAM_NAME
[ardour.git] / libs / ardour / linux_vst_info_file.cc
1 /***********************************************************/
2 /*vstfx infofile - module to manage info files             */
3 /*containing cached information about a plugin. e.g. its   */
4 /*name, creator etc etc                                    */
5 /***********************************************************/
6
7 #include <iostream>
8 #include <cassert>
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <errno.h>
14
15 #include <stdlib.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <libgen.h>
20
21 #include <glib.h>
22 #include <glib/gstdio.h>
23
24 #include "pbd/error.h"
25
26 #include "ardour/linux_vst_support.h"
27
28 #define MAX_STRING_LEN 256
29
30 static char* read_string(FILE *fp)
31 {
32     char buf[MAX_STRING_LEN];
33
34     if (!fgets( buf, MAX_STRING_LEN, fp )) {
35             return 0;
36     }
37     
38     if(strlen(buf) < MAX_STRING_LEN) {
39             if (strlen(buf)) {
40                     buf[strlen(buf)-1] = 0;
41             }
42             return strdup(buf);
43     } else {
44             return 0;
45     }
46 }
47
48 static VSTInfo *
49 load_vstfx_info_file (FILE* fp)
50 {
51         VSTInfo *info;
52         
53         if ((info = (VSTInfo*) malloc (sizeof (VSTInfo))) == 0) {
54                 return 0;
55         }
56
57         if((info->name = read_string(fp)) == 0) goto error;
58         if((info->creator = read_string(fp)) == 0) goto error;
59         if(1 != fscanf(fp, "%d\n", &info->UniqueID)) goto error;
60         if((info->Category = read_string(fp)) == 0) goto error;
61         if(1 != fscanf(fp, "%d\n", &info->numInputs)) goto error;
62         if(1 != fscanf(fp, "%d\n", &info->numOutputs)) goto error;
63         if(1 != fscanf(fp, "%d\n", &info->numParams)) goto error;
64         if(1 != fscanf(fp, "%d\n", &info->wantMidi)) goto error;
65         if(1 != fscanf(fp, "%d\n", &info->hasEditor)) goto error;
66         if(1 != fscanf(fp, "%d\n", &info->canProcessReplacing)) goto error;
67         
68         if((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) {
69                 goto error;
70         }
71
72         for (int i = 0; i < info->numParams; ++i) {
73                 if((info->ParamNames[i] = read_string(fp)) == 0) goto error;
74         }
75
76         if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) {
77                 goto error;
78         }
79         
80         for (int i = 0; i < info->numParams; ++i) {
81                 if((info->ParamLabels[i] = read_string(fp)) == 0) goto error;
82         }
83         
84         return info;
85         
86   error:
87         free( info );
88         return 0;
89 }
90
91 static int
92 save_vstfx_info_file (VSTInfo *info, FILE* fp)
93 {
94     if (info == 0) {
95             vstfx_error("** ERROR ** VSTFXinfofile : info ptr is 0\n");
96             return -1;
97     }
98
99     if (fp == 0) {
100             vstfx_error("** ERROR ** VSTFXinfofile : file ptr is 0\n");
101             return -1;
102     }
103     
104     fprintf( fp, "%s\n", info->name );
105     fprintf( fp, "%s\n", info->creator );
106     fprintf( fp, "%d\n", info->UniqueID );
107     fprintf( fp, "%s\n", info->Category );
108     fprintf( fp, "%d\n", info->numInputs );
109     fprintf( fp, "%d\n", info->numOutputs );
110     fprintf( fp, "%d\n", info->numParams );
111     fprintf( fp, "%d\n", info->wantMidi );
112     fprintf( fp, "%d\n", info->hasEditor );
113     fprintf( fp, "%d\n", info->canProcessReplacing );
114
115     for (int i = 0; i < info->numParams; i++) {
116                 fprintf(fp, "%s\n", info->ParamNames[i]);
117     }
118         
119     for (int i = 0; i < info->numParams; i++) {
120                 fprintf(fp, "%s\n", info->ParamLabels[i]);
121     }
122         
123     return 0;
124 }
125
126 static char* vstfx_infofile_stat (char *dllpath, struct stat* statbuf, int personal)
127 {
128         char* path;
129         char* dir_path;
130         char* basename;
131         char* base;
132         size_t blen;
133
134         if (strstr (dllpath, ".so" ) == 0) {
135                 return 0;
136         }
137         
138         if (personal) {
139                 dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL);
140         } else {
141                 dir_path = g_path_get_dirname (dllpath);
142         }
143         
144         base = g_path_get_basename (dllpath);
145         blen = strlen (base) + 2; // null char and '.'
146         basename = (char*) g_malloc (blen);
147         snprintf (basename, blen, ".%s.fsi", base);
148         g_free (base);
149         
150         path = g_build_filename (dir_path, basename, NULL);
151         
152         g_free (dir_path);
153         g_free (basename);
154
155
156         if (g_file_test (path, GFileTest (G_FILE_TEST_EXISTS|G_FILE_TEST_IS_REGULAR))) {
157
158                 /* info file exists in same location as the shared object, so
159                    check if its current and up to date
160                 */
161
162
163                 struct stat dllstat;
164                 
165                 if (stat (dllpath, &dllstat) == 0) {
166                         if (stat(path, statbuf) == 0) {
167                                 if (dllstat.st_mtime <= statbuf->st_mtime) {
168                                         /* plugin is older than info file */
169                                         return path;
170                                 }
171                         }
172                 } 
173         }
174
175         g_free (path);
176
177         return 0;
178 }
179
180
181 static FILE* vstfx_infofile_for_read (char* dllpath)
182 {
183         struct stat own_statbuf;
184         struct stat sys_statbuf;
185         char *own_info;
186         char *sys_info;
187         
188         own_info = vstfx_infofile_stat (dllpath, &own_statbuf, 1);
189         sys_info = vstfx_infofile_stat (dllpath, &sys_statbuf, 0);
190
191         if (own_info) {
192                 if (sys_info) {
193                         if (own_statbuf.st_mtime <= sys_statbuf.st_mtime) {
194                                 /* system info file is newer, use it */
195                                 return fopen (sys_info, "r");
196                         }
197                 } else {
198                         return fopen (own_info, "r");
199                 }
200         }
201
202         return 0;
203 }
204
205 static FILE* vstfx_infofile_create (char* dllpath, int personal)
206 {
207         char* path;
208         char* dir_path;
209         char* basename;
210         char* base;
211         size_t blen;
212
213         if (strstr (dllpath, ".so" ) == 0) {
214                 return 0;
215         }
216         
217         if (personal) {
218                 dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL);
219
220                 /* if the directory doesn't yet exist, try to create it */
221
222                 if (!g_file_test (dir_path, G_FILE_TEST_IS_DIR)) {
223                         if (g_mkdir (dir_path, 0700)) {
224                                 return 0;
225                         }
226                 }
227
228         } else {
229                 dir_path = g_path_get_dirname (dllpath);
230         }
231         
232         base = g_path_get_basename (dllpath);
233         blen = strlen (base) + 2; // null char and '.'
234         basename = (char*) g_malloc (blen);
235         snprintf (basename, blen, ".%s.fsi", base);
236         g_free (base);
237
238         path = g_build_filename (dir_path, basename, NULL);
239
240         g_free (dir_path);
241         g_free (basename);
242
243         FILE* f = fopen (path, "w");
244         g_free (path);
245
246         return f;
247 }
248
249 static FILE* vstfx_infofile_for_write (char* dllpath)
250 {
251         FILE* f;
252
253         if ((f = vstfx_infofile_create (dllpath, 0)) == 0) {
254                 f = vstfx_infofile_create (dllpath, 1);
255         }
256         
257         return f;
258 }
259
260 static
261 int vstfx_can_midi (VSTState* vstfx)
262 {
263         AEffect *plugin = vstfx->plugin;
264         
265         int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, 0, 0.0f);
266
267         if (vst_version >= 2)
268         {
269                 /* should we send it VST events (i.e. MIDI) */
270                 
271                 if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))
272                     return -1;
273         }
274         return false;
275 }
276
277 static VSTInfo *
278 vstfx_info_from_plugin (VSTState* vstfx)
279 {
280         assert (vstfx);
281         
282         VSTInfo* info = (VSTInfo*) malloc (sizeof (VSTInfo));
283         
284         AEffect *plugin;
285         
286         /*We need to init the creator because some plugins
287           fail to implement getVendorString, and so won't stuff the
288           string with any name*/
289         
290         char creator[65] = "Unknown\0";
291         
292         if(!info)
293                 return 0;
294         
295         plugin = vstfx->plugin;
296         
297         info->name = strdup(vstfx->handle->name ); 
298         
299         /*If the plugin doesn't bother to implement GetVendorString we will
300           have pre-stuffed the string with 'Unkown' */
301         
302         plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0);
303         
304         /*Some plugins DO implement GetVendorString, but DON'T put a name in it
305           so if its just a zero length string we replace it with 'Unknown' */
306         
307         if (strlen(creator) == 0) {
308                 info->creator = strdup("Unknown");
309         } else {
310                 info->creator = strdup (creator);
311         }
312         
313         info->UniqueID = plugin->uniqueID;
314         
315         info->Category = strdup("None");          // FIXME:  
316         info->numInputs = plugin->numInputs;
317         info->numOutputs = plugin->numOutputs;
318         info->numParams = plugin->numParams;
319         info->wantMidi = vstfx_can_midi(vstfx); 
320         info->hasEditor = plugin->flags & effFlagsHasEditor ? true : false;
321         info->canProcessReplacing = plugin->flags & effFlagsCanReplacing ? true : false;
322         info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams);
323         info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams);
324
325         for (int i = 0; i < info->numParams; ++i) {
326                 char name[64];
327                 char label[64];
328                 
329                 /*Not all plugins give parameters labels as well as names*/
330                 
331                 strcpy(name, "No Name");
332                 strcpy(label, "No Label");
333                 
334                 plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0);
335                 info->ParamNames[i] = strdup(name);
336                 
337                 //NOTE: 'effGetParamLabel' is no longer defined in vestige headers
338                 //plugin->dispatcher (plugin, effGetParamLabel, i, 0, label, 0);
339                 info->ParamLabels[i] = strdup(label);
340         }
341         return info;
342 }
343
344 /* A simple 'dummy' audiomaster callback which should be ok,
345 we will only be instantiating the plugin in order to get its info*/
346
347 static intptr_t
348 simple_master_callback (AEffect *, int32_t opcode, int32_t, intptr_t, void *, float)
349 {
350         if (opcode == audioMasterVersion)
351                 return 2;
352         else
353                 return 0;
354 }
355
356 /*Try to get plugin info - first by looking for a .fsi cache of the
357 data, and if that doesn't exist, load the plugin, get its data and
358 then cache it for future ref*/
359
360 VSTInfo *
361 vstfx_get_info (char* dllpath)
362 {
363         FILE* infofile;
364         VSTHandle* h;
365         VSTState* vstfx;
366         VSTInfo* info;
367
368         if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) {
369                 VSTInfo *info;
370                 info = load_vstfx_info_file (infofile);
371                 fclose (infofile);
372                 PBD::warning << "Cannot get LinuxVST information form " << dllpath << ": info file load failed." << endmsg;
373                 return info;
374         } 
375         
376         if(!(h = vstfx_load(dllpath))) {
377                 PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
378                 return 0;
379         }
380         
381         if(!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) {
382                 vstfx_unload(h);
383                 PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
384                 return 0;
385         }
386         
387         infofile = vstfx_infofile_for_write (dllpath);
388         
389         if(!infofile) {
390                 vstfx_close(vstfx);
391                 vstfx_unload(h);
392                 PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": cannot create new FST info file." << endmsg;
393                 return 0;
394         }
395         
396         info = vstfx_info_from_plugin(vstfx);
397         
398         save_vstfx_info_file(info, infofile);
399         fclose (infofile);
400         
401         vstfx_close(vstfx);
402         vstfx_unload(h);
403         
404         return info;
405 }
406
407 void
408 vstfx_free_info (VSTInfo *info)
409 {
410         for (int i = 0; i < info->numParams; i++) {
411                 free (info->ParamNames[i]);
412                 free (info->ParamLabels[i]);
413         }
414         
415         free (info->name);
416         free (info->creator);
417         free (info->Category);
418         free (info);
419 }
420
421