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.

617 lines
15KB

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