tinykw 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tinykw/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ from .wrapper import (
2
+ tkw_init,
3
+ tkw_add_keyword,
4
+ tkw_process_frame,
5
+ tkw_is_keyword_detected,
6
+ tkw_clear_keyword_flag,
7
+ AUDIO_FRAME_SIZE,
8
+ )
9
+
10
+ __all__ = [
11
+ "tkw_init",
12
+ "tkw_add_keyword",
13
+ "tkw_process_frame",
14
+ "tkw_is_keyword_detected",
15
+ "tkw_clear_keyword_flag",
16
+ "AUDIO_FRAME_SIZE"
17
+ ]
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
tinykw/wrapper.py ADDED
@@ -0,0 +1,160 @@
1
+ import ctypes
2
+ import platform
3
+ from pathlib import Path
4
+
5
+ def _load_library():
6
+ system = platform.system()
7
+ machine = platform.machine()
8
+ arch = {
9
+ "x86_64": "x86_64",
10
+ "AMD64": "x86_64",
11
+ "arm64": "arm64",
12
+ "aarch64": "arm64",
13
+ }.get(machine)
14
+
15
+ if arch is None:
16
+ raise OSError(f"Unsupported architecture: {machine}")
17
+
18
+ lib_dir = Path(__file__).parent / "libs"
19
+ if system == "Darwin":
20
+ lib_path = lib_dir / f"macos-{arch}" / "libtinykw.dylib"
21
+ elif system == "Linux":
22
+ lib_path = lib_dir / f"linux-{arch}" / "libtinykw.so"
23
+ elif system == "Windows":
24
+ lib_path = lib_dir / f"windows-{arch}" / "libtinykw.dll"
25
+ else:
26
+ raise OSError(f"Unsupported platform: {system}")
27
+
28
+ if not lib_path.exists():
29
+ raise OSError(f"No native library found for {system}/{arch}.")
30
+
31
+ return ctypes.CDLL(str(lib_path))
32
+
33
+ _lib = _load_library()
34
+
35
+ _lib.tkw_init.restype = ctypes.c_int32
36
+ _lib.tkw_init.argtypes = []
37
+
38
+ _lib.tkw_add_keyword.restype = ctypes.c_int32
39
+ _lib.tkw_add_keyword.argtypes = [ctypes.POINTER(ctypes.c_uint8),
40
+ ctypes.c_uint8,
41
+ ctypes.POINTER(ctypes.c_uint32)]
42
+
43
+ _lib.tkw_process_frame.restype = ctypes.c_int32
44
+ _lib.tkw_process_frame.argtypes = [ctypes.POINTER(ctypes.c_int16),
45
+ ctypes.c_size_t]
46
+
47
+ _lib.tkw_is_keyword_detected.restype = ctypes.c_int32
48
+ _lib.tkw_is_keyword_detected.argtypes = [ctypes.c_uint32,
49
+ ctypes.POINTER(ctypes.c_uint32)]
50
+
51
+ _lib.tkw_clear_keyword_flag.restype = ctypes.c_int32
52
+ _lib.tkw_clear_keyword_flag.argtypes = [ctypes.c_uint32]
53
+
54
+ _lib.tkw_status_string.restype = ctypes.c_char_p
55
+ _lib.tkw_status_string.argtypes = [ctypes.c_int32]
56
+
57
+ AUDIO_FRAME_SIZE = 480
58
+
59
+ class tkwError(RuntimeError):
60
+ pass
61
+
62
+ def _check_status(status: int):
63
+ if status != 0:
64
+ msg = _lib.tkw_status_string(status)
65
+ msg = msg.decode() if msg else f"Error code {status}"
66
+ raise tkwError(msg)
67
+
68
+ def tkw_init() -> None:
69
+ """
70
+ Initialize or reset the library.
71
+
72
+ This should be called before any other API function. Calling tkw_init() multiple times is allowed
73
+ and resets all internal state, including registered keywords and detection flags.
74
+ """
75
+ status = _lib.tkw_init()
76
+ _check_status(status)
77
+
78
+
79
+ def tkw_add_keyword(data: list[int] | bytes, detection_threshold: float = 0.5) -> int:
80
+ """
81
+ Adds a keyword to the engine.
82
+
83
+ Args:
84
+ data: 48 bytes keyword encoding (can be obtained from https://voyelle.io/tinykw).
85
+ detection_threshold: value between 0 and 1.
86
+ Controls how strict the engine is when deciding whether a detection is valid.
87
+ A high value makes the engine more conservative: only high-scoring detections are accepted (fewer false positives, but more missed detections).
88
+ A low value makes the engine more permissive: more detections are accepted (fewer misses, but more false positives).
89
+ This parameter may need tuning depending on the keyword, use case and environment. A good starting point is to set it mid-range at 0.5, and
90
+ then eventually adjust it based on your tolerance for false positives vs missed detections.
91
+
92
+ Returns:
93
+ id: the assigned keyword ID
94
+
95
+ Example:
96
+ data = [0x3f, 0x12, 0x00, ...] # 48 bytes
97
+ id = tkw_add_keyword(data, 0.75)
98
+ """
99
+ if isinstance(data, bytes):
100
+ data = list(data)
101
+ if len(data) != 48:
102
+ raise ValueError(f"data must be exactly 48 bytes, got {len(data)}")
103
+ if not (0.0 <= detection_threshold <= 1.0):
104
+ raise ValueError("detection_threshold must be in [0.0, 1.0]")
105
+ detection_threshold = max(0, min(255, round(detection_threshold * 256)))
106
+ c_data = (ctypes.c_uint8 * 48)(*data)
107
+ out_id = ctypes.c_uint32()
108
+ status = _lib.tkw_add_keyword(c_data,
109
+ ctypes.c_uint8(detection_threshold),
110
+ ctypes.byref(out_id))
111
+ _check_status(status)
112
+ return out_id.value
113
+
114
+
115
+ def tkw_process_frame(waveform) -> None:
116
+ """
117
+ Processes one frame of 16-bit PCM audio sampled at 16 kHz.
118
+
119
+ Args:
120
+ waveform: int16 numpy array or list of length tinykw.AUDIO_FRAME_SIZE.
121
+ """
122
+ if len(waveform) != AUDIO_FRAME_SIZE:
123
+ raise ValueError(
124
+ f"waveform must have length {AUDIO_FRAME_SIZE}, got {len(waveform)}"
125
+ )
126
+ if hasattr(waveform, "ctypes"):
127
+ ptr = waveform.ctypes.data_as(ctypes.POINTER(ctypes.c_int16))
128
+ else:
129
+ arr = (ctypes.c_int16 * len(waveform))(*waveform)
130
+ ptr = arr
131
+ status = _lib.tkw_process_frame(ptr, len(waveform))
132
+ _check_status(status)
133
+
134
+
135
+ def tkw_is_keyword_detected(keyword_id: int) -> bool:
136
+ """
137
+ Reads the detection flag for a specific keyword.
138
+
139
+ Args:
140
+ id: keyword assigned ID.
141
+
142
+ Returns:
143
+ True if detected, False otherwise.
144
+ Detection remains set until cleared with tkw_clear_keyword_flag().
145
+ """
146
+ out = ctypes.c_uint32()
147
+ status = _lib.tkw_is_keyword_detected(ctypes.c_uint32(keyword_id), ctypes.byref(out))
148
+ _check_status(status)
149
+ return bool(out.value)
150
+
151
+
152
+ def tkw_clear_keyword_flag(keyword_id: int) -> None:
153
+ """
154
+ Clear detection flag.
155
+
156
+ Args:
157
+ id: keyword id
158
+ """
159
+ status = _lib.tkw_clear_keyword_flag(ctypes.c_uint32(keyword_id))
160
+ _check_status(status)
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinykw
3
+ Version: 1.0.0
4
+ Summary: Python binding for TinyKW
5
+ Project-URL: Homepage, https://voyelle.io/tinykw
6
+ Project-URL: Repository, https://github.com/voyelle-io/tinykw.git
7
+ Keywords: keyword spotting,wake word
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Provides-Extra: dev
12
+ Requires-Dist: numpy; extra == "dev"
13
+ Requires-Dist: scipy; extra == "dev"
14
+ Requires-Dist: pytest; extra == "dev"
15
+ Dynamic: license-file
16
+
17
+ Python binding for TinyKW.
18
+
19
+ ## Installation
20
+
21
+ `pip install tinykw`
22
+
23
+ ## Usage
24
+
25
+ ```python
26
+ import tinykw
27
+
28
+ tinykw.tkw_init()
29
+
30
+ kw_id = tinykw.tkw_add_keyword(keyword_bytes, detection_threshold)
31
+ # ...
32
+ tinykw.tkw_process_frame(samples)
33
+ # ...
34
+ if tinykw.tkw_is_keyword_detected(kw_id):
35
+ tinykw.tkw_clear_keyword_flag(kw_id)
36
+ ```
37
+
38
+ See [examples/python](../examples/python/) for full usage.
39
+
40
+ ## License
41
+
42
+ TinyKW is free for non-commercial use, including research, education, prototyping, and evaluation.
43
+ Please see [LICENSE](LICENSE) for details. For commercial enquiries, please contact [contact@voyelle.io](mailto:contact@voyelle.io).
@@ -0,0 +1,13 @@
1
+ tinykw/__init__.py,sha256=g0FqZFzbzrH9hxScfGhcbOxX0VdUCP-mvpYGd21Gdv0,324
2
+ tinykw/wrapper.py,sha256=j6nAJEXNsg3d5yWAHyleN2PiJ1szJkSQ89Geu_52NTg,5462
3
+ tinykw/libs/linux-arm64/libtinykw.so,sha256=LCaP360L8dQgtRaaKmvDgi6wplBUKUwnTWMPP9Fy9n8,317648
4
+ tinykw/libs/linux-x86_64/libtinykw.so,sha256=Nd__IvAJUGpRKksI215YOcuWFpXTZQCf4bPkQRp_QjQ,320960
5
+ tinykw/libs/macos-arm64/libtinykw.dylib,sha256=CHTl5oMmw_p6jMLc2r0pdfjGre9Z_1k2tQFCxIC_DiY,362192
6
+ tinykw/libs/macos-x86_64/libtinykw.dylib,sha256=fU7W6ZoK7jeWA9_UwHjBSb7HUaifDAPi2bWbKTRDHhw,332752
7
+ tinykw/libs/windows-arm64/libtinykw.dll,sha256=H6p-88X6cC3uAiL2w0fWGygzleoQopi5IpDbaIPlS1w,326144
8
+ tinykw/libs/windows-x86_64/libtinykw.dll,sha256=CcDElE8NXxw3YNedr6CjRirCXD8UDg3PpjqmNGk-LGA,333824
9
+ tinykw-1.0.0.dist-info/licenses/LICENSE,sha256=7TQoAJLKq41XnHkEUzq-U4nuBdjTxR6d-I67YwT9dcA,1499
10
+ tinykw-1.0.0.dist-info/METADATA,sha256=WXk9FZ9iGnUTcZ8l0AkHwAXUd9Bkv8yLe87uqBdKSdo,1105
11
+ tinykw-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ tinykw-1.0.0.dist-info/top_level.txt,sha256=5-pvJSN5r_PM_tAxf0v6m24ZAJlEufMinzHYJbjbLUQ,7
13
+ tinykw-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2026 TinyKW
2
+
3
+ Permission is hereby granted, free of charge, to any person or organization
4
+ obtaining a copy of this software and associated documentation files
5
+ (the "Software") to use, reproduce, and distribute the Software in binary form
6
+ for non-commercial purposes only, including internal evaluation,
7
+ research, prototyping, and educational use, provided that this license notice is
8
+ retained in all copies or distributions.
9
+
10
+ Commercial use of the Software is prohibited without a separate written
11
+ commercial license from the copyright holder. For purposes of this license,
12
+ "commercial use" means use of the Software in or for a product, service, or
13
+ activity that generates revenue or other commercial benefit, including use
14
+ in products or services that are sold, licensed, hosted, subscription-based,
15
+ or monetized in any form.
16
+
17
+ You may not:
18
+ - reverse engineer, decompile, or disassemble the Software except where
19
+ such restriction is prohibited by applicable law;
20
+ - remove or alter copyright or license notices.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL
25
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
27
+ FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ tinykw