Standard library · Encoding and data

audio/aiff

Reading and writing AIFF and AIFF-C files.

import audio.aiff

AIFF is the big-endian cousin of WAVE: the same idea of a chunked container, written the other way round because it came from the Motorola side of the world. AIFF-C adds a compression tag, and this package reads the ones that matter: plain PCM, the little-endian sowt variant Apple tools write, IEEE floating point, and A-law and mu-law.

The one genuinely odd part is the sample rate, which AIFF stores as an 80 bit extended float. This package reads and writes it, so 44100 comes back as 44100 and not 44099.

Things to know

  • 8, 16 and 24 bit PCM makes the round trip through a clip exactly. Unlike WAVE, 8 bit AIFF is signed.
  • ReadCommon gives you the COMM and SSND chunks in one struct, because you always need both: one says the shape, the other says where the sound is.
  • A file whose COMM frame count disagrees with the bytes actually present decodes to whichever is shorter, rather than reading off the end.
  • Writing always produces plain big-endian AIFF. The AIFC variants are read, not written.

Types

Common

struct Common
What an AIFF COMM chunk says, plus where the samples are.
NameTypeDescription
channelsint = 0How many channels.
framesint = 0How many frames the header claims.
bitsint = 0Bits per sample.
rateint = 0Frames per second, read from the 80 bit float AIFF stores it in.
compressionint = 0Which Comp number the file uses. Plain AIFF is always CompNone.
data_offsetint = 0Where the samples start, past the SSND header and its offset field.
data_sizeint = 0How many bytes of samples there are.
aiff-Read.salam
import audio
import audio.aiff
import compress
import filepath
import mem
import os

func main:
    mut c := audio.New(44100, 2, 64)
    audio.SetSample(c, 7, 1, 0.5)
    buf := aiff.EncodeAIFFBits(c, 24)
    println aiff.IsAIFF(buf.data, buf.len), aiff.IsAIFF(buf.data, 4)
    println audio.FormatName(audio.DetectFormat(buf.data, buf.len))

    mut cm := aiff.Common {}
    println aiff.ReadCommon(buf.data, buf.len, cm)
    println cm.channels, cm.frames, cm.bits, cm.rate
    println cm.compression == aiff.CompNone, cm.data_size

    mut ok := false
    info := aiff.ProbeAIFF(buf.data, buf.len, ok)
    println ok, info.rate, info.channels, info.bits, info.frames, info.lossless

    mut dok := false
    back := aiff.DecodeAIFF(buf.data, buf.len, dok)
    println dok, back.frames, back.bits, audio.Sample(back, 7, 1)

    each rate in [8000, 22050, 44100, 96000]:
        mut one := audio.New(rate, 1, 2)
        probe := aiff.EncodeAIFFBits(one, 16)
        mut c2 := aiff.Common {}
        aiff.ReadCommon(probe.data, probe.len, c2)
        println rate, c2.rate == rate
        compress.BufFree(probe)
        audio.Free(one)
    end

    println compress.BufLen(aiff.EncodeAIFFBits(c, 12)), compress.BufLen(aiff.EncodeAIFF(audio.Clip {}))

    path := filepath.Join(os.TempDir(), "salam_aiff_example.aiff")
    defer mem.Free(path as void*)
    println aiff.SaveAIFFBits(c, path, 16)
    mut fok := false
    disk := aiff.OpenAIFF(path, fok)
    println fok, disk.bits, disk.frames
    mut gone := false
    missing := filepath.Join(os.TempDir(), "salam_aiff_none.aiff")
    defer mem.Free(missing as void*)
    aiff.OpenAIFF(missing, gone)
    println gone

    compress.BufFree(buf)
    audio.Free(c)
    audio.Free(back)
    audio.Free(disk)
end
Outputtrue false aiff true 2 64 24 44100 true 384 true 44100 2 24 64 true true 64 24 0.5 8000 true 22050 true 44100 true 96000 true 0 0 true true 16 64 false

Constants and variables

Compression numbers Comp…

Which compression an AIFF-C file declares. Plain AIFF is always CompNone.
NameValue
CompNone0
CompSowt1
CompFloat322
CompFloat643
CompALaw4
CompMuLaw5

The example for this item is shown with the ones above: aiff-Read.salam

Functions

IsAIFF

func IsAIFF(p: void*, n: int): bool
True when these bytes start with a FORM header naming AIFF or AIFC.

The example for this item is shown with the ones above: aiff-Read.salam

ReadCommon

func ReadCommon(p: void*, n: int, out &: Common): bool
Reads the COMM and SSND chunks together, so one call gives you the shape and where the sound is.

The example for this item is shown with the ones above: aiff-Read.salam

ProbeAIFF

func ProbeAIFF(p: void*, n: int, ok &: bool): audio.Info
Reads only the header, so you learn the length and rate without decoding.

The example for this item is shown with the ones above: aiff-Read.salam

DecodeAIFF

func DecodeAIFF(p: void*, n: int, ok &: bool): audio.Clip
Decodes a whole AIFF or AIFC file from memory into a clip.

The example for this item is shown with the ones above: aiff-Read.salam

EncodeAIFFBits

func EncodeAIFFBits(c: audio.Clip, bits: int): compress.ByteBuf
Encodes a clip as AIFF at a depth you choose.

The example for this item is shown with the ones above: aiff-Read.salam

EncodeAIFF

func EncodeAIFF(c: audio.Clip): compress.ByteBuf
Encodes a clip as AIFF at the depth the clip remembers, or 16 bit when it has none.

The example for this item is shown with the ones above: aiff-Read.salam

OpenAIFF

func OpenAIFF(path: str, ok &: bool): audio.Clip
Reads an AIFF file from disk into a clip.

The example for this item is shown with the ones above: aiff-Read.salam

SaveAIFF

func SaveAIFF(c: audio.Clip, path: str): bool
Writes a clip to an AIFF file.

The example for this item is shown with the ones above: aiff-Read.salam

SaveAIFFBits

func SaveAIFFBits(c: audio.Clip, path: str, bits: int): bool
Writes a clip to an AIFF file at a depth you choose.

The example for this item is shown with the ones above: aiff-Read.salam