Skip to content

Commit 781a8f1

Browse files
committed
v4.0 C Binding & Demo
1 parent 3f5465a commit 781a8f1

File tree

11 files changed

+303
-33
lines changed

11 files changed

+303
-33
lines changed

.github/workflows/c-demos.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ jobs:
108108

109109
strategy:
110110
matrix:
111+
device: [ cpu, cpu:1 ]
111112
os: [ubuntu-latest, windows-latest, macos-latest]
112113
include:
113114
- os: ubuntu-latest
@@ -143,13 +144,14 @@ jobs:
143144
run: pip install -r test/requirements.txt
144145

145146
- name: Test
146-
run: python test/test_rhino_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.platform }} ${{ matrix.arch }}
147+
run: python test/test_rhino_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.device }} ${{ matrix.platform }} ${{ matrix.arch }}
147148

148149
build-filedemo-self-hosted:
149150
runs-on: ${{ matrix.machine }}
150151

151152
strategy:
152153
matrix:
154+
device: [ cpu, cpu:1 ]
153155
machine: [rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64]
154156
include:
155157
- machine: rpi3-32
@@ -182,6 +184,24 @@ jobs:
182184
arch: arm64
183185
make_file: "MinGW Makefiles"
184186
pv_recorder_platform: "windows-arm64"
187+
- device: gpu
188+
machine: pv-linux
189+
platform: linux
190+
arch: x86_64
191+
make_file: "Unix Makefiles"
192+
pv_recorder_platform: "linux"
193+
- device: gpu
194+
machine: pv-windows
195+
platform: windows
196+
arch: amd64
197+
make_file: "MinGW Makefiles"
198+
pv_recorder_platform: "windows-amd64"
199+
- device: gpu
200+
machine: pv-ios
201+
platform: mac
202+
arch: arm64
203+
make_file: "Unix Makefiles"
204+
pv_recorder_platform: "mac-arm64"
185205

186206
steps:
187207
- uses: actions/checkout@v3
@@ -198,4 +218,4 @@ jobs:
198218
run: pip3 install -r test/requirements.txt
199219

200220
- name: Test
201-
run: python3 test/test_rhino_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.platform }} ${{ matrix.arch }}
221+
run: python3 test/test_rhino_c.py ${{secrets.PV_VALID_ACCESS_KEY}} ${{ matrix.device }} ${{ matrix.platform }} ${{ matrix.arch }}

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1219,12 +1219,13 @@ header file contains relevant information. An instance of the Rhino object can b
12191219
```c
12201220
const char *access_key = "${ACCESS_KEY}" // obtained from the Picovoice Console (https://console.picovoice.ai/)
12211221
const char *model_path = ... // Available at lib/common/rhino_params.pv
1222+
const char *device = "best";
12221223
const char *context_path = ... // absolute path to context file for the domain of interest
12231224
const float sensitivity = 0.5f;
12241225
bool require_endpoint = false;
12251226

12261227
pv_rhino_t *handle = NULL;
1227-
const pv_status_t status = pv_rhino_init(access_key, model_path, context_path, sensitivity, require_endpoint, &handle);
1228+
const pv_status_t status = pv_rhino_init(access_key, model_path, device, context_path, sensitivity, require_endpoint, &handle);
12281229
if (status != PV_STATUS_SUCCESS) {
12291230
// add error handling code
12301231
}

demo/c/rhino_demo_file.c

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2018-2023 Picovoice Inc.
2+
Copyright 2018-2025 Picovoice Inc.
33
44
You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
55
file accompanying this source.
@@ -90,19 +90,21 @@ static struct option long_options[] = {
9090
{"access_key", required_argument, NULL, 'a'},
9191
{"library_path", required_argument, NULL, 'l'},
9292
{"model_path", required_argument, NULL, 'm'},
93+
{"device", required_argument, NULL, 'y'},
9394
{"context_path", required_argument, NULL, 'c'},
9495
{"wav_path", required_argument, NULL, 'w'},
9596
{"sensitivity", required_argument, NULL, 't'},
9697
{"endpoint_duration_sec", required_argument, NULL, 'u'},
9798
{"require_endpoint", required_argument, NULL, 'e'},
98-
{"performance_threshold_sec", optional_argument, NULL, 'p'}
99+
{"performance_threshold_sec", optional_argument, NULL, 'p'},
100+
{"show_inference_devices", no_argument, NULL, 'i'},
99101
};
100102

101103
void print_usage(const char *program_name) {
102104
fprintf(
103105
stderr,
104-
"Usage : %s -a ACCESS_KEY -l LIBRARY_PATH -m MODEL_PATH -c CONTEXT_PATH -w WAV_PATH [-t SENSITIVITY] "
105-
"[-u, --endpoint_duration_sec] [-e, --require_endpoint (true,false)]\n",
106+
"Usage : %s -a ACCESS_KEY -l LIBRARY_PATH -m MODEL_PATH -y DEVICE -c CONTEXT_PATH -w WAV_PATH [-t SENSITIVITY] "
107+
"[-u, --endpoint_duration_sec] [-e, --require_endpoint (true,false)] [-i, --show_inference_devices]\n",
106108
program_name);
107109
}
108110

@@ -112,19 +114,98 @@ void print_error_message(char **message_stack, int32_t message_stack_depth) {
112114
}
113115
}
114116

117+
void print_inference_devices(const char *library_path) {
118+
void *dl_handle = open_dl(library_path);
119+
if (!dl_handle) {
120+
fprintf(stderr, "Failed to open library at '%s'.\n", library_path);
121+
exit(EXIT_FAILURE);
122+
}
123+
124+
const char *(*pv_status_to_string_func)(pv_status_t) = load_symbol(dl_handle, "pv_status_to_string");
125+
if (!pv_status_to_string_func) {
126+
print_dl_error("Failed to load 'pv_status_to_string'");
127+
exit(EXIT_FAILURE);
128+
}
129+
130+
pv_status_t (*pv_rhino_list_hardware_devices_func)(char ***, int32_t *) =
131+
load_symbol(dl_handle, "pv_rhino_list_hardware_devices");
132+
if (!pv_rhino_list_hardware_devices_func) {
133+
print_dl_error("failed to load `pv_rhino_list_hardware_devices`");
134+
exit(EXIT_FAILURE);
135+
}
136+
137+
pv_status_t (*pv_rhino_free_hardware_devices_func)(char **, int32_t) =
138+
load_symbol(dl_handle, "pv_rhino_free_hardware_devices");
139+
if (!pv_rhino_free_hardware_devices_func) {
140+
print_dl_error("failed to load `pv_rhino_free_hardware_devices`");
141+
exit(EXIT_FAILURE);
142+
}
143+
144+
pv_status_t (*pv_get_error_stack_func)(char ***, int32_t *) =
145+
load_symbol(dl_handle, "pv_get_error_stack");
146+
if (!pv_get_error_stack_func) {
147+
print_dl_error("failed to load 'pv_get_error_stack_func'");
148+
exit(EXIT_FAILURE);
149+
}
150+
151+
void (*pv_free_error_stack_func)(char **) =
152+
load_symbol(dl_handle, "pv_free_error_stack");
153+
if (!pv_free_error_stack_func) {
154+
print_dl_error("failed to load 'pv_free_error_stack_func'");
155+
exit(EXIT_FAILURE);
156+
}
157+
158+
char **message_stack = NULL;
159+
int32_t message_stack_depth = 0;
160+
pv_status_t error_status = PV_STATUS_RUNTIME_ERROR;
161+
162+
char **hardware_devices = NULL;
163+
int32_t num_hardware_devices = 0;
164+
pv_status_t status = pv_rhino_list_hardware_devices_func(&hardware_devices, &num_hardware_devices);
165+
if (status != PV_STATUS_SUCCESS) {
166+
fprintf(
167+
stderr,
168+
"Failed to list hardware devices with `%s`.\n",
169+
pv_status_to_string_func(status));
170+
error_status = pv_get_error_stack_func(&message_stack, &message_stack_depth);
171+
if (error_status != PV_STATUS_SUCCESS) {
172+
fprintf(
173+
stderr,
174+
".\nUnable to get Rhino error state with '%s'.\n",
175+
pv_status_to_string_func(error_status));
176+
exit(EXIT_FAILURE);
177+
}
178+
179+
if (message_stack_depth > 0) {
180+
fprintf(stderr, ":\n");
181+
print_error_message(message_stack, message_stack_depth);
182+
pv_free_error_stack_func(message_stack);
183+
}
184+
exit(EXIT_FAILURE);
185+
}
186+
187+
for (int32_t i = 0; i < num_hardware_devices; i++) {
188+
fprintf(stdout, "%s\n", hardware_devices[i]);
189+
}
190+
pv_rhino_free_hardware_devices_func(hardware_devices, num_hardware_devices);
191+
close_dl(dl_handle);
192+
}
193+
115194
int picovoice_main(int argc, char *argv[]) {
116195
const char *access_key = NULL;
117196
const char *library_path = NULL;
118197
const char *model_path = NULL;
198+
const char *device = "best";
119199
const char *context_path = NULL;
120200
const char *wav_path = NULL;
121201
float sensitivity = 0.5f;
122202
float endpoint_duration_sec = 1.f;
123203
bool require_endpoint = true;
124204
double performance_threshold_sec = 0;
205+
bool show_inference_devices = false;
125206

126207
int c;
127-
while ((c = getopt_long(argc, argv, "a:l:m:c:w:t:u:e:p:", long_options, NULL)) != -1) {
208+
while ((c = getopt_long(argc, argv, "a:l:m:y:c:w:t:u:e:p:i", long_options, NULL)) != -1) {
128209
switch (c) {
129210
case 'a':
130211
access_key = optarg;
@@ -135,6 +216,9 @@ int picovoice_main(int argc, char *argv[]) {
135216
case 'm':
136217
model_path = optarg;
137218
break;
219+
case 'y':
220+
device = optarg;
221+
break;
138222
case 'c':
139223
context_path = optarg;
140224
break;
@@ -153,11 +237,25 @@ int picovoice_main(int argc, char *argv[]) {
153237
case 'p':
154238
performance_threshold_sec = strtod(optarg, NULL);
155239
break;
240+
case 'i':
241+
show_inference_devices = true;
242+
break;
156243
default:
157244
exit(1);
158245
}
159246
}
160247

248+
if (show_inference_devices) {
249+
if (!library_path) {
250+
fprintf(stderr, "`library_path` is required to view available inference devices.\n");
251+
print_usage(argv[0]);
252+
exit(1);
253+
}
254+
255+
print_inference_devices(library_path);
256+
return 0;
257+
}
258+
161259
if (!access_key || !library_path || !model_path || !context_path || !wav_path) {
162260
print_usage(argv[0]);
163261
exit(1);
@@ -185,6 +283,7 @@ int picovoice_main(int argc, char *argv[]) {
185283
const char *,
186284
const char *,
187285
const char *,
286+
const char *,
188287
float,
189288
float,
190289
bool,
@@ -303,6 +402,7 @@ int picovoice_main(int argc, char *argv[]) {
303402
pv_status_t status = pv_rhino_init_func(
304403
access_key,
305404
model_path,
405+
device,
306406
context_path,
307407
sensitivity,
308408
endpoint_duration_sec,

0 commit comments

Comments
 (0)