1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! # porcupine-sys
//!
//! A Rust binding for Porcupine

// libc
extern crate libc;
use libc::c_int;

mod c;

// std
use std::ffi::{CStr, CString};
use std::fmt::{self, Display};
use std::ptr;

/// The status code returned by various porcupine functions.
#[derive(Debug)]
pub enum Status {
    Success = 0,
    OutOfMemory = 1,
    IOError = 2,
    InvalidArgument = 3,
    Unknown = 4,
}

/// Audio sample rate accepted by Picovoice.
pub unsafe fn sample_rate() -> usize {
    c::pv_sample_rate() as usize
}

/// Version of Porcupine.
pub unsafe fn version() -> &'static str {
    CStr::from_ptr(c::pv_porcupine_version()).to_str().unwrap()
}

/// Length (number of audio samples) per frame.
pub unsafe fn frame_length() -> usize {
    c::pv_porcupine_frame_length() as usize
}

pub struct Object {
    _object: *mut c::pv_porcupine_object_t,
}

unsafe impl Send for Object {}

impl Object {
    /// Creates a new Porcupine object.
    pub unsafe fn new(
        model_file_path: &str,
        keyword_file_path: &str,
        sensitivity: f32,
    ) -> Result<Self, Status> {
        let mut _object: *mut c::pv_porcupine_object_t = ptr::null_mut();
        let _model_file_path = CString::new(model_file_path).unwrap().into_raw();
        let _keyword_file_path = CString::new(keyword_file_path).unwrap().into_raw();

        let status = c::pv_porcupine_init(
            _model_file_path,
            _keyword_file_path,
            sensitivity,
            &mut _object,
        );
        if status != 0 {
            return Err(status.into());
        }

        Ok(Object { _object })
    }

    /// Creates a new Porcupine object that is capable of detecting multiple keywords.
    pub unsafe fn new_multiple_keywords(
        model_file_path: &str,
        keyword_file_paths: &[&str],
        sensitivities: &[f32],
    ) -> Result<Self, Status> {
        let mut _object: *mut c::pv_porcupine_object_t = ptr::null_mut();
        let _model_file_path = CString::new(model_file_path).unwrap().into_raw();
        let _number_keywords = keyword_file_paths.len() as c_int;
        let _keyword_file_paths: Vec<CString> = keyword_file_paths
            .iter()
            .map(|p| CString::new(*p).unwrap())
            .collect();
        let _keyword_file_paths: Vec<_> = _keyword_file_paths.iter().map(|p| p.as_ptr()).collect();

        let status = c::pv_porcupine_multiple_keywords_init(
            _model_file_path,
            _number_keywords,
            _keyword_file_paths.as_ptr() as *const *const i8,
            sensitivities.as_ptr(),
            &mut _object,
        );
        if status != 0 {
            return Err(status.into());
        }

        Ok(Object { _object })
    }

    /// Delete the Porcupine object.
    pub unsafe fn delete(&mut self) {
        c::pv_porcupine_delete(self._object);
    }

    /// 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()`.
    pub unsafe fn process(&self, pcm: &[i16]) -> Result<bool, Status> {
        let mut detected = false;

        let status = c::pv_porcupine_process(self._object, pcm.as_ptr(), &mut detected);
        if status != 0 {
            return Err(status.into());
        }

        Ok(detected)
    }

    /// 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.
    pub unsafe fn process_multiple_keywords(&self, pcm: &[i16]) -> Result<isize, Status> {
        let mut keyword_index: c_int = -1;

        let status = c::pv_porcupine_multiple_keywords_process(
            self._object,
            pcm.as_ptr(),
            &mut keyword_index,
        );
        if status != 0 {
            return Err(status.into());
        }

        Ok(keyword_index as isize)
    }
}

impl From<c::pv_status_t> for Status {
    fn from(status: c::pv_status_t) -> Self {
        match status {
            0 => Status::Success,
            1 => Status::OutOfMemory,
            2 => Status::IOError,
            3 => Status::InvalidArgument,
            _ => Status::Unknown,
        }
    }
}

impl Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Status::Success => write!(f, "Success"),
            Status::OutOfMemory => write!(f, "Out Of Memory"),
            Status::IOError => write!(f, "I/O Error"),
            Status::InvalidArgument => write!(f, "Invalid Argument"),
            _ => write!(f, "Unknown"),
        }
    }
}