SAF
Loading...
Searching...
No Matches
saf_sofa_reader.c
Go to the documentation of this file.
1/*
2 * Copyright 2017-2018 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
34#include "saf_sofa_reader.h"
36#include "saf_externals.h"
37
38#ifdef SAF_ENABLE_SOFA_READER_MODULE
39
40/* ========================================================================== */
41/* Main Functions */
42/* ========================================================================== */
43
45(
47 char* sofa_filepath,
49)
50{
51 /* libmysofa reader vars */
52 int err;
53 MYSOFA_HRTF *hrtf;
54 MYSOFA_ATTRIBUTE* tmp_a;
55#ifdef SAF_ENABLE_NETCDF
56 /* NetCDF reader vars */
57 int varid, attnum, i, j, ncid, retval, ndimsp, nvarsp, nattsp, unlimdimidp, varnattsp;
58 size_t tmp_size, lenp;
59 char varname[NC_MAX_NAME+1], attname[NC_MAX_NAME+1];
60 char* dimname;
61 double* tmp_data;
62 int* dimids, *dimid;
63 nc_type typep;
64 size_t* dimlength;
65#endif /* SAF_ENABLE_NETCDF */
66
67 /* Default variables */
68 h->nSources = h->nReceivers = h->DataLengthIR = -1;
69 h->DataSamplingRate = 0.0f;
70 h->nEmitters = h->nListeners = -1;
72 h->ListenerUp = h->ListenerView = h->EmitterPosition = NULL;
73 h->DataDelay = NULL;
74
75 /* Default variable attributes */
79 = h->ListenerViewType = h->ListenerViewUnits = NULL;
80
81 /* Default global attributes */
84 = h->AuthorContact = h->Comment = h->DataType = h->History = h->License
85 = h->Organisation = h->References = h->RoomType = h->Origin = h->DateCreated
86 = h->DateModified = h->Title = h->DatabaseName = h->ListenerShortName = NULL;
87
88 /* Read the SOFA file */
89 switch(option){
90 case SAF_SOFA_READER_OPTION_DEFAULT: /* fall through */
92 /* Load SOFA file using the libmysofa library: */
93 hrtf = mysofa_load(sofa_filepath, &err);
94 h->hLMSOFA = (void*)hrtf;
95 switch(err){
96 case MYSOFA_OK:
97 /* Copy variables and pointers to data: */
98 h->nSources = hrtf->M;
99 h->nReceivers = hrtf->R;
100 h->DataLengthIR = hrtf->N;
101 h->DataSamplingRate = hrtf->DataSamplingRate.values[0];
102 h->nEmitters = hrtf->E;
103 h->nListeners = hrtf->M; // changed to M for multiple listeners
104 h->DataIR = hrtf->DataIR.values;
105 h->DataDelay = hrtf->DataDelay.values;
106 h->SourcePosition = hrtf->SourcePosition.values;
107 h->ReceiverPosition = hrtf->ReceiverPosition.values;
108 h->ListenerPosition = hrtf->ListenerPosition.values;
109 h->ListenerUp = hrtf->ListenerUp.values;
110 h->ListenerView = hrtf->ListenerView.values;
111 h->EmitterPosition = hrtf->EmitterPosition.values;
112
113 /* Variable attributes */
114 tmp_a = hrtf->ListenerPosition.attributes;
115 while (tmp_a) {
116 if (!strcmp((char*)tmp_a->name,"Type"))
117 h->ListenerPositionType = tmp_a->value;
118 else if (!strcmp((char*)tmp_a->name,"Units"))
119 h->ListenerPositionUnits = tmp_a->value;
120 tmp_a = tmp_a->next;
121 }
122 tmp_a = hrtf->ReceiverPosition.attributes;
123 while (tmp_a) {
124 if (!strcmp((char*)tmp_a->name,"Type"))
125 h->ReceiverPositionType = tmp_a->value;
126 else if (!strcmp((char*)tmp_a->name,"Units"))
127 h->ReceiverPositionUnits = tmp_a->value;
128 tmp_a = tmp_a->next;
129 }
130 tmp_a = hrtf->SourcePosition.attributes;
131 while (tmp_a) {
132 if (!strcmp((char*)tmp_a->name,"Type"))
133 h->SourcePositionType = tmp_a->value;
134 else if (!strcmp((char*)tmp_a->name,"Units"))
135 h->SourcePositionUnits = tmp_a->value;
136 tmp_a = tmp_a->next;
137 }
138 tmp_a = hrtf->EmitterPosition.attributes;
139 while (tmp_a) {
140 if (!strcmp((char*)tmp_a->name,"Type"))
141 h->EmitterPositionType = tmp_a->value;
142 else if (!strcmp((char*)tmp_a->name,"Units"))
143 h->EmitterPositionUnits = tmp_a->value;
144 tmp_a = tmp_a->next;
145 }
146 tmp_a = hrtf->ListenerView.attributes;
147 while (tmp_a) {
148 if (!strcmp((char*)tmp_a->name,"Type"))
149 h->ListenerViewType = tmp_a->value;
150 else if (!strcmp((char*)tmp_a->name,"Units"))
151 h->ListenerViewUnits = tmp_a->value;
152 tmp_a = tmp_a->next;
153 }
154 tmp_a = hrtf->DataSamplingRate.attributes;
155 while (tmp_a) {
156 if (!strcmp((char*)tmp_a->name,"Units"))
157 h->DataSamplingRateUnits = tmp_a->value;
158 tmp_a = tmp_a->next;
159 }
160
161 /* Global attributes */
162 tmp_a = hrtf->attributes;
163 while (tmp_a) {
164 if (!strcmp((char*)tmp_a->name,"Conventions"))
165 h->Conventions = tmp_a->value;
166 else if (!strcmp((char*)tmp_a->name,"Version"))
167 h->Version = tmp_a->value;
168 else if (!strcmp((char*)tmp_a->name,"SOFAConventions"))
169 h->SOFAConventions = tmp_a->value;
170 else if (!strcmp((char*)tmp_a->name,"SOFAConventionsVersion"))
171 h->SOFAConventionsVersion = tmp_a->value;
172 else if (!strcmp((char*)tmp_a->name,"APIName"))
173 h->APIName = tmp_a->value;
174 else if (!strcmp((char*)tmp_a->name,"APIVersion"))
175 h->APIVersion = tmp_a->value;
176 else if (!strcmp((char*)tmp_a->name,"ApplicationName"))
177 h->ApplicationName = tmp_a->value;
178 else if (!strcmp((char*)tmp_a->name,"ApplicationVersion"))
179 h->ApplicationVersion = tmp_a->value;
180 else if (!strcmp((char*)tmp_a->name,"AuthorContact"))
181 h->AuthorContact = tmp_a->value;
182 else if (!strcmp((char*)tmp_a->name,"Comment"))
183 h->Comment = tmp_a->value;
184 else if (!strcmp((char*)tmp_a->name,"DataType"))
185 h->DataType = tmp_a->value;
186 else if (!strcmp((char*)tmp_a->name,"History"))
187 h->History = tmp_a->value;
188 else if (!strcmp((char*)tmp_a->name,"License"))
189 h->License = tmp_a->value;
190 else if (!strcmp((char*)tmp_a->name,"Organization"))
191 h->Organisation = tmp_a->value;
192 else if (!strcmp((char*)tmp_a->name,"References"))
193 h->References = tmp_a->value;
194 else if (!strcmp((char*)tmp_a->name,"RoomType"))
195 h->RoomType = tmp_a->value;
196 else if (!strcmp((char*)tmp_a->name,"Origin"))
197 h->Origin = tmp_a->value;
198 else if (!strcmp((char*)tmp_a->name,"DateCreated"))
199 h->DateCreated = tmp_a->value;
200 else if (!strcmp((char*)tmp_a->name,"DateModified"))
201 h->DateModified = tmp_a->value;
202 else if (!strcmp((char*)tmp_a->name,"Title"))
203 h->Title = tmp_a->value;
204 else if (!strcmp((char*)tmp_a->name,"DatabaseName"))
205 h->DatabaseName = tmp_a->value;
206 else if (!strcmp((char*)tmp_a->name,"ListenerShortName"))
207 h->ListenerShortName = tmp_a->value;
208 tmp_a = tmp_a->next;
209 }
210 break;
211 case MYSOFA_READ_ERROR:
213 case MYSOFA_INVALID_DIMENSIONS:
215 default:
217 }
218 break;
219
221#ifdef SAF_ENABLE_NETCDF
222 h->hLMSOFA = NULL; /* Not used */
223
224 /* Open NetCDF file */
225 if ((retval = nc_open(sofa_filepath, NC_NOWRITE, &ncid)))
226 nc_strerror(retval);
227 if(retval!=NC_NOERR)/* if error: */
229 nc_inq(ncid, &ndimsp, &nvarsp, &nattsp, &unlimdimidp); /* find number of possible dimensions, variables, and attributes */
230
231 /* Find dimension IDs and lengths */
232 dimid = malloc1d(ndimsp*sizeof(int));
233 dimlength = malloc1d(ndimsp*sizeof(size_t));
234 dimname = malloc1d(ndimsp*(NC_MAX_NAME+1)*sizeof(char)); /* +1 as NC_MAX_NAME does not include null-termination */
235 for (i=0; i<ndimsp; i++){
236 nc_inq_dim(ncid, i, &dimname[i*(NC_MAX_NAME+1)], &dimlength[i]);
237 nc_inq_dimid(ncid, &dimname[i*(NC_MAX_NAME+1)], &dimid[i]);
238 }
239
240 /* Loop over the variables and pull the data accordingly */
241 dimids = NULL;
242 tmp_data = NULL;
243 for(varid=0; varid<nvarsp; varid++){
244 nc_inq_var(ncid, varid, (char*)varname, NULL, NULL, NULL, NULL); /* Variable name */
245 nc_inq_varndims(ncid, varid, &ndimsp); /* Variable dimensionality */
246 dimids = realloc1d(dimids, ndimsp*sizeof(int));
247 nc_inq_vardimid(ncid, varid, dimids); /* Variable dimension IDs */
248 nc_inq_vartype(ncid, varid, &typep); /* Variable data type */
249 nc_inq_varnatts(ncid, varid, &varnattsp); /* Variable number of associated attributes */
250
251 if (!strcmp((char*)varname,"Data.IR")){
252 /* Checks */
253 if(ndimsp!=3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
254 if(h->nReceivers!=-1 && (int)dimlength[dimid[dimids[1]]] != h->nReceivers) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
255 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
256
257 /* Pull data */
258 h->nSources = (int)dimlength[dimid[dimids[0]]];
259 h->nReceivers = (int)dimlength[dimid[dimids[1]]];
260 h->DataLengthIR = (int)dimlength[dimid[dimids[2]]];
261 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]] * dimlength[dimid[dimids[2]]];
262 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
263 nc_get_var(ncid, varid, tmp_data);
264 h->DataIR = malloc1d(tmp_size*sizeof(float));
265 for(i=0; i<(int)tmp_size; i++)
266 h->DataIR[i] = (float)tmp_data[i];
267 }
268 else if(!strcmp((char*)varname,"Data.SamplingRate")){
269 /* Checks */
270 if(!(ndimsp==1 && dimlength[dimid[dimids[0]]] == 1)) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
271 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
272
273 /* Pull data */
274 tmp_data = realloc1d(tmp_data, 1*sizeof(double));
275 nc_get_var(ncid, varid, tmp_data);
276 h->DataSamplingRate = (float)tmp_data[0];
277
278 /* Pull associated attributes */
279 for(attnum=0; attnum<varnattsp; attnum++){
280 nc_inq_attname(ncid, varid, attnum, attname);
281 nc_inq_attlen(ncid, varid, attname, &lenp);
282 if (!strcmp((char*)attname,"Units")){
283 h->DataSamplingRateUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
284 nc_get_att(ncid, varid, attname, h->DataSamplingRateUnits);
285 }
286 }
287 }
288 else if (!strcmp((char*)varname,"Data.Delay")){
289 /* Checks */
290 if(!(ndimsp==2 || ndimsp==3)) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
291 if(h->nReceivers!=-1 && !((int)dimlength[dimid[dimids[1]]] == h->nReceivers || (int)dimlength[dimid[dimids[0]]] == h->nReceivers)) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
292 if((int)dimlength[dimid[dimids[0]]] != 1 && (int)dimlength[dimid[dimids[1]]] != 1) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
293 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
294
295 /* Pull data */
296 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
297 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
298 nc_get_var(ncid, varid, tmp_data);
299 h->DataDelay = malloc1d(tmp_size*sizeof(int));
300 for(i=0; i<(int)tmp_size; i++)
301 h->DataDelay[i] = (float)tmp_data[i];
302 }
303 else if (!strcmp((char*)varname,"SourcePosition")){
304 /* Checks */
305 if(ndimsp!=2) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
306 if(h->nSources!=-1 && (int)dimlength[dimid[dimids[0]]] != h->nSources) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
307 if((int)dimlength[dimid[dimids[1]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
308 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
309
310 /* Pull data */
311 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
312 h->nSources = (int)dimlength[dimid[dimids[0]]];
313 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
314 nc_get_var(ncid, varid, tmp_data);
315 h->SourcePosition = malloc1d(tmp_size*sizeof(float));
316 for(i=0; i<(int)tmp_size; i++)
317 h->SourcePosition[i] = (float)tmp_data[i];
318
319 /* Pull associated attributes */
320 for(attnum=0; attnum<varnattsp; attnum++){
321 nc_inq_attname(ncid, varid, attnum, attname);
322 nc_inq_attlen(ncid, varid, attname, &lenp);
323 if (!strcmp((char*)attname,"Type")){
324 h->SourcePositionType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
325 nc_get_att(ncid, varid, attname, h->SourcePositionType);
326 }
327 else if (!strcmp((char*)attname,"Units")){
328 h->SourcePositionUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
329 nc_get_att(ncid, varid, attname, h->SourcePositionUnits);
330 }
331 }
332 }
333 else if (!strcmp((char*)varname,"ReceiverPosition")){
334 switch(ndimsp){
335 /* Many SOFA files have the "ReceiverPosition" variable with the following dimensions: nReceivers x 3 */
336 case 2:
337 /* Checks */
338 if(h->nReceivers!=-1 && (int)dimlength[dimid[dimids[0]]] != h->nReceivers) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
339 if((int)dimlength[dimid[dimids[1]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
340 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
341
342 /* Pull data */
343 h->nReceivers = (int)dimlength[dimid[dimids[0]]];
344 tmp_data = realloc1d(tmp_data, h->nReceivers*3*sizeof(double));
345 nc_get_var(ncid, varid, tmp_data);
346 h->ReceiverPosition = malloc1d(h->nReceivers*3*sizeof(float));
347 for(i=0; i<h->nReceivers*3; i++)
348 h->ReceiverPosition[i] = (float)tmp_data[i];
349 break;
350
351 /* Some SOFA files have the "ReceiverPosition" variable with the following dimensions: 1 x nReceivers x 3
352 * This is the reason for the switch case found here {2,3}, as it is not fully understood if this '1' is
353 * for the number of emmiters or listeners? - Until this is made clear, the
354 * following code will just pull the first one (i.e. nReceivers x 3). Therefore, if you have an example
355 * of a file that has "ReceiverPosition" with the dimensions: N x nReceivers x 3 (where N>1)
356 * then please send it to the developers :-) */
357 case 3:
358 /* Checks */
359 if(h->nReceivers!=-1 && (int)dimlength[dimid[dimids[0]]] != h->nReceivers) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
360 if((int)dimlength[dimid[dimids[1]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
361 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
362
363 /* Pull data */
364 h->nReceivers = (int)dimlength[dimid[dimids[0]]];
365 tmp_data = realloc1d(tmp_data, h->nReceivers*3*sizeof(double));
366 nc_get_var(ncid, varid, tmp_data);
367 h->ReceiverPosition = malloc1d(h->nReceivers*3*sizeof(float));
368 for(i=0; i<h->nReceivers*3; i++)
369 h->ReceiverPosition[i] = (float)tmp_data[i];
370 break;
371 default:
373 }
374
375 /* Pull associated attributes */
376 for(attnum=0; attnum<varnattsp; attnum++){
377 nc_inq_attname(ncid, varid, attnum, attname);
378 nc_inq_attlen(ncid, varid, attname, &lenp);
379 if (!strcmp((char*)attname,"Type")){
380 h->ReceiverPositionType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
381 nc_get_att(ncid, varid, attname, h->ReceiverPositionType);
382 }
383 else if (!strcmp((char*)attname,"Units")){
384 h->ReceiverPositionUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
385 nc_get_att(ncid, varid, attname, h->ReceiverPositionUnits);
386 }
387 }
388 }
389 else if (!strcmp((char*)varname,"ListenerPosition")){
390 /* Checks */
391 if(ndimsp!=2) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
392 if((int)dimlength[dimid[dimids[1]]] != 3 && (int)dimlength[dimid[dimids[0]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
393 //if((int)dimlength[dimid[dimids[1]]] != 1 && (int)dimlength[dimid[dimids[0]]] != 1) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
394 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
395
396 /* Pull data */
397 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
398 h->nListeners = (int)dimlength[dimid[dimids[0]]];
399 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
400 nc_get_var(ncid, varid, tmp_data);
401 h->ListenerPosition = malloc1d(tmp_size*sizeof(float));
402 for(j=0; j<(int)tmp_size; j++)
403 h->ListenerPosition[j] = (float)tmp_data[j];
404
405 /* Pull associated attributes */
406 for(attnum=0; attnum<varnattsp; attnum++){
407 nc_inq_attname(ncid, varid, attnum, attname);
408 nc_inq_attlen(ncid, varid, attname, &lenp);
409 if (!strcmp((char*)attname,"Type")){
410 h->ListenerPositionType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
411 nc_get_att(ncid, varid, attname, h->ListenerPositionType);
412 }
413 else if (!strcmp((char*)attname,"Units")){
414 h->ListenerPositionUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
415 nc_get_att(ncid, varid, attname, h->ListenerPositionUnits);
416 }
417 }
418 }
419 else if (!strcmp((char*)varname,"ListenerUp")){
420 /* Checks */
421 if(ndimsp!=2) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
422 if((int)dimlength[dimid[dimids[1]]] != 3 && (int)dimlength[dimid[dimids[0]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
423 if((int)dimlength[dimid[dimids[1]]] != 1 && (int)dimlength[dimid[dimids[0]]] != 1) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
424 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
425
426 /* Pull data */
427 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
428 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
429 nc_get_var(ncid, varid, tmp_data);
430 h->ListenerUp = malloc1d(tmp_size*sizeof(float));
431 for(j=0; j<(int)tmp_size; j++)
432 h->ListenerUp[j] = (float)tmp_data[j];
433 }
434 else if (!strcmp((char*)varname,"ListenerView")){
435 /* Checks */
436 if(ndimsp!=2) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
437 if((int)dimlength[dimid[dimids[1]]] != 3 && (int)dimlength[dimid[dimids[0]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
438 if((int)dimlength[dimid[dimids[1]]] != 1 && (int)dimlength[dimid[dimids[0]]] != 1) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
439 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
440
441 /* Pull data */
442 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
443 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
444 nc_get_var(ncid, varid, tmp_data);
445 h->ListenerView = malloc1d(tmp_size*sizeof(float));
446 for(j=0; j<(int)tmp_size; j++)
447 h->ListenerView[j] = (float)tmp_data[j];
448
449 /* Pull associated attributes */
450 for(attnum=0; attnum<varnattsp; attnum++){
451 nc_inq_attname(ncid, varid, attnum, attname);
452 nc_inq_attlen(ncid, varid, attname, &lenp);
453 if (!strcmp((char*)attname,"Type")){
454 h->ListenerViewType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
455 nc_get_att(ncid, varid, attname, h->ListenerViewType);
456 }
457 else if (!strcmp((char*)attname,"Units")){
458 h->ListenerViewUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
459 nc_get_att(ncid, varid, attname, h->ListenerViewUnits);
460 }
461 }
462 }
463 else if (!strcmp((char*)varname,"EmitterPosition")){
464 switch(ndimsp){
465 /* Many SOFA files have the "EmitterPosition" variable with the following dimensions: nEmitters x 3 */
466 case 2:
467 /* Checks */
468 if((int)dimlength[dimid[dimids[1]]] != 3 && (int)dimlength[dimid[dimids[0]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
469 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
470
471 /* Pull data */
472 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
473 tmp_size *= ndimsp==3 ? dimlength[dimid[dimids[2]]] : 1;
474 h->nEmitters = dimlength[dimid[dimids[1]]] == 3 ? (int)dimlength[dimid[dimids[0]]] : (int)dimlength[dimid[dimids[1]]];
475 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
476 nc_get_var(ncid, varid, tmp_data);
477 h->EmitterPosition = malloc1d(tmp_size*sizeof(float));
478 for(i=0; i<(int)tmp_size; i++)
479 h->EmitterPosition[i] = (float)tmp_data[i];
480 break;
481
482 /* Some SOFA files have the "EmitterPosition" variable with the following dimensions: 1 x nEmitters x 3
483 * This is the reason for the switch case found here {2,3}, as it is not fully understood if this '1' is
484 * for the number of listeners? - Until this is made clear, the
485 * following code will just pull the first one (i.e. nEmitters x 3). Therefore, if you have an example
486 * of a file that has "EmitterPosition" with the dimensions: N x nEmitters x 3 (where N>1)
487 * then please send it to the developers :-) */
488 case 3:
489 /* Checks */
490 if((int)dimlength[dimid[dimids[1]]] != 3 && (int)dimlength[dimid[dimids[0]]] != 3) { return SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED; }
491 if(typep!=NC_DOUBLE) { return SAF_SOFA_ERROR_FORMAT_UNEXPECTED; }
492
493 /* Pull data */
494 tmp_size = dimlength[dimid[dimids[0]]] * dimlength[dimid[dimids[1]]];
495 tmp_size *= ndimsp==3 ? dimlength[dimid[dimids[2]]] : 1;
496 h->nEmitters = dimlength[dimid[dimids[1]]] == 3 ? (int)dimlength[dimid[dimids[0]]] : (int)dimlength[dimid[dimids[1]]];
497 tmp_data = realloc1d(tmp_data, tmp_size*sizeof(double));
498 nc_get_var(ncid, varid, tmp_data);
499 h->EmitterPosition = malloc1d(tmp_size*sizeof(float));
500 for(i=0; i<(int)tmp_size; i++)
501 h->EmitterPosition[i] = (float)tmp_data[i];
502 break;
503 default:
505 }
506
507 /* Pull associated attributes */
508 for(attnum=0; attnum<varnattsp; attnum++){
509 nc_inq_attname(ncid, varid, attnum, attname);
510 nc_inq_attlen(ncid, varid, attname, &lenp);
511 if (!strcmp((char*)attname,"Type")){
512 h->EmitterPositionType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
513 nc_get_att(ncid, varid, attname, h->EmitterPositionType);
514 }
515 else if (!strcmp((char*)attname,"Units")){
516 h->EmitterPositionUnits = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
517 nc_get_att(ncid, varid, attname, h->EmitterPositionUnits);
518 }
519 }
520 }
521 }
522
523 /* Loop over the attributes and pull the info accordingly */
524 for(attnum=0; attnum<nattsp; attnum++){
525 nc_inq_attname(ncid, -1, attnum, attname);
526 nc_inq_attlen(ncid, -1, attname, &lenp);
527
528 if (!strcmp((char*)attname,"DataType")){
529 h->DataType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
530 nc_get_att(ncid, -1, attname, h->DataType);
531 }
532 else if (!strcmp((char*)attname,"Conventions")){
533 h->Conventions = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
534 nc_get_att(ncid, -1, attname, h->Conventions);
535 }
536 else if (!strcmp((char*)attname,"Version")){
537 h->Version = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
538 nc_get_att(ncid, NC_GLOBAL, attname, h->Version);
539 }
540 else if (!strcmp((char*)attname,"SOFAConventions")){
541 h->SOFAConventions = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
542 nc_get_att(ncid, NC_GLOBAL, attname, h->SOFAConventions);
543 }
544 else if (!strcmp((char*)attname,"SOFAConventionsVersion")){
545 h->SOFAConventionsVersion = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
546 nc_get_att(ncid, NC_GLOBAL, attname, h->SOFAConventionsVersion);
547 }
548 else if (!strcmp((char*)attname,"APIName")){
549 h->APIName = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
550 nc_get_att(ncid, NC_GLOBAL, attname, h->APIName);
551 }
552 else if (!strcmp((char*)attname,"APIVersion")){
553 h->APIVersion = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
554 nc_get_att(ncid, NC_GLOBAL, attname, h->APIVersion);
555 }
556 else if (!strcmp((char*)attname,"ApplicationName")){
557 h->ApplicationName = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
558 nc_get_att(ncid, NC_GLOBAL, attname, h->ApplicationName);
559 }
560 else if (!strcmp((char*)attname,"ApplicationVersion")){
561 h->ApplicationVersion = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
562 nc_get_att(ncid, NC_GLOBAL, attname, h->ApplicationVersion);
563 }
564 else if (!strcmp((char*)attname,"AuthorContact")){
565 h->AuthorContact = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
566 nc_get_att(ncid, NC_GLOBAL, attname, h->AuthorContact);
567 }
568 else if (!strcmp((char*)attname,"Comment")){
569 h->Comment = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
570 nc_get_att(ncid, NC_GLOBAL, attname, h->Comment);
571 }
572 else if (!strcmp((char*)attname,"History")){
573 h->History = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
574 nc_get_att(ncid, NC_GLOBAL, attname, h->History);
575 }
576 else if (!strcmp((char*)attname,"License")){
577 h->License = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
578 nc_get_att(ncid, NC_GLOBAL, attname, h->License);
579 }
580 else if (!strcmp((char*)attname,"Organization")||!strcmp((char*)attname,"Organisation")){
581 h->Organisation = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
582 nc_get_att(ncid, NC_GLOBAL, attname, h->Organisation);
583 }
584 else if (!strcmp((char*)attname,"References")){
585 h->References = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
586 nc_get_att(ncid, NC_GLOBAL, attname, h->References);
587 }
588 else if (!strcmp((char*)attname,"RoomType")){
589 h->RoomType = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
590 nc_get_att(ncid, NC_GLOBAL, attname, h->RoomType);
591 }
592 else if (!strcmp((char*)attname,"Origin")){
593 h->Origin = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
594 nc_get_att(ncid, NC_GLOBAL, attname, h->Origin);
595 }
596 else if (!strcmp((char*)attname,"DateCreated")){
597 h->DateCreated = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
598 nc_get_att(ncid, NC_GLOBAL, attname, h->DateCreated);
599 }
600 else if (!strcmp((char*)attname,"DateModified")){
601 h->DateModified = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
602 nc_get_att(ncid, NC_GLOBAL, attname, h->DateModified);
603 }
604 else if (!strcmp((char*)attname,"Title")){
605 h->Title = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
606 nc_get_att(ncid, NC_GLOBAL, attname, h->Title);
607 }
608 else if (!strcmp((char*)attname,"DatabaseName")){
609 h->DatabaseName = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
610 nc_get_att(ncid, NC_GLOBAL, attname, h->DatabaseName);
611 }
612 else if (!strcmp((char*)attname,"ListenerShortName")){
613 h->ListenerShortName = calloc1d(SAF_MAX((NC_MAX_NAME+1),lenp), sizeof(char));
614 nc_get_att(ncid, NC_GLOBAL, attname, h->ListenerShortName);
615 }
616 }
617
618 /* Close the file and clean-up */
619 nc_close(ncid);
620 free(dimid);
621 free(dimlength);
622 free(dimname);
623 free(dimids);
624 free(tmp_data);
625#else
626 saf_print_error("SAF_ENABLE_NETCDF must be defined to use this SOFA reader!");
627#endif /* SAF_ENABLE_NETCDF */
628 break;
629 }
630
631 return SAF_SOFA_OK;
632}
633
635(
637)
638{
639 /* If NetCDF was used: */
640 if (c->hLMSOFA == NULL){
641 /* Vars */
642 free(c->DataIR);
643 free(c->SourcePosition);
644 free(c->ReceiverPosition);
645 free(c->DataDelay);
646 free(c->ListenerPosition);
647 free(c->ListenerView);
648 free(c->ListenerUp);
649 free(c->EmitterPosition);
650
651 /* Var Atts */
652 free(c->ListenerPositionType);
653 free(c->ListenerPositionUnits);
654 free(c->ListenerViewType);
655 free(c->ListenerViewUnits);
656 free(c->ReceiverPositionType);
657 free(c->ReceiverPositionUnits);
658 free(c->SourcePositionType);
659 free(c->SourcePositionUnits);
660 free(c->EmitterPositionType);
661 free(c->EmitterPositionUnits);
662 free(c->DataSamplingRateUnits);
663
664 /* Global Atts */
665 free(c->Conventions);
666 free(c->Version);
667 free(c->SOFAConventions);
668 free(c->SOFAConventionsVersion);
669 free(c->APIName);
670 free(c->APIVersion);
671 free(c->ApplicationName);
672 free(c->ApplicationVersion);
673 free(c->AuthorContact);
674 free(c->Comment);
675 free(c->DataType);
676 free(c->History);
677 free(c->License);
678 free(c->Organisation);
679 free(c->References);
680 free(c->RoomType);
681 free(c->Origin);
682 free(c->DateCreated);
683 free(c->DateModified);
684 free(c->Title);
685 free(c->DatabaseName);
686 free(c->ListenerShortName);
687 }
688 else
689 mysofa_free((MYSOFA_HRTF*)c->hLMSOFA);
690}
691
692#endif /* SAF_ENABLE_SOFA_READER_MODULE */
void saf_sofa_close(saf_sofa_container *c)
Frees all SOFA data in a sofa_container.
SAF_SOFA_READER_OPTIONS
SOFA file reader options.
SAF_SOFA_ERROR_CODES saf_sofa_open(saf_sofa_container *h, char *sofa_filepath, SAF_SOFA_READER_OPTIONS option)
Fills a 'sofa_container' with data found in a SOFA file (GeneralFIR or SimpleFreeFieldHRIR),...
SAF_SOFA_ERROR_CODES
SOFA loader error codes.
@ SAF_SOFA_READER_OPTION_DEFAULT
The default option is SAF_SOFA_READER_OPTION_LIBMYSOFA.
@ SAF_SOFA_READER_OPTION_NETCDF
If SAF_ENABLE_NETCDF is defined, then an alternative SOFA reader may be used.
@ SAF_SOFA_READER_OPTION_LIBMYSOFA
This option uses the libmysofa library to load SOFA files, which is adopted from: https://github....
@ SAF_SOFA_ERROR_INVALID_FILE_OR_FILE_PATH
Not a SOFA file, or no such file was found in the specified location.
@ SAF_SOFA_OK
None of the error checks failed.
@ SAF_SOFA_ERROR_FORMAT_UNEXPECTED
The data-type of the SOFA data was not as expected.
@ SAF_SOFA_ERROR_DIMENSIONS_UNEXPECTED
Dimensions of the SOFA data were not as expected.
#define saf_print_error(message)
Macro to print a error message along with the filename and line number.
#define SAF_MAX(a, b)
Returns the maximum of the two values.
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void * calloc1d(size_t dim1, size_t data_size)
1-D calloc (same as calloc, but with error checking)
Definition md_malloc.c:69
void * realloc1d(void *ptr, size_t dim1_data_size)
1-D realloc (same as realloc, but with error checking)
Definition md_malloc.c:79
Include header for SAF externals.
Main header for the sofa reader module (SAF_SOFA_READER_MODULE)
Main header for the utilities module (SAF_UTILITIES_MODULE)
SOFA container struct comprising all possible data that can be extracted from SOFA 1....
float * DataDelay
Delay in samples; nReceivers x 1.
char * Organisation
Organisation reponsible for the file.
int DataLengthIR
Length of the IRs, in samples.
char * Origin
Where this file came from.
char * SOFAConventionsVersion
SOFA convention number.
int nSources
Number of source/measurement positions.
char * RoomType
Room type (free field etc.)
char * DataSamplingRateUnits
{'hertz'}
char * EmitterPositionUnits
{'degree, degree, metre'|'metre'}
char * ReceiverPositionUnits
{'degree, degree, metre'|'metre'}
float * SourcePosition
Source positions (refer to SourcePositionType & SourcePositionUnits for the convention and units); FL...
int nListeners
Number of listener positions.
char * DateCreated
Date file was created.
char * References
References.
float * ReceiverPosition
Receiver positions (refer to ReceiverPositionType & ReceiverPositionUnits for the convention and unit...
char * SourcePositionUnits
{'degree, degree, metre'|'metre'}
char * Conventions
{'SOFA'}
char * DataType
{'FIR'|'TF'}
char * SourcePositionType
{'cartesian'|'spherical'}
char * ReceiverPositionType
{'cartesian'|'spherical'}
char * EmitterPositionType
{'cartesian'|'spherical'}
char * ListenerPositionType
{'cartesian'|'spherical'}
char * SOFAConventions
{'GeneralFIR'|'GeneralTF'| 'SimpleFreeFieldHRIR'}
char * ListenerPositionUnits
{'degree, degree, metre'|'metre'}
float * EmitterPosition
Positions of acoustic excitation used for the measurement (refer to EmitterPositionType & EmitterPosi...
char * Title
Title of file.
char * Comment
File comments.
char * APIVersion
API version.
float * ListenerPosition
Listener position (The object incorporating all receivers; refer to ListenerPositionType & ListenerPo...
char * ApplicationVersion
Ver.
char * DatabaseName
Name of database this file belongs to.
float * ListenerView
Vector pointing forwards from the listener position (Cartesian); 3 x 1.
float DataSamplingRate
Sampling rate used to measure the IRs.
char * History
History information.
int nEmitters
Number of emitter positions.
char * Version
Version number.
char * APIName
API name.
char * ApplicationName
Name of Application that created file.
char * ListenerViewUnits
{'degree, degree, metre'|'metre'}
void * hLMSOFA
libmysofa handle
char * ListenerShortName
Name of the listener/dummyhead/mic etc.
int nReceivers
Number of ears/number of mics etc.
char * DateModified
Date file was modified.
char * ListenerViewType
{'cartesian'|'spherical'}
float * ListenerUp
Vector pointing upwards from the listener position (Cartesian); 1 x 3 or FLAT: nListeners x 3
char * AuthorContact
Contact information.
char * License
License under which file is provided.
float * DataIR
The impulse response (IR) Data; FLAT:nSources x nReceivers x DataLengthIR.