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