The base-application for Laborejo, Fluajho, Patroneo, Vico etc.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

533 lines
16KB

  1. /*
  2. Calf Box, an open source musical instrument.
  3. Copyright (C) 2010-2011 Krzysztof Foltman
  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 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "sfzparser.h"
  16. #include "tarfile.h"
  17. #include <ctype.h>
  18. #include <errno.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. int debug_variable_definitions = 0;
  23. int debug_variable_substitutions = 0;
  24. int debug_includes = 0;
  25. struct sfz_parser_state
  26. {
  27. struct sfz_parser_client *client;
  28. gboolean (*handler)(struct sfz_parser_state *state, int ch);
  29. const char *filename;
  30. const char *buf;
  31. int pos, len, line;
  32. int token_start;
  33. int key_start, key_end;
  34. int value_start, value_end;
  35. GHashTable *variables;
  36. struct cbox_tarfile *tarfile;
  37. GError **error;
  38. };
  39. static gboolean load_sfz_into_state(struct sfz_parser_state *s, const char *name);
  40. static gboolean handle_char(struct sfz_parser_state *state, int ch);
  41. static void unexpected_char(struct sfz_parser_state *state, int ch)
  42. {
  43. g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unexpected character '%c' (%d)", ch, ch);
  44. }
  45. static gboolean handle_header(struct sfz_parser_state *state, int ch)
  46. {
  47. if (ch >= 'a' && ch <= 'z')
  48. return TRUE;
  49. if (ch == '>')
  50. {
  51. char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start);
  52. gboolean result = state->client->token(state->client, token, state->error);
  53. g_free(token);
  54. state->handler = handle_char;
  55. return result;
  56. }
  57. unexpected_char(state, ch);
  58. return FALSE;
  59. }
  60. static void scan_for_value(struct sfz_parser_state *state)
  61. {
  62. state->value_start = state->pos;
  63. while(state->pos < state->len)
  64. {
  65. if (state->pos < state->len + 2 && state->buf[state->pos] == '/' && state->buf[state->pos + 1] == '/')
  66. {
  67. state->value_end = state->pos;
  68. while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
  69. state->value_end--;
  70. state->pos += 2;
  71. while (state->pos < state->len && state->buf[state->pos] != '\r' && state->buf[state->pos] != '\n')
  72. state->pos++;
  73. return;
  74. }
  75. int ch = state->buf[state->pos];
  76. if (ch == 0 || ch == '\r' || ch == '\n' || ch == '<')
  77. {
  78. state->value_end = state->pos;
  79. // remove spaces before next key
  80. while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
  81. state->value_end--;
  82. return;
  83. }
  84. if (ch == '=')
  85. {
  86. state->value_end = state->pos;
  87. // remove next key
  88. while(state->value_end > state->value_start && !isspace(state->buf[state->value_end - 1]))
  89. state->value_end--;
  90. // remove spaces before next key
  91. while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
  92. state->value_end--;
  93. state->pos = state->value_end;
  94. return;
  95. }
  96. state->pos++;
  97. }
  98. state->value_end = state->pos;
  99. while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
  100. state->value_end--;
  101. }
  102. static gchar *expand_variables(struct sfz_parser_state *state, gchar *text)
  103. {
  104. gchar *pos = strchr(text, '$');
  105. // No variables, no changes.
  106. if (!pos)
  107. return text;
  108. GString *result = g_string_new_len(text, pos - text);
  109. pos++;
  110. // Start of the variable name
  111. gchar *start = pos;
  112. if (!*start)
  113. return text;
  114. pos = start + 1;
  115. while(1)
  116. {
  117. gchar ch = *pos;
  118. gpointer value;
  119. if (ch)
  120. {
  121. *pos = '\0';
  122. value = g_hash_table_lookup(state->variables, start);
  123. *pos = ch;
  124. }
  125. else
  126. value = g_hash_table_lookup(state->variables, start);
  127. if (value)
  128. {
  129. g_string_append(result, value);
  130. // pos = first char that is not part of the variable name
  131. if (!ch)
  132. break;
  133. start = strchr(pos, '$');
  134. if (!start)
  135. {
  136. // Remainder has no variable references, add and stop
  137. g_string_append(result, pos);
  138. break;
  139. }
  140. // Add everything up to the next variable reference
  141. if (start != pos)
  142. g_string_append_len(result, pos, start - pos);
  143. // Restart, variable name starts at the next character
  144. start++;
  145. if (!*start)
  146. break;
  147. pos = start + 1;
  148. }
  149. else
  150. {
  151. if (!ch)
  152. {
  153. // Might throw an error here, but just quote the var name verbatim instead for now
  154. g_string_append(result, "$");
  155. g_string_append(result, start);
  156. break;
  157. }
  158. pos++;
  159. }
  160. }
  161. // Replace with a substituted version
  162. gchar *substituted = g_string_free(result, FALSE);
  163. if (debug_variable_substitutions)
  164. printf("Substitute: '%s' -> '%s'\n", text, substituted);
  165. g_free(text);
  166. return substituted;
  167. }
  168. static gboolean handle_key(struct sfz_parser_state *state, int ch)
  169. {
  170. if (isalpha(ch) || isdigit(ch) || ch == '_')
  171. return TRUE;
  172. if(ch == '=')
  173. {
  174. state->key_end = state->pos - 1;
  175. scan_for_value(state);
  176. gchar *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
  177. gchar *value = g_strndup(state->buf + state->value_start, state->value_end - state->value_start);
  178. key = expand_variables(state, key);
  179. value = expand_variables(state, value);
  180. gboolean result = state->client->key_value(state->client, key, value);
  181. g_free(key);
  182. g_free(value);
  183. state->handler = handle_char;
  184. return result;
  185. }
  186. unexpected_char(state, ch);
  187. return FALSE;
  188. }
  189. static gboolean do_include(struct sfz_parser_state *state, const char *name)
  190. {
  191. if (debug_includes)
  192. printf("Include file: %s\n", name);
  193. gchar *dir = g_path_get_dirname(state->filename);
  194. gchar *combined = g_build_filename(dir, name, NULL);
  195. gboolean result = load_sfz_into_state(state, combined);
  196. g_free(combined);
  197. g_free(dir);
  198. if (debug_includes)
  199. printf("End include file: %s\n", name);
  200. return result;
  201. }
  202. static void do_define(struct sfz_parser_state *state, char *key, char *value)
  203. {
  204. if (debug_variable_definitions)
  205. printf("Define: '%s' -> '%s'\n", key, value);
  206. g_hash_table_insert(state->variables, key, value);
  207. }
  208. static gboolean handle_include_filename(struct sfz_parser_state *state, int ch)
  209. {
  210. if (ch == '"')
  211. {
  212. char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start);
  213. gboolean result = do_include(state, token);
  214. g_free(token);
  215. state->handler = handle_char;
  216. return result;
  217. }
  218. if ((unsigned)(ch) >= ' ')
  219. return TRUE;
  220. unexpected_char(state, ch);
  221. return FALSE;
  222. }
  223. static gboolean handle_include_skipwhite(struct sfz_parser_state *state, int ch)
  224. {
  225. if (isspace(ch))
  226. return TRUE;
  227. if (ch == '"')
  228. {
  229. state->token_start = state->pos;
  230. state->handler = handle_include_filename;
  231. return TRUE;
  232. }
  233. unexpected_char(state, ch);
  234. return FALSE;
  235. }
  236. static gboolean handle_define_value(struct sfz_parser_state *state, int ch)
  237. {
  238. if (ch == '\n' || ch == '\r' || ch == -1)
  239. {
  240. char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
  241. char *value = g_strndup(state->buf + state->value_start, state->pos - state->value_start - 1);
  242. do_define(state, key, value);
  243. state->handler = handle_char;
  244. return TRUE;
  245. }
  246. return TRUE;
  247. }
  248. static gboolean handle_define_skipwhite2(struct sfz_parser_state *state, int ch)
  249. {
  250. if (ch == '\n' || ch == '\r' || ch == -1)
  251. {
  252. char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
  253. g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unspecified variable value for '%s'", key);
  254. g_free(key);
  255. return FALSE;
  256. }
  257. if (isspace(ch))
  258. return TRUE;
  259. state->value_start = state->pos - 1;
  260. state->handler = handle_define_value;
  261. return TRUE;
  262. }
  263. static gboolean handle_define_varname(struct sfz_parser_state *state, int ch)
  264. {
  265. if (ch == '\n' || ch == '\r' || ch == -1)
  266. {
  267. char *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
  268. g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unspecified variable name");
  269. g_free(key);
  270. return FALSE;
  271. }
  272. if (isspace(ch))
  273. {
  274. state->key_end = state->pos - 1;
  275. state->handler = handle_define_skipwhite2;
  276. return TRUE;
  277. }
  278. if (ch >= 33 && ch <= 127 && ch != '$')
  279. return TRUE;
  280. unexpected_char(state, ch);
  281. return FALSE;
  282. }
  283. static gboolean handle_define_skipwhite(struct sfz_parser_state *state, int ch)
  284. {
  285. if (isspace(ch))
  286. return TRUE;
  287. if (ch == '$')
  288. {
  289. state->key_start = state->pos;
  290. state->handler = handle_define_varname;
  291. return TRUE;
  292. }
  293. unexpected_char(state, ch);
  294. return FALSE;
  295. }
  296. static gboolean handle_preprocessor(struct sfz_parser_state *state, int ch)
  297. {
  298. if (isalpha(ch))
  299. return TRUE;
  300. if (isspace(ch))
  301. {
  302. if (!memcmp(state->buf + state->token_start, "include", state->pos - state->token_start - 1))
  303. {
  304. state->handler = handle_include_skipwhite;
  305. return TRUE;
  306. }
  307. if (!memcmp(state->buf + state->token_start, "define", state->pos - state->token_start - 1))
  308. {
  309. state->handler = handle_define_skipwhite;
  310. return TRUE;
  311. }
  312. char *preproc = g_strndup(state->buf + state->token_start, state->pos - state->token_start - 1);
  313. g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "%s:%d Unsupported parser directive '%s'", state->filename, state->line, preproc);
  314. g_free(preproc);
  315. state->handler = handle_char;
  316. return FALSE;
  317. }
  318. unexpected_char(state, ch);
  319. return FALSE;
  320. }
  321. static gboolean handle_char(struct sfz_parser_state *state, int ch)
  322. {
  323. if (isalpha(ch) || isdigit(ch))
  324. {
  325. state->key_start = state->pos - 1;
  326. state->handler = handle_key;
  327. return TRUE;
  328. }
  329. switch(ch)
  330. {
  331. case '_':
  332. return TRUE;
  333. case '\r':
  334. case '\n':
  335. case ' ':
  336. case '\t':
  337. case -1:
  338. return TRUE;
  339. case '<':
  340. state->token_start = state->pos;
  341. state->handler = handle_header;
  342. return TRUE;
  343. case '#':
  344. state->token_start = state->pos;
  345. state->handler = handle_preprocessor;
  346. return TRUE;
  347. default:
  348. unexpected_char(state, ch);
  349. return FALSE;
  350. }
  351. }
  352. gboolean load_sfz_from_string_into_state(struct sfz_parser_state *s, const char *buf, int len)
  353. {
  354. const char *oldbuf = s->buf;
  355. int oldpos = s->pos, oldlen = s->len, oldline = s->line;
  356. gboolean ok = FALSE;
  357. s->buf = buf;
  358. s->pos = 0;
  359. s->len = len;
  360. s->handler = handle_char;
  361. s->token_start = 0;
  362. if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
  363. {
  364. // UTF-8 BOM
  365. s->pos += 3;
  366. }
  367. while(s->pos < len && s->handler != NULL)
  368. {
  369. if (s->pos < len + 2 && buf[s->pos] == '/' && buf[s->pos + 1] == '/')
  370. {
  371. s->pos += 2;
  372. while (s->pos < len && buf[s->pos] != '\r' && buf[s->pos] != '\n')
  373. s->pos++;
  374. continue;
  375. }
  376. char ch = buf[s->pos++];
  377. gboolean newline = FALSE, eat_lf = FALSE;
  378. // Convert CR or CR/LF to LF
  379. if (ch == '\r') {
  380. newline = TRUE;
  381. eat_lf = (buf[s->pos] == '\n');
  382. ch = '\n';
  383. } else if (ch == '\n')
  384. newline = TRUE;
  385. if (!(*s->handler)(s, ch))
  386. goto restore;
  387. if (newline)
  388. s->line++;
  389. if (eat_lf)
  390. s->pos++;
  391. }
  392. if (s->handler)
  393. {
  394. if (!(*s->handler)(s, -1))
  395. goto restore;
  396. }
  397. ok = TRUE;
  398. restore:
  399. s->buf = oldbuf;
  400. s->pos = oldpos;
  401. s->line = oldline;
  402. s->len = oldlen;
  403. s->handler = handle_char;
  404. s->token_start = oldpos;
  405. return ok;
  406. }
  407. /*
  408. * This is not only called when literally constructing a sfz string
  409. * but also when loading a null instrument e.g. to first create the jack ports and only later
  410. * actually load the sfz and samples, which can be costly.
  411. */
  412. gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error)
  413. {
  414. struct sfz_parser_state s;
  415. memset(&s, 0, sizeof(s));
  416. s.line = 1;
  417. s.filename = "<inline>";
  418. s.tarfile = NULL;
  419. s.client = c;
  420. s.error = error;
  421. s.variables = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
  422. gboolean result = load_sfz_from_string_into_state(&s, buf, len);
  423. g_hash_table_destroy(s.variables);
  424. return result;
  425. }
  426. /*
  427. * Called once per sfz.
  428. * Does not load samples, but only the sfz file.
  429. */
  430. gboolean load_sfz_into_state(struct sfz_parser_state *s, const char *name)
  431. {
  432. g_clear_error(s->error);
  433. FILE *f;
  434. int len = -1;
  435. if (s->tarfile)
  436. { //This only extracts the .sfz file itself and will not attempt to load any sample waveforms, eventhough cbox_tarfile_get_item_by_name will later be used to extract the sample as well.
  437. struct cbox_taritem *item = cbox_tarfile_get_item_by_name(s->tarfile, name, TRUE);
  438. if (!item)
  439. {
  440. g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (2), "Cannot find '%s' in the tarfile", name);
  441. return FALSE;
  442. }
  443. int fd = cbox_tarfile_openitem(s->tarfile, item);
  444. if (fd < 0)
  445. {
  446. g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s' in the tarfile", name);
  447. return FALSE;
  448. }
  449. f = fdopen(fd, "rb");
  450. len = item->size;
  451. }
  452. else
  453. f = fopen(name, "rb");
  454. if (!f)
  455. {
  456. g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s'", name);
  457. return FALSE;
  458. }
  459. if (len == -1)
  460. {
  461. fseek(f, 0, SEEK_END);
  462. len = ftell(f);
  463. fseek(f, 0, SEEK_SET);
  464. }
  465. unsigned char *buf = malloc(len + 1);
  466. buf[len] = '\0';
  467. if (fread(buf, 1, len, f) != (size_t)len)
  468. {
  469. g_set_error(s->error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot read '%s'", name);
  470. fclose(f);
  471. return FALSE;
  472. }
  473. fclose(f);
  474. gboolean result = load_sfz_from_string_into_state(s, (char *)buf, len);
  475. free(buf);
  476. return result;
  477. }
  478. gboolean load_sfz(const char *name, struct cbox_tarfile *tarfile, struct sfz_parser_client *c, GError **error)
  479. {
  480. struct sfz_parser_state s;
  481. memset(&s, 0, sizeof(s));
  482. s.line = 1;
  483. s.filename = name;
  484. s.tarfile = tarfile;
  485. s.client = c;
  486. s.error = error;
  487. s.variables = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
  488. gboolean result = load_sfz_into_state(&s, name);
  489. g_hash_table_destroy(s.variables);
  490. return result;
  491. }
  492. GQuark cbox_sfz_parser_error_quark(void)
  493. {
  494. return g_quark_from_string("cbox-sfz-parser-error-quark");
  495. }