Automation: add save/load of automation data to rack save/load functionality
[calf.git] / src / preset.cpp
blob5f1c169815068ebb09221b40afd28bbf9d1443c1
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 const char *const *vnames = metadata->get_configure_vars();
100 if (vnames)
102 for (; *vnames; vnames++)
104 const char *key = *vnames;
105 map<string, string>::const_iterator i = variables.find(key);
106 if (i == variables.end())
107 plugin->configure(key, NULL);
108 else
109 plugin->configure(key, i->second.c_str());
114 void plugin_preset::get_from(plugin_ctl_iface *plugin)
116 const plugin_metadata_iface *metadata = plugin->get_metadata_iface();
117 int count = metadata->get_param_count();
118 for (int i = 0; i < count; i++) {
119 param_names.push_back(metadata->get_param_props(i)->short_name);
120 values.push_back(plugin->get_param_value(i));
122 struct store_obj: public send_configure_iface
124 map<string, string> *data;
125 void send_configure(const char *key, const char *value)
127 (*data)[key] = value;
129 } tmp;
130 variables.clear();
131 tmp.data = &variables;
132 plugin->send_configures(&tmp);
135 string calf_plugins::preset_list::get_preset_filename(bool builtin)
137 if (builtin)
139 return PKGLIBDIR "/presets.xml";
141 else
143 const char *home = getenv("HOME");
144 return string(home)+"/.calfpresets";
148 void preset_list::plugin_snapshot::reset()
150 type.clear();
151 instance_name.clear();
152 preset_offset = input_index = output_index = midi_index = 0;
153 automation_entries.clear();
156 void preset_list::xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
158 preset_list &self = *(preset_list *)user_data;
159 bool rack_mode = self.rack_mode;
160 parser_state &state = self.state;
161 plugin_preset &parser_preset = self.parser_preset;
162 switch(state)
164 case START:
165 if (!rack_mode && !strcmp(name, "presets")) {
166 state = LIST;
167 return;
169 if (rack_mode && !strcmp(name, "rack")) {
170 state = RACK;
171 return;
173 break;
174 case RACK:
175 if (!strcmp(name, "plugin")) {
176 self.parser_plugin.reset();
177 self.parser_plugin.preset_offset = self.presets.size();
178 for(; *attrs; attrs += 2) {
179 if (!strcmp(attrs[0], "type")) self.parser_plugin.type = attrs[1];
180 else
181 if (!strcmp(attrs[0], "instance-name")) self.parser_plugin.instance_name = attrs[1];
182 else
183 if (!strcmp(attrs[0], "input-index")) self.parser_plugin.input_index = atoi(attrs[1]);
184 else
185 if (!strcmp(attrs[0], "output-index")) self.parser_plugin.output_index = atoi(attrs[1]);
186 else
187 if (!strcmp(attrs[0], "midi-index")) self.parser_plugin.midi_index = atoi(attrs[1]);
189 state = PLUGIN;
190 return;
192 break;
193 case PLUGIN:
194 if (!strcmp(name, "automation"))
196 string key, value;
197 for(; *attrs; attrs += 2) {
198 if (!strcmp(*attrs, "key")) key = attrs[1];
199 else
200 if (!strcmp(*attrs, "value")) value = attrs[1];
202 if (!key.empty() && !value.empty())
203 self.parser_plugin.automation_entries.push_back(make_pair(key, value));
204 state = AUTOMATION_ENTRY;
205 return;
207 // fall through
208 case LIST:
209 if (!strcmp(name, "preset")) {
211 parser_preset.bank = parser_preset.program = 0;
212 parser_preset.name = "";
213 parser_preset.plugin = "";
214 parser_preset.param_names.clear();
215 parser_preset.values.clear();
216 parser_preset.variables.clear();
217 for(; *attrs; attrs += 2) {
218 if (!strcmp(attrs[0], "name")) self.parser_preset.name = attrs[1];
219 else
220 if (!strcmp(attrs[0], "plugin")) self.parser_preset.plugin = attrs[1];
222 // autonumbering of programs for DSSI
223 if (!self.last_preset_ids.count(self.parser_preset.plugin))
224 self.last_preset_ids[self.parser_preset.plugin] = 0;
225 self.parser_preset.program = ++self.last_preset_ids[self.parser_preset.plugin];
226 self.parser_preset.bank = (self.parser_preset.program >> 7);
227 self.parser_preset.program &= 127;
228 state = PRESET;
229 return;
231 break;
232 case PRESET:
233 if (!strcmp(name, "param")) {
234 std::string name;
235 float value = 0.f;
236 for(; *attrs; attrs += 2) {
237 if (!strcmp(attrs[0], "name")) name = attrs[1];
238 else
239 if (!strcmp(attrs[0], "value")) {
240 istringstream str(attrs[1]);
241 str >> value;
244 self.parser_preset.param_names.push_back(name);
245 self.parser_preset.values.push_back(value);
246 state = VALUE;
247 return;
249 if (!strcmp(name, "var")) {
250 self.current_key = "";
251 for(; *attrs; attrs += 2) {
252 if (!strcmp(attrs[0], "name")) self.current_key = attrs[1];
254 if (self.current_key.empty())
255 throw preset_exception("No name specified for preset variable", "", 0);
256 self.parser_preset.variables[self.current_key].clear();
257 state = VAR;
258 return;
260 break;
261 case VALUE:
262 // no nested elements allowed inside <param>
263 break;
264 case VAR:
265 // no nested elements allowed inside <var>
266 break;
267 case AUTOMATION_ENTRY:
268 // no nested elements allowed inside <automation>
269 break;
271 // g_warning("Invalid XML element: %s", name);
272 throw preset_exception("Invalid XML element: %s", name, 0);
275 void preset_list::xml_end_element_handler(void *user_data, const char *name)
277 preset_list &self = *(preset_list *)user_data;
278 bool rack_mode = self.rack_mode;
279 preset_vector &presets = self.presets;
280 parser_state &state = self.state;
281 switch(state)
283 case START:
284 break;
285 case AUTOMATION_ENTRY:
286 if (!strcmp(name, "automation"))
288 state = PLUGIN;
289 return;
291 break;
292 case LIST:
293 if (!strcmp(name, "presets")) {
294 state = START;
295 return;
297 break;
298 case PLUGIN:
299 if (!strcmp(name, "plugin")) {
300 self.plugins.push_back(self.parser_plugin);
301 state = RACK;
302 return;
304 break;
305 case RACK:
306 if (!strcmp(name, "rack")) {
307 state = START;
308 return;
310 break;
311 case PRESET:
312 if (!strcmp(name, "preset")) {
313 presets.push_back(self.parser_preset);
314 state = rack_mode ? PLUGIN : LIST;
315 return;
317 break;
318 case VALUE:
319 if (!strcmp(name, "param")) {
320 state = PRESET;
321 return;
323 break;
324 case VAR:
325 if (!strcmp(name, "var")) {
326 state = PRESET;
327 return;
329 break;
331 throw preset_exception("Invalid XML element close: %s", name, 0);
334 void preset_list::xml_character_data_handler(void *user_data, const XML_Char *data, int len)
336 preset_list &self = *(preset_list *)user_data;
337 parser_state &state = self.state;
338 if (state == VAR)
340 self.parser_preset.variables[self.current_key] += string(data, len);
341 return;
345 bool preset_list::load_defaults(bool builtin)
347 try {
348 struct stat st;
349 string name = preset_list::get_preset_filename(builtin);
350 if (!stat(name.c_str(), &st)) {
351 load(name.c_str(), false);
352 if (!presets.empty())
353 return true;
356 catch(preset_exception &ex)
358 return false;
360 return false;
363 void preset_list::parse(const std::string &data, bool in_rack_mode)
365 rack_mode = in_rack_mode;
366 state = START;
367 XML_Parser parser = XML_ParserCreate("UTF-8");
368 XML_SetUserData(parser, this);
369 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
370 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
371 XML_Status status = XML_Parse(parser, data.c_str(), data.length(), 1);
372 if (status == XML_STATUS_ERROR) {
373 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
374 XML_ParserFree(parser);
375 throw preset_exception(err, "string", errno);
377 XML_ParserFree(parser);
380 void preset_list::load(const char *filename, bool in_rack_mode)
382 rack_mode = in_rack_mode;
383 state = START;
384 XML_Parser parser = XML_ParserCreate("UTF-8");
385 XML_SetUserData(parser, this);
386 int fd = open(filename, O_RDONLY);
387 if (fd < 0)
388 throw preset_exception("Could not load the presets from ", filename, errno);
389 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
390 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
391 char buf[4096];
394 int len = read(fd, buf, 4096);
395 // XXXKF not an optimal error/EOF handling :)
396 if (len <= 0)
397 break;
398 XML_Status status = XML_Parse(parser, buf, len, 0);
399 if (status == XML_STATUS_ERROR)
400 throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
401 } while(1);
402 XML_Status status = XML_Parse(parser, buf, 0, 1);
403 close(fd);
404 if (status == XML_STATUS_ERROR)
406 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
407 XML_ParserFree(parser);
408 throw preset_exception(err, filename, errno);
410 XML_ParserFree(parser);
413 void preset_list::save(const char *filename)
415 string xml = "<presets>\n";
416 for (unsigned int i = 0; i < presets.size(); i++)
418 xml += presets[i].to_xml();
420 xml += "</presets>";
421 int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
422 if (fd < 0 || ((unsigned)write(fd, xml.c_str(), xml.length()) != xml.length()))
423 throw preset_exception("Could not save the presets in ", filename, errno);
424 close(fd);
427 void preset_list::get_for_plugin(preset_vector &vec, const char *plugin)
429 for (unsigned int i = 0; i < presets.size(); i++)
431 if (presets[i].plugin == plugin)
432 vec.push_back(presets[i]);
436 void preset_list::add(const plugin_preset &sp)
438 for (unsigned int i = 0; i < presets.size(); i++)
440 if (presets[i].plugin == sp.plugin && presets[i].name == sp.name)
442 presets[i] = sp;
443 return;
446 presets.push_back(sp);