I guess -1 isn't allowed for the output
[openal-soft.git] / Alc / ambdec.c
blobda1143354c60cdd153fa6927f0316df4add0e0a0
2 #include "config.h"
4 #include "ambdec.h"
6 #include <stdio.h>
7 #include <string.h>
8 #include <ctype.h>
10 #include "compat.h"
13 static char *lstrip(char *line)
15 while(isspace(line[0]))
16 line++;
17 return line;
20 static char *rstrip(char *line)
22 size_t len = strlen(line);
23 while(len > 0 && isspace(line[len-1]))
24 len--;
25 line[len] = 0;
26 return line;
29 static int readline(FILE *f, char **output, size_t *maxlen)
31 size_t len = 0;
32 int c;
34 while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
36 if(c == EOF)
37 return 0;
39 do {
40 if(len+1 >= *maxlen)
42 void *temp = NULL;
43 size_t newmax;
45 newmax = (*maxlen ? (*maxlen)<<1 : 32);
46 if(newmax > *maxlen)
47 temp = realloc(*output, newmax);
48 if(!temp)
50 ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
51 return 0;
54 *output = temp;
55 *maxlen = newmax;
57 (*output)[len++] = c;
58 (*output)[len] = '\0';
59 } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
61 return 1;
65 /* Custom strtok_r, since we can't rely on it existing. */
66 static char *my_strtok_r(char *str, const char *delim, char **saveptr)
68 /* Sanity check and update internal pointer. */
69 if(!saveptr || !delim) return NULL;
70 if(str) *saveptr = str;
71 str = *saveptr;
73 /* Nothing more to do with this string. */
74 if(!str) return NULL;
76 /* Find the first non-delimiter character. */
77 while(*str != '\0' && strchr(delim, *str) != NULL)
78 str++;
79 if(*str == '\0')
81 /* End of string. */
82 *saveptr = NULL;
83 return NULL;
86 /* Find the next delimiter character. */
87 *saveptr = strpbrk(str, delim);
88 if(*saveptr) *((*saveptr)++) = '\0';
90 return str;
93 static char *read_int(ALint *num, const char *line, int base)
95 char *end;
96 *num = strtol(line, &end, base);
97 if(end && *end != '\0')
98 end = lstrip(end);
99 return end;
102 static char *read_uint(ALuint *num, const char *line, int base)
104 char *end;
105 *num = strtoul(line, &end, base);
106 if(end && *end != '\0')
107 end = lstrip(end);
108 return end;
111 static char *read_float(ALfloat *num, const char *line)
113 char *end;
114 #ifdef HAVE_STRTOF
115 *num = strtof(line, &end);
116 #else
117 *num = (ALfloat)strtod(line, &end);
118 #endif
119 if(end && *end != '\0')
120 end = lstrip(end);
121 return end;
125 char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
127 while(readline(f, buffer, maxlen))
129 char *line, *comment;
131 line = lstrip(*buffer);
132 comment = strchr(line, '#');
133 if(comment) *(comment++) = 0;
135 line = rstrip(line);
136 if(line[0]) return line;
138 return NULL;
141 static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
143 ALsizei cur = 0;
144 while(cur < conf->NumSpeakers)
146 const char *cmd = my_strtok_r(NULL, " \t", saveptr);
147 if(!cmd)
149 char *line = read_clipped_line(f, buffer, maxlen);
150 if(!line)
152 ERR("Unexpected end of file\n");
153 return 0;
155 cmd = my_strtok_r(line, " \t", saveptr);
158 if(strcmp(cmd, "add_spkr") == 0)
160 const char *name = my_strtok_r(NULL, " \t", saveptr);
161 const char *dist = my_strtok_r(NULL, " \t", saveptr);
162 const char *az = my_strtok_r(NULL, " \t", saveptr);
163 const char *elev = my_strtok_r(NULL, " \t", saveptr);
164 const char *conn = my_strtok_r(NULL, " \t", saveptr);
166 if(!name) WARN("Name not specified for speaker %u\n", cur+1);
167 else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
168 if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
169 else read_float(&conf->Speakers[cur].Distance, dist);
170 if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
171 else read_float(&conf->Speakers[cur].Azimuth, az);
172 if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
173 else read_float(&conf->Speakers[cur].Elevation, elev);
174 if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
175 else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
177 cur++;
179 else
181 ERR("Unexpected speakers command: %s\n", cmd);
182 return 0;
185 cmd = my_strtok_r(NULL, " \t", saveptr);
186 if(cmd)
188 ERR("Unexpected junk on line: %s\n", cmd);
189 return 0;
193 return 1;
196 static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
198 int gotgains = 0;
199 ALsizei cur = 0;
200 while(cur < maxrow)
202 const char *cmd = my_strtok_r(NULL, " \t", saveptr);
203 if(!cmd)
205 char *line = read_clipped_line(f, buffer, maxlen);
206 if(!line)
208 ERR("Unexpected end of file\n");
209 return 0;
211 cmd = my_strtok_r(line, " \t", saveptr);
214 if(strcmp(cmd, "order_gain") == 0)
216 ALuint curgain = 0;
217 char *line;
218 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
220 ALfloat value;
221 line = read_float(&value, line);
222 if(line && *line != '\0')
224 ERR("Extra junk on gain %u: %s\n", curgain+1, line);
225 return 0;
227 if(curgain < MAX_AMBI_ORDER+1)
228 gains[curgain] = value;
229 curgain++;
231 while(curgain < MAX_AMBI_ORDER+1)
232 gains[curgain++] = 0.0f;
233 gotgains = 1;
235 else if(strcmp(cmd, "add_row") == 0)
237 ALuint curidx = 0;
238 char *line;
239 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
241 ALfloat value;
242 line = read_float(&value, line);
243 if(line && *line != '\0')
245 ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
246 return 0;
248 if(curidx < MAX_AMBI_COEFFS)
249 matrix[cur][curidx] = value;
250 curidx++;
252 while(curidx < MAX_AMBI_COEFFS)
253 matrix[cur][curidx++] = 0.0f;
254 cur++;
256 else
258 ERR("Unexpected speakers command: %s\n", cmd);
259 return 0;
262 cmd = my_strtok_r(NULL, " \t", saveptr);
263 if(cmd)
265 ERR("Unexpected junk on line: %s\n", cmd);
266 return 0;
270 if(!gotgains)
272 ERR("Matrix order_gain not specified\n");
273 return 0;
276 return 1;
279 void ambdec_init(AmbDecConf *conf)
281 ALsizei i;
283 memset(conf, 0, sizeof(*conf));
284 AL_STRING_INIT(conf->Description);
285 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
287 AL_STRING_INIT(conf->Speakers[i].Name);
288 AL_STRING_INIT(conf->Speakers[i].Connection);
292 void ambdec_deinit(AmbDecConf *conf)
294 ALsizei i;
296 alstr_reset(&conf->Description);
297 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
299 alstr_reset(&conf->Speakers[i].Name);
300 alstr_reset(&conf->Speakers[i].Connection);
302 memset(conf, 0, sizeof(*conf));
305 int ambdec_load(AmbDecConf *conf, const char *fname)
307 char *buffer = NULL;
308 size_t maxlen = 0;
309 char *line;
310 FILE *f;
312 f = al_fopen(fname, "r");
313 if(!f)
315 ERR("Failed to open: %s\n", fname);
316 return 0;
319 while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
321 char *saveptr;
322 char *command;
324 command = my_strtok_r(line, "/ \t", &saveptr);
325 if(!command)
327 ERR("Malformed line: %s\n", line);
328 goto fail;
331 if(strcmp(command, "description") == 0)
333 char *value = my_strtok_r(NULL, "", &saveptr);
334 alstr_copy_cstr(&conf->Description, lstrip(value));
336 else if(strcmp(command, "version") == 0)
338 line = my_strtok_r(NULL, "", &saveptr);
339 line = read_uint(&conf->Version, line, 10);
340 if(line && *line != '\0')
342 ERR("Extra junk after version: %s\n", line);
343 goto fail;
345 if(conf->Version != 3)
347 ERR("Unsupported version: %u\n", conf->Version);
348 goto fail;
351 else if(strcmp(command, "dec") == 0)
353 const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
354 if(strcmp(dec, "chan_mask") == 0)
356 line = my_strtok_r(NULL, "", &saveptr);
357 line = read_uint(&conf->ChanMask, line, 16);
358 if(line && *line != '\0')
360 ERR("Extra junk after mask: %s\n", line);
361 goto fail;
364 else if(strcmp(dec, "freq_bands") == 0)
366 line = my_strtok_r(NULL, "", &saveptr);
367 line = read_uint(&conf->FreqBands, line, 10);
368 if(line && *line != '\0')
370 ERR("Extra junk after freq_bands: %s\n", line);
371 goto fail;
373 if(conf->FreqBands != 1 && conf->FreqBands != 2)
375 ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
376 goto fail;
379 else if(strcmp(dec, "speakers") == 0)
381 line = my_strtok_r(NULL, "", &saveptr);
382 line = read_int(&conf->NumSpeakers, line, 10);
383 if(line && *line != '\0')
385 ERR("Extra junk after speakers: %s\n", line);
386 goto fail;
388 if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
390 ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
391 goto fail;
394 else if(strcmp(dec, "coeff_scale") == 0)
396 line = my_strtok_r(NULL, " \t", &saveptr);
397 if(strcmp(line, "n3d") == 0)
398 conf->CoeffScale = ADS_N3D;
399 else if(strcmp(line, "sn3d") == 0)
400 conf->CoeffScale = ADS_SN3D;
401 else if(strcmp(line, "fuma") == 0)
402 conf->CoeffScale = ADS_FuMa;
403 else
405 ERR("Unsupported coeff scale: %s\n", line);
406 goto fail;
409 else
411 ERR("Unexpected /dec option: %s\n", dec);
412 goto fail;
415 else if(strcmp(command, "opt") == 0)
417 const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
418 if(strcmp(opt, "xover_freq") == 0)
420 line = my_strtok_r(NULL, "", &saveptr);
421 line = read_float(&conf->XOverFreq, line);
422 if(line && *line != '\0')
424 ERR("Extra junk after xover_freq: %s\n", line);
425 goto fail;
428 else if(strcmp(opt, "xover_ratio") == 0)
430 line = my_strtok_r(NULL, "", &saveptr);
431 line = read_float(&conf->XOverRatio, line);
432 if(line && *line != '\0')
434 ERR("Extra junk after xover_ratio: %s\n", line);
435 goto fail;
438 else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
439 strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
441 /* Unused */
442 my_strtok_r(NULL, " \t", &saveptr);
444 else
446 ERR("Unexpected /opt option: %s\n", opt);
447 goto fail;
450 else if(strcmp(command, "speakers") == 0)
452 const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
453 if(strcmp(value, "{") != 0)
455 ERR("Expected { after %s command, got %s\n", command, value);
456 goto fail;
458 if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
459 goto fail;
460 value = my_strtok_r(NULL, "/ \t", &saveptr);
461 if(!value)
463 line = read_clipped_line(f, &buffer, &maxlen);
464 if(!line)
466 ERR("Unexpected end of file\n");
467 goto fail;
469 value = my_strtok_r(line, "/ \t", &saveptr);
471 if(strcmp(value, "}") != 0)
473 ERR("Expected } after speaker definitions, got %s\n", value);
474 goto fail;
477 else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
478 strcmp(command, "matrix") == 0)
480 const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
481 if(strcmp(value, "{") != 0)
483 ERR("Expected { after %s command, got %s\n", command, value);
484 goto fail;
486 if(conf->FreqBands == 1)
488 if(strcmp(command, "matrix") != 0)
490 ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
491 goto fail;
493 if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
494 f, &buffer, &maxlen, &saveptr))
495 goto fail;
497 else
499 if(strcmp(command, "lfmatrix") == 0)
501 if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
502 f, &buffer, &maxlen, &saveptr))
503 goto fail;
505 else if(strcmp(command, "hfmatrix") == 0)
507 if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
508 f, &buffer, &maxlen, &saveptr))
509 goto fail;
511 else
513 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
514 goto fail;
517 value = my_strtok_r(NULL, "/ \t", &saveptr);
518 if(!value)
520 line = read_clipped_line(f, &buffer, &maxlen);
521 if(!line)
523 ERR("Unexpected end of file\n");
524 goto fail;
526 value = my_strtok_r(line, "/ \t", &saveptr);
528 if(strcmp(value, "}") != 0)
530 ERR("Expected } after matrix definitions, got %s\n", value);
531 goto fail;
534 else if(strcmp(command, "end") == 0)
536 line = my_strtok_r(NULL, "/ \t", &saveptr);
537 if(line)
539 ERR("Unexpected junk on end: %s\n", line);
540 goto fail;
543 fclose(f);
544 free(buffer);
545 return 1;
547 else
549 ERR("Unexpected command: %s\n", command);
550 goto fail;
553 line = my_strtok_r(NULL, "/ \t", &saveptr);
554 if(line)
556 ERR("Unexpected junk on line: %s\n", line);
557 goto fail;
560 ERR("Unexpected end of file\n");
562 fail:
563 fclose(f);
564 free(buffer);
565 return 0;