audio/wav
Reading and writing WAVE files: PCM, floating point, A-law and mu-law.
import audio.wav
WAVE is the format to reach for when nothing should be lost. It is a RIFF container holding a fmt chunk that says what the samples are and a data chunk holding them, and this package reads and writes it without any outside library.
Integer samples at 8, 16, 24 and 32 bits are all handled, as are IEEE floating point samples and the two eight bit companding schemes from telephony, A-law and mu-law. OpenWAV and SaveWAV are the short way in; audio/codec is shorter still when you do not want to name the format at all.
Things to know
- 8, 16 and 24 bit PCM makes the round trip through a clip exactly. A true 32 bit integer file loses its lowest 8 bits, because a clip holds 32 bit floats; write such a file with
EncodeWAVFloatinstead, which keeps every bit. EncodeWAVwrites at the depth the clip remembers inbits, and 16 bit when the clip has none.EncodeWAVBitstakes the depth from you.- A companded file is a quarter the size of 24 bit PCM and does lose a little, most of it where the sound is loud. Near silence the error is smaller than one step of 16 bit PCM.
ProbeWAVreads the header alone, so a listing of a thousand files does not have to decode any of them.Chunkshands back a vector you must free.FindChunkallocates nothing, and is what the decoder uses.
Types
Format
struct Format| Name | Type | Description |
|---|---|---|
codec | int = 0 | Which codec the samples use, one of the Codec numbers. |
channels | int = 0 | How many channels. |
rate | int = 0 | Frames per second. |
bits | int = 0 | Bits per sample. |
block_align | int = 0 | How many bytes one frame takes. |
byte_rate | int = 0 | Bytes per second of audio. |
import audio
import audio.wav
import compress
func main:
mut c := audio.New(22050, 2, 100)
audio.SetSample(c, 7, 1, 0.5)
buf := wav.EncodeWAVBits(c, 24)
println wav.IsWAV(buf.data, buf.len), wav.IsWAV(buf.data, 4)
mut f := wav.Format {}
println wav.ReadFormat(buf.data, buf.len, f)
println f.codec == wav.CodecPCM, f.channels, f.rate, f.bits, f.block_align, f.byte_rate
chunks := wav.Chunks(buf.data, buf.len)
println chunks.len()
mut ck := wav.Chunk {}
println wav.FindChunk(buf.data, buf.len, wav.Tag("data"), ck), ck.offset, ck.size
mut miss := wav.Chunk {}
println wav.FindChunk(buf.data, buf.len, wav.Tag("cue "), miss)
mut ok := false
info := wav.ProbeWAV(buf.data, buf.len, ok)
println ok, info.rate, info.channels, info.bits, info.frames, info.lossless
mut dok := false
back := wav.DecodeWAV(buf.data, buf.len, dok)
println dok, back.frames, back.bits, audio.Sample(back, 7, 1)
compress.BufFree(buf)
audio.Free(c)
endChunk
struct Chunk| Name | Type | Description |
|---|---|---|
tag | int = 0 | The four letter tag packed into an integer. Tag builds one from a string. |
offset | int = 0 | Where the body starts in the file. |
size | int = 0 | How many bytes the body holds, never past the end of the file. |
The example for this item is shown with the ones above: wav-Read.salam
Constants and variables
Codec numbers Codec…
| Name | Value |
|---|---|
CodecPCM | 1 |
CodecFloat | 3 |
CodecALaw | 6 |
CodecMuLaw | 7 |
CodecExtensible | 65534 |
import audio
import audio.wav
import compress
import math
func main:
println audio.PcmToMuLaw(0), audio.MuLawToPcm(audio.PcmToMuLaw(0))
println audio.PcmToMuLaw(16000), audio.MuLawToPcm(144)
println audio.PcmToALaw(16000), audio.ALawToPcm(186)
println audio.PcmToALaw(0), audio.ALawToPcm(213)
mut near := 0
mut far := 0
mut v := -32768
until v < 32768:
d := math.AbsI(audio.MuLawToPcm(audio.PcmToMuLaw(v)) - v)
if math.AbsI(v) <= 256:
if d > near:
near = d
end
end
if d > far:
far = d
end
v += 7
end
println near, far
mut c := audio.New(8000, 2, 50)
audio.SetSample(c, 10, 0, 0.5)
mu := wav.EncodeMuLaw(c)
al := wav.EncodeALaw(c)
println compress.BufLen(mu), compress.BufLen(al), compress.BufLen(wav.EncodeWAVBits(c, 16))
mut ok := false
back := wav.DecodeWAV(mu.data, mu.len, ok)
println ok, back.frames, back.channels, back.bits
println math.Abs(audio.Sample(back, 10, 0) - 0.5) < 0.01
compress.BufFree(mu)
compress.BufFree(al)
audio.Free(c)
endFunctions
EncodeWAVBits
func EncodeWAVBits(c: audio.Clip, bits: int): compress.ByteBufimport audio
import audio.wav
import compress
import filepath
import mem
import os
func main:
mut c := audio.New(8000, 1, 100)
audio.SetSample(c, 0, 0, 0.5)
audio.SetSample(c, 1, 0, -0.25)
println compress.BufLen(wav.EncodeWAVBits(c, 8)), compress.BufLen(wav.EncodeWAVBits(c, 16))
println compress.BufLen(wav.EncodeWAVBits(c, 24)), compress.BufLen(wav.EncodeWAVBits(c, 32))
println compress.BufLen(wav.EncodeWAVFloat(c))
println compress.BufLen(wav.EncodeWAV(c))
c.bits = 24
println compress.BufLen(wav.EncodeWAV(c))
println compress.BufLen(wav.EncodeWAVBits(c, 12)), compress.BufLen(wav.EncodeWAV(audio.Clip {}))
path := filepath.Join(os.TempDir(), "salam_wav_write.wav")
defer mem.Free(path as void*)
println wav.SaveWAVBits(c, path, 24)
mut ok := false
disk := wav.OpenWAV(path, ok)
println ok, disk.frames, disk.bits, audio.Sample(disk, 0, 0), audio.Sample(disk, 1, 0)
mut iok := false
info := wav.ProbeFileWAV(path, iok)
println iok, info.frames, info.bits
fpath := filepath.Join(os.TempDir(), "salam_wav_write_f32.wav")
defer mem.Free(fpath as void*)
println wav.SaveWAVFloat(c, fpath)
mut fok := false
fdisk := wav.OpenWAV(fpath, fok)
println fok, fdisk.bits, audio.Sample(fdisk, 1, 0)
mut gone := false
missing := filepath.Join(os.TempDir(), "salam_wav_nope.wav")
defer mem.Free(missing as void*)
wav.OpenWAV(missing, gone)
println gone
audio.Free(c)
endEncodeWAV
func EncodeWAV(c: audio.Clip): compress.ByteBufThe example for this item is shown with the ones above: wav-Write.salam
EncodeWAVFloat
func EncodeWAVFloat(c: audio.Clip): compress.ByteBufThe example for this item is shown with the ones above: wav-Write.salam
EncodeMuLaw
func EncodeMuLaw(c: audio.Clip): compress.ByteBufThe example for this item is shown with the ones above: wav-Law.salam
EncodeALaw
func EncodeALaw(c: audio.Clip): compress.ByteBufThe example for this item is shown with the ones above: wav-Law.salam
SaveWAVBits
func SaveWAVBits(c: audio.Clip, path: str, bits: int): boolThe example for this item is shown with the ones above: wav-Write.salam
SaveWAV
func SaveWAV(c: audio.Clip, path: str): boolThe example for this item is shown with the ones above: wav-Write.salam
SaveWAVFloat
func SaveWAVFloat(c: audio.Clip, path: str): boolThe example for this item is shown with the ones above: wav-Write.salam
Tag
func Tag(s: str): intThe example for this item is shown with the ones above: wav-Read.salam
IsWAV
func IsWAV(p: void*, n: int): boolThe example for this item is shown with the ones above: wav-Read.salam
Chunks
func Chunks(p: void*, n: int): Vector<Chunk>The example for this item is shown with the ones above: wav-Read.salam
FindChunk
func FindChunk(p: void*, n: int, tag: int, out &: Chunk): boolThe example for this item is shown with the ones above: wav-Read.salam
ReadFormat
func ReadFormat(p: void*, n: int, out &: Format): boolThe example for this item is shown with the ones above: wav-Read.salam
ProbeWAV
func ProbeWAV(p: void*, n: int, ok &: bool): audio.InfoThe example for this item is shown with the ones above: wav-Read.salam
DecodeWAV
func DecodeWAV(p: void*, n: int, ok &: bool): audio.ClipThe example for this item is shown with the ones above: wav-Read.salam
OpenWAV
func OpenWAV(path: str, ok &: bool): audio.ClipThe example for this item is shown with the ones above: wav-Read.salam
ProbeFileWAV
func ProbeFileWAV(path: str, ok &: bool): audio.InfoThe example for this item is shown with the ones above: wav-Read.salam