Commits

Albert Graef committed 94c1098

Add support for LV2 presets.

Comments (0)

Files changed (4)

pure-lilv/examples/lilv_examp.pure

 
-using lilv;
+using lilv, system;
 
 // Load all plugins.
 let world = lilv::world;
 // A little helper function to print the (basic or extended) info of a plugin
 // in a readable format.
 
-using system;
-
 print (name, uri, class, author, email, homepage, bundle, binary, data_uris,
        required, optional, extension_data, presets, ports) = ()
 when
 // Output buffer (just a zero matrix).
 let out = dmatrix (2,8);
 
-// Activate the plugin.
+// We first need to activate the plugin.
 lilv::activate p;
 
-// Let's run this through the plugin.
+// Now let's run some samples through the plugin.
 lilv::run p in out;
 
 // Port #2 is used to set the gain (amplification in dB).
 // left and the right channel.
 map (lilv::get_control p) [4,5];
 
-// Let's compute some more samples.
+// Let's compute some more samples. This will show that the values of port #4
+// and #5 continue to change.
 lilv::run p in out;
 map (lilv::get_control p) [4,5];
 
 lilv::set_midi p 10 [(4,{0xb0, 8, 64})];
 lilv::run p in out;
 map (lilv::get_control p) [4,5];
+
+// Finally, the presets. If an LV2 plugin has any presets associated with it,
+// the lilv::presets function will return their names and corresponding URIs
+// (as with plugins, the latter are used to identify a preset and may be
+// specified to load it).
+lilv::presets p;
+
+// Ok, so the amp plugin doesn't have any presets. But we can make our own
+// from the current state of our plugin instance:
+let ps =
+  lilv::get_preset world "http://faust-lv2.googlecode.com/presets#myamp" p;
+
+// Note that we invented our own URI for the preset there. The result is a
+// string in Turtle syntax. Here is how it looks like:
+puts ps;
+
+// You can reload the saved plugin state at any time:
+lilv::set_control p 2 10, lilv::set_control p 3 1;
+map (lilv::get_control p) [2,3];
+lilv::set_preset world ps p;
+map (lilv::get_control p) [2,3];

pure-lilv/examples/synth.pure

 // Synth and effect plugins to be used. The defaults here require the
 // faust-lv2 plugins (http://code.google.com/p/faust-lv2/).
 let synth_name,chorus_name,reverb_name = "organ","chorus","freeverb";
+// Pick a preset for the synth.
+let preset_name = "";
 
 // Here are some alternatives from the Calf LV2 plugin collection
 // (http://calf.sourceforge.net/).
   midip _ = false;
 end;
 
+let have_synth, have_midi = false, true;
+
 #! --if compiled
-// In the compiled script, allow the user to pick a synth from the command
-// line.
-let synth_name = if compiling || argc<=2 then synth_name else argv!1;
+// Parse the command line. Check whether we got a synth in argv!1.
+let have_synth = argc>=2 && any (==argv!1) synths;
+// Check whether we got a MIDI file as well.
+let have_midi = argc~=2 || ~have_synth;
+// In the compiled script, allow the user to pick a synth (and, optionally, a
+// preset) from the command line.
+let synth_name = if compiling || ~have_synth then synth_name else argv!1;
+let preset_name = if compiling || ~have_synth || argc<=3 then "" else argv!2;
 // Print a short usage message and the list of available synth units if we're
 // invoked without arguments.
+let USAGE = "Usage: %s [synth-name [preset-name]] midifile\n\
+Or use %s synth-name to get a list of all presets for the given synth.\n\
+E.g., if you have the Calf Organ installed, try something like:\n\
+%s 'Calf Organ' Shamrock prelude3.mid\n\
+\nAvailable synth units:\n%s\n";
 compiling || argc>=2 ||
-(fprintf stderr "Usage: %s [synth] midifile\nAvailable synth units:\n%s\n"
- (head argv,join ", " synths) $$ exit 1);
+(fprintf stderr USAGE (prog,prog,prog,join ", " synths) $$ exit 1 when
+   prog = head argv;
+ end);
 #! --endif
 
-// Audio setup.
+// Audio setup. We have to do that here before instantiating the plugins
+// because we need to know our sample rate for that. Ugh.
 
 using audio, realtime;
 
   throw "no MIDI input, check your synth unit";
 let midi_in:_ = lilv::midi_inputs synth;
 
+// Let the user select presets for the synth unit by their names.
+let presets = record {name=>uri | name,uri = lilv::presets synth};
+~stringp preset_name || null preset_name || ~member presets preset_name ||
+lilv::load_preset world (presets!preset_name) synth;
+
+#! --if compiled
+// Print the name of the synth that was chosen and the preset.
+compiling || printf "Synth: %s\n" synth_name;
+let have_preset = stringp preset_name && ~null preset_name;
+compiling || ~have_preset || printf "Preset: %s\n" preset_name;
+// If no MIDI file or preset was given, print the available presets.
+compiling || have_preset || have_midi || null presets ||
+printf "Available presets:\n%s\n" (join ", " (list (keys presets)));
+// If we were invoked with just the synth name, bail out now.
+compiling || have_midi || exit 1;
+#! --endif
+
 // Create buffers.
 let in,out = dmatrix (k,n), dmatrix (l,n);
 let out1,out2 = dmatrix (l1,n), dmatrix (l2,n);
 // Main program, lets you specify a midi file on the command line.
 
 #! --if compiled
-main = play (load (last argv));
+main = play seq if listp seq when seq = load (last argv) end;
+= fprintf stderr "bad MIDI file: %s\n" (last argv) $$ exit 1 otherwise;
 compiling || catch (\_->exit 1) main;
 #! --endif
   if (!uri) return 0;
   const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
   const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri);
+  lilv_node_free(uri);
   if (!p) return 0;
   // Cached URIs.
   LilvNode* input_class = lilv_new_uri(world, LILV_URI_INPUT_PORT);
   LilvInstance *instance;
   double sample_rate;
   uint32_t block_size, ev_buf_size;
-  uint32_t atom_chunk, atom_sequence, midi_event;
+  uint32_t atom_chunk, atom_sequence, midi_event,
+    atom_float, atom_double, atom_int, atom_long;
+  // Number of associated presets.
+  uint32_t n_presets;
+  // Preset names and URIs.
+  char **preset_names, **preset_uris;
   // Total number of ports.
   uint32_t n;
   // Port names and symbols.
   if (!uri) return 0;
   const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
   const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri);
+  lilv_node_free(uri);
   if (!p) return 0;
   PluginInstance *ret = (PluginInstance*)malloc(sizeof(PluginInstance));
   if (!ret) return 0;
   LilvNode* cv_class = lilv_new_uri(world, MY_URI_CV_PORT);
   LilvNode* event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT);
   LilvNode* atom_class = lilv_new_uri(world, MY_URI_ATOM_PORT);
+  LilvNode* preset_class = lilv_new_uri(world, LV2_PRESETS__Preset);
+  LilvNode* label_pred = lilv_new_uri(world, LILV_NS_RDFS "label");
   LilvNode* midi_event = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
   LilvNode* atom_Chunk = lilv_new_uri(world, LV2_ATOM__Chunk);
   LilvNode* atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
-  // We need these URIs later when dealing with the event buffers.
+  // We need these URIs later.
   ret->midi_event = map.map(map.handle, LILV_URI_MIDI_EVENT);
   ret->atom_chunk = map.map(map.handle, LV2_ATOM__Chunk);
   ret->atom_sequence = map.map(map.handle, LV2_ATOM__Sequence);
+  ret->atom_float = map.map(map.handle, LV2_ATOM__Float);
+  ret->atom_double = map.map(map.handle, LV2_ATOM__Double);
+  ret->atom_int = map.map(map.handle, LV2_ATOM__Int);
+  ret->atom_long = map.map(map.handle, LV2_ATOM__Long);
+  // Presets.
+  LilvNodes *data = lilv_plugin_get_related(p, preset_class);
+  size_t k = 0, l = lilv_nodes_size(data);
+  ret->preset_names = calloc(l, sizeof(char*));
+  ret->preset_uris = calloc(l, sizeof(char*));
+  if (data) {
+    LILV_FOREACH (nodes, i, data) {
+      const char *plabel = 0;
+      const char *puri = lilv_node_as_uri(lilv_nodes_get(data, i));
+      const LilvNode* preset = lilv_nodes_get(data, i);
+      lilv_world_load_resource(world, preset);
+      LilvNodes* titles =
+	lilv_world_find_nodes(world, preset, label_pred, NULL);
+      if (titles) {
+	const LilvNode* title = lilv_nodes_get_first(titles);
+	plabel = lilv_node_as_string(title);
+	lilv_nodes_free(titles);
+      }
+      if (!plabel) plabel = "";
+      assert(k<l);
+      ret->preset_names[k] = strdup(plabel);
+      ret->preset_uris[k] = strdup(puri);
+      k++;
+    }
+    lilv_nodes_free(data);
+  }
+  ret->n_presets = k;
   // Make a first pass through the port list to fill in the basic port data
   // and determine the number of audio/CV and atom/event input/output ports.
   // We also connect all ports to their corresponding buffers here.
   lilv_node_free(control_class);
   lilv_node_free(event_class);
   lilv_node_free(atom_class);
+  lilv_node_free(preset_class);
+  lilv_node_free(label_pred);
   lilv_node_free(midi_event);
   lilv_node_free(atom_Chunk);
   lilv_node_free(atom_Sequence);
 {
   if (!p) return;
   lilv_instance_free(p->instance);
+  if (p->preset_names) {
+    for (uint32_t i = 0; i < p->n_presets; i++)
+      free(p->preset_names[i]);
+    free(p->preset_names);
+  }
+  if (p->preset_uris) {
+    for (uint32_t i = 0; i < p->n_presets; i++)
+      free(p->preset_uris[i]);
+    free(p->preset_uris);
+  }
   if (p->sym) {
     for (uint32_t i = 0; i < p->n; i++)
       free(p->sym[i]);
   free(xv);
   return 0;
 }
+
+/* Retrieve preset information. This returns a list with pairs of names and
+   uris of the known presets for the plugin. The meaning is the same as with
+   the lilv_plugin_info() operation. */
+
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+
+pure_expr *lilv_plugin_presets(PluginInstance *p)
+{
+  if (!p) return 0;
+  pure_expr **xv = (pure_expr**)calloc(p->n_presets, sizeof(pure_expr*));
+  for (int32_t i = 0; i < p->n_presets; i++)
+    xv[i] = pure_tuplel(2, pure_cstring_dup(p->preset_names[i]),
+			pure_cstring_dup(p->preset_uris[i]));
+  pure_expr *ret = pure_listv(p->n_presets, xv); free(xv);
+  return ret;
+}
+
+/* Load a preset given by its URI from the given world state and modify the
+   control values of the given plugin accordingly. */
+
+static void
+set_port_value(const char* port_symbol,
+               void*       user_data,
+               const void* value,
+               uint32_t    size,
+               uint32_t    type)
+{
+  PluginInstance *p = (PluginInstance*)user_data;
+  int32_t k = 0;
+  // XXXFIXME: Linear search, we should really do something better here.
+  while (k < p->n && strcmp(port_symbol, p->sym[k])) k++;
+  if (k >= p->n)
+    return; // maybe we should output an error message here?
+  float x;
+  if (type == p->atom_float)
+    x = *(const float*)value;
+  else if (type == p->atom_double)
+    x = *(const double*)value;
+  else if (type == p->atom_int)
+    x = *(const int32_t*)value;
+  else if (type == p->atom_long)
+    x = *(const int64_t*)value;
+  else
+    return; // maybe we should output an error message here?
+#if 0
+  printf("%s -> %f\n", port_symbol, x);
+#endif
+  p->data[k] = x;
+}
+
+pure_expr *lilv_plugin_load_preset(LilvWorld* world, const char* preset_uri,
+				   PluginInstance *p)
+{
+  if (!p) return 0;
+  LilvNode* uri = lilv_new_uri(world, preset_uri);
+  if (!uri) return 0;
+  lilv_world_load_resource(world, uri);
+  LilvState* state = lilv_state_new_from_world(world, &map, uri);
+  lilv_node_free(uri);
+  if (state) {
+    lilv_state_restore(state, p->instance, set_port_value, p, 0, NULL);
+    lilv_state_free(state);
+    return pure_tuplel(0, 0);
+  } else
+    return 0;
+}
+
+/* Load a preset from the given string (as returned by a previous call to
+   lilv_plugin_get_preset, see below) and modify the control values of the
+   given plugin accordingly. */
+
+pure_expr *lilv_plugin_set_preset(LilvWorld* world, const char* preset,
+				  PluginInstance *p)
+{
+  if (!p) return 0;
+  LilvState* state = lilv_state_new_from_string(world, &map, preset);
+  if (state) {
+    lilv_state_restore(state, p->instance, set_port_value, p, 0, NULL);
+    lilv_state_free(state);
+    return pure_tuplel(0, 0);
+  } else
+    return 0;
+}
+
+/* This returns the current state of a plugin as an LV2 preset in Turtle
+   format. The resulting string can be passed to lilv_plugin_set_preset to
+   restore the corresponding plugin state. */
+
+static const void*
+get_port_value(const char* port_symbol,
+               void*       user_data,
+               uint32_t*   size,
+               uint32_t*   type)
+{
+  PluginInstance *p = (PluginInstance*)user_data;
+  int32_t k = 0;
+  // XXXFIXME: Linear search, we should really do something better here.
+  while (k < p->n && strcmp(port_symbol, p->sym[k])) k++;
+  if (k < p->n && p->ty[k] == 1 && (p->flags[k]&1)) {
+    *size = sizeof(float);
+    *type = p->atom_float;
+    return &p->data[k];
+  } else {
+    *size = *type = 0;
+    return NULL;
+  }
+}
+
+pure_expr *lilv_plugin_get_preset(LilvWorld* world, const char* preset_uri,
+				  PluginInstance *p)
+{
+  if (!p) return 0;
+  LilvNode* uri = lilv_new_uri(world, lilv_instance_get_uri(p->instance));
+  if (!uri) return 0;
+  const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+  const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, uri);
+  lilv_node_free(uri);
+  if (!plugin) return 0;
+  LilvState* const state = lilv_state_new_from_instance
+    (plugin, p->instance, &map,	NULL, NULL, NULL, NULL,
+     get_port_value, p, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, NULL);
+  if (!state) return 0;
+  char *ret = lilv_state_to_string
+    (world, &map, &unmap, state, preset_uri, NULL);
+  lilv_state_free(state);
+  if (ret)
+    return pure_cstring(ret);
+  else
+    return 0;
+}

pure-lilv/lilv.pure

 
 extern expr *lilv_plugin_get_midi(PluginInstance *p, int k) = get_midi;
 extern expr *lilv_plugin_set_midi(PluginInstance *p, int k, expr *x) = set_midi;
+
+/* Handle preset information. lilv::presets returns a list with (name,uri)
+   pairs of known presets for the plugin. Each of the listed uris can then be
+   used with lilv::load_preset to load the preset into the plugin, modifying
+   its control values accordingly. The presets are loaded from the lilv world
+   state, so the world pointer needs to be passed as the first argument of
+   lilv::load_preset. */
+
+extern expr *lilv_plugin_presets(PluginInstance *p) = presets;
+extern expr *lilv_plugin_load_preset(LilvWorld* world, char* preset_uri,
+				     PluginInstance *p) = load_preset;
+
+/* Alternatively, the current state can also be retrieved from a plugin as a
+   string in Turtle syntax (lilv::get_preset) and later restored from that
+   string (lilv::set_preset). Both operations take the lilv world state as the
+   first and the plugin instance as the last parameter. When used in concert,
+   these functions give you a way to serialize the state of a plugin using a
+   format which can easily be written to disk and reloaded later.
+
+   The second parameter of lilv::get_preset must denote a valid uri for the
+   preset (given as a string). The function returns the preset in Turtle
+   syntax, as a string. The second parameter of lilv::set_preset is the preset
+   itself, as returned by a previous call to lilv::get_preset. The function
+   modifies the controls of the plugin accordingly and returns (). Both
+   operations fail in case of error. */
+
+extern expr *lilv_plugin_get_preset(LilvWorld* world, char* preset_uri,
+				    PluginInstance *p) = get_preset;
+extern expr *lilv_plugin_set_preset(LilvWorld* world, char* preset,
+				    PluginInstance *p) = set_preset;