+ Organ: add pitch bend inertia
[calf.git] / src / preset.cpp
blob02fb383e8ce210404ee1cb77d9307ae51cf7f336
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., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
22 #include <config.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/stat.h>
29 #include <expat.h>
30 #include <calf/preset.h>
31 #include <calf/giface.h>
32 #include <calf/utils.h>
34 using namespace std;
35 using namespace calf_plugins;
36 using namespace calf_utils;
38 extern calf_plugins::preset_list &calf_plugins::get_builtin_presets()
40 static calf_plugins::preset_list plist;
41 return plist;
44 extern calf_plugins::preset_list &calf_plugins::get_user_presets()
46 static calf_plugins::preset_list plist;
47 return plist;
50 std::string plugin_preset::to_xml()
52 std::stringstream ss;
53 ss << "<preset bank=\"" << bank << "\" program=\"" << program << "\" plugin=\"" << xml_escape(plugin) << "\" name=\"" << xml_escape(name) << "\">\n";
54 for (unsigned int i = 0; i < values.size(); i++) {
55 if (i < param_names.size())
56 ss << " <param name=\"" << xml_escape(param_names[i]) << "\" value=\"" << values[i] << "\" />\n";
57 else
58 ss << " <param value=\"" << values[i] << "\" />\n";
60 for (map<string, string>::iterator i = variables.begin(); i != variables.end(); i++)
62 ss << " <var name=\"" << xml_escape(i->first) << "\">" << xml_escape(i->second) << "</var>\n";
64 ss << "</preset>\n";
65 return ss.str();
68 void plugin_preset::activate(plugin_ctl_iface *plugin)
70 // First, clear everything to default values (in case some parameters or variables are missing)
71 plugin->clear_preset();
73 map<string, int> names;
74 int count = plugin->get_param_count();
75 // this is deliberately done in two separate loops - if you wonder why, just think for a while :)
76 for (int i = 0; i < count; i++)
77 names[plugin->get_param_props(i)->name] = i;
78 for (int i = 0; i < count; i++)
79 names[plugin->get_param_props(i)->short_name] = i;
80 // no support for unnamed parameters... tough luck :)
81 for (unsigned int i = 0; i < min(param_names.size(), values.size()); i++)
83 map<string, int>::iterator pos = names.find(param_names[i]);
84 if (pos == names.end()) {
85 // XXXKF should have a mechanism for notifying a GUI
86 printf("Warning: unknown parameter %s for plugin %s\n", param_names[i].c_str(), this->plugin.c_str());
87 continue;
89 plugin->set_param_value(pos->second, values[i]);
91 for (map<string, string>::iterator i = variables.begin(); i != variables.end(); i++)
93 printf("configure %s: %s\n", i->first.c_str(), i->second.c_str());
94 plugin->configure(i->first.c_str(), i->second.c_str());
98 void plugin_preset::get_from(plugin_ctl_iface *plugin)
100 int count = plugin->get_param_count();
101 for (int i = 0; i < count; i++) {
102 if ((plugin->get_param_props(i)->flags & PF_TYPEMASK) >= PF_STRING)
103 continue;
104 param_names.push_back(plugin->get_param_props(i)->short_name);
105 values.push_back(plugin->get_param_value(i));
107 struct store_obj: public send_configure_iface
109 map<string, string> *data;
110 void send_configure(const char *key, const char *value)
112 (*data)[key] = value;
114 } tmp;
115 variables.clear();
116 tmp.data = &variables;
117 plugin->send_configures(&tmp);
120 string calf_plugins::preset_list::get_preset_filename(bool builtin)
122 if (builtin)
124 return PKGLIBDIR "/presets.xml";
126 else
128 const char *home = getenv("HOME");
129 return string(home)+"/.calfpresets";
133 void preset_list::xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
135 preset_list &self = *(preset_list *)user_data;
136 parser_state &state = self.state;
137 plugin_preset &parser_preset = self.parser_preset;
138 switch(state)
140 case START:
141 if (!strcmp(name, "presets")) {
142 state = LIST;
143 return;
145 break;
146 case LIST:
147 if (!strcmp(name, "preset")) {
149 parser_preset.bank = parser_preset.program = 0;
150 parser_preset.name = "";
151 parser_preset.plugin = "";
152 parser_preset.param_names.clear();
153 parser_preset.values.clear();
154 parser_preset.variables.clear();
155 for(; *attrs; attrs += 2) {
156 if (!strcmp(attrs[0], "name")) self.parser_preset.name = attrs[1];
157 else
158 if (!strcmp(attrs[0], "plugin")) self.parser_preset.plugin = attrs[1];
160 // autonumbering of programs for DSSI
161 if (!self.last_preset_ids.count(self.parser_preset.plugin))
162 self.last_preset_ids[self.parser_preset.plugin] = 0;
163 self.parser_preset.program = ++self.last_preset_ids[self.parser_preset.plugin];
164 self.parser_preset.bank = (self.parser_preset.program >> 7);
165 self.parser_preset.program &= 127;
166 state = PRESET;
167 return;
169 break;
170 case PRESET:
171 if (!strcmp(name, "param")) {
172 std::string name;
173 float value = 0.f;
174 for(; *attrs; attrs += 2) {
175 if (!strcmp(attrs[0], "name")) name = attrs[1];
176 else
177 if (!strcmp(attrs[0], "value")) {
178 istringstream str(attrs[1]);
179 str >> value;
182 self.parser_preset.param_names.push_back(name);
183 self.parser_preset.values.push_back(value);
184 state = VALUE;
185 return;
187 if (!strcmp(name, "var")) {
188 self.current_key = "";
189 for(; *attrs; attrs += 2) {
190 if (!strcmp(attrs[0], "name")) self.current_key = attrs[1];
192 if (self.current_key.empty())
193 throw preset_exception("No name specified for preset variable", "", 0);
194 self.parser_preset.variables[self.current_key].clear();
195 state = VAR;
196 return;
198 break;
199 case VALUE:
200 // no nested elements allowed inside <param>
201 break;
202 case VAR:
203 // no nested elements allowed inside <var>
204 break;
206 // g_warning("Invalid XML element: %s", name);
207 throw preset_exception("Invalid XML element: %s", name, 0);
210 void preset_list::xml_end_element_handler(void *user_data, const char *name)
212 preset_list &self = *(preset_list *)user_data;
213 preset_vector &presets = self.presets;
214 parser_state &state = self.state;
215 switch(state)
217 case START:
218 break;
219 case LIST:
220 if (!strcmp(name, "presets")) {
221 state = START;
222 return;
224 break;
225 case PRESET:
226 if (!strcmp(name, "preset")) {
227 presets.push_back(self.parser_preset);
228 state = LIST;
229 return;
231 break;
232 case VALUE:
233 if (!strcmp(name, "param")) {
234 state = PRESET;
235 return;
237 break;
238 case VAR:
239 if (!strcmp(name, "var")) {
240 state = PRESET;
241 return;
243 break;
245 throw preset_exception("Invalid XML element close: %s", name, 0);
248 void preset_list::xml_character_data_handler(void *user_data, const XML_Char *data, int len)
250 preset_list &self = *(preset_list *)user_data;
251 parser_state &state = self.state;
252 if (state == VAR)
254 self.parser_preset.variables[self.current_key] += string(data, len);
255 return;
259 bool preset_list::load_defaults(bool builtin)
261 try {
262 struct stat st;
263 string name = preset_list::get_preset_filename(builtin);
264 if (!stat(name.c_str(), &st)) {
265 load(name.c_str());
266 if (!presets.empty())
267 return true;
270 catch(preset_exception &ex)
272 return false;
274 return false;
277 void preset_list::parse(const std::string &data)
279 state = START;
280 XML_Parser parser = XML_ParserCreate("UTF-8");
281 XML_SetUserData(parser, this);
282 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
283 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
284 XML_Status status = XML_Parse(parser, data.c_str(), data.length(), 1);
285 if (status == XML_STATUS_ERROR) {
286 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
287 XML_ParserFree(parser);
288 throw preset_exception(err, "string", errno);
290 XML_ParserFree(parser);
293 void preset_list::load(const char *filename)
295 state = START;
296 XML_Parser parser = XML_ParserCreate("UTF-8");
297 XML_SetUserData(parser, this);
298 int fd = open(filename, O_RDONLY);
299 if (fd < 0)
300 throw preset_exception("Could not load the presets from ", filename, errno);
301 XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
302 XML_SetCharacterDataHandler(parser, xml_character_data_handler);
303 char buf[4096];
306 int len = read(fd, buf, 4096);
307 // XXXKF not an optimal error/EOF handling :)
308 if (len <= 0)
309 break;
310 XML_Status status = XML_Parse(parser, buf, len, 0);
311 if (status == XML_STATUS_ERROR)
312 throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
313 } while(1);
314 XML_Status status = XML_Parse(parser, buf, 0, 1);
315 close(fd);
316 if (status == XML_STATUS_ERROR)
318 string err = string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ";
319 XML_ParserFree(parser);
320 throw preset_exception(err, filename, errno);
322 XML_ParserFree(parser);
325 void preset_list::save(const char *filename)
327 string xml = "<presets>\n";
328 for (unsigned int i = 0; i < presets.size(); i++)
330 xml += presets[i].to_xml();
332 xml += "</presets>";
333 int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
334 if (fd < 0 || ((unsigned)write(fd, xml.c_str(), xml.length()) != xml.length()))
335 throw preset_exception("Could not save the presets in ", filename, errno);
336 close(fd);
339 void preset_list::get_for_plugin(preset_vector &vec, const char *plugin)
341 for (unsigned int i = 0; i < presets.size(); i++)
343 if (presets[i].plugin == plugin)
344 vec.push_back(presets[i]);
348 void preset_list::add(const plugin_preset &sp)
350 for (unsigned int i = 0; i < presets.size(); i++)
352 if (presets[i].plugin == sp.plugin && presets[i].name == sp.name)
354 presets[i] = sp;
355 return;
358 presets.push_back(sp);