A minimal, portable, header-only C library for a dead-simple audio format. No magic numbers, no validation overhead, no dependencies. You own the data, you own the responsibility.
Most audio formats carry significant overhead — chunk negotiation, codec detection, metadata sprawl. SAF doesn't. The format assumes the user knows what they're doing. If you give it a file, it trusts you. This makes it fast, simple, and easy to embed anywhere.
| Offset | Size | Type | Description |
|---|---|---|---|
| 0 | 4 | uint32 | Sample rate (little-endian) |
| 4 | 1 | uint8 | Channel count |
| 5 | 1 | uint8 | Bit depth |
| 6 | ... | raw | PCM samples (interleaved) |
Total header size: 6 bytes.
Samples are raw interleaved PCM. Stereo audio is stored LRLRLR... No padding, no alignment, no checksums. File length determines data length.
Supported bit depths: 8, 16, 32 (determined by your data — SAF doesn't enforce this).
SAF is header-only. Copy saf.h into your project.
In exactly one.c file, define SAF_IMPL before including:
#defineSAF_IMPL#include"saf.h"All other files just include normally:
#include"saf.h"SAF_Headerheader= {
.sample_rate=44100,
.channels=1,
.bit_depth=16,
};
int16_t*samples=/* your audio data */;
size_tcount=/* number of samples */;
SAF_RetCoderet=saf_write("audio.saf", header, samples, count);
if (ret!=SAF_OK)
fprintf(stderr, "Error: %s\n", saf_strerror(ret));SAF_Headerheader;
void*samples=NULL;
size_tsample_count=0;
SAF_RetCoderet=saf_read("audio.saf", &header, &samples, &sample_count);
if (ret!=SAF_OK)
fprintf(stderr, "Error: %s\n", saf_strerror(ret));
int16_t*pcm= (int16_t*)samples;
/* use pcm... */saf_free(&samples);The library allocates the sample buffer. Always free with saf_free — never free() directly, in case the implementation changes.
// Write audio data to a .saf fileSAF_RetCodesaf_write(constchar*filename, SAF_Headerheader, constvoid*samples, size_tsample_count);
// Read a .saf file — allocates sample buffer, caller must saf_free()SAF_RetCodesaf_read(constchar*filename, SAF_Header*header, void**samples, size_t*sample_count);
// Free a buffer allocated by saf_read — nulls the pointervoidsaf_free(void**samples);
// Return human-readable string for a return codeconstchar*saf_strerror(SAF_RetCodecode);| Code | Meaning |
|---|---|
SAF_OK | Success |
SAF_ERR_OPEN | Failed to open file |
SAF_ERR_WRITE | Failed to write file |
SAF_ERR_READ | Failed to read file |
SAF_ERR_MEM | Memory allocation failed |
make # build and run test
make clean # clean artifactsRequires GCC and libm.
SAF manually serializes header fields byte-by-byte, so it is endian-safe and makes no assumptions about struct layout or compiler padding. Tested on Linux (GCC, Clang). Should work on any C99-compliant compiler.
MIT — see LICENSE.