Standard library · Encoding and data

audio

Sound as a clip of samples: cutting, mixing, measuring and converting it.

import audio

This package is the middle of everything to do with sound in Salam. A Clip holds the sound itself: plain 32 bit floating point samples between -1 and 1, one frame after another, with the channels side by side. Reading and writing files is the job of the format packages, audio/wav today, and audio/codec opens any of them without being told which. Filters, a Fourier transform and careful resampling live in audio/dsp.

What is here is the clip and everything that does not need to know a file format: cutting, joining, mixing, fading, measuring how loud something is, moving between mono and stereo and between sample rates, and the most significant bit first bitstream reader that every audio codec is written on top of.

Things to know

  • Samples are 32 bit floats. Every clip owns that memory; release it with Free.
  • Because a float carries 24 bits of mantissa, 8, 16 and 24 bit sound makes the round trip through a clip exactly. Only a true 32 bit integer source loses its lowest 8 bits.
  • A three minute stereo song at 44100 is about 63 MB as a clip, so decode a long file in pieces rather than all at once.
  • bits remembers the depth a clip was read at, so writing it again keeps that depth. A clip made with New has no depth, and an encoder then picks 16 bit.
  • Anything that fails returns an empty clip rather than stopping the program, so check with IsEmpty.
  • MsbReader reads the most significant bit first. The BitReader in compress reads the other way round, for DEFLATE, and is not interchangeable with it.

A clip of sound

A clip is a rate, a channel count, a frame count and the samples. New gives you silence to fill in; SetSample and Sample reach one sample at a time by frame and channel. A frame is one sample for every channel, so a stereo clip of 100 frames holds 200 samples.

Every clip holds memory of its own, so release each one with Free. Reading or writing outside the clip is not an error: Sample gives 0 and SetSample does nothing, which keeps loops at the edges simple.

How loud is it

Peak is the furthest any single sample gets from zero and RMS follows loudness as an ear hears it. Both have a decibel form, where 0 dB is as loud as a sample can be and everything quieter is negative. GainToDb and DbToGain move between a plain multiplier and decibels.

Silence has no decibel value, so GainToDb(0.0) returns SilenceFloorDb instead of running off to minus infinity. FirstSound and LastSound find where the sound really starts and stops, which is what TrimSilence is built from.

Cutting and joining

Slice and Trim take a piece out, by frame or by time; Concat puts one clip after another and Mix lays them on top of each other. Normalize scales a clip so its peak lands exactly where you ask, and the two fades shape the ends so a cut does not click.

Joining and mixing need both clips to agree on the sample rate and the channel count, and return an empty clip when they do not. Use ToRate and ToChannels first to bring them into line.

Rates, channels and depths

ToMono averages the channels together and ToStereo spreads one channel across two. ToRate changes the sample rate by interpolating between samples, which is quick and good enough for speech; for music use ResampleSinc from audio/dsp. ToBits does not write a file, it rounds the samples to the steps of a depth so you can hear what that depth will cost.

Deinterleave hands back one clip per channel, each of which you must free, and Interleave weaves them back together.

Which format is this

DetectFormat looks at the first few bytes and FormatFromExt at the file name. Both answer with a Format number, which FormatName turns into a short name. Knowing the format does not mean this build can decode it: ask CanDecode in audio/codec for that.

An Info is what a file says about itself, which the format packages fill in by reading a header rather than the whole file. It is the cheap way to show a length or a bit rate in a listing.

Reading a bitstream

Compressed audio is not laid out in bytes, it is laid out in bits, and every format from MP3 to FLAC counts those bits from the most significant end. MsbReader does that, with MsbReadSigned for two's complement fields and MsbUnary for the run of zeros that Rice and Golomb codes are built from. MsbWriter is the same thing in reverse.

A read past the end does not stop the program: it hands back zeros and sets bad, which stays set, so one check after a group of reads is enough. Note again that compress has a bit reader of its own that runs the other way round, for DEFLATE; do not use it for audio.

JobUse
Make and free a clipNew, NewSilence, Clone, Free
Reach the samplesSample, SetSample, AddSample, Data
Cut and joinSlice, Trim, Concat, Mix, Split, Repeat
Shape the soundGain, Normalize, FadeIn, FadeOut, Clamp
Measure itPeak, RMS, PeakDb, DcOffset, IsSilent
Change its shapeToMono, ToStereo, ToRate, ToBits
Work out a formatDetectFormat, FormatFromExt, FormatName
Write a codecMsbReader, MsbWriter

Types

Clip

struct Clip
A piece of sound: interleaved f32 samples with a sample rate, a channel count and a frame count.
NameTypeDescription
rateint = 0Frames per second, such as 44100.
channelsint = 0How many channels each frame holds; 1 is mono, 2 is stereo.
framesint = 0How many frames the clip holds. One frame is one sample per channel.
bitsint = 0The bit depth the samples came from, or 0 when they were made in memory. An encoder with no depth of its own writes 16 bit.
samplesvoid*The samples themselves, 32 bit floats, one frame after another with the channels side by side.
audio-Clip.salam
import audio

func main:
    mut c := audio.New(8000, 2, 400)
    println c.rate, c.channels, c.frames, c.bits
    println audio.SampleCount(c), audio.Duration(c), audio.IsEmpty(c)
    audio.SetSample(c, 0, 0, 0.5)
    audio.SetSample(c, 0, 1, -0.25)
    audio.AddSample(c, 0, 0, 0.25)
    println audio.Sample(c, 0, 0), audio.Sample(c, 0, 1), audio.Sample(c, 399, 0)
    println audio.Sample(c, 4000, 0), audio.InBounds(c, 399, 1), audio.InBounds(c, 400, 0)
    println audio.FrameAt(c, 0.025), audio.SecondsAt(c, 200), audio.FrameAt(c, 99.0)
    mut q := audio.Clone(c)
    println audio.Sample(q, 0, 0), audio.Peak(q)
    audio.Silence(q)
    println audio.Peak(q)
    mut s := audio.NewSilence(8000, 1, 0.25)
    println s.frames, audio.MaxSamples
    audio.Free(c)
    println audio.IsEmpty(c), c.frames
    audio.Free(q)
    audio.Free(s)
end
Output8000 2 400 0 800 0.05 false 0.75 -0.25 0 0 true false 200 0.025 400 0.75 0.75 0 2000 536870912 true 0

MsbReader

struct MsbReader
Reads a bit at a time, most significant bit first, the order every audio codec uses.
NameTypeDescription
datavoid*The bytes being read.
lenint = 0How many bytes there are.
posint = 0Which byte comes next.
acci64 = 0Bits already pulled out of the bytes but not yet handed back.
nbitsint = 0How many bits are waiting in the accumulator.
badbool = falseTrue once a read ran past the end. It stays true.
audio-Msb.salam
import audio
import compress

func main:
    mut w := audio.NewMsbWriter()
    audio.MsbWrite(w, 10, 4)
    audio.MsbWrite(w, 5, 4)
    println audio.MsbLen(w)
    audio.MsbWrite(w, 60, 8)
    bytes := audio.MsbFinish(w)
    println compress.BufLen(bytes), compress.BufGet(bytes, 0), compress.BufGet(bytes, 1)
    mut r := audio.MsbReaderFromBuf(bytes)
    println audio.MsbRead(r, 4), audio.MsbRead(r, 4), audio.MsbRead(r, 8), audio.MsbBad(r)
    mut s := audio.MsbReaderFromBuf(bytes)
    println audio.MsbReadSigned(s, 4), audio.MsbReadBit(s), audio.MsbTell(s)
    mut t := audio.MsbReaderFromBuf(bytes)
    println audio.MsbReadI64(t, 16)
    mut u := audio.NewMsbWriter()
    audio.MsbWriteUnary(u, 0)
    audio.MsbWriteUnary(u, 3)
    audio.MsbWriteUnary(u, 7)
    runs := audio.MsbFinish(u)
    mut ur := audio.MsbReaderFromBuf(runs)
    println audio.MsbUnary(ur), audio.MsbUnary(ur), audio.MsbUnary(ur)
    mut j := audio.MsbReaderFromBuf(bytes)
    audio.MsbSkip(j, 8)
    println audio.MsbRead(j, 8)
    audio.MsbSeek(j, 4)
    println audio.MsbRead(j, 4), audio.MsbTell(j), audio.MsbLeft(j)
    mut k := audio.MsbReaderFromBuf(bytes)
    audio.MsbRead(k, 3)
    audio.MsbAlign(k)
    println audio.MsbRead(k, 8)
    mut p := audio.NewMsbWriter()
    audio.MsbWriteBit(p, 1)
    audio.MsbPad(p)
    padded := audio.MsbFinish(p)
    println compress.BufLen(padded), compress.BufGet(padded, 0)
    mut over := audio.NewMsbReader(bytes.data, 1)
    audio.MsbRead(over, 16)
    println audio.MsbBad(over)
    audio.MsbFree(w)
    audio.MsbFree(u)
    audio.MsbFree(p)
end
Output1 2 165 60 10 5 60 false -6 0 5 42300 0 3 7 60 5 8 8 60 1 128 true

MsbWriter

struct MsbWriter
Writes a bit at a time, most significant bit first.
NameTypeDescription
bufcompress.ByteBufThe bytes written so far.
acci64 = 0Bits not yet complete enough to make a byte.
nbitsint = 0How many bits are waiting in the accumulator.

The example for this item is shown with the ones above: audio-Msb.salam

Info

struct Info
What a file says about itself, read without decoding the sound.
NameTypeDescription
formatint = 0Which format it is, one of the Format numbers.
rateint = 0Frames per second.
channelsint = 0How many channels.
framesint = 0How many frames, so how long it is.
bitsint = 0Bits per sample.
bitrateint = 0Bits per second of audio.
vbrbool = falseTrue when the bit rate changes through the file.
durationf64 = 0How many seconds long it is.
losslessbool = falseTrue when decoding gives back exactly what was encoded.
audio-Formats.salam
import audio
import audio.wav
import compress

func main:
    mut c := audio.New(44100, 2, 44100)
    buf := wav.EncodeWAV(c)
    println audio.FormatName(audio.DetectFormat(buf.data, buf.len))
    println audio.DetectFormat(buf.data, 4) == audio.FormatUnknown
    println audio.FormatName(audio.FormatFromExt("song.flac")), audio.FormatName(audio.FormatFromExt("SONG.WAV"))
    println audio.FormatName(audio.FormatFromExt("a.mp3")), audio.FormatName(audio.FormatFromExt("a.ogg")), audio.FormatName(audio.FormatFromExt("a.mid"))
    println audio.FormatName(audio.FormatFromExt("notes.txt")), audio.FormatName(999)
    println audio.FormatIsLossless(audio.FormatWAV), audio.FormatIsLossless(audio.FormatFLAC), audio.FormatIsLossless(audio.FormatMP3)
    mut ok := false
    info := wav.ProbeWAV(buf.data, buf.len, ok)
    println ok, audio.FormatName(info.format)
    println info.rate, info.channels, info.bits, info.frames
    println info.duration, info.bitrate, info.lossless, info.vbr
    compress.BufFree(buf)
    audio.Free(c)
end
Outputwav true flac wav mp3 ogg midi unknown unknown true true false true wav 44100 2 16 44100 1 1411200 true false

Constants and variables

MaxSamples

const MaxSamples: i32 = 536870912
The largest number of samples one clip may hold, so that a broken header cannot ask for all the memory.

The example for this item is shown with the ones above: audio-Clip.salam

SilenceFloorDb

const SilenceFloorDb: f64 = -200
The decibel value used for silence, because true silence has no decibel value.
audio-Measure.salam
import audio

func main:
    mut c := audio.New(8000, 1, 4)
    audio.SetSample(c, 0, 0, 1.0)
    audio.SetSample(c, 1, 0, -0.5)
    audio.SetSample(c, 2, 0, 0.0)
    audio.SetSample(c, 3, 0, 0.25)
    println audio.Peak(c), audio.PeakChannel(c, 0), audio.DcOffset(c)
    println audio.IsClipped(c), audio.CountClipped(c)
    println audio.GainToDb(1.0), audio.DbToGain(0.0)
    println audio.DbToGain(audio.GainToDb(0.25))
    println audio.GainToDb(0.0), audio.SilenceFloorDb, audio.DbToGain(audio.SilenceFloorDb)
    println audio.IsSilent(c, -60.0), audio.FirstSound(c, -60.0), audio.LastSound(c, -60.0)
    mut gap := audio.New(8000, 1, 8)
    audio.SetSample(gap, 3, 0, 0.5)
    audio.SetSample(gap, 4, 0, 0.5)
    println audio.FirstSound(gap, -60.0), audio.LastSound(gap, -60.0)
    mut quiet := audio.New(8000, 1, 8)
    println audio.IsSilent(quiet, -60.0), audio.FirstSound(quiet, -60.0)
    println audio.RMS(gap), audio.RMSRange(gap, 3, 2)
    audio.Free(c)
    audio.Free(gap)
    audio.Free(quiet)
end
Output1 1 0.1875 true 1 0 1 0.25 -200 -200 0 false 0 3 3 4 true -1 0.25 0.5

Format numbers Format…

The formats this family knows by name. DetectFormat and FormatFromExt answer with one of these; whether this build can actually read one is a separate question for CanDecode in audio/codec.
NameValue
FormatUnknown0
FormatWAV1
FormatMP32
FormatFLAC3
FormatOGG4
FormatAIFF5
FormatAU6
FormatOpus7
FormatAAC8
FormatMIDI9

The example for this item is shown with the ones above: audio-Formats.salam

Functions

Slice

func Slice(c: Clip, from_frame: int, count: int): Clip
A new clip holding these frames.
audio-Edit.salam
import audio

func ramp(frames: int): audio.Clip:
    mut c := audio.New(8000, 1, frames)
    mut i := 0
    until i < frames:
        audio.SetSample(c, i, 0, (i as f64) / 32.0)
        i += 1
    end
    ret c
end

func main:
    a := ramp(8)
    sl := audio.Slice(a, 2, 3)
    println sl.frames, audio.Sample(sl, 0, 0), audio.Sample(sl, 2, 0)
    println audio.Trim(a, 0.0, 0.0005).frames, audio.Slice(a, 6, 10).frames
    cat := audio.Concat(sl, sl)
    println cat.frames, audio.Sample(cat, 3, 0)
    println audio.Repeat(sl, 4).frames
    mix := audio.Mix(a, a, 1.0, 0.5)
    println audio.Sample(mix, 4, 0)
    mut g := audio.Clone(a)
    audio.Scale(g, 2.0)
    println audio.Sample(g, 1, 0)
    audio.Gain(g, 0.0)
    println audio.Sample(g, 1, 0)
    audio.Scale(g, 100.0)
    audio.Clamp(g)
    println audio.Peak(g)
    mut n := audio.Clone(a)
    audio.Normalize(n, 0.0)
    println audio.Peak(n)
    mut r := audio.Clone(a)
    audio.Reverse(r)
    println audio.Sample(r, 0, 0), audio.Sample(r, 7, 0)
    mut f := audio.Clone(a)
    audio.FadeIn(f, audio.Duration(f))
    println audio.Sample(f, 0, 0), audio.Sample(f, 4, 0)
    mut o := audio.Clone(a)
    audio.FadeOut(o, audio.Duration(o))
    println audio.Sample(o, 0, 0)
    println audio.Pad(a, 0.001, 0.001).frames
    mut head := audio.Clip {}
    mut tail := audio.Clip {}
    audio.Split(a, 0.0005, head, tail)
    println head.frames, tail.frames, audio.Sample(tail, 0, 0)
    mut gap := audio.New(8000, 1, 8)
    audio.SetSample(gap, 3, 0, 0.5)
    audio.SetSample(gap, 4, 0, 0.5)
    println audio.TrimSilence(gap, -60.0).frames
    mut dst := audio.New(8000, 1, 8)
    audio.MixAt(dst, sl, 2, 1.0)
    println audio.Sample(dst, 2, 0), audio.Sample(dst, 0, 0)
    audio.Free(a)
end
Output3 0.0625 0.125 4 2 6 0.0625 12 0.1875 0.0625 0.0625 1 1 0.21875 0 0 0.0625 0 24 4 4 0.125 2 0.0625 0

Trim

func Trim(c: Clip, from_s: f64, to_s: f64): Clip
A new clip holding the sound between these two times.

The example for this item is shown with the ones above: audio-Edit.salam

Concat

func Concat(a: Clip, b: Clip): Clip
One clip after the other. They must share a rate and a channel count.

The example for this item is shown with the ones above: audio-Edit.salam

MixAt

func MixAt(dst &: Clip, src: Clip, at_frame: int, gain: f64)
Adds one clip into another at a frame, at a gain, in place.

The example for this item is shown with the ones above: audio-Edit.salam

Mix

func Mix(a: Clip, b: Clip, gain_a: f64, gain_b: f64): Clip
Two clips added together, each at its own gain, as long as a clip.

The example for this item is shown with the ones above: audio-Edit.salam

Split

func Split(c: Clip, at_s: f64, head &: Clip, tail &: Clip)
Cuts the clip in two at a time, into head and tail.

The example for this item is shown with the ones above: audio-Edit.salam

Gain

func Gain(c &: Clip, db: f64)
Makes the clip louder or quieter by this many decibels.

The example for this item is shown with the ones above: audio-Edit.salam

Scale

func Scale(c &: Clip, factor: f64)
Multiplies every sample by this number.

The example for this item is shown with the ones above: audio-Edit.salam

Clamp

func Clamp(c &: Clip)
Holds every sample inside -1 and 1.

The example for this item is shown with the ones above: audio-Edit.salam

Normalize

func Normalize(c &: Clip, target_db: f64)
Scales the clip so its peak lands on this many decibels.

The example for this item is shown with the ones above: audio-Edit.salam

Reverse

func Reverse(c &: Clip)
Turns the clip back to front.

The example for this item is shown with the ones above: audio-Edit.salam

FadeIn

func FadeIn(c &: Clip, seconds: f64)
Fades up from silence over this many seconds.

The example for this item is shown with the ones above: audio-Edit.salam

FadeOut

func FadeOut(c &: Clip, seconds: f64)
Fades down to silence over the last seconds.

The example for this item is shown with the ones above: audio-Edit.salam

Pad

func Pad(c: Clip, before_s: f64, after_s: f64): Clip
A new clip with silence added before and after.

The example for this item is shown with the ones above: audio-Edit.salam

Repeat

func Repeat(c: Clip, times: int): Clip
The clip laid end to end this many times.

The example for this item is shown with the ones above: audio-Edit.salam

TrimSilence

func TrimSilence(c: Clip, threshold_db: f64): Clip
A new clip with the silence at each end removed.

The example for this item is shown with the ones above: audio-Edit.salam

New

func New(rate: int, channels: int, frames: int): Clip
A new clip of silence with this rate, channel count and frame count. The samples belong to the clip; release them with Free.

The example for this item is shown with the ones above: audio-Clip.salam

NewSilence

func NewSilence(rate: int, channels: int, seconds: f64): Clip
A new clip of silence this many seconds long.

The example for this item is shown with the ones above: audio-Clip.salam

Free

func Free(c &: Clip)
Releases the samples and leaves the clip empty.

The example for this item is shown with the ones above: audio-Clip.salam

IsEmpty

func IsEmpty(c: Clip): bool
True when the clip holds no sound, which is also what a failed call returns.

The example for this item is shown with the ones above: audio-Clip.salam

Clone

func Clone(c: Clip): Clip
A separate copy with its own samples.

The example for this item is shown with the ones above: audio-Clip.salam

SampleCount

func SampleCount(c: Clip): int
Frames times channels, the number of samples in all.

The example for this item is shown with the ones above: audio-Clip.salam

InBounds

func InBounds(c: Clip, frame: int, ch: int): bool
True when this frame and channel are inside the clip.

The example for this item is shown with the ones above: audio-Clip.salam

Sample

func Sample(c: Clip, frame: int, ch: int): f64
One sample, between -1 and 1. Outside the clip it is 0.

The example for this item is shown with the ones above: audio-Clip.salam

SetSample

func SetSample(c &: Clip, frame: int, ch: int, v: f64)
Writes one sample. Outside the clip it does nothing.

The example for this item is shown with the ones above: audio-Clip.salam

AddSample

func AddSample(c &: Clip, frame: int, ch: int, v: f64)
Adds to one sample instead of replacing it.

The example for this item is shown with the ones above: audio-Clip.salam

Data

func Data(c: Clip): f32*
The samples as a pointer, for code that walks them itself.

The example for this item is shown with the ones above: audio-Clip.salam

Duration

func Duration(c: Clip): f64
How many seconds long the clip is.

The example for this item is shown with the ones above: audio-Clip.salam

FrameAt

func FrameAt(c: Clip, seconds: f64): int
The frame at this many seconds, held inside the clip.

The example for this item is shown with the ones above: audio-Clip.salam

SecondsAt

func SecondsAt(c: Clip, frame: int): f64
How many seconds in this frame is.

The example for this item is shown with the ones above: audio-Clip.salam

Silence

func Silence(c &: Clip)
Sets every sample to zero without changing the shape.

The example for this item is shown with the ones above: audio-Clip.salam

Channel

func Channel(c: Clip, ch: int): Clip
One channel of the clip on its own, as a clip with one channel.
audio-Convert.salam
import audio

func main:
    mut st := audio.New(8000, 2, 4)
    mut i := 0
    until i < 4:
        audio.SetSample(st, i, 0, (i as f64) / 8.0)
        audio.SetSample(st, i, 1, 0.0 - (i as f64) / 16.0)
        i += 1
    end
    st.bits = 16
    mono := audio.ToMono(st)
    println mono.channels, audio.Sample(mono, 2, 0), mono.bits
    wide := audio.ToStereo(mono)
    println wide.channels, audio.Sample(wide, 2, 0) == audio.Sample(wide, 2, 1)
    quad := audio.ToChannels(mono, 4)
    println quad.channels, audio.Sample(quad, 1, 3)
    left := audio.Channel(st, 0)
    right := audio.Channel(st, 1)
    println left.channels, audio.Sample(left, 3, 0), audio.Sample(right, 3, 0)
    parts := audio.Deinterleave(st)
    println parts.len()
    woven := audio.Interleave(parts)
    println woven.channels, audio.Sample(woven, 3, 1) == audio.Sample(st, 3, 1)
    down := audio.ToRate(st, 4000)
    println down.rate, down.frames, down.bits
    up := audio.ToRate(mono, 16000)
    println up.rate, up.frames, audio.Sample(up, 2, 0)
    rough := audio.ToBits(mono, 4)
    println rough.bits, audio.Sample(rough, 1, 0), audio.Sample(rough, 3, 0)
    audio.Free(st)
    audio.Free(mono)
end
Output1 0.0625 16 2 true 4 0.03125 1 0.375 -0.1875 2 2 true 4000 2 16 16000 8 0.03125 4 0 0.125

Deinterleave

func Deinterleave(c: Clip): Vector<Clip>
Every channel as its own clip. Free each of them.

The example for this item is shown with the ones above: audio-Convert.salam

Interleave

func Interleave(parts: Vector<Clip>): Clip
Several one channel clips woven into one clip with that many channels.

The example for this item is shown with the ones above: audio-Convert.salam

ToMono

func ToMono(c: Clip): Clip
Every channel averaged down to one.

The example for this item is shown with the ones above: audio-Convert.salam

ToStereo

func ToStereo(c: Clip): Clip
The clip as two channels.

The example for this item is shown with the ones above: audio-Convert.salam

ToChannels

func ToChannels(c: Clip, channels: int): Clip
The clip with this many channels, copying or averaging as needed.

The example for this item is shown with the ones above: audio-Convert.salam

ToRate

func ToRate(c: Clip, rate: int): Clip
The clip at a new sample rate, interpolating between samples. For a cleaner result use ResampleSinc in audio/dsp.

The example for this item is shown with the ones above: audio-Convert.salam

ToBits

func ToBits(c: Clip, bits: int): Clip
The clip rounded to the steps of this bit depth, which is how it will sound once written at that depth.

The example for this item is shown with the ones above: audio-Convert.salam

NewMsbReader

func NewMsbReader(p: void*, n: int): MsbReader
A reader over these bytes.

The example for this item is shown with the ones above: audio-Msb.salam

MsbReaderFromBuf

func MsbReaderFromBuf(b: compress.ByteBuf): MsbReader
A reader over a byte buffer.

The example for this item is shown with the ones above: audio-Msb.salam

MsbReadI64

func MsbReadI64(r &: MsbReader, count: int): i64
The next bits as a long number, up to 56 of them.

The example for this item is shown with the ones above: audio-Msb.salam

MsbRead

func MsbRead(r &: MsbReader, count: int): int
The next bits as a number, up to 32 of them, most significant first.

The example for this item is shown with the ones above: audio-Msb.salam

MsbReadBit

func MsbReadBit(r &: MsbReader): int
The next single bit.

The example for this item is shown with the ones above: audio-Msb.salam

MsbReadSigned

func MsbReadSigned(r &: MsbReader, count: int): int
The next bits read as a signed number in two's complement.

The example for this item is shown with the ones above: audio-Msb.salam

MsbUnary

func MsbUnary(r &: MsbReader): int
Counts the zeros before the next one, the shape Rice and Golomb codes use.

The example for this item is shown with the ones above: audio-Msb.salam

MsbSkip

func MsbSkip(r &: MsbReader, count: int)
Steps over this many bits.

The example for this item is shown with the ones above: audio-Msb.salam

MsbAlign

func MsbAlign(r &: MsbReader)
Moves on to the next whole byte.

The example for this item is shown with the ones above: audio-Msb.salam

MsbTell

func MsbTell(r: MsbReader): int
Which bit the reader is on.

The example for this item is shown with the ones above: audio-Msb.salam

MsbSeek

func MsbSeek(r &: MsbReader, bitpos: int)
Moves the reader to this bit.

The example for this item is shown with the ones above: audio-Msb.salam

MsbLeft

func MsbLeft(r: MsbReader): int
How many bits are still unread.

The example for this item is shown with the ones above: audio-Msb.salam

MsbBad

func MsbBad(r: MsbReader): bool
True once a read ran past the end.

The example for this item is shown with the ones above: audio-Msb.salam

NewMsbWriter

func NewMsbWriter(): MsbWriter
A new writer with an empty buffer.

The example for this item is shown with the ones above: audio-Msb.salam

MsbWrite

func MsbWrite(w &: MsbWriter, value: i64, count: int)
Writes the low bits of a number, most significant first.

The example for this item is shown with the ones above: audio-Msb.salam

MsbWriteBit

func MsbWriteBit(w &: MsbWriter, bit: int)
Writes one bit.

The example for this item is shown with the ones above: audio-Msb.salam

MsbWriteUnary

func MsbWriteUnary(w &: MsbWriter, n: int)
Writes this many zeros and then a one.

The example for this item is shown with the ones above: audio-Msb.salam

MsbPad

func MsbPad(w &: MsbWriter)
Fills the last byte out with zeros.

The example for this item is shown with the ones above: audio-Msb.salam

MsbLen

func MsbLen(w &: MsbWriter): int
How many whole bytes the writer has finished.

The example for this item is shown with the ones above: audio-Msb.salam

MsbFinish

func MsbFinish(w &: MsbWriter): compress.ByteBuf
Pads the last byte and hands back the bytes.

The example for this item is shown with the ones above: audio-Msb.salam

MsbFree

func MsbFree(w &: MsbWriter)
Releases the writer's buffer.

The example for this item is shown with the ones above: audio-Msb.salam

GainToDb

func GainToDb(gain: f64): f64
Turns a multiplier into decibels. Zero gives SilenceFloorDb.

The example for this item is shown with the ones above: audio-Measure.salam

DbToGain

func DbToGain(db: f64): f64
Turns decibels into a multiplier.

The example for this item is shown with the ones above: audio-Measure.salam

Peak

func Peak(c: Clip): f64
The largest distance from zero any sample reaches.

The example for this item is shown with the ones above: audio-Measure.salam

PeakDb

func PeakDb(c: Clip): f64
The peak in decibels, where 0 is as loud as a sample can be.

The example for this item is shown with the ones above: audio-Measure.salam

PeakChannel

func PeakChannel(c: Clip, ch: int): f64
The peak of one channel on its own.

The example for this item is shown with the ones above: audio-Measure.salam

RMS

func RMS(c: Clip): f64
The root mean square of every sample, which follows loudness better than the peak does.

The example for this item is shown with the ones above: audio-Measure.salam

RMSDb

func RMSDb(c: Clip): f64
The root mean square in decibels.

The example for this item is shown with the ones above: audio-Measure.salam

RMSRange

func RMSRange(c: Clip, from_frame: int, count: int): f64
The root mean square of a stretch of frames.

The example for this item is shown with the ones above: audio-Measure.salam

DcOffset

func DcOffset(c: Clip): f64
The average of every sample. Anything far from zero means the waveform is not centred.

The example for this item is shown with the ones above: audio-Measure.salam

IsClipped

func IsClipped(c: Clip): bool
True when a sample reaches the limit, where it would have been cut off.

The example for this item is shown with the ones above: audio-Measure.salam

CountClipped

func CountClipped(c: Clip): int
How many samples reach the limit.

The example for this item is shown with the ones above: audio-Measure.salam

IsSilent

func IsSilent(c: Clip, threshold_db: f64): bool
True when nothing in the clip rises above this many decibels.

The example for this item is shown with the ones above: audio-Measure.salam

FirstSound

func FirstSound(c: Clip, threshold_db: f64): int
The first frame above this many decibels, or -1 when there is none.

The example for this item is shown with the ones above: audio-Measure.salam

LastSound

func LastSound(c: Clip, threshold_db: f64): int
The last frame above this many decibels, or -1 when there is none.

The example for this item is shown with the ones above: audio-Measure.salam

DetectFormat

func DetectFormat(p: void*, n: int): int
Which format these bytes are, read from the few bytes at the front.

The example for this item is shown with the ones above: audio-Formats.salam

FormatFromExt

func FormatFromExt(path: str): int
Which format this file name claims, from its extension.

The example for this item is shown with the ones above: audio-Formats.salam

FormatName

func FormatName(format: int): str
The short name of a format, such as wav.

The example for this item is shown with the ones above: audio-Formats.salam

FormatIsLossless

func FormatIsLossless(format: int): bool
True when the format gives back exactly what was encoded.

The example for this item is shown with the ones above: audio-Formats.salam

MuLawToPcm

func MuLawToPcm(b: int): int
One mu-law byte back to a 16 bit sample, following G.711.
audio-Pcm.salam
import audio
import compress
import math

func main:
    println audio.PcmDepthValid(16), audio.PcmDepthValid(12), audio.PcmDepthValid(32)

    mut c := audio.New(8000, 1, 4)
    audio.SetSample(c, 0, 0, 0.5)
    audio.SetSample(c, 1, 0, -0.5)
    audio.SetSample(c, 2, 0, 0.25)

    mut le := compress.NewByteBuf()
    audio.EncodePcm(le, c, 16, false, false)
    println compress.BufLen(le), compress.BufGet(le, 0), compress.BufGet(le, 1)
    mut be := compress.NewByteBuf()
    audio.EncodePcm(be, c, 16, true, false)
    println compress.BufGet(be, 0), compress.BufGet(be, 1)

    mut back := audio.New(8000, 1, 4)
    println audio.DecodePcm(le.data, 0, back, 16, false, false)
    println audio.Sample(back, 0, 0), audio.Sample(back, 1, 0), audio.Sample(back, 2, 0)
    mut bback := audio.New(8000, 1, 4)
    audio.DecodePcm(be.data, 0, bback, 16, true, false)
    println audio.Sample(bback, 0, 0) == audio.Sample(back, 0, 0)

    mut f := compress.NewByteBuf()
    audio.EncodeFloatPcm(f, c, 32, false)
    mut fback := audio.New(8000, 1, 4)
    println audio.DecodeFloatPcm(f.data, f.len, 0, fback, 32, false)
    println audio.Sample(fback, 0, 0) == audio.Sample(c, 0, 0)

    println audio.PcmToMuLaw(0), audio.MuLawToPcm(audio.PcmToMuLaw(0))
    println audio.PcmToMuLaw(16000), audio.MuLawToPcm(144)
    println audio.PcmToALaw(16000), audio.ALawToPcm(186)
    mut worst := 0
    mut v := -32768
    until v < 32768:
        d := math.AbsI(audio.MuLawToPcm(audio.PcmToMuLaw(v)) - v)
        if d > worst:
            worst = d
        end
        v += 7
    end
    println worst < 820

    mut law := compress.NewByteBuf()
    audio.EncodeLawPcm(law, c, false)
    println compress.BufLen(law)
    mut lback := audio.New(8000, 1, 4)
    println audio.DecodeLawPcm(law.data, 0, lback, false)
    println math.Abs(audio.Sample(lback, 0, 0) - 0.5) < 0.02

    compress.BufFree(le)
    compress.BufFree(be)
    compress.BufFree(f)
    compress.BufFree(law)
    audio.Free(c)
    audio.Free(back)
    audio.Free(bback)
    audio.Free(fback)
    audio.Free(lback)
end
Outputtrue false true 8 0 64 64 0 true 0.5 -0.5 0.25 true true true 255 0 144 15996 186 16128 true 4 true true

PcmToMuLaw

func PcmToMuLaw(sample: int): int
One 16 bit sample as one mu-law byte. Telephony in North America and Japan.

The example for this item is shown with the ones above: audio-Pcm.salam

ALawToPcm

func ALawToPcm(b: int): int
One A-law byte back to a 16 bit sample, following G.711.

The example for this item is shown with the ones above: audio-Pcm.salam

PcmToALaw

func PcmToALaw(sample: int): int
One 16 bit sample as one A-law byte. Telephony in Europe.

The example for this item is shown with the ones above: audio-Pcm.salam

PcmDepthValid

func PcmDepthValid(bits: int): bool
True for the four integer depths the PCM helpers accept: 8, 16, 24 and 32.

The example for this item is shown with the ones above: audio-Pcm.salam

DecodePcm

func DecodePcm(p: void*, off: int, c &: Clip, bits: int, big: bool, unsigned8: bool): bool
Fills a clip from raw integer PCM at an offset, at any of the four depths, either byte order. 8 bit is unsigned in WAVE and signed in AIFF and au, which is what the last argument picks.

The example for this item is shown with the ones above: audio-Pcm.salam

DecodeFloatPcm

func DecodeFloatPcm(p: void*, total: int, off: int, c &: Clip, bits: int, big: bool): bool
Fills a clip from raw IEEE floating point samples, 32 or 64 bit, either byte order.

The example for this item is shown with the ones above: audio-Pcm.salam

DecodeLawPcm

func DecodeLawPcm(p: void*, off: int, c &: Clip, alaw: bool): bool
Fills a clip from A-law or mu-law bytes, one byte per sample.

The example for this item is shown with the ones above: audio-Pcm.salam

EncodePcm

func EncodePcm(b &: compress.ByteBuf, c: Clip, bits: int, big: bool, unsigned8: bool)
Writes a clip as raw integer PCM at a depth and byte order, rounding and clamping as it goes.

The example for this item is shown with the ones above: audio-Pcm.salam

EncodeFloatPcm

func EncodeFloatPcm(b &: compress.ByteBuf, c: Clip, bits: int, big: bool)
Writes a clip as raw IEEE floating point samples, which keeps them exactly.

The example for this item is shown with the ones above: audio-Pcm.salam

EncodeLawPcm

func EncodeLawPcm(b &: compress.ByteBuf, c: Clip, alaw: bool)
Writes a clip as A-law or mu-law bytes.

The example for this item is shown with the ones above: audio-Pcm.salam