forked from Picovoice/porcupine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPorcupine.cs
182 lines (154 loc) · 7.98 KB
/
Porcupine.cs
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace PorcupineCS
{
// ReSharper disable UnusedMember.Global
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
public enum PicoVoiceStatus
{
SUCCESS = 0,
OUTOFMEMORY = 1,
IOERROR = 2,
INVALIDARGUMENT = 3
}
// ReSharper restore InconsistentNaming
// ReSharper restore IdentifierTypo
// ReSharper restore UnusedMember.Global
public class Porcupine
{
public PicoVoiceStatus Status { get; private set; }
private const string LIBRARY_NAME = "libpv_porcupine";
private IntPtr _libraryPointer;
private static readonly string _extension = $"{GetExtension()}";
#region PINVOKE
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern PicoVoiceStatus pv_porcupine_init(string modelFilepath, string keywordsFilePath, float sensitivity, out IntPtr pointer);
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern PicoVoiceStatus pv_porcupine_multiple_keywords_init(string modelFilepath, int numberOfKeywords, string[] keywordsFilePaths, float[] sensitivities, out IntPtr pointer);
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int pv_sample_rate();
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void pv_porcupine_delete(IntPtr pointer);
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern PicoVoiceStatus pv_porcupine_process(IntPtr pointer, short[] voiceData, out bool results);
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern PicoVoiceStatus pv_porcupine_multiple_keywords_process(IntPtr pointer, short[] voiceData, out int keywordIndex);
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern string pv_porcupine_version();
[DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int pv_porcupine_frame_length();
#endregion
/// <summary>
/// Loads Porcupine's shared library and creates an instance of wake word detection object.
/// </summary>
/// <param name="libraryPath">Absolute path to Porcupine's shared library.</param>
/// <param name="modelFilePath">Absolute path to file containing model parameters.</param>
/// <param name="keywordFilePath">Absolute path to keyword file containing hyper-parameters. If not present then 'keyword_file_paths' will be used.</param>
/// <param name="sensitivity">Sensitivity parameter. A higher sensitivity value lowers miss rate at the cost of increased false alarm rate.
/// For more information regarding this parameter refer to 'include/pv_porcupine.h'.
/// If not present then 'sensitivities' is used.</param>
/// <param name="keywordFilePaths"> List of absolute paths to keyword files. Intended to be used for multiple keyword scenario.
/// This parameter is used only when 'keyword_file_path' is not set.</param>
/// <param name="sensitivities"> List of sensitivity parameters. Intended to be used for multiple keyword scenario.
/// This parameter is used only when 'sensitivity' is not set.</param>
public Porcupine(string modelFilePath, string keywordFilePath = null,
float? sensitivity = null, IEnumerable<string> keywordFilePaths = null,
IEnumerable<float> sensitivities = null)
{
if(!File.Exists(LIBRARY_NAME + _extension))
throw new Exception($"the {LIBRARY_NAME} cannot be found.\nThis should be in the same folder as this or on a known path.");
if (keywordFilePath == null)
{
if(keywordFilePaths == null)
throw new ArgumentNullException(nameof(keywordFilePaths));
if(sensitivities == null)
throw new ArgumentNullException(nameof(sensitivities));
Status = pv_porcupine_multiple_keywords_init(modelFilePath, keywordFilePaths.Count(), keywordFilePaths.ToArray(), sensitivities.ToArray(), out _libraryPointer);
}
else
{
if (sensitivity == null)
throw new ArgumentNullException(nameof(sensitivity));
Status = pv_porcupine_init(modelFilePath, keywordFilePath, sensitivity.Value, out _libraryPointer);
}
}
/// <summary>
/// Monitors incoming audio stream for a given keyword.
/// </summary>
/// <param name="data">A frame of audio samples. The number of samples per frame can be attained by calling 'pv_porcupine_frame_length()'.
/// The incoming audio needs to have a sample rate equal to 'pv_sample_rate()' and be 16-bit linearly-encoded.
/// Furthermore, porcupine operates on single channel audio.</param>
/// <param name="result">result Flag indicating if the keyword has been observed ending at the current frame.</param>
/// <returns>Status code. Returns 'PV_STATUS_INVALID_ARGUMENT' on failure.</returns>
public PicoVoiceStatus Process(Int16[] data, out bool result)
{
return pv_porcupine_process(_libraryPointer, data, out result);
}
/// <summary>
/// Monitors incoming audio stream for multiple keywords.
/// </summary>
/// <param name="data">A frame of audio samples. For more information about required audio properties refer to documentation of '<seealso cref="Process"/>'.</param>
/// <param name="index">Index of observed keyword at the end of current frame.
/// Indexing is 0-based and based on the ordering of 'keyword_file_paths' passed to 'pv_porcupine_multiple_keywords_init()'.
/// If no keyword is detected it is set to -1.</param>
/// <returns>Status code. Returns 'PV_STATUS_INVALID_ARGUMENT' on failure.</returns>
public PicoVoiceStatus ProcessMultipleKeywords(Int16[] data, out int index)
{
return pv_porcupine_multiple_keywords_process(_libraryPointer, data, out index);
}
/// <summary>
/// Audio sample rate accepted by Picovoice.
/// </summary>
/// <returns></returns>
public int SampleRate()
{
return pv_sample_rate();
}
/// <summary>
/// Getter for length (number of audio samples) per frame.
/// </summary>
/// <returns></returns>
public int FrameLength()
{
return pv_porcupine_frame_length();
}
/// <summary>
/// Getter for the version string of the library
/// </summary>
/// <returns></returns>
public string GetVersion()
{
return pv_porcupine_version();
}
private static string GetExtension()
{
PlatformID platform = Environment.OSVersion.Platform;
if (platform == PlatformID.MacOSX)
{
return ".dylib";
}
if (platform == PlatformID.Unix)
{
return ".so";
}
if (platform == PlatformID.Win32NT)
{
return ".dll";
}
throw new NotImplementedException("this OS has no binding logic implemented yet.");
}
~Porcupine()
{
pv_porcupine_delete(_libraryPointer);
}
public void Dispose()
{
pv_porcupine_delete(_libraryPointer);
_libraryPointer = IntPtr.Zero;
}
}
}