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_int(ALint
*num
, const char *line
, int base
)
96 *num
= strtol(line
, &end
, base
);
97 if(end
&& *end
!= '\0')
102 static char *read_uint(ALuint
*num
, const char *line
, int base
)
105 *num
= strtoul(line
, &end
, base
);
106 if(end
&& *end
!= '\0')
111 static char *read_float(ALfloat
*num
, const char *line
)
115 *num
= strtof(line
, &end
);
117 *num
= (ALfloat
)strtod(line
, &end
);
119 if(end
&& *end
!= '\0')
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;
136 if(line
[0]) return line
;
141 static int load_ambdec_speakers(AmbDecConf
*conf
, FILE *f
, char **buffer
, size_t *maxlen
, char **saveptr
)
144 while(cur
< conf
->NumSpeakers
)
146 const char *cmd
= my_strtok_r(NULL
, " \t", saveptr
);
149 char *line
= read_clipped_line(f
, buffer
, maxlen
);
152 ERR("Unexpected end of file\n");
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
);
181 ERR("Unexpected speakers command: %s\n", cmd
);
185 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
188 ERR("Unexpected junk on line: %s\n", cmd
);
196 static int load_ambdec_matrix(ALfloat
*gains
, ALfloat (*matrix
)[MAX_AMBI_COEFFS
], ALsizei maxrow
, FILE *f
, char **buffer
, size_t *maxlen
, char **saveptr
)
202 const char *cmd
= my_strtok_r(NULL
, " \t", saveptr
);
205 char *line
= read_clipped_line(f
, buffer
, maxlen
);
208 ERR("Unexpected end of file\n");
211 cmd
= my_strtok_r(line
, " \t", saveptr
);
214 if(strcmp(cmd
, "order_gain") == 0)
218 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
221 line
= read_float(&value
, line
);
222 if(line
&& *line
!= '\0')
224 ERR("Extra junk on gain %u: %s\n", curgain
+1, line
);
227 if(curgain
< MAX_AMBI_ORDER
+1)
228 gains
[curgain
] = value
;
231 while(curgain
< MAX_AMBI_ORDER
+1)
232 gains
[curgain
++] = 0.0f
;
235 else if(strcmp(cmd
, "add_row") == 0)
239 while((line
=my_strtok_r(NULL
, " \t", saveptr
)) != NULL
)
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
);
248 if(curidx
< MAX_AMBI_COEFFS
)
249 matrix
[cur
][curidx
] = value
;
252 while(curidx
< MAX_AMBI_COEFFS
)
253 matrix
[cur
][curidx
++] = 0.0f
;
258 ERR("Unexpected speakers command: %s\n", cmd
);
262 cmd
= my_strtok_r(NULL
, " \t", saveptr
);
265 ERR("Unexpected junk on line: %s\n", cmd
);
272 ERR("Matrix order_gain not specified\n");
279 void ambdec_init(AmbDecConf
*conf
)
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
)
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
)
312 f
= al_fopen(fname
, "r");
315 ERR("Failed to open: %s\n", fname
);
319 while((line
=read_clipped_line(f
, &buffer
, &maxlen
)) != NULL
)
324 command
= my_strtok_r(line
, "/ \t", &saveptr
);
327 ERR("Malformed line: %s\n", line
);
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
);
345 if(conf
->Version
!= 3)
347 ERR("Unsupported version: %u\n", conf
->Version
);
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
);
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
);
373 if(conf
->FreqBands
!= 1 && conf
->FreqBands
!= 2)
375 ERR("Invalid freq_bands value: %u\n", conf
->FreqBands
);
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
);
388 if(conf
->NumSpeakers
> MAX_OUTPUT_CHANNELS
)
390 ERR("Unsupported speaker count: %u\n", conf
->NumSpeakers
);
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
;
405 ERR("Unsupported coeff scale: %s\n", line
);
411 ERR("Unexpected /dec option: %s\n", dec
);
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
);
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
);
438 else if(strcmp(opt
, "input_scale") == 0 || strcmp(opt
, "nfeff_comp") == 0 ||
439 strcmp(opt
, "delay_comp") == 0 || strcmp(opt
, "level_comp") == 0)
442 my_strtok_r(NULL
, " \t", &saveptr
);
446 ERR("Unexpected /opt option: %s\n", opt
);
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
);
458 if(!load_ambdec_speakers(conf
, f
, &buffer
, &maxlen
, &saveptr
))
460 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
463 line
= read_clipped_line(f
, &buffer
, &maxlen
);
466 ERR("Unexpected end of file\n");
469 value
= my_strtok_r(line
, "/ \t", &saveptr
);
471 if(strcmp(value
, "}") != 0)
473 ERR("Expected } after speaker definitions, got %s\n", value
);
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
);
486 if(conf
->FreqBands
== 1)
488 if(strcmp(command
, "matrix") != 0)
490 ERR("Unexpected \"%s\" type for a single-band decoder\n", command
);
493 if(!load_ambdec_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
494 f
, &buffer
, &maxlen
, &saveptr
))
499 if(strcmp(command
, "lfmatrix") == 0)
501 if(!load_ambdec_matrix(conf
->LFOrderGain
, conf
->LFMatrix
, conf
->NumSpeakers
,
502 f
, &buffer
, &maxlen
, &saveptr
))
505 else if(strcmp(command
, "hfmatrix") == 0)
507 if(!load_ambdec_matrix(conf
->HFOrderGain
, conf
->HFMatrix
, conf
->NumSpeakers
,
508 f
, &buffer
, &maxlen
, &saveptr
))
513 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command
);
517 value
= my_strtok_r(NULL
, "/ \t", &saveptr
);
520 line
= read_clipped_line(f
, &buffer
, &maxlen
);
523 ERR("Unexpected end of file\n");
526 value
= my_strtok_r(line
, "/ \t", &saveptr
);
528 if(strcmp(value
, "}") != 0)
530 ERR("Expected } after matrix definitions, got %s\n", value
);
534 else if(strcmp(command
, "end") == 0)
536 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
539 ERR("Unexpected junk on end: %s\n", line
);
549 ERR("Unexpected command: %s\n", command
);
553 line
= my_strtok_r(NULL
, "/ \t", &saveptr
);
556 ERR("Unexpected junk on line: %s\n", line
);
560 ERR("Unexpected end of file\n");