From 66603ed39fcc4db8663f9b6480e06d47ff926139 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 3 May 2024 13:51:30 -0700 Subject: [PATCH] fix ci, review 2 --- .github/workflows/c-demos.yml | 2 +- .github/workflows/python-demo.yml | 2 +- demo/c/test/test_orca_c.py | 23 +- demo/python/__init__.py | 14 -- demo/python/_audio_device.py | 109 ---------- demo/python/_orca_thread.py | 137 ------------ demo/python/_util.py | 64 ------ demo/python/orca_demo.py | 55 ++--- demo/python/orca_demo_streaming.py | 331 ++++++++++++++++++++++++----- demo/python/setup.py | 6 +- 10 files changed, 327 insertions(+), 416 deletions(-) delete mode 100644 demo/python/__init__.py delete mode 100644 demo/python/_audio_device.py delete mode 100644 demo/python/_orca_thread.py delete mode 100644 demo/python/_util.py diff --git a/.github/workflows/c-demos.yml b/.github/workflows/c-demos.yml index cce2b924..9b7f54b7 100644 --- a/.github/workflows/c-demos.yml +++ b/.github/workflows/c-demos.yml @@ -49,7 +49,7 @@ jobs: make_file: "MinGW Makefiles" - os: macos-latest platform: mac - arch: x86_64 + arch: undetermined make_file: "Unix Makefiles" steps: diff --git a/.github/workflows/python-demo.yml b/.github/workflows/python-demo.yml index 09e222b0..c343d31a 100644 --- a/.github/workflows/python-demo.yml +++ b/.github/workflows/python-demo.yml @@ -32,7 +32,7 @@ jobs: install_dep: sudo apt install libportaudio2 - os: windows-latest - os: macos-latest - install_dep: brew install portaudio --HEAD + install_dep: brew uninstall portaudio && brew install portaudio --HEAD steps: - uses: actions/checkout@v3 diff --git a/demo/c/test/test_orca_c.py b/demo/c/test/test_orca_c.py index 9a49355b..c88728eb 100644 --- a/demo/c/test/test_orca_c.py +++ b/demo/c/test/test_orca_c.py @@ -10,6 +10,7 @@ # import os.path +import platform as pltf import subprocess import sys import unittest @@ -23,8 +24,15 @@ class OrcaCTestCase(unittest.TestCase): @classmethod def setUpClass(cls): cls._access_key = sys.argv[1] - cls._platform = sys.argv[2] - cls._arch = "" if len(sys.argv) != 4 else sys.argv[3] + platform = sys.argv[2] + if platform == "mac": + if pltf.machine() == "x86_64": + cls._arch = "x86_64" + elif pltf.machine() == "arm64": + cls._arch = "arm64" + else: + cls._arch = "" if len(sys.argv) != 4 else sys.argv[3] + cls._platform = platform cls._root_dir = os.path.join(os.path.dirname(__file__), "../../..") @staticmethod @@ -59,12 +67,13 @@ def run_orca(self, model_path: str) -> None: process = subprocess.Popen(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout, stderr = process.communicate() - print("Command:", args) # Print the command being run - print("stdout:", stdout.decode()) # Print the stdout output - print("stderr:", stderr.decode()) # Print the stderr output - print("Exit code:", process.poll()) # Print the exit code + poll_result = process.poll() + if poll_result != 0: + print(stdout.decode('utf-8')) + print(stderr.decode('utf-8')) + raise RuntimeError("Error running demo. See details above") - self.assertEqual(process.poll(), 0) + self.assertEqual(poll_result, 0) self.assertEqual(stderr.decode('utf-8'), '') self.assertTrue("Saved audio" in stdout.decode('utf-8')) os.remove(output_path) diff --git a/demo/python/__init__.py b/demo/python/__init__.py deleted file mode 100644 index 199b1f3a..00000000 --- a/demo/python/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright 2024 Picovoice Inc. -# -# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" -# file accompanying this source. -# -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# - -from ._util import * -from ._orca_thread import * -from ._audio_device import * diff --git a/demo/python/_audio_device.py b/demo/python/_audio_device.py deleted file mode 100644 index 7df98846..00000000 --- a/demo/python/_audio_device.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright 2024 Picovoice Inc. -# -# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" -# file accompanying this source. -# -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# - -import time -from queue import Queue -from typing import ( - Any, - Dict, - Optional, - Sequence, -) - -import numpy as np -from numpy.typing import NDArray -from sounddevice import OutputStream, query_devices - - -class StreamingAudioDevice: - def __init__(self, device_index: Optional[int] = None) -> None: - if device_index is None: - device_info = query_devices(kind="output") - device_index = int(device_info["index"]) - - self._device_index = device_index - self._queue: Queue[Sequence[int]] = Queue() - - self._buffer = None - self._stream = None - self._sample_rate = None - self._blocksize = None - - def start(self, sample_rate: int) -> None: - self._sample_rate = sample_rate - self._blocksize = self._sample_rate // 20 - self._stream = OutputStream( - channels=1, - samplerate=self._sample_rate, - dtype=np.int16, - device=self._device_index, - callback=self._callback, - blocksize=self._blocksize) - self._stream.start() - - # noinspection PyShadowingNames - # noinspection PyUnusedLocal - def _callback(self, outdata: NDArray, frames: int, time: Any, status: Any) -> None: - if self._queue.empty(): - outdata[:] = 0 - return - - pcm = self._queue.get() - outdata[:, 0] = pcm - - def play(self, pcm_chunk: Sequence[int]) -> None: - if self._stream is None: - raise ValueError("Stream is not started. Call `start` method first.") - - pcm_chunk = np.array(pcm_chunk, dtype=np.int16) - - if self._buffer is not None: - if pcm_chunk is not None: - pcm_chunk = np.concatenate([self._buffer, pcm_chunk]) - else: - pcm_chunk = self._buffer - self._buffer = None - - length = pcm_chunk.shape[0] - for index_block in range(0, length, self._blocksize): - if (length - index_block) < self._blocksize: - self._buffer = pcm_chunk[index_block: index_block + (length - index_block)] - else: - self._queue.put_nowait(pcm_chunk[index_block: index_block + self._blocksize]) - - def flush_and_terminate(self) -> None: - self.flush() - self.terminate() - - def flush(self) -> None: - if self._buffer is not None: - chunk = np.zeros(self._blocksize, dtype=np.int16) - chunk[:self._buffer.shape[0]] = self._buffer - self._queue.put_nowait(chunk) - - time_interval = self._blocksize / self._sample_rate - while not self._queue.empty(): - time.sleep(time_interval) - - time.sleep(time_interval) - - def terminate(self) -> None: - self._stream.stop() - self._stream.close() - - @staticmethod - def list_output_devices() -> Dict[str, Any]: - return query_devices(kind="output") - - -__all__ = [ - "StreamingAudioDevice", -] diff --git a/demo/python/_orca_thread.py b/demo/python/_orca_thread.py deleted file mode 100644 index 022512ca..00000000 --- a/demo/python/_orca_thread.py +++ /dev/null @@ -1,137 +0,0 @@ -# -# Copyright 2024 Picovoice Inc. -# -# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" -# file accompanying this source. -# -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# - -import platform -import threading -import time -from dataclasses import dataclass -from queue import Queue -from typing import ( - Callable, - Optional, - Sequence, -) - -import pvorca -from pvorca import OrcaInvalidArgumentError - -from _util import JETSON_MACHINES, linux_machine, RASPBERRY_PI_MACHINES - - -class OrcaThread: - @dataclass - class OrcaInput: - text: str - flush: bool - - def __init__( - self, - play_audio_callback: Callable[[Sequence[int]], None], - access_key: str, - num_tokens_per_second: int, - model_path: Optional[str] = None, - library_path: Optional[str] = None, - audio_wait_chunks: Optional[int] = None, - ) -> None: - - self._orca = pvorca.create(access_key=access_key, model_path=model_path, library_path=library_path) - self._orca_stream = self._orca.open_stream() - self._sample_rate = self._orca.sample_rate - - self._play_audio_callback = play_audio_callback - self._num_tokens_per_second = num_tokens_per_second - assert self._num_tokens_per_second > 0 - - self._queue: Queue[Optional[OrcaThread.OrcaInput]] = Queue() - self._thread = None - - self._time_first_audio_available = -1 - self._pcm_buffer: Queue[Sequence[int]] = Queue() - - self._wait_chunks = audio_wait_chunks or self._get_first_audio_wait_chunks() - self._num_pcm_chunks_processed = 0 - - @staticmethod - def _get_first_audio_wait_chunks() -> int: - wait_chunks = 0 - if platform.system() == "Linux": - machine = linux_machine() - if machine in JETSON_MACHINES: - wait_chunks = 1 - elif machine in RASPBERRY_PI_MACHINES: - wait_chunks = 1 - return wait_chunks - - def _run(self) -> None: - while True: - orca_input = self._queue.get() - if orca_input is None: - while not self._pcm_buffer.empty(): - self._play_audio_callback(self._pcm_buffer.get()) - break - - try: - if not orca_input.flush: - pcm = self._orca_stream.synthesize(orca_input.text) - else: - pcm = self._orca_stream.flush() - except OrcaInvalidArgumentError as e: - raise ValueError(f"Orca could not synthesize text input `{orca_input.text}`: `{e}`") - - if pcm is not None: - if self._num_pcm_chunks_processed < self._wait_chunks: - self._pcm_buffer.put_nowait(pcm) - else: - while not self._pcm_buffer.empty(): - self._play_audio_callback(self._pcm_buffer.get()) - self._play_audio_callback(pcm) - - if self._num_pcm_chunks_processed == 0: - self._time_first_audio_available = time.time() - - self._num_pcm_chunks_processed += 1 - - def _close_thread_blocking(self): - self._queue.put_nowait(None) - self._thread.join() - - def start(self) -> None: - self._thread = threading.Thread(target=self._run) - self._thread.start() - - def synthesize(self, text: str) -> None: - self._queue.put_nowait(self.OrcaInput(text=text, flush=False)) - - def flush(self) -> None: - self._queue.put_nowait(self.OrcaInput(text="", flush=True)) - self._close_thread_blocking() - self.start() - - def delete(self) -> None: - self._close_thread_blocking() - self._orca_stream.close() - self._orca.delete() - - def get_time_first_audio_available(self) -> float: - return self._time_first_audio_available - - @property - def sample_rate(self) -> int: - return self._sample_rate - - @property - def version(self) -> str: - return self._orca.version - - -__all__ = [ - "OrcaThread", -] diff --git a/demo/python/_util.py b/demo/python/_util.py deleted file mode 100644 index fb16a7af..00000000 --- a/demo/python/_util.py +++ /dev/null @@ -1,64 +0,0 @@ -# -# Copyright 2024 Picovoice Inc. -# -# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" -# file accompanying this source. -# -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# - -import platform -import subprocess - - -RASPBERRY_PI_MACHINES = { - "cortex-a53", - "cortex-a72", - "cortex-a76", - "cortex-a53-aarch64", - "cortex-a72-aarch64", - "cortex-a76-aarch64", -} -JETSON_MACHINES = {"cortex-a57-aarch64"} - - -def _is_64bit(): - return "64bit" in platform.architecture()[0] - - -def linux_machine() -> str: - machine = platform.machine() - if machine == "x86_64": - return machine - elif machine in ["aarch64", "armv7l"]: - arch_info = ("-" + machine) if _is_64bit() else "" - else: - raise NotImplementedError("Unsupported CPU architecture: `%s`" % machine) - - cpu_info = "" - try: - cpu_info = subprocess.check_output(["cat", "/proc/cpuinfo"]).decode("utf-8") - cpu_part_list = [x for x in cpu_info.split("\n") if "CPU part" in x] - cpu_part = cpu_part_list[0].split(" ")[-1].lower() - except Exception as e: - raise RuntimeError("Failed to identify the CPU with `%s`\nCPU info: `%s`" % (e, cpu_info)) - - if "0xd03" == cpu_part: - return "cortex-a53" + arch_info - elif "0xd07" == cpu_part: - return "cortex-a57" + arch_info - elif "0xd08" == cpu_part: - return "cortex-a72" + arch_info - elif "0xd0b" == cpu_part: - return "cortex-a76" + arch_info - else: - raise NotImplementedError("Unsupported CPU: `%s`." % cpu_part) - - -__all__ = [ - "JETSON_MACHINES", - "linux_machine", - "RASPBERRY_PI_MACHINES", -] diff --git a/demo/python/orca_demo.py b/demo/python/orca_demo.py index b21bebe9..1f2a0cbe 100644 --- a/demo/python/orca_demo.py +++ b/demo/python/orca_demo.py @@ -17,7 +17,33 @@ from pvorca import create, OrcaActivationLimitError -def main(args: argparse.Namespace) -> None: +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + '--access_key', + '-a', + required=True, + help='AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)') + parser.add_argument( + '--text', + '-t', + required=True, + help='Text to be synthesized') + parser.add_argument( + '--output_path', + '-o', + required=True, + help='Absolute path to .wav file where the generated audio will be stored') + parser.add_argument( + '--library_path', + '-l', + help='Absolute path to dynamic library. Default: using the library provided by `pvorca`') + parser.add_argument( + '--model_path', + '-m', + help='Absolute path to Orca model. Default: using the model provided by `pvorca`') + args = parser.parse_args() + access_key = args.access_key model_path = args.model_path library_path = args.library_path @@ -56,29 +82,4 @@ def main(args: argparse.Namespace) -> None: if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - '--access_key', - '-a', - required=True, - help='AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)') - parser.add_argument( - '--text', - '-t', - required=True, - help='Text to be synthesized') - parser.add_argument( - '--output_path', - '-o', - required=True, - help='Absolute path to .wav file where the generated audio will be stored') - parser.add_argument( - '--library_path', - '-l', - help='Absolute path to dynamic library. Default: using the library provided by `pvorca`') - parser.add_argument( - '--model_path', - '-m', - help='Absolute path to Orca model. Default: using the model provided by `pvorca`') - - main(parser.parse_args()) + main() diff --git a/demo/python/orca_demo_streaming.py b/demo/python/orca_demo_streaming.py index 17722557..d8a30002 100644 --- a/demo/python/orca_demo_streaming.py +++ b/demo/python/orca_demo_streaming.py @@ -10,22 +10,251 @@ # import argparse +import platform import re +import subprocess +import threading import time import traceback -from typing import Sequence - +from dataclasses import dataclass +from queue import Queue +from typing import ( + Any, + Callable, + Dict, + Optional, + Sequence, +) + +import numpy as np +import pvorca import tiktoken -from pvorca import OrcaActivationLimitError -from sounddevice import PortAudioError - -from _audio_device import StreamingAudioDevice -from _orca_thread import OrcaThread +from numpy.typing import NDArray +from pvorca import OrcaActivationLimitError, OrcaInvalidArgumentError +from sounddevice import ( + OutputStream, + query_devices, + PortAudioError, +) CUSTOM_PRON_PATTERN = r"\{(.*?\|.*?)\}" CUSTOM_PRON_PATTERN_NO_WHITESPACE = r"\{(.*?\|.*?)\}(?!\s)" +class StreamingAudioDevice: + def __init__(self, device_index: Optional[int] = None) -> None: + if device_index is None: + device_info = query_devices(kind="output") + device_index = int(device_info["index"]) + + self._device_index = device_index + self._queue: Queue[Sequence[int]] = Queue() + + self._buffer = None + self._stream = None + self._sample_rate = None + self._blocksize = None + + def start(self, sample_rate: int) -> None: + self._sample_rate = sample_rate + self._blocksize = self._sample_rate // 20 + self._stream = OutputStream( + channels=1, + samplerate=self._sample_rate, + dtype=np.int16, + device=self._device_index, + callback=self._callback, + blocksize=self._blocksize) + self._stream.start() + + # noinspection PyShadowingNames + # noinspection PyUnusedLocal + def _callback(self, outdata: NDArray, frames: int, time: Any, status: Any) -> None: + if self._queue.empty(): + outdata[:] = 0 + return + + pcm = self._queue.get() + outdata[:, 0] = pcm + + def play(self, pcm_chunk: Sequence[int]) -> None: + if self._stream is None: + raise ValueError("Stream is not started. Call `start` method first.") + + pcm_chunk = np.array(pcm_chunk, dtype=np.int16) + + if self._buffer is not None: + if pcm_chunk is not None: + pcm_chunk = np.concatenate([self._buffer, pcm_chunk]) + else: + pcm_chunk = self._buffer + self._buffer = None + + length = pcm_chunk.shape[0] + for index_block in range(0, length, self._blocksize): + if (length - index_block) < self._blocksize: + self._buffer = pcm_chunk[index_block: index_block + (length - index_block)] + else: + self._queue.put_nowait(pcm_chunk[index_block: index_block + self._blocksize]) + + def flush_and_terminate(self) -> None: + self.flush() + self.terminate() + + def flush(self) -> None: + if self._buffer is not None: + chunk = np.zeros(self._blocksize, dtype=np.int16) + chunk[:self._buffer.shape[0]] = self._buffer + self._queue.put_nowait(chunk) + + time_interval = self._blocksize / self._sample_rate + while not self._queue.empty(): + time.sleep(time_interval) + + time.sleep(time_interval) + + def terminate(self) -> None: + self._stream.stop() + self._stream.close() + + @staticmethod + def list_output_devices() -> Dict[str, Any]: + return query_devices(kind="output") + + +def linux_machine() -> str: + machine = platform.machine() + if machine == "x86_64": + return machine + elif machine in ["aarch64", "armv7l"]: + arch_info = ("-" + machine) if "64bit" in platform.architecture()[0] else "" + else: + raise NotImplementedError("Unsupported CPU architecture: `%s`" % machine) + + cpu_info = "" + try: + cpu_info = subprocess.check_output(["cat", "/proc/cpuinfo"]).decode("utf-8") + cpu_part_list = [x for x in cpu_info.split("\n") if "CPU part" in x] + cpu_part = cpu_part_list[0].split(" ")[-1].lower() + except Exception as e: + raise RuntimeError("Failed to identify the CPU with `%s`\nCPU info: `%s`" % (e, cpu_info)) + + if "0xd03" == cpu_part: + return "cortex-a53" + arch_info + elif "0xd07" == cpu_part: + return "cortex-a57" + arch_info + elif "0xd08" == cpu_part: + return "cortex-a72" + arch_info + elif "0xd0b" == cpu_part: + return "cortex-a76" + arch_info + else: + raise NotImplementedError("Unsupported CPU: `%s`." % cpu_part) + + +class OrcaThread: + @dataclass + class OrcaInput: + text: str + flush: bool + + def __init__( + self, + play_audio_callback: Callable[[Sequence[int]], None], + access_key: str, + num_tokens_per_second: int, + model_path: Optional[str] = None, + library_path: Optional[str] = None, + audio_wait_chunks: Optional[int] = None, + ) -> None: + + self._orca = pvorca.create(access_key=access_key, model_path=model_path, library_path=library_path) + self._orca_stream = self._orca.open_stream() + self._sample_rate = self._orca.sample_rate + + self._play_audio_callback = play_audio_callback + self._num_tokens_per_second = num_tokens_per_second + assert self._num_tokens_per_second > 0 + + self._queue: Queue[Optional[OrcaThread.OrcaInput]] = Queue() + self._thread = None + + self._time_first_audio_available = -1 + self._pcm_buffer: Queue[Sequence[int]] = Queue() + + self._wait_chunks = audio_wait_chunks or self._get_first_audio_wait_chunks() + self._num_pcm_chunks_processed = 0 + + @staticmethod + def _get_first_audio_wait_chunks() -> int: + wait_chunks = 0 + if platform.system() == "Linux": + machine = linux_machine() + if "cortex" in machine: + wait_chunks = 1 + return wait_chunks + + def _run(self) -> None: + while True: + orca_input = self._queue.get() + if orca_input is None: + while not self._pcm_buffer.empty(): + self._play_audio_callback(self._pcm_buffer.get()) + break + + try: + if not orca_input.flush: + pcm = self._orca_stream.synthesize(orca_input.text) + else: + pcm = self._orca_stream.flush() + except OrcaInvalidArgumentError as e: + raise ValueError(f"Orca could not synthesize text input `{orca_input.text}`: `{e}`") + + if pcm is not None: + if self._num_pcm_chunks_processed < self._wait_chunks: + self._pcm_buffer.put_nowait(pcm) + else: + while not self._pcm_buffer.empty(): + self._play_audio_callback(self._pcm_buffer.get()) + self._play_audio_callback(pcm) + + if self._num_pcm_chunks_processed == 0: + self._time_first_audio_available = time.time() + + self._num_pcm_chunks_processed += 1 + + def _close_thread_blocking(self): + self._queue.put_nowait(None) + self._thread.join() + + def start(self) -> None: + self._thread = threading.Thread(target=self._run) + self._thread.start() + + def synthesize(self, text: str) -> None: + self._queue.put_nowait(self.OrcaInput(text=text, flush=False)) + + def flush(self) -> None: + self._queue.put_nowait(self.OrcaInput(text="", flush=True)) + self._close_thread_blocking() + self.start() + + def delete(self) -> None: + self._close_thread_blocking() + self._orca_stream.close() + self._orca.delete() + + def get_time_first_audio_available(self) -> float: + return self._time_first_audio_available + + @property + def sample_rate(self) -> int: + return self._sample_rate + + @property + def version(self) -> str: + return self._orca.version + + def tokenize_text(text: str) -> Sequence[str]: text = re.sub(CUSTOM_PRON_PATTERN_NO_WHITESPACE, r'{\1} ', text) @@ -55,7 +284,47 @@ def tokenize_text(text: str) -> Sequence[str]: return tokens_with_custom_pronunciations -def main(args: argparse.Namespace) -> None: +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--access_key", + "-a", + required=True, + help="AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)") + parser.add_argument( + "--library_path", + "-l", + help="Absolute path to dynamic library. Default: using the library provided by `pvorca`") + parser.add_argument( + "--model_path", + "-m", + help="Absolute path to Orca model. Default: using the model provided by `pvorca`") + parser.add_argument( + "--text-to-stream", + "-t", + required=True, + help="Text to be streamed to Orca") + parser.add_argument( + "--tokens-per-second", + type=int, + default=15, + help="Number of tokens per second to be streamed to Orca, simulating an LLM response.") + parser.add_argument( + "--audio-wait-chunks", + type=int, + default=None, + help="Number of PCM chunks to wait before starting to play audio. Default: system-dependent.") + parser.add_argument( + "--show-audio-devices", + action="store_true", + help="Only list available audio output devices and exit") + parser.add_argument('--audio-device-index', type=int, default=None, help='Index of input audio device') + args = parser.parse_args() + + if args.show_audio_devices: + print(StreamingAudioDevice.list_output_devices()) + exit(0) + access_key = args.access_key model_path = args.model_path library_path = args.library_path @@ -70,13 +339,14 @@ def main(args: argparse.Namespace) -> None: audio_device.start(sample_rate=16000) audio_device.terminate() play_audio_callback = audio_device.play - except PortAudioError as e: + except PortAudioError: print(traceback.format_exc()) print( "WARNING: Failed to initialize audio device, see details above. Falling back to running " "the demo without audio playback.\n") audio_device = None + # noinspection PyUnusedLocal def play_audio_callback(pcm: Sequence[int]): pass @@ -126,45 +396,4 @@ def play_audio_callback(pcm: Sequence[int]): if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "--access_key", - "-a", - required=True, - help="AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)") - parser.add_argument( - "--library_path", - "-l", - help="Absolute path to dynamic library. Default: using the library provided by `pvorca`") - parser.add_argument( - "--model_path", - "-m", - help="Absolute path to Orca model. Default: using the model provided by `pvorca`") - parser.add_argument( - "--text-to-stream", - "-t", - required=True, - help="Text to be streamed to Orca") - parser.add_argument( - "--tokens-per-second", - type=int, - default=15, - help="Number of tokens per second to be streamed to Orca, simulating an LLM response.") - parser.add_argument( - "--audio-wait-chunks", - type=int, - default=None, - help="Number of PCM chunks to wait before starting to play audio. Default: system-dependent.") - parser.add_argument( - "--show-audio-devices", - action="store_true", - help="Only list available audio output devices and exit") - parser.add_argument('--audio-device-index', type=int, default=None, help='Index of input audio device') - - arg = parser.parse_args() - - if arg.show_audio_devices: - print(StreamingAudioDevice.list_output_devices()) - exit(0) - - main(arg) + main() diff --git a/demo/python/setup.py b/demo/python/setup.py index 58a1e9b7..535613dd 100644 --- a/demo/python/setup.py +++ b/demo/python/setup.py @@ -6,11 +6,7 @@ INCLUDE_FILES = [ "../../LICENSE", "orca_demo.py", - "orca_demo_streaming.py", - "_audio_device.py", - "_orca_thread.py", - "_util.py", - "__init__.py"] + "orca_demo_streaming.py"] os.system("git clean -dfx")