porcupine_sys/
lib.rs

1//! # porcupine-sys
2//!
3//! A Rust binding for Porcupine
4
5// libc
6extern crate libc;
7use libc::c_int;
8
9mod c;
10
11// std
12use std::ffi::{CStr, CString};
13use std::fmt::{self, Display};
14use std::ptr;
15
16/// The status code returned by various porcupine functions.
17#[derive(Debug)]
18pub enum Status {
19    Success = 0,
20    OutOfMemory = 1,
21    IOError = 2,
22    InvalidArgument = 3,
23    Unknown = 4,
24}
25
26/// Audio sample rate accepted by Picovoice.
27pub unsafe fn sample_rate() -> usize {
28    c::pv_sample_rate() as usize
29}
30
31/// Version of Porcupine.
32pub unsafe fn version() -> &'static str {
33    CStr::from_ptr(c::pv_porcupine_version()).to_str().unwrap()
34}
35
36/// Length (number of audio samples) per frame.
37pub unsafe fn frame_length() -> usize {
38    c::pv_porcupine_frame_length() as usize
39}
40
41pub struct Object {
42    _object: *mut c::pv_porcupine_object_t,
43}
44
45unsafe impl Send for Object {}
46
47impl Object {
48    /// Creates a new Porcupine object.
49    pub unsafe fn new(
50        model_file_path: &str,
51        keyword_file_path: &str,
52        sensitivity: f32,
53    ) -> Result<Self, Status> {
54        let mut _object: *mut c::pv_porcupine_object_t = ptr::null_mut();
55        let _model_file_path = CString::new(model_file_path).unwrap().into_raw();
56        let _keyword_file_path = CString::new(keyword_file_path).unwrap().into_raw();
57
58        let status = c::pv_porcupine_init(
59            _model_file_path,
60            _keyword_file_path,
61            sensitivity,
62            &mut _object,
63        );
64        if status != 0 {
65            return Err(status.into());
66        }
67
68        Ok(Object { _object })
69    }
70
71    /// Creates a new Porcupine object that is capable of detecting multiple keywords.
72    pub unsafe fn new_multiple_keywords(
73        model_file_path: &str,
74        keyword_file_paths: &[&str],
75        sensitivities: &[f32],
76    ) -> Result<Self, Status> {
77        let mut _object: *mut c::pv_porcupine_object_t = ptr::null_mut();
78        let _model_file_path = CString::new(model_file_path).unwrap().into_raw();
79        let _number_keywords = keyword_file_paths.len() as c_int;
80        let _keyword_file_paths: Vec<CString> = keyword_file_paths
81            .iter()
82            .map(|p| CString::new(*p).unwrap())
83            .collect();
84        let _keyword_file_paths: Vec<_> = _keyword_file_paths.iter().map(|p| p.as_ptr()).collect();
85
86        let status = c::pv_porcupine_multiple_keywords_init(
87            _model_file_path,
88            _number_keywords,
89            _keyword_file_paths.as_ptr() as *const *const i8,
90            sensitivities.as_ptr(),
91            &mut _object,
92        );
93        if status != 0 {
94            return Err(status.into());
95        }
96
97        Ok(Object { _object })
98    }
99
100    /// Delete the Porcupine object.
101    pub unsafe fn delete(&mut self) {
102        c::pv_porcupine_delete(self._object);
103    }
104
105    /// Detect keyword within the provided audio data. The data must be 16-bit linearly-encoded and single-channel with sample rate equal to `sample_rate()`.
106    pub unsafe fn process(&self, pcm: &[i16]) -> Result<bool, Status> {
107        let mut detected = false;
108
109        let status = c::pv_porcupine_process(self._object, pcm.as_ptr(), &mut detected);
110        if status != 0 {
111            return Err(status.into());
112        }
113
114        Ok(detected)
115    }
116
117    /// Detect one of the keywords within the provided audio data. The data must be 16-bit linearly-encoded and single-channel with sample rate equal to `sample_rate()`. It returns the index of the detected keyword if successful.
118    pub unsafe fn process_multiple_keywords(&self, pcm: &[i16]) -> Result<isize, Status> {
119        let mut keyword_index: c_int = -1;
120
121        let status = c::pv_porcupine_multiple_keywords_process(
122            self._object,
123            pcm.as_ptr(),
124            &mut keyword_index,
125        );
126        if status != 0 {
127            return Err(status.into());
128        }
129
130        Ok(keyword_index as isize)
131    }
132}
133
134impl From<c::pv_status_t> for Status {
135    fn from(status: c::pv_status_t) -> Self {
136        match status {
137            0 => Status::Success,
138            1 => Status::OutOfMemory,
139            2 => Status::IOError,
140            3 => Status::InvalidArgument,
141            _ => Status::Unknown,
142        }
143    }
144}
145
146impl Display for Status {
147    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148        match self {
149            Status::Success => write!(f, "Success"),
150            Status::OutOfMemory => write!(f, "Out Of Memory"),
151            Status::IOError => write!(f, "I/O Error"),
152            Status::InvalidArgument => write!(f, "Invalid Argument"),
153            _ => write!(f, "Unknown"),
154        }
155    }
156}