diff --git a/node-hub/dora-pyaudio/README.md b/node-hub/dora-pyaudio/README.md new file mode 100644 index 00000000..afc5c15a --- /dev/null +++ b/node-hub/dora-pyaudio/README.md @@ -0,0 +1,37 @@ +# dora-pyaudio + +## Getting started + +- Install it with pip: + +```bash +pip install -e . +``` + +## Contribution Guide + +- Format with [black](https://github.com/psf/black): + +```bash +black . # Format +``` + +- Lint with [pylint](https://github.com/pylint-dev/pylint): + +```bash +pylint --disable=C,R --ignored-modules=cv2 . # Lint +``` + +- Test with [pytest](https://github.com/pytest-dev/pytest) + +```bash +pytest . # Test +``` + +## YAML Specification + +## Examples + +## License + +dora-pyaudio's code are released under the MIT License diff --git a/node-hub/dora-pyaudio/dora_pyaudio/__init__.py b/node-hub/dora-pyaudio/dora_pyaudio/__init__.py new file mode 100644 index 00000000..ac3cbef9 --- /dev/null +++ b/node-hub/dora-pyaudio/dora_pyaudio/__init__.py @@ -0,0 +1,11 @@ +import os + +# Define the path to the README file relative to the package directory +readme_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "README.md") + +# Read the content of the README file +try: + with open(readme_path, "r", encoding="utf-8") as f: + __doc__ = f.read() +except FileNotFoundError: + __doc__ = "README file not found." diff --git a/node-hub/dora-pyaudio/dora_pyaudio/__main__.py b/node-hub/dora-pyaudio/dora_pyaudio/__main__.py new file mode 100644 index 00000000..bcbfde6d --- /dev/null +++ b/node-hub/dora-pyaudio/dora_pyaudio/__main__.py @@ -0,0 +1,5 @@ +from .main import main + + +if __name__ == "__main__": + main() diff --git a/node-hub/dora-pyaudio/dora_pyaudio/main.py b/node-hub/dora-pyaudio/dora_pyaudio/main.py new file mode 100644 index 00000000..1b7895e7 --- /dev/null +++ b/node-hub/dora-pyaudio/dora_pyaudio/main.py @@ -0,0 +1,43 @@ +from dora import Node +import pyarrow as pa +import pyaudio +import os +import numpy as np + +SAMPLE_RATE = os.getenv("SAMPLE_RATE", 16000) + + +p = pyaudio.PyAudio() + + +def play_audio(audio_array: pa.array, sample_rate: int, stream): + + if np.issubdtype(audio_array.dtype, np.floating): + max_val = np.max(np.abs(audio_array)) + audio_array = (audio_array / max_val) * 32767 + audio_array = audio_array.astype(np.int16) + if stream is None: + stream = p.open( + format=pyaudio.paInt16, channels=1, rate=sample_rate, output=True + ) + stream.write(audio_array.tobytes()) + return stream + + +def main(): + node = Node() + stream = None + for event in node: + if event["type"] == "INPUT": + if event["id"] == "audio": + audio = event["value"].to_numpy() + sr = event["metadata"].get("sample_rate", SAMPLE_RATE) + stream = play_audio(audio, sr, stream) + + stream.stop_stream() + stream.close() + p.terminate() + + +if __name__ == "__main__": + main() diff --git a/node-hub/dora-pyaudio/pyproject.toml b/node-hub/dora-pyaudio/pyproject.toml new file mode 100644 index 00000000..261d54d6 --- /dev/null +++ b/node-hub/dora-pyaudio/pyproject.toml @@ -0,0 +1,29 @@ +[tool.poetry] +name = "dora-pyaudio" +version = "0.0.0" +authors = ["author"] +description = "dora-pyaudio" +license = "MIT License" +homepage = "https://github.com/dora-rs/dora.git" +documentation = "https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyaudio/README.md" +readme = "README.md" +packages = [{ include = "dora_pyaudio" }] + +[tool.poetry.dependencies] +dora-rs = "^0.3.6" +numpy = ">= 1.0.0" +pyarrow = ">= 5.0.0" +python = ">= 3.8" +pyaudio = ">= 0.1.0" + +[tool.poetry.dev-dependencies] +pytest = ">= 6.3.4" +pylint = ">= 2.5.2" +black = ">= 22.10" + +[tool.poetry.scripts] +dora-pyaudio = "dora_pyaudio.main:main" + +[build-system] +requires = ["poetry-core>=1.8.0"] +build-backend = "poetry.core.masonry.api" diff --git a/node-hub/dora-pyaudio/tests/test_dora-pyaudio.py b/node-hub/dora-pyaudio/tests/test_dora-pyaudio.py new file mode 100644 index 00000000..73d84a70 --- /dev/null +++ b/node-hub/dora-pyaudio/tests/test_dora-pyaudio.py @@ -0,0 +1,9 @@ +import pytest + + +def test_import_main(): + from dora_pyaudio.main import main + + # Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow. + with pytest.raises(RuntimeError): + main()