Button: emit slopes (fix for stuck modulaor plug-ins)
[calf.git] / src / preset.cpp
blobf59b427f44a6f04fc6dbb69269d27ccc71c7edf4
1 /* Calf DSP Library
2 * Preset management.
4 * Copyright (C) 2001-2007 Krzysztof Foltman
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #include <calf/giface.h>
23 #include <calf/preset.h>
24 #include <expat.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
30 using namespace std;
31 using namespace calf_plugins;
32 using namespace calf_utils;
34 extern calf_plugins::preset_list &calf_plugins::get_builtin_presets()
36 static calf_plugins::preset_list plist;
37 return plist;
40 extern calf_plugins::preset_list &calf_plugins::get_user_presets()
42 static calf_plugins::preset_list plist;
43 return plist;
46 std::string plugin_preset::to_xml()
48 std::stringstream ss;
49 ss << "<preset bank=\"" << bank << "\" program=\"" << program << "\" plugin=\"" << xml_escape(plugin) << "\" name=\"" << xml_escape(name) << "\">\n";
50 for (unsigned int i = 0; i < values.size(); i++) {
51 if (i < param_names.size())
52 ss << " <param name=\"" << xml_escape(param_names[i]) << "\" value=\"" << values[i] << "\" />\n";
53 else
54 ss << " <param value=\"" << values[i] << "\" />\n";
56 for (map<string, string>::iterator i = variables.begin(); i != variables.end(); i++)
58 ss << " <var name=\"" << xml_escape(i->first) << "\">" << xml_escape(i->second) << "</var>\n";
60 ss << "</preset>\n";
61 return ss.str();
64 string plugin_preset::get_safe_name()
66 stringstream ss;
67 for (size_t i = 0; i < name.length(); i++)
69 if (isdigit(name[i]) || isalpha(name[i]))
70 ss << name[i];
72 return ss.str();
75 void plugin_preset::activate(plugin_ctl_iface *plugin)
77 // First, clear everything to default values (in case some parameters or variables are missing)
78 plugin->clear_preset();
79 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
81 map<string, int> names;
82 int count = metadata->get_param_count();
83 // this is deliberately done in two separate loops - if you wonder why, just think for a while :)
84 for (int i = 0; i < count; i++)
85 names[metadata->get_param_props(i)->name] = i;
86 for (int i = 0; i < count; i++)
87 names[metadata->get_param_props(i)->short_name] = i;
88 // no support for unnamed parameters... tough luck :)
89 for (unsigned int i = 0; i < min(param_names.size(), values.size()); i++)
91 map<string, int>::iterator pos = names.find(param_names[i]);
92 if (pos == names.end()) {
93 // XXXKF should have a mechanism for notifying a GUI
94 printf("Warning: unknown parameter %s for plugin %s\n", param_names[i].c_str(), this->plugin.c_str());
95 continue;
97 plugin->set_param_value(pos->second, values[i]);
99 vector<string> vnames;
100 metadata->get_configure_vars(vnames);
101 for (unsigned n = 0; n < vnames.size(); ++n)
103 const char *key = vnames[n].c_str();
104 map<string, string>::const_iterator i = variables.find(key);
105 if (i == variables.end())
106 plugin->configure(key, NULL);
107 else
108 plugin->configure(key, i->second.c_str());
112 void plugin_preset::get_from(plugin_ctl_iface *plugin)
114 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
115 int count = metadata->get_param_count();
116 for (int i = 0; i < count; i++) {
117 param_names.push_back(metadata->get_param_props(i)->short_name);
118 values.push_back(plugin->get_param_value(i));
120 struct store_obj: public send_configure_iface
122 map<string, string> *data;
123 void send_configure(const char *key, const char *value)
125 (*data)[key] = value;
127 } tmp;
128 variables.clear();
129 tmp.data = &variables;
130 plugin->send_configures(&tmp);
133 string calf_plugins::preset_list::get_preset_filename(bool builtin)
135 if (builtin)
137 return PKGLIBDIR "/presets.xml";
139 else
141 const char *home = getenv("HOME");
142 return string(home)+"/.calfpresets";
146 void preset_list::plugin_snapshot::reset()
148 type.clear();
149 instance_name.clear();
150 preset_offset = input_index = output_index = midi_index = 0;
151 automation_entries.clear();
154 void preset_list::xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
156 preset_list &self = *(preset_list *)user_data;
157 bool rack_mode = self.rack_mode;
158 parser_state &state = self.state;
159 plugin_preset &parser_preset = self.parser_preset;
160 switch(state)
162 case START:
163 if (!rack_mode && !strcmp(name, "presets")) {
164 state = LIST;
165 return;
167 if (rack_mode && !strcmp(name, "rack")) {
168 state = RACK;
169 return;
171 break;
172 case RACK:
173 if (!strcmp(name, "plugin")) {
174 self.parser_plugin.reset();
175 self.parser_plugin.preset_offset = self.presets.size();
176 for(; *attrs; attrs += 2) {
177 if (!strcmp(attrs[0], "type")) self.parser_plugin.type = attrs[1];
178 else
179 if (!strcmp(attrs[0], "instance-name")) self.parser_plugin.instance_name = attrs[1];
180 else
181 if (!strcmp(attrs[0], "input-index")) self.parser_plugin.input_index = atoi(attrs[1]);
182 else
183 if (!strcmp(attrs[0], "output-index")) self.parser_plugin.output_index = atoi(attrs[1]);
184 else
185 if (!strcmp(attrs[0], "midi-index")) self.parser_plugin.midi_index = atoi(attrs[1]);
187 state = PLUGIN;
188 return;
190 break;
191 case PLUGIN:
192 if (!strcmp(name, "automation"))
194 string key, value;
195 for(; *attrs; attrs += 2) {
196 if (!strcmp(*attrs, "key")) key = attrs[1];
197 else
198 if (!strcmp(*attrs, "value")) value = attrs[1];
200 if (!key.empty() && !value.empty())
201 self.parser_plugin.automation_entries.push_back(make_pair(key, value));
202 state = AUTOMATION_ENTRY;
203 return;
205 // fall through
206 case LIST:
207 if (!strcmp(name, "preset")) {
209 parser_preset.bank = parser_preset.program = 0;
210 parser_preset.name = "";
211 parser_preset.plugin = "";
212 parser_preset.param_names.clear();
213 parser_preset.values.clear();
214 parser_preset.variables.clear();
215 for(; *attrs; attrs += 2) {
216 if (!strcmp(attrs[0], "name")) self.parser_preset.name = attrs[1];
217 else
218 if (!strcmp(attrs[0], "plugin")) self.parser_preset.plugin = attrs[1];
220 // autonumbering of programs for DSSI
221 if (!self.last_preset_ids.count(self.parser_preset.plugin))
222 self.last_preset_ids[self.parser_preset.plugin] = 0;
223 self.parser_preset.program = ++self.last_preset_ids[self.parser_preset.plugin];
224 self.parser_preset.bank = (self.parser_preset.program >> 7);
225 self.parser_preset.program &= 127;
226 state = PRESET;
227 return;
229 break;
230 case PRESET:
231 if (!strcmp(name, "param")) {
232 std::string name;
233 float value = 0.f;
234 for(; *attrs; attrs += 2) {
235 if (!strcmp(attrs[0], "name")) name = attrs[1];
236 else
237 if (!strcmp(attrs[0], "value")) {
238 istringstream str(attrs[1]);
239 str >> value;
242 self.parser_preset.param_names.push_back(name);
243 self.parser_preset.values.push_back(value);
244 state = VALUE;
245 return;
247 if (!strcmp(name, "var")) {
248 self.current_key = "";
249 for(; *attrs; attrs += 2) {
250 if (!strcmp(attrs[0], "name")) self.current_key = attrs[1];
252 if (self.current_key.empty())
253 throw preset_exception("No name specified for preset variable", "", 0);
254 self.parser_preset.variables[self.current_key].clear();
255 state = VAR;
256 return;
258 break;
259 case VALUE:
260 // no nested elements allowed inside <param>
261 break;
262 case VAR:
263 // no nested elements allowed inside <var>
264 break;
265 case AUTOMATION_ENTRY:
266 // no nested elements allowed inside <automation>
267 break;
269 // g_warning("Invalid XML element: %s", name);
270 throw preset_exception("Invalid XML element: %s", name, 0);
273 void preset_list::xml_end_element_handler(void *user_data, const char *name)
275 preset_list &self = *(preset_list *)user_data;
276 bool rack_mode = self.rack_mode;
277 preset_vector &presets = self.presets;
278 parser_state &state = self.state;
279 switch(state)
281 case START:
282 break;
283 case AUTOMATION_ENTRY:
284 if (!strcmp(name, "automation"))
286 state = PLUGIN;
287 return;
289 break;
290 case LIST:
291 if (!strcmp(name, "presets")) {
292 state = START;
293 return;
295 break;
296 case PLUGIN:
297 if (!strcmp(name, "plugin")) {
298 self.plugins.push_back(self.parser_plugin);
299 state = RACK;
300 return;
302 break;
303 case RACK:
304 if (!strcmp(name, "rack")) {
305 state = START;
306 return;
308 break;
309 case PRESET:
310 if (!strcmp(name, "preset")) {
311 presets.push_back(self.parser_preset);
312 state = rack_mode ? PLUGIN : LIST;
313 return;
315 break;
316 case VALUE:
317 if (!strcmp(name, "param")) {
318 state = PRESET;
319 return;
321 break;
322 case VAR:
323 if (!strcmp(name, "var")) {
324 state = PRESET;
325 return;
327 break;
329 throw preset_exception("Invalid XML element close: %s", name, 0);
332 void preset_list::xml_character_data_handler(void *user_data, const XML_Char *data, int len)
334 preset_list &self = *(preset_list *)user_data;
335 parser_state &state = self.state;
336 if (state == VAR)
338 self.parser_preset.variables[self.current_key] += string(data, len);
339 return;
343 bool preset_list::load_defaults(bool builtin)
345 try {
346 struct stat st;
347 string name = preset_list::get_preset_filename(builtin);
348 if (!stat(name.c_str(), &st)) {
349 load(name.c_str(), false);
350 if (!presets.empty())
351 return true;
354 catch(preset_exception &ex)
356 return false;
358 return false;
361 void preset_list::parse(const std::string &data, bool in_rack_mode)
363 rack_mode = in_rack_mode;
364 state = START;
365 XML_Parser parser = XML_ParserCreate("UTF-8");
366 XML_SetUserData(parser, this);
367 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
368 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
369 XML_Status status = XML_Parse(parser, data.c_str(), data.length(), 1);
370 if (status == XML_STATUS_ERROR) {
371 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
372 XML_ParserFree(parser);
373 throw preset_exception(err, "string", errno);
375 XML_ParserFree(parser);
378 void preset_list::load(const char *filename, bool in_rack_mode)
380 rack_mode = in_rack_mode;
381 state = START;
382 XML_Parser parser = XML_ParserCreate("UTF-8");
383 XML_SetUserData(parser, this);
384 int fd = open(filename, O_RDONLY);
385 if (fd < 0)
386 throw preset_exception("Could not load the presets from ", filename, errno);
387 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
388 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
389 char buf[4096];
392 int len = read(fd, buf, 4096);
393 // XXXKF not an optimal error/EOF handling :)
394 if (len <= 0)
395 break;
396 XML_Status status = XML_Parse(parser, buf, len, 0);
397 if (status == XML_STATUS_ERROR)
398 throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
399 } while(1);
400 XML_Status status = XML_Parse(parser, buf, 0, 1);
401 close(fd);
402 if (status == XML_STATUS_ERROR)
404 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
405 XML_ParserFree(parser);
406 throw preset_exception(err, filename, errno);
408 XML_ParserFree(parser);
411 void preset_list::save(const char *filename)
413 string xml = "<presets>\n";
414 for (unsigned int i = 0; i < presets.size(); i++)
416 xml += presets[i].to_xml();
418 xml += "</presets>";
419 int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
420 if (fd < 0 || ((unsigned)write(fd, xml.c_str(), xml.length()) != xml.length()))
421 throw preset_exception("Could not save the presets in ", filename, errno);
422 close(fd);
425 void preset_list::get_for_plugin(preset_vector &vec, const char *plugin)
427 for (unsigned int i = 0; i < presets.size(); i++)
429 if (presets[i].plugin == plugin)
430 vec.push_back(presets[i]);
434 void preset_list::add(const plugin_preset &sp)
436 for (unsigned int i = 0; i < presets.size(); i++)
438 if (presets[i].plugin == sp.plugin && presets[i].name == sp.name)
440 presets[i] = sp;
441 return;
444 presets.push_back(sp);