opuslib-next 1.1.2__py2.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.
- opuslib_next/__init__.py +29 -0
- opuslib_next/api/__init__.py +27 -0
- opuslib_next/api/ctl.py +199 -0
- opuslib_next/api/decoder.py +318 -0
- opuslib_next/api/encoder.py +184 -0
- opuslib_next/api/info.py +24 -0
- opuslib_next/classes.py +323 -0
- opuslib_next/constants.py +103 -0
- opuslib_next/exceptions.py +29 -0
- opuslib_next-1.1.2.dist-info/LICENSE +24 -0
- opuslib_next-1.1.2.dist-info/METADATA +56 -0
- opuslib_next-1.1.2.dist-info/RECORD +13 -0
- opuslib_next-1.1.2.dist-info/WHEEL +4 -0
opuslib_next/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# OpusLib Python Module.
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
OpusLib Python Module.
|
|
8
|
+
~~~~~~~
|
|
9
|
+
|
|
10
|
+
Python bindings to the libopus, IETF low-delay audio codec
|
|
11
|
+
|
|
12
|
+
:author: kalicyh <kalicyh@qq.com>
|
|
13
|
+
:copyright: Copyright (c) 2025, Kalicyh
|
|
14
|
+
:license: BSD 3-Clause License
|
|
15
|
+
:source: <https://github.com/kalicyh/opuslib-next>
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .exceptions import OpusError # NOQA
|
|
20
|
+
|
|
21
|
+
from .constants import * # NOQA
|
|
22
|
+
|
|
23
|
+
from .constants import OK, APPLICATION_TYPES_MAP # NOQA
|
|
24
|
+
|
|
25
|
+
from .classes import Encoder, Decoder # NOQA
|
|
26
|
+
|
|
27
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
28
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
29
|
+
__license__ = 'BSD 3-Clause License'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=invalid-name
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""OpusLib Package."""
|
|
7
|
+
|
|
8
|
+
import ctypes # type: ignore
|
|
9
|
+
|
|
10
|
+
from ctypes.util import find_library # type: ignore
|
|
11
|
+
|
|
12
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
13
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
14
|
+
__license__ = 'BSD 3-Clause License'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
lib_location = find_library('opus')
|
|
18
|
+
|
|
19
|
+
if lib_location is None:
|
|
20
|
+
raise Exception(
|
|
21
|
+
'Could not find Opus library. Make sure it is installed.')
|
|
22
|
+
|
|
23
|
+
libopus = ctypes.CDLL(lib_location)
|
|
24
|
+
|
|
25
|
+
c_int_pointer = ctypes.POINTER(ctypes.c_int)
|
|
26
|
+
c_int16_pointer = ctypes.POINTER(ctypes.c_int16)
|
|
27
|
+
c_float_pointer = ctypes.POINTER(ctypes.c_float)
|
opuslib_next/api/ctl.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=invalid-name
|
|
4
|
+
|
|
5
|
+
"""CTL macros rewritten to Python
|
|
6
|
+
|
|
7
|
+
Usage example:
|
|
8
|
+
|
|
9
|
+
>>> import opuslib.api.decoder
|
|
10
|
+
>>> import opuslib.api.ctl
|
|
11
|
+
>>> dec = opuslib.api.decoder.create_state(48000, 2)
|
|
12
|
+
>>> opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.set_gain, -15)
|
|
13
|
+
>>> gain_value = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_gain)
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
18
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
19
|
+
__license__ = 'BSD 3-Clause License'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
import ctypes # type: ignore
|
|
23
|
+
|
|
24
|
+
import opuslib_next.api
|
|
25
|
+
import opuslib_next.exceptions
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def query(request):
|
|
29
|
+
|
|
30
|
+
"""Query encoder/decoder with a request value"""
|
|
31
|
+
|
|
32
|
+
def inner(func, obj):
|
|
33
|
+
result_code = func(obj, request)
|
|
34
|
+
|
|
35
|
+
if result_code is not opuslib_next.OK:
|
|
36
|
+
raise opuslib_next.exceptions.OpusError(result_code)
|
|
37
|
+
|
|
38
|
+
return result_code
|
|
39
|
+
|
|
40
|
+
return inner
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get(request, result_type):
|
|
44
|
+
|
|
45
|
+
"""Get CTL value from a encoder/decoder"""
|
|
46
|
+
|
|
47
|
+
def inner(func, obj):
|
|
48
|
+
result = result_type()
|
|
49
|
+
result_code = func(obj, request, ctypes.byref(result))
|
|
50
|
+
|
|
51
|
+
if result_code is not opuslib_next.OK:
|
|
52
|
+
raise opuslib_next.exceptions.OpusError(result_code)
|
|
53
|
+
|
|
54
|
+
return result.value
|
|
55
|
+
|
|
56
|
+
return inner
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def ctl_set(request):
|
|
60
|
+
|
|
61
|
+
"""Set new CTL value to a encoder/decoder"""
|
|
62
|
+
|
|
63
|
+
def inner(func, obj, value):
|
|
64
|
+
result_code = func(obj, request, value)
|
|
65
|
+
if result_code is not opuslib_next.OK:
|
|
66
|
+
raise opuslib_next.exceptions.OpusError(result_code)
|
|
67
|
+
|
|
68
|
+
return inner
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Generic CTLs
|
|
72
|
+
#
|
|
73
|
+
|
|
74
|
+
# Resets the codec state to be equivalent to a freshly initialized state
|
|
75
|
+
reset_state = query(opuslib_next.RESET_STATE) # NOQA
|
|
76
|
+
|
|
77
|
+
# Gets the final state of the codec's entropy coder
|
|
78
|
+
get_final_range = get(
|
|
79
|
+
opuslib_next.GET_FINAL_RANGE_REQUEST,
|
|
80
|
+
ctypes.c_uint
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Gets the encoder's configured bandpass or the decoder's last bandpass
|
|
84
|
+
get_bandwidth = get(opuslib_next.GET_BANDWIDTH_REQUEST, ctypes.c_int)
|
|
85
|
+
|
|
86
|
+
# Gets the pitch of the last decoded frame, if available
|
|
87
|
+
get_pitch = get(opuslib_next.GET_PITCH_REQUEST, ctypes.c_int)
|
|
88
|
+
|
|
89
|
+
# Configures the depth of signal being encoded
|
|
90
|
+
set_lsb_depth = ctl_set(opuslib_next.SET_LSB_DEPTH_REQUEST)
|
|
91
|
+
|
|
92
|
+
# Gets the encoder's configured signal depth
|
|
93
|
+
get_lsb_depth = get(opuslib_next.GET_LSB_DEPTH_REQUEST, ctypes.c_int)
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
# Decoder related CTLs
|
|
97
|
+
#
|
|
98
|
+
|
|
99
|
+
# Gets the decoder's configured gain adjustment
|
|
100
|
+
get_gain = get(opuslib_next.GET_GAIN_REQUEST, ctypes.c_int)
|
|
101
|
+
|
|
102
|
+
# Configures decoder gain adjustment
|
|
103
|
+
set_gain = ctl_set(opuslib_next.SET_GAIN_REQUEST)
|
|
104
|
+
|
|
105
|
+
#
|
|
106
|
+
# Encoder related CTLs
|
|
107
|
+
#
|
|
108
|
+
|
|
109
|
+
# Configures the encoder's computational complexity
|
|
110
|
+
set_complexity = ctl_set(opuslib_next.SET_COMPLEXITY_REQUEST)
|
|
111
|
+
|
|
112
|
+
# Gets the encoder's complexity configuration
|
|
113
|
+
get_complexity = get(
|
|
114
|
+
opuslib_next.GET_COMPLEXITY_REQUEST, ctypes.c_int)
|
|
115
|
+
|
|
116
|
+
# Configures the bitrate in the encoder
|
|
117
|
+
set_bitrate = ctl_set(opuslib_next.SET_BITRATE_REQUEST)
|
|
118
|
+
|
|
119
|
+
# Gets the encoder's bitrate configuration
|
|
120
|
+
get_bitrate = get(opuslib_next.GET_BITRATE_REQUEST, ctypes.c_int)
|
|
121
|
+
|
|
122
|
+
# Enables or disables variable bitrate (VBR) in the encoder
|
|
123
|
+
set_vbr = ctl_set(opuslib_next.SET_VBR_REQUEST)
|
|
124
|
+
|
|
125
|
+
# Determine if variable bitrate (VBR) is enabled in the encoder
|
|
126
|
+
get_vbr = get(opuslib_next.GET_VBR_REQUEST, ctypes.c_int)
|
|
127
|
+
|
|
128
|
+
# Enables or disables constrained VBR in the encoder
|
|
129
|
+
set_vbr_constraint = ctl_set(opuslib_next.SET_VBR_CONSTRAINT_REQUEST)
|
|
130
|
+
|
|
131
|
+
# Determine if constrained VBR is enabled in the encoder
|
|
132
|
+
get_vbr_constraint = get(
|
|
133
|
+
opuslib_next.GET_VBR_CONSTRAINT_REQUEST, ctypes.c_int)
|
|
134
|
+
|
|
135
|
+
# Configures mono/stereo forcing in the encoder
|
|
136
|
+
set_force_channels = ctl_set(opuslib_next.SET_FORCE_CHANNELS_REQUEST)
|
|
137
|
+
|
|
138
|
+
# Gets the encoder's forced channel configuration
|
|
139
|
+
get_force_channels = get(
|
|
140
|
+
opuslib_next.GET_FORCE_CHANNELS_REQUEST, ctypes.c_int)
|
|
141
|
+
|
|
142
|
+
# Configures the maximum bandpass that the encoder will select automatically
|
|
143
|
+
set_max_bandwidth = ctl_set(opuslib_next.SET_MAX_BANDWIDTH_REQUEST)
|
|
144
|
+
|
|
145
|
+
# Gets the encoder's configured maximum allowed bandpass
|
|
146
|
+
get_max_bandwidth = get(
|
|
147
|
+
opuslib_next.GET_MAX_BANDWIDTH_REQUEST, ctypes.c_int)
|
|
148
|
+
|
|
149
|
+
# Sets the encoder's bandpass to a specific value
|
|
150
|
+
set_bandwidth = ctl_set(opuslib_next.SET_BANDWIDTH_REQUEST)
|
|
151
|
+
|
|
152
|
+
# Configures the type of signal being encoded
|
|
153
|
+
set_signal = ctl_set(opuslib_next.SET_SIGNAL_REQUEST)
|
|
154
|
+
|
|
155
|
+
# Gets the encoder's configured signal type
|
|
156
|
+
get_signal = get(opuslib_next.GET_SIGNAL_REQUEST, ctypes.c_int)
|
|
157
|
+
|
|
158
|
+
# Configures the encoder's intended application
|
|
159
|
+
set_application = ctl_set(opuslib_next.SET_APPLICATION_REQUEST)
|
|
160
|
+
|
|
161
|
+
# Gets the encoder's configured application
|
|
162
|
+
get_application = get(
|
|
163
|
+
opuslib_next.GET_APPLICATION_REQUEST, ctypes.c_int)
|
|
164
|
+
|
|
165
|
+
# Gets the sampling rate the encoder or decoder was initialized with
|
|
166
|
+
get_sample_rate = get(
|
|
167
|
+
opuslib_next.GET_SAMPLE_RATE_REQUEST, ctypes.c_int)
|
|
168
|
+
|
|
169
|
+
# Gets the total samples of delay added by the entire codec
|
|
170
|
+
get_lookahead = get(opuslib_next.GET_LOOKAHEAD_REQUEST, ctypes.c_int)
|
|
171
|
+
|
|
172
|
+
# Configures the encoder's use of inband forward error correction (FEC)
|
|
173
|
+
set_inband_fec = ctl_set(opuslib_next.SET_INBAND_FEC_REQUEST)
|
|
174
|
+
|
|
175
|
+
# Gets encoder's configured use of inband forward error correction
|
|
176
|
+
get_inband_fec = get(
|
|
177
|
+
opuslib_next.GET_INBAND_FEC_REQUEST, ctypes.c_int)
|
|
178
|
+
|
|
179
|
+
# Configures the encoder's expected packet loss percentage
|
|
180
|
+
set_packet_loss_perc = ctl_set(
|
|
181
|
+
opuslib_next.SET_PACKET_LOSS_PERC_REQUEST)
|
|
182
|
+
|
|
183
|
+
# Gets the encoder's configured packet loss percentage
|
|
184
|
+
get_packet_loss_perc = get(
|
|
185
|
+
opuslib_next.GET_PACKET_LOSS_PERC_REQUEST,
|
|
186
|
+
ctypes.c_int
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Configures the encoder's use of discontinuous transmission (DTX)
|
|
190
|
+
set_dtx = ctl_set(opuslib_next.SET_DTX_REQUEST)
|
|
191
|
+
|
|
192
|
+
# Gets encoder's configured use of discontinuous transmission
|
|
193
|
+
get_dtx = get(opuslib_next.GET_DTX_REQUEST, ctypes.c_int)
|
|
194
|
+
|
|
195
|
+
#
|
|
196
|
+
# Other stuff
|
|
197
|
+
#
|
|
198
|
+
|
|
199
|
+
unimplemented = query(opuslib_next.UNIMPLEMENTED)
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=invalid-name,too-few-public-methods
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
CTypes mapping between libopus functions and Python.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import array
|
|
11
|
+
import ctypes # type: ignore
|
|
12
|
+
import typing
|
|
13
|
+
|
|
14
|
+
import opuslib_next
|
|
15
|
+
import opuslib_next.api
|
|
16
|
+
|
|
17
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
18
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
19
|
+
__license__ = 'BSD 3-Clause License'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Decoder(ctypes.Structure):
|
|
23
|
+
"""Opus decoder state.
|
|
24
|
+
This contains the complete state of an Opus decoder.
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
DecoderPointer = ctypes.POINTER(Decoder)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
libopus_get_size = opuslib_next.api.libopus.opus_decoder_get_size
|
|
33
|
+
libopus_get_size.argtypes = (ctypes.c_int,)
|
|
34
|
+
libopus_get_size.restype = ctypes.c_int
|
|
35
|
+
libopus_get_size.__doc__ = 'Gets the size of an OpusDecoder structure'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
libopus_create = opuslib_next.api.libopus.opus_decoder_create
|
|
39
|
+
libopus_create.argtypes = (
|
|
40
|
+
ctypes.c_int,
|
|
41
|
+
ctypes.c_int,
|
|
42
|
+
opuslib_next.api.c_int_pointer
|
|
43
|
+
)
|
|
44
|
+
libopus_create.restype = DecoderPointer
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_state(fs: int, channels: int) -> ctypes.Structure:
|
|
48
|
+
"""
|
|
49
|
+
Allocates and initializes a decoder state.
|
|
50
|
+
Wrapper for C opus_decoder_create()
|
|
51
|
+
|
|
52
|
+
`fs` must be one of 8000, 12000, 16000, 24000, or 48000.
|
|
53
|
+
|
|
54
|
+
Internally Opus stores data at 48000 Hz, so that should be the default
|
|
55
|
+
value for Fs. However, the decoder can efficiently decode to buffers
|
|
56
|
+
at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use data
|
|
57
|
+
at the full sample rate, or knows the compressed data doesn't use the full
|
|
58
|
+
frequency range, it can request decoding at a reduced rate. Likewise, the
|
|
59
|
+
decoder is capable of filling in either mono or interleaved stereo pcm
|
|
60
|
+
buffers, at the caller's request.
|
|
61
|
+
|
|
62
|
+
:param fs: Sample rate to decode at (Hz).
|
|
63
|
+
:param int: Number of channels (1 or 2) to decode.
|
|
64
|
+
"""
|
|
65
|
+
result_code = ctypes.c_int()
|
|
66
|
+
|
|
67
|
+
decoder_state = libopus_create(
|
|
68
|
+
fs,
|
|
69
|
+
channels,
|
|
70
|
+
ctypes.byref(result_code)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if result_code.value != 0:
|
|
74
|
+
raise opuslib_next.exceptions.OpusError(result_code.value)
|
|
75
|
+
|
|
76
|
+
return decoder_state
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
libopus_packet_get_bandwidth = opuslib_next.api.libopus.opus_packet_get_bandwidth
|
|
80
|
+
# `argtypes` must be a sequence (,) of types!
|
|
81
|
+
libopus_packet_get_bandwidth.argtypes = (ctypes.c_char_p,)
|
|
82
|
+
libopus_packet_get_bandwidth.restype = ctypes.c_int
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
86
|
+
def packet_get_bandwidth(data: bytes) -> typing.Union[int, typing.Any]:
|
|
87
|
+
"""Gets the bandwidth of an Opus packet."""
|
|
88
|
+
data_pointer = ctypes.c_char_p(data)
|
|
89
|
+
|
|
90
|
+
result = libopus_packet_get_bandwidth(data_pointer)
|
|
91
|
+
|
|
92
|
+
if result < 0:
|
|
93
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
94
|
+
|
|
95
|
+
return result
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
libopus_packet_get_nb_channels = opuslib_next.api.libopus.opus_packet_get_nb_channels # NOQA
|
|
99
|
+
# `argtypes` must be a sequence (,) of types!
|
|
100
|
+
libopus_packet_get_nb_channels.argtypes = (ctypes.c_char_p,)
|
|
101
|
+
libopus_packet_get_nb_channels.restype = ctypes.c_int
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
105
|
+
def packet_get_nb_channels(data: bytes) -> typing.Union[int, typing.Any]:
|
|
106
|
+
"""Gets the number of channels from an Opus packet"""
|
|
107
|
+
data_pointer = ctypes.c_char_p(data)
|
|
108
|
+
|
|
109
|
+
result = libopus_packet_get_nb_channels(data_pointer)
|
|
110
|
+
|
|
111
|
+
if result < 0:
|
|
112
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
libopus_packet_get_nb_frames = opuslib_next.api.libopus.opus_packet_get_nb_frames
|
|
118
|
+
libopus_packet_get_nb_frames.argtypes = (ctypes.c_char_p, ctypes.c_int)
|
|
119
|
+
libopus_packet_get_nb_frames.restype = ctypes.c_int
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
123
|
+
def packet_get_nb_frames(
|
|
124
|
+
data: bytes,
|
|
125
|
+
length: typing.Optional[int] = None
|
|
126
|
+
) -> typing.Union[int, typing.Any]:
|
|
127
|
+
"""Gets the number of frames in an Opus packet"""
|
|
128
|
+
data_pointer = ctypes.c_char_p(data)
|
|
129
|
+
|
|
130
|
+
if length is None:
|
|
131
|
+
length = len(data)
|
|
132
|
+
|
|
133
|
+
result = libopus_packet_get_nb_frames(data_pointer, ctypes.c_int(length))
|
|
134
|
+
|
|
135
|
+
if result < 0:
|
|
136
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
libopus_packet_get_samples_per_frame = \
|
|
142
|
+
opuslib_next.api.libopus.opus_packet_get_samples_per_frame
|
|
143
|
+
libopus_packet_get_samples_per_frame.argtypes = (ctypes.c_char_p, ctypes.c_int)
|
|
144
|
+
libopus_packet_get_samples_per_frame.restype = ctypes.c_int
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
148
|
+
def packet_get_samples_per_frame(
|
|
149
|
+
data: bytes,
|
|
150
|
+
fs: int
|
|
151
|
+
) -> typing.Union[int, typing.Any]:
|
|
152
|
+
"""Gets the number of samples per frame from an Opus packet"""
|
|
153
|
+
data_pointer = ctypes.c_char_p(data)
|
|
154
|
+
|
|
155
|
+
result = libopus_packet_get_nb_frames(data_pointer, ctypes.c_int(fs))
|
|
156
|
+
|
|
157
|
+
if result < 0:
|
|
158
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
159
|
+
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
libopus_get_nb_samples = opuslib_next.api.libopus.opus_decoder_get_nb_samples
|
|
164
|
+
libopus_get_nb_samples.argtypes = (
|
|
165
|
+
DecoderPointer,
|
|
166
|
+
ctypes.c_char_p,
|
|
167
|
+
ctypes.c_int32
|
|
168
|
+
)
|
|
169
|
+
libopus_get_nb_samples.restype = ctypes.c_int
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
173
|
+
def get_nb_samples(
|
|
174
|
+
decoder_state: ctypes.Structure,
|
|
175
|
+
packet: bytes,
|
|
176
|
+
length: int
|
|
177
|
+
) -> typing.Union[int, typing.Any]:
|
|
178
|
+
"""
|
|
179
|
+
Gets the number of samples of an Opus packet.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
[in] dec OpusDecoder*: Decoder state
|
|
183
|
+
[in] packet char*: Opus packet
|
|
184
|
+
[in] len opus_int32: Length of packet
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
Number of samples
|
|
188
|
+
|
|
189
|
+
Return values
|
|
190
|
+
OPUS_BAD_ARG Insufficient data was passed to the function
|
|
191
|
+
OPUS_INVALID_PACKET The compressed data passed is corrupted or of an
|
|
192
|
+
unsupported type
|
|
193
|
+
"""
|
|
194
|
+
result = libopus_get_nb_samples(decoder_state, packet, length)
|
|
195
|
+
|
|
196
|
+
if result < 0:
|
|
197
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
198
|
+
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
libopus_decode = opuslib_next.api.libopus.opus_decode
|
|
203
|
+
libopus_decode.argtypes = (
|
|
204
|
+
DecoderPointer,
|
|
205
|
+
ctypes.c_char_p,
|
|
206
|
+
ctypes.c_int32,
|
|
207
|
+
opuslib_next.api.c_int16_pointer,
|
|
208
|
+
ctypes.c_int,
|
|
209
|
+
ctypes.c_int
|
|
210
|
+
)
|
|
211
|
+
libopus_decode.restype = ctypes.c_int
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
215
|
+
def decode( # pylint: disable=too-many-arguments
|
|
216
|
+
decoder_state: ctypes.Structure,
|
|
217
|
+
opus_data: bytes,
|
|
218
|
+
length: int,
|
|
219
|
+
frame_size: int,
|
|
220
|
+
decode_fec: bool,
|
|
221
|
+
channels: int = 2
|
|
222
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
223
|
+
"""
|
|
224
|
+
Decode an Opus Frame to PCM.
|
|
225
|
+
|
|
226
|
+
Unlike the `opus_decode` function , this function takes an additional
|
|
227
|
+
parameter `channels`, which indicates the number of channels in the frame.
|
|
228
|
+
"""
|
|
229
|
+
_decode_fec = int(decode_fec)
|
|
230
|
+
result = 0
|
|
231
|
+
|
|
232
|
+
pcm_size = frame_size * channels * ctypes.sizeof(ctypes.c_int16)
|
|
233
|
+
pcm = (ctypes.c_int16 * pcm_size)()
|
|
234
|
+
pcm_pointer = ctypes.cast(pcm, opuslib_next.api.c_int16_pointer)
|
|
235
|
+
|
|
236
|
+
result = libopus_decode(
|
|
237
|
+
decoder_state,
|
|
238
|
+
opus_data,
|
|
239
|
+
length,
|
|
240
|
+
pcm_pointer,
|
|
241
|
+
frame_size,
|
|
242
|
+
_decode_fec
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if result < 0:
|
|
246
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
247
|
+
|
|
248
|
+
return array.array('h', pcm_pointer[:result * channels]).tobytes()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
libopus_decode_float = opuslib_next.api.libopus.opus_decode_float
|
|
252
|
+
libopus_decode_float.argtypes = (
|
|
253
|
+
DecoderPointer,
|
|
254
|
+
ctypes.c_char_p,
|
|
255
|
+
ctypes.c_int32,
|
|
256
|
+
opuslib_next.api.c_float_pointer,
|
|
257
|
+
ctypes.c_int,
|
|
258
|
+
ctypes.c_int
|
|
259
|
+
)
|
|
260
|
+
libopus_decode_float.restype = ctypes.c_int
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
264
|
+
def decode_float( # pylint: disable=too-many-arguments
|
|
265
|
+
decoder_state: ctypes.Structure,
|
|
266
|
+
opus_data: bytes,
|
|
267
|
+
length: int,
|
|
268
|
+
frame_size: int,
|
|
269
|
+
decode_fec: bool,
|
|
270
|
+
channels: int = 2
|
|
271
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
272
|
+
"""
|
|
273
|
+
Decode an Opus Frame.
|
|
274
|
+
|
|
275
|
+
Unlike the `opus_decode` function , this function takes an additional
|
|
276
|
+
parameter `channels`, which indicates the number of channels in the frame.
|
|
277
|
+
"""
|
|
278
|
+
_decode_fec = int(decode_fec)
|
|
279
|
+
|
|
280
|
+
pcm_size = frame_size * channels * ctypes.sizeof(ctypes.c_float)
|
|
281
|
+
pcm = (ctypes.c_float * pcm_size)()
|
|
282
|
+
pcm_pointer = ctypes.cast(pcm, opuslib_next.api.c_float_pointer)
|
|
283
|
+
|
|
284
|
+
result = libopus_decode_float(
|
|
285
|
+
decoder_state,
|
|
286
|
+
opus_data,
|
|
287
|
+
length,
|
|
288
|
+
pcm_pointer,
|
|
289
|
+
frame_size,
|
|
290
|
+
_decode_fec
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if result < 0:
|
|
294
|
+
raise opuslib_next.exceptions.OpusError(result)
|
|
295
|
+
|
|
296
|
+
return array.array('f', pcm[:result * channels]).tobytes()
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
libopus_ctl = opuslib_next.api.libopus.opus_decoder_ctl
|
|
300
|
+
libopus_ctl.argtypes = (DecoderPointer, ctypes.c_int,)
|
|
301
|
+
libopus_ctl.restype = ctypes.c_int
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
305
|
+
def decoder_ctl(
|
|
306
|
+
decoder_state: ctypes.Structure,
|
|
307
|
+
request,
|
|
308
|
+
value=None
|
|
309
|
+
) -> typing.Union[int, typing.Any]:
|
|
310
|
+
if value is not None:
|
|
311
|
+
return request(libopus_ctl, decoder_state, value)
|
|
312
|
+
return request(libopus_ctl, decoder_state)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
destroy = opuslib_next.api.libopus.opus_decoder_destroy
|
|
316
|
+
destroy.argtypes = (DecoderPointer,)
|
|
317
|
+
destroy.restype = None
|
|
318
|
+
destroy.__doc__ = 'Frees an OpusDecoder allocated by opus_decoder_create()'
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=invalid-name
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
CTypes mapping between libopus functions and Python.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import array
|
|
11
|
+
import ctypes # type: ignore
|
|
12
|
+
import typing
|
|
13
|
+
|
|
14
|
+
import opuslib_next
|
|
15
|
+
import opuslib_next.api
|
|
16
|
+
|
|
17
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
18
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
19
|
+
__license__ = 'BSD 3-Clause License'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Encoder(ctypes.Structure): # pylint: disable=too-few-public-methods
|
|
23
|
+
"""Opus encoder state.
|
|
24
|
+
This contains the complete state of an Opus encoder.
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
EncoderPointer = ctypes.POINTER(Encoder)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
libopus_get_size = opuslib_next.api.libopus.opus_encoder_get_size
|
|
33
|
+
libopus_get_size.argtypes = (ctypes.c_int,) # must be sequence (,) of types!
|
|
34
|
+
libopus_get_size.restype = ctypes.c_int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
38
|
+
def get_size(channels: int) -> typing.Union[int, typing.Any]:
|
|
39
|
+
"""Gets the size of an OpusEncoder structure."""
|
|
40
|
+
if channels not in (1, 2):
|
|
41
|
+
raise ValueError('Wrong channels value. Must be equal to 1 or 2')
|
|
42
|
+
return libopus_get_size(channels)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
libopus_create = opuslib_next.api.libopus.opus_encoder_create
|
|
46
|
+
libopus_create.argtypes = (
|
|
47
|
+
ctypes.c_int,
|
|
48
|
+
ctypes.c_int,
|
|
49
|
+
ctypes.c_int,
|
|
50
|
+
opuslib_next.api.c_int_pointer
|
|
51
|
+
)
|
|
52
|
+
libopus_create.restype = EncoderPointer
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_state(fs: int, channels: int, application: int) -> ctypes.Structure:
|
|
56
|
+
"""Allocates and initializes an encoder state."""
|
|
57
|
+
result_code = ctypes.c_int()
|
|
58
|
+
|
|
59
|
+
result = libopus_create(
|
|
60
|
+
fs,
|
|
61
|
+
channels,
|
|
62
|
+
application,
|
|
63
|
+
ctypes.byref(result_code)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if result_code.value is not opuslib_next.OK:
|
|
67
|
+
raise opuslib_next.OpusError(result_code.value)
|
|
68
|
+
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
libopus_ctl = opuslib_next.api.libopus.opus_encoder_ctl
|
|
73
|
+
libopus_ctl.argtypes = (EncoderPointer, ctypes.c_int,)
|
|
74
|
+
libopus_ctl.restype = ctypes.c_int
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
78
|
+
def encoder_ctl(
|
|
79
|
+
encoder_state: ctypes.Structure,
|
|
80
|
+
request,
|
|
81
|
+
value=None
|
|
82
|
+
) -> typing.Union[int, typing.Any]:
|
|
83
|
+
if value is not None:
|
|
84
|
+
return request(libopus_ctl, encoder_state, value)
|
|
85
|
+
return request(libopus_ctl, encoder_state)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
libopus_encode = opuslib_next.api.libopus.opus_encode
|
|
89
|
+
libopus_encode.argtypes = (
|
|
90
|
+
EncoderPointer,
|
|
91
|
+
opuslib_next.api.c_int16_pointer,
|
|
92
|
+
ctypes.c_int,
|
|
93
|
+
ctypes.c_char_p,
|
|
94
|
+
ctypes.c_int32
|
|
95
|
+
)
|
|
96
|
+
libopus_encode.restype = ctypes.c_int32
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
100
|
+
def encode(
|
|
101
|
+
encoder_state: ctypes.Structure,
|
|
102
|
+
pcm_data: bytes,
|
|
103
|
+
frame_size: int,
|
|
104
|
+
max_data_bytes: int
|
|
105
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
106
|
+
"""
|
|
107
|
+
Encodes an Opus Frame.
|
|
108
|
+
|
|
109
|
+
Returns string output payload.
|
|
110
|
+
|
|
111
|
+
Parameters:
|
|
112
|
+
[in] st OpusEncoder*: Encoder state
|
|
113
|
+
[in] pcm opus_int16*: Input signal (interleaved if 2 channels). length
|
|
114
|
+
is frame_size*channels*sizeof(opus_int16)
|
|
115
|
+
[in] frame_size int: Number of samples per channel in the input signal.
|
|
116
|
+
This must be an Opus frame size for the encoder's sampling rate. For
|
|
117
|
+
example, at 48 kHz the permitted values are 120, 240, 480, 960,
|
|
118
|
+
1920, and 2880. Passing in a duration of less than 10 ms
|
|
119
|
+
(480 samples at 48 kHz) will prevent the encoder from using the
|
|
120
|
+
LPC or hybrid modes.
|
|
121
|
+
[out] data unsigned char*: Output payload. This must contain storage
|
|
122
|
+
for at least max_data_bytes.
|
|
123
|
+
[in] max_data_bytes opus_int32: Size of the allocated memory for the
|
|
124
|
+
output payload. This may be used to impose an upper limit on the
|
|
125
|
+
instant bitrate, but should not be used as the only bitrate control.
|
|
126
|
+
Use OPUS_SET_BITRATE to control the bitrate.
|
|
127
|
+
"""
|
|
128
|
+
pcm_pointer = ctypes.cast(pcm_data, opuslib_next.api.c_int16_pointer)
|
|
129
|
+
opus_data = (ctypes.c_char * max_data_bytes)()
|
|
130
|
+
|
|
131
|
+
result = libopus_encode(
|
|
132
|
+
encoder_state,
|
|
133
|
+
pcm_pointer,
|
|
134
|
+
frame_size,
|
|
135
|
+
opus_data,
|
|
136
|
+
max_data_bytes
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if result < 0:
|
|
140
|
+
raise opuslib_next.OpusError(result)
|
|
141
|
+
|
|
142
|
+
return array.array('b', opus_data[:result]).tobytes()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
libopus_encode_float = opuslib_next.api.libopus.opus_encode_float
|
|
146
|
+
libopus_encode_float.argtypes = (
|
|
147
|
+
EncoderPointer,
|
|
148
|
+
opuslib_next.api.c_float_pointer,
|
|
149
|
+
ctypes.c_int,
|
|
150
|
+
ctypes.c_char_p,
|
|
151
|
+
ctypes.c_int32
|
|
152
|
+
)
|
|
153
|
+
libopus_encode_float.restype = ctypes.c_int32
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
157
|
+
def encode_float(
|
|
158
|
+
encoder_state: ctypes.Structure,
|
|
159
|
+
pcm_data: bytes,
|
|
160
|
+
frame_size: int,
|
|
161
|
+
max_data_bytes: int
|
|
162
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
163
|
+
"""Encodes an Opus frame from floating point input"""
|
|
164
|
+
pcm_pointer = ctypes.cast(pcm_data, opuslib_next.api.c_float_pointer)
|
|
165
|
+
opus_data = (ctypes.c_char * max_data_bytes)()
|
|
166
|
+
|
|
167
|
+
result = libopus_encode_float(
|
|
168
|
+
encoder_state,
|
|
169
|
+
pcm_pointer,
|
|
170
|
+
frame_size,
|
|
171
|
+
opus_data,
|
|
172
|
+
max_data_bytes
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if result < 0:
|
|
176
|
+
raise opuslib_next.OpusError(result)
|
|
177
|
+
|
|
178
|
+
return array.array('b', opus_data[:result]).tobytes()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
destroy = opuslib_next.api.libopus.opus_encoder_destroy
|
|
182
|
+
destroy.argtypes = (EncoderPointer,) # must be sequence (,) of types!
|
|
183
|
+
destroy.restype = None
|
|
184
|
+
destroy.__doc__ = "Frees an OpusEncoder allocated by opus_encoder_create()"
|
opuslib_next/api/info.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=invalid-name
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
import ctypes # type: ignore
|
|
7
|
+
|
|
8
|
+
import opuslib_next.api
|
|
9
|
+
|
|
10
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
11
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
12
|
+
__license__ = 'BSD 3-Clause License'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
strerror = opuslib_next.api.libopus.opus_strerror
|
|
16
|
+
strerror.argtypes = (ctypes.c_int,) # must be sequence (,) of types!
|
|
17
|
+
strerror.restype = ctypes.c_char_p
|
|
18
|
+
strerror.__doc__ = 'Converts an opus error code into a human readable string'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
get_version_string = opuslib_next.api.libopus.opus_get_version_string
|
|
22
|
+
get_version_string.argtypes = None
|
|
23
|
+
get_version_string.restype = ctypes.c_char_p
|
|
24
|
+
get_version_string.__doc__ = 'Gets the libopus version string'
|
opuslib_next/classes.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""High-level interface to a Opus decoder functions"""
|
|
5
|
+
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
import opuslib_next
|
|
9
|
+
import opuslib_next.api
|
|
10
|
+
import opuslib_next.api.ctl
|
|
11
|
+
import opuslib_next.api.decoder
|
|
12
|
+
import opuslib_next.api.encoder
|
|
13
|
+
|
|
14
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
15
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
16
|
+
__license__ = 'BSD 3-Clause License'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Decoder(object):
|
|
20
|
+
|
|
21
|
+
"""High-Level Decoder Object."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, fs: int, channels: int) -> None:
|
|
24
|
+
"""
|
|
25
|
+
:param fs: Sample Rate.
|
|
26
|
+
:param channels: Number of channels.
|
|
27
|
+
"""
|
|
28
|
+
self._fs = fs
|
|
29
|
+
self._channels = channels
|
|
30
|
+
self.decoder_state = opuslib_next.api.decoder.create_state(fs, channels)
|
|
31
|
+
|
|
32
|
+
def __del__(self) -> None:
|
|
33
|
+
if hasattr(self, 'decoder_state'):
|
|
34
|
+
# Destroying state only if __init__ completed successfully
|
|
35
|
+
opuslib_next.api.decoder.destroy(self.decoder_state)
|
|
36
|
+
|
|
37
|
+
def reset_state(self) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Resets the codec state to be equivalent to a freshly initialized state
|
|
40
|
+
"""
|
|
41
|
+
opuslib_next.api.decoder.decoder_ctl(
|
|
42
|
+
self.decoder_state,
|
|
43
|
+
opuslib_next.api.ctl.reset_state
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
47
|
+
def decode(
|
|
48
|
+
self,
|
|
49
|
+
opus_data: bytes,
|
|
50
|
+
frame_size: int,
|
|
51
|
+
decode_fec: bool = False
|
|
52
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
53
|
+
"""
|
|
54
|
+
Decodes given Opus data to PCM.
|
|
55
|
+
"""
|
|
56
|
+
return opuslib_next.api.decoder.decode(
|
|
57
|
+
self.decoder_state,
|
|
58
|
+
opus_data,
|
|
59
|
+
len(opus_data),
|
|
60
|
+
frame_size,
|
|
61
|
+
decode_fec,
|
|
62
|
+
channels=self._channels
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
66
|
+
def decode_float(
|
|
67
|
+
self,
|
|
68
|
+
opus_data: bytes,
|
|
69
|
+
frame_size: int,
|
|
70
|
+
decode_fec: bool = False
|
|
71
|
+
) -> typing.Union[bytes, typing.Any]:
|
|
72
|
+
"""
|
|
73
|
+
Decodes given Opus data to PCM.
|
|
74
|
+
"""
|
|
75
|
+
return opuslib_next.api.decoder.decode_float(
|
|
76
|
+
self.decoder_state,
|
|
77
|
+
opus_data,
|
|
78
|
+
len(opus_data),
|
|
79
|
+
frame_size,
|
|
80
|
+
decode_fec,
|
|
81
|
+
channels=self._channels
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# CTL interfaces
|
|
85
|
+
|
|
86
|
+
_get_final_range = lambda self: opuslib_next.api.decoder.decoder_ctl(
|
|
87
|
+
self.decoder_state,
|
|
88
|
+
opuslib_next.api.ctl.get_final_range
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
final_range = property(_get_final_range)
|
|
92
|
+
|
|
93
|
+
_get_bandwidth = lambda self: opuslib_next.api.decoder.decoder_ctl(
|
|
94
|
+
self.decoder_state,
|
|
95
|
+
opuslib_next.api.ctl.get_bandwidth
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
bandwidth = property(_get_bandwidth)
|
|
99
|
+
|
|
100
|
+
_get_pitch = lambda self: opuslib_next.api.decoder.decoder_ctl(
|
|
101
|
+
self.decoder_state,
|
|
102
|
+
opuslib_next.api.ctl.get_pitch
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
pitch = property(_get_pitch)
|
|
106
|
+
|
|
107
|
+
_get_lsb_depth = lambda self: opuslib_next.api.decoder.decoder_ctl(
|
|
108
|
+
self.decoder_state,
|
|
109
|
+
opuslib_next.api.ctl.get_lsb_depth
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
_set_lsb_depth = lambda self, x: opuslib_next.api.decoder.decoder_ctl(
|
|
113
|
+
self.decoder_state,
|
|
114
|
+
opuslib_next.api.ctl.set_lsb_depth,
|
|
115
|
+
x
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
lsb_depth = property(_get_lsb_depth, _set_lsb_depth)
|
|
119
|
+
|
|
120
|
+
_get_gain = lambda self: opuslib_next.api.decoder.decoder_ctl(
|
|
121
|
+
self.decoder_state,
|
|
122
|
+
opuslib_next.api.ctl.get_gain
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
_set_gain = lambda self, x: opuslib_next.api.decoder.decoder_ctl(
|
|
126
|
+
self.decoder_state,
|
|
127
|
+
opuslib_next.api.ctl.set_gain,
|
|
128
|
+
x
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
gain = property(_get_gain, _set_gain)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class Encoder(object):
|
|
135
|
+
|
|
136
|
+
"""High-Level Encoder Object."""
|
|
137
|
+
|
|
138
|
+
def __init__(self, fs, channels, application) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Parameters:
|
|
141
|
+
fs : sampling rate
|
|
142
|
+
channels : number of channels
|
|
143
|
+
"""
|
|
144
|
+
# Check to see if the Encoder Application Macro is available:
|
|
145
|
+
if application in list(opuslib_next.APPLICATION_TYPES_MAP.keys()):
|
|
146
|
+
application = opuslib_next.APPLICATION_TYPES_MAP[application]
|
|
147
|
+
elif application in list(opuslib_next.APPLICATION_TYPES_MAP.values()):
|
|
148
|
+
pass # Nothing to do here
|
|
149
|
+
else:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
"`application` value must be in 'voip', 'audio' or "
|
|
152
|
+
"'restricted_lowdelay'")
|
|
153
|
+
|
|
154
|
+
self._fs = fs
|
|
155
|
+
self._channels = channels
|
|
156
|
+
self._application = application
|
|
157
|
+
self.encoder_state = opuslib_next.api.encoder.create_state(
|
|
158
|
+
fs, channels, application)
|
|
159
|
+
|
|
160
|
+
def __del__(self) -> None:
|
|
161
|
+
if hasattr(self, 'encoder_state'):
|
|
162
|
+
# Destroying state only if __init__ completed successfully
|
|
163
|
+
opuslib_next.api.encoder.destroy(self.encoder_state)
|
|
164
|
+
|
|
165
|
+
def reset_state(self) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Resets the codec state to be equivalent to a freshly initialized state
|
|
168
|
+
"""
|
|
169
|
+
opuslib_next.api.encoder.encoder_ctl(
|
|
170
|
+
self.encoder_state, opuslib_next.api.ctl.reset_state)
|
|
171
|
+
|
|
172
|
+
def encode(self, pcm_data: bytes, frame_size: int) -> bytes:
|
|
173
|
+
"""
|
|
174
|
+
Encodes given PCM data as Opus.
|
|
175
|
+
"""
|
|
176
|
+
return opuslib_next.api.encoder.encode(
|
|
177
|
+
self.encoder_state,
|
|
178
|
+
pcm_data,
|
|
179
|
+
frame_size,
|
|
180
|
+
len(pcm_data)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def encode_float(self, pcm_data: bytes, frame_size: int) -> bytes:
|
|
184
|
+
"""
|
|
185
|
+
Encodes given PCM data as Opus.
|
|
186
|
+
"""
|
|
187
|
+
return opuslib_next.api.encoder.encode_float(
|
|
188
|
+
self.encoder_state,
|
|
189
|
+
pcm_data,
|
|
190
|
+
frame_size,
|
|
191
|
+
len(pcm_data)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# CTL interfaces
|
|
195
|
+
|
|
196
|
+
_get_final_range = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
197
|
+
self.encoder_state,
|
|
198
|
+
opuslib_next.api.ctl.get_final_range
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
final_range = property(_get_final_range)
|
|
202
|
+
|
|
203
|
+
_get_bandwidth = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
204
|
+
self.encoder_state, opuslib_next.api.ctl.get_bandwidth)
|
|
205
|
+
|
|
206
|
+
bandwidth = property(_get_bandwidth)
|
|
207
|
+
|
|
208
|
+
_get_pitch = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
209
|
+
self.encoder_state, opuslib_next.api.ctl.get_pitch)
|
|
210
|
+
|
|
211
|
+
pitch = property(_get_pitch)
|
|
212
|
+
|
|
213
|
+
_get_lsb_depth = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
214
|
+
self.encoder_state, opuslib_next.api.ctl.get_lsb_depth)
|
|
215
|
+
|
|
216
|
+
_set_lsb_depth = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
217
|
+
self.encoder_state, opuslib_next.api.ctl.set_lsb_depth, x)
|
|
218
|
+
|
|
219
|
+
lsb_depth = property(_get_lsb_depth, _set_lsb_depth)
|
|
220
|
+
|
|
221
|
+
_get_complexity = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
222
|
+
self.encoder_state, opuslib_next.api.ctl.get_complexity)
|
|
223
|
+
|
|
224
|
+
_set_complexity = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
225
|
+
self.encoder_state, opuslib_next.api.ctl.set_complexity, x)
|
|
226
|
+
|
|
227
|
+
complexity = property(_get_complexity, _set_complexity)
|
|
228
|
+
|
|
229
|
+
_get_bitrate = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
230
|
+
self.encoder_state, opuslib_next.api.ctl.get_bitrate)
|
|
231
|
+
|
|
232
|
+
_set_bitrate = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
233
|
+
self.encoder_state, opuslib_next.api.ctl.set_bitrate, x)
|
|
234
|
+
|
|
235
|
+
bitrate = property(_get_bitrate, _set_bitrate)
|
|
236
|
+
|
|
237
|
+
_get_vbr = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
238
|
+
self.encoder_state, opuslib_next.api.ctl.get_vbr)
|
|
239
|
+
|
|
240
|
+
_set_vbr = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
241
|
+
self.encoder_state, opuslib_next.api.ctl.set_vbr, x)
|
|
242
|
+
|
|
243
|
+
vbr = property(_get_vbr, _set_vbr)
|
|
244
|
+
|
|
245
|
+
_get_vbr_constraint = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
246
|
+
self.encoder_state, opuslib_next.api.ctl.get_vbr_constraint)
|
|
247
|
+
|
|
248
|
+
_set_vbr_constraint = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
249
|
+
self.encoder_state, opuslib_next.api.ctl.set_vbr_constraint, x)
|
|
250
|
+
|
|
251
|
+
vbr_constraint = property(_get_vbr_constraint, _set_vbr_constraint)
|
|
252
|
+
|
|
253
|
+
_get_force_channels = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
254
|
+
self.encoder_state, opuslib_next.api.ctl.get_force_channels)
|
|
255
|
+
|
|
256
|
+
_set_force_channels = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
257
|
+
self.encoder_state, opuslib_next.api.ctl.set_force_channels, x)
|
|
258
|
+
|
|
259
|
+
force_channels = property(_get_force_channels, _set_force_channels)
|
|
260
|
+
|
|
261
|
+
_get_max_bandwidth = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
262
|
+
self.encoder_state, opuslib_next.api.ctl.get_max_bandwidth)
|
|
263
|
+
|
|
264
|
+
_set_max_bandwidth = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
265
|
+
self.encoder_state, opuslib_next.api.ctl.set_max_bandwidth, x)
|
|
266
|
+
|
|
267
|
+
max_bandwidth = property(_get_max_bandwidth, _set_max_bandwidth)
|
|
268
|
+
|
|
269
|
+
_set_bandwidth = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
270
|
+
self.encoder_state, opuslib_next.api.ctl.set_bandwidth, x)
|
|
271
|
+
|
|
272
|
+
bandwidth = property(None, _set_bandwidth)
|
|
273
|
+
|
|
274
|
+
_get_signal = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
275
|
+
self.encoder_state, opuslib_next.api.ctl.get_signal)
|
|
276
|
+
|
|
277
|
+
_set_signal = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
278
|
+
self.encoder_state, opuslib_next.api.ctl.set_signal, x)
|
|
279
|
+
|
|
280
|
+
signal = property(_get_signal, _set_signal)
|
|
281
|
+
|
|
282
|
+
_get_application = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
283
|
+
self.encoder_state, opuslib_next.api.ctl.get_application)
|
|
284
|
+
|
|
285
|
+
_set_application = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
286
|
+
self.encoder_state, opuslib_next.api.ctl.set_application, x)
|
|
287
|
+
|
|
288
|
+
application = property(_get_application, _set_application)
|
|
289
|
+
|
|
290
|
+
_get_sample_rate = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
291
|
+
self.encoder_state, opuslib_next.api.ctl.get_sample_rate)
|
|
292
|
+
|
|
293
|
+
sample_rate = property(_get_sample_rate)
|
|
294
|
+
|
|
295
|
+
_get_lookahead = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
296
|
+
self.encoder_state, opuslib_next.api.ctl.get_lookahead)
|
|
297
|
+
|
|
298
|
+
lookahead = property(_get_lookahead)
|
|
299
|
+
|
|
300
|
+
_get_inband_fec = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
301
|
+
self.encoder_state, opuslib_next.api.ctl.get_inband_fec)
|
|
302
|
+
|
|
303
|
+
_set_inband_fec = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
304
|
+
self.encoder_state, opuslib_next.api.ctl.set_inband_fec)
|
|
305
|
+
|
|
306
|
+
inband_fec = property(_get_inband_fec, _set_inband_fec)
|
|
307
|
+
|
|
308
|
+
_get_packet_loss_perc = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
309
|
+
self.encoder_state, opuslib_next.api.ctl.get_packet_loss_perc)
|
|
310
|
+
|
|
311
|
+
_set_packet_loss_perc = \
|
|
312
|
+
lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
313
|
+
self.encoder_state, opuslib_next.api.ctl.set_packet_loss_perc, x)
|
|
314
|
+
|
|
315
|
+
packet_loss_perc = property(_get_packet_loss_perc, _set_packet_loss_perc)
|
|
316
|
+
|
|
317
|
+
_get_dtx = lambda self: opuslib_next.api.encoder.encoder_ctl(
|
|
318
|
+
self.encoder_state, opuslib_next.api.ctl.get_dtx)
|
|
319
|
+
|
|
320
|
+
_set_dtx = lambda self, x: opuslib_next.api.encoder.encoder_ctl(
|
|
321
|
+
self.encoder_state, opuslib_next.api.ctl.set_dtx, x)
|
|
322
|
+
|
|
323
|
+
dtx = property(_get_dtx, _set_dtx)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
OpusLib constants.
|
|
6
|
+
Matches to `opus_defines.h`
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
10
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
11
|
+
__license__ = 'BSD 3-Clause License'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# No Error
|
|
15
|
+
OK = 0
|
|
16
|
+
|
|
17
|
+
# One or more invalid/out of range arguments
|
|
18
|
+
BAD_ARG = -1
|
|
19
|
+
|
|
20
|
+
# The mode struct passed is invalid
|
|
21
|
+
BUFFER_TOO_SMALL = -2
|
|
22
|
+
|
|
23
|
+
# An internal error was detected
|
|
24
|
+
INTERNAL_ERROR = -3
|
|
25
|
+
|
|
26
|
+
# The compressed data passed is corrupted
|
|
27
|
+
INVALID_PACKET = -4
|
|
28
|
+
|
|
29
|
+
# Invalid/unsupported request number
|
|
30
|
+
UNIMPLEMENTED = -5
|
|
31
|
+
|
|
32
|
+
# An encoder or decoder structure is invalid or already freed
|
|
33
|
+
INVALID_STATE = -6
|
|
34
|
+
|
|
35
|
+
# Memory allocation has failed
|
|
36
|
+
ALLOC_FAIL = -7
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Pre-defined values for CTL interface
|
|
40
|
+
|
|
41
|
+
APPLICATION_VOIP = 2048
|
|
42
|
+
APPLICATION_AUDIO = 2049
|
|
43
|
+
APPLICATION_RESTRICTED_LOWDELAY = 2051
|
|
44
|
+
|
|
45
|
+
SIGNAL_VOICE = 3001
|
|
46
|
+
SIGNAL_MUSIC = 3002
|
|
47
|
+
|
|
48
|
+
# Values for the various encoder CTLs
|
|
49
|
+
|
|
50
|
+
SET_APPLICATION_REQUEST = 4000
|
|
51
|
+
GET_APPLICATION_REQUEST = 4001
|
|
52
|
+
SET_BITRATE_REQUEST = 4002
|
|
53
|
+
GET_BITRATE_REQUEST = 4003
|
|
54
|
+
SET_MAX_BANDWIDTH_REQUEST = 4004
|
|
55
|
+
GET_MAX_BANDWIDTH_REQUEST = 4005
|
|
56
|
+
SET_VBR_REQUEST = 4006
|
|
57
|
+
GET_VBR_REQUEST = 4007
|
|
58
|
+
SET_BANDWIDTH_REQUEST = 4008
|
|
59
|
+
GET_BANDWIDTH_REQUEST = 4009
|
|
60
|
+
SET_COMPLEXITY_REQUEST = 4010
|
|
61
|
+
GET_COMPLEXITY_REQUEST = 4011
|
|
62
|
+
SET_INBAND_FEC_REQUEST = 4012
|
|
63
|
+
GET_INBAND_FEC_REQUEST = 4013
|
|
64
|
+
SET_PACKET_LOSS_PERC_REQUEST = 4014
|
|
65
|
+
GET_PACKET_LOSS_PERC_REQUEST = 4015
|
|
66
|
+
SET_DTX_REQUEST = 4016
|
|
67
|
+
GET_DTX_REQUEST = 4017
|
|
68
|
+
SET_VBR_CONSTRAINT_REQUEST = 4020
|
|
69
|
+
GET_VBR_CONSTRAINT_REQUEST = 4021
|
|
70
|
+
SET_FORCE_CHANNELS_REQUEST = 4022
|
|
71
|
+
GET_FORCE_CHANNELS_REQUEST = 4023
|
|
72
|
+
SET_SIGNAL_REQUEST = 4024
|
|
73
|
+
GET_SIGNAL_REQUEST = 4025
|
|
74
|
+
GET_LOOKAHEAD_REQUEST = 4027
|
|
75
|
+
RESET_STATE = 4028
|
|
76
|
+
GET_SAMPLE_RATE_REQUEST = 4029
|
|
77
|
+
GET_FINAL_RANGE_REQUEST = 4031
|
|
78
|
+
GET_PITCH_REQUEST = 4033
|
|
79
|
+
SET_GAIN_REQUEST = 4034
|
|
80
|
+
GET_GAIN_REQUEST = 4045 # Should have been 4035
|
|
81
|
+
SET_LSB_DEPTH_REQUEST = 4036
|
|
82
|
+
GET_LSB_DEPTH_REQUEST = 4037
|
|
83
|
+
GET_LAST_PACKET_DURATION_REQUEST = 4039
|
|
84
|
+
SET_EXPERT_FRAME_DURATION_REQUEST = 4040
|
|
85
|
+
GET_EXPERT_FRAME_DURATION_REQUEST = 4041
|
|
86
|
+
SET_PREDICTION_DISABLED_REQUEST = 4042
|
|
87
|
+
GET_PREDICTION_DISABLED_REQUEST = 4043
|
|
88
|
+
|
|
89
|
+
# Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST
|
|
90
|
+
|
|
91
|
+
AUTO = -1000
|
|
92
|
+
|
|
93
|
+
BANDWIDTH_NARROWBAND = 1101
|
|
94
|
+
BANDWIDTH_MEDIUMBAND = 1102
|
|
95
|
+
BANDWIDTH_WIDEBAND = 1103
|
|
96
|
+
BANDWIDTH_SUPERWIDEBAND = 1104
|
|
97
|
+
BANDWIDTH_FULLBAND = 1105
|
|
98
|
+
|
|
99
|
+
APPLICATION_TYPES_MAP = {
|
|
100
|
+
'voip': APPLICATION_VOIP,
|
|
101
|
+
'audio': APPLICATION_AUDIO,
|
|
102
|
+
'restricted_lowdelay': APPLICATION_RESTRICTED_LOWDELAY,
|
|
103
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Exceptions for OpusLib.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import typing
|
|
9
|
+
|
|
10
|
+
import opuslib_next.api.info
|
|
11
|
+
|
|
12
|
+
__author__ = 'kalicyh <kalicyh@qq.com>'
|
|
13
|
+
__copyright__ = 'Copyright (c) 2025, Kalicyh'
|
|
14
|
+
__license__ = 'BSD 3-Clause License'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class OpusError(Exception):
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
Generic handler for OpusLib errors from C library.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, code: int) -> None:
|
|
24
|
+
self.code = code
|
|
25
|
+
super().__init__()
|
|
26
|
+
|
|
27
|
+
# FIXME: Remove typing.Any once we have a stub for ctypes
|
|
28
|
+
def __str__(self) -> typing.Union[str, typing.Any]:
|
|
29
|
+
return str(opuslib_next.api.info.strerror(self.code))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2025, Kalicyh
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
* Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
* Neither the name of the SvartalF nor the
|
|
12
|
+
names of its contributors may be used to endorse or promote products
|
|
13
|
+
derived from this software without specific prior written permission.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: opuslib_next
|
|
3
|
+
Version: 1.1.2
|
|
4
|
+
Summary: Python bindings to the libopus, IETF low-delay audio codec
|
|
5
|
+
Home-page: https://github.com/kalicyh/opuslib-next
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Author: kalicyh
|
|
8
|
+
Author-email: kalicyh@qq.com
|
|
9
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
+
Classifier: Programming Language :: Python :: 2
|
|
11
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.4
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Project-URL: Documentation, https://github.com/kalicyh/opuslib-next
|
|
24
|
+
Project-URL: Repository, https://github.com/kalicyh/opuslib-next/issues
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# **python-opus**
|
|
28
|
+
|
|
29
|
+
Python bindings to the libopus, IETF low-delay audio codec
|
|
30
|
+
|
|
31
|
+
**Usage**
|
|
32
|
+
------------
|
|
33
|
+
|
|
34
|
+
The usage remains the same as the original `opuslib`, except that now the library is called `opuslib_next`. You can install it using:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install opuslib-next
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After installation, you can import and use it like before:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import opuslib_next
|
|
44
|
+
# your code here
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**About the Fork**
|
|
48
|
+
------------
|
|
49
|
+
|
|
50
|
+
Since the original author of [opuslib](https://github.com/orion-labs/opuslib) has not maintained or updated the library for a long time, I decided to fork and update it to fix issues.
|
|
51
|
+
|
|
52
|
+
**Contributing**
|
|
53
|
+
-------------
|
|
54
|
+
|
|
55
|
+
If you want to contribute, please follow the [pep8](http://www.python.org/dev/peps/pep-0008/) guideline and include tests.
|
|
56
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
opuslib_next/__init__.py,sha256=B_pXMfh6GCZuzDtbWvlCZATxa4oU6t0RlEKjUgvBPfs,636
|
|
2
|
+
opuslib_next/api/__init__.py,sha256=FGk-tHC8RvoGyMiFIPMA4RKHYc0M0YKArDCK3uBMtKM,645
|
|
3
|
+
opuslib_next/api/ctl.py,sha256=rsfrTABTf98Xg094O1c-587qNO79GuIrVovcltmPkMY,5787
|
|
4
|
+
opuslib_next/api/decoder.py,sha256=VF0SJhdiNVMhk7aqWxM_DJTjRDH7e1eVmfbnmf6mXcs,9034
|
|
5
|
+
opuslib_next/api/encoder.py,sha256=tFlqMocSelzqOEktbXJd1dE_UPnawpltE0B4pmsyXis,5300
|
|
6
|
+
opuslib_next/api/info.py,sha256=JdTmzdmbSpSoiaUwEYOwP4dtqQo4GnQz9ocbyYB0zv4,709
|
|
7
|
+
opuslib_next/classes.py,sha256=qrcz9K_awyuqJ5zrEofqus_n0pXjyfE5bWImxtY6m-0,10823
|
|
8
|
+
opuslib_next/constants.py,sha256=nDI026Bb2ET-6qtKWJJNkVZOQSA4X2KNRTJQihNAJBQ,2425
|
|
9
|
+
opuslib_next/exceptions.py,sha256=s91ahMA9HClqlodvNGwSOXkx4LKurbmYOpJRJybef5w,627
|
|
10
|
+
opuslib_next-1.1.2.dist-info/LICENSE,sha256=QYIcoymg8sVhmqq7eJuB1Aju-hcYLRZbkVg-vftr9Rs,1487
|
|
11
|
+
opuslib_next-1.1.2.dist-info/METADATA,sha256=zJ2S8BqoHAfJIiW7Z6231dacNrKJSx20Ncp4S8AYiiY,1892
|
|
12
|
+
opuslib_next-1.1.2.dist-info/WHEEL,sha256=iAMR_6Qh95yyjYIwRxyjpiFq4FhDPemrEV-SyWIQB3U,92
|
|
13
|
+
opuslib_next-1.1.2.dist-info/RECORD,,
|