Skip to content

UniquePython/saf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SAF — Simple Audio Format

CI

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.

Philosophy

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.

Format Specification

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).

Usage

SAF is header-only. Copy saf.h into your project.

In exactly one .c file, define SAF_IMPL before including:

#define SAF_IMPL
#include "saf.h"

All other files just include normally:

#include "saf.h"

Writing

SAF_Header header = {
    .sample_rate = 44100,
    .channels    = 1,
    .bit_depth   = 16,
};

int16_t *samples = /* your audio data */;
size_t   count   = /* number of samples */;

SAF_RetCode ret = saf_write("audio.saf", header, samples, count);
if (ret != SAF_OK)
    fprintf(stderr, "Error: %s\n", saf_strerror(ret));

Reading

SAF_Header header;
void      *samples     = NULL;
size_t     sample_count = 0;

SAF_RetCode ret = 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.

API

// Write audio data to a .saf file
SAF_RetCode saf_write(const char *filename, SAF_Header header, const void *samples, size_t sample_count);

// Read a .saf file — allocates sample buffer, caller must saf_free()
SAF_RetCode saf_read(const char *filename, SAF_Header *header, void **samples, size_t *sample_count);

// Free a buffer allocated by saf_read — nulls the pointer
void saf_free(void **samples);

// Return human-readable string for a return code
const char *saf_strerror(SAF_RetCode code);

Return Codes

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

Building the Test

make        # build and run test
make clean  # clean artifacts

Requires GCC and libm.

Portability

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.

License

MIT — see LICENSE.

About

Simple Audio Format — minimal header-only C library

Topics

Resources

License

Stars

Watchers

Forks

Contributors