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
= 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_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
) 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
);
171 ERR("Unexpected speakers command: %s\n", cmd
);
175 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
178 ERR("Unexpected junk on line: %s\n", cmd
);
186 static int load_matrix(ALfloat
*gains
, ALfloat (*matrix
)[MAX_AMBI_COEFFS
], ALuint maxrow
, FILE *f
, char **buffer
, size_t *maxlen
, char **saveptr
)
192 const char *cmd
= my_strtok_r(NULL
, " \t", saveptr
);
195 char *line
= read_clipped_line(f
, buffer
, maxlen
);
198 ERR("Unexpected end of file\n");
201 cmd
= my_strtok_r(line
, " \t", saveptr
);
204 if(strcmp(cmd
, "order_gain") == 0)
208 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
211 line
= read_float(&value
, line
);
212 if(line
&& *line
!= '\0')
214 ERR("Extra junk on gain %u: %s\n", curgain
+1, line
);
217 if(curgain
< MAX_AMBI_ORDER
+1)
218 gains
[curgain
] = value
;
221 while(curgain
< MAX_AMBI_ORDER
+1)
222 gains
[curgain
++] = 0.0f
;
225 else if(strcmp(cmd
, "add_row") == 0)
229 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
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
);
238 if(curidx
< MAX_AMBI_COEFFS
)
239 matrix
[cur
][curidx
] = value
;
242 while(curidx
< MAX_AMBI_COEFFS
)
243 matrix
[cur
][curidx
++] = 0.0f
;
248 ERR("Unexpected speakers command: %s\n", cmd
);
252 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
255 ERR("Unexpected junk on line: %s\n", cmd
);
262 ERR("Matrix order_gain not specified\n");
269 void ambdec_init(AmbDecConf
*conf
)
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
)
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
)
301 f
= al_fopen(fname
, "r");
304 ERR("Failed to open: %s\n", fname
);
308 while(read_clipped_line(f
, &buffer
, &maxlen
))
310 char *line
, *saveptr
;
313 command
= my_strtok_r(buffer
, "/ \t", &saveptr
);
316 ERR("Malformed line: %s\n", line
);
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
);
334 if(conf
->Version
!= 3)
336 ERR("Unsupported version: %u\n", conf
->Version
);
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
);
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
);
362 if(conf
->FreqBands
!= 1 && conf
->FreqBands
!= 2)
364 ERR("Invalid freq_bands value: %u\n", conf
->FreqBands
);
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
);
377 if(conf
->NumSpeakers
> MAX_OUTPUT_CHANNELS
)
379 ERR("Unsupported speaker count: %u\n", conf
->NumSpeakers
);
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
;
394 ERR("Unsupported coeff scale: %s\n", line
);
400 ERR("Unexpected /dec option: %s\n", dec
);
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
);
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
);
427 else if(strcmp(opt
, "input_scale") == 0 || strcmp(opt
, "nfeff_comp") == 0 ||
428 strcmp(opt
, "delay_comp") == 0 || strcmp(opt
, "level_comp") == 0)
431 my_strtok_r(NULL
, " \t", &saveptr
);
435 ERR("Unexpected /opt option: %s\n", opt
);
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
);
447 if(!load_speakers(conf
, f
, &buffer
, &maxlen
, &saveptr
))
449 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
452 line
= read_clipped_line(f
, &buffer
, &maxlen
);
455 ERR("Unexpected end of file\n");
458 value
= my_strtok_r(line
, "/ \t", &saveptr
);
460 if(strcmp(value
, "}") != 0)
462 ERR("Expected } after speaker definitions, got %s\n", value
);
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
);
475 if(conf
->FreqBands
== 1)
477 if(strcmp(command
, "matrix") != 0)
479 ERR("Unexpected \"%s\" type for a single-band decoder\n", command
);
482 if(!load_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
483 f
, &buffer
, &maxlen
, &saveptr
))
488 if(strcmp(command
, "lfmatrix") == 0)
490 if(!load_matrix(conf
->LFOrderGain
, conf
->LFMatrix
, conf
->NumSpeakers
,
491 f
, &buffer
, &maxlen
, &saveptr
))
494 else if(strcmp(command
, "hfmatrix") == 0)
496 if(!load_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
497 f
, &buffer
, &maxlen
, &saveptr
))
502 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command
);
506 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
509 line
= read_clipped_line(f
, &buffer
, &maxlen
);
512 ERR("Unexpected end of file\n");
515 value
= my_strtok_r(line
, "/ \t", &saveptr
);
517 if(strcmp(value
, "}") != 0)
519 ERR("Expected } after matrix definitions, got %s\n", value
);
523 else if(strcmp(command
, "end") == 0)
525 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
528 ERR("Unexpected junk on end: %s\n", line
);
538 ERR("Unexpected command: %s\n", command
);
542 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
545 ERR("Unexpected junk on line: %s\n", line
);
549 ERR("Unexpected end of file\n");