Claude Code for audioop: Python Audio Signal Operations — Claude Skills 360 Blog
Blog / AI / Claude Code for audioop: Python Audio Signal Operations
AI

Claude Code for audioop: Python Audio Signal Operations

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

Python’s audioop module performs sample-level operations on raw PCM audio buffers. import audioop. All functions take (fragment, width) where fragment is bytes and width is the sample byte width: 1 (8-bit unsigned), 2 (16-bit signed), or 4 (32-bit signed). Level: audioop.max(frag, width) → int; audioop.min(frag, width); audioop.minmax(frag, width) → (min, max); audioop.avg(frag, width) → int; audioop.rms(frag, width) → int (power). Gain: audioop.mul(frag, width, factor) → bytes (clip); audioop.bias(frag, width, bias) → bytes; audioop.lin2lin(frag, inwidth, outwidth) → bytes (bit depth conversion). Channels: audioop.tomono(frag, width, lfactor, rfactor) → bytes (stereo→mono); audioop.tostereo(frag, width, lfactor, rfactor) → bytes (mono→stereo). Codecs: audioop.lin2ulaw(frag, width) → bytes; audioop.ulaw2lin(data, width) → bytes; audioop.lin2alaw(frag, width); audioop.alaw2lin(data, width); audioop.lin2adpcm(frag, width, state) → (bytes, state); audioop.adpcm2lin(data, width, state) → (bytes, state). Rate: audioop.ratecv(frag, width, nchannels, inrate, outrate, state, weightA, weightB) → (bytes, state). Combine: audioop.add(frag1, frag2, width) → bytes (mix); audioop.cross(frag, width) → int (zero-crossing count). Note: deprecated 3.11, removed 3.13 — include compatibility guard. Claude Code generates audio normalizers, resampling pipelines, codec converters, stereo mixers, and PCM signal analyzers.

CLAUDE.md for audioop

## audioop Stack
- Stdlib: import audioop  (deprecated 3.11, removed 3.13 — guard with try/except)
- Level:  audioop.rms(frag, width)  audioop.max(frag, width)
- Gain:   audioop.mul(frag, width, factor)   # clip to range
- Codec:  audioop.lin2ulaw(frag, width)   audioop.ulaw2lin(data, width)
- Rate:   frag, state = audioop.ratecv(frag, w, ch, inrate, outrate, None)
- Mix:    audioop.add(frag1, frag2, width)  audioop.tomono(frag, width, 0.5, 0.5)
- Note:   width = bytes per sample: 1=8bit, 2=16bit, 4=32bit

audioop PCM Signal Operations Pipeline

# app/audiooputil.py — level, gain, codec, resample, mix (with py3.13 fallback)
from __future__ import annotations

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

# Guard for Python 3.13+ where audioop is removed
try:
    import audioop as _audioop
    _AUDIOOP_AVAILABLE = True
except ImportError:
    _AUDIOOP_AVAILABLE = False


# ─────────────────────────────────────────────────────────────────────────────
# 1. Level measurement
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class LevelInfo:
    peak:    int     # max absolute sample value
    rms:     int     # root mean square level
    minimum: int     # minimum raw sample
    maximum: int     # maximum raw sample
    width:   int     # bytes per sample

    @property
    def peak_db(self) -> float:
        """Peak level in dBFS (0 dBFS = full scale)."""
        full_scale = (1 << (self.width * 8 - 1)) - 1
        return 20 * math.log10(self.peak / full_scale) if self.peak > 0 else -math.inf

    @property
    def rms_db(self) -> float:
        """RMS level in dBFS."""
        full_scale = (1 << (self.width * 8 - 1)) - 1
        return 20 * math.log10(self.rms / full_scale) if self.rms > 0 else -math.inf

    def __str__(self) -> str:
        return (f"peak={self.peak}  rms={self.rms}  "
                f"peak_db={self.peak_db:.1f}dBFS  rms_db={self.rms_db:.1f}dBFS  "
                f"min={self.minimum}  max={self.maximum}")


def measure_level(frag: bytes, width: int) -> LevelInfo:
    """
    Measure peak, RMS, min, and max of a PCM buffer.

    Example:
        info = measure_level(pcm_bytes, width=2)
        print(info)
    """
    if _AUDIOOP_AVAILABLE:
        peak = _audioop.max(frag, width)
        rms  = _audioop.rms(frag, width)
        mn, mx = _audioop.minmax(frag, width)
    else:
        # Pure Python fallback
        fmt = {1: "B", 2: "<h", 4: "<i"}[width]
        step = width
        unpacked = [struct.unpack_from(fmt, frag, i)[0]
                    for i in range(0, len(frag), step)]
        if not unpacked:
            return LevelInfo(0, 0, 0, 0, width)
        mn = min(unpacked)
        mx = max(unpacked)
        peak = max(abs(mn), abs(mx))
        rms = int(math.sqrt(sum(s * s for s in unpacked) / len(unpacked)))
    return LevelInfo(peak=peak, rms=rms, minimum=mn, maximum=mx, width=width)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Gain / normalization
# ─────────────────────────────────────────────────────────────────────────────

def apply_gain(frag: bytes, width: int, factor: float) -> bytes:
    """
    Multiply all samples by factor, clipping to the valid range.

    Example:
        quieter = apply_gain(pcm_bytes, 2, 0.5)
        louder  = apply_gain(pcm_bytes, 2, 2.0)
    """
    if _AUDIOOP_AVAILABLE:
        return _audioop.mul(frag, width, factor)
    # Pure Python fallback (16-bit only)
    fmt = "<h" if width == 2 else ">b"
    out = bytearray()
    lim = (1 << (width * 8 - 1)) - 1
    for i in range(0, len(frag), width):
        s = struct.unpack_from("<h", frag, i)[0]
        v = max(-lim - 1, min(lim, int(s * factor)))
        out.extend(struct.pack("<h", v))
    return bytes(out)


def normalize(frag: bytes, width: int, target_peak: float = 0.9) -> bytes:
    """
    Normalize a PCM buffer so its peak reaches target_peak fraction of full scale.
    Does nothing if the buffer is silent.

    Example:
        normalized = normalize(pcm_bytes, width=2, target_peak=0.9)
    """
    if _AUDIOOP_AVAILABLE:
        peak = _audioop.max(frag, width)
    else:
        info = measure_level(frag, width)
        peak = info.peak
    if peak == 0:
        return frag
    full_scale = (1 << (width * 8 - 1)) - 1
    factor = (full_scale * target_peak) / peak
    return apply_gain(frag, width, factor)


def add_bias(frag: bytes, width: int, bias: int) -> bytes:
    """
    Add a constant DC bias to each sample (useful for DC offset removal).

    Example:
        centered = add_bias(pcm_bytes, 2, -avg_sample_value)
    """
    if _AUDIOOP_AVAILABLE:
        return _audioop.bias(frag, width, bias)
    out = bytearray()
    lim = (1 << (width * 8 - 1)) - 1
    for i in range(0, len(frag), width):
        s = struct.unpack_from("<h", frag, i)[0]
        v = max(-lim - 1, min(lim, s + bias))
        out.extend(struct.pack("<h", v))
    return bytes(out)


def change_bit_depth(frag: bytes, in_width: int, out_width: int) -> bytes:
    """
    Convert PCM between bit depths (1, 2, or 4 bytes per sample).

    Example:
        pcm_24bit = change_bit_depth(pcm_16bit, in_width=2, out_width=3)
    """
    if _AUDIOOP_AVAILABLE:
        return _audioop.lin2lin(frag, in_width, out_width)
    raise NotImplementedError("lin2lin fallback requires audioop")


# ─────────────────────────────────────────────────────────────────────────────
# 3. Channel conversion
# ─────────────────────────────────────────────────────────────────────────────

def stereo_to_mono(frag: bytes, width: int,
                   left_weight: float = 0.5, right_weight: float = 0.5) -> bytes:
    """
    Mix stereo interleaved PCM to mono.
    frag must be 2*width bytes per frame (L, R interleaved).

    Example:
        mono = stereo_to_mono(stereo_bytes, width=2)
    """
    if _AUDIOOP_AVAILABLE:
        return _audioop.tomono(frag, width, left_weight, right_weight)
    # Pure Python (16-bit only)
    out = bytearray()
    for i in range(0, len(frag), width * 2):
        l = struct.unpack_from("<h", frag, i)[0]
        r = struct.unpack_from("<h", frag, i + width)[0]
        v = max(-32768, min(32767, int(l * left_weight + r * right_weight)))
        out.extend(struct.pack("<h", v))
    return bytes(out)


def mono_to_stereo(frag: bytes, width: int,
                   left_weight: float = 1.0, right_weight: float = 1.0) -> bytes:
    """
    Duplicate mono PCM to stereo interleaved, with independent channel scaling.

    Example:
        stereo = mono_to_stereo(mono_bytes, width=2)
    """
    if _AUDIOOP_AVAILABLE:
        return _audioop.tostereo(frag, width, left_weight, right_weight)
    out = bytearray()
    lim = (1 << (width * 8 - 1)) - 1
    for i in range(0, len(frag), width):
        s = struct.unpack_from("<h", frag, i)[0]
        l = max(-lim - 1, min(lim, int(s * left_weight)))
        r = max(-lim - 1, min(lim, int(s * right_weight)))
        out.extend(struct.pack("<h", l))
        out.extend(struct.pack("<h", r))
    return bytes(out)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Codec conversion
# ─────────────────────────────────────────────────────────────────────────────

def to_ulaw(frag: bytes, width: int) -> bytes:
    """Encode linear PCM to G.711 µ-law (8-bit, 1 byte/sample)."""
    if _AUDIOOP_AVAILABLE:
        return _audioop.lin2ulaw(frag, width)
    from .sunauutil import encode_ulaw   # pure Python fallback from sunauutil
    if width != 2:
        raise ValueError("encode_ulaw fallback requires 16-bit PCM")
    return encode_ulaw(frag)


def from_ulaw(data: bytes, width: int = 2) -> bytes:
    """Decode G.711 µ-law bytes to linear PCM at the given bit depth."""
    if _AUDIOOP_AVAILABLE:
        return _audioop.ulaw2lin(data, width)
    from .sunauutil import decode_ulaw
    pcm16 = decode_ulaw(data)
    if width == 2:
        return pcm16
    return change_bit_depth(pcm16, 2, width)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Sample rate conversion
# ─────────────────────────────────────────────────────────────────────────────

def resample(
    frag: bytes,
    width: int,
    nchannels: int,
    in_rate: int,
    out_rate: int,
) -> bytes:
    """
    Resample PCM audio from in_rate to out_rate Hz.
    Uses audioop.ratecv internally (linear interpolation).
    Falls back to a simple linear resampler when audioop is unavailable.

    Example:
        pcm_48k = resample(pcm_44k, width=2, nchannels=2,
                           in_rate=44100, out_rate=48000)
    """
    if _AUDIOOP_AVAILABLE:
        result, _ = _audioop.ratecv(frag, width, nchannels,
                                    in_rate, out_rate, None)
        return result

    # Pure Python linear interpolation fallback (mono 16-bit only)
    if width != 2 or nchannels != 1:
        raise NotImplementedError("Pure Python resample only supports mono 16-bit")
    samples = array.array("h", frag)
    n_out = int(len(samples) * out_rate / in_rate)
    out = array.array("h")
    for i in range(n_out):
        src = i * in_rate / out_rate
        idx = int(src)
        frac = src - idx
        a = samples[min(idx, len(samples) - 1)]
        b = samples[min(idx + 1, len(samples) - 1)]
        out.append(int(a + frac * (b - a)))
    return out.tobytes()


# ─────────────────────────────────────────────────────────────────────────────
# 6. Mix / combine
# ─────────────────────────────────────────────────────────────────────────────

def mix(frag1: bytes, frag2: bytes, width: int,
        gain1: float = 1.0, gain2: float = 1.0) -> bytes:
    """
    Mix two PCM buffers sample-by-sample (same length and format required).
    Applies per-channel gain before adding.

    Example:
        mixed = mix(voice_pcm, music_pcm, width=2, gain1=1.0, gain2=0.3)
    """
    if gain1 != 1.0:
        frag1 = apply_gain(frag1, width, gain1)
    if gain2 != 1.0:
        frag2 = apply_gain(frag2, width, gain2)
    # Pad shorter buffer with silence
    if len(frag1) < len(frag2):
        frag1 = frag1 + bytes(len(frag2) - len(frag1))
    elif len(frag2) < len(frag1):
        frag2 = frag2 + bytes(len(frag1) - len(frag2))

    if _AUDIOOP_AVAILABLE:
        return _audioop.add(frag1, frag2, width)
    # Pure Python fallback (16-bit)
    out = bytearray()
    for i in range(0, len(frag1), width):
        a = struct.unpack_from("<h", frag1, i)[0]
        b = struct.unpack_from("<h", frag2, i)[0]
        v = max(-32768, min(32767, a + b))
        out.extend(struct.pack("<h", v))
    return bytes(out)


def zero_crossing_rate(frag: bytes, width: int) -> float:
    """
    Return the fraction of samples that cross zero (0.0–1.0).
    High ZCR indicates noise or high-frequency content; low ZCR indicates tonal signal.

    Example:
        zcr = zero_crossing_rate(pcm_bytes, 2)
        print(f"ZCR: {zcr:.3f}")
    """
    if _AUDIOOP_AVAILABLE:
        crossings = _audioop.cross(frag, width)
        n_samples = len(frag) // width
        return crossings / n_samples if n_samples > 1 else 0.0
    # Pure Python fallback
    samples = array.array("h", frag)
    if len(samples) < 2:
        return 0.0
    crossings = sum(1 for i in range(1, len(samples))
                    if (samples[i - 1] >= 0) != (samples[i] >= 0))
    return crossings / len(samples)


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

if __name__ == "__main__":
    print("=== audioop demo ===")
    if not _AUDIOOP_AVAILABLE:
        print("  audioop not available (Python 3.13+); using pure Python fallbacks")

    # ── synthesize two tones ──────────────────────────────────────────────────
    rate = 44100; dur = 0.5
    n = int(rate * dur)

    a440 = array.array("h")
    a880 = array.array("h")
    for i in range(n):
        v440 = int(16000 * math.sin(2 * math.pi * 440 * i / rate))
        v880 = int(8000  * math.sin(2 * math.pi * 880 * i / rate))
        a440.append(max(-32768, min(32767, v440)))
        a880.append(max(-32768, min(32767, v880)))
    pcm440 = a440.tobytes()
    pcm880 = a880.tobytes()

    # ── level measurement ─────────────────────────────────────────────────────
    print("\n--- measure_level ---")
    info = measure_level(pcm440, 2)
    print(f"  440Hz: {info}")
    info2 = measure_level(pcm880, 2)
    print(f"  880Hz: {info2}")

    # ── gain ──────────────────────────────────────────────────────────────────
    print("\n--- apply_gain + normalize ---")
    quiet = apply_gain(pcm440, 2, 0.25)
    print(f"  0.25x gain peak: {measure_level(quiet, 2).peak}")
    normed = normalize(pcm440, 2, 0.9)
    print(f"  normalized peak: {measure_level(normed, 2).peak}")
    fs = 32767
    print(f"  90% full-scale = {int(fs * 0.9)}")

    # ── stereo ────────────────────────────────────────────────────────────────
    print("\n--- mono↔stereo ---")
    stereo = mono_to_stereo(pcm440, 2)
    print(f"  mono {len(pcm440)}B → stereo {len(stereo)}B")
    back_mono = stereo_to_mono(stereo, 2)
    print(f"  stereo → mono {len(back_mono)}B")
    lvl = measure_level(back_mono, 2)
    print(f"  back_mono level: {lvl.peak}")

    # ── ULAW codec ────────────────────────────────────────────────────────────
    print("\n--- ULAW encode/decode ---")
    ulaw = to_ulaw(pcm440, 2)
    pcm_back = from_ulaw(ulaw, 2)
    print(f"  PCM16: {len(pcm440)}B  ULAW: {len(ulaw)}B  ratio: {len(ulaw)/len(pcm440)*100:.0f}%")
    orig_lvl = measure_level(pcm440, 2)
    back_lvl = measure_level(pcm_back, 2)
    print(f"  original rms={orig_lvl.rms}  decoded rms={back_lvl.rms}")

    # ── resample ──────────────────────────────────────────────────────────────
    print("\n--- resample 44100→48000 ---")
    mono440 = a440.tobytes()  # mono 16-bit
    resampled = resample(mono440, 2, 1, 44100, 48000)
    expected = int(len(mono440) * 48000 / 44100)
    print(f"  44100Hz: {len(mono440)//2} samples  "
          f"48000Hz: {len(resampled)//2} samples  "
          f"expected≈{expected//2}")

    # ── mix two tones ─────────────────────────────────────────────────────────
    print("\n--- mix 440Hz + 880Hz ---")
    mixed = mix(pcm440, pcm880, 2, gain1=1.0, gain2=0.5)
    print(f"  mixed peak={measure_level(mixed, 2).peak}")

    # ── ZCR ───────────────────────────────────────────────────────────────────
    print("\n--- zero_crossing_rate ---")
    print(f"  440Hz ZCR={zero_crossing_rate(pcm440, 2):.4f} "
          f"(expected ≈{2*440/rate:.4f})")
    print(f"  880Hz ZCR={zero_crossing_rate(pcm880, 2):.4f} "
          f"(expected ≈{2*880/rate:.4f})")

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

For the soundfile (PyPI) alternative — soundfile.read(path) and soundfile.write(path, data, samplerate) wrap libsndfile and provide cross-platform reading and writing of WAV, FLAC, OGG, AIFF, and other formats along with resampling, channel conversion, and format normalization via a stable NumPy array API — use soundfile in any new code needing audio signal processing since it works on Python 3.13+ and offers far wider format support than audioop; use audioop sketches as reference for the signal operations (gain, RMS, rate conversion, µ-law codec) and then port to soundfile + numpy for production. For the array + struct alternative — array.array("h", pcm_bytes) and arithmetic on the resulting integers provide all the gain, normalize, and mix operations that audioop exposes, without the deprecated module dependency — combine array for storage with math.sqrt for RMS, list comprehensions for gain/clip, and struct.pack for ULAW encoding to replicate every audioop feature in stdlib-only Python 3.13 safe code. The Claude Skills 360 bundle includes audioop skill sets covering LevelInfo with measure_level() peak/RMS/dBFS, apply_gain()/normalize()/add_bias()/change_bit_depth() signal processing, stereo_to_mono()/mono_to_stereo() channel conversion, to_ulaw()/from_ulaw() G.711 codec, resample() rate conversion, mix()/zero_crossing_rate() combination analysis — all with pure Python fallbacks. Start with the free tier to try PCM signal processing patterns and audioop 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