diff --git a/.github/workflows/c-demos.yml b/.github/workflows/c-demos.yml index f386bd3..989aac6 100644 --- a/.github/workflows/c-demos.yml +++ b/.github/workflows/c-demos.yml @@ -37,7 +37,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ ubuntu-latest, windows-latest, macos-latest ] include: - os: ubuntu-latest pv_recorder_platform: "linux" @@ -62,7 +62,7 @@ jobs: strategy: matrix: - machine: [rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64] + machine: [ rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64 ] include: - machine: rpi3-32 make_file: "Unix Makefiles" @@ -99,7 +99,8 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + device: [ cpu, cpu:1 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] include: - os: ubuntu-latest platform: linux @@ -131,14 +132,15 @@ jobs: run: cmake --build ./build --target koala_demo_file - name: Test - run: python test/test_koala_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.platform }} ${{ matrix.arch }} + run: python test/test_koala_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.device }} ${{ matrix.platform }} ${{ matrix.arch }} build-filedemo-self-hosted: runs-on: ${{ matrix.machine }} strategy: matrix: - machine: [rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64] + device: [ best ] + machine: [ rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64 ] include: - machine: rpi3-32 platform: raspberry-pi @@ -170,6 +172,24 @@ jobs: arch: arm64 make_file: "MinGW Makefiles" pv_recorder_platform: "windows-arm64" + - device: gpu + machine: pv-linux + platform: linux + arch: x86_64 + make_file: "Unix Makefiles" + pv_recorder_platform: "linux" + - device: gpu + machine: pv-windows + platform: windows + arch: amd64 + make_file: "MinGW Makefiles" + pv_recorder_platform: "windows-amd64" + - device: gpu + machine: pv-ios + platform: mac + arch: arm64 + make_file: "Unix Makefiles" + pv_recorder_platform: "mac-arm64" steps: - uses: actions/checkout@v3 @@ -183,4 +203,4 @@ jobs: run: cmake --build ./build --target koala_demo_file - name: Test - run: python test/test_koala_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.platform }} ${{ matrix.arch }} + run: python3 test/test_koala_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.device }} ${{ matrix.platform }} ${{ matrix.arch }} diff --git a/README.md b/README.md index 807820e..d6ca813 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,9 @@ When done be sure to explicitly release the resources using `koala.delete()`. ```c pv_koala_t *handle = NULL; const char *model_path = "${MODEL_PATH}"; - pv_status_t status = pv_koala_init(${ACCESS_KEY}, model_path, &handle); + const char *device = "best"; + + pv_status_t status = pv_koala_init(${ACCESS_KEY}, model_path, device, &handle); if (status != PV_STATUS_SUCCESS) { // error handling logic } @@ -320,6 +322,13 @@ Replace `${ACCESS_KEY}` with yours obtained from [Picovoice Console](https://con ## Releases +### v3.0.0 - December 9th, 2025 + + - Improved engine performance + - Added support for running on GPU or multiple CPU cores + - Node.js min version bumped to Node 18 + - iOS min version bumped to iOS 16 + ### v2.0.0 - November 24th, 2023 - Improvements to error reporting diff --git a/demo/c/koala_demo_file.c b/demo/c/koala_demo_file.c index 9865320..aaef70c 100644 --- a/demo/c/koala_demo_file.c +++ b/demo/c/koala_demo_file.c @@ -1,9 +1,9 @@ /* - Copyright 2023 Picovoice Inc. + Copyright 2023-2025 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. @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -87,17 +88,21 @@ static void print_dl_error(const char *message) { } static struct option long_options[] = { - {"access_key", required_argument, NULL, 'a'}, - {"library_path", required_argument, NULL, 'l'}, - {"model_path", required_argument, NULL, 'm'}, - {"input_path", required_argument, NULL, 'i'}, - {"output_path", required_argument, NULL, 'o'}, + {"access_key", required_argument, NULL, 'a'}, + {"library_path", required_argument, NULL, 'l'}, + {"model_path", required_argument, NULL, 'm'}, + {"device", required_argument, NULL, 'y'}, + {"input_path", required_argument, NULL, 'i'}, + {"output_path", required_argument, NULL, 'o'}, + {"show_inference_devices", required_argument, NULL, 'z'}, }; static void print_usage(const char *program_name) { fprintf( stdout, - "Usage: %s [-l LIBRARY_PATH -m MODEL_PATH -a ACCESS_KEY -i INPUT_PATH -o OUTPUT_PATH]\n", + "Usage: %s -l LIBRARY_PATH [-m MODEL_PATH -a ACCESS_KEY -y DEVICE -i INPUT_PATH -o OUTPUT_PATH]\n" + " %s [-z, --show_inference_devices] -l LIBRARY_PATH\n", + program_name, program_name); } @@ -122,6 +127,82 @@ static void print_progress_bar(size_t num_total_samples, size_t num_processed_sa fflush(stdout); } +static void print_inference_devices(const char *library_path) { + void *dl_handle = open_dl(library_path); + if (!dl_handle) { + fprintf(stderr, "Failed to open library at '%s'.\n", library_path); + exit(EXIT_FAILURE); + } + + const char *(*pv_status_to_string_func)(pv_status_t) = load_symbol(dl_handle, "pv_status_to_string"); + if (!pv_status_to_string_func) { + print_dl_error("Failed to load 'pv_status_to_string'"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_koala_list_hardware_devices_func)(char ***, int32_t *) = + load_symbol(dl_handle, "pv_koala_list_hardware_devices"); + if (!pv_koala_list_hardware_devices_func) { + print_dl_error("failed to load `pv_koala_list_hardware_devices`"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_koala_free_hardware_devices_func)(char **, int32_t) = + load_symbol(dl_handle, "pv_koala_free_hardware_devices"); + if (!pv_koala_free_hardware_devices_func) { + print_dl_error("failed to load `pv_koala_free_hardware_devices`"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_get_error_stack_func)(char ***, int32_t *) = + load_symbol(dl_handle, "pv_get_error_stack"); + if (!pv_get_error_stack_func) { + print_dl_error("failed to load 'pv_get_error_stack_func'"); + exit(EXIT_FAILURE); + } + + void (*pv_free_error_stack_func)(char **) = + load_symbol(dl_handle, "pv_free_error_stack"); + if (!pv_free_error_stack_func) { + print_dl_error("failed to load 'pv_free_error_stack_func'"); + exit(EXIT_FAILURE); + } + + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = PV_STATUS_RUNTIME_ERROR; + + char **hardware_devices = NULL; + int32_t num_hardware_devices = 0; + pv_status_t status = pv_koala_list_hardware_devices_func(&hardware_devices, &num_hardware_devices); + if (status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + "Failed to list hardware devices with `%s`.\n", + pv_status_to_string_func(status)); + error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Koala error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } + + if (message_stack_depth > 0) { + fprintf(stderr, ":\n"); + print_error_message(message_stack, message_stack_depth); + pv_free_error_stack_func(message_stack); + } + exit(EXIT_FAILURE); + } + + for (int32_t i = 0; i < num_hardware_devices; i++) { + fprintf(stdout, "%s\n", hardware_devices[i]); + } + pv_koala_free_hardware_devices_func(hardware_devices, num_hardware_devices); + close_dl(dl_handle); +} int picovoice_main(int argc, char *argv[]) { const char *library_path = NULL; @@ -129,9 +210,11 @@ int picovoice_main(int argc, char *argv[]) { const char *access_key = NULL; const char *input_path = NULL; const char *output_path = NULL; + const char *device = NULL; + bool show_inference_devices = false; int c; - while ((c = getopt_long(argc, argv, "l:m:a:i:o:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "zl:m:a:y:i:o:", long_options, NULL)) != -1) { switch (c) { case 'l': library_path = optarg; @@ -148,16 +231,37 @@ int picovoice_main(int argc, char *argv[]) { case 'o': output_path = optarg; break; + case 'y': + device = optarg; + break; + case 'z': + show_inference_devices = true; + break; default: exit(EXIT_FAILURE); } } + if (show_inference_devices) { + if (!library_path) { + fprintf(stderr, "`library_path` is required to view available inference devices.\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + print_inference_devices(library_path); + return EXIT_SUCCESS; + } + if (!library_path || !access_key || !input_path || !output_path) { print_usage(argv[0]); exit(EXIT_FAILURE); } + if (device == NULL) { + device = "best"; + } + void *koala_library = open_dl(library_path); if (!koala_library) { fprintf(stderr, "Failed to open library at '%s'.\n", library_path); @@ -177,7 +281,7 @@ int picovoice_main(int argc, char *argv[]) { exit(EXIT_FAILURE); } - pv_status_t (*pv_koala_init_func)(const char *, const char *, pv_koala_t **) = + pv_status_t (*pv_koala_init_func)(const char *, const char *, const char *, pv_koala_t **) = load_symbol(koala_library, "pv_koala_init"); if (!pv_koala_init_func) { print_dl_error("Failed to load 'pv_koala_init'"); @@ -229,7 +333,7 @@ int picovoice_main(int argc, char *argv[]) { } pv_koala_t *koala = NULL; - pv_status_t koala_status = pv_koala_init_func(access_key, model_path, &koala); + pv_status_t koala_status = pv_koala_init_func(access_key, model_path, device, &koala); if (koala_status != PV_STATUS_SUCCESS) { fprintf(stderr, "Failed to init with '%s'", pv_status_to_string_func(koala_status)); char **message_stack = NULL; diff --git a/demo/c/koala_demo_mic.c b/demo/c/koala_demo_mic.c index 953f6de..5886f8b 100644 --- a/demo/c/koala_demo_mic.c +++ b/demo/c/koala_demo_mic.c @@ -92,18 +92,24 @@ static void print_dl_error(const char *message) { static volatile bool is_interrupted = false; static struct option long_options[] = { - {"access_key", required_argument, NULL, 'a'}, - {"audio_device_index", required_argument, NULL, 'd'}, - {"library_path", required_argument, NULL, 'l'}, - {"model_path", required_argument, NULL, 'm'}, - {"output_audio_path", required_argument, NULL, 'o'}, - {"reference_audio_path", no_argument, NULL, 'r'}, - {"show_audio_devices", no_argument, NULL, 's'}, + {"access_key", required_argument, NULL, 'a'}, + {"audio_device_index", required_argument, NULL, 'd'}, + {"library_path", required_argument, NULL, 'l'}, + {"model_path", required_argument, NULL, 'm'}, + {"device", required_argument, NULL, 'y'}, + {"output_audio_path", required_argument, NULL, 'o'}, + {"reference_audio_path", no_argument, NULL, 'r'}, + {"show_audio_devices", no_argument, NULL, 's'}, + {"show_inference_devices", no_argument, NULL, 'z'}, }; static void print_usage(const char *program_name) { fprintf(stdout, - "Usage: %s [-s] [-l LIBRARY_PATH -m MODEL_PATH -a ACCESS_KEY -d AUDIO_DEVICE_INDEX -o WAV_OUTPUT_PATH -r WAV_REFERENCE_PATH]\n", + "Usage: %s -l LIBRARY_PATH [-m MODEL_PATH -a ACCESS_KEY -y DEVICE -d AUDIO_DEVICE_INDEX -o WAV_OUTPUT_PATH -r WAV_REFERENCE_PATH]\n" + " %s [-s, --show_audio_devices]\n" + " %s [-z, --show_inference_devices] -l LIBRARY_PATH\n", + program_name, + program_name, program_name); } @@ -136,6 +142,83 @@ static void show_audio_devices(void) { pv_recorder_free_available_devices(count, devices); } +static void print_inference_devices(const char *library_path) { + void *dl_handle = open_dl(library_path); + if (!dl_handle) { + fprintf(stderr, "Failed to open library at '%s'.\n", library_path); + exit(EXIT_FAILURE); + } + + const char *(*pv_status_to_string_func)(pv_status_t) = load_symbol(dl_handle, "pv_status_to_string"); + if (!pv_status_to_string_func) { + print_dl_error("Failed to load 'pv_status_to_string'"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_koala_list_hardware_devices_func)(char ***, int32_t *) = + load_symbol(dl_handle, "pv_koala_list_hardware_devices"); + if (!pv_koala_list_hardware_devices_func) { + print_dl_error("failed to load `pv_koala_list_hardware_devices`"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_koala_free_hardware_devices_func)(char **, int32_t) = + load_symbol(dl_handle, "pv_koala_free_hardware_devices"); + if (!pv_koala_free_hardware_devices_func) { + print_dl_error("failed to load `pv_koala_free_hardware_devices`"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_get_error_stack_func)(char ***, int32_t *) = + load_symbol(dl_handle, "pv_get_error_stack"); + if (!pv_get_error_stack_func) { + print_dl_error("failed to load 'pv_get_error_stack_func'"); + exit(EXIT_FAILURE); + } + + void (*pv_free_error_stack_func)(char **) = + load_symbol(dl_handle, "pv_free_error_stack"); + if (!pv_free_error_stack_func) { + print_dl_error("failed to load 'pv_free_error_stack_func'"); + exit(EXIT_FAILURE); + } + + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = PV_STATUS_RUNTIME_ERROR; + + char **hardware_devices = NULL; + int32_t num_hardware_devices = 0; + pv_status_t status = pv_koala_list_hardware_devices_func(&hardware_devices, &num_hardware_devices); + if (status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + "Failed to list hardware devices with `%s`.\n", + pv_status_to_string_func(status)); + error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Koala error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } + + if (message_stack_depth > 0) { + fprintf(stderr, ":\n"); + print_error_message(message_stack, message_stack_depth); + pv_free_error_stack_func(message_stack); + } + exit(EXIT_FAILURE); + } + + for (int32_t i = 0; i < num_hardware_devices; i++) { + fprintf(stdout, "%s\n", hardware_devices[i]); + } + pv_koala_free_hardware_devices_func(hardware_devices, num_hardware_devices); + close_dl(dl_handle); +} + static void print_vu_meter(const int16_t *pcm_buffer, int32_t num_samples) { float sum = 0; for (uint32_t i = 0; i < num_samples; i++) { @@ -166,10 +249,12 @@ int picovoice_main(int argc, char *argv[]) { const char *output_path = NULL; const char *reference_path = NULL; const char *model_path = NULL; + const char *device = NULL; + bool show_inference_devices = false; int32_t device_index = -1; int c; - while ((c = getopt_long(argc, argv, "hsl:a:d:o:m:r:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hszl:a:y:d:o:m:r:", long_options, NULL)) != -1) { switch (c) { case 's': show_audio_devices(); @@ -192,16 +277,37 @@ int picovoice_main(int argc, char *argv[]) { case 'd': device_index = (int32_t) strtol(optarg, NULL, 10); break; + case 'y': + device = optarg; + break; + case 'z': + show_inference_devices = true; + break; default: exit(EXIT_FAILURE); } } + if (show_inference_devices) { + if (!library_path) { + fprintf(stderr, "`library_path` is required to view available inference devices.\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + print_inference_devices(library_path); + return EXIT_SUCCESS; + } + if (!library_path || !access_key || !output_path || !model_path) { print_usage(argv[0]); exit(EXIT_FAILURE); } + if (device == NULL) { + device = "best"; + } + drwav_data_format format; format.container = drwav_container_riff; format.format = DR_WAVE_FORMAT_PCM; @@ -271,6 +377,7 @@ int picovoice_main(int argc, char *argv[]) { } pv_status_t (*pv_koala_init_func)( + const char *, const char *, const char *, pv_koala_t **) = load_symbol(koala_library, "pv_koala_init"); @@ -319,7 +426,7 @@ int picovoice_main(int argc, char *argv[]) { } pv_koala_t *koala = NULL; - pv_status_t koala_status = pv_koala_init_func(access_key, model_path, &koala); + pv_status_t koala_status = pv_koala_init_func(access_key, model_path, device, &koala); if (koala_status != PV_STATUS_SUCCESS) { fprintf(stderr, "Failed to init with '%s'", pv_status_to_string_func(koala_status)); char **message_stack = NULL; diff --git a/demo/c/test/requirements.txt b/demo/c/test/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/demo/c/test/test_koala_c.py b/demo/c/test/test_koala_c.py index 9477d76..7d02748 100644 --- a/demo/c/test/test_koala_c.py +++ b/demo/c/test/test_koala_c.py @@ -1,5 +1,5 @@ # -# Copyright 2023 Picovoice Inc. +# Copyright 2023-2025 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. @@ -20,9 +20,10 @@ class KoalaCTestCase(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] - cls._root_dir = os.path.join(os.path.dirname(__file__), "../../..") + cls._device = sys.argv[2] + cls._platform = sys.argv[3] + cls._arch = "" if len(sys.argv) != 5 else sys.argv[4] + cls._root_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..") @staticmethod def _get_lib_ext(platform): @@ -37,6 +38,10 @@ def _get_model_path(self): return os.path.join(self._root_dir, 'lib/common/koala_params.pv') def _get_library_file(self): + if self._platform == "windows": + if self._arch == "amd64": + os.environ["PATH"] += os.pathsep + os.path.join(self._root_dir, "lib", "windows", "amd64") + return os.path.join( self._root_dir, "lib", @@ -54,8 +59,9 @@ def run_koala(self, audio_file_name): "-a", self._access_key, "-l", self._get_library_file(), "-m", self._get_model_path(), + "-y", self._device, "-i", self._get_audio_file(audio_file_name), - "-o", os.path.join(os.path.dirname(__file__), "output.wav") + "-o", os.path.join(os.path.dirname(__file__), "output.wav"), ] process = subprocess.Popen(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout, stderr = process.communicate() @@ -66,9 +72,20 @@ def run_koala(self, audio_file_name): def test_koala(self): self.run_koala("test.wav") + def test_list_hardware_devices(self): + args = [ + os.path.join(os.path.dirname(__file__), "../build/koala_demo_file"), + "-l", self._get_library_file(), + "-z" + ] + process = subprocess.Popen(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = process.communicate() + self.assertEqual(process.poll(), 0) + self.assertEqual(stderr.decode('utf-8'), '') + if __name__ == '__main__': - if len(sys.argv) < 3 or len(sys.argv) > 4: - print("usage: test_koala_c.py ${AccessKey} ${Platform} [${Arch}]") + if len(sys.argv) < 4 or len(sys.argv) > 5: + print("usage: test_koala_c.py ${AccessKey} ${Device} ${Platform} [${Arch}]") exit(1) unittest.main(argv=sys.argv[:1]) diff --git a/include/picovoice.h b/include/picovoice.h index 886558d..6aa6a68 100644 --- a/include/picovoice.h +++ b/include/picovoice.h @@ -1,5 +1,5 @@ /* - Copyright 2018-2023 Picovoice Inc. + Copyright 2018-2025 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. @@ -20,8 +20,16 @@ extern "C" { #endif +#ifdef _MSC_VER + +#define PV_API __declspec(dllexport) + +#else + #define PV_API __attribute__((visibility("default"))) +#endif + /** * Audio sample rate accepted by Picovoice. */ diff --git a/include/pv_koala.h b/include/pv_koala.h index 20e261e..6ba8628 100644 --- a/include/pv_koala.h +++ b/include/pv_koala.h @@ -1,5 +1,5 @@ /* - Copyright 2023 Picovoice Inc. + Copyright 2023-2025 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. @@ -39,12 +39,21 @@ typedef struct pv_koala pv_koala_t; * * @param access_key AccessKey obtained from Picovoice Console (https://console.picovoice.ai/) * @param model_path Absolute path to the file containing model parameters. + * @param device String representation of the device (e.g., CPU or GPU) to use for inference. If set to `best`, the most + * suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU device. + * To select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index of the + * target GPU. If set to `cpu`, the engine will run on the CPU with the default number of threads. To specify the + * number of threads, set this argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is the desired number of threads. * @param[out] object Constructed instance of Koala. * @return Status code. Returns `PV_STATUS_INVALID_ARGUMENT` or `PV_STATUS_OUT_OF_MEMORY`, * `PV_STATUS_RUNTIME_ERROR`, `PV_STATUS_ACTIVATION_ERROR`, `PV_STATUS_ACTIVATION_LIMIT_REACHED`, * `PV_STATUS_ACTIVATION_THROTTLED`, or `PV_STATUS_ACTIVATION_REFUSED` on failure. */ -PV_API pv_status_t pv_koala_init(const char *access_key, const char *model_path, pv_koala_t **object); +PV_API pv_status_t pv_koala_init( + const char *access_key, + const char *model_path, + const char *device, + pv_koala_t **object); /** * Destructor. @@ -104,7 +113,32 @@ PV_API int32_t pv_koala_frame_length(void); */ PV_API const char *pv_koala_version(void); +/** + * Gets a list of hardware devices that can be specified when calling `pv_koala_init` + * + * @param[out] hardware_devices Array of available hardware devices. Devices are NULL terminated strings. + * The array must be freed using `pv_koala_free_hardware_devices`. + * @param[out] num_hardware_devices The number of devices in the `hardware_devices` array. + * @return Status code. Returns `PV_STATUS_OUT_OF_MEMORY`, `PV_STATUS_INVALID_ARGUMENT`, `PV_STATUS_INVALID_STATE`, + * `PV_STATUS_RUNTIME_ERROR`, `PV_STATUS_ACTIVATION_ERROR`, `PV_STATUS_ACTIVATION_LIMIT_REACHED`, + * `PV_STATUS_ACTIVATION_THROTTLED`, or `PV_STATUS_ACTIVATION_REFUSED` on failure. + */ +PV_API pv_status_t pv_koala_list_hardware_devices( + char ***hardware_devices, + int32_t *num_hardware_devices); + +/** + * Frees memory allocated by `pv_koala_list_hardware_devices`. + * + * @param[out] hardware_devices Array of available hardware devices allocated by `pv_koala_list_hardware_devices`. + * @param[out] num_hardware_devices The number of devices in the `hardware_devices` array. + */ +PV_API void pv_koala_free_hardware_devices( + char **hardware_devices, + int32_t num_hardware_devices); + #ifdef __cplusplus + } #endif diff --git a/resources/.lint/spell-check/dict.txt b/resources/.lint/spell-check/dict.txt index 4c4ea9d..a9fe1c1 100644 --- a/resources/.lint/spell-check/dict.txt +++ b/resources/.lint/spell-check/dict.txt @@ -18,6 +18,7 @@ Emscripten frombuffer hanning HEAPU +hszl iife irfft jetson