audio/flac
Lossless compression: reading and writing FLAC.
import audio.flac
FLAC is lossless: what comes out is exactly what went in, and the file is usually a little over half the size of the same sound as 16 bit PCM. This package reads and writes it without any outside library.
The decoder handles the whole format: constant, verbatim, fixed-predictor and LPC subframes, Rice-coded and escaped residuals, and all four ways FLAC can share a stereo pair between two channels. The encoder writes constant, verbatim and fixed-predictor subframes, choosing per block whichever is smallest, which is where most of the compression comes from.
Things to know
- Lossless means lossless: a clip at 8, 16 or 24 bits comes back bit for bit.
VerifyFLACproves it on a given file by decoding it again. - An encode is only as good as the clip it was given. A clip has no bit depth of its own until something sets
bits, and the encoder then assumes 16. - Tags are not here. FLAC keeps them in a metadata block, which audio/tag reads and writes.
Blockshands back a vector you must free, and a stream header holds an MD5 you must release withFreeStreamInfo.- Compression depends entirely on the material. A tone goes to a fifth of its PCM size, noise barely moves, and real music lands somewhere near half.
Types
FrameInfo
struct FrameInfo| Name | Type | Description |
|---|---|---|
block_size | int = 0 | How many frames this block holds. |
rate | int = 0 | Frames per second. |
channels | int = 0 | How many channels this frame carries. |
bits | int = 0 | Bits per sample. |
assignment | int = 0 | How the channels are stored: independent, or left/side, right/side or mid/side. |
variable | bool = false | True when the frame is numbered by sample rather than by frame. |
number | i64 = 0 | The frame or sample number this frame starts at. |
import audio
import audio.flac
import audio.synth
import compress
func main:
src := synth.Tone(440.0, 0.1, 44100, synth.WaveSine)
mut q := audio.ToBits(src, 16)
q.bits = 16
enc := flac.EncodeFLAC(q)
println flac.IsFLAC(enc.data, enc.len), flac.IsFLAC(enc.data, 4)
println audio.FormatName(audio.DetectFormat(enc.data, enc.len))
blocks := flac.Blocks(enc.data, enc.len)
println blocks.len(), blocks.get(0).kind == flac.BlockStreamInfo, blocks.get(0).last, blocks.get(0).size
mut si := flac.StreamInfo {}
println flac.ReadStreamInfo(enc.data, enc.len, si)
println si.rate, si.channels, si.bits, si.total as int
println si.md5.len, si.audio_offset
println si.min_block, si.max_block
mut fi := flac.FrameInfo {}
head := flac.ReadFrameHeader(enc.data, enc.len, si.audio_offset, si, fi)
println head > 0, fi.channels, fi.bits, fi.rate, fi.block_size
mut ok := false
back := flac.DecodeFLAC(enc.data, enc.len, ok)
println ok, back.frames, back.channels, back.bits
println audio.Sample(back, 100, 0) == audio.Sample(q, 100, 0)
mut pok := false
info := flac.ProbeFLAC(enc.data, enc.len, pok)
println pok, info.rate, info.channels, info.bits, info.frames, info.lossless
println flac.MaxChannels, flac.MaxBlockSize
mut junk := compress.NewByteBuf()
compress.BufPush(junk, 1)
println flac.CRC8(junk.data, 0, 0), flac.CRC16Buf(junk, 0, 1) == flac.CRC16(junk.data, 0, 1)
mut bad := false
flac.DecodeFLAC(junk.data, junk.len, bad)
println bad
flac.FreeStreamInfo(si)
blocks.free()
compress.BufFree(enc)
compress.BufFree(junk)
audio.Free(src)
audio.Free(q)
audio.Free(back)
endStreamInfo
struct StreamInfo| Name | Type | Description |
|---|---|---|
min_block | int = 0 | The smallest block the encoder used. |
max_block | int = 0 | The largest block it used. |
min_frame | int = 0 | The smallest frame in bytes, or 0 when unknown. |
max_frame | int = 0 | The largest frame in bytes. |
rate | int = 0 | Frames per second. |
channels | int = 0 | How many channels. |
bits | int = 0 | Bits per sample, from 4 to 32. |
total | i64 = 0 | How many frames the whole stream holds, or 0 when the encoder did not know. |
md5 | compress.ByteBuf | The MD5 of the decoded signal, 16 bytes. Release it with FreeStreamInfo. |
audio_offset | int = 0 | Where the first audio frame starts, past every metadata block. |
The example for this item is shown with the ones above: flac-Read.salam
MetaBlock
struct MetaBlock| Name | Type | Description |
|---|---|---|
kind | int = 0 | Which Block number it is. |
offset | int = 0 | Where the body starts. |
size | int = 0 | How many bytes the body holds. |
last | bool = false | True on the final block before the audio. |
The example for this item is shown with the ones above: flac-Read.salam
Constants and variables
DefaultBlock
const DefaultBlock: i32 = 4096import audio
import audio.flac
import audio.synth
import compress
import filepath
import mem
import os
func main:
src := synth.Tone(440.0, 0.2, 44100, synth.WaveSine)
mut tone := audio.ToBits(src, 16)
tone.bits = 16
raw := tone.frames * tone.channels * 2
println tone.frames, raw
enc := flac.EncodeFLAC(tone)
println compress.BufLen(enc) < raw / 2
println flac.VerifyFLAC(enc.data, enc.len)
println flac.DefaultBlock
mut ok := false
back := flac.DecodeFLAC(enc.data, enc.len, ok)
mut worst := 0.0
mut i := 0
until i < tone.frames:
d := audio.Sample(back, i, 0) - audio.Sample(tone, i, 0)
if d > worst:
worst = d
end
if 0.0 - d > worst:
worst = 0.0 - d
end
i += 1
end
println worst
each lv in [0, 5, 8]:
one := flac.EncodeFLACLevel(tone, lv)
println lv, compress.BufLen(one) > 42, flac.VerifyFLAC(one.data, one.len)
compress.BufFree(one)
end
println compress.BufLen(flac.EncodeFLAC(audio.Clip {}))
path := filepath.Join(os.TempDir(), "salam_flac_example.flac")
defer mem.Free(path as void*)
println flac.SaveFLAC(tone, path)
mut dok := false
disk := flac.OpenFLAC(path, dok)
println dok, disk.frames == tone.frames, disk.bits
println flac.SaveFLACLevel(tone, path, 8)
mut iok := false
info := flac.ProbeFileFLAC(path, iok)
println iok, info.frames
mut gone := false
missing := filepath.Join(os.TempDir(), "salam_flac_none.flac")
defer mem.Free(missing as void*)
flac.OpenFLAC(missing, gone)
println gone
compress.BufFree(enc)
audio.Free(src)
audio.Free(tone)
audio.Free(back)
audio.Free(disk)
endMetadata block numbers Block…
| Name | Value |
|---|---|
BlockStreamInfo | 0 |
BlockPadding | 1 |
BlockApplication | 2 |
BlockSeekTable | 3 |
BlockVorbisComment | 4 |
BlockCueSheet | 5 |
BlockPicture | 6 |
The example for this item is shown with the ones above: flac-Read.salam
MaxBlockSize
const MaxBlockSize: i32 = 65535The example for this item is shown with the ones above: flac-Read.salam
MaxChannels
const MaxChannels: i32 = 8The example for this item is shown with the ones above: flac-Read.salam
Functions
EncodeFLACLevel
func EncodeFLACLevel(c: audio.Clip, level: int): compress.ByteBufThe example for this item is shown with the ones above: flac-Write.salam
EncodeFLAC
func EncodeFLAC(c: audio.Clip): compress.ByteBufThe example for this item is shown with the ones above: flac-Write.salam
VerifyFLAC
func VerifyFLAC(p: void*, n: int): boolThe example for this item is shown with the ones above: flac-Write.salam
ReadFrameHeader
func ReadFrameHeader(p: void*, n: int, at: int, si: StreamInfo, out &: FrameInfo): intThe example for this item is shown with the ones above: flac-Read.salam
DecodeFLAC
func DecodeFLAC(p: void*, n: int, ok &: bool): audio.ClipThe example for this item is shown with the ones above: flac-Read.salam
CRC8
func CRC8(p: void*, off: int, n: int): intThe example for this item is shown with the ones above: flac-Read.salam
CRC16
func CRC16(p: void*, off: int, n: int): intThe example for this item is shown with the ones above: flac-Read.salam
CRC16Buf
func CRC16Buf(b: compress.ByteBuf, off: int, n: int): intThe example for this item is shown with the ones above: flac-Read.salam
IsFLAC
func IsFLAC(p: void*, n: int): boolThe example for this item is shown with the ones above: flac-Read.salam
Blocks
func Blocks(p: void*, n: int): Vector<MetaBlock>The example for this item is shown with the ones above: flac-Read.salam
ReadStreamInfo
func ReadStreamInfo(p: void*, n: int, out &: StreamInfo): boolThe example for this item is shown with the ones above: flac-Read.salam
FreeStreamInfo
func FreeStreamInfo(si &: StreamInfo)The example for this item is shown with the ones above: flac-Read.salam
ProbeFLAC
func ProbeFLAC(p: void*, n: int, ok &: bool): audio.InfoThe example for this item is shown with the ones above: flac-Read.salam
OpenFLAC
func OpenFLAC(path: str, ok &: bool): audio.ClipThe example for this item is shown with the ones above: flac-Read.salam
ProbeFileFLAC
func ProbeFileFLAC(path: str, ok &: bool): audio.InfoThe example for this item is shown with the ones above: flac-Read.salam
SaveFLAC
func SaveFLAC(c: audio.Clip, path: str): boolThe example for this item is shown with the ones above: flac-Write.salam
SaveFLACLevel
func SaveFLACLevel(c: audio.Clip, path: str, level: int): boolThe example for this item is shown with the ones above: flac-Write.salam