From 137688722faeb63eda41551019da4cdc65d3fc80 Mon Sep 17 00:00:00 2001 From: Ian Lavery Date: Mon, 30 Oct 2023 17:43:10 -0700 Subject: [PATCH] v2.0 c (#125) --- demo/c/octopus_index_demo.c | 155 ++++++++++++++++++++++++++--------- demo/c/octopus_search_demo.c | 129 ++++++++++++++++++++--------- include/pv_octopus.h | 51 ++++++++++-- 3 files changed, 246 insertions(+), 89 deletions(-) diff --git a/demo/c/octopus_index_demo.c b/demo/c/octopus_index_demo.c index 77521a0..33ed1ad 100644 --- a/demo/c/octopus_index_demo.c +++ b/demo/c/octopus_index_demo.c @@ -1,7 +1,6 @@ #include #include #include -#include #if defined(_WIN32) || defined(_WIN64) @@ -75,13 +74,19 @@ static struct option long_options[] = { {"index_path", required_argument, NULL, 'i'}, }; -void print_usage(const char *program_name) { +static void print_usage(const char *program_name) { fprintf( stderr, "usage : %s -l LIBRARY_PATH -m MODEL_PATH -a ACCESS_KEY -w AUDIO_PATH -i INDEX_PATH\n", program_name); } +static void print_error_message(char **message_stack, int32_t message_stack_depth) { + for (int32_t i = 0; i < message_stack_depth; i++) { + fprintf(stderr, " [%d] %s\n", i, message_stack[i]); + } +} + int picovoice_main(int argc, char *argv[]) { const char *library_path = NULL; const char *model_path = NULL; @@ -108,91 +113,163 @@ int picovoice_main(int argc, char *argv[]) { index_path = optarg; break; default: - exit(1); + exit(EXIT_FAILURE); } } if (!library_path || !model_path || !audio_path || !access_key || !index_path) { print_usage(argv[0]); - exit(1); + exit(EXIT_FAILURE); } void *dl = pv_open_dl(library_path); if (!dl) { - fprintf(stderr, "failed to open library.\n"); - exit(1); + fprintf(stderr, "Failed to open library.\n"); + exit(EXIT_FAILURE); } const char *(*pv_status_to_string_func)(pv_status_t) = pv_load_sym(dl, "pv_status_to_string"); if (!pv_status_to_string_func) { - print_dl_error("failed to load 'pv_status_to_string'"); - exit(1); + print_dl_error("Failed to load 'pv_status_to_string'"); + exit(EXIT_FAILURE); } - pv_status_t (*pv_octopus_init_func)(const char *, const char *, pv_octopus_t **) = pv_load_sym(dl, "pv_octopus_init"); + pv_status_t + (*pv_octopus_init_func)(const char *, const char *, pv_octopus_t **) = pv_load_sym(dl, "pv_octopus_init"); if (!pv_octopus_init_func) { - print_dl_error("failed to load 'pv_octopus_init()'."); - exit(1); + print_dl_error("Failed to load 'pv_octopus_init'."); + exit(EXIT_FAILURE); } void (*pv_octopus_delete_func)(pv_octopus_t *) = pv_load_sym(dl, "pv_octopus_delete"); if (!pv_octopus_delete_func) { - print_dl_error("failed to load 'pv_octopus_delete()'"); - exit(1); + print_dl_error("Failed to load 'pv_octopus_delete'"); + exit(EXIT_FAILURE); } - pv_status_t (*pv_octopus_index_file_func)(pv_octopus_t *, const char *, void **, int32_t *) = NULL; + pv_status_t (*pv_octopus_index_file_size_func)(pv_octopus_t *, const char *, int32_t *) = NULL; + pv_octopus_index_file_size_func = pv_load_sym(dl, "pv_octopus_index_file_size"); + if (!pv_octopus_index_file_size_func) { + print_dl_error("Failed to load 'pv_octopus_index_file_size'"); + exit(EXIT_FAILURE); + } + + pv_status_t (*pv_octopus_index_file_func)(pv_octopus_t *, const char *, void *) = NULL; pv_octopus_index_file_func = pv_load_sym(dl, "pv_octopus_index_file"); if (!pv_octopus_index_file_func) { - print_dl_error("failed to load 'pv_octopus_index_file()'"); - exit(1); + print_dl_error("Failed to load 'pv_octopus_index_file'"); + exit(EXIT_FAILURE); } const char (*pv_octopus_version_func)(const pv_octopus_t *) = pv_load_sym(dl, "pv_octopus_version"); if (!pv_octopus_version_func) { - print_dl_error("failed to load 'pv_octopus_version()'"); - exit(1); + print_dl_error("Failed to load 'pv_octopus_version'"); + exit(EXIT_FAILURE); + } + + + pv_status_t (*pv_get_error_stack_func)(char ***, int32_t *) = pv_load_sym(dl, "pv_get_error_stack"); + if (!pv_get_error_stack_func) { + print_dl_error("Failed to load 'pv_get_error_stack'"); + exit(EXIT_FAILURE); + } + + void (*pv_free_error_stack_func)(char **) = pv_load_sym(dl, "pv_free_error_stack"); + if (!pv_free_error_stack_func) { + print_dl_error("Failed to load 'pv_free_error_stack'"); + exit(EXIT_FAILURE); } pv_octopus_t *o = NULL; pv_status_t status = pv_octopus_init_func(access_key, model_path, &o); if (status != PV_STATUS_SUCCESS) { - fprintf(stderr, "failed to init with '%s'.\n", pv_status_to_string_func(status)); - exit(1); - } + fprintf(stderr, "Failed to init with '%s'", pv_status_to_string_func(status)); + + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Octopus error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } - double total_cpu_time_usec = 0; + 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); + } - void *indices = NULL; int32_t num_indices_byte = 0; + status = pv_octopus_index_file_size_func(o, audio_path, &num_indices_byte); + if (status != PV_STATUS_SUCCESS) { + fprintf(stderr, "Failed to get index size with '%s'", pv_status_to_string_func(status)); + + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Octopus error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } - struct timeval before; - gettimeofday(&before, NULL); + 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); + } - status = pv_octopus_index_file_func(o, audio_path, &indices, &num_indices_byte); - if (status != PV_STATUS_SUCCESS) { - fprintf(stderr, "failed to index with '%s'.\n", pv_status_to_string_func(status)); - exit(1); + void *indices = calloc(num_indices_byte, sizeof(char)); + if (!indices) { + fprintf(stderr, "Failed to allocate '%d' bytes of memory for Octopus indices.\n", num_indices_byte); + exit(EXIT_FAILURE); } - struct timeval after; - gettimeofday(&after, NULL); + status = pv_octopus_index_file_func(o, audio_path, indices); + if (status != PV_STATUS_SUCCESS) { + fprintf(stderr, "Failed to index file with '%s'", pv_status_to_string_func(status)); + + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Octopus error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } - total_cpu_time_usec += - (double) (after.tv_sec - before.tv_sec) * 1e6 + (double) (after.tv_usec - before.tv_usec); + 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); + } pv_octopus_delete_func(o); pv_close_dl(dl); FILE *f = fopen(index_path, "wb"); if (!f) { - fprintf(stderr, "failed to create index file at '%s'.\n", index_path); - exit(1); + fprintf(stderr, "Failed to create index file at '%s'.\n", index_path); + exit(EXIT_FAILURE); } if (fwrite(indices, 1, num_indices_byte, f) != (size_t) num_indices_byte) { - fprintf(stderr, "failed to write into index file at '%s'.\n", index_path); - exit(1); + fprintf(stderr, "Failed to write into index file at '%s'.\n", index_path); + exit(EXIT_FAILURE); } fclose(f); @@ -211,7 +288,7 @@ int main(int argc, char *argv[]) { LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); if (wargv == NULL) { fprintf(stderr, "CommandLineToArgvW failed\n"); - exit(1); + exit(EXIT_FAILURE); } char *utf8_argv[argc]; @@ -221,7 +298,7 @@ int main(int argc, char *argv[]) { int arg_chars_num = WideCharToMultiByte(CP_UTF8, UTF8_COMPOSITION_FLAG, wargv[i], NULL_TERMINATED, NULL, 0, NULL, NULL); utf8_argv[i] = (char *) malloc(arg_chars_num * sizeof(char)); if (!utf8_argv[i]) { - fprintf(stderr, "failed to to allocate memory for converting args"); + fprintf(stderr, "Failed to to allocate memory for converting args"); } WideCharToMultiByte(CP_UTF8, UTF8_COMPOSITION_FLAG, wargv[i], NULL_TERMINATED, utf8_argv[i], arg_chars_num, NULL, NULL); } diff --git a/demo/c/octopus_search_demo.c b/demo/c/octopus_search_demo.c index f55bd8c..1c00515 100644 --- a/demo/c/octopus_search_demo.c +++ b/demo/c/octopus_search_demo.c @@ -1,7 +1,6 @@ #include #include #include -#include #if defined(_WIN32) || defined(_WIN64) @@ -75,13 +74,19 @@ static struct option long_options[] = { {"search_phrase", required_argument, NULL, 's'}, }; -void print_usage(const char *program_name) { +static void print_usage(const char *program_name) { fprintf( stderr, "usage : %s -l LIBRARY_PATH -m MODEL_PATH -a ACCESS_KEY -i INDEX_PATH -s SEARCH_PHRASE\n", program_name); } +static void print_error_message(char **message_stack, int32_t message_stack_depth) { + for (int32_t i = 0; i < message_stack_depth; i++) { + fprintf(stderr, " [%d] %s\n", i, message_stack[i]); + } +} + int picovoice_main(int argc, char *argv[]) { const char *library_path = NULL; const char *model_path = NULL; @@ -108,37 +113,44 @@ int picovoice_main(int argc, char *argv[]) { search_phrase = optarg; break; default: - exit(1); + exit(EXIT_FAILURE); } } if (!library_path || !model_path || !search_phrase || !access_key || !index_path) { print_usage(argv[0]); - exit(1); + exit(EXIT_FAILURE); } void *dl = pv_open_dl(library_path); if (!dl) { print_dl_error("Failed to open library"); - exit(1); + exit(EXIT_FAILURE); } const char *(*pv_status_to_string_func)(pv_status_t) = pv_load_sym(dl, "pv_status_to_string"); if (!pv_status_to_string_func) { print_dl_error("Failed to load symbol 'pv_status_to_string'"); - exit(1); + exit(EXIT_FAILURE); } - pv_status_t (*pv_octopus_init_func)(const char *, const char *, pv_octopus_t **) = pv_load_sym(dl, "pv_octopus_init"); + pv_status_t + (*pv_octopus_init_func)(const char *, const char *, pv_octopus_t **) = pv_load_sym(dl, "pv_octopus_init"); if (!pv_octopus_init_func) { print_dl_error("Failed to load symbol 'pv_octopus_init'"); - exit(1); + exit(EXIT_FAILURE); } void (*pv_octopus_delete_func)(pv_octopus_t *) = pv_load_sym(dl, "pv_octopus_delete"); if (!pv_octopus_delete_func) { print_dl_error("Failed to load symbol 'pv_octopus_delete'"); - exit(1); + exit(EXIT_FAILURE); + } + + void (*pv_octopus_matches_delete_func)(pv_octopus_match_t *) = pv_load_sym(dl, "pv_octopus_matches_delete"); + if (!pv_octopus_matches_delete_func) { + print_dl_error("Failed to load symbol 'pv_octopus_matches_delete'"); + exit(EXIT_FAILURE); } pv_status_t (*pv_octopus_search_func)( @@ -150,68 +162,103 @@ int picovoice_main(int argc, char *argv[]) { int32_t *) = pv_load_sym(dl, "pv_octopus_search"); if (!pv_octopus_search_func) { print_dl_error("Failed to load symbol 'pv_octopus_search'"); - exit(1); + exit(EXIT_FAILURE); } const char (*pv_octopus_version_func)(const pv_octopus_t *) = pv_load_sym(dl, "pv_octopus_version"); if (!pv_octopus_version_func) { print_dl_error("Failed to load symbol 'pv_octopus_version'"); - exit(1); + exit(EXIT_FAILURE); } - void (*pv_free_func)(void *) = pv_load_sym(dl, "pv_free"); - if (!pv_free_func) { - print_dl_error("failed to load `pv_free`"); - exit(1); + pv_status_t (*pv_get_error_stack_func)(char ***, int32_t *) = pv_load_sym(dl, "pv_get_error_stack"); + if (!pv_get_error_stack_func) { + print_dl_error("Failed to load 'pv_get_error_stack'"); + exit(EXIT_FAILURE); + } + + void (*pv_free_error_stack_func)(char **) = pv_load_sym(dl, "pv_free_error_stack"); + if (!pv_free_error_stack_func) { + print_dl_error("Failed to load 'pv_free_error_stack'"); + exit(EXIT_FAILURE); } pv_octopus_t *o = NULL; pv_status_t status = pv_octopus_init_func(access_key, model_path, &o); if (status != PV_STATUS_SUCCESS) { - fprintf(stderr, "failed to init with '%s'.\n", pv_status_to_string_func(status)); - exit(1); + fprintf(stderr, "Failed to init with '%s'", pv_status_to_string_func(status)); + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Octopus 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); } FILE *f = fopen(index_path, "rb"); if (!f) { - fprintf(stderr, "failed to open index file at '%s'.\n", index_path); - exit(1); + fprintf(stderr, "Failed to open index file at '%s'.\n", index_path); + exit(EXIT_FAILURE); } fseek(f, 0, SEEK_END); const int32_t num_indices_byte = (int32_t) ftell(f); fseek(f, 0, SEEK_SET); - void *indices = malloc(num_indices_byte); + + void *indices = calloc(num_indices_byte, sizeof(char)); if (!indices) { - fprintf(stderr, "failed to allocate '%d' bytes of memory for indices.\n", num_indices_byte); - exit(1); + fprintf(stderr, "Failed to allocate '%d' bytes of memory for indices.\n", num_indices_byte); + exit(EXIT_FAILURE); } if (fread(indices, 1, num_indices_byte, f) != (size_t) num_indices_byte) { - fprintf(stderr, "failed to read indices from '%s'.\n", index_path); - exit(1); + fprintf(stderr, "Failed to read indices from '%s'.\n", index_path); + exit(EXIT_FAILURE); } fclose(f); - double total_cpu_time_usec = 0; - pv_octopus_match_t *matches = NULL; int32_t num_matches = 0; - struct timeval before; - gettimeofday(&before, NULL); - - status = pv_octopus_search_func(o, indices, num_indices_byte, search_phrase, &matches, &num_matches); + status = pv_octopus_search_func( + o, + indices, + num_indices_byte, + search_phrase, + &matches, + &num_matches); if (status != PV_STATUS_SUCCESS) { - fprintf(stderr, "failed to search with '%s'.\n", pv_status_to_string_func(status)); - exit(1); - } - - struct timeval after; - gettimeofday(&after, NULL); + fprintf(stderr, "Failed to search with '%s'", pv_status_to_string_func(status)); + char **message_stack = NULL; + int32_t message_stack_depth = 0; + pv_status_t error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth); + if (error_status != PV_STATUS_SUCCESS) { + fprintf( + stderr, + ".\nUnable to get Octopus error state with '%s'.\n", + pv_status_to_string_func(error_status)); + exit(EXIT_FAILURE); + } - total_cpu_time_usec += - (double) (after.tv_sec - before.tv_sec) * 1e6 + (double) (after.tv_usec - before.tv_usec); + 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); + } free(indices); pv_octopus_delete_func(o); @@ -227,7 +274,7 @@ int picovoice_main(int argc, char *argv[]) { matches[i].probability); } - pv_free_func(matches); + pv_octopus_matches_delete_func(matches); pv_close_dl(dl); return 0; @@ -243,7 +290,7 @@ int main(int argc, char *argv[]) { LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); if (wargv == NULL) { fprintf(stderr, "CommandLineToArgvW failed\n"); - exit(1); + exit(EXIT_FAILURE); } char *utf8_argv[argc]; @@ -253,7 +300,7 @@ int main(int argc, char *argv[]) { int arg_chars_num = WideCharToMultiByte(CP_UTF8, UTF8_COMPOSITION_FLAG, wargv[i], NULL_TERMINATED, NULL, 0, NULL, NULL); utf8_argv[i] = (char *) malloc(arg_chars_num * sizeof(char)); if (!utf8_argv[i]) { - fprintf(stderr, "failed to to allocate memory for converting args"); + fprintf(stderr, "Failed to to allocate memory for converting args"); } WideCharToMultiByte(CP_UTF8, UTF8_COMPOSITION_FLAG, wargv[i], NULL_TERMINATED, utf8_argv[i], arg_chars_num, NULL, NULL); } diff --git a/include/pv_octopus.h b/include/pv_octopus.h index 8c7ed35..5f6eac7 100644 --- a/include/pv_octopus.h +++ b/include/pv_octopus.h @@ -1,5 +1,5 @@ /* - Copyright 2020-2022 Picovoice Inc. + Copyright 2020-2023 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. @@ -49,6 +49,19 @@ PV_API pv_status_t pv_octopus_init( */ PV_API void pv_octopus_delete(pv_octopus_t *object); +/** + * Determines size required for indices buffer when indexing audio data. + * + * @param object Octopus object. + * @param num_samples Number of audio samples to index. + * @param num_indices_bytes Size of index metadata in bytes. + * @return Status code. Returns 'PV_STATUS_INVALID_ARGUMENT' on failure + */ +PV_API pv_status_t pv_octopus_index_size( + pv_octopus_t *object, + int32_t num_samples, + int32_t *num_indices_bytes); + /** * Indexes audio data. * @@ -56,8 +69,7 @@ PV_API void pv_octopus_delete(pv_octopus_t *object); * @param pcm Audio data. The audio needs to have a sample rate equal to 'pv_sample_rate()' and be 16-bit * linearly-encoded. Octopus operates on single-channel audio. * @param num_samples Number of audio samples to index. - * @param indices Index metadata. - * @param num_indices_bytes Size of index metadata in bytes. + * @param indices Buffer to store index metadata. Must be pre-allocated with result of `pv_octopus_index_size()`. * @return Status code. Returns 'PV_STATUS_INVALID_ARGUMENT', '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 @@ -66,16 +78,31 @@ PV_API pv_status_t pv_octopus_index( pv_octopus_t *object, const int16_t *pcm, int32_t num_samples, - void **indices, + void *indices); + +/** + * Determines size required for indices buffer when indexing an audio file. + * + * @param object Octopus object. + * @param path Absolute path to the audio file. The file needs to have a sample rate equal to or greater than + * `pv_sample_rate()`. The supported formats are: `3gp (AMR)`, `FLAC`, `MP3`, `MP4/m4a (AAC)`, `Ogg`, `WAV`, `WebM`. + * Files with stereo audio are mixed into a single mono channel and then processed. + * @param num_indices_bytes Size of index metadata in bytes. + * @return Status code. Returns 'PV_STATUS_INVALID_ARGUMENT' on failure + */ +PV_API pv_status_t pv_octopus_index_file_size( + pv_octopus_t *object, + const char *path, int32_t *num_indices_bytes); /** * Indexes an audio file. * * @param object Octopus object. - * @param path Absolute path to the audio file. - * @param indices Index metadata. - * @param num_indices_bytes Size of index metadata in bytes. + * @param path Absolute path to the audio file. The file needs to have a sample rate equal to or greater than + * `pv_sample_rate()`. The supported formats are: `3gp (AMR)`, `FLAC`, `MP3`, `MP4/m4a (AAC)`, `Ogg`, `WAV`, `WebM`. + * Files with stereo audio are mixed into a single mono channel and then processed. + * @param indices Buffer to store index metadata. Must be pre-allocated with result of `pv_octopus_index_file_size()`. * @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 @@ -83,8 +110,7 @@ PV_API pv_status_t pv_octopus_index( PV_API pv_status_t pv_octopus_index_file( pv_octopus_t *object, const char *path, - void **indices, - int32_t *num_indices_bytes); + void *indices); /** * Container representing a matched utterance. @@ -114,6 +140,13 @@ PV_API pv_status_t pv_octopus_search( pv_octopus_match_t **matches, int32_t *num_matches); +/** + * Deletes matches returned from `pv_octopus_search()` + * + * @param matches matched utterances returned from `pv_octopus_search()` + */ +PV_API void pv_octopus_matches_delete(pv_octopus_match_t *matches); + /** * Getter for version. *