Claude Code for sunau: Python Sun AU Audio Format — Claude Skills 360 Blog
Blog / AI / Claude Code for sunau: Python Sun AU Audio Format
AI

Claude Code for sunau: Python Sun AU Audio Format

Published: October 28, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s sunau module reads and writes Sun AU (.au) and NeXT (.snd) audio files. import sunau. Open for reading: f = sunau.open(path, 'r'). Open for writing: f = sunau.open(path, 'w'). Reader attributes: .getnchannels() → int; .getsampwidth() → bytes per sample (1, 2, 3, or 4); .getframerate() → Hz; .getnframes() → total frames; .getcomptype()'ULAW', 'ALAW', or 'NONE'; .getcompname() → human-readable compression description; .getparams()namedtuple(nchannels, sampwidth, framerate, nframes, comptype, compname). Read: .readframes(n) → bytes; .rewind(). Writer setup (before any writeframes): .setnchannels(n), .setsampwidth(n), .setframerate(n), .setcomptype(type, name) — defaults to 'NONE'/'not compressed'. Write: .writeframes(bytes). Close: .close() or use as context manager. AU files store headers in big-endian byte order; µ-law (ULAW) is 8-bit encoded speech-quality audio at 8000 Hz common in telephony. Note: sunau is deprecated in Python 3.11 and removed in 3.13 — include a compatibility guard. Claude Code generates AU readers, WAV↔AU converters, µ-law decoders, and multi-format audio inspectors.

CLAUDE.md for sunau

## sunau Stack
- Stdlib: import sunau  (deprecated 3.11, removed 3.13 — guard with try/except)
- Read:   with sunau.open(path, 'r') as f:
-             params = f.getparams()   # nchannels sampwidth framerate nframes
-             raw = f.readframes(f.getnframes())
- Write:  with sunau.open(path, 'w') as f:
-             f.setnchannels(1); f.setsampwidth(2); f.setframerate(8000)
-             f.writeframes(pcm_bytes)
- Note:   AU is big-endian; ULAW compression is common in telephony .au files

sunau Sun AU Audio Pipeline

# app/sunauutil.py — read, write, inspect, convert, decode ULAW
from __future__ import annotations

import array
import io
import struct
import wave
from dataclasses import dataclass
from pathlib import Path

# Guard for Python 3.13+ where sunau is removed
try:
    import sunau as _sunau
    _SUNAU_AVAILABLE = True
except ImportError:
    _SUNAU_AVAILABLE = False


# ─────────────────────────────────────────────────────────────────────────────
# 1. File inspection
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class AuInfo:
    path:        str
    channels:    int
    sampwidth:   int      # bytes per sample
    framerate:   int      # Hz
    nframes:     int
    comptype:    str      # "ULAW", "ALAW", "NONE"
    compname:    str
    duration:    float    # seconds

    @property
    def bit_depth(self) -> int:
        return self.sampwidth * 8

    def __str__(self) -> str:
        return (
            f"{Path(self.path).name}: "
            f"{self.channels}ch  {self.framerate}Hz  "
            f"{self.bit_depth}bit  "
            f"{self.comptype}  "
            f"{self.nframes}fr  "
            f"{self.duration:.3f}s"
        )


def au_info(path: str | Path) -> AuInfo:
    """
    Read metadata from a Sun AU file without decoding the audio data.

    Example:
        info = au_info("voice.au")
        print(info)
    """
    if not _SUNAU_AVAILABLE:
        raise ImportError("sunau not available (Python 3.13+ removed it)")
    with _sunau.open(str(path), "r") as f:
        ch    = f.getnchannels()
        sw    = f.getsampwidth()
        rate  = f.getframerate()
        nfr   = f.getnframes()
        ct    = f.getcomptype()
        cn    = f.getcompname()
    duration = nfr / rate if rate else 0.0
    return AuInfo(
        path=str(path),
        channels=ch,
        sampwidth=sw,
        framerate=rate,
        nframes=nfr,
        comptype=ct,
        compname=cn,
        duration=duration,
    )


def read_au(path: str | Path) -> tuple[AuInfo, bytes]:
    """
    Read an AU file, returning (AuInfo, raw_bytes).
    Raw bytes are in the file's native encoding — use decode_ulaw() if ULAW.

    Example:
        info, raw = read_au("voice.au")
        if info.comptype == "ULAW":
            pcm = decode_ulaw(raw)
    """
    if not _SUNAU_AVAILABLE:
        raise ImportError("sunau not available")
    with _sunau.open(str(path), "r") as f:
        info_obj = au_info(path)
        f.rewind()
        raw = f.readframes(f.getnframes())
    return info_obj, raw


# ─────────────────────────────────────────────────────────────────────────────
# 2. Write AU files
# ─────────────────────────────────────────────────────────────────────────────

def write_au(
    path: str | Path,
    pcm_bytes: bytes,
    channels: int = 1,
    sampwidth: int = 2,
    framerate: int = 8000,
    comptype: str = "NONE",
) -> None:
    """
    Write raw PCM (or ULAW/ALAW) bytes to a Sun AU file.

    Example:
        write_au("output.au", pcm_bytes, channels=1, sampwidth=2, framerate=8000)
    """
    if not _SUNAU_AVAILABLE:
        raise ImportError("sunau not available")
    compname = {"NONE": "not compressed", "ULAW": "CCITT G.711 u-law",
                "ALAW": "CCITT G.711 A-law"}.get(comptype.upper(), "not compressed")
    with _sunau.open(str(path), "w") as f:
        f.setnchannels(channels)
        f.setsampwidth(sampwidth)
        f.setframerate(framerate)
        f.setcomptype(comptype.upper(), compname)
        f.writeframes(pcm_bytes)


# ─────────────────────────────────────────────────────────────────────────────
# 3. µ-law (ULAW) codec (pure Python — works even without sunau)
# ─────────────────────────────────────────────────────────────────────────────

# ITU-T G.711 µ-law (ULAW) decode table — 256 entries, output is 16-bit signed
_ULAW_TABLE: list[int] = []

def _build_ulaw_table() -> None:
    """Build the ULAW decode lookup table once at import time."""
    for byte in range(256):
        # Invert all bits (ULAW encoding inverts the byte)
        b = ~byte & 0xFF
        sign = b & 0x80
        exp  = (b >> 4) & 0x07
        mant = b & 0x0F
        val  = ((mant << 3) + 132) << exp
        val -= 132
        _ULAW_TABLE.append(-val if sign else val)

_build_ulaw_table()


def decode_ulaw(data: bytes) -> bytes:
    """
    Decode CCITT G.711 µ-law compressed bytes to 16-bit signed little-endian PCM.

    Example:
        info, ulaw_bytes = read_au("voice.au")
        if info.comptype == "ULAW":
            pcm16 = decode_ulaw(ulaw_bytes)
    """
    out = array.array("h", (_ULAW_TABLE[b] for b in data))
    return out.tobytes()


def encode_ulaw(pcm_bytes: bytes) -> bytes:
    """
    Encode 16-bit signed little-endian PCM to CCITT G.711 µ-law bytes.
    Input must be 16-bit PCM (2 bytes per sample).

    Example:
        ulaw = encode_ulaw(pcm16_bytes)
        write_au("compressed.au", ulaw, comptype="ULAW", sampwidth=1)
    """
    MU = 255
    out = bytearray()
    samples = array.array("h", pcm_bytes)
    for sample in samples:
        if sample < 0:
            sign = 0x80
            sample = -sample
        else:
            sign = 0
        sample = min(sample, 32635)
        sample += 132

        # Find exponent
        exp = 7
        exp_mask = 0x4000
        while exp > 0 and not (sample & exp_mask):
            exp -= 1
            exp_mask >>= 1

        mantissa = (sample >> max(exp + 3, 0)) & 0x0F
        encoded = ~(sign | (exp << 4) | mantissa) & 0xFF
        out.append(encoded)
    return bytes(out)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Format converters
# ─────────────────────────────────────────────────────────────────────────────

def au_to_wav(src: str | Path, dst: str | Path) -> AuInfo:
    """
    Convert an AU file to WAV. Handles ULAW → PCM16 expansion automatically.
    Returns AuInfo for the source file.

    Example:
        info = au_to_wav("voice.au", "voice.wav")
        print(info)
    """
    info, raw = read_au(src)

    # Decode ULAW if needed
    if info.comptype == "ULAW":
        pcm = decode_ulaw(raw)
        sampwidth = 2
    else:
        pcm = raw
        sampwidth = info.sampwidth

    with wave.open(str(dst), "wb") as wf:
        wf.setnchannels(info.channels)
        wf.setsampwidth(sampwidth)
        wf.setframerate(info.framerate)
        wf.writeframes(pcm)
    return info


def wav_to_au(src: str | Path, dst: str | Path, comptype: str = "NONE") -> None:
    """
    Convert a WAV file to AU format.
    Set comptype="ULAW" to encode as µ-law (reduces to 8-bit telephony format).

    Example:
        wav_to_au("speech.wav", "speech.au", comptype="ULAW")
    """
    if not _SUNAU_AVAILABLE:
        raise ImportError("sunau not available")
    with wave.open(str(src), "rb") as wf:
        channels  = wf.getnchannels()
        sampwidth = wf.getsampwidth()
        framerate = wf.getframerate()
        pcm       = wf.readframes(wf.getnframes())

    if comptype.upper() == "ULAW":
        # Encode PCM16 → ULAW (requires 16-bit input)
        raw = encode_ulaw(pcm) if sampwidth == 2 else pcm
        sampwidth = 1
    else:
        raw = pcm

    write_au(dst, raw, channels=channels, sampwidth=sampwidth,
             framerate=framerate, comptype=comptype.upper())


def au_from_bytes(data: bytes) -> tuple[AuInfo, bytes]:
    """
    Parse an AU file from an in-memory bytes object.

    Example:
        with open("voice.au", "rb") as f:
            data = f.read()
        info, frames = au_from_bytes(data)
    """
    if not _SUNAU_AVAILABLE:
        raise ImportError("sunau not available")
    buf = io.BytesIO(data)
    with _sunau.open(buf, "r") as f:
        nch   = f.getnchannels()
        sw    = f.getsampwidth()
        rate  = f.getframerate()
        nfr   = f.getnframes()
        ct    = f.getcomptype()
        cn    = f.getcompname()
        raw   = f.readframes(nfr)
    dur = nfr / rate if rate else 0.0
    info = AuInfo(path="<bytes>", channels=nch, sampwidth=sw,
                  framerate=rate, nframes=nfr, comptype=ct,
                  compname=cn, duration=dur)
    return info, raw


# ─────────────────────────────────────────────────────────────────────────────
# 5. Batch inspector
# ─────────────────────────────────────────────────────────────────────────────

def scan_au_files(directory: str | Path, recursive: bool = False) -> list[AuInfo]:
    """
    Scan a directory for .au files and return AuInfo for each.

    Example:
        for info in scan_au_files("/usr/share/sounds"):
            print(info)
    """
    root = Path(directory)
    pattern = "**/*.au" if recursive else "*.au"
    results = []
    for p in sorted(root.glob(pattern)):
        try:
            results.append(au_info(p))
        except Exception:
            pass
    return results


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import tempfile
    print("=== sunau demo ===")

    if not _SUNAU_AVAILABLE:
        print("  sunau not available (Python 3.13+)")
        print("  Demonstrating ULAW codec only:")
        # Synthesize a 440 Hz tone in 16-bit PCM
        import math
        rate = 8000; dur = 0.5
        samples = array.array("h")
        for i in range(int(rate * dur)):
            v = int(10000 * math.sin(2 * math.pi * 440 * i / rate))
            samples.append(max(-32768, min(32767, v)))
        pcm = samples.tobytes()
        ulaw = encode_ulaw(pcm)
        back = decode_ulaw(ulaw)
        print(f"  PCM16 samples: {len(samples)}")
        print(f"  ULAW bytes:    {len(ulaw)} ({len(ulaw)/len(pcm)*100:.0f}% of PCM)")
        print(f"  Decoded back:  {len(back)//2} samples")
        raise SystemExit(0)

    with tempfile.TemporaryDirectory() as tmp:
        # ── synthesize a short PCM tone ────────────────────────────────────────
        import math
        rate = 8000; dur = 1.0; freq = 440.0
        samples = array.array("h")
        for i in range(int(rate * dur)):
            v = int(16000 * math.sin(2 * math.pi * freq * i / rate))
            samples.append(max(-32768, min(32767, v)))
        pcm16 = samples.tobytes()

        # ── write AU (uncompressed) ────────────────────────────────────────────
        au_path = Path(tmp) / "tone.au"
        write_au(au_path, pcm16, channels=1, sampwidth=2, framerate=rate)
        print(f"\n--- wrote {au_path.name} ({au_path.stat().st_size} bytes) ---")

        # ── read back and inspect ──────────────────────────────────────────────
        info, raw = read_au(au_path)
        print(f"--- au_info: {info} ---")

        # ── write ULAW AU ──────────────────────────────────────────────────────
        ulaw_bytes = encode_ulaw(pcm16)
        ulaw_path = Path(tmp) / "tone_ulaw.au"
        write_au(ulaw_path, ulaw_bytes, channels=1, sampwidth=1,
                 framerate=rate, comptype="ULAW")
        print(f"\n--- ULAW AU: {ulaw_path.stat().st_size} bytes "
              f"({ulaw_path.stat().st_size / au_path.stat().st_size * 100:.0f}% of PCM) ---")

        # ── round-trip au_to_wav ───────────────────────────────────────────────
        wav_path = Path(tmp) / "tone.wav"
        src_info = au_to_wav(au_path, wav_path)
        print(f"\n--- au_to_wav ---")
        print(f"  source: {src_info}")
        with wave.open(str(wav_path), "rb") as wf:
            print(f"  WAV:    {wf.getnchannels()}ch  {wf.getframerate()}Hz  "
                  f"{wf.getsampwidth()*8}bit  {wf.getnframes()}fr")

        # ── au_from_bytes ──────────────────────────────────────────────────────
        print("\n--- au_from_bytes ---")
        raw_file = au_path.read_bytes()
        mem_info, mem_frames = au_from_bytes(raw_file)
        print(f"  {mem_info}")
        print(f"  frames match: {raw == mem_frames}")

        # ── ULAW encode/decode round-trip ──────────────────────────────────────
        print("\n--- ULAW round-trip (first 8 samples) ---")
        ulaw8 = encode_ulaw(pcm16[:16])
        decoded = decode_ulaw(ulaw8)
        orig_s = array.array("h", pcm16[:16])
        dec_s  = array.array("h", decoded)
        for o, d in zip(orig_s, dec_s):
            print(f"  orig={o:6d}  decoded={d:6d}  err={abs(o-d):4d}")

    print("\n=== done ===")

For the wave alternative — Python’s wave module reads and writes RIFF WAV files, which are the modern ubiquitous audio container format with uncompressed 16-bit PCM — use wave for all general-purpose audio I/O since WAV is universally supported; use sunau only when you specifically need to read .au/.snd files from legacy Unix workstations, NeXT systems, or telephony archives, or when you need µ-law ULAW encoding for 8-bit speech compression. For the audioop alternative — audioop.ulaw2lin(data, width) and audioop.lin2ulaw(data, width) convert between µ-law and linear PCM directly — note that both audioop and sunau are deprecated in Python 3.11 and removed in Python 3.13, so production code should use the pure-Python encode_ulaw()/decode_ulaw() implementations above or a library like soundfile (PyPI) which wraps libsndfile for ULAW/ALAW/FLAC/OGG support via a stable API. The Claude Skills 360 bundle includes sunau skill sets covering AuInfo with au_info()/read_au() inspection, write_au() writer, decode_ulaw()/encode_ulaw() pure-Python G.711 codec, au_to_wav()/wav_to_au()/au_from_bytes() converters, and scan_au_files() directory scanner. Start with the free tier to try AU audio patterns and sunau pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free