Usage

# Reading ambix files

When opening a file for read, the ambix_info_t struct should be set to 0. On successful open, all fields are filled by the library. If the open fails, the state of the ambix_info_t fields is undefined.

The only exception to this is, if you want to force the ambix file to be read as either "BASIC" or "EXTENDED", e.g. because you don't want to care about adaptor matrices or because you do. In this case you must set the fileformat field the requested format.

## Reading BASIC ambix files

You can read any ambix file as "BASIC" by setting the 'fileformat' member of the ambix_info_t struct to AMBIX_BASIC prior to opening the file. This will automatically do any conversion needed, by pre-multiplying the raw ambisonics data with an embedded adaptor matrix.

Real "BASIC" files lack extra audio channels. However, when opening a file that is not a "BASIC" ambix file (e.g. an "EXTENDED" ambix file) as a "BASIC" one, by forcing the 'fileformat' member to 'AMBIX_BASIC', extra channels might be readable.

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t));
ambix->fileformat = AMBIX_BASIC;
ambix = ambix_open("ambixfile.caf", AMBIX_READ, info);
if(ambix) {
uint64_t frames = info->frames;
uint64_t blocksize = 1024;
float32_t*ambidata = calloc(info->ambichannels * blocksize, sizeof(float32_t));
float32_t*extradata = calloc(info->extrachannels * blocksize, sizeof(float32_t));
while(frames>blocksize) {
uint64_t blocks = ambix_readf_float32(ambix, ambidata, extradata, blocksize);
// process blocks frames of interleaved ambidata and interleaved extradata
// ...
frames-=blocks;
}
ambix_readf_float32(ambix, ambidata, extradata, frames);
// process last block of interleaved ambidata and interleaved extradata
// ...
ambix_close(ambix);
free(ambidata);
free(extradata);
}
free(info);
float32_t
float float32_t
Definition ambix.h:41
AMBIX_BASIC
@ AMBIX_BASIC
Definition ambix.h:136
AMBIX_READ
@ AMBIX_READ
Definition ambix.h:123
ambix_t
struct ambix_t_struct ambix_t
Definition ambix.h:93
ambix_readf_float32
AMBIX_API int64_t ambix_readf_float32(ambix_t *ambix, float32_t *ambidata, float32_t *otherdata, int64_t frames)
Read samples (as single precision floating point values) from the ambix file.
ambix_close
AMBIX_API ambix_err_t ambix_close(ambix_t *ambix)
Close an ambix handle.
ambix_open
AMBIX_API ambix_t * ambix_open(const char *path, const ambix_filemode_t mode, ambix_info_t *ambixinfo)
Open an ambix file.
ambix_info_t
Definition ambix.h:207
ambix_info_t::frames
uint64_t frames
Definition ambix.h:209
ambix_info_t::ambichannels
uint32_t ambichannels
Definition ambix.h:246
ambix_info_t::extrachannels
uint32_t extrachannels
Definition ambix.h:233

## Reading EXTENDED ambix files

You can read an ambix file as "EXTENDED" by setting the 'fileformat' member of the ambix_info_t struct to AMBIX_EXTENDED prior to opening the file. You will then have to retrieve the adaptor matrix from the file, in order to be able to reconstruct the full ambisonics set. You can also analyze the matrix to make educated guesses about the original channel layout.

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t));
/* setting the fileformat to AMBIX_EXTENDED forces the ambi data
be delivered as stored in the file
/
ambix->fileformat = AMBIX_EXTENDED;
ambix = ambix_open("ambixfile.caf", AMBIX_READ, info);
if(ambix) {
uint64_t frames = info->frames;
uint64_t blocksize = 1024;
float32_t*ambidata = calloc(info->ambichannels * blocksize, sizeof(float32_t));
float32_t*extradata = calloc(info->extrachannels * blocksize, sizeof(float32_t));
const ambix_matrix_t*adaptormatrix=ambix_get_adaptormatrix(ambix);
while(frames>blocksize) {
uint64_t blocks = ambix_readf_float32(ambix, ambidata, extradata, blocksize);
// process blocks frames of interleaved ambidata and interleaved extradata,
// using the adaptormatrix
// ...
frames-=blocks;
}
ambix_readf_float32(ambix, ambidata, extradata, frames);
// process last block of interleaved ambidata and interleaved extradata
// using the adaptormatrix
// ...
ambix_close(ambix);
free(ambidata);
free(extradata);
}
free(info);

## Reading any ambix files

If you do not specify the format prior to opening, you can query the format of the file from the ambix_info_t struct.

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t)); /* initialize the format field (among others) to 0 */
ambix = ambix_open("ambixfile.caf", AMBIX_READ, info);
if(ambix) {
switch(ambix->fileformat) {
case(AMBIX_BASIC):
printf("this file is ambix basic\n");
break;
case(AMBIX_EXTENDED):
printf("this file is ambix extended\n");
break;
case(AMBIX_NONE):
printf("this file is not an ambix file\n");
break;
default:
printf("this file is of unknown format...\n");
}
ambix_close(ambix);
}
free(info);
AMBIX_NONE
@ AMBIX_NONE
Definition ambix.h:134
AMBIX_EXTENDED
@ AMBIX_EXTENDED
Definition ambix.h:138

# Writing ambix files

To write data to an ambix file, you have to open it with the AMBIX_WRITE flag. You also need to specify some global properties of the output data, namely the samplerate and the sampleformat, as well as the number of ambisonics channels and the number of extra channels that are physically stored on the disk.

## Writing BASIC ambix files

You can write "BASIC" ambix files by setting the 'fileformat' member of the ambix_info_t struct to AMBIX_BASIC prior to opening the file.

You will need to provide a full set of ambisonics channels when writing data to the file, and must not set an adaptor matrix (see also Writing EXTENDED ambix files using the BASIC interface). A full set of ambisonics must always satisfy the formula \(channels=(order_{ambi}+1)^2\).

You cannot write extra audio channels into a "BASIC" ambix file.

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t));
/* need to specify samplerate and sampleformat */
ambix->samplerate = 44100;
ambix->sampleformat = AMBIX_SAMPLEFORMAT_PCM24;
ambix->fileformat = AMBIX_BASIC;
ambix->ambichannels = 16; /* 16 channels means 3rd order ambisonics, according to L=(2N+1)^2 */
ambix = ambix_open("ambixfile.caf", AMBIX_WRITE, info);
if(ambix) {
uint64_t frames = info->frames;
uint64_t blocksize = 1024;
uint64_t block;
float32_t*ambidata = calloc(info->ambichannels * blocksize, sizeof(float32_t));
while(haveData) {
// acquire blocksize samples of a full set of 3rd order ambisonics data (16 channels)
// into ambidata (interleaved)
// ...
block = ambix_writef_float32(ambix, ambidata, NULL, blocksize);
}
ambix_close(ambix);
free(ambidata);
}
free(info);
AMBIX_SAMPLEFORMAT_PCM24
@ AMBIX_SAMPLEFORMAT_PCM24
Definition ambix.h:148
AMBIX_WRITE
@ AMBIX_WRITE
Definition ambix.h:125
ambix_writef_float32
AMBIX_API int64_t ambix_writef_float32(ambix_t *ambix, const float32_t *ambidata, const float32_t *otherdata, int64_t frames)
Write (32bit floating point) samples to the ambix file.

## Writing EXTENDED ambix files

You can write "EXTENDED" ambix files by setting the 'fileformat' member of the ambix_info_t struct to AMBIX_EXTENDED prior to opening the file.

You MUST set an adaptormatrix (to convert the reduced set to a full ambisonics set) using ambix_set_adaptormatrix(). It gets written to disk prior to writing any samples to the file (or closing the ambix file). It is an error to call ambix_set_adaptormatrix() after starting to write samples.

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t));
/* need to specify samplerate and sampleformat */
ambix->samplerate = 44100;
ambix->sampleformat = AMBIX_SAMPLEFORMAT_PCM24;
ambix->fileformat = AMBIX_EXTENDED;
ambix->ambichannels = 8; /* a reduced ambisonics set */
ambix->extrachannels = 1; /* an extrachannel, e.g. click-track */
ambix = ambix_open("ambixfile.caf", AMBIX_WRITE, info);
if(ambix) {
uint64_t frames = info->frames;
uint64_t blocksize = 1024;
uint64_t block;
float32_t*ambidata = calloc(info->ambichannels * blocksize, sizeof(float32_t));
float32_t*extradata = calloc(info->extrachannels * blocksize, sizeof(float32_t));
/* create an adaptormatrix: */
ambix_matrix_t adaptormatrix = {0, 0, NULL};
ambix_matrix_init(16, 8, &adaptormatrix);
// fill the adaptormatrix, that expands our 8 channels to a full 3D 3rd-order set (16 channels)
// ...
ambix_set_adapatormatrix(ambix, &adaptormatrix);
ambix_write_header(ambix);
while(haveData) {
// acquire blocksize samples of a full set of reduced ambisonics data (8 channels)
// into ambidata (interleaved), and a some (1) extra channels
// ...
block = ambix_writef_float32(ambix, ambidata, extradata, blocksize);
}
ambix_close(ambix);
ambix_matrix_deinit(&adaptormatrix);
free(ambidata);
free(extradata);
}
free(info);
ambix_matrix_init
AMBIX_API ambix_matrix_t * ambix_matrix_init(uint32_t rows, uint32_t cols, ambix_matrix_t *mtx)
Initialize a matrix.
ambix_matrix_deinit
AMBIX_API void ambix_matrix_deinit(ambix_matrix_t *mtx)
De-initialize a matrix.
ambix_matrix_t
Definition ambix.h:195

## Writing EXTENDED ambix files using the BASIC interface

Finally, you can create "EXTENDED" ambix files from a full set of ambisonics channels and an adaptor matrix. This can be useful if you have a setup that works with full ambisonics sets (e.g. a DAW project) and you want to create a size-optimized ambix file that only stores a reduced set.

This can be achieved by setting the 'fileformat' member of the ambix_info_t struct to AMBIX_BASIC (because you will provide the full ambisonics set as if the file were opened in "BASIC" mode) and the 'ambichannels' member to the (reduced) number of ambisonics channel as will be written to the disk, and then setting an adaptor matrix ambix_set_adaptormatrix(), that will convert the reduced set back to the full set. libambix will internally reduce the full ambisonics set (as passed to ambix_writef()) using the (pseudo) inverse of the adaptor-matrix.

Note
  You must ensure yourself that the adaptor matrix is inversible.

The adaptor matrix gets written to disk prior to writing any samples to the file (or closing the ambix file). It is an error to call ambix_set_adaptormatrix() after starting to write samples.

Note
  If you pass an adaptor matrix that expands a reduced set to full ambisonics (e.g. converting from 1st order horizontal-only ambisonics set to a 2nd order fully periphonic set) the reconstructed ambisonics channels (when reading the ambix file later), might be different from the ambisonics channels you passed in when writing the file (e.g. channels that contain 3rd-order and/or Z-axis components will be muted).

ambix_t*ambix = NULL;
ambix_info_t*info = calloc(1, sizeof(ambix_info_t));
/* need to specify samplerate and sampleformat */
info->samplerate = 44100;
info->sampleformat = AMBIX_SAMPLEFORMAT_PCM24;
info->fileformat = AMBIX_BASIC;
info->ambichannels = 7; /* 3rd-order horizontal-only(!) */
ambix = ambix_open("ambixfile.caf", AMBIX_WRITE, info);
if(ambix) {
uint64_t fullambichannels = 16;
uint64_t frames = info->frames;
uint64_t blocksize = 1024;
uint64_t block;
float32_t*ambidata = calloc(fullambichannels * blocksize, sizeof(float32_t));
/* on disk, the data is stored as 3rd-order horizontal-only Furse-Malham
set (7 channels); we need to provide an adaptor matrix that converts
from the 7 FuMa-channels to the 16 AMBIX channels
/
ambix_matrix_t*mtx = NULL;
mtx = ambix_matrix_init(fullambichannels, info->ambichannels, mtx);
mtx = ambix_matrix_fill(mtx, ABIX_MATRIX_FUMA); /* fuma->ambix matrix [16x7] */
ambix_set_adaptormatrix(ambix, mtx);
ambix_matrix_destroy(mtx);
while(haveData) {
// acquire blocksize samples of a full set of 3rd order ambisonics data (16 channels)
// into ambidata (interleaved)
// ...
block = ambix_write_float32(ambix, ambidata, NULL, blocksize);
}
ambix_close(ambix);
free(ambidata);
}
free(info);
ambix_matrix_destroy
AMBIX_API void ambix_matrix_destroy(ambix_matrix_t *mtx)
Destroy a matrix.
ambix_set_adaptormatrix
AMBIX_API ambix_err_t ambix_set_adaptormatrix(ambix_t *ambix, const ambix_matrix_t *matrix)
Set a matrix to be pre-multiplied.
ambix_info_t::fileformat
ambix_fileformat_t fileformat
Definition ambix.h:228
ambix_info_t::samplerate
double samplerate
Definition ambix.h:211
ambix_info_t::sampleformat
ambix_sampleformat_t sampleformat
Definition ambix.h:213