Audio plugin host https://kx.studio/carla
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.

649 lines
16KB

  1. /*************************************************************************************
  2. * Original code copyright (C) 2012 Steve Folta
  3. * Converted to Juce module (C) 2016 Leo Olivers
  4. * Forked from https://github.com/stevefolta/SFZero
  5. * For license info please see the LICENSE file distributed with this source code
  6. *************************************************************************************/
  7. #include "SFZReader.h"
  8. #include "SFZRegion.h"
  9. #include "SFZSound.h"
  10. #include "water/memory/MemoryBlock.h"
  11. namespace sfzero
  12. {
  13. Reader::Reader(Sound *soundIn) : sound_(soundIn), line_(1) {}
  14. Reader::~Reader() {}
  15. void Reader::read(const water::File &file)
  16. {
  17. water::MemoryBlock contents;
  18. bool ok = file.loadFileAsData(contents);
  19. if (!ok)
  20. {
  21. sound_->addError("Couldn't read \"" + file.getFullPathName() + "\"");
  22. return;
  23. }
  24. read(static_cast<const char *>(contents.getData()), static_cast<int>(contents.getSize()));
  25. }
  26. void Reader::read(const char *text, unsigned int length)
  27. {
  28. const char *p = text;
  29. const char *end = text + length;
  30. char c = 0;
  31. Region curGlobal;
  32. Region curGroup;
  33. Region curRegion;
  34. Region *buildingRegion = nullptr;
  35. bool inComment = false;
  36. bool inControl = false;
  37. bool inGroup = false;
  38. water::String defaultPath;
  39. while (p < end)
  40. {
  41. // We're at the start of a line; skip any whitespace.
  42. while (p < end)
  43. {
  44. c = *p;
  45. if ((c != ' ') && (c != '\t'))
  46. {
  47. break;
  48. }
  49. p += 1;
  50. }
  51. if (p >= end)
  52. {
  53. break;
  54. }
  55. // Check if it's a comment line.
  56. if (c == '/')
  57. {
  58. // Skip to end of line or c-style comment.
  59. do
  60. {
  61. c = *++p;
  62. if (inComment)
  63. {
  64. if (c != '*')
  65. continue;
  66. c = *++p;
  67. if (c != '/')
  68. continue;
  69. inComment = false;
  70. }
  71. if (c == '*')
  72. {
  73. inComment = true;
  74. continue;
  75. }
  76. if ((c == '\n') || (c == '\r'))
  77. {
  78. break;
  79. }
  80. }
  81. while (p < end);
  82. inComment = false;
  83. p = handleLineEnd(p);
  84. continue;
  85. }
  86. // Check if it's a blank line.
  87. if ((c == '\r') || (c == '\n'))
  88. {
  89. p = handleLineEnd(p);
  90. continue;
  91. }
  92. // Handle elements on the line.
  93. while (p < end)
  94. {
  95. c = *p;
  96. // Tag.
  97. if (c == '<')
  98. {
  99. p += 1;
  100. const char *tagStart = p;
  101. while (p < end)
  102. {
  103. c = *p++;
  104. if ((c == '\n') || (c == '\r'))
  105. {
  106. error("Unterminated tag");
  107. goto fatalError;
  108. }
  109. else if (c == '>')
  110. {
  111. break;
  112. }
  113. }
  114. if (p >= end)
  115. {
  116. error("Unterminated tag");
  117. goto fatalError;
  118. }
  119. StringSlice tag(tagStart, p - 1);
  120. if (tag == "global")
  121. {
  122. curGlobal.clear();
  123. buildingRegion = &curGlobal;
  124. inControl = false;
  125. inGroup = false;
  126. }
  127. else if (tag == "region")
  128. {
  129. if (buildingRegion && (buildingRegion == &curRegion))
  130. {
  131. finishRegion(&curRegion);
  132. }
  133. curRegion = curGroup;
  134. buildingRegion = &curRegion;
  135. inControl = false;
  136. inGroup = false;
  137. }
  138. else if (tag == "group")
  139. {
  140. if (buildingRegion && (buildingRegion == &curRegion))
  141. {
  142. finishRegion(&curRegion);
  143. }
  144. if (! inGroup)
  145. {
  146. curGroup = curGlobal;
  147. buildingRegion = &curGroup;
  148. inControl = false;
  149. inGroup = true;
  150. }
  151. }
  152. else if (tag == "control")
  153. {
  154. if (buildingRegion && (buildingRegion == &curRegion))
  155. {
  156. finishRegion(&curRegion);
  157. }
  158. curGroup.clear();
  159. buildingRegion = nullptr;
  160. inControl = true;
  161. }
  162. else
  163. {
  164. error("Illegal tag");
  165. }
  166. }
  167. // Comment.
  168. else if (c == '/')
  169. {
  170. // Skip to end of line or c-style comment.
  171. do
  172. {
  173. c = *++p;
  174. if (inComment)
  175. {
  176. if (c != '*')
  177. continue;
  178. c = *++p;
  179. if (c != '/')
  180. continue;
  181. inComment = false;
  182. }
  183. if (c == '*')
  184. {
  185. inComment = true;
  186. continue;
  187. }
  188. if ((c == '\r') || (c == '\n'))
  189. {
  190. break;
  191. }
  192. }
  193. while (p < end);
  194. inComment = false;
  195. }
  196. // Parameter.
  197. else
  198. {
  199. // Get the parameter name.
  200. const char *parameterStart = p;
  201. while (p < end)
  202. {
  203. c = *p++;
  204. if ((c == '=') || (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'))
  205. {
  206. break;
  207. }
  208. }
  209. if ((p >= end) || (c != '='))
  210. {
  211. error("Malformed parameter");
  212. goto nextElement;
  213. }
  214. StringSlice opcode(parameterStart, p - 1);
  215. if (inControl)
  216. {
  217. if (opcode == "default_path")
  218. {
  219. p = readPathInto(&defaultPath, p, end);
  220. }
  221. else
  222. {
  223. const char *valueStart = p;
  224. while (p < end)
  225. {
  226. c = *p;
  227. if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))
  228. {
  229. break;
  230. }
  231. p++;
  232. }
  233. water::String value(valueStart, (water::uint64)(p - valueStart));
  234. water::String fauxOpcode = water::String(opcode.getStart(), opcode.length()) + " (in <control>)";
  235. sound_->addUnsupportedOpcode(fauxOpcode);
  236. }
  237. }
  238. else if (opcode == "sample")
  239. {
  240. water::String path;
  241. p = readPathInto(&path, p, end);
  242. if (!path.isEmpty())
  243. {
  244. if (buildingRegion)
  245. {
  246. buildingRegion->sample = sound_->addSample(path, defaultPath);
  247. }
  248. else
  249. {
  250. error("Adding sample outside a group or region");
  251. }
  252. }
  253. else
  254. {
  255. error("Empty sample path");
  256. }
  257. }
  258. else
  259. {
  260. const char *valueStart = p;
  261. while (p < end)
  262. {
  263. c = *p;
  264. if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))
  265. {
  266. break;
  267. }
  268. p++;
  269. }
  270. water::String value(valueStart, (water::uint64)(p - valueStart));
  271. if (buildingRegion == nullptr)
  272. {
  273. error("Setting a parameter outside a region or group");
  274. }
  275. else if (opcode == "lokey")
  276. {
  277. buildingRegion->lokey = keyValue(value);
  278. }
  279. else if (opcode == "hikey")
  280. {
  281. buildingRegion->hikey = keyValue(value);
  282. }
  283. else if (opcode == "key")
  284. {
  285. buildingRegion->hikey = buildingRegion->lokey = buildingRegion->pitch_keycenter = keyValue(value);
  286. }
  287. else if (opcode == "lovel")
  288. {
  289. buildingRegion->lovel = value.getIntValue();
  290. }
  291. else if (opcode == "hivel")
  292. {
  293. buildingRegion->hivel = value.getIntValue();
  294. }
  295. else if (opcode == "trigger")
  296. {
  297. buildingRegion->trigger = static_cast<Region::Trigger>(triggerValue(value));
  298. }
  299. else if (opcode == "group")
  300. {
  301. buildingRegion->group = static_cast<int>(value.getLargeIntValue());
  302. }
  303. else if (opcode == "off_by" || opcode == "offby")
  304. {
  305. buildingRegion->off_by = value.getLargeIntValue();
  306. }
  307. else if (opcode == "offset")
  308. {
  309. buildingRegion->offset = value.getLargeIntValue();
  310. }
  311. else if (opcode == "end")
  312. {
  313. water::int64 end2 = value.getLargeIntValue();
  314. if (end2 < 0)
  315. {
  316. buildingRegion->negative_end = true;
  317. }
  318. else
  319. {
  320. buildingRegion->end = end2;
  321. }
  322. }
  323. else if (opcode == "loop_mode" || opcode == "loopmode")
  324. {
  325. bool modeIsSupported = value == "no_loop" || value == "one_shot" || value == "loop_continuous";
  326. if (modeIsSupported)
  327. {
  328. buildingRegion->loop_mode = static_cast<Region::LoopMode>(loopModeValue(value));
  329. }
  330. else
  331. {
  332. water::String fauxOpcode = water::String(opcode.getStart(), opcode.length()) + "=" + value;
  333. sound_->addUnsupportedOpcode(fauxOpcode);
  334. }
  335. }
  336. else if (opcode == "loop_start" || opcode == "loopstart")
  337. {
  338. buildingRegion->loop_start = value.getLargeIntValue();
  339. }
  340. else if (opcode == "loop_end" || opcode == "loopend")
  341. {
  342. buildingRegion->loop_end = value.getLargeIntValue();
  343. }
  344. else if (opcode == "transpose")
  345. {
  346. buildingRegion->transpose = value.getIntValue();
  347. }
  348. else if (opcode == "tune")
  349. {
  350. buildingRegion->tune = value.getIntValue();
  351. }
  352. else if (opcode == "pitch_keycenter")
  353. {
  354. buildingRegion->pitch_keycenter = keyValue(value);
  355. }
  356. else if (opcode == "pitch_keytrack")
  357. {
  358. buildingRegion->pitch_keytrack = value.getIntValue();
  359. }
  360. else if (opcode == "bend_up" || opcode == "bendup")
  361. {
  362. buildingRegion->bend_up = value.getIntValue();
  363. }
  364. else if (opcode == "bend_down" || opcode == "benddown")
  365. {
  366. buildingRegion->bend_down = value.getIntValue();
  367. }
  368. else if (opcode == "volume")
  369. {
  370. buildingRegion->volume = value.getFloatValue();
  371. }
  372. else if (opcode == "pan")
  373. {
  374. buildingRegion->pan = value.getFloatValue();
  375. }
  376. else if (opcode == "amp_veltrack")
  377. {
  378. buildingRegion->amp_veltrack = value.getFloatValue();
  379. }
  380. else if (opcode == "ampeg_delay")
  381. {
  382. buildingRegion->ampeg.delay = value.getFloatValue();
  383. }
  384. else if (opcode == "ampeg_start")
  385. {
  386. buildingRegion->ampeg.start = value.getFloatValue();
  387. }
  388. else if (opcode == "ampeg_attack")
  389. {
  390. buildingRegion->ampeg.attack = value.getFloatValue();
  391. }
  392. else if (opcode == "ampeg_hold")
  393. {
  394. buildingRegion->ampeg.hold = value.getFloatValue();
  395. }
  396. else if (opcode == "ampeg_decay")
  397. {
  398. buildingRegion->ampeg.decay = value.getFloatValue();
  399. }
  400. else if (opcode == "ampeg_sustain")
  401. {
  402. buildingRegion->ampeg.sustain = value.getFloatValue();
  403. }
  404. else if (opcode == "ampeg_release")
  405. {
  406. buildingRegion->ampeg.release = value.getFloatValue();
  407. }
  408. else if (opcode == "ampeg_vel2delay")
  409. {
  410. buildingRegion->ampeg_veltrack.delay = value.getFloatValue();
  411. }
  412. else if (opcode == "ampeg_vel2attack")
  413. {
  414. buildingRegion->ampeg_veltrack.attack = value.getFloatValue();
  415. }
  416. else if (opcode == "ampeg_vel2hold")
  417. {
  418. buildingRegion->ampeg_veltrack.hold = value.getFloatValue();
  419. }
  420. else if (opcode == "ampeg_vel2decay")
  421. {
  422. buildingRegion->ampeg_veltrack.decay = value.getFloatValue();
  423. }
  424. else if (opcode == "ampeg_vel2sustain")
  425. {
  426. buildingRegion->ampeg_veltrack.sustain = value.getFloatValue();
  427. }
  428. else if (opcode == "ampeg_vel2release")
  429. {
  430. buildingRegion->ampeg_veltrack.release = value.getFloatValue();
  431. }
  432. else if (opcode == "default_path")
  433. {
  434. error("\"default_path\" outside of <control> tag");
  435. }
  436. else
  437. {
  438. sound_->addUnsupportedOpcode(water::String(opcode.getStart(), opcode.length()));
  439. }
  440. }
  441. }
  442. // Skip to next element.
  443. nextElement:
  444. c = 0;
  445. while (p < end)
  446. {
  447. c = *p;
  448. if ((c != ' ') && (c != '\t'))
  449. {
  450. break;
  451. }
  452. p += 1;
  453. }
  454. if ((c == '\r') || (c == '\n'))
  455. {
  456. p = handleLineEnd(p);
  457. break;
  458. }
  459. }
  460. }
  461. fatalError:
  462. if (buildingRegion && (buildingRegion == &curRegion))
  463. {
  464. finishRegion(buildingRegion);
  465. }
  466. }
  467. const char *Reader::handleLineEnd(const char *p)
  468. {
  469. // Check for DOS-style line ending.
  470. char lineEndChar = *p++;
  471. if ((lineEndChar == '\r') && (*p == '\n'))
  472. {
  473. p += 1;
  474. }
  475. line_ += 1;
  476. return p;
  477. }
  478. const char *Reader::readPathInto(water::String *pathOut, const char *pIn, const char *endIn)
  479. {
  480. // Paths are kind of funny to parse because they can contain whitespace.
  481. const char *p = pIn;
  482. const char *end = endIn;
  483. const char *pathStart = p;
  484. const char *potentialEnd = nullptr;
  485. while (p < end)
  486. {
  487. char c = *p;
  488. if (c == ' ')
  489. {
  490. // Is this space part of the path? Or the start of the next opcode? We
  491. // don't know yet.
  492. potentialEnd = p;
  493. p += 1;
  494. // Skip any more spaces.
  495. while (p < end && *p == ' ')
  496. {
  497. p += 1;
  498. }
  499. }
  500. else if ((c == '\n') || (c == '\r') || (c == '\t'))
  501. {
  502. break;
  503. }
  504. else if (c == '=')
  505. {
  506. // We've been looking at an opcode; we need to rewind to
  507. // potentialEnd.
  508. p = potentialEnd;
  509. break;
  510. }
  511. p += 1;
  512. }
  513. if (p > pathStart)
  514. {
  515. // Can't do this:
  516. // water::String path(CharPointer_UTF8(pathStart), CharPointer_UTF8(p));
  517. // It won't compile for some unfathomable reason.
  518. water::CharPointer_UTF8 end2(p);
  519. water::String path(water::CharPointer_UTF8(pathStart), end2);
  520. *pathOut = path;
  521. }
  522. else
  523. {
  524. *pathOut = water::String();
  525. }
  526. return p;
  527. }
  528. int Reader::keyValue(const water::String &str)
  529. {
  530. const char* const chars = str.toRawUTF8();
  531. char c = chars[0];
  532. if ((c >= '0') && (c <= '9'))
  533. {
  534. return str.getIntValue();
  535. }
  536. int note = 0;
  537. static const int notes[] = {
  538. 12 + 0, 12 + 2, 3, 5, 7, 8, 10,
  539. };
  540. if ((c >= 'A') && (c <= 'G'))
  541. {
  542. note = notes[c - 'A'];
  543. }
  544. else if ((c >= 'a') && (c <= 'g'))
  545. {
  546. note = notes[c - 'a'];
  547. }
  548. int octaveStart = 1;
  549. c = chars[1];
  550. if ((c == 'b') || (c == '#'))
  551. {
  552. octaveStart += 1;
  553. if (c == 'b')
  554. {
  555. note -= 1;
  556. }
  557. else
  558. {
  559. note += 1;
  560. }
  561. }
  562. int octave = str.substring(octaveStart).getIntValue();
  563. // A3 == 57.
  564. int result = octave * 12 + note + (57 - 4 * 12);
  565. return result;
  566. }
  567. int Reader::triggerValue(const water::String &str)
  568. {
  569. if (str == "release")
  570. {
  571. return Region::release;
  572. }
  573. if (str == "first")
  574. {
  575. return Region::first;
  576. }
  577. if (str == "legato")
  578. {
  579. return Region::legato;
  580. }
  581. return Region::attack;
  582. }
  583. int Reader::loopModeValue(const water::String &str)
  584. {
  585. if (str == "no_loop")
  586. {
  587. return Region::no_loop;
  588. }
  589. if (str == "one_shot")
  590. {
  591. return Region::one_shot;
  592. }
  593. if (str == "loop_continuous")
  594. {
  595. return Region::loop_continuous;
  596. }
  597. if (str == "loop_sustain")
  598. {
  599. return Region::loop_sustain;
  600. }
  601. return Region::sample_loop;
  602. }
  603. void Reader::finishRegion(Region *region)
  604. {
  605. Region *newRegion = new Region();
  606. *newRegion = *region;
  607. sound_->addRegion(newRegion);
  608. }
  609. void Reader::error(const water::String &message)
  610. {
  611. water::String fullMessage = message;
  612. fullMessage += " (line " + water::String(line_) + ").";
  613. sound_->addError(fullMessage);
  614. }
  615. }