Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
pyaudioop.py
Go to the documentation of this file.
1"""Minimal shim providing a subset of the stdlib `audioop` API.
2
3This file is intended as a temporary compatibility layer for
4environments where the C-extension `audioop` is missing (e.g. custom
5Python builds or stripped-down containers). It implements a few small
6helpers that `pydub` commonly calls: `rms`, `max` and `avg`.
7
8This is a light-weight pure-Python implementation and may be slower
9than the C-extension for large buffers; it is only intended to
10unblock imports and normal usage of `pydub` in CI/dev containers.
11"""
12from __future__ import annotations
13
14import math
15from typing import Iterable, List
16
17
18def _samples_from_fragment(fragment: bytes, width: int) -> Iterable[int]:
19 import struct
20
21 if width == 1:
22 if not fragment:
23 return ()
24 fmt = f"{len(fragment)}b"
25 return struct.unpack(fmt, fragment)
26 if width == 2:
27 count = len(fragment) // 2
28 if count == 0:
29 return ()
30 fmt = f"<{count}h"
31 return struct.unpack(fmt, fragment)
32 if width == 3:
33 samples: List[int] = []
34 for i in range(0, len(fragment), 3):
35 b = fragment[i:i+3]
36 if len(b) < 3:
37 break
38 # sign-extend the most-significant byte to 4 bytes (little-endian)
39 sign = b[2] & 0x80
40 ext = b"\xff" if sign else b"\x00"
41 val = int.from_bytes(b + ext, "little", signed=True)
42 samples.append(val)
43 return samples
44 if width == 4:
45 count = len(fragment) // 4
46 if count == 0:
47 return ()
48 fmt = f"<{count}i"
49 return struct.unpack(fmt, fragment)
50 raise ValueError("width must be 1..4 bytes per sample")
51
52
53def rms(fragment: bytes, width: int) -> int:
54 """Return the root-mean-square of audio samples in `fragment`.
55
56 Parameters mirror the stdlib `audioop.rms(fragment, width)`.
57 """
58 samples = _samples_from_fragment(fragment, width)
59 total = 0
60 n = 0
61 for s in samples:
62 total += s * s
63 n += 1
64 if n == 0:
65 return 0
66 mean = total / n
67 return int(math.sqrt(mean))
68
69
70def max(fragment: bytes, width: int) -> int: # noqa: A003 - mirrors audioop API
71 samples = _samples_from_fragment(fragment, width)
72 maxv = 0
73 for s in samples:
74 v = abs(s)
75 if v > maxv:
76 maxv = v
77 return maxv
78
79
80def avg(fragment: bytes, width: int) -> int:
81 samples = _samples_from_fragment(fragment, width)
82 total = 0
83 n = 0
84 for s in samples:
85 total += s
86 n += 1
87 if n == 0:
88 return 0
89 return int(total / n)
90
91
92__all__ = ["rms", "max", "avg"]
int avg(bytes fragment, int width)
Definition pyaudioop.py:80
int max(bytes fragment, int width)
Definition pyaudioop.py:70
Iterable[int] _samples_from_fragment(bytes fragment, int width)
Definition pyaudioop.py:18
int rms(bytes fragment, int width)
Definition pyaudioop.py:53