Skip to content

Commit e73cce8

Browse files
committed
Add documentation
1 parent 97cf5f6 commit e73cce8

File tree

3 files changed

+136
-54
lines changed

3 files changed

+136
-54
lines changed

Makefile

Lines changed: 12 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,24 @@
1-
# Compiler and flags
2-
CC := gcc
3-
CFLAGS := -Wall -Wextra -Iinclude
4-
DEBUGFLAGS := -g
5-
RELEASEFLAGS := -O2
1+
CC := gcc
2+
CFLAGS := -Wall -Wextra -std=c99
3+
LIBS := -lm
64

7-
# Directories
8-
SRC_DIR := src
9-
INCLUDE_DIR := include
10-
BUILD_DIR := build
115
BIN_DIR := bin
6+
TARGET := $(BIN_DIR)/test
127

13-
# Executable name
14-
TARGET := saf
8+
.PHONY: all test clean
159

16-
# Automatically detect source and object files
17-
SRCS := $(wildcard $(SRC_DIR)/*.c)
18-
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS))
10+
all: test
1911

20-
# Default target: release build
21-
.PHONY: all
22-
all: release
12+
test: dirs $(TARGET)
13+
./$(TARGET)
2314

24-
# -----------------------------
25-
# Release build
26-
# -----------------------------
27-
.PHONY: release
28-
release: CFLAGS += $(RELEASEFLAGS)
29-
release: dirs $(BIN_DIR)/$(TARGET)
15+
$(TARGET): test.c saf.h
16+
$(CC) $(CFLAGS) test.c -o $@ $(LIBS)
3017

31-
# -----------------------------
32-
# Debug build
33-
# -----------------------------
34-
.PHONY: debug
35-
debug: CFLAGS += $(DEBUGFLAGS)
36-
debug: dirs $(BIN_DIR)/$(TARGET)
37-
38-
# -----------------------------
39-
# Build executable
40-
# -----------------------------
41-
$(BIN_DIR)/$(TARGET): $(OBJS)
42-
$(CC) $(OBJS) -o $@
43-
44-
# -----------------------------
45-
# Build object files with dependency tracking
46-
# -----------------------------
47-
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
48-
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
49-
50-
# Include dependency files
51-
-include $(OBJS:.o=.d)
52-
53-
# -----------------------------
54-
# Create necessary directories
55-
# -----------------------------
5618
.PHONY: dirs
5719
dirs:
58-
@mkdir -p $(BUILD_DIR) $(BIN_DIR)
20+
@mkdir -p $(BIN_DIR)
5921

60-
# -----------------------------
61-
# Clean build artifacts
62-
# -----------------------------
6322
.PHONY: clean
6423
clean:
65-
$(RM) -r $(BUILD_DIR)/* $(BIN_DIR)/*
24+
$(RM) -r $(BIN_DIR)

README.md

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,118 @@
1-
# saf
1+
# SAF — Simple Audio Format
2+
3+
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.
4+
5+
## Philosophy
6+
7+
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.
8+
9+
## Format Specification
10+
11+
| Offset | Size | Type | Description |
12+
| ------ | ---- | ------ | --------------------------- |
13+
| 0 | 4 | uint32 | Sample rate (little-endian) |
14+
| 4 | 1 | uint8 | Channel count |
15+
| 5 | 1 | uint8 | Bit depth |
16+
| 6 | ... | raw | PCM samples (interleaved) |
17+
18+
**Total header size: 6 bytes.**
19+
20+
Samples are raw interleaved PCM. Stereo audio is stored LRLRLR... No padding, no alignment, no checksums. File length determines data length.
21+
22+
Supported bit depths: 8, 16, 32 (determined by your data — SAF doesn't enforce this).
23+
24+
## Usage
25+
26+
SAF is header-only. Copy `saf.h` into your project.
27+
28+
In **exactly one** `.c` file, define `SAF_IMPL` before including:
29+
30+
```c
31+
#define SAF_IMPL
32+
#include "saf.h"
33+
```
34+
35+
All other files just include normally:
36+
37+
```c
38+
#include "saf.h"
39+
```
40+
41+
### Writing
42+
43+
```c
44+
SAF_Header header = {
45+
.sample_rate = 44100,
46+
.channels = 1,
47+
.bit_depth = 16,
48+
};
49+
50+
int16_t *samples = /* your audio data */;
51+
size_t count = /* number of samples */;
52+
53+
SAF_RetCode ret = saf_write("audio.saf", header, samples, count);
54+
if (ret != SAF_OK)
55+
fprintf(stderr, "Error: %s\n", saf_strerror(ret));
56+
```
57+
58+
### Reading
59+
60+
```c
61+
SAF_Header header;
62+
void *samples = NULL;
63+
size_t sample_count = 0;
64+
65+
SAF_RetCode ret = saf_read("audio.saf", &header, &samples, &sample_count);
66+
if (ret != SAF_OK)
67+
fprintf(stderr, "Error: %s\n", saf_strerror(ret));
68+
69+
int16_t *pcm = (int16_t *)samples;
70+
/* use pcm... */
71+
72+
saf_free(&samples);
73+
```
74+
75+
The library allocates the sample buffer. Always free with `saf_free` — never `free()` directly, in case the implementation changes.
76+
77+
## API
78+
79+
```c
80+
// Write audio data to a .saf file
81+
SAF_RetCode saf_write(const char *filename, SAF_Header header, const void *samples, size_t sample_count);
82+
83+
// Read a .saf file — allocates sample buffer, caller must saf_free()
84+
SAF_RetCode saf_read(const char *filename, SAF_Header *header, void **samples, size_t *sample_count);
85+
86+
// Free a buffer allocated by saf_read — nulls the pointer
87+
void saf_free(void **samples);
88+
89+
// Return human-readable string for a return code
90+
const char *saf_strerror(SAF_RetCode code);
91+
```
92+
93+
### Return Codes
94+
95+
| Code | Meaning |
96+
| --------------- | ------------------------ |
97+
| `SAF_OK` | Success |
98+
| `SAF_ERR_OPEN` | Failed to open file |
99+
| `SAF_ERR_WRITE` | Failed to write file |
100+
| `SAF_ERR_READ` | Failed to read file |
101+
| `SAF_ERR_MEM` | Memory allocation failed |
102+
103+
## Building the Test
104+
105+
```bash
106+
make # build and run test
107+
make clean # clean artifacts
108+
```
109+
110+
Requires GCC and `libm`.
111+
112+
## Portability
113+
114+
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.
115+
116+
## License
117+
118+
MIT — see [LICENSE](LICENSE).

saf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
#include <stdio.h>
66
#include <stdlib.h>
77

8+
#define SAF_VERSION_MAJOR 1
9+
#define SAF_VERSION_MINOR 0
10+
#define SAF_VERSION_PATCH 0
11+
#define SAF_VERSION "1.0.0"
12+
813
typedef struct saf_header
914
{
1015
uint32_t sample_rate;
@@ -24,6 +29,7 @@ typedef enum saf_return_codes
2429
SAF_RetCode saf_write(const char *filename, SAF_Header header, const void *samples, size_t sample_count);
2530
SAF_RetCode saf_read(const char *filename, SAF_Header *header, void **samples, size_t *sample_count);
2631
void saf_free(void **samples);
32+
const char *saf_strerror(SAF_RetCode code);
2733

2834
#ifdef SAF_IMPL
2935

0 commit comments

Comments
 (0)