flac_encode.h
Writing FLAC.
#include <sonore/flac_encode.h>- 1type
- 7other declarations
On this page
Design notes
We already decode FLAC ourselves; this is the other half. It is worth owning for the same reason: the format is simple enough to be provable, and a lossless encoder can be checked the only way that counts: encode, decode, compare SAMPLE FOR SAMPLE, and then have a completely independent decoder (ffmpeg) do the same.
Fixed predictors only, no LPC. The four fixed predictors are successive differences, they cost nothing to compute, and on real material they get most of what LPC would: an encoder that spends ten times the CPU for another five percent is the wrong trade for a plugin exporting a render. The output is fully compliant either way: a decoder cannot tell which predictors an encoder chose to use.
Offline: allocates and writes files.
Types 1
MSB-first bit writer. FLAC is bit-packed, and a writer that flushes a partial byte at the wrong moment produces a file that decodes as garbage from that point on.
Bits are gathered in a word and go out a byte at a time as they fill one. This wrote every bit on its own -- a shift, a count and a branch each -- and a four-minute render is half a billion bits. The bytes are the same: a write still puts the low bits bits of its value down, highest first, after everything written before it.
Functions 7
- fn
inline uint8_t crc8(const uint8_t* data, size_t size)FLAC's CRC-8 (x^8 + x^2 + x + 1) over the frame header. A decoder that verifies it (ffmpeg does) rejects the frame outright if this is wrong, so it is not optional however tempting a zero looks.flacenc::crc8 · flac_encode.h:125 - fn
inline uint16_t crc16(const uint8_t* data, size_t size)FLAC's CRC-16 (x^16 + x^15 + x^2 + 1) over the whole frame. By table: a render's frames are every byte of the file, and eight conditional shifts a byte was a quarter of what writing a FLAC cost. Each entry is what the eight steps do to a byte that has reached the top of the register; the steps are linear, so a lookup a byte gives the same CRC for every input.flacenc::crc16 · flac_encode.h:139 - fn
inline void writeUtf8(BitWriter& writer, uint64_t value)The UTF-8-style coded number FLAC uses for frame indices.flacenc::writeUtf8 · flac_encode.h:159 - fn
inline uint64_t fixedResiduals(const int32_t* samples, size_t count, int order, std::vector<int64_t>* out)Residuals for one fixed predictor order, and the total magnitude that decides whether it is the best one.flacenc::fixedResiduals · flac_encode.h:192 - fn
inline void fixedTotals(const int32_t* samples, size_t count, uint64_t totals[5])fixedResiduals' totals for orders 0 to 4 at once, in one pass and without storing a residual: choosing the order needs only these, and computing and storing all five sets of residuals to keep one was most of the encoder's arithmetic. The same formulas, so the same totals; an order the block is too short for gets its residuals from none of its samples and is never chosen.flacenc::fixedTotals · flac_encode.h:222 - fn
inline int bestRiceParam(const std::vector<int64_t>& residuals)The Rice parameter that spends the fewest bits on these residuals: up to 30, which the 5-bit parameters of residual method 1 can carry.flacenc::bestRiceParam · flac_encode.h:252 - fn
inline bool writeFlac(const char* path, const float* interleaved, size_t numFrames, uint16_t numChannels, uint32_t sampleRate, int bitDepth = 24)Write a FLAC file from interleaved float samples.flac_encode.h:272