sounddiff 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sounddiff-0.1.0/.coderabbit.yaml +72 -0
- sounddiff-0.1.0/.github/CODEOWNERS +8 -0
- sounddiff-0.1.0/.github/FUNDING.yml +1 -0
- sounddiff-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- sounddiff-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- sounddiff-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +35 -0
- sounddiff-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- sounddiff-0.1.0/.github/SECURITY.md +33 -0
- sounddiff-0.1.0/.github/labeler.yml +23 -0
- sounddiff-0.1.0/.github/workflows/ci.yml +49 -0
- sounddiff-0.1.0/.github/workflows/labeler.yml +17 -0
- sounddiff-0.1.0/.github/workflows/release.yml +55 -0
- sounddiff-0.1.0/.github/workflows/sentry.yml +23 -0
- sounddiff-0.1.0/.gitignore +43 -0
- sounddiff-0.1.0/.pre-commit-config.yaml +19 -0
- sounddiff-0.1.0/CHANGELOG.md +38 -0
- sounddiff-0.1.0/CLAUDE.md +50 -0
- sounddiff-0.1.0/CODE_OF_CONDUCT.md +41 -0
- sounddiff-0.1.0/CONTRIBUTING.md +80 -0
- sounddiff-0.1.0/LICENSE +21 -0
- sounddiff-0.1.0/PKG-INFO +169 -0
- sounddiff-0.1.0/README.md +124 -0
- sounddiff-0.1.0/docs/api.md +196 -0
- sounddiff-0.1.0/docs/architecture.md +81 -0
- sounddiff-0.1.0/docs/contributing.md +34 -0
- sounddiff-0.1.0/docs/index.md +17 -0
- sounddiff-0.1.0/docs/install.md +84 -0
- sounddiff-0.1.0/docs/usage.md +112 -0
- sounddiff-0.1.0/examples/basic_comparison.py +33 -0
- sounddiff-0.1.0/examples/batch_compare.py +53 -0
- sounddiff-0.1.0/examples/ci_regression_test.py +62 -0
- sounddiff-0.1.0/pyproject.toml +105 -0
- sounddiff-0.1.0/scripts/generate_test_audio.py +127 -0
- sounddiff-0.1.0/src/sounddiff/__init__.py +3 -0
- sounddiff-0.1.0/src/sounddiff/__main__.py +5 -0
- sounddiff-0.1.0/src/sounddiff/cli.py +79 -0
- sounddiff-0.1.0/src/sounddiff/core.py +66 -0
- sounddiff-0.1.0/src/sounddiff/detection.py +164 -0
- sounddiff-0.1.0/src/sounddiff/formats.py +100 -0
- sounddiff-0.1.0/src/sounddiff/loudness.py +98 -0
- sounddiff-0.1.0/src/sounddiff/report.py +311 -0
- sounddiff-0.1.0/src/sounddiff/spectral.py +94 -0
- sounddiff-0.1.0/src/sounddiff/temporal.py +166 -0
- sounddiff-0.1.0/src/sounddiff/types.py +181 -0
- sounddiff-0.1.0/templates/report.html.j2 +168 -0
- sounddiff-0.1.0/tests/__init__.py +0 -0
- sounddiff-0.1.0/tests/conftest.py +83 -0
- sounddiff-0.1.0/tests/test_cli.py +80 -0
- sounddiff-0.1.0/tests/test_detection.py +94 -0
- sounddiff-0.1.0/tests/test_formats.py +79 -0
- sounddiff-0.1.0/tests/test_loudness.py +65 -0
- sounddiff-0.1.0/tests/test_report.py +103 -0
- sounddiff-0.1.0/tests/test_spectral.py +71 -0
- sounddiff-0.1.0/tests/test_temporal.py +66 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
|
2
|
+
# sounddiff — Python CLI for structured audio comparison
|
|
3
|
+
inheritance: true
|
|
4
|
+
|
|
5
|
+
reviews:
|
|
6
|
+
request_changes_workflow: true
|
|
7
|
+
profile: assertive
|
|
8
|
+
pre_merge_checks:
|
|
9
|
+
docstrings:
|
|
10
|
+
mode: "off"
|
|
11
|
+
title:
|
|
12
|
+
mode: warning
|
|
13
|
+
description:
|
|
14
|
+
mode: warning
|
|
15
|
+
path_filters:
|
|
16
|
+
- "!CLAUDE.md"
|
|
17
|
+
- "!docs/**"
|
|
18
|
+
- "!.coderabbit.yaml"
|
|
19
|
+
- "!scripts/**"
|
|
20
|
+
path_instructions:
|
|
21
|
+
- path: "src/sounddiff/**/*.py"
|
|
22
|
+
instructions: |
|
|
23
|
+
sounddiff is a Python 3.10+ CLI tool for structured audio comparison.
|
|
24
|
+
Uses numpy, scipy, pyloudnorm for DSP. Click for CLI. Rich for terminal output.
|
|
25
|
+
mypy strict mode. Ruff for linting and formatting.
|
|
26
|
+
|
|
27
|
+
Review priorities (highest to lowest):
|
|
28
|
+
1. Correctness of DSP algorithms (loudness, spectral, correlation math)
|
|
29
|
+
2. Type safety (all public functions must have complete type annotations)
|
|
30
|
+
3. Edge cases in audio processing (empty signals, mono/stereo, different sample rates)
|
|
31
|
+
4. Performance issues with measurable impact on large audio files
|
|
32
|
+
5. Error handling (clear messages for file not found, unsupported format, corrupt audio)
|
|
33
|
+
|
|
34
|
+
Known patterns, DO NOT flag:
|
|
35
|
+
- `from __future__ import annotations` at top of every module (PEP 604 unions on 3.10)
|
|
36
|
+
- `np.ndarray` without generic parameters (numpy typing is limited)
|
|
37
|
+
- `pyloudnorm` and `soundfile` are untyped (ignore_missing_imports in mypy config)
|
|
38
|
+
- Frozen dataclasses for all result types (immutability is intentional)
|
|
39
|
+
- Properties on frozen dataclasses for computed deltas (avoids storing derived values)
|
|
40
|
+
|
|
41
|
+
Do NOT flag:
|
|
42
|
+
- Missing docstrings on private functions (underscore-prefixed)
|
|
43
|
+
- Import ordering (ruff handles this)
|
|
44
|
+
- Line length (ruff formatter handles this)
|
|
45
|
+
- path: "src/sounddiff/cli.py"
|
|
46
|
+
instructions: |
|
|
47
|
+
CLI entry point using Click. Review for:
|
|
48
|
+
- Clear error messages for all failure modes
|
|
49
|
+
- Correct exit codes (0 success, 1 for errors)
|
|
50
|
+
- No unhandled exceptions reaching the user
|
|
51
|
+
- path: "src/sounddiff/{loudness,spectral,temporal,detection}.py"
|
|
52
|
+
instructions: |
|
|
53
|
+
Core DSP modules. Review with extra care for:
|
|
54
|
+
- Mathematical correctness (dB conversions, RMS calculations, correlation)
|
|
55
|
+
- Handling of edge cases (silence, single-sample files, DC offset)
|
|
56
|
+
- Numerical stability (division by zero, log of zero)
|
|
57
|
+
- Consistent units (always dB, Hz, seconds — never raw sample counts in output)
|
|
58
|
+
- path: "tests/**/*.py"
|
|
59
|
+
instructions: |
|
|
60
|
+
Test suite using pytest and hypothesis. Review for:
|
|
61
|
+
- Test isolation (no shared mutable state between tests)
|
|
62
|
+
- Meaningful assertions (not just "doesn't crash")
|
|
63
|
+
- Edge case coverage (empty input, mono, extreme values)
|
|
64
|
+
- Deterministic test audio generation (fixed seeds, no randomness without rng fixture)
|
|
65
|
+
|
|
66
|
+
tools:
|
|
67
|
+
ruff:
|
|
68
|
+
enabled: true
|
|
69
|
+
|
|
70
|
+
knowledge_base:
|
|
71
|
+
web_search:
|
|
72
|
+
enabled: false
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Default owners for everything
|
|
2
|
+
* @claudesystemblueio
|
|
3
|
+
|
|
4
|
+
# Core audio analysis
|
|
5
|
+
src/sounddiff/loudness.py @claudesystemblueio
|
|
6
|
+
src/sounddiff/spectral.py @claudesystemblueio
|
|
7
|
+
src/sounddiff/temporal.py @claudesystemblueio
|
|
8
|
+
src/sounddiff/detection.py @claudesystemblueio
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: [systemblueteam]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug in sounddiff
|
|
3
|
+
title: "[Bug]: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: description
|
|
8
|
+
attributes:
|
|
9
|
+
label: What happened?
|
|
10
|
+
description: A clear description of the bug.
|
|
11
|
+
placeholder: When I run sounddiff on two WAV files...
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: expected
|
|
16
|
+
attributes:
|
|
17
|
+
label: What did you expect?
|
|
18
|
+
description: What should have happened instead.
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: reproduce
|
|
23
|
+
attributes:
|
|
24
|
+
label: Steps to reproduce
|
|
25
|
+
description: Minimal steps to reproduce the issue.
|
|
26
|
+
placeholder: |
|
|
27
|
+
1. Run `sounddiff a.wav b.wav`
|
|
28
|
+
2. See error...
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
- type: input
|
|
32
|
+
id: version
|
|
33
|
+
attributes:
|
|
34
|
+
label: sounddiff version
|
|
35
|
+
description: Output of `sounddiff --version`
|
|
36
|
+
placeholder: "0.1.0"
|
|
37
|
+
validations:
|
|
38
|
+
required: true
|
|
39
|
+
- type: input
|
|
40
|
+
id: python
|
|
41
|
+
attributes:
|
|
42
|
+
label: Python version
|
|
43
|
+
description: Output of `python --version`
|
|
44
|
+
placeholder: "3.12.0"
|
|
45
|
+
validations:
|
|
46
|
+
required: true
|
|
47
|
+
- type: dropdown
|
|
48
|
+
id: os
|
|
49
|
+
attributes:
|
|
50
|
+
label: Operating system
|
|
51
|
+
options:
|
|
52
|
+
- macOS
|
|
53
|
+
- Linux
|
|
54
|
+
- Windows
|
|
55
|
+
- Other
|
|
56
|
+
validations:
|
|
57
|
+
required: true
|
|
58
|
+
- type: textarea
|
|
59
|
+
id: audio
|
|
60
|
+
attributes:
|
|
61
|
+
label: Audio file details
|
|
62
|
+
description: Format, sample rate, channels, duration of the files involved (if relevant).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature for sounddiff
|
|
3
|
+
title: "[Feature]: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: problem
|
|
8
|
+
attributes:
|
|
9
|
+
label: What problem does this solve?
|
|
10
|
+
description: Describe the use case or workflow this feature would improve.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: solution
|
|
15
|
+
attributes:
|
|
16
|
+
label: Proposed solution
|
|
17
|
+
description: How do you think this should work? CLI interface, output format, etc.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: alternatives
|
|
22
|
+
attributes:
|
|
23
|
+
label: Alternatives considered
|
|
24
|
+
description: Any other approaches you've thought about.
|
|
25
|
+
- type: dropdown
|
|
26
|
+
id: area
|
|
27
|
+
attributes:
|
|
28
|
+
label: Area
|
|
29
|
+
options:
|
|
30
|
+
- Audio analysis
|
|
31
|
+
- CLI / UX
|
|
32
|
+
- Output formats
|
|
33
|
+
- Performance
|
|
34
|
+
- Documentation
|
|
35
|
+
- Other
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---------|--------------------|
|
|
7
|
+
| 0.1.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability, please report it responsibly.
|
|
12
|
+
|
|
13
|
+
**Do not open a public issue.**
|
|
14
|
+
|
|
15
|
+
Email security concerns to **dev@systemblue.io** with:
|
|
16
|
+
|
|
17
|
+
1. Description of the vulnerability
|
|
18
|
+
2. Steps to reproduce
|
|
19
|
+
3. Potential impact
|
|
20
|
+
4. Suggested fix (if any)
|
|
21
|
+
|
|
22
|
+
We will acknowledge receipt within 48 hours and provide a timeline for a fix. Security patches are prioritized over all other work.
|
|
23
|
+
|
|
24
|
+
## Scope
|
|
25
|
+
|
|
26
|
+
sounddiff processes audio files from disk. Relevant security concerns include:
|
|
27
|
+
|
|
28
|
+
- Path traversal in file handling
|
|
29
|
+
- Denial of service via malformed audio files
|
|
30
|
+
- Dependency vulnerabilities
|
|
31
|
+
- Secret leakage in CI/CD
|
|
32
|
+
|
|
33
|
+
We run [gitleaks](https://github.com/gitleaks/gitleaks) in CI and as a pre-commit hook to prevent accidental secret commits.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
audio-core:
|
|
2
|
+
- changed-files:
|
|
3
|
+
- any-glob-to-any-file:
|
|
4
|
+
- src/sounddiff/loudness.py
|
|
5
|
+
- src/sounddiff/spectral.py
|
|
6
|
+
- src/sounddiff/temporal.py
|
|
7
|
+
- src/sounddiff/detection.py
|
|
8
|
+
|
|
9
|
+
cli:
|
|
10
|
+
- changed-files:
|
|
11
|
+
- any-glob-to-any-file:
|
|
12
|
+
- src/sounddiff/cli.py
|
|
13
|
+
|
|
14
|
+
docs:
|
|
15
|
+
- changed-files:
|
|
16
|
+
- any-glob-to-any-file:
|
|
17
|
+
- docs/**
|
|
18
|
+
- "*.md"
|
|
19
|
+
|
|
20
|
+
tests:
|
|
21
|
+
- changed-files:
|
|
22
|
+
- any-glob-to-any-file:
|
|
23
|
+
- tests/**
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: pip install -e ".[dev]"
|
|
19
|
+
- name: Ruff lint
|
|
20
|
+
run: ruff check .
|
|
21
|
+
- name: Ruff format
|
|
22
|
+
run: ruff format --check .
|
|
23
|
+
- name: mypy
|
|
24
|
+
run: mypy src
|
|
25
|
+
|
|
26
|
+
test:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
matrix:
|
|
30
|
+
python-version: ["3.10", "3.13"]
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
- name: Install system dependencies
|
|
37
|
+
run: sudo apt-get update && sudo apt-get install -y libsndfile1
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: pip install -e ".[dev]"
|
|
40
|
+
- name: Generate test audio
|
|
41
|
+
run: python scripts/generate_test_audio.py
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: pytest --cov=sounddiff --cov-report=xml
|
|
44
|
+
- name: Upload coverage
|
|
45
|
+
if: matrix.python-version == '3.13'
|
|
46
|
+
uses: codecov/codecov-action@v4
|
|
47
|
+
with:
|
|
48
|
+
file: ./coverage.xml
|
|
49
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Labeler
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
label:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/labeler@v5
|
|
16
|
+
with:
|
|
17
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.13"
|
|
20
|
+
- name: Install build tools
|
|
21
|
+
run: pip install build
|
|
22
|
+
- name: Build sdist and wheel
|
|
23
|
+
run: python -m build
|
|
24
|
+
- name: Upload artifacts
|
|
25
|
+
uses: actions/upload-artifact@v4
|
|
26
|
+
with:
|
|
27
|
+
name: dist
|
|
28
|
+
path: dist/
|
|
29
|
+
|
|
30
|
+
publish:
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
environment: pypi
|
|
34
|
+
permissions:
|
|
35
|
+
id-token: write
|
|
36
|
+
steps:
|
|
37
|
+
- name: Download artifacts
|
|
38
|
+
uses: actions/download-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
|
|
45
|
+
github-release:
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
contents: write
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- name: Create GitHub Release
|
|
53
|
+
uses: softprops/action-gh-release@v2
|
|
54
|
+
with:
|
|
55
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Sentry Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
sentry-release:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
- name: Create Sentry Release
|
|
16
|
+
uses: getsentry/action-release@v3
|
|
17
|
+
env:
|
|
18
|
+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
19
|
+
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
|
20
|
+
SENTRY_PROJECT: sounddiff
|
|
21
|
+
with:
|
|
22
|
+
environment: production
|
|
23
|
+
version: ${{ github.ref_name }}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.egg
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
ENV/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
*~
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# Testing
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.mypy_cache/
|
|
30
|
+
.ruff_cache/
|
|
31
|
+
.hypothesis/
|
|
32
|
+
|
|
33
|
+
# Distribution
|
|
34
|
+
*.whl
|
|
35
|
+
*.tar.gz
|
|
36
|
+
|
|
37
|
+
# Generated test audio
|
|
38
|
+
tests/fixtures/*.wav
|
|
39
|
+
tests/fixtures/*.flac
|
|
40
|
+
|
|
41
|
+
# Environment
|
|
42
|
+
.env
|
|
43
|
+
.env.*
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.9.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
9
|
+
rev: v1.14.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: mypy
|
|
12
|
+
additional_dependencies: [types-click]
|
|
13
|
+
args: [--config-file=pyproject.toml]
|
|
14
|
+
pass_filenames: false
|
|
15
|
+
entry: mypy src
|
|
16
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
17
|
+
rev: v8.22.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: gitleaks
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-03-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Integrated LUFS comparison (ITU-R BS.1770 via pyloudnorm)
|
|
13
|
+
- True peak measurement (dBTP)
|
|
14
|
+
- Loudness range (LRA) comparison per EBU R128
|
|
15
|
+
- Spectral band energy comparison (low/mid/high, configurable bands)
|
|
16
|
+
- Waveform cross-correlation for segment similarity scoring
|
|
17
|
+
- Segment change detection: added, removed, shifted, or changed sections
|
|
18
|
+
- Clipping detection with timestamp, channel, and sample count
|
|
19
|
+
- Silence detection with configurable threshold and minimum duration
|
|
20
|
+
- Audio file loading via soundfile (WAV, FLAC, OGG, AIFF)
|
|
21
|
+
- CLI with click: `sounddiff <file1> <file2>`
|
|
22
|
+
- Terminal output with rich (colored, grouped by category)
|
|
23
|
+
- JSON output for scripts and CI pipelines
|
|
24
|
+
- HTML report output (self-contained, jinja2 templates)
|
|
25
|
+
- `--no-color` flag for plain terminal output
|
|
26
|
+
- `--version` flag
|
|
27
|
+
- Sample rate mismatch warnings
|
|
28
|
+
- Duration difference display in metadata section
|
|
29
|
+
- Test suite with 60 tests (pytest + hypothesis)
|
|
30
|
+
- CI pipeline (Python 3.10, 3.13 on Ubuntu)
|
|
31
|
+
- Documentation (install, usage, API reference, architecture)
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Terminal output no longer prints twice
|
|
36
|
+
- Spectral band delta no longer shows absurd values for near-zero energy bands
|
|
37
|
+
|
|
38
|
+
[0.1.0]: https://github.com/systemblueteam/sounddiff/releases/tag/v0.1.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# sounddiff
|
|
2
|
+
|
|
3
|
+
Structured audio comparison CLI. Python 3.10+, src layout, hatchling build.
|
|
4
|
+
|
|
5
|
+
## Build & Test
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pip install -e ".[dev]"
|
|
9
|
+
python scripts/generate_test_audio.py
|
|
10
|
+
pytest
|
|
11
|
+
ruff check . && ruff format --check . && mypy src
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
src/sounddiff/
|
|
18
|
+
types.py # Dataclasses for all results
|
|
19
|
+
formats.py # Audio I/O via soundfile
|
|
20
|
+
loudness.py # LUFS, true peak, LRA (pyloudnorm)
|
|
21
|
+
spectral.py # Band energy comparison (numpy FFT)
|
|
22
|
+
temporal.py # Cross-correlation, segment detection
|
|
23
|
+
detection.py # Clipping, silence detection
|
|
24
|
+
core.py # Pipeline orchestration
|
|
25
|
+
cli.py # Click CLI entry point
|
|
26
|
+
report.py # Output formatters (terminal/JSON/HTML)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Conventions
|
|
30
|
+
|
|
31
|
+
- **Ruff** for linting and formatting. No black, isort, or flake8.
|
|
32
|
+
- **mypy strict mode.** Type hints everywhere.
|
|
33
|
+
- **Google-style docstrings** on all public functions.
|
|
34
|
+
- **Conventional commits.** `feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`.
|
|
35
|
+
- **One concern per PR.** Small, focused diffs. Squash merge.
|
|
36
|
+
- **pytest + hypothesis** for testing. Property-based tests for DSP functions.
|
|
37
|
+
- **Test audio is generated, not committed.** Run `scripts/generate_test_audio.py`.
|
|
38
|
+
- **No `# type: ignore` without a comment.**
|
|
39
|
+
|
|
40
|
+
## Key dependencies
|
|
41
|
+
|
|
42
|
+
| Package | Purpose |
|
|
43
|
+
|---------|---------|
|
|
44
|
+
| soundfile | Audio I/O (wav, flac, ogg, aiff) |
|
|
45
|
+
| numpy | Array math, FFT, correlation |
|
|
46
|
+
| scipy | Signal processing, filtering |
|
|
47
|
+
| pyloudnorm | ITU-R BS.1770 LUFS measurement |
|
|
48
|
+
| click | CLI framework |
|
|
49
|
+
| rich | Terminal formatting |
|
|
50
|
+
| jinja2 | HTML report templates |
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment:
|
|
12
|
+
|
|
13
|
+
* Using welcoming and inclusive language
|
|
14
|
+
* Being respectful of differing viewpoints and experiences
|
|
15
|
+
* Gracefully accepting constructive criticism
|
|
16
|
+
* Focusing on what is best for the community
|
|
17
|
+
* Showing empathy towards other community members
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior:
|
|
20
|
+
|
|
21
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
22
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
23
|
+
* Public or private harassment
|
|
24
|
+
* Publishing others' private information without explicit permission
|
|
25
|
+
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement Responsibilities
|
|
28
|
+
|
|
29
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
30
|
+
|
|
31
|
+
## Scope
|
|
32
|
+
|
|
33
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces.
|
|
34
|
+
|
|
35
|
+
## Enforcement
|
|
36
|
+
|
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainers at dev@systemblue.io. All complaints will be reviewed and investigated promptly and fairly.
|
|
38
|
+
|
|
39
|
+
## Attribution
|
|
40
|
+
|
|
41
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Contributing to sounddiff
|
|
2
|
+
|
|
3
|
+
Contributions are welcome. This guide covers everything you need to get set up and submit a pull request.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
1. Fork and clone the repo
|
|
8
|
+
2. Create a virtual environment: `python -m venv .venv && source .venv/bin/activate`
|
|
9
|
+
3. Install dev dependencies: `pip install -e ".[dev]"`
|
|
10
|
+
4. Install pre-commit hooks: `pre-commit install`
|
|
11
|
+
5. Generate test audio fixtures: `python scripts/generate_test_audio.py`
|
|
12
|
+
6. Run the test suite: `pytest`
|
|
13
|
+
|
|
14
|
+
If all tests pass, your environment is ready.
|
|
15
|
+
|
|
16
|
+
## Finding work
|
|
17
|
+
|
|
18
|
+
The [issue board](https://github.com/systemblueteam/sounddiff/issues) is organized by milestone. Issues labeled [`good first issue`](https://github.com/systemblueteam/sounddiff/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) are scoped for newcomers and include enough context to get started without deep DSP knowledge.
|
|
19
|
+
|
|
20
|
+
If you want to work on something, leave a comment on the issue so nobody duplicates effort. If you have an idea that isn't on the board, open an issue first so we can align on scope before you write code.
|
|
21
|
+
|
|
22
|
+
## Development workflow
|
|
23
|
+
|
|
24
|
+
1. Create a branch from `main`: `git checkout -b feat/42-your-description`
|
|
25
|
+
2. Make your changes and write tests for new behavior
|
|
26
|
+
3. Run the full check: `ruff check . && ruff format --check . && mypy src && pytest`
|
|
27
|
+
4. Commit using [conventional commits](https://www.conventionalcommits.org/): `feat: add stereo field analysis`
|
|
28
|
+
5. Push your branch and open a PR against `main`
|
|
29
|
+
|
|
30
|
+
## Pull request guidelines
|
|
31
|
+
|
|
32
|
+
- **One concern per PR.** Keep diffs focused and reviewable.
|
|
33
|
+
- **Reference the issue** in your PR body: `Closes #42`
|
|
34
|
+
- **CI must pass** before review. The pipeline runs ruff, mypy, and pytest across Python 3.10-3.13 on Linux and macOS.
|
|
35
|
+
- **[CodeRabbit](https://coderabbit.ai) reviews every PR automatically.** Address its feedback or explain your reasoning if you disagree.
|
|
36
|
+
- Maintainers will review within a few days. If a week goes by without a response, ping us in the PR.
|
|
37
|
+
|
|
38
|
+
## Code standards
|
|
39
|
+
|
|
40
|
+
**Formatting and linting** are handled by [ruff](https://docs.astral.sh/ruff/). Pre-commit hooks run automatically, so you don't need to think about formatting manually.
|
|
41
|
+
|
|
42
|
+
**Type annotations** are required on all public functions. [mypy](https://mypy.readthedocs.io/) runs in strict mode.
|
|
43
|
+
|
|
44
|
+
**Docstrings** follow [Google style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) on public functions. Private functions (underscore-prefixed) don't require them.
|
|
45
|
+
|
|
46
|
+
**No `# type: ignore`** without a comment explaining why.
|
|
47
|
+
|
|
48
|
+
## Testing
|
|
49
|
+
|
|
50
|
+
Every new feature needs tests. Every bug fix needs a regression test.
|
|
51
|
+
|
|
52
|
+
We use [pytest](https://docs.pytest.org/) for the test suite and [hypothesis](https://hypothesis.readthedocs.io/) for property-based testing on DSP functions. Test audio is generated deterministically by `scripts/generate_test_audio.py` and is not committed to the repo. This keeps the repo lightweight and tests reproducible.
|
|
53
|
+
|
|
54
|
+
If you're unsure how to test something, ask in the issue. We'd rather help you write a good test than skip testing.
|
|
55
|
+
|
|
56
|
+
## Conventions
|
|
57
|
+
|
|
58
|
+
### Branch naming
|
|
59
|
+
|
|
60
|
+
Include the issue number when there is one:
|
|
61
|
+
|
|
62
|
+
- `feat/42-segment-detection`
|
|
63
|
+
- `fix/17-clipping-threshold`
|
|
64
|
+
- `docs/8-usage-guide`
|
|
65
|
+
- `chore/12-ci-update`
|
|
66
|
+
|
|
67
|
+
### Commit messages
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
feat: add spectral band comparison
|
|
71
|
+
fix: handle mono files in loudness calculation
|
|
72
|
+
docs: add installation guide
|
|
73
|
+
test: add property tests for temporal alignment
|
|
74
|
+
chore: update CI to Python 3.13
|
|
75
|
+
refactor: extract segment detection into its own module
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Questions
|
|
79
|
+
|
|
80
|
+
Open an [issue](https://github.com/systemblueteam/sounddiff/issues). We're happy to help with anything from setup problems to architecture questions.
|