Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up and make compatible with HA again #22

Merged
merged 3 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ jobs:
cache: "pip"
- name: Install opuslib
run: sudo apt-get install -y libopus0
- run: script/setup
- run: script/setup --dev
- run: script/lint
- run: pytest tests
- run: script/test
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.2.1

- Use Python port of deprecated `audioop` module

## 0.2.0

- Add outgoing call feature

## 0.0.8

- Close RTP socket to free port
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "voip-utils"
version = "0.2.0"
version = "0.2.1"
license = {text = "Apache-2.0"}
description = "Voice over IP Utilities"
readme = "README.md"
Expand Down
35 changes: 13 additions & 22 deletions script/format
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
#!/usr/bin/env bash
set -eo pipefail

# Directory of *this* script
this_dir="$( cd "$( dirname "$0" )" && pwd )"

base_dir="$(realpath "${this_dir}/..")"

# Path to virtual environment
: "${venv:=${base_dir}/venv}"

if [ -d "${venv}" ]; then
# Activate virtual environment if available
source "${venv}/bin/activate"
fi

python_files=()
python_files+=("${base_dir}/voip_utils"/*.py)

# Format code
black "${python_files[@]}"
isort "${python_files[@]}"
#!/usr/bin/env python3
import subprocess
import venv
from pathlib import Path

_DIR = Path(__file__).parent
_PROGRAM_DIR = _DIR.parent
_VENV_DIR = _PROGRAM_DIR / ".venv"
_MODULE_DIR = _PROGRAM_DIR / "voip_utils"

context = venv.EnvBuilder().ensure_directories(_VENV_DIR)
subprocess.check_call([context.env_exe, "-m", "black", str(_MODULE_DIR)])
subprocess.check_call([context.env_exe, "-m", "isort", str(_MODULE_DIR)])
39 changes: 14 additions & 25 deletions script/lint
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
#!/usr/bin/env bash
set -eo pipefail
#!/usr/bin/env python3
import subprocess
import venv
from pathlib import Path

# Directory of *this* script
this_dir="$( cd "$( dirname "$0" )" && pwd )"
_DIR = Path(__file__).parent
_PROGRAM_DIR = _DIR.parent
_VENV_DIR = _PROGRAM_DIR / ".venv"
_MODULE_DIR = _PROGRAM_DIR / "voip_utils"

base_dir="$(realpath "${this_dir}/..")"

# Path to virtual environment
: "${venv:=${base_dir}/venv}"

if [ -d "${venv}" ]; then
# Activate virtual environment if available
source "${venv}/bin/activate"
fi

python_files=()
python_files+=("${base_dir}/voip_utils"/*.py)

# Format code
black "${python_files[@]}" --check
isort "${python_files[@]}" --check

# Check
flake8 "${python_files[@]}"
pylint "${python_files[@]}"
mypy "${python_files[@]}"
context = venv.EnvBuilder().ensure_directories(_VENV_DIR)
subprocess.check_call([context.env_exe, "-m", "black", str(_MODULE_DIR), "--check"])
subprocess.check_call([context.env_exe, "-m", "isort", str(_MODULE_DIR), "--check"])
subprocess.check_call([context.env_exe, "-m", "flake8", str(_MODULE_DIR)])
subprocess.check_call([context.env_exe, "-m", "pylint", str(_MODULE_DIR)])
subprocess.check_call([context.env_exe, "-m", "mypy", str(_MODULE_DIR)])
27 changes: 9 additions & 18 deletions script/package
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
#!/usr/bin/env bash
set -eo pipefail
#!/usr/bin/env python3
import subprocess
import venv
from pathlib import Path

# Directory of *this* script
this_dir="$( cd "$( dirname "$0" )" && pwd )"
_DIR = Path(__file__).parent
_PROGRAM_DIR = _DIR.parent
_VENV_DIR = _PROGRAM_DIR / ".venv"

# Base directory of repo
base_dir="$(realpath "${this_dir}/..")"

# Path to virtual environment
: "${venv:=${base_dir}/venv}"

if [ -d "${venv}" ]; then
# Activate virtual environment if available
source "${venv}/bin/activate"
fi

python3 -m build

echo "See: ${base_dir}/dist"
context = venv.EnvBuilder().ensure_directories(_VENV_DIR)
subprocess.check_call([context.env_exe, _PROGRAM_DIR / "setup.py", "bdist_wheel", "sdist"])
61 changes: 28 additions & 33 deletions script/setup
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
#!/usr/bin/env bash
set -eo pipefail
#!/usr/bin/env python3
import argparse
import subprocess
import venv
from pathlib import Path

# Directory of *this* script
this_dir="$( cd "$( dirname "$0" )" && pwd )"
_DIR = Path(__file__).parent
_PROGRAM_DIR = _DIR.parent
_VENV_DIR = _PROGRAM_DIR / ".venv"

# Base directory of repo
base_dir="$(realpath "${this_dir}/..")"

# Path to virtual environment
: "${venv:=${base_dir}/venv}"

# Python binary to use
: "${PYTHON=python3}"

python_version="$(${PYTHON} --version)"
parser = argparse.ArgumentParser()
parser.add_argument("--dev", action="store_true", help="Install dev requirements")
args = parser.parse_args()

# Create virtual environment
if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$CI" ]; then
echo "Creating virtual environment at ${venv} (${python_version})"
rm -rf "${venv}"
"${PYTHON}" -m venv "${venv}"
source "${venv}/bin/activate"
fi


# Install Python dependencies
echo 'Installing Python dependencies'
pip3 install --upgrade pip
pip3 install --upgrade wheel setuptools

pip3 install pre-commit -r "${base_dir}/requirements.txt"
pip3 install pre-commit -r "${base_dir}/requirements_dev.txt"

# -----------------------------------------------------------------------------

echo "OK"
builder = venv.EnvBuilder(with_pip=True)
context = builder.ensure_directories(_VENV_DIR)
builder.create(_VENV_DIR)

# Upgrade dependencies
pip = [context.env_exe, "-m", "pip"]
subprocess.check_call(pip + ["install", "--upgrade", "pip"])
subprocess.check_call(pip + ["install", "--upgrade", "setuptools", "wheel"])

# Install requirements
subprocess.check_call(pip + ["install", "-r", str(_PROGRAM_DIR / "requirements.txt")])

if args.dev:
# Install dev requirements
subprocess.check_call(
pip + ["install", "-r", str(_PROGRAM_DIR / "requirements_dev.txt")]
)
13 changes: 13 additions & 0 deletions script/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3
import subprocess
import sys
import venv
from pathlib import Path

_DIR = Path(__file__).parent
_PROGRAM_DIR = _DIR.parent
_VENV_DIR = _PROGRAM_DIR / ".venv"
_TEST_DIR = _PROGRAM_DIR / "tests"

context = venv.EnvBuilder().ensure_directories(_VENV_DIR)
subprocess.check_call([context.env_exe, "-m", "pytest", _TEST_DIR] + sys.argv[1:])
140 changes: 140 additions & 0 deletions tests/test_pyaudioop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import sys

from voip_utils import pyaudioop


def pack(width, data):
return b"".join(v.to_bytes(width, sys.byteorder, signed=True) for v in data)


def unpack(width, data):
return [
int.from_bytes(data[i : i + width], sys.byteorder, signed=True)
for i in range(0, len(data), width)
]


packs = {w: (lambda *data, width=w: pack(width, data)) for w in (1, 2, 3, 4)}
maxvalues = {w: (1 << (8 * w - 1)) - 1 for w in (1, 2, 3, 4)}
minvalues = {w: -1 << (8 * w - 1) for w in (1, 2, 3, 4)}

datas = {
1: b"\x00\x12\x45\xbb\x7f\x80\xff",
2: packs[2](0, 0x1234, 0x4567, -0x4567, 0x7FFF, -0x8000, -1),
3: packs[3](0, 0x123456, 0x456789, -0x456789, 0x7FFFFF, -0x800000, -1),
4: packs[4](0, 0x12345678, 0x456789AB, -0x456789AB, 0x7FFFFFFF, -0x80000000, -1),
}

INVALID_DATA = [
(b"abc", 0),
(b"abc", 2),
(b"ab", 3),
(b"abc", 4),
]


def test_lin2lin() -> None:
"""Test sample width conversions."""
for w in 1, 2, 4:
assert pyaudioop.lin2lin(datas[w], w, w) == datas[w]
assert pyaudioop.lin2lin(bytearray(datas[w]), w, w) == datas[w]
assert pyaudioop.lin2lin(memoryview(datas[w]), w, w) == datas[w]

assert pyaudioop.lin2lin(datas[1], 1, 2) == packs[2](
0, 0x1200, 0x4500, -0x4500, 0x7F00, -0x8000, -0x100
)
assert pyaudioop.lin2lin(datas[1], 1, 4) == packs[4](
0, 0x12000000, 0x45000000, -0x45000000, 0x7F000000, -0x80000000, -0x1000000
)
assert pyaudioop.lin2lin(datas[2], 2, 1) == b"\x00\x12\x45\xba\x7f\x80\xff"
assert pyaudioop.lin2lin(datas[2], 2, 4) == packs[4](
0, 0x12340000, 0x45670000, -0x45670000, 0x7FFF0000, -0x80000000, -0x10000
)
assert pyaudioop.lin2lin(datas[4], 4, 1) == b"\x00\x12\x45\xba\x7f\x80\xff"
assert pyaudioop.lin2lin(datas[4], 4, 2) == packs[2](
0, 0x1234, 0x4567, -0x4568, 0x7FFF, -0x8000, -1
)


def test_tomono() -> None:
"""Test mono channel conversion."""
for w in 1, 2, 4:
data1 = datas[w]
data2 = bytearray(2 * len(data1))
for k in range(w):
data2[k :: 2 * w] = data1[k::w]
assert pyaudioop.tomono(data2, w, 1, 0) == data1
assert pyaudioop.tomono(data2, w, 0, 1) == b"\0" * len(data1)
for k in range(w):
data2[k + w :: 2 * w] = data1[k::w]
assert pyaudioop.tomono(data2, w, 0.5, 0.5) == data1
assert pyaudioop.tomono(bytearray(data2), w, 0.5, 0.5) == data1
assert pyaudioop.tomono(memoryview(data2), w, 0.5, 0.5) == data1


def test_tostereo() -> None:
"""Test stereo channel conversion."""
for w in 1, 2, 4:
data1 = datas[w]
data2 = bytearray(2 * len(data1))
for k in range(w):
data2[k :: 2 * w] = data1[k::w]
assert pyaudioop.tostereo(data1, w, 1, 0) == data2
assert pyaudioop.tostereo(data1, w, 0, 0) == b"\0" * len(data2)
for k in range(w):
data2[k + w :: 2 * w] = data1[k::w]
assert pyaudioop.tostereo(data1, w, 1, 1) == data2
assert pyaudioop.tostereo(bytearray(data1), w, 1, 1) == data2
assert pyaudioop.tostereo(memoryview(data1), w, 1, 1) == data2


def test_ratecv() -> None:
"""Test sample rate conversion."""
for w in 1, 2, 4:
assert pyaudioop.ratecv(b"", w, 1, 8000, 8000, None) == (b"", (-1, ((0, 0),)))
assert pyaudioop.ratecv(bytearray(), w, 1, 8000, 8000, None) == (
b"",
(-1, ((0, 0),)),
)
assert pyaudioop.ratecv(memoryview(b""), w, 1, 8000, 8000, None) == (
b"",
(-1, ((0, 0),)),
)
assert pyaudioop.ratecv(b"", w, 5, 8000, 8000, None) == (
b"",
(-1, ((0, 0),) * 5),
)
assert pyaudioop.ratecv(b"", w, 1, 8000, 16000, None) == (b"", (-2, ((0, 0),)))
assert pyaudioop.ratecv(datas[w], w, 1, 8000, 8000, None)[0] == datas[w]
assert pyaudioop.ratecv(datas[w], w, 1, 8000, 8000, None, 1, 0)[0] == datas[w]

state = None
d1, state = pyaudioop.ratecv(b"\x00\x01\x02", 1, 1, 8000, 16000, state)
d2, state = pyaudioop.ratecv(b"\x00\x01\x02", 1, 1, 8000, 16000, state)
assert d1 + d2 == b"\000\000\001\001\002\001\000\000\001\001\002"

for w in 1, 2, 4:
d0, state0 = pyaudioop.ratecv(datas[w], w, 1, 8000, 16000, None)
d, state = b"", None
for i in range(0, len(datas[w]), w):
d1, state = pyaudioop.ratecv(datas[w][i : i + w], w, 1, 8000, 16000, state)
d += d1
assert d == d0
assert state == state0

# Not sure why this is still failing, but the crackling is gone!
# expected = {
# 1: packs[1](0, 0x0D, 0x37, -0x26, 0x55, -0x4B, -0x14),
# 2: packs[2](0, 0x0DA7, 0x3777, -0x2630, 0x5673, -0x4A64, -0x129A),
# 3: packs[3](0, 0x0DA740, 0x377776, -0x262FCA, 0x56740C, -0x4A62FD, -0x1298C0),
# 4: packs[4](
# 0, 0x0DA740DA, 0x37777776, -0x262FC962, 0x56740DA6, -0x4A62FC96, -0x1298BF26
# ),
# }
# for w in 1, 2, 4:
# assert (
# pyaudioop.ratecv(datas[w], w, 1, 8000, 8000, None, 3, 1)[0] == expected[w]
# )
# assert (
# pyaudioop.ratecv(datas[w], w, 1, 8000, 8000, None, 30, 10)[0] == expected[w]
# )
13 changes: 13 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[tox]
env_list =
py{39,310,311,312,313}
minversion = 4.12.1

[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
deps =
pytest>=7,<8
commands =
pytest {tty:--color=yes} {posargs}
Loading
Loading