ambdec.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. #include "config.h"
  2. #include "ambdec.h"
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #include "compat.h"
  7. static char *lstrip(char *line)
  8. {
  9. while(isspace(line[0]))
  10. line++;
  11. return line;
  12. }
  13. static char *rstrip(char *line)
  14. {
  15. size_t len = strlen(line);
  16. while(len > 0 && isspace(line[len-1]))
  17. len--;
  18. line[len] = 0;
  19. return line;
  20. }
  21. static int readline(FILE *f, char **output, size_t *maxlen)
  22. {
  23. size_t len = 0;
  24. int c;
  25. while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
  26. ;
  27. if(c == EOF)
  28. return 0;
  29. do {
  30. if(len+1 >= *maxlen)
  31. {
  32. void *temp = NULL;
  33. size_t newmax;
  34. newmax = (*maxlen ? (*maxlen)<<1 : 32);
  35. if(newmax > *maxlen)
  36. temp = realloc(*output, newmax);
  37. if(!temp)
  38. {
  39. ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
  40. return 0;
  41. }
  42. *output = temp;
  43. *maxlen = newmax;
  44. }
  45. (*output)[len++] = c;
  46. (*output)[len] = '\0';
  47. } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
  48. return 1;
  49. }
  50. /* Custom strtok_r, since we can't rely on it existing. */
  51. static char *my_strtok_r(char *str, const char *delim, char **saveptr)
  52. {
  53. /* Sanity check and update internal pointer. */
  54. if(!saveptr || !delim) return NULL;
  55. if(str) *saveptr = str;
  56. str = *saveptr;
  57. /* Nothing more to do with this string. */
  58. if(!str) return NULL;
  59. /* Find the first non-delimiter character. */
  60. while(*str != '\0' && strchr(delim, *str) != NULL)
  61. str++;
  62. if(*str == '\0')
  63. {
  64. /* End of string. */
  65. *saveptr = NULL;
  66. return NULL;
  67. }
  68. /* Find the next delimiter character. */
  69. *saveptr = strpbrk(str, delim);
  70. if(*saveptr) *((*saveptr)++) = '\0';
  71. return str;
  72. }
  73. static char *read_int(ALint *num, const char *line, int base)
  74. {
  75. char *end;
  76. *num = strtol(line, &end, base);
  77. if(end && *end != '\0')
  78. end = lstrip(end);
  79. return end;
  80. }
  81. static char *read_uint(ALuint *num, const char *line, int base)
  82. {
  83. char *end;
  84. *num = strtoul(line, &end, base);
  85. if(end && *end != '\0')
  86. end = lstrip(end);
  87. return end;
  88. }
  89. static char *read_float(ALfloat *num, const char *line)
  90. {
  91. char *end;
  92. #ifdef HAVE_STRTOF
  93. *num = strtof(line, &end);
  94. #else
  95. *num = (ALfloat)strtod(line, &end);
  96. #endif
  97. if(end && *end != '\0')
  98. end = lstrip(end);
  99. return end;
  100. }
  101. char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
  102. {
  103. while(readline(f, buffer, maxlen))
  104. {
  105. char *line, *comment;
  106. line = lstrip(*buffer);
  107. comment = strchr(line, '#');
  108. if(comment) *(comment++) = 0;
  109. line = rstrip(line);
  110. if(line[0]) return line;
  111. }
  112. return NULL;
  113. }
  114. static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
  115. {
  116. ALsizei cur = 0;
  117. while(cur < conf->NumSpeakers)
  118. {
  119. const char *cmd = my_strtok_r(NULL, " \t", saveptr);
  120. if(!cmd)
  121. {
  122. char *line = read_clipped_line(f, buffer, maxlen);
  123. if(!line)
  124. {
  125. ERR("Unexpected end of file\n");
  126. return 0;
  127. }
  128. cmd = my_strtok_r(line, " \t", saveptr);
  129. }
  130. if(strcmp(cmd, "add_spkr") == 0)
  131. {
  132. const char *name = my_strtok_r(NULL, " \t", saveptr);
  133. const char *dist = my_strtok_r(NULL, " \t", saveptr);
  134. const char *az = my_strtok_r(NULL, " \t", saveptr);
  135. const char *elev = my_strtok_r(NULL, " \t", saveptr);
  136. const char *conn = my_strtok_r(NULL, " \t", saveptr);
  137. if(!name) WARN("Name not specified for speaker %u\n", cur+1);
  138. else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
  139. if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
  140. else read_float(&conf->Speakers[cur].Distance, dist);
  141. if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
  142. else read_float(&conf->Speakers[cur].Azimuth, az);
  143. if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
  144. else read_float(&conf->Speakers[cur].Elevation, elev);
  145. if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
  146. else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
  147. cur++;
  148. }
  149. else
  150. {
  151. ERR("Unexpected speakers command: %s\n", cmd);
  152. return 0;
  153. }
  154. cmd = my_strtok_r(NULL, " \t", saveptr);
  155. if(cmd)
  156. {
  157. ERR("Unexpected junk on line: %s\n", cmd);
  158. return 0;
  159. }
  160. }
  161. return 1;
  162. }
  163. static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
  164. {
  165. int gotgains = 0;
  166. ALsizei cur = 0;
  167. while(cur < maxrow)
  168. {
  169. const char *cmd = my_strtok_r(NULL, " \t", saveptr);
  170. if(!cmd)
  171. {
  172. char *line = read_clipped_line(f, buffer, maxlen);
  173. if(!line)
  174. {
  175. ERR("Unexpected end of file\n");
  176. return 0;
  177. }
  178. cmd = my_strtok_r(line, " \t", saveptr);
  179. }
  180. if(strcmp(cmd, "order_gain") == 0)
  181. {
  182. ALuint curgain = 0;
  183. char *line;
  184. while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
  185. {
  186. ALfloat value;
  187. line = read_float(&value, line);
  188. if(line && *line != '\0')
  189. {
  190. ERR("Extra junk on gain %u: %s\n", curgain+1, line);
  191. return 0;
  192. }
  193. if(curgain < MAX_AMBI_ORDER+1)
  194. gains[curgain] = value;
  195. curgain++;
  196. }
  197. while(curgain < MAX_AMBI_ORDER+1)
  198. gains[curgain++] = 0.0f;
  199. gotgains = 1;
  200. }
  201. else if(strcmp(cmd, "add_row") == 0)
  202. {
  203. ALuint curidx = 0;
  204. char *line;
  205. while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
  206. {
  207. ALfloat value;
  208. line = read_float(&value, line);
  209. if(line && *line != '\0')
  210. {
  211. ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
  212. return 0;
  213. }
  214. if(curidx < MAX_AMBI_COEFFS)
  215. matrix[cur][curidx] = value;
  216. curidx++;
  217. }
  218. while(curidx < MAX_AMBI_COEFFS)
  219. matrix[cur][curidx++] = 0.0f;
  220. cur++;
  221. }
  222. else
  223. {
  224. ERR("Unexpected speakers command: %s\n", cmd);
  225. return 0;
  226. }
  227. cmd = my_strtok_r(NULL, " \t", saveptr);
  228. if(cmd)
  229. {
  230. ERR("Unexpected junk on line: %s\n", cmd);
  231. return 0;
  232. }
  233. }
  234. if(!gotgains)
  235. {
  236. ERR("Matrix order_gain not specified\n");
  237. return 0;
  238. }
  239. return 1;
  240. }
  241. void ambdec_init(AmbDecConf *conf)
  242. {
  243. ALsizei i;
  244. memset(conf, 0, sizeof(*conf));
  245. AL_STRING_INIT(conf->Description);
  246. for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
  247. {
  248. AL_STRING_INIT(conf->Speakers[i].Name);
  249. AL_STRING_INIT(conf->Speakers[i].Connection);
  250. }
  251. }
  252. void ambdec_deinit(AmbDecConf *conf)
  253. {
  254. ALsizei i;
  255. alstr_reset(&conf->Description);
  256. for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
  257. {
  258. alstr_reset(&conf->Speakers[i].Name);
  259. alstr_reset(&conf->Speakers[i].Connection);
  260. }
  261. memset(conf, 0, sizeof(*conf));
  262. }
  263. int ambdec_load(AmbDecConf *conf, const char *fname)
  264. {
  265. char *buffer = NULL;
  266. size_t maxlen = 0;
  267. char *line;
  268. FILE *f;
  269. f = al_fopen(fname, "r");
  270. if(!f)
  271. {
  272. ERR("Failed to open: %s\n", fname);
  273. return 0;
  274. }
  275. while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
  276. {
  277. char *saveptr;
  278. char *command;
  279. command = my_strtok_r(line, "/ \t", &saveptr);
  280. if(!command)
  281. {
  282. ERR("Malformed line: %s\n", line);
  283. goto fail;
  284. }
  285. if(strcmp(command, "description") == 0)
  286. {
  287. char *value = my_strtok_r(NULL, "", &saveptr);
  288. alstr_copy_cstr(&conf->Description, lstrip(value));
  289. }
  290. else if(strcmp(command, "version") == 0)
  291. {
  292. line = my_strtok_r(NULL, "", &saveptr);
  293. line = read_uint(&conf->Version, line, 10);
  294. if(line && *line != '\0')
  295. {
  296. ERR("Extra junk after version: %s\n", line);
  297. goto fail;
  298. }
  299. if(conf->Version != 3)
  300. {
  301. ERR("Unsupported version: %u\n", conf->Version);
  302. goto fail;
  303. }
  304. }
  305. else if(strcmp(command, "dec") == 0)
  306. {
  307. const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
  308. if(strcmp(dec, "chan_mask") == 0)
  309. {
  310. line = my_strtok_r(NULL, "", &saveptr);
  311. line = read_uint(&conf->ChanMask, line, 16);
  312. if(line && *line != '\0')
  313. {
  314. ERR("Extra junk after mask: %s\n", line);
  315. goto fail;
  316. }
  317. }
  318. else if(strcmp(dec, "freq_bands") == 0)
  319. {
  320. line = my_strtok_r(NULL, "", &saveptr);
  321. line = read_uint(&conf->FreqBands, line, 10);
  322. if(line && *line != '\0')
  323. {
  324. ERR("Extra junk after freq_bands: %s\n", line);
  325. goto fail;
  326. }
  327. if(conf->FreqBands != 1 && conf->FreqBands != 2)
  328. {
  329. ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
  330. goto fail;
  331. }
  332. }
  333. else if(strcmp(dec, "speakers") == 0)
  334. {
  335. line = my_strtok_r(NULL, "", &saveptr);
  336. line = read_int(&conf->NumSpeakers, line, 10);
  337. if(line && *line != '\0')
  338. {
  339. ERR("Extra junk after speakers: %s\n", line);
  340. goto fail;
  341. }
  342. if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
  343. {
  344. ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
  345. goto fail;
  346. }
  347. }
  348. else if(strcmp(dec, "coeff_scale") == 0)
  349. {
  350. line = my_strtok_r(NULL, " \t", &saveptr);
  351. if(strcmp(line, "n3d") == 0)
  352. conf->CoeffScale = ADS_N3D;
  353. else if(strcmp(line, "sn3d") == 0)
  354. conf->CoeffScale = ADS_SN3D;
  355. else if(strcmp(line, "fuma") == 0)
  356. conf->CoeffScale = ADS_FuMa;
  357. else
  358. {
  359. ERR("Unsupported coeff scale: %s\n", line);
  360. goto fail;
  361. }
  362. }
  363. else
  364. {
  365. ERR("Unexpected /dec option: %s\n", dec);
  366. goto fail;
  367. }
  368. }
  369. else if(strcmp(command, "opt") == 0)
  370. {
  371. const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
  372. if(strcmp(opt, "xover_freq") == 0)
  373. {
  374. line = my_strtok_r(NULL, "", &saveptr);
  375. line = read_float(&conf->XOverFreq, line);
  376. if(line && *line != '\0')
  377. {
  378. ERR("Extra junk after xover_freq: %s\n", line);
  379. goto fail;
  380. }
  381. }
  382. else if(strcmp(opt, "xover_ratio") == 0)
  383. {
  384. line = my_strtok_r(NULL, "", &saveptr);
  385. line = read_float(&conf->XOverRatio, line);
  386. if(line && *line != '\0')
  387. {
  388. ERR("Extra junk after xover_ratio: %s\n", line);
  389. goto fail;
  390. }
  391. }
  392. else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
  393. strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
  394. {
  395. /* Unused */
  396. my_strtok_r(NULL, " \t", &saveptr);
  397. }
  398. else
  399. {
  400. ERR("Unexpected /opt option: %s\n", opt);
  401. goto fail;
  402. }
  403. }
  404. else if(strcmp(command, "speakers") == 0)
  405. {
  406. const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
  407. if(strcmp(value, "{") != 0)
  408. {
  409. ERR("Expected { after %s command, got %s\n", command, value);
  410. goto fail;
  411. }
  412. if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
  413. goto fail;
  414. value = my_strtok_r(NULL, "/ \t", &saveptr);
  415. if(!value)
  416. {
  417. line = read_clipped_line(f, &buffer, &maxlen);
  418. if(!line)
  419. {
  420. ERR("Unexpected end of file\n");
  421. goto fail;
  422. }
  423. value = my_strtok_r(line, "/ \t", &saveptr);
  424. }
  425. if(strcmp(value, "}") != 0)
  426. {
  427. ERR("Expected } after speaker definitions, got %s\n", value);
  428. goto fail;
  429. }
  430. }
  431. else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
  432. strcmp(command, "matrix") == 0)
  433. {
  434. const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
  435. if(strcmp(value, "{") != 0)
  436. {
  437. ERR("Expected { after %s command, got %s\n", command, value);
  438. goto fail;
  439. }
  440. if(conf->FreqBands == 1)
  441. {
  442. if(strcmp(command, "matrix") != 0)
  443. {
  444. ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
  445. goto fail;
  446. }
  447. if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
  448. f, &buffer, &maxlen, &saveptr))
  449. goto fail;
  450. }
  451. else
  452. {
  453. if(strcmp(command, "lfmatrix") == 0)
  454. {
  455. if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
  456. f, &buffer, &maxlen, &saveptr))
  457. goto fail;
  458. }
  459. else if(strcmp(command, "hfmatrix") == 0)
  460. {
  461. if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
  462. f, &buffer, &maxlen, &saveptr))
  463. goto fail;
  464. }
  465. else
  466. {
  467. ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
  468. goto fail;
  469. }
  470. }
  471. value = my_strtok_r(NULL, "/ \t", &saveptr);
  472. if(!value)
  473. {
  474. line = read_clipped_line(f, &buffer, &maxlen);
  475. if(!line)
  476. {
  477. ERR("Unexpected end of file\n");
  478. goto fail;
  479. }
  480. value = my_strtok_r(line, "/ \t", &saveptr);
  481. }
  482. if(strcmp(value, "}") != 0)
  483. {
  484. ERR("Expected } after matrix definitions, got %s\n", value);
  485. goto fail;
  486. }
  487. }
  488. else if(strcmp(command, "end") == 0)
  489. {
  490. line = my_strtok_r(NULL, "/ \t", &saveptr);
  491. if(line)
  492. {
  493. ERR("Unexpected junk on end: %s\n", line);
  494. goto fail;
  495. }
  496. fclose(f);
  497. free(buffer);
  498. return 1;
  499. }
  500. else
  501. {
  502. ERR("Unexpected command: %s\n", command);
  503. goto fail;
  504. }
  505. line = my_strtok_r(NULL, "/ \t", &saveptr);
  506. if(line)
  507. {
  508. ERR("Unexpected junk on line: %s\n", line);
  509. goto fail;
  510. }
  511. }
  512. ERR("Unexpected end of file\n");
  513. fail:
  514. fclose(f);
  515. free(buffer);
  516. return 0;
  517. }