fix compose mess, and a number of 64 bit printf specs
[ardour.git] / libs / ardour / vst_plugin.cc
1 /*
2     Copyright (C) 2004 Paul Davis 
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <algorithm>
22 #include <vector>
23 #include <string>
24 #include <ctype.h>
25
26 #include <cstdlib>
27 #include <cstdio> // so libraptor doesn't complain
28 #include <cmath>
29 #include <dirent.h>
30 #include <string.h> // for memmove
31 #include <sys/stat.h>
32 #include <cerrno>
33
34 #include <lrdf.h>
35 #include <fst.h>
36
37 #include <pbd/compose.h>
38 #include <pbd/error.h>
39 #include <pbd/pathscanner.h>
40 #include <pbd/xml++.h>
41
42 #include <vst/aeffectx.h>
43
44 #include <midi++/manager.h>
45
46 #include <ardour/ardour.h>
47 #include <ardour/session.h>
48 #include <ardour/audioengine.h>
49 #include <ardour/vst_plugin.h>
50
51 #include <pbd/stl_delete.h>
52
53 #include "i18n.h"
54 #include <locale.h>
55
56 using namespace ARDOUR;
57 using std::min;
58 using std::max;
59
60 VSTPlugin::VSTPlugin (AudioEngine& e, Session& session, FSTHandle* h)
61         : Plugin (e, session)
62 {
63         handle = h;
64
65         if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) {
66                 throw failed_constructor();
67         }
68
69         _plugin = _fst->plugin;
70         _plugin->user = this;
71
72         /* set rate and blocksize */
73
74         _plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL, 
75                              (float) session.frame_rate());
76         _plugin->dispatcher (_plugin, effSetBlockSize, 0, 
77                              session.get_block_size(), NULL, 0.0f);
78         
79         /* set program to zero */
80
81         _plugin->dispatcher (_plugin, effSetProgram, 0, 0, NULL, 0.0f);
82         
83         Plugin::setup_midi_controls ();
84 }
85
86 VSTPlugin::VSTPlugin (const VSTPlugin &other)
87         : Plugin (other)
88 {
89         handle = other.handle;
90
91         if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) {
92                 throw failed_constructor();
93         }
94         _plugin = _fst->plugin;
95
96         Plugin::setup_midi_controls ();
97 }
98
99 VSTPlugin::~VSTPlugin ()
100 {
101         deactivate ();
102         GoingAway (this); /* EMIT SIGNAL */
103         fst_close (_fst);
104 }
105
106 void
107 VSTPlugin::set_block_size (jack_nframes_t nframes)
108 {
109         deactivate ();
110         _plugin->dispatcher (_plugin, effSetBlockSize, 0, nframes, NULL, 0.0f);
111         activate ();
112 }
113
114 void
115 VSTPlugin::store_state (PluginState& state)
116 {
117 }
118
119 void
120 VSTPlugin::restore_state (PluginState& state)
121 {
122 }
123
124 float
125 VSTPlugin::default_value (uint32_t port)
126 {
127         return 0;
128 }       
129
130 void
131 VSTPlugin::set_parameter (uint32_t which, float val)
132 {
133         _plugin->setParameter (_plugin, which, val);
134         ParameterChanged (which, val); /* EMIT SIGNAL */
135
136         if (session().get_midi_feedback()) {
137                 
138                 if (which < parameter_count() && midi_controls[which]) {
139                         midi_controls[which]->send_feedback (val);
140                 }
141         }
142 }
143
144 float
145 VSTPlugin::get_parameter (uint32_t which) const
146 {
147         return _plugin->getParameter (_plugin, which);
148         
149 }
150
151 uint32_t
152 VSTPlugin::nth_parameter (uint32_t n, bool& ok) const
153 {
154         ok = true;
155         return n;
156 }
157
158 XMLNode&
159 VSTPlugin::get_state()
160 {
161         XMLNode *root = new XMLNode (state_node_name());
162         LocaleGuard lg (X_("POSIX"));
163         
164         if (_plugin->flags & effFlagsProgramChunks) {
165
166                 /* fetch the current chunk */
167                 
168                 void* data;
169                 long  data_size;
170                 
171                 if ((data_size = _plugin->dispatcher (_plugin, effGetChunk, 0, 0, &data, false)) == 0) {
172                         return *root;
173                 }
174
175                 /* save it to a file */
176
177                 string path;
178                 struct stat sbuf;
179
180                 path = getenv ("HOME");
181                 path += "/.ardour/vst";
182
183                 if (stat (path.c_str(), &sbuf)) {
184                         if (errno == ENOENT) {
185                                 if (mkdir (path.c_str(), 0600)) {
186                                         error << string_compose (_("cannot create VST chunk directory: %1"),
187                                                           strerror (errno))
188                                               << endmsg;
189                                         return *root;
190                                 }
191
192                         } else {
193
194                                 error << string_compose (_("cannot check VST chunk directory: %1"),
195                                                   strerror (errno))
196                                       << endmsg;
197                                 return *root;
198                         }
199
200                 } else if (!S_ISDIR (sbuf.st_mode)) {
201                         error << string_compose (_("%1 exists but is not a directory"), path)
202                               << endmsg;
203                         return *root;
204                 }
205                 
206                 path += "something";
207                 
208                 /* store information */
209
210                 XMLNode* chunk_node = new XMLNode (X_("chunk"));
211                 chunk_node->add_property ("path", path);
212                 
213                 root->add_child_nocopy (*chunk_node);
214                 
215         } else {
216
217                 XMLNode* parameters = new XMLNode ("parameters");
218
219                 for (long n = 0; n < _plugin->numParams; ++n) {
220                         char index[64];
221                         char val[32];
222                         snprintf (index, sizeof (index), "param_%ld", n);
223                         snprintf (val, sizeof (val), "%f", _plugin->getParameter (_plugin, n));
224                         parameters->add_property (index, val);
225                 }
226
227                 root->add_child_nocopy (*parameters);
228         }
229
230         return *root;
231 }
232
233 int
234 VSTPlugin::set_state(const XMLNode& node)
235 {
236         LocaleGuard lg (X_("POSIX"));
237
238         if (node.name() != state_node_name()) {
239                 error << _("Bad node sent to VSTPlugin::set_state") << endmsg;
240                 return 0;
241         }
242
243         XMLNode* child;
244
245         if ((child = find_named_node (node, X_("chunks"))) != 0) {
246
247                 return 0;
248
249         } else if ((child = find_named_node (node, X_("parameters"))) != 0) {
250                 
251                 XMLPropertyList::const_iterator i;
252
253                 for (i = child->properties().begin(); i != child->properties().end(); ++i) {
254                         long param;
255                         float val;
256                         sscanf ((*i)->name().c_str(), "param_%ld", &param);
257                         sscanf ((*i)->value().c_str(), "%f", &val);
258
259                         _plugin->setParameter (_plugin, param, val);
260                 }
261
262                 return 0;
263         }
264
265         return -1;
266 }
267
268 int
269 VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const
270 {
271         VstParameterProperties prop;
272
273         desc.min_unbound = false;
274         desc.max_unbound = false;
275
276         if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
277
278                 /* i have yet to find or hear of a VST plugin that uses this */
279
280                 if (prop.flags & kVstParameterUsesIntegerMinMax) {
281                         desc.lower = prop.minInteger;
282                         desc.upper = prop.maxInteger;
283                 } else {
284                         desc.lower = 0;
285                         desc.upper = 1.0;
286                 }
287                 
288                 if (prop.flags & kVstParameterUsesIntStep) {
289                         
290                         desc.step = prop.stepInteger;
291                         desc.smallstep = prop.stepInteger;
292                         desc.largestep = prop.stepInteger;
293                         
294                 } else if (prop.flags & kVstParameterUsesFloatStep) {
295                         
296                         desc.step = prop.stepFloat;
297                         desc.smallstep = prop.smallStepFloat;
298                         desc.largestep = prop.largeStepFloat;
299                         
300                 } else {
301                         
302                         float range = desc.upper - desc.lower;
303                         
304                         desc.step = range / 100.0f;
305                         desc.smallstep = desc.step / 2.0f;
306                         desc.largestep = desc.step * 10.0f;
307                 }
308                 
309                 desc.toggled = prop.flags & kVstParameterIsSwitch;
310                 desc.logarithmic = false;
311                 desc.sr_dependent = false;
312                 desc.label = prop.label;
313
314         } else {
315
316                 /* old style */
317
318                 char label[64];
319                 label[0] = '\0';
320
321                 _plugin->dispatcher (_plugin, effGetParamName, which, 0, label, 0);
322
323                 desc.label = label;
324                 desc.integer_step = false;
325                 desc.lower = 0.0f;
326                 desc.upper = 1.0f;
327                 desc.step = 0.01f;
328                 desc.smallstep = 0.005f;
329                 desc.largestep = 0.1f;
330                 desc.toggled = false;
331                 desc.logarithmic = false;
332                 desc.sr_dependent = false;
333         }
334
335         return 0;
336 }
337
338 bool
339 VSTPlugin::load_preset (string name)
340 {
341         if (_plugin->flags & effFlagsProgramChunks) {
342                 error << _("no support for presets using chunks at this time")
343                       << endmsg;
344                 return false;
345         }
346         return Plugin::load_preset (name);
347 }
348
349 bool
350 VSTPlugin::save_preset (string name)
351 {
352         if (_plugin->flags & effFlagsProgramChunks) {
353                 error << _("no support for presets using chunks at this time")
354                       << endmsg;
355                 return false;
356         }
357         return Plugin::save_preset (name, "vst");
358 }
359
360 string
361 VSTPlugin::describe_parameter (uint32_t param)
362 {
363         char name[64];
364         _plugin->dispatcher (_plugin, effGetParamName, param, 0, name, 0);
365         return name;
366 }
367
368 jack_nframes_t
369 VSTPlugin::latency () const
370 {
371         return _plugin->initialDelay;
372 }
373
374 set<uint32_t>
375 VSTPlugin::automatable () const
376 {
377         set<uint32_t> ret;
378
379         for (uint32_t i = 0; i < parameter_count(); ++i){
380                 ret.insert (ret.end(), i);
381         }
382
383         return ret;
384 }
385
386 int
387 VSTPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in_index, int32_t& out_index, jack_nframes_t nframes, jack_nframes_t offset)
388 {
389         float *ins[_plugin->numInputs];
390         float *outs[_plugin->numOutputs];
391         int32_t i;
392
393         for (i = 0; i < (int32_t) _plugin->numInputs; ++i) {
394                 ins[i] = bufs[min((uint32_t) in_index,maxbuf)];
395                 in_index++;
396         }
397
398         for (i = 0; i < (int32_t) _plugin->numOutputs; ++i) {
399                 outs[i] = bufs[min((uint32_t) out_index,maxbuf)];
400
401                 /* unbelievably, several VST plugins still rely on Cubase
402                    behaviour and do not silence the buffer in processReplacing 
403                    when they have no output.
404                 */
405                 
406                 // memset (outs[i], 0, sizeof (Sample) * nframes);
407                 out_index++;
408         }
409
410
411         /* we already know it can support processReplacing */
412
413         _plugin->processReplacing (_plugin, ins, outs, nframes);
414         
415         return 0;
416 }
417
418 void
419 VSTPlugin::deactivate ()
420 {
421         _plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f);
422 }
423
424 void
425 VSTPlugin::activate ()
426 {
427         _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f);
428 }
429
430 uint32_t 
431 VSTPlugin::unique_id() const
432 {
433         return _plugin->uniqueID;
434 }
435
436
437 const char *
438 VSTPlugin::name () const
439 {
440         return handle->name;
441 }
442
443 const char *
444 VSTPlugin::maker () const
445 {
446         return "imadeit";
447 }
448
449 const char *
450 VSTPlugin::label () const
451 {
452         return handle->name;
453 }
454
455 uint32_t
456 VSTPlugin::parameter_count() const
457 {
458         return _plugin->numParams;
459 }
460
461 bool
462 VSTPlugin::has_editor () const
463 {
464         return _plugin->flags & effFlagsHasEditor;
465 }
466
467 void
468 VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
469 {
470         char lab[9];
471         char *first_nonws;
472
473         _plugin->dispatcher (_plugin, effGetParamLabel, param, 0, lab, 0);
474         _plugin->dispatcher (_plugin, effGetParamDisplay, param, 0, buf, 0);
475
476         if (buf[0] == '\0') {
477                 return;
478         }
479
480         first_nonws = buf;
481         while (*first_nonws && isspace (*first_nonws)) {
482                 first_nonws++;
483         }
484         if (*first_nonws == '\0') {
485                 return;
486         }
487
488         memmove (buf, first_nonws, strlen (buf) - (first_nonws - buf) + 1);
489 }