13 static char *lstrip(char *line
)
15 while(isspace(line
[0]))
20 static char *rstrip(char *line
)
22 size_t len
= strlen(line
);
23 while(len
> 0 && isspace(line
[len
-1]))
29 static int readline(FILE *f
, char **output
, size_t *maxlen
)
34 while((c
=fgetc(f
)) != EOF
&& (c
== '\r' || c
== '\n'))
45 newmax
= (*maxlen
? (*maxlen
)<<1 : 32);
47 temp
= realloc(*output
, newmax
);
50 ERR("Failed to realloc "SZFMT
" bytes from "SZFMT
"!\n", newmax
, *maxlen
);
58 (*output
)[len
] = '\0';
59 } while((c
=fgetc(f
)) != EOF
&& c
!= '\r' && c
!= '\n');
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
;
73 /* Nothing more to do with this string. */
76 /* Find the first non-delimiter character. */
77 while(*str
!= '\0' && strchr(delim
, *str
) != NULL
)
86 /* Find the next delimiter character. */
87 *saveptr
= strpbrk(str
, delim
);
88 if(*saveptr
) *((*saveptr
)++) = '\0';
93 static char *read_uint(ALuint
*num
, const char *line
, int base
)
96 *num
= strtoul(line
, &end
, base
);
97 if(end
&& *end
!= '\0')
102 static char *read_float(ALfloat
*num
, const char *line
)
106 *num
= strtof(line
, &end
);
108 *num
= (ALfloat
)strtod(line
, &end
);
110 if(end
&& *end
!= '\0')
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;
127 if(line
[0]) return line
;
132 static int load_ambdec_speakers(AmbDecConf
*conf
, FILE *f
, char **buffer
, size_t *maxlen
, char **saveptr
)
135 while(cur
< conf
->NumSpeakers
)
137 const char *cmd
= my_strtok_r(NULL
, " \t", saveptr
);
140 char *line
= read_clipped_line(f
, buffer
, maxlen
);
143 ERR("Unexpected end of file\n");
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
) WARN("Name not specified for speaker %u\n", cur
+1);
158 else al_string_copy_cstr(&conf
->Speakers
[cur
].Name
, name
);
159 if(!dist
) WARN("Distance not specified for speaker %u\n", cur
+1);
160 else read_float(&conf
->Speakers
[cur
].Distance
, dist
);
161 if(!az
) WARN("Azimuth not specified for speaker %u\n", cur
+1);
162 else read_float(&conf
->Speakers
[cur
].Azimuth
, az
);
163 if(!elev
) WARN("Elevation not specified for speaker %u\n", cur
+1);
164 else read_float(&conf
->Speakers
[cur
].Elevation
, elev
);
165 if(!conn
) TRACE("Connection not specified for speaker %u\n", cur
+1);
166 else al_string_copy_cstr(&conf
->Speakers
[cur
].Connection
, conn
);
172 ERR("Unexpected speakers command: %s\n", cmd
);
176 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
179 ERR("Unexpected junk on line: %s\n", cmd
);
187 static int load_ambdec_matrix(ALfloat
*gains
, ALfloat (*matrix
)[MAX_AMBI_COEFFS
], ALuint maxrow
, FILE *f
, char **buffer
, size_t *maxlen
, char **saveptr
)
193 const char *cmd
= my_strtok_r(NULL
, " \t", saveptr
);
196 char *line
= read_clipped_line(f
, buffer
, maxlen
);
199 ERR("Unexpected end of file\n");
202 cmd
= my_strtok_r(line
, " \t", saveptr
);
205 if(strcmp(cmd
, "order_gain") == 0)
209 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
212 line
= read_float(&value
, line
);
213 if(line
&& *line
!= '\0')
215 ERR("Extra junk on gain %u: %s\n", curgain
+1, line
);
218 if(curgain
< MAX_AMBI_ORDER
+1)
219 gains
[curgain
] = value
;
222 while(curgain
< MAX_AMBI_ORDER
+1)
223 gains
[curgain
++] = 0.0f
;
226 else if(strcmp(cmd
, "add_row") == 0)
230 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
233 line
= read_float(&value
, line
);
234 if(line
&& *line
!= '\0')
236 ERR("Extra junk on matrix element %ux%u: %s\n", cur
, curidx
, line
);
239 if(curidx
< MAX_AMBI_COEFFS
)
240 matrix
[cur
][curidx
] = value
;
243 while(curidx
< MAX_AMBI_COEFFS
)
244 matrix
[cur
][curidx
++] = 0.0f
;
249 ERR("Unexpected speakers command: %s\n", cmd
);
253 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
256 ERR("Unexpected junk on line: %s\n", cmd
);
263 ERR("Matrix order_gain not specified\n");
270 void ambdec_init(AmbDecConf
*conf
)
274 memset(conf
, 0, sizeof(*conf
));
275 AL_STRING_INIT(conf
->Description
);
276 for(i
= 0;i
< MAX_OUTPUT_CHANNELS
;i
++)
278 AL_STRING_INIT(conf
->Speakers
[i
].Name
);
279 AL_STRING_INIT(conf
->Speakers
[i
].Connection
);
283 void ambdec_deinit(AmbDecConf
*conf
)
287 al_string_deinit(&conf
->Description
);
288 for(i
= 0;i
< MAX_OUTPUT_CHANNELS
;i
++)
290 al_string_deinit(&conf
->Speakers
[i
].Name
);
291 al_string_deinit(&conf
->Speakers
[i
].Connection
);
293 memset(conf
, 0, sizeof(*conf
));
296 int ambdec_load(AmbDecConf
*conf
, const char *fname
)
303 f
= al_fopen(fname
, "r");
306 ERR("Failed to open: %s\n", fname
);
310 while((line
=read_clipped_line(f
, &buffer
, &maxlen
)) != NULL
)
315 command
= my_strtok_r(line
, "/ \t", &saveptr
);
318 ERR("Malformed line: %s\n", line
);
322 if(strcmp(command
, "description") == 0)
324 char *value
= my_strtok_r(NULL
, "", &saveptr
);
325 al_string_copy_cstr(&conf
->Description
, lstrip(value
));
327 else if(strcmp(command
, "version") == 0)
329 line
= my_strtok_r(NULL
, "", &saveptr
);
330 line
= read_uint(&conf
->Version
, line
, 10);
331 if(line
&& *line
!= '\0')
333 ERR("Extra junk after version: %s\n", line
);
336 if(conf
->Version
!= 3)
338 ERR("Unsupported version: %u\n", conf
->Version
);
342 else if(strcmp(command
, "dec") == 0)
344 const char *dec
= my_strtok_r(NULL
, "/ \t", &saveptr
);
345 if(strcmp(dec
, "chan_mask") == 0)
347 line
= my_strtok_r(NULL
, "", &saveptr
);
348 line
= read_uint(&conf
->ChanMask
, line
, 16);
349 if(line
&& *line
!= '\0')
351 ERR("Extra junk after mask: %s\n", line
);
355 else if(strcmp(dec
, "freq_bands") == 0)
357 line
= my_strtok_r(NULL
, "", &saveptr
);
358 line
= read_uint(&conf
->FreqBands
, line
, 10);
359 if(line
&& *line
!= '\0')
361 ERR("Extra junk after freq_bands: %s\n", line
);
364 if(conf
->FreqBands
!= 1 && conf
->FreqBands
!= 2)
366 ERR("Invalid freq_bands value: %u\n", conf
->FreqBands
);
370 else if(strcmp(dec
, "speakers") == 0)
372 line
= my_strtok_r(NULL
, "", &saveptr
);
373 line
= read_uint(&conf
->NumSpeakers
, line
, 10);
374 if(line
&& *line
!= '\0')
376 ERR("Extra junk after speakers: %s\n", line
);
379 if(conf
->NumSpeakers
> MAX_OUTPUT_CHANNELS
)
381 ERR("Unsupported speaker count: %u\n", conf
->NumSpeakers
);
385 else if(strcmp(dec
, "coeff_scale") == 0)
387 line
= my_strtok_r(NULL
, " \t", &saveptr
);
388 if(strcmp(line
, "n3d") == 0)
389 conf
->CoeffScale
= ADS_N3D
;
390 else if(strcmp(line
, "sn3d") == 0)
391 conf
->CoeffScale
= ADS_SN3D
;
392 else if(strcmp(line
, "fuma") == 0)
393 conf
->CoeffScale
= ADS_FuMa
;
396 ERR("Unsupported coeff scale: %s\n", line
);
402 ERR("Unexpected /dec option: %s\n", dec
);
406 else if(strcmp(command
, "opt") == 0)
408 const char *opt
= my_strtok_r(NULL
, "/ \t", &saveptr
);
409 if(strcmp(opt
, "xover_freq") == 0)
411 line
= my_strtok_r(NULL
, "", &saveptr
);
412 line
= read_float(&conf
->XOverFreq
, line
);
413 if(line
&& *line
!= '\0')
415 ERR("Extra junk after xover_freq: %s\n", line
);
419 else if(strcmp(opt
, "xover_ratio") == 0)
421 line
= my_strtok_r(NULL
, "", &saveptr
);
422 line
= read_float(&conf
->XOverRatio
, line
);
423 if(line
&& *line
!= '\0')
425 ERR("Extra junk after xover_ratio: %s\n", line
);
429 else if(strcmp(opt
, "input_scale") == 0 || strcmp(opt
, "nfeff_comp") == 0 ||
430 strcmp(opt
, "delay_comp") == 0 || strcmp(opt
, "level_comp") == 0)
433 my_strtok_r(NULL
, " \t", &saveptr
);
437 ERR("Unexpected /opt option: %s\n", opt
);
441 else if(strcmp(command
, "speakers") == 0)
443 const char *value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
444 if(strcmp(value
, "{") != 0)
446 ERR("Expected { after %s command, got %s\n", command
, value
);
449 if(!load_ambdec_speakers(conf
, f
, &buffer
, &maxlen
, &saveptr
))
451 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
454 line
= read_clipped_line(f
, &buffer
, &maxlen
);
457 ERR("Unexpected end of file\n");
460 value
= my_strtok_r(line
, "/ \t", &saveptr
);
462 if(strcmp(value
, "}") != 0)
464 ERR("Expected } after speaker definitions, got %s\n", value
);
468 else if(strcmp(command
, "lfmatrix") == 0 || strcmp(command
, "hfmatrix") == 0 ||
469 strcmp(command
, "matrix") == 0)
471 const char *value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
472 if(strcmp(value
, "{") != 0)
474 ERR("Expected { after %s command, got %s\n", command
, value
);
477 if(conf
->FreqBands
== 1)
479 if(strcmp(command
, "matrix") != 0)
481 ERR("Unexpected \"%s\" type for a single-band decoder\n", command
);
484 if(!load_ambdec_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
485 f
, &buffer
, &maxlen
, &saveptr
))
490 if(strcmp(command
, "lfmatrix") == 0)
492 if(!load_ambdec_matrix(conf
->LFOrderGain
, conf
->LFMatrix
, conf
->NumSpeakers
,
493 f
, &buffer
, &maxlen
, &saveptr
))
496 else if(strcmp(command
, "hfmatrix") == 0)
498 if(!load_ambdec_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
499 f
, &buffer
, &maxlen
, &saveptr
))
504 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command
);
508 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
511 line
= read_clipped_line(f
, &buffer
, &maxlen
);
514 ERR("Unexpected end of file\n");
517 value
= my_strtok_r(line
, "/ \t", &saveptr
);
519 if(strcmp(value
, "}") != 0)
521 ERR("Expected } after matrix definitions, got %s\n", value
);
525 else if(strcmp(command
, "end") == 0)
527 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
530 ERR("Unexpected junk on end: %s\n", line
);
540 ERR("Unexpected command: %s\n", command
);
544 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
547 ERR("Unexpected junk on line: %s\n", line
);
551 ERR("Unexpected end of file\n");