Skip to content

Commit

Permalink
feat: initial audioinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
haoxiangsnr committed Aug 10, 2022
1 parent 3f758be commit 78816b7
Show file tree
Hide file tree
Showing 24 changed files with 1,002 additions and 0 deletions.
108 changes: 108 additions & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: CI_CD

on: [ push, pull_request ]

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}

# Define job steps
steps:
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"

- name: Check-out repository
uses: actions/checkout@v3

- name: Install system build deps
run: sudo apt-get update -y && sudo apt-get install -y --no-install-recommends build-essential gcc libsndfile1

- name: Install build dependencies
run: pip install -e .[build]

- name: Install test dependencies
run: pip install -e .[test]

- name: Install local dependencies on editable mode
run: pip install -e .

- name: Test using pytest
run: pytest tests/ --cov=audioinfo --cov-report=xml

- name: Use Codecov to track coverage
uses: codecov/codecov-action@v2
with:
files: ./coverage.xml # coverage report
token: ${{ secrets.CODECOV_TOKEN }} # Just for test
verbose: true

release:
# Only run this job if new work is pushed to "main"
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}

# Define job steps
steps:
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"

- name: Check-out repository
uses: actions/checkout@v3
with:
fetch-depth: 0 # 0 indicates all history for all branches and tags.

- name: Install system build deps
run: sudo apt-get update -y && sudo apt-get install -y --no-install-recommends build-essential gcc libsndfile1

- name: Install build dependencies
run: pip install -e .[build]

- name: Install test dependencies
run: pip install -e .[test]

- name: Install local dependencies on editable mode
run: pip install -e .

- name: Build pypi package using Flit # PSR may rebuild next time. For robustness of pipeline.
run: flit build

- name: Use Python Semantic Release to prepare release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name github-actions
git config user.email github-actions@github.com
semantic-release publish
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
skip_existing: true

- name: Test install from TestPyPI
run: |
pip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple \
audioinfo
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
skip_existing: true
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

<!--next-version-placeholder-->
3 changes: 3 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Code of Conduct

Everyone interacting in the project's codebases and documentation is expected to follow the [PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/). This includes, but is not limited to, issue trackers, chat rooms, mailing lists, and other virtual or real-life communication.
36 changes: 36 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Contributing

## Development workflow

Hi there! This repository follows the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow). The GitHub flow contains the main branch and many feature branches. Generally speaking, the main branch always uses no direct commit and only can be integrated by rebase and merge. The feature branches, like new features, bug fixes, refactoring, experiments, etc., are used for development. The GitHub flow keeps the main branch working well with documents and tests.

## Commit

This repository uses the [Angular commit style](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commit-message-format), which looks like this:

```shell
<type>(optional scope): short summary in present tense

(optional body: explains motivation for the change)

(optional footer: note BREAKING CHANGES here, and issues to be closed)
```

Generally speaking, you need to at least specify a type and a short summary for each commit. `<type>` refers to the kind of change made and is usually one of:

- `feat`: A new feature.
- `fix`: A bug fix.
- `docs`: Documentation changes.
- `style`: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc).
- `refactor`: A code change that neither fixes a bug nor adds a feature.
- `perf`: A code change that improves performance.
- `test`: Changes to the test framework.
- `build`: Changes to the build process or tools.

By using the standardized commit message in this Angular commit style, the continuous integration configuration will automatically bump version numbers based on keywords it finds in commit messages.

## References

- [Git for Professionals Tutorial - Tools & Concepts for Mastering Version Control with Git](https://www.youtube.com/watch?v=Uszj_k0DGsg)
- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)
- [How to Write a Git Commit Message](https://cbea.ms/git-commit/)
Empty file added audioinfo/__init__.py
Empty file.
Empty file added audioinfo/logger.py
Empty file.
127 changes: 127 additions & 0 deletions audioinfo/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import argparse
import glob
import os

import soundfile as sf
from rich.progress import track


def __get_files(dir_name, extensions):
"""Helper function to get files in a single directory"""

# Expand out the directory
dir_name = os.path.abspath(os.path.expanduser(dir_name))

files = set()

for sub_ext in extensions:
globstr = os.path.join(dir_name, "*" + os.path.extsep + sub_ext)
files |= set(glob.glob(globstr))

return files


def find_files(directory, ext=None, recurse=True, case_sensitive=False):
"""
Find files in a directory.
Args:
directory (str): The directory to search.
ext (str): The extension to search for.
recurse (bool): Whether to recurse into subdirectories.
case_sensitive (bool): Whether to do a case sensitive search.
"""
if ext is None:
ext = ["aac", "au", "flac", "m4a", "mp3", "ogg", "wav"]

elif isinstance(ext, str):
ext = [ext]

# Cast into a set
ext = set(ext)

# Generate upper-case versions
if not case_sensitive:
# Force to lower-case
ext = set([e.lower() for e in ext])
# Add in upper-case versions
ext |= set([e.upper() for e in ext])

files = set()

if recurse:
for walk in os.walk(directory):
files |= __get_files(walk[0], ext)
else:
files = __get_files(directory, ext)

files = list(files)
files.sort()
return files


def duration_str(duration):
hours, rest = divmod(duration, 3600)
minutes, seconds = divmod(rest, 60)
if hours >= 1:
duration = f"{hours:.0f}h {minutes:.0f}min {seconds:.3f}s"
elif minutes >= 1:
duration = "{0:02.0g}:{1:05.3f} min".format(minutes, seconds)
else:
duration = f"{seconds:.2f}s"
return duration


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"--directory",
"-d",
help="The directory to search",
)
parser.add_argument(
"--ext",
"-e",
nargs="+",
default="wav",
help="The extension to search for",
)
parser.add_argument(
"--recurse",
"-r",
default=True,
action="store_true",
help="Recurse into subdirectories",
)
parser.add_argument(
"--case-sensitive",
"-c",
default=False,
action="store_true",
help="Case sensitive search",
)
args = parser.parse_args()

print("Searching for files...")
files = find_files(args.directory, args.ext, args.recurse, args.case_sensitive)
print(f"Finished searching for {len(files)} files")

total_duration = 0.0
for file in track(files, description="Analyzing files:"):
with sf.SoundFile(file) as f:
name = f.name
samplerate = f.samplerate
channels = f.channels
frames = f.frames
duration = float(frames) / f.samplerate
format = f.format

print(
f"{name} - {samplerate} - {format} - # channels: {channels}, duration: {duration:.3f}s"
)

total_duration += duration

print(f"Total number of files: {len(files)}.")
print(f"Total duration: {duration_str(total_duration)}.")
print(f"Average duration: {duration_str(total_duration / len(files))}.")
19 changes: 19 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -m sphinx
SPHINXPROJ = audioinfo
SOURCEDIR = source
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
3 changes: 3 additions & 0 deletions docs/source/_static/css/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div.wy-nav-content {
max-width: 800px;
}
1 change: 1 addition & 0 deletions docs/source/api/util.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. automodule:: audioinfo.util
56 changes: 56 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import importlib_metadata

# -- Project information -----------------------------------------------------
project = "audioinfo"
author = "HAO Xiang <haoxiangsnr@gmail.com>"
project_copyright = "2022, HAO Xiang"
release = importlib_metadata.version(project)
version = ".".join(
release.split(".")[:2]
) # e.g., "0.3" stand for the major is "0" and the minor is "3"

# -- MetaConfig configuration ---------------------------------------------------
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
extensions = [
"myst_parser", # markdown file parser.
"sphinx.ext.todo", # enable the todo.
"sphinx.ext.autodoc", # provide automatic documentation for module (*.py), class, function, and typehints.
"sphinx.ext.autosummary", # auto-generate the summary (include links) of the modules.
"sphinx.ext.intersphinx", # enable cross-referencing between Sphinx projects.
"sphinx.ext.viewcode", # add a helpful link to the source code of each object in the API reference sheet.
"sphinx.ext.mathjax", # enable math support in the documentation.
"sphinx.ext.napoleon", # [ordered] parse our docstrings and generate Google-style docstrings.
"sphinxcontrib.autodoc_pydantic", # generate the suitable docstrings to pydantic models.
]

# -- Extension configuration -------------------------------------------------
napoleon_numpy_docstring = False
napoleon_attr_annotations = True
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"numpy": ("https://numpy.org/doc/stable/", None),
}
autosummary_generate = True
autodoc_mock_imports = ["soundfile", "gpuRIR"]
autodoc_pydantic_model_signature_prefix = "Config"
autodoc_pydantic_member_order = "bysource"
autodoc_pydantic_model_show_field_summary = False
autodoc_pydantic_model_show_json = False
autodoc_pydantic_model_show_validator_members = False
autodoc_pydantic_model_show_validator_summary = False
autodoc_pydantic_model_summary_list_order = "bysource"
autodoc_pydantic_model_list_validators = False
autodoc_pydantic_field_signature_prefix = "option"

# -- Options for HTML output -------------------------------------------------
html_theme = "sphinx_rtd_theme"
html_context = {
"display_github": True, # edit on Github, see https://github.com/readthedocs/sphinx_rtd_theme/issues/529
"github_user": "haoxiangsnr",
"github_repo": "audioinfo",
"github_version": "main",
}
html_static_path = ["_static"]
html_css_files = [
"css/custom.css",
]
Loading

0 comments on commit 78816b7

Please sign in to comment.