XDF_WRITE(3)

✨ NAME ✨

xdf_write β€” write samples to a xDF file πŸ’Ύβœ¨ (yes bestie, this is the one that actually puts your data in the file)


🫢 SYNOPSIS 🫢

#include <xdfio.h>

ssize_t xdf_write(struct xdf* xdf, size_t ns, ...);

πŸ’… DESCRIPTION πŸ’…

okay so xdf_write() writes ns samples into the xDF file that xdf points at. For this to even be a thing, that file needs to have been opened with mode XDF_WRITE, and xdf_prepare_arrays(3) needs to have been successfully called on it. If not? she's gonna fail. no cap. 😭

The data you're adding has to live in arrays, and you hand them over as pointers in the variable list of arguments of the function. The function expects the same number of arrays as were specified by the previous call to xdf_define_arrays(3). And how the data is internally organised inside those arrays? That should have been set up earlier with calls to xdf_set_chconf(3). πŸ’–

Also super important, bestie, please note: none of the arrays should overlap. like at all. fr fr. πŸ™ˆ


🌸 RETURN VALUE 🌸

On success, the function returns the number of samples successfully added to the xDF file. Otherwise -1 is returned and errno is set appropriately. that's it, that's the vibe. πŸ’€


πŸ”₯ ERRORS πŸ”₯

  • EINVAL β€” xdf is NULL
  • EPERM β€” No successful call to xdf_prepare_transfer(3) has been done on xdf, or it has been opened using the mode XDF_READ. (reading things you're supposed to write? not the move. 😀)
  • EFBIG β€” An attempt was made to write a file that exceeds the implementation-defined maximum file size or the process's file size limit, or to write at a position past the maximum allowed offset.
  • EINTR β€” The call was interrupted by a signal before any data was written; see signal(7).
  • EIO β€” A low-level I/O error occurred while modifying the inode.
  • ENOSPC β€” The device containing the xDF file has no room for the data. disk said "no". 😭
  • ESTALE β€” Stale file handle. This error can occur for NFS and for other file systems.

πŸ‘‘ PERFORMANCE CONSIDERATION πŸ‘‘

By design of the library, a call to xdf_write() is "almost" ensured to be executed in a linear time β€” meaning that, given a fixed configuration of an xDF file, for the same number of samples to be passed, a call to xdf_write will almost always take the same time to complete. And that time increases linearly with the number of samples. This guarantee is particularly useful for realtime processing of data, because storing the data will impact the main loop in a predictable way. predictably iconic, honestly. ⭐

How does she do it? double buffering the data for writing, bestie. A front and a back buffer are available: the front buffer gets filled with the incoming data, and gets swapped with the back buffer when it's full. That swap signals a background thread to convert, reorganise, scale and save to the disk the data contained in the full buffer, making it afterwards available for the next swap. πŸ¦‹

This approach ensures a linear calltime of xdf_write(), providing that the I/O subsystem is not saturated neither all processing units (cores or processors) β€” i.e. the application is neither I/O bound nor CPU bound. (so like. don't max out your whole machine and then act surprised. πŸ’…)


πŸŽ€ DATA SAFETY πŸŽ€

The library makes sure that data written to xDF files is safely stored on stable storage on a regular basis, but because of double buffering, there is a risk to lose data in case of problem. 😭 However, the design of the xdf_write() ensures that if a problem occurs (no more disk space, power supply cut), at most two records of data plus the size of the chunks of data supplied to the function will be lost.

As an example, assuming you record a xDF file at 256Hz using records of 256 samples and you feed xdf_write() with chunks of 8 samples, you are ensured to receive notification of failure after at most 520 samples, corresponding to a loss of at most a little more than 2s of data in case of problems. math is mathing and honestly we love a bounded worst case. 🫢


🌈 EXAMPLE 🌈

/* Assume xdf references a xDF file opened for writing whose
channels source their data in 2 arrays of float whose strides
are the length of respectively 4 and 6 float values,
i.e. 16 and 24 bytes (in most platforms)*/
#define NS    3
float array1[NS][4], array2[NS][6];
unsigned int strides = {4*sizeof(float), 6*sizeof(float)};
unsigned int i;

xdf_define_arrays(xdf, 2, strides);
if (xdf_prepare_transfer(xdf))
     return 1;

for (i=0; i<45; i+=NS) {
     /* Update the values contained in array1 and array2*/
     ...

     /* Write the values to the file */
        if (xdf_write(xdf, NS, array1, array2))
          return 1;
}

xdf_close(xdf);

πŸ’œ SEE ALSO πŸ’œ

xdf_set_chconf(3), xdf_define_arrays(3), xdf_prepare_transfer(3)


and that's the whole page, bestie β€” brought to you by EPFL, 2010, from the xdffileio library manual, filed under XDF_WRITE(3). go write some samples. periodt. πŸ’…βœ¨