pax_global_header00006660000000000000000000000064132273127150014515gustar00rootroot0000000000000052 comment=5fa7b628d2f9c191d435421d576821d6a0c2b6d3 orion-labs-opuslib-43410b7/000077500000000000000000000000001322731271500155035ustar00rootroot00000000000000orion-labs-opuslib-43410b7/.gitignore000066400000000000000000000003611322731271500174730ustar00rootroot00000000000000*.py[co] # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg .fuse_* orion-labs-opuslib-43410b7/.travis.yml000066400000000000000000000013451322731271500176170ustar00rootroot00000000000000language: python python: - '2.5' - '2.6' - '2.7' - 'pypy' - '3.6' # Compile C library before_install: - 'pushd .' - 'git clone -b v1.0.1 --depth 1 git://git.xiph.org/opus.git /tmp/opus' - 'cd /tmp/opus && ./autogen.sh && ./configure' - 'cd /tmp/opus && make && sudo make install' - 'popd' # Install test dependencies install: - 'sudo pip install pep8 --use-mirrors' before_script: - 'pep8 --ignore=E501,E225 opus' - 'if [[ $TRAVIS_PYTHON_VERSION == "2.5" ]]; then sudo pip install unittest2 --use-mirrors; fi' - 'if [[ $TRAVIS_PYTHON_VERSION == "2.6" ]]; then sudo pip install unittest2 --use-mirrors; fi' script: - 'LD_PRELOAD=/usr/local/lib/libopus.so python setup.py test' orion-labs-opuslib-43410b7/LICENSE000066400000000000000000000027201322731271500165110ustar00rootroot00000000000000Copyright (c) 2012, SvartalF All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the SvartalF nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.orion-labs-opuslib-43410b7/Makefile000066400000000000000000000024311322731271500171430ustar00rootroot00000000000000# Makefile for opuslib. # # Author:: Никита Кузнецов # Copyright:: Copyright (c) 2012, SvartalF # License:: BSD 3-Clause License # .DEFAULT_GOAL := all all: develop develop: python setup.py develop install: python setup.py install install_requirements_test: pip install -r requirements_test.txt uninstall: pip uninstall -y opuslib reinstall: uninstall develop remember_test: @echo @echo "Hello from the Makefile..." @echo "Don't forget to run: 'make install_requirements_test'" @echo clean: rm -rf *.egg* build dist *.py[oc] */*.py[co] cover doctest_pypi.cfg \ nosetests.xml pylint.log *.egg output.xml flake8.log tests.log \ test-result.xml htmlcov fab.log *.deb .coverage publish: python setup.py sdist twine upload dist/* nosetests: remember_test python setup.py nosetests flake8: pep8 pep8: remember_test flake8 --ignore=E402,E731 --max-complexity 12 --exit-zero opuslib/*.py \ opuslib/api/*.py tests/*.py pylint: lint lint: remember_test pylint --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" \ -r n opuslib/*.py opuslib/api/*.py tests/*.py || exit 0 test: lint pep8 mypy nosetests mypy: mypy --strict . docker_build: docker build . checkmetadata: python setup.py check -s --metadata --restructuredtext orion-labs-opuslib-43410b7/README.md000066400000000000000000000006661322731271500167720ustar00rootroot00000000000000python-opus =========== Python bindings to the libopus, IETF low-delay audio codec Testing -------- Run tests with a python setup.py test command or look for [Travis build logs](http://travis-ci.org/#!/svartalf/python-opus). ![](https://secure.travis-ci.org/svartalf/python-opus.png) Contributing ------------- If you want to contribute, follow the [pep8](http://www.python.org/dev/peps/pep-0008/) guideline, and include the tests. orion-labs-opuslib-43410b7/opuslib/000077500000000000000000000000001322731271500171605ustar00rootroot00000000000000orion-labs-opuslib-43410b7/opuslib/__init__.py000066400000000000000000000012541322731271500212730ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # OpusLib Python Module. """ OpusLib Python Module. ~~~~~~~ Python bindings to the libopus, IETF low-delay audio codec :author: Никита Кузнецов :copyright: Copyright (c) 2012, SvartalF :license: BSD 3-Clause License :source: """ from .exceptions import OpusError # NOQA from .constants import * # NOQA from .constants import OK, APPLICATION_TYPES_MAP # NOQA from .classes import Encoder, Decoder # NOQA __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' orion-labs-opuslib-43410b7/opuslib/api/000077500000000000000000000000001322731271500177315ustar00rootroot00000000000000orion-labs-opuslib-43410b7/opuslib/api/__init__.py000066400000000000000000000012401322731271500220370ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=invalid-name # """OpusLib Package.""" import ctypes # type: ignore from ctypes.util import find_library # type: ignore __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' lib_location = find_library('opus') if lib_location is None: raise Exception( 'Could not find Opus library. Make sure it is installed.') libopus = ctypes.CDLL(lib_location) c_int_pointer = ctypes.POINTER(ctypes.c_int) c_int16_pointer = ctypes.POINTER(ctypes.c_int16) c_float_pointer = ctypes.POINTER(ctypes.c_float) orion-labs-opuslib-43410b7/opuslib/api/ctl.py000066400000000000000000000127441322731271500210750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=invalid-name """CTL macros rewritten to Python Usage example: >>> import opuslib.api.decoder >>> import opuslib.api.ctl >>> dec = opuslib.api.decoder.create_state(48000, 2) >>> opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.set_gain, -15) >>> gain_value = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_gain) """ __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' import ctypes # type: ignore import opuslib.api import opuslib.exceptions def query(request): """Query encoder/decoder with a request value""" def inner(func, obj): result_code = func(obj, request) if result_code is not opuslib.OK: raise opuslib.exceptions.OpusError(result_code) return result_code return inner def get(request, result_type): """Get CTL value from a encoder/decoder""" def inner(func, obj): result = result_type() result_code = func(obj, request, ctypes.byref(result)) if result_code is not opuslib.OK: raise opuslib.exceptions.OpusError(result_code) return result.value return inner def ctl_set(request): """Set new CTL value to a encoder/decoder""" def inner(func, obj, value): result_code = func(obj, request, value) if result_code is not opuslib.OK: raise opuslib.exceptions.OpusError(result_code) return inner # # Generic CTLs # # Resets the codec state to be equivalent to a freshly initialized state reset_state = query(opuslib.RESET_STATE) # NOQA # Gets the final state of the codec's entropy coder get_final_range = get( opuslib.GET_FINAL_RANGE_REQUEST, ctypes.c_uint ) # Gets the encoder's configured bandpass or the decoder's last bandpass get_bandwidth = get(opuslib.GET_BANDWIDTH_REQUEST, ctypes.c_int) # Gets the pitch of the last decoded frame, if available get_pitch = get(opuslib.GET_PITCH_REQUEST, ctypes.c_int) # Configures the depth of signal being encoded set_lsb_depth = ctl_set(opuslib.SET_LSB_DEPTH_REQUEST) # Gets the encoder's configured signal depth get_lsb_depth = get(opuslib.GET_LSB_DEPTH_REQUEST, ctypes.c_int) # # Decoder related CTLs # # Gets the decoder's configured gain adjustment get_gain = get(opuslib.GET_GAIN_REQUEST, ctypes.c_int) # Configures decoder gain adjustment set_gain = ctl_set(opuslib.SET_GAIN_REQUEST) # # Encoder related CTLs # # Configures the encoder's computational complexity set_complexity = ctl_set(opuslib.SET_COMPLEXITY_REQUEST) # Gets the encoder's complexity configuration get_complexity = get( opuslib.GET_COMPLEXITY_REQUEST, ctypes.c_int) # Configures the bitrate in the encoder set_bitrate = ctl_set(opuslib.SET_BITRATE_REQUEST) # Gets the encoder's bitrate configuration get_bitrate = get(opuslib.GET_BITRATE_REQUEST, ctypes.c_int) # Enables or disables variable bitrate (VBR) in the encoder set_vbr = ctl_set(opuslib.SET_VBR_REQUEST) # Determine if variable bitrate (VBR) is enabled in the encoder get_vbr = get(opuslib.GET_VBR_REQUEST, ctypes.c_int) # Enables or disables constrained VBR in the encoder set_vbr_constraint = ctl_set(opuslib.SET_VBR_CONSTRAINT_REQUEST) # Determine if constrained VBR is enabled in the encoder get_vbr_constraint = get( opuslib.GET_VBR_CONSTRAINT_REQUEST, ctypes.c_int) # Configures mono/stereo forcing in the encoder set_force_channels = ctl_set(opuslib.SET_FORCE_CHANNELS_REQUEST) # Gets the encoder's forced channel configuration get_force_channels = get( opuslib.GET_FORCE_CHANNELS_REQUEST, ctypes.c_int) # Configures the maximum bandpass that the encoder will select automatically set_max_bandwidth = ctl_set(opuslib.SET_MAX_BANDWIDTH_REQUEST) # Gets the encoder's configured maximum allowed bandpass get_max_bandwidth = get( opuslib.GET_MAX_BANDWIDTH_REQUEST, ctypes.c_int) # Sets the encoder's bandpass to a specific value set_bandwidth = ctl_set(opuslib.SET_BANDWIDTH_REQUEST) # Configures the type of signal being encoded set_signal = ctl_set(opuslib.SET_SIGNAL_REQUEST) # Gets the encoder's configured signal type get_signal = get(opuslib.GET_SIGNAL_REQUEST, ctypes.c_int) # Configures the encoder's intended application set_application = ctl_set(opuslib.SET_APPLICATION_REQUEST) # Gets the encoder's configured application get_application = get( opuslib.GET_APPLICATION_REQUEST, ctypes.c_int) # Gets the sampling rate the encoder or decoder was initialized with get_sample_rate = get( opuslib.GET_SAMPLE_RATE_REQUEST, ctypes.c_int) # Gets the total samples of delay added by the entire codec get_lookahead = get(opuslib.GET_LOOKAHEAD_REQUEST, ctypes.c_int) # Configures the encoder's use of inband forward error correction (FEC) set_inband_fec = ctl_set(opuslib.SET_INBAND_FEC_REQUEST) # Gets encoder's configured use of inband forward error correction get_inband_fec = get( opuslib.GET_INBAND_FEC_REQUEST, ctypes.c_int) # Configures the encoder's expected packet loss percentage set_packet_loss_perc = ctl_set( opuslib.SET_PACKET_LOSS_PERC_REQUEST) # Gets the encoder's configured packet loss percentage get_packet_loss_perc = get( opuslib.GET_PACKET_LOSS_PERC_REQUEST, ctypes.c_int ) # Configures the encoder's use of discontinuous transmission (DTX) set_dtx = ctl_set(opuslib.SET_DTX_REQUEST) # Gets encoder's configured use of discontinuous transmission get_dtx = get(opuslib.GET_DTX_REQUEST, ctypes.c_int) # # Other stuff # unimplemented = query(opuslib.UNIMPLEMENTED) orion-labs-opuslib-43410b7/opuslib/api/decoder.py000066400000000000000000000212651322731271500217160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=invalid-name,too-few-public-methods # """ CTypes mapping between libopus functions and Python. """ import array import ctypes # type: ignore import typing import opuslib import opuslib.api __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class Decoder(ctypes.Structure): """Opus decoder state. This contains the complete state of an Opus decoder. """ pass DecoderPointer = ctypes.POINTER(Decoder) libopus_get_size = opuslib.api.libopus.opus_decoder_get_size libopus_get_size.argtypes = (ctypes.c_int,) libopus_get_size.restype = ctypes.c_int libopus_get_size.__doc__ = 'Gets the size of an OpusDecoder structure' libopus_create = opuslib.api.libopus.opus_decoder_create libopus_create.argtypes = ( ctypes.c_int, ctypes.c_int, opuslib.api.c_int_pointer ) libopus_create.restype = DecoderPointer def create_state(fs: int, channels: int) -> ctypes.Structure: """ Allocates and initializes a decoder state. Wrapper for C opus_decoder_create() `fs` must be one of 8000, 12000, 16000, 24000, or 48000. Internally Opus stores data at 48000 Hz, so that should be the default value for Fs. However, the decoder can efficiently decode to buffers at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use data at the full sample rate, or knows the compressed data doesn't use the full frequency range, it can request decoding at a reduced rate. Likewise, the decoder is capable of filling in either mono or interleaved stereo pcm buffers, at the caller's request. :param fs: Sample rate to decode at (Hz). :param int: Number of channels (1 or 2) to decode. """ result_code = ctypes.c_int() decoder_state = libopus_create( fs, channels, ctypes.byref(result_code) ) if result_code.value is not 0: raise opuslib.exceptions.OpusError(result_code.value) return decoder_state libopus_packet_get_bandwidth = opuslib.api.libopus.opus_packet_get_bandwidth # `argtypes` must be a sequence (,) of types! libopus_packet_get_bandwidth.argtypes = (ctypes.c_char_p,) libopus_packet_get_bandwidth.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def packet_get_bandwidth(data: bytes) -> typing.Union[int, typing.Any]: """Gets the bandwidth of an Opus packet.""" data_pointer = ctypes.c_char_p(data) result = libopus_packet_get_bandwidth(data_pointer) if result < 0: raise opuslib.exceptions.OpusError(result) return result libopus_packet_get_nb_channels = opuslib.api.libopus.opus_packet_get_nb_channels # NOQA # `argtypes` must be a sequence (,) of types! libopus_packet_get_nb_channels.argtypes = (ctypes.c_char_p,) libopus_packet_get_nb_channels.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def packet_get_nb_channels(data: bytes) -> typing.Union[int, typing.Any]: """Gets the number of channels from an Opus packet""" data_pointer = ctypes.c_char_p(data) result = libopus_packet_get_nb_channels(data_pointer) if result < 0: raise opuslib.exceptions.OpusError(result) return result libopus_packet_get_nb_frames = opuslib.api.libopus.opus_packet_get_nb_frames libopus_packet_get_nb_frames.argtypes = (ctypes.c_char_p, ctypes.c_int) libopus_packet_get_nb_frames.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def packet_get_nb_frames( data: bytes, length: typing.Optional[int] = None ) -> typing.Union[int, typing.Any]: """Gets the number of frames in an Opus packet""" data_pointer = ctypes.c_char_p(data) if length is None: length = len(data) result = libopus_packet_get_nb_frames(data_pointer, ctypes.c_int(length)) if result < 0: raise opuslib.exceptions.OpusError(result) return result libopus_packet_get_samples_per_frame = \ opuslib.api.libopus.opus_packet_get_samples_per_frame libopus_packet_get_samples_per_frame.argtypes = (ctypes.c_char_p, ctypes.c_int) libopus_packet_get_samples_per_frame.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def packet_get_samples_per_frame( data: bytes, fs: int ) -> typing.Union[int, typing.Any]: """Gets the number of samples per frame from an Opus packet""" data_pointer = ctypes.c_char_p(data) result = libopus_packet_get_nb_frames(data_pointer, ctypes.c_int(fs)) if result < 0: raise opuslib.exceptions.OpusError(result) return result libopus_get_nb_samples = opuslib.api.libopus.opus_decoder_get_nb_samples libopus_get_nb_samples.argtypes = ( DecoderPointer, ctypes.c_char_p, ctypes.c_int32 ) libopus_get_nb_samples.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def get_nb_samples( decoder_state: ctypes.Structure, packet: bytes, length: int ) -> typing.Union[int, typing.Any]: """ Gets the number of samples of an Opus packet. Parameters [in] dec OpusDecoder*: Decoder state [in] packet char*: Opus packet [in] len opus_int32: Length of packet Returns Number of samples Return values OPUS_BAD_ARG Insufficient data was passed to the function OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type """ result = libopus_get_nb_samples(decoder_state, packet, length) if result < 0: raise opuslib.exceptions.OpusError(result) return result libopus_decode = opuslib.api.libopus.opus_decode libopus_decode.argtypes = ( DecoderPointer, ctypes.c_char_p, ctypes.c_int32, opuslib.api.c_int16_pointer, ctypes.c_int, ctypes.c_int ) libopus_decode.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def decode( # pylint: disable=too-many-arguments decoder_state: ctypes.Structure, opus_data: bytes, length: int, frame_size: int, decode_fec: bool, channels: int = 2 ) -> typing.Union[bytes, typing.Any]: """ Decode an Opus Frame to PCM. Unlike the `opus_decode` function , this function takes an additional parameter `channels`, which indicates the number of channels in the frame. """ _decode_fec = int(decode_fec) result: int = 0 pcm_size = frame_size * channels * ctypes.sizeof(ctypes.c_int16) pcm = (ctypes.c_int16 * pcm_size)() pcm_pointer = ctypes.cast(pcm, opuslib.api.c_int16_pointer) result = libopus_decode( decoder_state, opus_data, length, pcm_pointer, frame_size, _decode_fec ) if result < 0: raise opuslib.exceptions.OpusError(result) return array.array('h', pcm_pointer[:result * channels]).tobytes() libopus_decode_float = opuslib.api.libopus.opus_decode_float libopus_decode_float.argtypes = ( DecoderPointer, ctypes.c_char_p, ctypes.c_int32, opuslib.api.c_float_pointer, ctypes.c_int, ctypes.c_int ) libopus_decode_float.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def decode_float( # pylint: disable=too-many-arguments decoder_state: ctypes.Structure, opus_data: bytes, length: int, frame_size: int, decode_fec: bool, channels: int = 2 ) -> typing.Union[bytes, typing.Any]: """ Decode an Opus Frame. Unlike the `opus_decode` function , this function takes an additional parameter `channels`, which indicates the number of channels in the frame. """ _decode_fec = int(decode_fec) pcm_size = frame_size * channels * ctypes.sizeof(ctypes.c_float) pcm = (ctypes.c_float * pcm_size)() pcm_pointer = ctypes.cast(pcm, opuslib.api.c_float_pointer) result = libopus_decode_float( decoder_state, opus_data, length, pcm_pointer, frame_size, _decode_fec ) if result < 0: raise opuslib.exceptions.OpusError(result) return array.array('f', pcm[:result * channels]).tobytes() libopus_ctl = opuslib.api.libopus.opus_decoder_ctl libopus_ctl.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def decoder_ctl( decoder_state: ctypes.Structure, request, value=None ) -> typing.Union[int, typing.Any]: if value is not None: return request(libopus_ctl, decoder_state, value) return request(libopus_ctl, decoder_state) destroy = opuslib.api.libopus.opus_decoder_destroy destroy.argtypes = (DecoderPointer,) destroy.restype = None destroy.__doc__ = 'Frees an OpusDecoder allocated by opus_decoder_create()' orion-labs-opuslib-43410b7/opuslib/api/encoder.py000066400000000000000000000122601322731271500217230ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=invalid-name # """ CTypes mapping between libopus functions and Python. """ import array import ctypes # type: ignore import typing import opuslib import opuslib.api __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class Encoder(ctypes.Structure): # pylint: disable=too-few-public-methods """Opus encoder state. This contains the complete state of an Opus encoder. """ pass EncoderPointer = ctypes.POINTER(Encoder) libopus_get_size = opuslib.api.libopus.opus_encoder_get_size libopus_get_size.argtypes = (ctypes.c_int,) # must be sequence (,) of types! libopus_get_size.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def get_size(channels: int) -> typing.Union[int, typing.Any]: """Gets the size of an OpusEncoder structure.""" if channels not in (1, 2): raise ValueError('Wrong channels value. Must be equal to 1 or 2') return libopus_get_size(channels) libopus_create = opuslib.api.libopus.opus_encoder_create libopus_create.argtypes = ( ctypes.c_int, ctypes.c_int, ctypes.c_int, opuslib.api.c_int_pointer ) libopus_create.restype = EncoderPointer def create_state(fs: int, channels: int, application: int) -> ctypes.Structure: """Allocates and initializes an encoder state.""" result_code = ctypes.c_int() result = libopus_create( fs, channels, application, ctypes.byref(result_code) ) if result_code.value is not opuslib.OK: raise opuslib.OpusError(result_code.value) return result libopus_ctl = opuslib.api.libopus.opus_encoder_ctl libopus_ctl.restype = ctypes.c_int # FIXME: Remove typing.Any once we have a stub for ctypes def encoder_ctl( encoder_state: ctypes.Structure, request, value=None ) -> typing.Union[int, typing.Any]: if value is not None: return request(libopus_ctl, encoder_state, value) return request(libopus_ctl, encoder_state) libopus_encode = opuslib.api.libopus.opus_encode libopus_encode.argtypes = ( EncoderPointer, opuslib.api.c_int16_pointer, ctypes.c_int, ctypes.c_char_p, ctypes.c_int32 ) libopus_encode.restype = ctypes.c_int32 # FIXME: Remove typing.Any once we have a stub for ctypes def encode( encoder_state: ctypes.Structure, pcm_data: bytes, frame_size: int, max_data_bytes: int ) -> typing.Union[bytes, typing.Any]: """ Encodes an Opus Frame. Returns string output payload. Parameters: [in] st OpusEncoder*: Encoder state [in] pcm opus_int16*: Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(opus_int16) [in] frame_size int: Number of samples per channel in the input signal. This must be an Opus frame size for the encoder's sampling rate. For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10 ms (480 samples at 48 kHz) will prevent the encoder from using the LPC or hybrid modes. [out] data unsigned char*: Output payload. This must contain storage for at least max_data_bytes. [in] max_data_bytes opus_int32: Size of the allocated memory for the output payload. This may be used to impose an upper limit on the instant bitrate, but should not be used as the only bitrate control. Use OPUS_SET_BITRATE to control the bitrate. """ pcm_pointer = ctypes.cast(pcm_data, opuslib.api.c_int16_pointer) opus_data = (ctypes.c_char * max_data_bytes)() result = libopus_encode( encoder_state, pcm_pointer, frame_size, opus_data, max_data_bytes ) if result < 0: raise opuslib.OpusError( 'Opus Encoder returned result="{}"'.format(result)) return array.array('b', opus_data[:result]).tobytes() libopus_encode_float = opuslib.api.libopus.opus_encode_float libopus_encode_float.argtypes = ( EncoderPointer, opuslib.api.c_float_pointer, ctypes.c_int, ctypes.c_char_p, ctypes.c_int32 ) libopus_encode_float.restype = ctypes.c_int32 # FIXME: Remove typing.Any once we have a stub for ctypes def encode_float( encoder_state: ctypes.Structure, pcm_data: bytes, frame_size: int, max_data_bytes: int ) -> typing.Union[bytes, typing.Any]: """Encodes an Opus frame from floating point input""" pcm_pointer = ctypes.cast(pcm_data, opuslib.api.c_float_pointer) opus_data = (ctypes.c_char * max_data_bytes)() result = libopus_encode_float( encoder_state, pcm_pointer, frame_size, opus_data, max_data_bytes ) if result < 0: raise opuslib.OpusError( 'Encoder returned result="{}"'.format(result)) return array.array('b', opus_data[:result]).tobytes() destroy = opuslib.api.libopus.opus_encoder_destroy destroy.argtypes = (EncoderPointer,) # must be sequence (,) of types! destroy.restype = None destroy.__doc__ = "Frees an OpusEncoder allocated by opus_encoder_create()" orion-labs-opuslib-43410b7/opuslib/api/info.py000066400000000000000000000013211322731271500212330ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=invalid-name # import ctypes # type: ignore import opuslib.api __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' strerror = opuslib.api.libopus.opus_strerror strerror.argtypes = (ctypes.c_int,) # must be sequence (,) of types! strerror.restype = ctypes.c_char_p strerror.__doc__ = 'Converts an opus error code into a human readable string' get_version_string = opuslib.api.libopus.opus_get_version_string get_version_string.argtypes = None get_version_string.restype = ctypes.c_char_p get_version_string.__doc__ = 'Gets the libopus version string' orion-labs-opuslib-43410b7/opuslib/classes.py000066400000000000000000000241441322731271500211740ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """High-level interface to a Opus decoder functions""" import typing import opuslib import opuslib.api import opuslib.api.ctl import opuslib.api.decoder import opuslib.api.encoder __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class Decoder(object): """High-Level Decoder Object.""" def __init__(self, fs: int, channels: int) -> None: """ :param fs: Sample Rate. :param channels: Number of channels. """ self._fs = fs self._channels = channels self.decoder_state = opuslib.api.decoder.create_state(fs, channels) def __del__(self) -> None: if hasattr(self, 'decoder_state'): # Destroying state only if __init__ completed successfully opuslib.api.decoder.destroy(self.decoder_state) def reset_state(self) -> None: """ Resets the codec state to be equivalent to a freshly initialized state """ opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.reset_state ) # FIXME: Remove typing.Any once we have a stub for ctypes def decode( self, opus_data: bytes, frame_size: int, decode_fec: bool = False ) -> typing.Union[bytes, typing.Any]: """ Decodes given Opus data to PCM. """ return opuslib.api.decoder.decode( self.decoder_state, opus_data, len(opus_data), frame_size, decode_fec, channels=self._channels ) # FIXME: Remove typing.Any once we have a stub for ctypes def decode_float( self, opus_data: bytes, frame_size: int, decode_fec: bool = False ) -> typing.Union[bytes, typing.Any]: """ Decodes given Opus data to PCM. """ return opuslib.api.decoder.decode_float( self.decoder_state, opus_data, len(opus_data), frame_size, decode_fec, channels=self._channels ) # CTL interfaces _get_final_range = lambda self: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.get_final_range ) final_range = property(_get_final_range) _get_bandwidth = lambda self: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.get_bandwidth ) bandwidth = property(_get_bandwidth) _get_pitch = lambda self: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.get_pitch ) pitch = property(_get_pitch) _get_lsb_depth = lambda self: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.get_lsb_depth ) _set_lsb_depth = lambda self, x: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.set_lsb_depth, x ) lsb_depth = property(_get_lsb_depth, _set_lsb_depth) _get_gain = lambda self: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.get_gain ) _set_gain = lambda self, x: opuslib.api.decoder.decoder_ctl( self.decoder_state, opuslib.api.ctl.set_gain, x ) gain = property(_get_gain, _set_gain) class Encoder(object): """High-Level Encoder Object.""" def __init__(self, fs, channels, application) -> None: """ Parameters: fs : sampling rate channels : number of channels """ # Check to see if the Encoder Application Macro is available: if application in list(opuslib.APPLICATION_TYPES_MAP.keys()): application = opuslib.APPLICATION_TYPES_MAP[application] elif application in list(opuslib.APPLICATION_TYPES_MAP.values()): pass # Nothing to do here else: raise ValueError( "`application` value must be in 'voip', 'audio' or " "'restricted_lowdelay'") self._fs = fs self._channels = channels self._application = application self.encoder_state = opuslib.api.encoder.create_state( fs, channels, application) def __del__(self) -> None: if hasattr(self, 'encoder_state'): # Destroying state only if __init__ completed successfully opuslib.api.encoder.destroy(self.encoder_state) def reset_state(self) -> None: """ Resets the codec state to be equivalent to a freshly initialized state """ opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.reset_state) def encode(self, pcm_data: bytes, frame_size: int) -> bytes: """ Encodes given PCM data as Opus. """ return opuslib.api.encoder.encode( self.encoder_state, pcm_data, frame_size, len(pcm_data) ) def encode_float(self, pcm_data: bytes, frame_size: int) -> bytes: """ Encodes given PCM data as Opus. """ return opuslib.api.encoder.encode_float( self.encoder_state, pcm_data, frame_size, len(pcm_data) ) # CTL interfaces _get_final_range = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_final_range ) final_range = property(_get_final_range) _get_bandwidth = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_bandwidth) bandwidth = property(_get_bandwidth) _get_pitch = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_pitch) pitch = property(_get_pitch) _get_lsb_depth = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_lsb_depth) _set_lsb_depth = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_lsb_depth, x) lsb_depth = property(_get_lsb_depth, _set_lsb_depth) _get_complexity = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_complexity) _set_complexity = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_complexity, x) complexity = property(_get_complexity, _set_complexity) _get_bitrate = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_bitrate) _set_bitrate = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_bitrate, x) bitrate = property(_get_bitrate, _set_bitrate) _get_vbr = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_vbr) _set_vbr = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_vbr, x) vbr = property(_get_vbr, _set_vbr) _get_vbr_constraint = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_vbr_constraint) _set_vbr_constraint = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_vbr_constraint, x) vbr_constraint = property(_get_vbr_constraint, _set_vbr_constraint) _get_force_channels = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_force_channels) _set_force_channels = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_force_channels, x) force_channels = property(_get_force_channels, _set_force_channels) _get_max_bandwidth = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_max_bandwidth) _set_max_bandwidth = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_max_bandwidth, x) max_bandwidth = property(_get_max_bandwidth, _set_max_bandwidth) _set_bandwidth = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_bandwidth, x) bandwidth = property(None, _set_bandwidth) _get_signal = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_signal) _set_signal = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_signal, x) signal = property(_get_signal, _set_signal) _get_application = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_application) _set_application = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_application, x) application = property(_get_application, _set_application) _get_sample_rate = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_sample_rate) sample_rate = property(_get_sample_rate) _get_lookahead = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_lookahead) lookahead = property(_get_lookahead) _get_inband_fec = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_inband_fec) _set_inband_fec = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_inband_fec) inband_fec = property(_get_inband_fec, _set_inband_fec) _get_packet_loss_perc = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_packet_loss_perc) _set_packet_loss_perc = \ lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.set_packet_loss_perc, x) packet_loss_perc = property(_get_packet_loss_perc, _set_packet_loss_perc) _get_dtx = lambda self: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_dtx) _set_dtx = lambda self, x: opuslib.api.encoder.encoder_ctl( self.encoder_state, opuslib.api.ctl.get_dtx, x) orion-labs-opuslib-43410b7/opuslib/constants.py000066400000000000000000000046241322731271500215540ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ OpusLib constants. Matches to `opus_defines.h` """ __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' # No Error OK = 0 # One or more invalid/out of range arguments BAD_ARG = -1 # The mode struct passed is invalid BUFFER_TOO_SMALL = -2 # An internal error was detected INTERNAL_ERROR = -3 # The compressed data passed is corrupted INVALID_PACKET = -4 # Invalid/unsupported request number UNIMPLEMENTED = -5 # An encoder or decoder structure is invalid or already freed INVALID_STATE = -6 # Memory allocation has failed ALLOC_FAIL = -7 # Pre-defined values for CTL interface APPLICATION_VOIP = 2048 APPLICATION_AUDIO = 2049 APPLICATION_RESTRICTED_LOWDELAY = 2051 SIGNAL_VOICE = 3001 SIGNAL_MUSIC = 3002 # Values for the various encoder CTLs SET_APPLICATION_REQUEST = 4000 GET_APPLICATION_REQUEST = 4001 SET_BITRATE_REQUEST = 4002 GET_BITRATE_REQUEST = 4003 SET_MAX_BANDWIDTH_REQUEST = 4004 GET_MAX_BANDWIDTH_REQUEST = 4005 SET_VBR_REQUEST = 4006 GET_VBR_REQUEST = 4007 SET_BANDWIDTH_REQUEST = 4008 GET_BANDWIDTH_REQUEST = 4009 SET_COMPLEXITY_REQUEST = 4010 GET_COMPLEXITY_REQUEST = 4011 SET_INBAND_FEC_REQUEST = 4012 GET_INBAND_FEC_REQUEST = 4013 SET_PACKET_LOSS_PERC_REQUEST = 4014 GET_PACKET_LOSS_PERC_REQUEST = 4015 SET_DTX_REQUEST = 4016 GET_DTX_REQUEST = 4017 SET_VBR_CONSTRAINT_REQUEST = 4020 GET_VBR_CONSTRAINT_REQUEST = 4021 SET_FORCE_CHANNELS_REQUEST = 4022 GET_FORCE_CHANNELS_REQUEST = 4023 SET_SIGNAL_REQUEST = 4024 GET_SIGNAL_REQUEST = 4025 GET_LOOKAHEAD_REQUEST = 4027 RESET_STATE = 4028 GET_SAMPLE_RATE_REQUEST = 4029 GET_FINAL_RANGE_REQUEST = 4031 GET_PITCH_REQUEST = 4033 SET_GAIN_REQUEST = 4034 GET_GAIN_REQUEST = 4045 # Should have been 4035 SET_LSB_DEPTH_REQUEST = 4036 GET_LSB_DEPTH_REQUEST = 4037 GET_LAST_PACKET_DURATION_REQUEST = 4039 SET_EXPERT_FRAME_DURATION_REQUEST = 4040 GET_EXPERT_FRAME_DURATION_REQUEST = 4041 SET_PREDICTION_DISABLED_REQUEST = 4042 GET_PREDICTION_DISABLED_REQUEST = 4043 # Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST AUTO = -1000 BANDWIDTH_NARROWBAND = 1101 BANDWIDTH_MEDIUMBAND = 1102 BANDWIDTH_WIDEBAND = 1103 BANDWIDTH_SUPERWIDEBAND = 1104 BANDWIDTH_FULLBAND = 1105 APPLICATION_TYPES_MAP = { 'voip': APPLICATION_VOIP, 'audio': APPLICATION_AUDIO, 'restricted_lowdelay': APPLICATION_RESTRICTED_LOWDELAY, } orion-labs-opuslib-43410b7/opuslib/exceptions.py000066400000000000000000000012041322731271500217100ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Exceptions for OpusLib. """ import typing import opuslib.api.info __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class OpusError(Exception): """ Generic handler for OpusLib errors from C library. """ def __init__(self, code: int) -> None: self.code = code super().__init__() # FIXME: Remove typing.Any once we have a stub for ctypes def __str__(self) -> typing.Union[str, typing.Any]: return str(opuslib.api.info.strerror(self.code)) orion-labs-opuslib-43410b7/requirements_test.txt000066400000000000000000000001241322731271500220230ustar00rootroot00000000000000# Python Distribution Package Requirements for OpusLib. # flake8 pylint twine mypy orion-labs-opuslib-43410b7/setup.cfg000066400000000000000000000002661322731271500173300ustar00rootroot00000000000000# Nosetests configuration for Python OpusLib. [nosetests] with-xunit = 1 with-coverage = 1 cover-html = 1 with-doctest = 1 doctest-tests = 1 cover-tests = 0 cover-package = opuslib orion-labs-opuslib-43410b7/setup.py000066400000000000000000000024451322731271500172220ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """OpusLib Package.""" import setuptools # type: ignore __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' setuptools.setup( name='opuslib', version='3.0.1', author='Никита Кузнецов', author_email='self@svartalf.info', maintainer='Orion Labs, Inc.', maintainer_email='code@orionlabs.io', license='BSD 3-Clause License', url='https://github.com/onbeep/opuslib', description='Python bindings to the libopus, IETF low-delay audio codec', packages=('opuslib', 'opuslib.api'), test_suite='tests', zip_safe=False, tests_require=[ 'coverage >= 4.4.1', 'nose >= 1.3.7', ], classifiers=( 'Development Status :: 1 - Planning', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Software Development :: Libraries', 'Topic :: Multimedia :: Sound/Audio :: Conversion', ), ) orion-labs-opuslib-43410b7/tests/000077500000000000000000000000001322731271500166455ustar00rootroot00000000000000orion-labs-opuslib-43410b7/tests/__init__.py000066400000000000000000000003371322731271500207610ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for opuslib.""" __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' orion-labs-opuslib-43410b7/tests/decoder.py000066400000000000000000000201611322731271500206240ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=missing-docstring # import sys import unittest import opuslib.api import opuslib.api.decoder import opuslib.api.ctl __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class DecoderTest(unittest.TestCase): """Decoder basic API tests From the `tests/test_opus_api.c` """ def test_get_size(self): """Invalid configurations which should fail""" for csx in range(4): ixx = opuslib.api.decoder.libopus_get_size(csx) if csx in (1, 2): self.assertFalse(1 << 16 < ixx <= 2048) else: self.assertEqual(ixx, 0) def _test_unsupported_sample_rates(self): """ Unsupported sample rates TODO: make the same test with a opus_decoder_init() function """ for csx in range(4): for ixx in range(-7, 96000): if ixx in (8000, 12000, 16000, 24000, 48000) and csx in (1, 2): continue if ixx == -5: fsx = -8000 elif ixx == -6: fsx = sys.maxsize # TODO: should be a INT32_MAX elif ixx == -7: fsx = -1 * (sys.maxsize - 1) # Emulation of the INT32_MIN else: fsx = ixx try: dec = opuslib.api.decoder.create_state(fsx, csx) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) else: opuslib.api.decoder.destroy(dec) @classmethod def test_create(cls): try: dec = opuslib.api.decoder.create_state(48000, 2) except opuslib.OpusError: raise AssertionError() else: opuslib.api.decoder.destroy(dec) # TODO: rewrite this code # VG_CHECK(dec,opus_decoder_get_size(2)); @classmethod def test_get_final_range(cls): dec = opuslib.api.decoder.create_state(48000, 2) opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_final_range) opuslib.api.decoder.destroy(dec) def test_unimplemented(self): dec = opuslib.api.decoder.create_state(48000, 2) try: opuslib.api.decoder.decoder_ctl( dec, opuslib.api.ctl.unimplemented) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.UNIMPLEMENTED) opuslib.api.decoder.destroy(dec) def test_get_bandwidth(self): dec = opuslib.api.decoder.create_state(48000, 2) value = opuslib.api.decoder.decoder_ctl( dec, opuslib.api.ctl.get_bandwidth) self.assertEqual(value, 0) opuslib.api.decoder.destroy(dec) def test_get_pitch(self): dec = opuslib.api.decoder.create_state(48000, 2) i = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_pitch) self.assertIn(i, (-1, 0)) packet = bytes([252, 0, 0]) opuslib.api.decoder.decode(dec, packet, 3, 960, False) i = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_pitch) self.assertIn(i, (-1, 0)) packet = bytes([1, 0, 0]) opuslib.api.decoder.decode(dec, packet, 3, 960, False) i = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_pitch) self.assertIn(i, (-1, 0)) opuslib.api.decoder.destroy(dec) def test_gain(self): dec = opuslib.api.decoder.create_state(48000, 2) i = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_gain) self.assertEqual(i, 0) try: opuslib.api.decoder.decoder_ctl( dec, opuslib.api.ctl.set_gain, -32769) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) try: opuslib.api.decoder.decoder_ctl( dec, opuslib.api.ctl.set_gain, 32768) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.set_gain, -15) i = opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.get_gain) self.assertEqual(i, -15) opuslib.api.decoder.destroy(dec) @classmethod def test_reset_state(cls): dec = opuslib.api.decoder.create_state(48000, 2) opuslib.api.decoder.decoder_ctl(dec, opuslib.api.ctl.reset_state) opuslib.api.decoder.destroy(dec) def test_get_nb_samples(self): """opus_decoder_get_nb_samples()""" dec = opuslib.api.decoder.create_state(48000, 2) self.assertEqual( 480, opuslib.api.decoder.get_nb_samples(dec, bytes([0]), 1)) packet = bytes() for xxc in ((63 << 2) | 3, 63): packet += bytes([xxc]) # TODO: check for exception code self.assertRaises( opuslib.OpusError, lambda: opuslib.api.decoder.get_nb_samples(dec, packet, 2) ) opuslib.api.decoder.destroy(dec) def test_packet_get_nb_frames(self): """opus_packet_get_nb_frames()""" packet = bytes() for xxc in ((63 << 2) | 3, 63): packet += bytes([xxc]) self.assertRaises( opuslib.OpusError, lambda: opuslib.api.decoder.packet_get_nb_frames(packet, 0) ) l1res = (1, 2, 2, opuslib.INVALID_PACKET) for ixc in range(0, 256): packet = bytes([ixc]) expected_result = l1res[ixc & 3] try: self.assertEqual( expected_result, opuslib.api.decoder.packet_get_nb_frames(packet, 1) ) except opuslib.OpusError as exc: if exc.code == expected_result: continue for jxc in range(0, 256): packet = bytes([ixc, jxc]) self.assertEqual( expected_result if expected_result != 3 else (packet[1] & 63), # NOQA opuslib.api.decoder.packet_get_nb_frames(packet, 2) ) def test_packet_get_bandwidth(self): """Tests `opuslib.api.decoder.opus_packet_get_bandwidth()`""" for ixc in range(0, 256): packet = bytes([ixc]) bwx = ixc >> 4 # Very cozy code from the test_opus_api.c _bwx = opuslib.BANDWIDTH_NARROWBAND + (((((bwx & 7) * 9) & (63 - (bwx & 8))) + 2 + 12 * ((bwx & 8) != 0)) >> 4) # NOQA pylint: disable=line-too-long self.assertEqual( _bwx, opuslib.api.decoder.packet_get_bandwidth(packet) ) def test_decode(self): """opus_decode()""" packet = bytes([255, 49]) for _ in range(2, 51): packet += bytes([0]) dec = opuslib.api.decoder.create_state(48000, 2) try: opuslib.api.decoder.decode(dec, packet, 51, 960, 0) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.INVALID_PACKET) packet = bytes([252, 0, 0]) try: opuslib.api.decoder.decode(dec, packet, -1, 960, 0) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) try: opuslib.api.decoder.decode(dec, packet, 3, 60, 0) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BUFFER_TOO_SMALL) try: opuslib.api.decoder.decode(dec, packet, 3, 480, 0) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BUFFER_TOO_SMALL) try: opuslib.api.decoder.decode(dec, packet, 3, 960, 0) except opuslib.OpusError: self.fail('Decode failed') opuslib.api.decoder.destroy(dec) def test_decode_float(self): dec = opuslib.api.decoder.create_state(48000, 2) packet = bytes([252, 0, 0]) try: opuslib.api.decoder.decode_float(dec, packet, 3, 960, 0) except opuslib.OpusError: self.fail('Decode failed') opuslib.api.decoder.destroy(dec) orion-labs-opuslib-43410b7/tests/encoder.py000066400000000000000000000227631322731271500206500ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=missing-docstring # import ctypes # type: ignore import sys import unittest import opuslib.api import opuslib.api.encoder import opuslib.api.ctl __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class EncoderTest(unittest.TestCase): """Encoder basic API tests From the `tests/test_opus_api.c` """ def _test_unsupported_sample_rates(self): for cxx in range(0, 4): for ixx in range(-7, 96000 + 1): if ixx in (8000, 12000, 16000, 24000, 48000) and cxx in (1, 2): continue if ixx == -5: fsx = -8000 elif ixx == -6: fsx = sys.maxsize # TODO: Must be an INT32_MAX elif ixx == -7: fsx = -1 * (sys.maxsize - 1) # TODO: Must be an INT32_MIN else: fsx = ixx try: opuslib.api.encoder.create_state( fsx, cxx, opuslib.APPLICATION_VOIP) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) def test_create(self): try: opuslib.api.encoder.create_state(48000, 2, opuslib.AUTO) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_VOIP) opuslib.api.encoder.destroy(enc) enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_RESTRICTED_LOWDELAY) # TODO: rewrite that code # i = opuslib.api.encoder.encoder_ctl( # enc, opuslib.api.ctl.get_lookahead) # if(err!=OPUS_OK || i<0 || i>32766)test_failed(); opuslib.api.encoder.destroy(enc) enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) # TODO: rewrite that code # i = opuslib.api.encoder.encoder_ctl( # enc, opuslib.api.ctl.get_lookahead) # err=opus_encoder_ctl(enc,OPUS_GET_LOOKAHEAD(&i)); # if(err!=OPUS_OK || i<0 || i>32766)test_failed(); opuslib.api.encoder.destroy(enc) @classmethod def test_encode(cls): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) data = chr(0) * ctypes.sizeof(ctypes.c_short) * 2 * 960 opuslib.api.encoder.encode(enc, data, 960, len(data)) opuslib.api.encoder.destroy(enc) @classmethod def test_encode_float(cls): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) data = chr(0) * ctypes.sizeof(ctypes.c_float) * 2 * 960 opuslib.api.encoder.encode_float(enc, data, 960, len(data)) opuslib.api.encoder.destroy(enc) def test_unimplemented(self): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) try: opuslib.api.encoder.encoder_ctl(enc, opuslib.api.ctl.unimplemented) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.UNIMPLEMENTED) opuslib.api.encoder.destroy(enc) def test_application(self): self.check_setget( opuslib.api.ctl.set_application, opuslib.api.ctl.get_application, (-1, opuslib.AUTO), (opuslib.APPLICATION_AUDIO, opuslib.APPLICATION_RESTRICTED_LOWDELAY) ) def test_bitrate(self): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bitrate, 1073741832) value = opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.get_bitrate) self.assertLess(value, 700000) self.assertGreater(value, 256000) opuslib.api.encoder.destroy(enc) self.check_setget( opuslib.api.ctl.set_bitrate, opuslib.api.ctl.get_bitrate, (-12345, 0), (500, 256000) ) def test_force_channels(self): self.check_setget( opuslib.api.ctl.set_force_channels, opuslib.api.ctl.get_force_channels, (-1, 3), (1, opuslib.AUTO) ) def test_bandwidth(self): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) # Set bandwidth ixx = -2 self.assertRaises( opuslib.OpusError, lambda: opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ixx) ) ix1 = opuslib.BANDWIDTH_FULLBAND + 1 self.assertRaises( opuslib.OpusError, lambda: opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ix1) ) ix2 = opuslib.BANDWIDTH_NARROWBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ix2) ix3 = opuslib.BANDWIDTH_FULLBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ix3) ix4 = opuslib.BANDWIDTH_WIDEBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ix4) ix5 = opuslib.BANDWIDTH_MEDIUMBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, ix5) # Get bandwidth value = opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.get_bandwidth) self.assertIn( value, (opuslib.BANDWIDTH_FULLBAND, opuslib.BANDWIDTH_MEDIUMBAND, opuslib.BANDWIDTH_WIDEBAND, opuslib.BANDWIDTH_NARROWBAND, opuslib.AUTO) ) opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_bandwidth, opuslib.AUTO) opuslib.api.encoder.destroy(enc) def test_max_bandwidth(self): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) i = -2 self.assertRaises( opuslib.OpusError, lambda: opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) ) i = opuslib.BANDWIDTH_FULLBAND + 1 self.assertRaises( opuslib.OpusError, lambda: opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) ) i = opuslib.BANDWIDTH_NARROWBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) i = opuslib.BANDWIDTH_FULLBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) i = opuslib.BANDWIDTH_WIDEBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) i = opuslib.BANDWIDTH_MEDIUMBAND opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.set_max_bandwidth, i) i = -12345 value = opuslib.api.encoder.encoder_ctl( enc, opuslib.api.ctl.get_max_bandwidth) self.assertIn( value, (opuslib.BANDWIDTH_FULLBAND, opuslib.BANDWIDTH_MEDIUMBAND, opuslib.BANDWIDTH_WIDEBAND, opuslib.BANDWIDTH_NARROWBAND, opuslib.AUTO) ) opuslib.api.encoder.destroy(enc) def test_dtx(self): self.check_setget( opuslib.api.ctl.set_dtx, opuslib.api.ctl.get_dtx, (-1, 2), (1, 0)) def test_complexity(self): self.check_setget( opuslib.api.ctl.set_complexity, opuslib.api.ctl.get_complexity, (-1, 11), (0, 10) ) def test_inband_fec(self): self.check_setget( opuslib.api.ctl.set_inband_fec, opuslib.api.ctl.get_inband_fec, (-1, 2), (1, 0) ) def test_packet_loss_perc(self): self.check_setget( opuslib.api.ctl.set_packet_loss_perc, opuslib.api.ctl.get_packet_loss_perc, (-1, 101), (100, 0) ) def test_vbr(self): self.check_setget( opuslib.api.ctl.set_vbr, opuslib.api.ctl.get_vbr, (-1, 2), (1, 0)) def test_vbr_constraint(self): self.check_setget( opuslib.api.ctl.set_vbr_constraint, opuslib.api.ctl.get_vbr_constraint, (-1, 2), (1, 0) ) def test_signal(self): self.check_setget( opuslib.api.ctl.set_signal, opuslib.api.ctl.get_signal, (-12345, 0x7FFFFFFF), (opuslib.SIGNAL_MUSIC, opuslib.AUTO) ) def test_lsb_depth(self): self.check_setget( opuslib.api.ctl.set_lsb_depth, opuslib.api.ctl.get_lsb_depth, (7, 25), (16, 24) ) def check_setget(self, v_set, v_get, bad, good): enc = opuslib.api.encoder.create_state( 48000, 2, opuslib.APPLICATION_AUDIO) for value in bad: self.assertRaises( opuslib.OpusError, lambda: opuslib.api.encoder.encoder_ctl(enc, v_set, value) ) for valuex in good: opuslib.api.encoder.encoder_ctl(enc, v_set, valuex) result = opuslib.api.encoder.encoder_ctl(enc, v_get) self.assertEqual(valuex, result) opuslib.api.encoder.destroy(enc) orion-labs-opuslib-43410b7/tests/hl_decoder.py000066400000000000000000000053311322731271500213110ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=missing-docstring # """Tests for a high-level Decoder object""" import unittest import opuslib __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class DecoderTest(unittest.TestCase): def test_create(self): try: opuslib.Decoder(1000, 3) except opuslib.OpusError as ex: self.assertEqual(ex.code, opuslib.BAD_ARG) opuslib.Decoder(48000, 2) def test_get_bandwidth(self): decoder = opuslib.Decoder(48000, 2) self.assertEqual(decoder.bandwidth, 0) def test_get_pitch(self): decoder = opuslib.Decoder(48000, 2) self.assertIn(decoder.pitch, (-1, 0)) packet = bytes([252, 0, 0]) decoder.decode(packet, frame_size=960) self.assertIn(decoder.pitch, (-1, 0)) packet = bytes([1, 0, 0]) decoder.decode(packet, frame_size=960) self.assertIn(decoder.pitch, (-1, 0)) def test_gain(self): decoder = opuslib.Decoder(48000, 2) self.assertEqual(decoder.gain, 0) try: decoder.gain = -32769 except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) try: decoder.gain = 32768 except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BAD_ARG) decoder.gain = -15 self.assertEqual(decoder.gain, -15) @classmethod def test_reset_state(cls): decoder = opuslib.Decoder(48000, 2) decoder.reset_state() def test_decode(self): decoder = opuslib.Decoder(48000, 2) packet = bytes([255, 49]) for _ in range(2, 51): packet += bytes([0]) try: decoder.decode(packet, frame_size=960) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.INVALID_PACKET) packet = bytes([252, 0, 0]) try: decoder.decode(packet, frame_size=60) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BUFFER_TOO_SMALL) try: decoder.decode(packet, frame_size=480) except opuslib.OpusError as exc: self.assertEqual(exc.code, opuslib.BUFFER_TOO_SMALL) try: decoder.decode(packet, frame_size=960) except opuslib.OpusError: self.fail('Decode failed') def test_decode_float(self): decoder = opuslib.Decoder(48000, 2) packet = bytes([252, 0, 0]) try: decoder.decode_float(packet, frame_size=960) except opuslib.OpusError: self.fail('Decode failed') orion-labs-opuslib-43410b7/tests/hl_encoder.py000066400000000000000000000013751322731271500213270ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=missing-docstring # """Tests for a high-level Decoder object""" import unittest import opuslib __author__ = 'Никита Кузнецов ' __copyright__ = 'Copyright (c) 2012, SvartalF' __license__ = 'BSD 3-Clause License' class EncoderTest(unittest.TestCase): def test_create(self): try: opuslib.Encoder(1000, 3, opuslib.APPLICATION_AUDIO) except opuslib.OpusError as ex: self.assertEqual(ex.code, opuslib.BAD_ARG) opuslib.Encoder(48000, 2, opuslib.APPLICATION_AUDIO) @classmethod def test_reset_state(cls): encoder = opuslib.Encoder(48000, 2, opuslib.APPLICATION_AUDIO) encoder.reset_state()