Always mix to the real output for DirectChannels
[openal-soft.git] / Alc / ambdec.c
blob9b4676484476a9d54fd94420f38d30e0309819ac
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_uint(ALuint *num, const char *line, int base)
95 char *end;
96 *num = strtoul(line, &end, base);
97 if(end && *end != '\0')
98 end = lstrip(end);
99 return end;
102 static char *read_float(ALfloat *num, const char *line)
104 char *end;
105 #ifdef HAVE_STRTOF
106 *num = strtof(line, &end);
107 #else
108 *num = strtod(line, &end);
109 #endif
110 if(end && *end != '\0')
111 end = lstrip(end);
112 return end;
116 char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
118 while(readline(f, buffer, maxlen))
120 char *line, *comment;
122 line = lstrip(*buffer);
123 comment = strchr(line, '#');
124 if(comment) *(comment++) = 0;
126 line = rstrip(line);
127 if(line[0]) return line;
129 return NULL;
132 static int load_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
134 ALuint cur = 0;
135 while(cur < conf->NumSpeakers)
137 const char *cmd = my_strtok_r(NULL, " \t", saveptr);
138 if(!cmd)
140 char *line = read_clipped_line(f, buffer, maxlen);
141 if(!line)
143 ERR("Unexpected end of file\n");
144 return 0;
146 cmd = my_strtok_r(line, " \t", saveptr);
149 if(strcmp(cmd, "add_spkr") == 0)
151 const char *name = my_strtok_r(NULL, " \t", saveptr);
152 const char *dist = my_strtok_r(NULL, " \t", saveptr);
153 const char *az = my_strtok_r(NULL, " \t", saveptr);
154 const char *elev = my_strtok_r(NULL, " \t", saveptr);
155 const char *conn = my_strtok_r(NULL, " \t", saveptr);
157 if(!name) ERR("Name not specified for speaker %u\n", cur+1);
158 else al_string_copy_cstr(&conf->Speakers[cur].Name, name);
159 if(!dist) ERR("Distance not specified for speaker %u\n", cur+1);
160 else read_float(&conf->Speakers[cur].Distance, dist);
161 if(!az) ERR("Azimuth not specified for speaker %u\n", cur+1);
162 else read_float(&conf->Speakers[cur].Azimuth, az);
163 if(!elev) ERR("Elevation not specified for speaker %u\n", cur+1);
164 else read_float(&conf->Speakers[cur].Elevation, elev);
165 if(!conn) ERR("Connection not specified for speaker %u\n", cur+1);
166 else al_string_copy_cstr(&conf->Speakers[cur].Connection, conn);
167 cur++;
169 else
171 ERR("Unexpected speakers command: %s\n", cmd);
172 return 0;
175 cmd = my_strtok_r(NULL, " \t", saveptr);
176 if(cmd)
178 ERR("Unexpected junk on line: %s\n", cmd);
179 return 0;
183 return 1;
186 static int load_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALuint maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
188 int gotgains = 0;
189 ALuint cur = 0;
190 while(cur < maxrow)
192 const char *cmd = my_strtok_r(NULL, " \t", saveptr);
193 if(!cmd)
195 char *line = read_clipped_line(f, buffer, maxlen);
196 if(!line)
198 ERR("Unexpected end of file\n");
199 return 0;
201 cmd = my_strtok_r(line, " \t", saveptr);
204 if(strcmp(cmd, "order_gain") == 0)
206 ALuint curgain = 0;
207 char *line;
208 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
210 ALfloat value;
211 line = read_float(&value, line);
212 if(line && *line != '\0')
214 ERR("Extra junk on gain %u: %s\n", curgain+1, line);
215 return 0;
217 if(curgain < MAX_AMBI_ORDER+1)
218 gains[curgain] = value;
219 curgain++;
221 while(curgain < MAX_AMBI_ORDER+1)
222 gains[curgain++] = 0.0f;
223 gotgains = 1;
225 else if(strcmp(cmd, "add_row") == 0)
227 ALuint curidx = 0;
228 char *line;
229 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
231 ALfloat value;
232 line = read_float(&value, line);
233 if(line && *line != '\0')
235 ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
236 return 0;
238 if(curidx < MAX_AMBI_COEFFS)
239 matrix[cur][curidx] = value;
240 curidx++;
242 while(curidx < MAX_AMBI_COEFFS)
243 matrix[cur][curidx++] = 0.0f;
244 cur++;
246 else
248 ERR("Unexpected speakers command: %s\n", cmd);
249 return 0;
252 cmd = my_strtok_r(NULL, " \t", saveptr);
253 if(cmd)
255 ERR("Unexpected junk on line: %s\n", cmd);
256 return 0;
260 if(!gotgains)
262 ERR("Matrix order_gain not specified\n");
263 return 0;
266 return 1;
269 void ambdec_init(AmbDecConf *conf)
271 ALuint i;
273 memset(conf, 0, sizeof(*conf));
274 AL_STRING_INIT(conf->Description);
275 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
277 AL_STRING_INIT(conf->Speakers[i].Name);
278 AL_STRING_INIT(conf->Speakers[i].Connection);
282 void ambdec_deinit(AmbDecConf *conf)
284 ALuint i;
286 al_string_deinit(&conf->Description);
287 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
289 al_string_deinit(&conf->Speakers[i].Name);
290 al_string_deinit(&conf->Speakers[i].Connection);
292 memset(conf, 0, sizeof(*conf));
295 int ambdec_load(AmbDecConf *conf, const char *fname)
297 char *buffer = NULL;
298 size_t maxlen = 0;
299 FILE *f;
301 f = al_fopen(fname, "r");
302 if(!f)
304 ERR("Failed to open: %s\n", fname);
305 return 0;
308 while(read_clipped_line(f, &buffer, &maxlen))
310 char *line, *saveptr;
311 char *command;
313 command = my_strtok_r(buffer, "/ \t", &saveptr);
314 if(!command)
316 ERR("Malformed line: %s\n", line);
317 goto fail;
320 if(strcmp(command, "description") == 0)
322 char *value = my_strtok_r(NULL, "", &saveptr);
323 al_string_copy_cstr(&conf->Description, lstrip(value));
325 else if(strcmp(command, "version") == 0)
327 line = my_strtok_r(NULL, "", &saveptr);
328 line = read_uint(&conf->Version, line, 10);
329 if(line && *line != '\0')
331 ERR("Extra junk after version: %s\n", line);
332 goto fail;
334 if(conf->Version != 3)
336 ERR("Unsupported version: %u\n", conf->Version);
337 goto fail;
340 else if(strcmp(command, "dec") == 0)
342 const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
343 if(strcmp(dec, "chan_mask") == 0)
345 line = my_strtok_r(NULL, "", &saveptr);
346 line = read_uint(&conf->ChanMask, line, 16);
347 if(line && *line != '\0')
349 ERR("Extra junk after mask: %s\n", line);
350 goto fail;
353 else if(strcmp(dec, "freq_bands") == 0)
355 line = my_strtok_r(NULL, "", &saveptr);
356 line = read_uint(&conf->FreqBands, line, 10);
357 if(line && *line != '\0')
359 ERR("Extra junk after freq_bands: %s\n", line);
360 goto fail;
362 if(conf->FreqBands != 1 && conf->FreqBands != 2)
364 ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
365 goto fail;
368 else if(strcmp(dec, "speakers") == 0)
370 line = my_strtok_r(NULL, "", &saveptr);
371 line = read_uint(&conf->NumSpeakers, line, 10);
372 if(line && *line != '\0')
374 ERR("Extra junk after speakers: %s\n", line);
375 goto fail;
377 if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
379 ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
380 goto fail;
383 else if(strcmp(dec, "coeff_scale") == 0)
385 line = my_strtok_r(NULL, " \t", &saveptr);
386 if(strcmp(line, "n3d") == 0)
387 conf->CoeffScale = ADS_N3D;
388 else if(strcmp(line, "sn3d") == 0)
389 conf->CoeffScale = ADS_SN3D;
390 else if(strcmp(line, "fuma") == 0)
391 conf->CoeffScale = ADS_FuMa;
392 else
394 ERR("Unsupported coeff scale: %s\n", line);
395 goto fail;
398 else
400 ERR("Unexpected /dec option: %s\n", dec);
401 goto fail;
404 else if(strcmp(command, "opt") == 0)
406 const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
407 if(strcmp(opt, "xover_freq") == 0)
409 line = my_strtok_r(NULL, "", &saveptr);
410 line = read_float(&conf->XOverFreq, line);
411 if(line && *line != '\0')
413 ERR("Extra junk after xover_freq: %s\n", line);
414 goto fail;
417 else if(strcmp(opt, "xover_ratio") == 0)
419 line = my_strtok_r(NULL, "", &saveptr);
420 line = read_float(&conf->XOverRatio, line);
421 if(line && *line != '\0')
423 ERR("Extra junk after xover_ratio: %s\n", line);
424 goto fail;
427 else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
428 strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
430 /* Unused */
431 my_strtok_r(NULL, " \t", &saveptr);
433 else
435 ERR("Unexpected /opt option: %s\n", opt);
436 goto fail;
439 else if(strcmp(command, "speakers") == 0)
441 const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
442 if(strcmp(value, "{") != 0)
444 ERR("Expected { after speakers command, got %s\n", value);
445 goto fail;
447 if(!load_speakers(conf, f, &buffer, &maxlen, &saveptr))
448 goto fail;
449 value = my_strtok_r(NULL, "/ \t", &saveptr);
450 if(!value)
452 line = read_clipped_line(f, &buffer, &maxlen);
453 if(!line)
455 ERR("Unexpected end of file\n");
456 goto fail;
458 value = my_strtok_r(line, "/ \t", &saveptr);
460 if(strcmp(value, "}") != 0)
462 ERR("Expected } after speaker definitions, got %s\n", value);
463 goto fail;
466 else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
467 strcmp(command, "matrix") == 0)
469 const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
470 if(strcmp(value, "{") != 0)
472 ERR("Expected { after speakers command, got %s\n", value);
473 goto fail;
475 if(conf->FreqBands == 1)
477 if(strcmp(command, "matrix") != 0)
479 ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
480 goto fail;
482 if(!load_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
483 f, &buffer, &maxlen, &saveptr))
484 goto fail;
486 else
488 if(strcmp(command, "lfmatrix") == 0)
490 if(!load_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
491 f, &buffer, &maxlen, &saveptr))
492 goto fail;
494 else if(strcmp(command, "hfmatrix") == 0)
496 if(!load_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
497 f, &buffer, &maxlen, &saveptr))
498 goto fail;
500 else
502 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
503 goto fail;
506 value = my_strtok_r(NULL, "/ \t", &saveptr);
507 if(!value)
509 line = read_clipped_line(f, &buffer, &maxlen);
510 if(!line)
512 ERR("Unexpected end of file\n");
513 goto fail;
515 value = my_strtok_r(line, "/ \t", &saveptr);
517 if(strcmp(value, "}") != 0)
519 ERR("Expected } after matrix definitions, got %s\n", value);
520 goto fail;
523 else if(strcmp(command, "end") == 0)
525 line = my_strtok_r(NULL, "/ \t", &saveptr);
526 if(line)
528 ERR("Unexpected junk on end: %s\n", line);
529 goto fail;
532 fclose(f);
533 free(buffer);
534 return 1;
536 else
538 ERR("Unexpected command: %s\n", command);
539 goto fail;
542 line = my_strtok_r(NULL, "/ \t", &saveptr);
543 if(line)
545 ERR("Unexpected junk on line: %s\n", line);
546 goto fail;
549 ERR("Unexpected end of file\n");
551 fail:
552 fclose(f);
553 free(buffer);
554 return 0;