silk-python 0.2.7__cp313-cp313-win32.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.
- pysilk/__init__.py +22 -0
- pysilk/backends/__init__.py +3 -0
- pysilk/backends/cffi/__init__.py +264 -0
- pysilk/backends/cffi/_silk.pyd +0 -0
- pysilk/backends/cffi/build.py +97 -0
- pysilk/backends/cython/__init__.py +4 -0
- pysilk/backends/cython/_silk.c +13503 -0
- pysilk/backends/cython/_silk.pyd +0 -0
- pysilk/backends/cython/_silk.pyi +27 -0
- pysilk/backends/cython/_silk.pyx +275 -0
- pysilk/backends/cython/silk.pxd +72 -0
- silk_python-0.2.7.dist-info/METADATA +108 -0
- silk_python-0.2.7.dist-info/RECORD +15 -0
- silk_python-0.2.7.dist-info/WHEEL +5 -0
- silk_python-0.2.7.dist-info/top_level.txt +1 -0
pysilk/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
impl = platform.python_implementation()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _should_use_cffi() -> bool:
|
|
8
|
+
ev = os.getenv("SILK_USE_CFFI")
|
|
9
|
+
if ev is not None:
|
|
10
|
+
return True
|
|
11
|
+
if impl == "CPython":
|
|
12
|
+
return False
|
|
13
|
+
else:
|
|
14
|
+
return True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if not _should_use_cffi():
|
|
18
|
+
from pysilk.backends.cython import *
|
|
19
|
+
else:
|
|
20
|
+
from pysilk.backends.cffi import *
|
|
21
|
+
|
|
22
|
+
__version__ = "0.2.7"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
from typing import IO
|
|
3
|
+
|
|
4
|
+
from pysilk.backends.cffi._silk import ffi, lib
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SilkError(Exception):
|
|
9
|
+
def __init__(self, code):
|
|
10
|
+
self.code = code
|
|
11
|
+
|
|
12
|
+
def __str__(self):
|
|
13
|
+
if isinstance(self.code, int):
|
|
14
|
+
if self.code == -1:
|
|
15
|
+
return "ENC_INPUT_INVALID_NO_OF_SAMPLES"
|
|
16
|
+
elif self.code == -2:
|
|
17
|
+
return "ENC_FS_NOT_SUPPORTED"
|
|
18
|
+
elif self.code == -3:
|
|
19
|
+
return "ENC_PACKET_SIZE_NOT_SUPPORTED"
|
|
20
|
+
elif self.code == -4:
|
|
21
|
+
return "ENC_PAYLOAD_BUF_TOO_SHORT"
|
|
22
|
+
elif self.code == -5:
|
|
23
|
+
return "ENC_INVALID_LOSS_RATE"
|
|
24
|
+
elif self.code == -6:
|
|
25
|
+
return "ENC_INVALID_COMPLEXITY_SETTING"
|
|
26
|
+
elif self.code == -7:
|
|
27
|
+
return "ENC_INVALID_INBAND_FEC_SETTING"
|
|
28
|
+
elif self.code == -8:
|
|
29
|
+
return "ENC_INVALID_DTX_SETTING"
|
|
30
|
+
elif self.code == -9:
|
|
31
|
+
return "ENC_INTERNAL_ERROR"
|
|
32
|
+
elif self.code == -10:
|
|
33
|
+
return "DEC_INVALID_SAMPLING_FREQUENCY"
|
|
34
|
+
elif self.code == -11:
|
|
35
|
+
return "DEC_PAYLOAD_TOO_LARGE"
|
|
36
|
+
elif self.code == -12:
|
|
37
|
+
return "DEC_PAYLOAD_ERROR"
|
|
38
|
+
else:
|
|
39
|
+
return "Other error"
|
|
40
|
+
else:
|
|
41
|
+
return str(self.code)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def i16_to_bytes(data: int) -> bytes:
|
|
45
|
+
return struct.pack("=h", data)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def bytes_to_i16(data: bytes) -> int:
|
|
49
|
+
return struct.unpack("=h", data)[0]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def write_i16_le(output: IO, data: int):
|
|
53
|
+
if lib.SHOULD_SWAP():
|
|
54
|
+
data = lib.swap_i16(data)
|
|
55
|
+
output.write(i16_to_bytes(data))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def read_i16_le(input: IO) -> int:
|
|
59
|
+
chunk = input.read(2) # type: bytes
|
|
60
|
+
data: int = bytes_to_i16(chunk)
|
|
61
|
+
if lib.SHOULD_SWAP():
|
|
62
|
+
data = lib.swap_i16(data)
|
|
63
|
+
return data
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def check_file(file) -> bool:
|
|
67
|
+
if hasattr(file, "read") and hasattr(file, "write"):
|
|
68
|
+
return True
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def encode(
|
|
73
|
+
input: IO,
|
|
74
|
+
output: IO,
|
|
75
|
+
sample_rate: int,
|
|
76
|
+
bit_rate: int,
|
|
77
|
+
max_internal_sample_rate: int = 24000,
|
|
78
|
+
packet_loss_percentage: int = 0,
|
|
79
|
+
complexity: int = 2,
|
|
80
|
+
use_inband_fec: bool = False,
|
|
81
|
+
use_dtx: bool = False,
|
|
82
|
+
tencent: bool = True,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""encode(input: BinaryIO, output: BinaryIO, sample_rate: int, bit_rate: int, max_internal_sample_rate: int = 24000, packet_loss_percentage: int = 0, complexity: int = 2, use_inband_fec: bool = False, use_dtx: bool = False, tencent: bool = True) -> bytes
|
|
85
|
+
encode pcm to silk
|
|
86
|
+
:param input: BytesIO or an openfile with "rb" mode
|
|
87
|
+
:param output: BytesIO or an openfile with "wb" mode
|
|
88
|
+
:param sample_rate:
|
|
89
|
+
:param bit_rate:
|
|
90
|
+
:param max_internal_sample_rate:
|
|
91
|
+
:param packet_loss_percentage:
|
|
92
|
+
:param complexity:
|
|
93
|
+
:param use_inband_fec:
|
|
94
|
+
:param use_dtx:
|
|
95
|
+
:param tencent: Tencent's special tag
|
|
96
|
+
:return: None
|
|
97
|
+
"""
|
|
98
|
+
if not check_file(input):
|
|
99
|
+
raise TypeError(
|
|
100
|
+
"input except a file-like object, got %s" % type(input).__name__
|
|
101
|
+
)
|
|
102
|
+
if not check_file(output):
|
|
103
|
+
raise TypeError(
|
|
104
|
+
"output except a file-like object, got %s" % type(output).__name__
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
enc_control = ffi.new("SKP_SILK_SDK_EncControlStruct *")
|
|
108
|
+
enc_control.API_sampleRate = sample_rate
|
|
109
|
+
enc_control.maxInternalSampleRate = max_internal_sample_rate
|
|
110
|
+
enc_control.packetSize = (20 * sample_rate) // 1000
|
|
111
|
+
enc_control.bitRate = bit_rate
|
|
112
|
+
enc_control.packetLossPercentage = packet_loss_percentage
|
|
113
|
+
enc_control.complexity = complexity
|
|
114
|
+
enc_control.useInBandFEC = use_inband_fec
|
|
115
|
+
enc_control.useDTX = use_dtx
|
|
116
|
+
|
|
117
|
+
enc_status = ffi.new("SKP_SILK_SDK_EncControlStruct *")
|
|
118
|
+
enc_status.API_sampleRate = 0
|
|
119
|
+
enc_status.maxInternalSampleRate = 0
|
|
120
|
+
enc_status.packetSize = 0
|
|
121
|
+
enc_status.bitRate = 0
|
|
122
|
+
enc_status.packetLossPercentage = 0
|
|
123
|
+
enc_status.complexity = 0
|
|
124
|
+
enc_status.useInBandFEC = 0
|
|
125
|
+
enc_status.useDTX = 0
|
|
126
|
+
|
|
127
|
+
enc_size_bytes = ffi.new("int32_t *", 0)
|
|
128
|
+
code: int = lib.SKP_Silk_SDK_Get_Encoder_Size(enc_size_bytes)
|
|
129
|
+
if code != 0:
|
|
130
|
+
raise SilkError(code)
|
|
131
|
+
enc = lib.PyMem_Malloc(enc_size_bytes[0])
|
|
132
|
+
if enc == ffi.NULL:
|
|
133
|
+
raise MemoryError
|
|
134
|
+
code = lib.SKP_Silk_SDK_InitEncoder(enc, enc_status)
|
|
135
|
+
if code != 0:
|
|
136
|
+
lib.PyMem_Free(enc)
|
|
137
|
+
raise SilkError(code)
|
|
138
|
+
frame_size: int = sample_rate // 1000 * 40
|
|
139
|
+
if tencent:
|
|
140
|
+
output.write(b"\x02#!SILK_V3")
|
|
141
|
+
else:
|
|
142
|
+
output.write(b"#!SILK_V3")
|
|
143
|
+
n_bytes = ffi.new("int16_t *", 1250)
|
|
144
|
+
payload = ffi.new("uint8_t[1250]")
|
|
145
|
+
try:
|
|
146
|
+
while True:
|
|
147
|
+
chunk = input.read(frame_size) # type: bytes
|
|
148
|
+
if not isinstance(chunk, bytes):
|
|
149
|
+
raise TypeError(
|
|
150
|
+
f"input must be a file-like rb object, got {type(input).__name__}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
n_bytes[0] = 1250
|
|
154
|
+
if len(chunk) < frame_size:
|
|
155
|
+
break
|
|
156
|
+
c_chunk = ffi.from_buffer("int16_t[]", chunk)
|
|
157
|
+
code = lib.SKP_Silk_SDK_Encode(
|
|
158
|
+
enc, enc_control, c_chunk, len(chunk) // 2, payload, n_bytes
|
|
159
|
+
)
|
|
160
|
+
if code != 0:
|
|
161
|
+
raise SilkError(code)
|
|
162
|
+
|
|
163
|
+
write_i16_le(output, n_bytes[0])
|
|
164
|
+
output.write(ffi.unpack(ffi.cast("char *", payload), n_bytes[0]))
|
|
165
|
+
finally:
|
|
166
|
+
lib.PyMem_Free(enc)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def decode(
|
|
170
|
+
input: IO,
|
|
171
|
+
output: IO,
|
|
172
|
+
sample_rate: int,
|
|
173
|
+
frame_size: int = 0,
|
|
174
|
+
frames_per_packet: int = 1,
|
|
175
|
+
more_internal_decoder_frames: bool = False,
|
|
176
|
+
in_band_fec_offset: int = 0,
|
|
177
|
+
loss: bool = False,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""decode(input: BinaryIO, output: BinaryIO, sample_rate: int, frame_size: int = 0, frames_per_packet: int = 1, more_internal_decoder_frames: bool = False, in_band_fec_offset: int = 0, loss: bool = False) -> bytes
|
|
180
|
+
decode silk to pcm
|
|
181
|
+
:param input:
|
|
182
|
+
:param output:
|
|
183
|
+
:param sample_rate:
|
|
184
|
+
:param frame_size:
|
|
185
|
+
:param frames_per_packet:
|
|
186
|
+
:param more_internal_decoder_frames:
|
|
187
|
+
:param in_band_fec_offset:
|
|
188
|
+
:param loss:
|
|
189
|
+
:return:
|
|
190
|
+
"""
|
|
191
|
+
if not check_file(input):
|
|
192
|
+
raise TypeError(
|
|
193
|
+
"input except a file-like object, got %s" % type(input).__name__
|
|
194
|
+
)
|
|
195
|
+
if not check_file(output):
|
|
196
|
+
raise TypeError(
|
|
197
|
+
"output except a file-like object, got %s" % type(output).__name__
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
chunk = input.read(9) # type: bytes
|
|
201
|
+
if not isinstance(chunk, bytes):
|
|
202
|
+
raise TypeError(
|
|
203
|
+
f"input must be a file-like rb object, got {type(input).__name__}"
|
|
204
|
+
)
|
|
205
|
+
if chunk != b"#!SILK_V3" and chunk != b"\x02#!SILK_V":
|
|
206
|
+
raise SilkError("INVALID")
|
|
207
|
+
elif chunk == b"\x02#!SILK_V":
|
|
208
|
+
chunk = input.read(1)
|
|
209
|
+
if chunk != b"3":
|
|
210
|
+
raise SilkError("INVALID")
|
|
211
|
+
|
|
212
|
+
dec_control = ffi.new("SKP_SILK_SDK_DecControlStruct *")
|
|
213
|
+
dec_control.API_sampleRate = sample_rate
|
|
214
|
+
dec_control.frameSize = frame_size
|
|
215
|
+
dec_control.framesPerPacket = frames_per_packet
|
|
216
|
+
dec_control.moreInternalDecoderFrames = more_internal_decoder_frames
|
|
217
|
+
dec_control.inBandFECOffset = in_band_fec_offset
|
|
218
|
+
dec_size = ffi.new("int32_t *", 0)
|
|
219
|
+
code: int = lib.SKP_Silk_SDK_Get_Decoder_Size(dec_size)
|
|
220
|
+
if code != 0:
|
|
221
|
+
raise SilkError(code)
|
|
222
|
+
dec = lib.PyMem_Malloc(dec_size[0])
|
|
223
|
+
if dec == ffi.NULL:
|
|
224
|
+
raise MemoryError
|
|
225
|
+
code = lib.SKP_Silk_SDK_InitDecoder(dec)
|
|
226
|
+
if code != 0:
|
|
227
|
+
lib.PyMem_Free(dec)
|
|
228
|
+
raise SilkError(code)
|
|
229
|
+
frame_size = sample_rate // 1000 * 40
|
|
230
|
+
# cdef uint8_t buf[frame_size] # otherwise need malloc
|
|
231
|
+
buf = lib.PyMem_Malloc(frame_size)
|
|
232
|
+
if buf == ffi.NULL:
|
|
233
|
+
lib.PyMem_Free(dec)
|
|
234
|
+
raise MemoryError
|
|
235
|
+
n_bytes = ffi.new("int16_t *")
|
|
236
|
+
try:
|
|
237
|
+
while True:
|
|
238
|
+
chunk = input.read(2)
|
|
239
|
+
if len(chunk) < 2:
|
|
240
|
+
break
|
|
241
|
+
n_bytes[0] = bytes_to_i16(chunk)
|
|
242
|
+
if lib.SHOULD_SWAP():
|
|
243
|
+
n_bytes[0] = lib.swap_i16(n_bytes[0])
|
|
244
|
+
if n_bytes[0] > frame_size:
|
|
245
|
+
raise SilkError("INVALID")
|
|
246
|
+
chunk = input.read(n_bytes[0]) # type: bytes
|
|
247
|
+
if len(chunk) < n_bytes[0]: # not enough data
|
|
248
|
+
raise SilkError("INVALID")
|
|
249
|
+
c_chunk = ffi.from_buffer("uint8_t[]", chunk)
|
|
250
|
+
code = lib.SKP_Silk_SDK_Decode(
|
|
251
|
+
dec,
|
|
252
|
+
dec_control,
|
|
253
|
+
loss,
|
|
254
|
+
c_chunk,
|
|
255
|
+
n_bytes[0],
|
|
256
|
+
ffi.cast("int16_t *", buf),
|
|
257
|
+
n_bytes,
|
|
258
|
+
)
|
|
259
|
+
if code != 0:
|
|
260
|
+
raise SilkError(code)
|
|
261
|
+
output.write(ffi.unpack(ffi.cast("char*", buf), n_bytes[0] * 2))
|
|
262
|
+
finally:
|
|
263
|
+
lib.PyMem_Free(buf)
|
|
264
|
+
lib.PyMem_Free(dec)
|
|
Binary file
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from cffi import FFI
|
|
5
|
+
|
|
6
|
+
ffibuilder = FFI()
|
|
7
|
+
ffibuilder.cdef(
|
|
8
|
+
"""
|
|
9
|
+
typedef struct {
|
|
10
|
+
/* I: Input signal sampling rate in Hertz; 8000/12000/16000/24000 */
|
|
11
|
+
int API_sampleRate;
|
|
12
|
+
/* I: Maximum internal sampling rate in Hertz; 8000/12000/16000/24000 */
|
|
13
|
+
int maxInternalSampleRate;
|
|
14
|
+
/* I: Number of samples per packet; must be equivalent of 20, 40, 60, 80 or 100 ms */
|
|
15
|
+
int packetSize;
|
|
16
|
+
/* I: Bitrate during active speech in bits/second; internally limited */
|
|
17
|
+
int bitRate;
|
|
18
|
+
/* I: Uplink packet loss in percent (0-100) */
|
|
19
|
+
int packetLossPercentage;
|
|
20
|
+
|
|
21
|
+
/* I: Complexity mode; 0 is lowest; 1 is medium and 2 is highest complexity */
|
|
22
|
+
int complexity;
|
|
23
|
+
/* I: Flag to enable in-band Forward Error Correction (FEC); 0/1 */
|
|
24
|
+
int useInBandFEC;
|
|
25
|
+
/* I: Flag to enable discontinuous transmission (DTX); 0/1 */
|
|
26
|
+
int useDTX;
|
|
27
|
+
} SKP_SILK_SDK_EncControlStruct;
|
|
28
|
+
typedef struct {
|
|
29
|
+
/* I: Output signal sampling rate in Hertz; 8000/12000/16000/24000 */
|
|
30
|
+
int API_sampleRate;
|
|
31
|
+
/* O: Number of samples per frame */
|
|
32
|
+
int frameSize;
|
|
33
|
+
/* O: Frames per packet 1, 2, 3, 4, 5 */
|
|
34
|
+
int framesPerPacket;
|
|
35
|
+
/* O: Flag to indicate that the decoder has remaining payloads internally */
|
|
36
|
+
int moreInternalDecoderFrames;
|
|
37
|
+
/* O: Distance between main payload and redundant payload in packets */
|
|
38
|
+
int inBandFECOffset;
|
|
39
|
+
} SKP_SILK_SDK_DecControlStruct;
|
|
40
|
+
int32_t SKP_Silk_SDK_Get_Encoder_Size(int32_t *encSizeBytes);
|
|
41
|
+
int32_t SKP_Silk_SDK_InitEncoder(void *encState, SKP_SILK_SDK_EncControlStruct *encStatus);
|
|
42
|
+
int32_t SKP_Silk_SDK_Encode(void *encState,
|
|
43
|
+
const SKP_SILK_SDK_EncControlStruct *encControl,
|
|
44
|
+
const int16_t *samplesIn,
|
|
45
|
+
int32_t nSamplesIn,
|
|
46
|
+
uint8_t *outData,
|
|
47
|
+
int16_t *nBytesOut);
|
|
48
|
+
int32_t SKP_Silk_SDK_Get_Decoder_Size(int32_t *decSizeBytes);
|
|
49
|
+
int32_t SKP_Silk_SDK_InitDecoder(void *decState);
|
|
50
|
+
int32_t SKP_Silk_SDK_Decode(void * decState,
|
|
51
|
+
SKP_SILK_SDK_DecControlStruct *decControl,
|
|
52
|
+
int32_t lostFlag,
|
|
53
|
+
const uint8_t *inData,
|
|
54
|
+
const int32_t nBytesIn,
|
|
55
|
+
int16_t *samplesOut,
|
|
56
|
+
int16_t *nSamplesOut);
|
|
57
|
+
uint8_t is_le();
|
|
58
|
+
int16_t swap_i16(int16_t data);
|
|
59
|
+
int SHOULD_SWAP();
|
|
60
|
+
void *PyMem_Malloc(size_t n);
|
|
61
|
+
void PyMem_Free(void* p);
|
|
62
|
+
"""
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
source = """
|
|
66
|
+
#include "SKP_Silk_typedef.h"
|
|
67
|
+
#include "SKP_Silk_SDK_API.h"
|
|
68
|
+
#include "SKP_Silk_control.h"
|
|
69
|
+
uint8_t is_le()
|
|
70
|
+
{
|
|
71
|
+
uint16_t data=1;
|
|
72
|
+
return *(uint8_t*)&data;
|
|
73
|
+
}
|
|
74
|
+
#ifdef _WIN32
|
|
75
|
+
#define swap_i16 _byteswap_ushort
|
|
76
|
+
#else
|
|
77
|
+
#define swap_i16 __builtin_bswap16
|
|
78
|
+
#endif /* _WIN32 */
|
|
79
|
+
#ifdef WORDS_BIGENDIAN
|
|
80
|
+
#define SHOULD_SWAP() 1
|
|
81
|
+
#else
|
|
82
|
+
#define SHOULD_SWAP() 0
|
|
83
|
+
#endif
|
|
84
|
+
"""
|
|
85
|
+
macro_base = []
|
|
86
|
+
if sys.byteorder != "little":
|
|
87
|
+
macro_base.append(("WORDS_BIGENDIAN", None))
|
|
88
|
+
ffibuilder.set_source(
|
|
89
|
+
"pysilk.backends.cffi._silk",
|
|
90
|
+
source,
|
|
91
|
+
sources=glob.glob("./silk-v3-decoder/silk/src/*.c"),
|
|
92
|
+
include_dirs=["./silk-v3-decoder/silk/interface"],
|
|
93
|
+
define_macros=macro_base,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
ffibuilder.compile()
|