Claude Code for aifc: Python AIFF and AIFF-C Audio Format — Claude Skills 360 Blog
Blog / AI / Claude Code for aifc: Python AIFF and AIFF-C Audio Format
AI

Claude Code for aifc: Python AIFF and AIFF-C Audio Format

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

Python’s aifc module reads and writes AIFF (Audio Interchange File Format) and AIFF-C (compressed AIFF) files, the native audio format on Apple platforms and professional audio workstations. import aifc. Open for reading: f = aifc.open(path, 'r'). Open for writing: f = aifc.open(path, 'w'). Reader attributes: .getnchannels() → int; .getsampwidth() → bytes (1, 2, 3, or 4); .getframerate() → Hz; .getnframes() → total frames; .getcomptype()b'NONE', b'sowt', b'ulaw', b'alaw', or custom 4-byte code; .getcompname() → human-readable description; .getparams() → namedtuple. Read: .readframes(n) → bytes (big-endian for AIFF, comp-dependent for AIFF-C); .rewind(). Writer setup: .setnchannels(n), .setsampwidth(n), .setframerate(n), .setcomptype(type, name) — defaults to b'NONE'/'not compressed'. Markers: .getmarkers() → list of (id, pos, name) tuples; .setmark(id, pos, name). Write: .writeframes(bytes). Close: .close() or context manager. AIFF uses big-endian PCM; AIFF-C sowt extension uses little-endian (compatible with Core Audio). Note: aifc is deprecated in Python 3.11 and removed in 3.13 — include compatibility guard. Claude Code generates AIFF readers, WAV↔AIFF converters, marker extractors, and multi-format audio inspectors.

CLAUDE.md for aifc

## aifc Stack
- Stdlib: import aifc  (deprecated 3.11, removed 3.13 — guard with try/except)
- Read:   with aifc.open(path, 'r') as f:
-             params = f.getparams()     # nchannels sampwidth framerate nframes
-             raw = f.readframes(f.getnframes())
-             marks = f.getmarkers()     # [(id, pos, name), ...]
- Write:  with aifc.open(path, 'w') as f:
-             f.setnchannels(2); f.setsampwidth(2); f.setframerate(44100)
-             f.setcomptype(b'NONE', b'not compressed')
-             f.writeframes(pcm_bytes)   # big-endian PCM for AIFF
- Note:   AIFF = big-endian; AIFF-C sowt = little-endian (Core Audio)

aifc AIFF Audio Pipeline

# app/aifcutil.py — read, write, inspect, convert, swap endian, markers
from __future__ import annotations

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

# Guard for Python 3.13+ where aifc is removed
try:
    import aifc as _aifc
    _AIFC_AVAILABLE = True
except ImportError:
    _AIFC_AVAILABLE = False


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

@dataclass
class AiffInfo:
    path:       str
    channels:   int
    sampwidth:  int     # bytes per sample
    framerate:  int     # Hz
    nframes:    int
    comptype:   bytes   # b'NONE', b'sowt', b'ulaw', etc.
    compname:   bytes
    markers:    list[tuple[int, int, bytes]]   # (id, pos, name)
    duration:   float

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

    @property
    def is_compressed(self) -> bool:
        return self.comptype not in (b'NONE', b'sowt')

    @property
    def is_little_endian(self) -> bool:
        return self.comptype == b'sowt'

    def __str__(self) -> str:
        endian = "LE" if self.is_little_endian else "BE"
        return (
            f"{Path(self.path).name}: "
            f"{self.channels}ch  {self.framerate}Hz  "
            f"{self.bit_depth}bit-{endian}  "
            f"{self.comptype.decode(errors='replace')}  "
            f"{self.nframes}fr  "
            f"{self.duration:.3f}s  "
            f"markers={len(self.markers)}"
        )


def aiff_info(path: str | Path) -> AiffInfo:
    """
    Read AIFF/AIFF-C metadata without decoding audio data.

    Example:
        info = aiff_info("recording.aif")
        print(info)
        for mark in info.markers:
            print(f"  marker id={mark[0]} pos={mark[1]} name={mark[2]}")
    """
    if not _AIFC_AVAILABLE:
        raise ImportError("aifc not available (Python 3.13+ removed it)")
    with _aifc.open(str(path), "r") as f:
        ch   = f.getnchannels()
        sw   = f.getsampwidth()
        rate = f.getframerate()
        nfr  = f.getnframes()
        ct   = f.getcomptype()
        cn   = f.getcompname()
        marks = f.getmarkers() or []
    dur = nfr / rate if rate else 0.0
    return AiffInfo(
        path=str(path), channels=ch, sampwidth=sw,
        framerate=rate, nframes=nfr, comptype=ct, compname=cn,
        markers=list(marks), duration=dur,
    )


def read_aiff(path: str | Path) -> tuple[AiffInfo, bytes]:
    """
    Read an AIFF/AIFF-C file, returning (AiffInfo, raw_bytes).
    Raw bytes are in the file's native endianness — use swap_endian() if needed.

    Example:
        info, raw = read_aiff("track.aif")
        if info.is_little_endian:
            pcm_be = swap_endian(raw, info.sampwidth)
    """
    if not _AIFC_AVAILABLE:
        raise ImportError("aifc not available")
    with _aifc.open(str(path), "r") as f:
        info = aiff_info(path)
        f.rewind()
        raw = f.readframes(f.getnframes())
    return info, raw


# ─────────────────────────────────────────────────────────────────────────────
# 2. Write AIFF files
# ─────────────────────────────────────────────────────────────────────────────

def write_aiff(
    path: str | Path,
    pcm_bytes: bytes,
    channels: int = 2,
    sampwidth: int = 2,
    framerate: int = 44100,
    markers: list[tuple[int, int, str]] | None = None,
    little_endian: bool = False,
) -> None:
    """
    Write raw PCM bytes to an AIFF (big-endian) or AIFF-C sowt (little-endian) file.

    Example:
        write_aiff("output.aif", pcm_be_bytes, channels=2, framerate=44100)
        write_aiff("output_le.aif", pcm_le_bytes, little_endian=True)
    """
    if not _AIFC_AVAILABLE:
        raise ImportError("aifc not available")
    with _aifc.open(str(path), "w") as f:
        f.setnchannels(channels)
        f.setsampwidth(sampwidth)
        f.setframerate(framerate)
        if little_endian:
            f.setcomptype(b"sowt", b"AIFF-C little-endian")
        else:
            f.setcomptype(b"NONE", b"not compressed")
        if markers:
            for mark_id, pos, name in markers:
                f.setmark(mark_id, pos, name.encode() if isinstance(name, str) else name)
        f.writeframes(pcm_bytes)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Endian swap helper
# ─────────────────────────────────────────────────────────────────────────────

def swap_endian(pcm: bytes, sampwidth: int) -> bytes:
    """
    Swap byte order of 16-bit or 32-bit PCM samples.
    Use when converting between AIFF (big-endian) and WAV/AIFF-C sowt (little-endian).

    Example:
        # AIFF big-endian → little-endian for WAV write
        pcm_le = swap_endian(pcm_be, sampwidth=2)
    """
    if sampwidth == 1:
        return pcm   # 8-bit is unsigned, no byte-swap needed
    if sampwidth == 2:
        fmt = f">{len(pcm)//2}h"
        samples = struct.unpack(fmt, pcm)
        return struct.pack(f"<{len(samples)}h", *samples)
    if sampwidth == 3:
        # 24-bit: swap 3-byte groups
        out = bytearray()
        for i in range(0, len(pcm), 3):
            out.extend(pcm[i:i+3][::-1])
        return bytes(out)
    if sampwidth == 4:
        fmt = f">{len(pcm)//4}i"
        samples = struct.unpack(fmt, pcm)
        return struct.pack(f"<{len(samples)}i", *samples)
    raise ValueError(f"Unsupported sampwidth: {sampwidth}")


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

def aiff_to_wav(src: str | Path, dst: str | Path) -> AiffInfo:
    """
    Convert an AIFF/AIFF-C file to WAV.
    Automatically swaps byte order from big-endian AIFF to little-endian WAV.
    Returns AiffInfo for the source file.

    Example:
        info = aiff_to_wav("track.aif", "track.wav")
    """
    info, raw = read_aiff(src)

    # WAV wants little-endian; swap if AIFF big-endian
    if info.sampwidth > 1 and not info.is_little_endian:
        pcm_le = swap_endian(raw, info.sampwidth)
    else:
        pcm_le = raw

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


def wav_to_aiff(src: str | Path, dst: str | Path, little_endian: bool = False) -> None:
    """
    Convert a WAV file to AIFF. By default writes big-endian AIFF.
    Set little_endian=True to produce AIFF-C sowt (compatible with Core Audio).

    Example:
        wav_to_aiff("speech.wav", "speech.aif")
        wav_to_aiff("speech.wav", "speech_le.aif", little_endian=True)
    """
    if not _AIFC_AVAILABLE:
        raise ImportError("aifc not available")
    with wave.open(str(src), "rb") as wf:
        channels  = wf.getnchannels()
        sampwidth = wf.getsampwidth()
        framerate = wf.getframerate()
        pcm_le    = wf.readframes(wf.getnframes())

    # WAV is little-endian; AIFF wants big-endian
    if not little_endian and sampwidth > 1:
        pcm = swap_endian(pcm_le, sampwidth)
    else:
        pcm = pcm_le

    write_aiff(dst, pcm, channels=channels, sampwidth=sampwidth,
               framerate=framerate, little_endian=little_endian)


def aiff_from_bytes(data: bytes) -> tuple[AiffInfo, bytes]:
    """
    Parse an AIFF/AIFF-C file from an in-memory bytes object.

    Example:
        with open("recording.aif", "rb") as f:
            data = f.read()
        info, frames = aiff_from_bytes(data)
    """
    if not _AIFC_AVAILABLE:
        raise ImportError("aifc not available")
    buf = io.BytesIO(data)
    with _aifc.open(buf, "r") as f:
        nch   = f.getnchannels()
        sw    = f.getsampwidth()
        rate  = f.getframerate()
        nfr   = f.getnframes()
        ct    = f.getcomptype()
        cn    = f.getcompname()
        marks = f.getmarkers() or []
        raw   = f.readframes(nfr)
    dur = nfr / rate if rate else 0.0
    info = AiffInfo(path="<bytes>", channels=nch, sampwidth=sw,
                    framerate=rate, nframes=nfr, comptype=ct,
                    compname=cn, markers=list(marks), duration=dur)
    return info, raw


# ─────────────────────────────────────────────────────────────────────────────
# 5. Marker utilities
# ─────────────────────────────────────────────────────────────────────────────

def extract_markers(path: str | Path) -> list[tuple[int, float, str]]:
    """
    Extract AIFF markers as (id, time_seconds, name) tuples.
    Returns empty list if no markers present.

    Example:
        for mark_id, t, name in extract_markers("session.aif"):
            print(f"  marker {mark_id} @ {t:.3f}s: {name}")
    """
    info = aiff_info(path)
    result = []
    rate = info.framerate or 1
    for mark_id, pos, name in info.markers:
        t = pos / rate
        name_str = name.decode(errors="replace") if isinstance(name, bytes) else name
        result.append((mark_id, t, name_str))
    return result


def add_markers_to_aiff(
    src: str | Path,
    dst: str | Path,
    markers: list[tuple[int, float, str]],
) -> None:
    """
    Copy an AIFF file and add new time-based markers (seconds).

    Example:
        add_markers_to_aiff("raw.aif", "marked.aif",
                            [(1, 2.5, "verse"), (2, 45.0, "chorus")])
    """
    info, raw = read_aiff(src)
    int_marks = [(mid, int(t * info.framerate), name)
                 for mid, t, name in markers]
    write_aiff(dst, raw, channels=info.channels,
               sampwidth=info.sampwidth, framerate=info.framerate,
               markers=int_marks, little_endian=info.is_little_endian)


def scan_aiff_files(directory: str | Path, recursive: bool = False) -> list[AiffInfo]:
    """
    Scan a directory for .aif and .aiff files, returning AiffInfo for each.

    Example:
        for info in scan_aiff_files("/Music/sessions", recursive=True):
            print(info)
    """
    root = Path(directory)
    results = []
    patterns = ["**/*.aif", "**/*.aiff"] if recursive else ["*.aif", "*.aiff"]
    for pattern in patterns:
        for p in sorted(root.glob(pattern)):
            try:
                results.append(aiff_info(p))
            except Exception:
                pass
    return results


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

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

    if not _AIFC_AVAILABLE:
        print("  aifc not available (Python 3.13+)")
        print("  Demonstrating swap_endian only:")
        be_samples = struct.pack(">4h", 100, -100, 200, -200)
        le_samples = swap_endian(be_samples, 2)
        back = swap_endian(le_samples, 2)
        print(f"  BE: {list(struct.unpack('>4h', be_samples))}")
        print(f"  LE: {list(struct.unpack('<4h', le_samples))}")
        print(f"  roundtrip: {be_samples == back}")
        raise SystemExit(0)

    with tempfile.TemporaryDirectory() as tmp:
        # ── synthesize 440 Hz stereo PCM (big-endian for AIFF) ────────────────
        rate = 44100; dur = 0.5; freq = 440.0
        n = int(rate * dur)
        be_bytes = bytearray()
        for i in range(n):
            val = int(16000 * math.sin(2 * math.pi * freq * i / rate))
            val = max(-32768, min(32767, val))
            be_bytes.extend(struct.pack(">h", val))  # big-endian L
            be_bytes.extend(struct.pack(">h", val))  # big-endian R (same)
        pcm_be = bytes(be_bytes)

        # ── write AIFF ────────────────────────────────────────────────────────
        aif_path = Path(tmp) / "tone.aif"
        write_aiff(aif_path, pcm_be, channels=2, sampwidth=2, framerate=rate,
                   markers=[(1, n // 4, "quarter"), (2, n // 2, "half")])
        print(f"\n--- wrote {aif_path.name} ({aif_path.stat().st_size} bytes) ---")

        # ── read + inspect ────────────────────────────────────────────────────
        info, raw = read_aiff(aif_path)
        print(f"--- aiff_info: {info} ---")

        # ── markers ───────────────────────────────────────────────────────────
        print("\n--- extract_markers ---")
        for mid, t, name in extract_markers(aif_path):
            print(f"  id={mid}  t={t:.3f}s  name={name!r}")

        # ── endian swap ───────────────────────────────────────────────────────
        print("\n--- swap_endian round-trip (first 4 samples) ---")
        le = swap_endian(pcm_be[:16], 2)
        back = swap_endian(le, 2)
        print(f"  BE→LE→BE match: {pcm_be[:16] == back}")

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

        # ── wav_to_aiff round-trip ────────────────────────────────────────────
        aif2_path = Path(tmp) / "tone_roundtrip.aif"
        wav_to_aiff(wav_path, aif2_path)
        info2, raw2 = read_aiff(aif2_path)
        print(f"\n--- wav_to_aiff: {info2} ---")
        # Compare frames (should be identical after double swap)
        print(f"  frames match original: {pcm_be == raw2}")

        # ── aiff_from_bytes ───────────────────────────────────────────────────
        print("\n--- aiff_from_bytes ---")
        file_bytes = aif_path.read_bytes()
        mem_info, mem_raw = aiff_from_bytes(file_bytes)
        print(f"  {mem_info}")
        print(f"  frames match: {raw == mem_raw}")

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

For the wave alternative — wave reads and writes RIFF WAV files (little-endian PCM), which is the universal audio interchange format supported by every DAW, audio library, and operating system — use wave for all modern audio I/O; use aifc only when reading .aif/.aiff files from Apple macOS, professional audio workstations, or archives that use AIFF’s big-endian PCM or AIFF-C compression, keeping in mind that aifc is removed in Python 3.13 so production code needs a fallback to soundfile (PyPI) or pure struct-based parsing. For the chunk alternative — Python’s chunk module parses generic IFF chunk-based binary formats (the basis of both RIFF/WAV and IFF/AIFF) at the raw block level — use chunk when you need to inspect or extract individual IFF chunks from an AIFF file without using aifc’s higher-level API, or when dealing with non-standard AIFF extensions that aifc ignores; use aifc for the standard get/set/readframes/writeframes interface. The Claude Skills 360 bundle includes aifc skill sets covering AiffInfo with aiff_info()/read_aiff() inspection, write_aiff() writer with marker support, swap_endian() byte-order conversion, aiff_to_wav()/wav_to_aiff()/aiff_from_bytes() converters, and extract_markers()/add_markers_to_aiff()/scan_aiff_files() marker utilities. Start with the free tier to try AIFF audio patterns and aifc 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