@@ -54,6 +54,10 @@ type PvCobraVersionFn = unsafe extern "C" fn() -> *mut c_char;
54
54
type PvCobraProcessFn =
55
55
unsafe extern "C" fn ( object : * mut CCobra , pcm : * const i16 , is_voiced : * mut f32 ) -> PvStatus ;
56
56
type PvCobraDeleteFn = unsafe extern "C" fn ( object : * mut CCobra ) ;
57
+ type PvGetErrorStackFn =
58
+ unsafe extern "C" fn ( message_stack : * mut * mut * mut c_char , message_stack_depth : * mut i32 ) ;
59
+ type PvFreeErrorStackFn = unsafe extern "C" fn ( message_stack : * mut * mut c_char ) ;
60
+ type PvSetSdkFn = unsafe extern "C" fn ( sdk : * const c_char ) ;
57
61
58
62
#[ derive( Clone , Debug ) ]
59
63
pub enum CobraErrorStatus {
@@ -66,24 +70,44 @@ pub enum CobraErrorStatus {
66
70
#[ derive( Clone , Debug ) ]
67
71
pub struct CobraError {
68
72
pub status : CobraErrorStatus ,
69
- pub message : Option < String > ,
73
+ pub message : String ,
74
+ pub message_stack : Vec < String > ,
70
75
}
71
76
72
77
impl CobraError {
73
78
pub fn new ( status : CobraErrorStatus , message : impl Into < String > ) -> Self {
74
79
Self {
75
80
status,
76
- message : Some ( message. into ( ) ) ,
81
+ message : message. into ( ) ,
82
+ message_stack : Vec :: new ( )
83
+ }
84
+ }
85
+
86
+ pub fn new_with_stack (
87
+ status : CobraErrorStatus ,
88
+ message : impl Into < String > ,
89
+ message_stack : impl Into < Vec < String > >
90
+ ) -> Self {
91
+ Self {
92
+ status,
93
+ message : message. into ( ) ,
94
+ message_stack : message_stack. into ( ) ,
77
95
}
78
96
}
79
97
}
80
98
81
99
impl std:: fmt:: Display for CobraError {
82
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
83
- match & self . message {
84
- Some ( message) => write ! ( f, "{}: {:?}" , message, self . status) ,
85
- None => write ! ( f, "Cobra error: {:?}" , self . status) ,
100
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
101
+ let mut message_string = String :: new ( ) ;
102
+ message_string. push_str ( & format ! ( "{} with status '{:?}'" , self . message, self . status) ) ;
103
+
104
+ if !self . message_stack . is_empty ( ) {
105
+ message_string. push ( ':' ) ;
106
+ for x in 0 ..self . message_stack . len ( ) {
107
+ message_string. push_str ( & format ! ( " [{}] {}\n " , x, self . message_stack[ x] ) )
108
+ } ;
86
109
}
110
+ write ! ( f, "{}" , message_string)
87
111
}
88
112
}
89
113
@@ -145,44 +169,83 @@ unsafe fn load_library_fn<T>(
145
169
} )
146
170
}
147
171
148
- fn check_fn_call_status ( status : PvStatus , function_name : & str ) -> Result < ( ) , CobraError > {
172
+ fn check_fn_call_status (
173
+ vtable : & CobraInnerVTable ,
174
+ status : PvStatus ,
175
+ function_name : & str
176
+ ) -> Result < ( ) , CobraError > {
149
177
match status {
150
178
PvStatus :: SUCCESS => Ok ( ( ) ) ,
151
- _ => Err ( CobraError :: new (
152
- CobraErrorStatus :: LibraryError ( status) ,
153
- format ! ( "Function '{}' in the cobra library failed" , function_name) ,
154
- ) ) ,
179
+ _ => unsafe {
180
+ let mut message_stack_ptr: * mut c_char = std:: ptr:: null_mut ( ) ;
181
+ let mut message_stack_ptr_ptr = addr_of_mut ! ( message_stack_ptr) ;
182
+
183
+ let mut message_stack_depth: i32 = 0 ;
184
+ ( vtable. pv_get_error_stack ) (
185
+ addr_of_mut ! ( message_stack_ptr_ptr) ,
186
+ addr_of_mut ! ( message_stack_depth) ,
187
+ ) ;
188
+
189
+ let mut message_stack = Vec :: new ( ) ;
190
+ for i in 0 ..message_stack_depth as usize {
191
+ let message = CStr :: from_ptr ( * message_stack_ptr_ptr. add ( i) ) ;
192
+ let message = message. to_string_lossy ( ) . into_owned ( ) ;
193
+ message_stack. push ( message) ;
194
+ }
195
+
196
+ ( vtable. pv_free_error_stack ) ( message_stack_ptr_ptr) ;
197
+
198
+ Err ( CobraError :: new_with_stack (
199
+ CobraErrorStatus :: LibraryError ( status) ,
200
+ format ! ( "'{function_name}' failed" ) ,
201
+ message_stack,
202
+ ) )
203
+ } ,
155
204
}
156
205
}
157
206
158
207
struct CobraInnerVTable {
208
+ pv_cobra_init : RawSymbol < PvCobraInitFn > ,
159
209
pv_cobra_process : RawSymbol < PvCobraProcessFn > ,
160
210
pv_cobra_delete : RawSymbol < PvCobraDeleteFn > ,
211
+ pv_sample_rate : RawSymbol < PvSampleRateFn > ,
212
+ pv_cobra_frame_length : RawSymbol < PvCobraFrameLengthFn > ,
213
+ pv_cobra_version : RawSymbol < PvCobraVersionFn > ,
214
+ pv_get_error_stack : RawSymbol < PvGetErrorStackFn > ,
215
+ pv_free_error_stack : RawSymbol < PvFreeErrorStackFn > ,
216
+ pv_set_sdk : RawSymbol < PvSetSdkFn > ,
161
217
162
218
_lib_guard : Library ,
163
219
}
164
220
165
- struct CobraInner {
166
- ccobra : * mut CCobra ,
167
- frame_length : i32 ,
168
- sample_rate : i32 ,
169
- version : String ,
170
- vtable : CobraInnerVTable ,
171
- }
172
-
173
221
impl CobraInnerVTable {
174
222
pub fn new ( lib : Library ) -> Result < Self , CobraError > {
175
223
unsafe {
176
224
Ok ( Self {
225
+ pv_cobra_init : load_library_fn ( & lib, b"pv_cobra_init" ) ?,
177
226
pv_cobra_process : load_library_fn :: < PvCobraProcessFn > ( & lib, b"pv_cobra_process" ) ?,
178
227
pv_cobra_delete : load_library_fn :: < PvCobraDeleteFn > ( & lib, b"pv_cobra_delete" ) ?,
228
+ pv_sample_rate : load_library_fn ( & lib, b"pv_sample_rate" ) ?,
229
+ pv_cobra_frame_length : load_library_fn ( & lib, b"pv_cobra_frame_length" ) ?,
230
+ pv_cobra_version : load_library_fn ( & lib, b"pv_cobra_version" ) ?,
231
+ pv_get_error_stack : load_library_fn ( & lib, b"pv_get_error_stack" ) ?,
232
+ pv_free_error_stack : load_library_fn ( & lib, b"pv_free_error_stack" ) ?,
233
+ pv_set_sdk : load_library_fn ( & lib, b"pv_set_sdk" ) ?,
179
234
180
235
_lib_guard : lib,
181
236
} )
182
237
}
183
238
}
184
239
}
185
240
241
+ struct CobraInner {
242
+ ccobra : * mut CCobra ,
243
+ frame_length : i32 ,
244
+ sample_rate : i32 ,
245
+ version : String ,
246
+ vtable : CobraInnerVTable ,
247
+ }
248
+
186
249
impl CobraInner {
187
250
pub fn init < S : Into < String > , P : Into < PathBuf > > ( access_key : S , library_path : P ) -> Result < Self , CobraError > {
188
251
@@ -212,6 +275,17 @@ impl CobraInner {
212
275
format ! ( "Failed to load cobra dynamic library: {}" , err) ,
213
276
)
214
277
} ) ?;
278
+ let vtable = CobraInnerVTable :: new ( lib) ?;
279
+
280
+ let sdk_string = match CString :: new ( "rust" ) {
281
+ Ok ( sdk_string) => sdk_string,
282
+ Err ( err) => {
283
+ return Err ( CobraError :: new (
284
+ CobraErrorStatus :: ArgumentError ,
285
+ format ! ( "sdk_string is not a valid C string {err}" ) ,
286
+ ) )
287
+ }
288
+ } ;
215
289
216
290
let pv_access_key = CString :: new ( access_key) . map_err ( |err| {
217
291
CobraError :: new (
@@ -220,37 +294,27 @@ impl CobraInner {
220
294
)
221
295
} ) ?;
222
296
223
- let ( ccobra, sample_rate, frame_length, version) = unsafe {
224
- let pv_cobra_init = load_library_fn :: < PvCobraInitFn > ( & lib, b"pv_cobra_init" ) ?;
225
- let pv_cobra_version = load_library_fn :: < PvCobraVersionFn > ( & lib, b"pv_cobra_version" ) ?;
226
- let pv_sample_rate = load_library_fn :: < PvSampleRateFn > ( & lib, b"pv_sample_rate" ) ?;
227
- let pv_cobra_frame_length =
228
- load_library_fn :: < PvCobraFrameLengthFn > ( & lib, b"pv_cobra_frame_length" ) ?;
297
+ let mut ccobra = std:: ptr:: null_mut ( ) ;
229
298
230
- let mut ccobra = std:: ptr:: null_mut ( ) ;
299
+ // SAFETY: most of the unsafe comes from the `load_library_fn` which is
300
+ // safe, because we don't use the raw symbols after this function
301
+ // anymore.
302
+ let ( sample_rate, frame_length, version) = unsafe {
303
+ ( vtable. pv_set_sdk ) ( sdk_string. as_ptr ( ) ) ;
231
304
232
- check_fn_call_status (
233
- pv_cobra_init (
234
- pv_access_key. as_ptr ( ) ,
235
- addr_of_mut ! ( ccobra) ,
236
- ) ,
237
- "pv_cobra_init" ,
238
- ) ?;
239
-
240
- let version = match CStr :: from_ptr ( pv_cobra_version ( ) ) . to_str ( ) {
241
- Ok ( string) => string. to_string ( ) ,
242
- Err ( err) => {
243
- return Err ( CobraError :: new (
244
- CobraErrorStatus :: LibraryLoadError ,
245
- format ! ( "Failed to get version info from Cobra Library: {}" , err) ,
246
- ) )
247
- }
248
- } ;
305
+ let status = ( vtable. pv_cobra_init ) (
306
+ pv_access_key. as_ptr ( ) ,
307
+ addr_of_mut ! ( ccobra) ,
308
+ ) ;
309
+ check_fn_call_status ( & vtable, status, "pv_cobra_init" ) ?;
310
+
311
+ let version = CStr :: from_ptr ( ( vtable. pv_cobra_version ) ( ) )
312
+ . to_string_lossy ( )
313
+ . into_owned ( ) ;
249
314
250
315
(
251
- ccobra,
252
- pv_sample_rate ( ) ,
253
- pv_cobra_frame_length ( ) ,
316
+ ( vtable. pv_sample_rate ) ( ) ,
317
+ ( vtable. pv_cobra_frame_length ) ( ) ,
254
318
version,
255
319
)
256
320
} ;
@@ -260,7 +324,7 @@ impl CobraInner {
260
324
sample_rate,
261
325
frame_length,
262
326
version,
263
- vtable : CobraInnerVTable :: new ( lib ) ? ,
327
+ vtable,
264
328
} )
265
329
}
266
330
@@ -280,7 +344,7 @@ impl CobraInner {
280
344
let status = unsafe {
281
345
( self . vtable . pv_cobra_process ) ( self . ccobra , pcm. as_ptr ( ) , addr_of_mut ! ( result) )
282
346
} ;
283
- check_fn_call_status ( status, "pv_cobra_process" ) ?;
347
+ check_fn_call_status ( & self . vtable , status, "pv_cobra_process" ) ?;
284
348
285
349
Ok ( result)
286
350
}
0 commit comments