Recognize Headset formfactors as headphones
[openal-soft.git] / Alc / ambdec.c
blob255011d5e84d67fd40df4859838c0c670dbfa721
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 = (ALfloat)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_ambdec_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) 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);
168 cur++;
170 else
172 ERR("Unexpected speakers command: %s\n", cmd);
173 return 0;
176 cmd = my_strtok_r(NULL, " \t", saveptr);
177 if(cmd)
179 ERR("Unexpected junk on line: %s\n", cmd);
180 return 0;
184 return 1;
187 static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALuint maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
189 int gotgains = 0;
190 ALuint cur = 0;
191 while(cur < maxrow)
193 const char *cmd = my_strtok_r(NULL, " \t", saveptr);
194 if(!cmd)
196 char *line = read_clipped_line(f, buffer, maxlen);
197 if(!line)
199 ERR("Unexpected end of file\n");
200 return 0;
202 cmd = my_strtok_r(line, " \t", saveptr);
205 if(strcmp(cmd, "order_gain") == 0)
207 ALuint curgain = 0;
208 char *line;
209 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
211 ALfloat value;
212 line = read_float(&value, line);
213 if(line && *line != '\0')
215 ERR("Extra junk on gain %u: %s\n", curgain+1, line);
216 return 0;
218 if(curgain < MAX_AMBI_ORDER+1)
219 gains[curgain] = value;
220 curgain++;
222 while(curgain < MAX_AMBI_ORDER+1)
223 gains[curgain++] = 0.0f;
224 gotgains = 1;
226 else if(strcmp(cmd, "add_row") == 0)
228 ALuint curidx = 0;
229 char *line;
230 while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
232 ALfloat value;
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);
237 return 0;
239 if(curidx < MAX_AMBI_COEFFS)
240 matrix[cur][curidx] = value;
241 curidx++;
243 while(curidx < MAX_AMBI_COEFFS)
244 matrix[cur][curidx++] = 0.0f;
245 cur++;
247 else
249 ERR("Unexpected speakers command: %s\n", cmd);
250 return 0;
253 cmd = my_strtok_r(NULL, " \t", saveptr);
254 if(cmd)
256 ERR("Unexpected junk on line: %s\n", cmd);
257 return 0;
261 if(!gotgains)
263 ERR("Matrix order_gain not specified\n");
264 return 0;
267 return 1;
270 void ambdec_init(AmbDecConf *conf)
272 ALuint i;
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)
285 ALuint i;
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)
298 char *buffer = NULL;
299 size_t maxlen = 0;
300 char *line;
301 FILE *f;
303 f = al_fopen(fname, "r");
304 if(!f)
306 ERR("Failed to open: %s\n", fname);
307 return 0;
310 while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
312 char *saveptr;
313 char *command;
315 command = my_strtok_r(line, "/ \t", &saveptr);
316 if(!command)
318 ERR("Malformed line: %s\n", line);
319 goto fail;
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);
334 goto fail;
336 if(conf->Version != 3)
338 ERR("Unsupported version: %u\n", conf->Version);
339 goto fail;
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);
352 goto fail;
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);
362 goto fail;
364 if(conf->FreqBands != 1 && conf->FreqBands != 2)
366 ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
367 goto fail;
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);
377 goto fail;
379 if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
381 ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
382 goto fail;
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;
394 else
396 ERR("Unsupported coeff scale: %s\n", line);
397 goto fail;
400 else
402 ERR("Unexpected /dec option: %s\n", dec);
403 goto fail;
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);
416 goto fail;
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);
426 goto fail;
429 else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
430 strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
432 /* Unused */
433 my_strtok_r(NULL, " \t", &saveptr);
435 else
437 ERR("Unexpected /opt option: %s\n", opt);
438 goto fail;
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);
447 goto fail;
449 if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
450 goto fail;
451 value = my_strtok_r(NULL, "/ \t", &saveptr);
452 if(!value)
454 line = read_clipped_line(f, &buffer, &maxlen);
455 if(!line)
457 ERR("Unexpected end of file\n");
458 goto fail;
460 value = my_strtok_r(line, "/ \t", &saveptr);
462 if(strcmp(value, "}") != 0)
464 ERR("Expected } after speaker definitions, got %s\n", value);
465 goto fail;
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);
475 goto fail;
477 if(conf->FreqBands == 1)
479 if(strcmp(command, "matrix") != 0)
481 ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
482 goto fail;
484 if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
485 f, &buffer, &maxlen, &saveptr))
486 goto fail;
488 else
490 if(strcmp(command, "lfmatrix") == 0)
492 if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
493 f, &buffer, &maxlen, &saveptr))
494 goto fail;
496 else if(strcmp(command, "hfmatrix") == 0)
498 if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
499 f, &buffer, &maxlen, &saveptr))
500 goto fail;
502 else
504 ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
505 goto fail;
508 value = my_strtok_r(NULL, "/ \t", &saveptr);
509 if(!value)
511 line = read_clipped_line(f, &buffer, &maxlen);
512 if(!line)
514 ERR("Unexpected end of file\n");
515 goto fail;
517 value = my_strtok_r(line, "/ \t", &saveptr);
519 if(strcmp(value, "}") != 0)
521 ERR("Expected } after matrix definitions, got %s\n", value);
522 goto fail;
525 else if(strcmp(command, "end") == 0)
527 line = my_strtok_r(NULL, "/ \t", &saveptr);
528 if(line)
530 ERR("Unexpected junk on end: %s\n", line);
531 goto fail;
534 fclose(f);
535 free(buffer);
536 return 1;
538 else
540 ERR("Unexpected command: %s\n", command);
541 goto fail;
544 line = my_strtok_r(NULL, "/ \t", &saveptr);
545 if(line)
547 ERR("Unexpected junk on line: %s\n", line);
548 goto fail;
551 ERR("Unexpected end of file\n");
553 fail:
554 fclose(f);
555 free(buffer);
556 return 0;