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.
@@ -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)
@@ -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()"
@@ -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'
@@ -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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.1
3
+ Root-Is-Purelib: true
4
+ Tag: py2.py3-none-any