├── .github └── workflows │ └── build_and_test.yml ├── .gitignore ├── CMakeLists.txt ├── GNUmakefile ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── build_tools ├── CheckVersion.py └── __init__.py ├── docker ├── Dockerfile.build └── Dockerfile.python_wheels ├── g722.h ├── g722_codec.h ├── g722_common.h ├── g722_decode.c ├── g722_decoder.h ├── g722_encode.c ├── g722_encoder.h ├── g722_private.h ├── ld_sugar ├── Symbol.map └── Versions.def ├── library.properties ├── pyproject.toml ├── python ├── G722_mod.c ├── requirements.txt └── symbols.map ├── scripts ├── build │ └── install_depends_wheels.sh └── do-test.sh ├── setup.py ├── test.c ├── test.py ├── test_data ├── fullscale.g722 ├── pcminb.checksum ├── pcminb.dat ├── test.checksum └── test.g722 └── tests ├── test_decode.py ├── test_encode.py └── test_encode_decode.py /.github/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build, Test & Publush 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on all push or pull request events 8 | push: 9 | pull_request: 10 | 11 | release: 12 | types: [published] 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | schedule: 18 | - cron: "0 0 * * *" 19 | 20 | # added using https://github.com/step-security/secure-repo 21 | permissions: 22 | contents: read 23 | 24 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 25 | jobs: 26 | build_and_test: 27 | # The type of runner that the job will run on 28 | runs-on: ubuntu-latest 29 | 30 | # Steps represent a sequence of tasks that will be executed as part of the job 31 | steps: 32 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 33 | - uses: actions/checkout@v4 34 | 35 | - name: Install Dependencies 36 | run: | 37 | sudo apt-get update 38 | sudo apt-get install -y bmake cmake 39 | 40 | - name: Build with GNU Make 41 | run: make clean all 42 | 43 | - name: Test with GNU Make 44 | run: make test 45 | 46 | - name: Build with BSD Make 47 | run: bmake clean all 48 | 49 | - name: Test with BSD Make 50 | run: bmake test 51 | 52 | - name: Build with CMake 53 | run: | 54 | cmake -B build 55 | make -C build clean all 56 | 57 | - name: Test with CMake 58 | run: bmake -C build test 59 | 60 | build_and_test_python: 61 | needs: build_and_test 62 | strategy: 63 | fail-fast: false 64 | matrix: 65 | python-version: ['3.10', '3.11', '3.12'] 66 | compiler: ['gcc', 'clang'] 67 | os: [macos, ubuntu] 68 | include: 69 | - python-version: '3.10' 70 | compiler: microsoft 71 | os: windows 72 | - python-version: '3.11' 73 | compiler: microsoft 74 | os: windows 75 | - python-version: '3.12' 76 | compiler: microsoft 77 | os: windows 78 | 79 | runs-on: ${{ matrix.os }}-latest 80 | env: 81 | COMPILER: ${{ matrix.compiler }} 82 | 83 | # Steps represent a sequence of tasks that will be executed as part of the job 84 | steps: 85 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 86 | - uses: actions/checkout@v4 87 | 88 | - name: Set up Python ${{ matrix.python-version }} 89 | uses: actions/setup-python@v5 90 | with: 91 | python-version: ${{ matrix.python-version }} 92 | 93 | - name: Install dependencies 94 | run: | 95 | python -m pip install --upgrade pip 96 | pip install --upgrade setuptools wheel 97 | pip install -r python/requirements.txt 98 | shell: bash 99 | 100 | - name: build 101 | run: CC=${COMPILER} python setup.py build sdist 102 | shell: bash 103 | 104 | - name: install 105 | run: pip install dist/[Gg]722*.gz 106 | shell: bash 107 | 108 | - name: test 109 | if: matrix.os != 'windows' 110 | run: ./scripts/do-test.sh "python test.py" 111 | shell: bash 112 | 113 | - name: unit tests 114 | run: python -m unittest discover tests '*.py' 115 | shell: bash 116 | 117 | build_wheels: 118 | permissions: 119 | packages: write 120 | needs: [build_and_test_python, build_in_docker] 121 | uses: sippy/cimagic/.github/workflows/BuildPythonWheels.yml@v1 122 | 123 | publish_all_wheels: 124 | needs: [build_wheels] 125 | runs-on: ubuntu-latest 126 | environment: 127 | name: pypi 128 | url: https://pypi.org/p/G722 129 | permissions: 130 | actions: read 131 | contents: read 132 | id-token: write 133 | steps: 134 | - uses: actions/checkout@v4 135 | 136 | - name: Download all wheel artifacts 137 | uses: actions/download-artifact@v4 138 | with: 139 | path: dist 140 | pattern: 'dist-*' 141 | merge-multiple: true 142 | 143 | - name: Set up Python 144 | uses: actions/setup-python@v5 145 | with: 146 | python-version: '3.12' 147 | 148 | - name: Install dependencies 149 | run: | 150 | python -m pip install --upgrade pip 151 | pip install setuptools wheel 152 | pip install -r python/requirements.txt 153 | 154 | - name: build 155 | run: python setup.py build sdist 156 | 157 | - name: Show context tree 158 | run: ls -lR dist 159 | 160 | - name: Check release version number 161 | if: github.event_name == 'release' && github.event.action == 'published' 162 | run: | 163 | GIT_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" 164 | GIT_BRANCH="${GIT_BRANCH#refs/tags/}" 165 | python setup.py checkversion --tag="${GIT_BRANCH}" 166 | 167 | - name: Publish package distributions to PyPI 168 | if: github.event_name == 'release' && github.event.action == 'published' 169 | uses: pypa/gh-action-pypi-publish@release/v1 170 | 171 | build_in_docker: 172 | needs: [build_and_test, build_and_test_python] 173 | runs-on: ubuntu-latest 174 | strategy: 175 | fail-fast: false 176 | matrix: 177 | base_image: ['debian:sid-slim', 'debian:12-slim', 'ubuntu:latest'] 178 | env: 179 | DOCKER_REPO: sippylabs/libg722 180 | BASE_IMAGE: ${{ matrix.base_image }} 181 | DOCKER_RW: ${{ ( github.repository == 'sippy/libg722' && github.event_name != 'pull_request' ) && 'true' || 'false' }} 182 | steps: 183 | - name: Checkout repository 184 | uses: actions/checkout@v4 185 | 186 | - name: Set up QEMU 187 | id: qemu 188 | uses: docker/setup-qemu-action@v3 189 | 190 | - name: Log in to Docker Hub 191 | if: env.DOCKER_RW == 'true' 192 | uses: docker/login-action@v3 193 | with: 194 | username: ${{ secrets.DOCKER_USERNAME }} 195 | password: ${{ secrets.DOCKER_PASSWORD }} 196 | 197 | - name: Set up Docker Buildx 198 | uses: docker/setup-buildx-action@v3 199 | 200 | - name: Set environments 201 | run: | 202 | # mips64 seems to be broken at the moment... 203 | if [ "${{ env.BASE_IMAGE }}" = "debian:sid-slim" ] 204 | then 205 | FILT="grep -v mips64le" 206 | else 207 | FILT="cat" 208 | fi 209 | PLATFORMS="`docker manifest inspect ${{ env.BASE_IMAGE }} | \ 210 | jq -r '.manifests[] | "\(.platform.os)/\(.platform.architecture)\(if .platform.variant != null then "/\(.platform.variant)" else "" end)"' | \ 211 | sort -u | grep -v unknown | ${FILT} | paste -sd ','`" 212 | echo "PLATFORMS=${PLATFORMS}" >> $GITHUB_ENV 213 | GIT_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" 214 | GIT_BRANCH="${GIT_BRANCH#refs/tags/}" 215 | echo "GIT_BRANCH=${GIT_BRANCH}" >> $GITHUB_ENV 216 | OS_TAG="`echo ${BASE_IMAGE} | sed 's|:|_|g'`" 217 | echo "OS_TAG=${OS_TAG}" >> $GITHUB_ENV 218 | 219 | - name: Extract metadata (tags, labels) for Docker 220 | id: meta 221 | uses: docker/metadata-action@v5 222 | with: 223 | images: ${{ env.DOCKER_REPO }} 224 | tags: | 225 | type=schedule,suffix=-${{ env.OS_TAG}} 226 | type=ref,event=branch,suffix=-${{ env.OS_TAG }} 227 | type=ref,event=tag,suffix=-${{ env.OS_TAG }} 228 | type=ref,event=pr,suffix=-${{ env.OS_TAG }} 229 | type=raw,value=latest-${{ env.OS_TAG }},enable={{is_default_branch}} 230 | type=sha,suffix=-${{ env.OS_TAG }} 231 | 232 | - name: Build Docker image 233 | uses: docker/build-push-action@v6 234 | env: 235 | CACHE_SPEC: "type=registry,ref=${{ env.DOCKER_REPO }}:${{ env.GIT_BRANCH }}-${{ env.OS_TAG}}-buildcache" 236 | with: 237 | context: . 238 | file: ./docker/Dockerfile.build 239 | build-args: | 240 | BASE_IMAGE=${{ env.BASE_IMAGE }} 241 | platforms: ${{ env.PLATFORMS }} 242 | tags: ${{ steps.meta.outputs.tags }} 243 | labels: ${{ steps.meta.outputs.labels }} 244 | push: ${{ env.DOCKER_RW }} 245 | cache-from: ${{ env.CACHE_SPEC }} 246 | cache-to: ${{ env.CACHE_SPEC }},mode=max 247 | 248 | roll_release: 249 | needs: publish_all_wheels 250 | permissions: 251 | contents: write 252 | uses: sippy/cimagic/.github/workflows/RollReleaseDraft.yml@v1 253 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./build/ 2 | *.out 3 | *.So 4 | .depend.* 5 | *.a 6 | *.o 7 | *.pico 8 | test 9 | Version.map 10 | *.so* 11 | __pycache__ 12 | *.egg-info 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | # set the project name 4 | project(libg722 C) 5 | 6 | option(ENABLE_SHARED_LIB "Build shared library" ON) 7 | option(ENABLE_STATIC_LIB "Build static library" ON) 8 | 9 | # lots of warnings and all warnings as errors 10 | ## add_compile_options(-Wall -Wextra ) 11 | set(CMAKE_C_STANDARD 11) 12 | 13 | file(GLOB_RECURSE SRC_LIST_C CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/g722*.c" ) 14 | 15 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 16 | include(CTest) 17 | endif() 18 | 19 | # define libraries 20 | if( ENABLE_SHARED_LIB ) 21 | add_library (g722 SHARED ${SRC_LIST_C}) 22 | set_target_properties(g722 PROPERTIES VERSION 0) 23 | set_target_properties(g722 PROPERTIES PUBLIC_HEADER "g722_codec.h;g722_decoder.h;g722_encoder.h;g722.h") 24 | add_executable(test_dynamic test.c) 25 | target_link_libraries(test_dynamic g722) 26 | add_test(NAME TestDynamic COMMAND ${PROJECT_SOURCE_DIR}/scripts/do-test.sh $) 27 | install(TARGETS g722 LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include) 28 | target_compile_options(g722 PRIVATE -Wdouble-promotion -Wno-attributes) 29 | target_include_directories(g722 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 30 | endif() 31 | if( ENABLE_STATIC_LIB ) 32 | add_library (g722_static STATIC ${SRC_LIST_C}) 33 | set_target_properties(g722_static PROPERTIES PUBLIC_HEADER "g722_codec.h;g722_decoder.h;g722_encoder.h;g722.h") 34 | set_target_properties(g722_static PROPERTIES OUTPUT_NAME g722) 35 | add_executable(test_static test.c) 36 | target_link_libraries(test_static g722_static) 37 | add_test(NAME TestStatic COMMAND ${PROJECT_SOURCE_DIR}/scripts/do-test.sh $) 38 | install(TARGETS g722_static ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include) 39 | target_compile_options(g722_static PRIVATE -Wdouble-promotion -Wno-attributes) 40 | target_include_directories(g722_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 41 | endif() 42 | 43 | include(CheckIPOSupported) 44 | check_ipo_supported(RESULT lto_supported OUTPUT lto_error) 45 | if( lto_supported ) 46 | message(STATUS "LTO enabled") 47 | if( ENABLE_SHARED_LIB ) 48 | set_property(TARGET g722 PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 49 | set_property(TARGET test_dynamic PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 50 | endif() 51 | if( ENABLE_STATIC_LIB ) 52 | set_property(TARGET g722_static PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 53 | set_property(TARGET test_static PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 54 | endif() 55 | else() 56 | message(STATUS "LTO not supported: <${lto_error}>") 57 | endif() 58 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .So 2 | VPATH = . 3 | 4 | PREFIX?= /usr/local 5 | LIBDIR= ${PREFIX}/lib 6 | INCLUDEDIR= ${PREFIX}/include 7 | 8 | SRCS_C= g722_decode.c g722_encode.c 9 | SRCS_H= g722.h g722_private.h g722_encoder.h g722_decoder.h 10 | 11 | CFLAGS?= -O2 -pipe -Wno-attributes 12 | 13 | OBJS = $(SRCS_C:.c=.o) 14 | OBJS_PIC = $(SRCS_C:.c=.So) 15 | 16 | all: libg722.a libg722.so.0 libg722.so 17 | 18 | libg722.a: $(OBJS) $(SRCS_H) 19 | $(AR) cq $@ $(OBJS) 20 | ranlib $@ 21 | 22 | libg722.so.0: $(OBJS_PIC) $(SRCS_H) 23 | $(CC) -shared -o $@ -Wl,-soname,$@ $(OBJS_PIC) 24 | 25 | libg722.so: libg722.so.0 26 | ln -sf libg722.so.0 $@ 27 | 28 | .c.o: 29 | $(CC) -c $(CFLAGS) $< -o $@ 30 | 31 | .c.So: 32 | $(CC) -fpic -DPIC -c $(CFLAGS) $< -o $@ 33 | 34 | clean: 35 | rm -f libg722.a libg722.so.0 $(OBJS) $(OBJS_PIC) test *.out 36 | 37 | test: test.c libg722.a libg722.so.0 38 | ${CC} ${CFLAGS} -o $@ test.c -lm -L. -lg722 39 | LD_LIBRARY_PATH=. ./scripts/do-test.sh ./$@ 40 | 41 | install: 42 | install -d ${DESTDIR}${LIBDIR} 43 | install libg722.a ${DESTDIR}${LIBDIR} 44 | install libg722.so.0 ${DESTDIR}${LIBDIR} 45 | ln -sf libg722.so.0 ${DESTDIR}${LIBDIR}/libg722.so 46 | install -d ${DESTDIR}${INCLUDEDIR} 47 | install ${SRCS_H} ${DESTDIR}${INCLUDEDIR} 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ***** 2 | * Copyright (c) CMU 1993 3 | * 4 | * Computer Science, Speech Group 5 | * Chengxiang Lu and Alex Hauptmann 6 | * 7 | * The Carnegie Mellon ADPCM program is Copyright (c) 1993 by Carnegie Mellon 8 | * University. Use of this program, for any research or commercial purpose, is 9 | * completely unrestricted. If you make use of or redistribute this material, 10 | * we would appreciate acknowlegement of its origin. 11 | ***** 12 | 13 | ***** 14 | * Copyright (C) 2005 Steve Underwood 15 | * 16 | * Despite my general liking of the GPL, I place my own contributions 17 | * to this code in the public domain for the benefit of all mankind - 18 | * even the slimy ones who might try to proprietize my work and use it 19 | * to my detriment. 20 | ***** 21 | 22 | ***** 23 | * Copyright (c) 2014-2016 Sippy Software, Inc., http://www.sippysoft.com 24 | * All rights reserved. 25 | * 26 | * Redistribution and use in source and binary forms, with or without 27 | * modification, are permitted provided that the following conditions 28 | * are met: 29 | * 1. Redistributions of source code must retain the above copyright 30 | * notice, this list of conditions and the following disclaimer. 31 | * 2. Redistributions in binary form must reproduce the above copyright 32 | * notice, this list of conditions and the following disclaimer in the 33 | * documentation and/or other materials provided with the distribution. 34 | * 35 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 36 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 39 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 40 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 41 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 42 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 44 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45 | * SUCH DAMAGE. 46 | ***** 47 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include build_tools/__init__.py build_tools/CheckVersion.py 2 | include g722.h g722_codec.h g722_common.h g722_decoder.h g722_encoder.h g722_private.h 3 | include python/requirements.txt python/symbols.map 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # $Id: Makefile,v 1.1 2012/08/07 11:33:45 sobomax Exp $ 2 | 3 | LIB= g722 4 | SHLIB_MAJOR= 0 5 | PREFIX?= /usr/local 6 | LIBDIR= ${PREFIX}/lib 7 | MK_PROFILE= no 8 | INCLUDEDIR= ${PREFIX}/include 9 | MAN= 10 | SRCS= g722_decode.c g722_encode.c 11 | INCS= g722.h g722_private.h g722_encoder.h g722_decoder.h 12 | WARNS?= 2 13 | CFLAGS+= -I${.CURDIR} ${PICFLAG} -Wno-attributes 14 | 15 | VERSION_DEF= ${.CURDIR}/ld_sugar/Versions.def 16 | SYMBOL_MAPS= ${.CURDIR}/ld_sugar/Symbol.map 17 | CFLAGS+= -DSYMBOL_VERSIONING 18 | 19 | CLEANFILES+=test *.out 20 | 21 | TDDIR= ${.CURDIR}/test_data 22 | 23 | test: test.c lib${LIB}.a lib${LIB}.so.${SHLIB_MAJOR} ${TDDIR}/fullscale.g722 ${TDDIR}/pcminb.dat ${TDDIR}/test.checksum ${TDDIR}/test.g722 Makefile 24 | rm -f ${TEST_OUT_FILES} 25 | ${CC} ${CFLAGS} -o ${.TARGET} test.c -lm -L. -l${LIB} 26 | LD_LIBRARY_PATH=${.CURDIR} ${.CURDIR}/scripts/do-test.sh ${.CURDIR}/${.TARGET} 27 | 28 | .include 29 | 30 | # XXX: fix ld error on Linux 31 | LD_shared:= ${LD_shared:S/-soname /-soname=/} 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libg722 2 | 3 | [![Build & Test](https://github.com/sippy/libg722/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/sippy/libg722/actions/workflows/build_and_test.yml) 4 | 5 | ## Introduction 6 | 7 | The G.722 module is a bit exact implementation of the ITU G.722 specification 8 | for all three specified bit rates - 64000bps, 56000bps and 48000bps. It passes 9 | the ITU tests. 10 | 11 | To allow fast and flexible interworking with narrow band telephony, the 12 | encoder and decoder support an option for the linear audio to be an 8k 13 | samples/second stream. In this mode the codec is considerably faster, and 14 | still fully compatible with wideband terminals using G.722. 15 | 16 | ## History 17 | 18 | The code was originally written by Milton Anderson 19 | from BELLCORE, and has been modified by the Chengxiang Lu and Alex Hauptmann 20 | from the Speech Group, School of Computer Science, Carnegie Mellon University, 21 | to be fairly fast and efficient, while retaining high fidelity. 22 | 23 | Steve Underwood improved the code a lot later on and 24 | contributed it into several popular open source projects. 25 | 26 | Himanshu Soni has adjusted some coefficients 27 | to avoid over/under-flows in the decoder. 28 | 29 | Phil Schatzmann has added cmake-glue and Arduino 30 | support. 31 | 32 | Librarized by Sippy Software, Inc. 33 | 34 | ## Build and Install library: 35 | 36 | ``` 37 | git clone https://github.com/sippy/libg722.git 38 | cmake -B libg722/build -S libg722 39 | make -C ibg722/build clean all test install 40 | ``` 41 | 42 | ## Install Python module from PyPy: 43 | 44 | ``` 45 | pip install G722 46 | ``` 47 | 48 | ## Build and Install Python module from source code: 49 | 50 | ``` 51 | git clone https://github.com/sippy/libg722.git 52 | pip install libg722/ 53 | ``` 54 | 55 | ## Pull library into your Docker container: 56 | ``` 57 | ARG BASE_IMAGE=debian:sid-slim 58 | ARG LIBG722_IMAGE=sippylabs/libg722:latest-debian_sid-slim 59 | FROM ${LIBG722_IMAGE} AS libg722 60 | FROM ${BASE_IMAGE} AS build 61 | COPY --from=libg722 /usr/local/lib/libg722.* /usr/local/lib/ 62 | COPY --from=libg722 /usr/local/include/g722* /usr/local/include/ 63 | ``` 64 | 65 | ## License 66 | 67 | This code is mostly Public Domain. Library test code is under BSD 2-clause 68 | license. 69 | 70 | See LICENSE for copyright details and each individual file for specifics. 71 | -------------------------------------------------------------------------------- /build_tools/CheckVersion.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from distutils.core import Command 3 | 4 | class CheckVersion(Command): 5 | description = "Check version number" 6 | user_options = [ 7 | ('tag=', 't', 'git tag to compare against package version'), 8 | ] 9 | extra_compile_args = [] 10 | extra_link_args = [] 11 | 12 | def initialize_options(self): 13 | self.tag = None 14 | 15 | def finalize_options(self): 16 | if not self.tag: 17 | raise DistutilsOptionError("You must specify --tag") 18 | 19 | def run(self): 20 | pkg_version = self.distribution.get_version() 21 | if self.tag == f'v{pkg_version}': 22 | return 23 | sys.stderr.write(f"❌ version {pkg_version} != tag {self.tag}\n") 24 | sys.exit(1) 25 | -------------------------------------------------------------------------------- /build_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/libg722/b4c3d4b15f939d8e25cd9af02a72dbab233f366d/build_tools/__init__.py -------------------------------------------------------------------------------- /docker/Dockerfile.build: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.7-labs 2 | 3 | ARG BASE_IMAGE="debian:sid-slim" 4 | FROM ${BASE_IMAGE} AS build 5 | LABEL maintainer="Maksym Sobolyev " 6 | 7 | ARG BUILD_PKGS="cmake make clang libc6-dev openssl" 8 | ARG DEBIAN_FRONTEND=noninteractive 9 | RUN apt-get update && apt-get install -y --no-install-recommends ${BUILD_PKGS} 10 | WORKDIR /src 11 | RUN --mount=type=bind,target=.,rw cmake -B build && \ 12 | make -C build CTEST_OUTPUT_ON_FAILURE=1 all test install 13 | FROM ${BASE_IMAGE} AS clean 14 | COPY --from=build /usr/local/lib/libg722.* /usr/local/lib/ 15 | COPY --from=build --exclude=g722_common.h --exclude=g722_private.h \ 16 | /usr/local/include/g722* /usr/local/include/ 17 | -------------------------------------------------------------------------------- /docker/Dockerfile.python_wheels: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.7-labs 2 | 3 | ARG BASE_IMAGE="quay.io/pypa/manylinux_2_28:latest" 4 | ARG CACHE_IMAGE="scratch" 5 | 6 | FROM ${CACHE_IMAGE} AS pipcache 7 | 8 | FROM --platform=$TARGETPLATFORM $BASE_IMAGE AS build 9 | LABEL maintainer="Maksym Sobolyev " 10 | 11 | USER root 12 | 13 | WORKDIR /src 14 | 15 | ARG PY_VER 16 | ARG PYTHON_CMD 17 | 18 | RUN mkdir -p /root/.cache/pip 19 | RUN --mount=type=bind,from=pipcache,source=/,target=/tmp/pip_cache_root \ 20 | test ! -d /tmp/pip_cache_root/root/.cache/pip || \ 21 | cp -a /tmp/pip_cache_root/root/.cache/pip /root/.cache/pip 22 | 23 | RUN --mount=type=bind,source=scripts/build,target=scripts/build \ 24 | sh -x scripts/build/install_depends_wheels.sh 25 | RUN --mount=type=bind,source=python/requirements.txt,target=python/requirements.txt \ 26 | ${PYTHON_CMD} -m pip install -r python/requirements.txt 27 | 28 | COPY --exclude=.git --exclude=.github --exclude=docker --exclude=dist \ 29 | --exclude=scripts . . 30 | 31 | RUN ${PYTHON_CMD} -m build --wheel 32 | RUN auditwheel repair dist/*.whl --wheel-dir dist_out 33 | 34 | FROM build AS test 35 | RUN ${PYTHON_CMD} -m pip install dist_out/*.whl 36 | RUN ${PYTHON_CMD} -m unittest discover -v -s tests -p '*.py' 37 | RUN date > dist_out/test_done.ts 38 | 39 | FROM scratch AS export 40 | COPY --from=build /src/dist_out /dist 41 | COPY --from=build /root/.cache/pip /root/.cache/pip 42 | COPY --from=test /src/dist_out/test_done.ts /dist 43 | -------------------------------------------------------------------------------- /g722.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SpanDSP - a series of DSP components for telephony 3 | * 4 | * g722.h - The ITU G.722 codec. 5 | * 6 | * Written by Steve Underwood 7 | * 8 | * Copyright (C) 2005 Steve Underwood 9 | * 10 | * Despite my general liking of the GPL, I place my own contributions 11 | * to this code in the public domain for the benefit of all mankind - 12 | * even the slimy ones who might try to proprietize my work and use it 13 | * to my detriment. 14 | * 15 | * Based on a single channel G.722 codec which is: 16 | * 17 | ***** Copyright (c) CMU 1993 ***** 18 | * Computer Science, Speech Group 19 | * Chengxiang Lu and Alex Hauptmann 20 | * 21 | * $Id: g722.h,v 1.1 2012/08/07 11:33:45 sobomax Exp $ 22 | */ 23 | 24 | 25 | /*! \file */ 26 | 27 | #pragma once 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /*! \page g722_page G.722 encoding and decoding 34 | \section g722_page_sec_1 What does it do? 35 | The G.722 module is a bit exact implementation of the ITU G.722 specification for all three 36 | specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests. 37 | 38 | To allow fast and flexible interworking with narrow band telephony, the encoder and decoder 39 | support an option for the linear audio to be an 8k samples/second stream. In this mode the 40 | codec is considerably faster, and still fully compatible with wideband terminals using G.722. 41 | 42 | \section g722_page_sec_2 How does it work? 43 | ???. 44 | */ 45 | 46 | enum 47 | { 48 | G722_DEFAULT = 0x0000, 49 | G722_SAMPLE_RATE_8000 = 0x0001, 50 | G722_PACKED = 0x0002 51 | }; 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /g722_codec.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file g722_codec.h 3 | * @author Phil Schatzmann 4 | * @brief Include for encoder and decoder which supports C++ 5 | * @version 0.1 6 | * @date 2022-05-08 7 | * 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "g722_encoder.h" 15 | #include "g722_decoder.h" 16 | -------------------------------------------------------------------------------- /g722_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * g722_common.h - The ITU G.722 codec, common functions. 3 | * 4 | * Written by Steve Underwood 5 | * 6 | * Copyright (C) 2005 Steve Underwood 7 | * 8 | * All rights reserved. 9 | * 10 | * Despite my general liking of the GPL, I place my own contributions 11 | * to this code in the public domain for the benefit of all mankind - 12 | * even the slimy ones who might try to proprietize my work and use it 13 | * to my detriment. 14 | * 15 | * Based on a single channel 64kbps only G.722 codec which is: 16 | * 17 | ***** Copyright (c) CMU 1993 ***** 18 | * Computer Science, Speech Group 19 | * Chengxiang Lu and Alex Hauptmann 20 | * 21 | * The Carnegie Mellon ADPCM program is Copyright (c) 1993 by Carnegie Mellon 22 | * University. Use of this program, for any research or commercial purpose, is 23 | * completely unrestricted. If you make use of or redistribute this material, 24 | * we would appreciate acknowlegement of its origin. 25 | ***** 26 | */ 27 | 28 | #pragma once 29 | 30 | #if !defined(FALSE) 31 | #define FALSE 0 32 | #endif 33 | #if !defined(TRUE) 34 | #define TRUE (!FALSE) 35 | #endif 36 | 37 | static inline int16_t saturate(int32_t amp) 38 | { 39 | int16_t amp16; 40 | 41 | /* Hopefully this is optimised for the common case - not clipping */ 42 | amp16 = (int16_t) amp; 43 | if (amp == amp16) 44 | return amp16; 45 | if (amp > INT16_MAX) 46 | return INT16_MAX; 47 | return INT16_MIN; 48 | } 49 | /*- End of function --------------------------------------------------------*/ 50 | 51 | static inline void block4(struct g722_band *band, int d) 52 | { 53 | int wd1; 54 | int wd2; 55 | int wd3; 56 | int i; 57 | 58 | /* Block 4, RECONS */ 59 | band->d[0] = d; 60 | band->r[0] = saturate(band->s + d); 61 | 62 | /* Block 4, PARREC */ 63 | band->p[0] = saturate(band->sz + d); 64 | 65 | /* Block 4, UPPOL2 */ 66 | for (i = 0; i < 3; i++) 67 | band->sg[i] = band->p[i] >> 15; 68 | wd1 = saturate(band->a[1] << 2); 69 | 70 | wd2 = (band->sg[0] == band->sg[1]) ? -wd1 : wd1; 71 | if (wd2 > 32767) 72 | wd2 = 32767; 73 | wd3 = (wd2 >> 7) + ((band->sg[0] == band->sg[2]) ? 128 : -128); 74 | wd3 += (band->a[2]*32512) >> 15; 75 | if (wd3 > 12288) 76 | wd3 = 12288; 77 | else if (wd3 < -12288) 78 | wd3 = -12288; 79 | band->ap[2] = wd3; 80 | 81 | /* Block 4, UPPOL1 */ 82 | band->sg[0] = band->p[0] >> 15; 83 | band->sg[1] = band->p[1] >> 15; 84 | wd1 = (band->sg[0] == band->sg[1]) ? 192 : -192; 85 | wd2 = (band->a[1]*32640) >> 15; 86 | 87 | band->ap[1] = saturate(wd1 + wd2); 88 | wd3 = saturate(15360 - band->ap[2]); 89 | if (band->ap[1] > wd3) 90 | band->ap[1] = wd3; 91 | else if (band->ap[1] < -wd3) 92 | band->ap[1] = -wd3; 93 | 94 | /* Block 4, UPZERO */ 95 | wd1 = (d == 0) ? 0 : 128; 96 | band->sg[0] = d >> 15; 97 | for (i = 1; i < 7; i++) 98 | { 99 | band->sg[i] = band->d[i] >> 15; 100 | wd2 = (band->sg[i] == band->sg[0]) ? wd1 : -wd1; 101 | wd3 = (band->b[i]*32640) >> 15; 102 | band->bp[i] = saturate(wd2 + wd3); 103 | } 104 | 105 | /* Block 4, DELAYA */ 106 | for (i = 6; i > 0; i--) 107 | { 108 | band->d[i] = band->d[i - 1]; 109 | band->b[i] = band->bp[i]; 110 | } 111 | 112 | for (i = 2; i > 0; i--) 113 | { 114 | band->r[i] = band->r[i - 1]; 115 | band->p[i] = band->p[i - 1]; 116 | band->a[i] = band->ap[i]; 117 | } 118 | 119 | /* Block 4, FILTEP */ 120 | wd1 = saturate(band->r[1] + band->r[1]); 121 | wd1 = (band->a[1]*wd1) >> 15; 122 | wd2 = saturate(band->r[2] + band->r[2]); 123 | wd2 = (band->a[2]*wd2) >> 15; 124 | band->sp = saturate(wd1 + wd2); 125 | 126 | /* Block 4, FILTEZ */ 127 | band->sz = 0; 128 | for (i = 6; i > 0; i--) 129 | { 130 | wd1 = saturate(band->d[i] + band->d[i]); 131 | band->sz += (band->b[i]*wd1) >> 15; 132 | } 133 | band->sz = saturate(band->sz); 134 | 135 | /* Block 4, PREDIC */ 136 | band->s = saturate(band->sp + band->sz); 137 | } 138 | /*- End of function --------------------------------------------------------*/ 139 | -------------------------------------------------------------------------------- /g722_decode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SpanDSP - a series of DSP components for telephony 3 | * 4 | * g722_decode.c - The ITU G.722 codec, decode part. 5 | * 6 | * Written by Steve Underwood 7 | * 8 | * Copyright (C) 2005 Steve Underwood 9 | * 10 | * Despite my general liking of the GPL, I place my own contributions 11 | * to this code in the public domain for the benefit of all mankind - 12 | * even the slimy ones who might try to proprietize my work and use it 13 | * to my detriment. 14 | * 15 | * Based in part on a single channel G.722 codec which is: 16 | * 17 | * Copyright (c) CMU 1993 18 | * Computer Science, Speech Group 19 | * Chengxiang Lu and Alex Hauptmann 20 | * 21 | * The Carnegie Mellon ADPCM program is Copyright (c) 1993 by Carnegie Mellon 22 | * University. Use of this program, for any research or commercial purpose, is 23 | * completely unrestricted. If you make use of or redistribute this material, 24 | * we would appreciate acknowlegement of its origin. 25 | */ 26 | 27 | /*! \file */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "g722_private.h" 35 | #include "g722_common.h" 36 | #include "g722.h" 37 | #include "g722_decoder.h" 38 | 39 | G722_DEC_CTX *g722_decoder_new(int rate, int options) 40 | { 41 | G722_DEC_CTX *s; 42 | 43 | if ((s = (G722_DEC_CTX *) malloc(sizeof(*s))) == NULL) 44 | return NULL; 45 | memset(s, 0, sizeof(*s)); 46 | if (rate == 48000) 47 | s->bits_per_sample = 6; 48 | else if (rate == 56000) 49 | s->bits_per_sample = 7; 50 | else 51 | s->bits_per_sample = 8; 52 | if ((options & G722_SAMPLE_RATE_8000)) 53 | s->eight_k = TRUE; 54 | if ((options & G722_PACKED) && s->bits_per_sample != 8) 55 | s->packed = TRUE; 56 | else 57 | s->packed = FALSE; 58 | s->band[0].det = 32; 59 | s->band[1].det = 8; 60 | return s; 61 | } 62 | /*- End of function --------------------------------------------------------*/ 63 | 64 | int g722_decoder_destroy(G722_DEC_CTX *s) 65 | { 66 | free(s); 67 | return 0; 68 | } 69 | /*- End of function --------------------------------------------------------*/ 70 | 71 | int g722_decode(G722_DEC_CTX *s, const uint8_t g722_data[], int len, int16_t amp[]) 72 | { 73 | static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 }; 74 | static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; 75 | static const int ilb[32] = 76 | { 77 | 2048, 2093, 2139, 2186, 2233, 2282, 2332, 78 | 2383, 2435, 2489, 2543, 2599, 2656, 2714, 79 | 2774, 2834, 2896, 2960, 3025, 3091, 3158, 80 | 3228, 3298, 3371, 3444, 3520, 3597, 3676, 81 | 3756, 3838, 3922, 4008 82 | }; 83 | static const int wh[3] = {0, -214, 798}; 84 | static const int rh2[4] = {2, 1, 2, 1}; 85 | static const int qm2[4] = {-7408, -1616, 7408, 1616}; 86 | static const int qm4[16] = 87 | { 88 | 0, -20456, -12896, -8968, 89 | -6288, -4240, -2584, -1200, 90 | 20456, 12896, 8968, 6288, 91 | 4240, 2584, 1200, 0 92 | }; 93 | static const int qm5[32] = 94 | { 95 | -280, -280, -23352, -17560, 96 | -14120, -11664, -9752, -8184, 97 | -6864, -5712, -4696, -3784, 98 | -2960, -2208, -1520, -880, 99 | 23352, 17560, 14120, 11664, 100 | 9752, 8184, 6864, 5712, 101 | 4696, 3784, 2960, 2208, 102 | 1520, 880, 280, -280 103 | }; 104 | static const int qm6[64] = 105 | { 106 | -136, -136, -136, -136, 107 | -24808, -21904, -19008, -16704, 108 | -14984, -13512, -12280, -11192, 109 | -10232, -9360, -8576, -7856, 110 | -7192, -6576, -6000, -5456, 111 | -4944, -4464, -4008, -3576, 112 | -3168, -2776, -2400, -2032, 113 | -1688, -1360, -1040, -728, 114 | 24808, 21904, 19008, 16704, 115 | 14984, 13512, 12280, 11192, 116 | 10232, 9360, 8576, 7856, 117 | 7192, 6576, 6000, 5456, 118 | 4944, 4464, 4008, 3576, 119 | 3168, 2776, 2400, 2032, 120 | 1688, 1360, 1040, 728, 121 | 432, 136, -432, -136 122 | }; 123 | static const int qmf_coeffs[12] = 124 | { 125 | 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, 126 | }; 127 | 128 | int dlowt; 129 | int rlow; 130 | int ihigh; 131 | int dhigh; 132 | int rhigh; 133 | int xout1; 134 | int xout2; 135 | int wd1; 136 | int wd2; 137 | int wd3; 138 | int code; 139 | int outlen; 140 | int i; 141 | int j; 142 | 143 | outlen = 0; 144 | rhigh = 0; 145 | for (j = 0; j < len; ) 146 | { 147 | if (s->packed) 148 | { 149 | /* Unpack the code bits */ 150 | if (s->in_bits < s->bits_per_sample) 151 | { 152 | s->in_buffer |= (g722_data[j++] << s->in_bits); 153 | s->in_bits += 8; 154 | } 155 | code = s->in_buffer & ((1 << s->bits_per_sample) - 1); 156 | s->in_buffer >>= s->bits_per_sample; 157 | s->in_bits -= s->bits_per_sample; 158 | } 159 | else 160 | { 161 | code = g722_data[j++]; 162 | } 163 | 164 | switch (s->bits_per_sample) 165 | { 166 | default: 167 | case 8: 168 | wd1 = code & 0x3F; 169 | ihigh = (code >> 6) & 0x03; 170 | wd2 = qm6[wd1]; 171 | wd1 >>= 2; 172 | break; 173 | case 7: 174 | wd1 = code & 0x1F; 175 | ihigh = (code >> 5) & 0x03; 176 | wd2 = qm5[wd1]; 177 | wd1 >>= 1; 178 | break; 179 | case 6: 180 | wd1 = code & 0x0F; 181 | ihigh = (code >> 4) & 0x03; 182 | wd2 = qm4[wd1]; 183 | break; 184 | } 185 | /* Block 5L, LOW BAND INVQBL */ 186 | wd2 = (s->band[0].det*wd2) >> 15; 187 | /* Block 5L, RECONS */ 188 | rlow = s->band[0].s + wd2; 189 | /* Block 6L, LIMIT */ 190 | if (rlow > 16383) 191 | rlow = 16383; 192 | else if (rlow < -16384) 193 | rlow = -16384; 194 | 195 | /* Block 2L, INVQAL */ 196 | wd2 = qm4[wd1]; 197 | dlowt = (s->band[0].det*wd2) >> 15; 198 | 199 | /* Block 3L, LOGSCL */ 200 | wd2 = rl42[wd1]; 201 | wd1 = (s->band[0].nb*127) >> 7; 202 | wd1 += wl[wd2]; 203 | if (wd1 < 0) 204 | wd1 = 0; 205 | else if (wd1 > 18432) 206 | wd1 = 18432; 207 | s->band[0].nb = wd1; 208 | 209 | /* Block 3L, SCALEL */ 210 | wd1 = (s->band[0].nb >> 6) & 31; 211 | wd2 = 8 - (s->band[0].nb >> 11); 212 | wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); 213 | s->band[0].det = wd3 << 2; 214 | 215 | block4(&s->band[0], dlowt); 216 | 217 | if (!s->eight_k) 218 | { 219 | /* Block 2H, INVQAH */ 220 | wd2 = qm2[ihigh]; 221 | dhigh = (s->band[1].det*wd2) >> 15; 222 | /* Block 5H, RECONS */ 223 | rhigh = dhigh + s->band[1].s; 224 | /* Block 6H, LIMIT */ 225 | if (rhigh > 16383) 226 | rhigh = 16383; 227 | else if (rhigh < -16384) 228 | rhigh = -16384; 229 | 230 | /* Block 2H, INVQAH */ 231 | wd2 = rh2[ihigh]; 232 | wd1 = (s->band[1].nb*127) >> 7; 233 | wd1 += wh[wd2]; 234 | if (wd1 < 0) 235 | wd1 = 0; 236 | else if (wd1 > 22528) 237 | wd1 = 22528; 238 | s->band[1].nb = wd1; 239 | 240 | /* Block 3H, SCALEH */ 241 | wd1 = (s->band[1].nb >> 6) & 31; 242 | wd2 = 10 - (s->band[1].nb >> 11); 243 | wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); 244 | s->band[1].det = wd3 << 2; 245 | 246 | block4(&s->band[1], dhigh); 247 | } 248 | 249 | if (s->itu_test_mode) 250 | { 251 | amp[outlen++] = (int16_t) (rlow << 1); 252 | amp[outlen++] = (int16_t) (rhigh << 1); 253 | } 254 | else 255 | { 256 | if (s->eight_k) 257 | { 258 | amp[outlen++] = (int16_t) (rlow << 1); 259 | } 260 | else 261 | { 262 | /* Apply the receive QMF */ 263 | for (i = 0; i < 22; i++) 264 | s->x[i] = s->x[i + 2]; 265 | s->x[22] = rlow + rhigh; 266 | s->x[23] = rlow - rhigh; 267 | 268 | xout1 = 0; 269 | xout2 = 0; 270 | for (i = 0; i < 12; i++) 271 | { 272 | xout2 += s->x[2*i]*qmf_coeffs[i]; 273 | xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i]; 274 | } 275 | amp[outlen++] = saturate(xout1 >> 11); 276 | amp[outlen++] = saturate(xout2 >> 11); 277 | } 278 | } 279 | } 280 | return outlen; 281 | } 282 | /*- End of function --------------------------------------------------------*/ 283 | /*- End of file ------------------------------------------------------------*/ 284 | -------------------------------------------------------------------------------- /g722_decoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * g722.h - The ITU G.722 codec. 3 | * 4 | * Written by Steve Underwood 5 | * 6 | * Copyright (C) 2005 Steve Underwood 7 | * 8 | * Despite my general liking of the GPL, I place my own contributions 9 | * to this code in the public domain for the benefit of all mankind - 10 | * even the slimy ones who might try to proprietize my work and use it 11 | * to my detriment. 12 | * 13 | * Based on a single channel G.722 codec which is: 14 | * 15 | ***** Copyright (c) CMU 1993 ***** 16 | * Computer Science, Speech Group 17 | * Chengxiang Lu and Alex Hauptmann 18 | * 19 | * $Id: g722_decoder.h,v 1.1 2012/08/07 11:33:45 sobomax Exp $ 20 | */ 21 | 22 | 23 | /*! \file */ 24 | 25 | #pragma once 26 | 27 | #include "g722.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #ifndef _G722_DEC_CTX_DEFINED 34 | typedef void G722_DEC_CTX; 35 | #define _G722_DEC_CTX_DEFINED 36 | #endif 37 | 38 | G722_DEC_CTX *g722_decoder_new(int rate, int options); 39 | int g722_decoder_destroy(G722_DEC_CTX *s); 40 | int g722_decode(G722_DEC_CTX *s, const uint8_t g722_data[], int len, int16_t amp[]); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /g722_encode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * g722_encode.c - The ITU G.722 codec, encode part. 3 | * 4 | * Written by Steve Underwood 5 | * 6 | * Copyright (C) 2005 Steve Underwood 7 | * 8 | * All rights reserved. 9 | * 10 | * Despite my general liking of the GPL, I place my own contributions 11 | * to this code in the public domain for the benefit of all mankind - 12 | * even the slimy ones who might try to proprietize my work and use it 13 | * to my detriment. 14 | * 15 | * Based on a single channel 64kbps only G.722 codec which is: 16 | * 17 | ***** Copyright (c) CMU 1993 ***** 18 | * Computer Science, Speech Group 19 | * Chengxiang Lu and Alex Hauptmann 20 | * 21 | * The Carnegie Mellon ADPCM program is Copyright (c) 1993 by Carnegie Mellon 22 | * University. Use of this program, for any research or commercial purpose, is 23 | * completely unrestricted. If you make use of or redistribute this material, 24 | * we would appreciate acknowlegement of its origin. 25 | ***** 26 | */ 27 | 28 | /*! \file */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "g722_private.h" 36 | #include "g722_common.h" 37 | #include "g722_encoder.h" 38 | 39 | G722_ENC_CTX * 40 | g722_encoder_new(int rate, int options) 41 | { 42 | G722_ENC_CTX *s; 43 | 44 | if ((s = (G722_ENC_CTX *) malloc(sizeof(*s))) == NULL) 45 | return NULL; 46 | memset(s, 0, sizeof(*s)); 47 | if (rate == 48000) 48 | s->bits_per_sample = 6; 49 | else if (rate == 56000) 50 | s->bits_per_sample = 7; 51 | else 52 | s->bits_per_sample = 8; 53 | if ((options & G722_SAMPLE_RATE_8000)) 54 | s->eight_k = TRUE; 55 | if ((options & G722_PACKED) && s->bits_per_sample != 8) 56 | s->packed = TRUE; 57 | else 58 | s->packed = FALSE; 59 | s->band[0].det = 32; 60 | s->band[1].det = 8; 61 | return s; 62 | } 63 | /*- End of function --------------------------------------------------------*/ 64 | 65 | int g722_encoder_destroy(G722_ENC_CTX *s) 66 | { 67 | free(s); 68 | return 0; 69 | } 70 | /*- End of function --------------------------------------------------------*/ 71 | 72 | int g722_encode(G722_ENC_CTX *s, const int16_t amp[], int len, uint8_t g722_data[]) 73 | { 74 | static const int q6[32] = 75 | { 76 | 0, 35, 72, 110, 150, 190, 233, 276, 77 | 323, 370, 422, 473, 530, 587, 650, 714, 78 | 786, 858, 940, 1023, 1121, 1219, 1339, 1458, 79 | 1612, 1765, 1980, 2195, 2557, 2919, 0, 0 80 | }; 81 | static const int iln[32] = 82 | { 83 | 0, 63, 62, 31, 30, 29, 28, 27, 84 | 26, 25, 24, 23, 22, 21, 20, 19, 85 | 18, 17, 16, 15, 14, 13, 12, 11, 86 | 10, 9, 8, 7, 6, 5, 4, 0 87 | }; 88 | static const int ilp[32] = 89 | { 90 | 0, 61, 60, 59, 58, 57, 56, 55, 91 | 54, 53, 52, 51, 50, 49, 48, 47, 92 | 46, 45, 44, 43, 42, 41, 40, 39, 93 | 38, 37, 36, 35, 34, 33, 32, 0 94 | }; 95 | static const int wl[8] = 96 | { 97 | -60, -30, 58, 172, 334, 538, 1198, 3042 98 | }; 99 | static const int rl42[16] = 100 | { 101 | 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 102 | }; 103 | static const int ilb[32] = 104 | { 105 | 2048, 2093, 2139, 2186, 2233, 2282, 2332, 106 | 2383, 2435, 2489, 2543, 2599, 2656, 2714, 107 | 2774, 2834, 2896, 2960, 3025, 3091, 3158, 108 | 3228, 3298, 3371, 3444, 3520, 3597, 3676, 109 | 3756, 3838, 3922, 4008 110 | }; 111 | static const int qm4[16] = 112 | { 113 | 0, -20456, -12896, -8968, 114 | -6288, -4240, -2584, -1200, 115 | 20456, 12896, 8968, 6288, 116 | 4240, 2584, 1200, 0 117 | }; 118 | static const int qm2[4] = 119 | { 120 | -7408, -1616, 7408, 1616 121 | }; 122 | static const int qmf_coeffs[12] = 123 | { 124 | 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, 125 | }; 126 | static const int ihn[3] = {0, 1, 0}; 127 | static const int ihp[3] = {0, 3, 2}; 128 | static const int wh[3] = {0, -214, 798}; 129 | static const int rh2[4] = {2, 1, 2, 1}; 130 | 131 | int dlow; 132 | int dhigh; 133 | int el; 134 | int wd; 135 | int wd1; 136 | int ril; 137 | int wd2; 138 | int il4; 139 | int ih2; 140 | int wd3; 141 | int eh; 142 | int mih; 143 | int i; 144 | int j; 145 | /* Low and high band PCM from the QMF */ 146 | int xlow; 147 | int xhigh; 148 | int g722_bytes; 149 | /* Even and odd tap accumulators */ 150 | int sumeven; 151 | int sumodd; 152 | int ihigh; 153 | int ilow; 154 | int code; 155 | 156 | g722_bytes = 0; 157 | xhigh = 0; 158 | for (j = 0; j < len; ) 159 | { 160 | if (s->itu_test_mode) 161 | { 162 | xlow = 163 | xhigh = amp[j++] >> 1; 164 | } 165 | else 166 | { 167 | if (s->eight_k) 168 | { 169 | xlow = amp[j++] >> 1; 170 | } 171 | else 172 | { 173 | /* Apply the transmit QMF */ 174 | /* Shuffle the buffer down */ 175 | for (i = 0; i < 22; i++) 176 | s->x[i] = s->x[i + 2]; 177 | s->x[22] = amp[j++]; 178 | s->x[23] = amp[j++]; 179 | 180 | /* Discard every other QMF output */ 181 | sumeven = 0; 182 | sumodd = 0; 183 | for (i = 0; i < 12; i++) 184 | { 185 | sumodd += s->x[2*i]*qmf_coeffs[i]; 186 | sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i]; 187 | } 188 | xlow = (sumeven + sumodd) >> 14; 189 | xhigh = (sumeven - sumodd) >> 14; 190 | } 191 | } 192 | /* Block 1L, SUBTRA */ 193 | el = saturate(xlow - s->band[0].s); 194 | 195 | /* Block 1L, QUANTL */ 196 | wd = (el >= 0) ? el : -(el + 1); 197 | 198 | for (i = 1; i < 30; i++) 199 | { 200 | wd1 = (q6[i]*s->band[0].det) >> 12; 201 | if (wd < wd1) 202 | break; 203 | } 204 | ilow = (el < 0) ? iln[i] : ilp[i]; 205 | 206 | /* Block 2L, INVQAL */ 207 | ril = ilow >> 2; 208 | wd2 = qm4[ril]; 209 | dlow = (s->band[0].det*wd2) >> 15; 210 | 211 | /* Block 3L, LOGSCL */ 212 | il4 = rl42[ril]; 213 | wd = (s->band[0].nb*127) >> 7; 214 | s->band[0].nb = wd + wl[il4]; 215 | if (s->band[0].nb < 0) 216 | s->band[0].nb = 0; 217 | else if (s->band[0].nb > 18432) 218 | s->band[0].nb = 18432; 219 | 220 | /* Block 3L, SCALEL */ 221 | wd1 = (s->band[0].nb >> 6) & 31; 222 | wd2 = 8 - (s->band[0].nb >> 11); 223 | wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); 224 | s->band[0].det = wd3 << 2; 225 | 226 | block4(&s->band[0], dlow); 227 | 228 | if (s->eight_k) 229 | { 230 | /* Just leave the high bits as zero */ 231 | code = (0xC0 | ilow) >> (8 - s->bits_per_sample); 232 | } 233 | else 234 | { 235 | /* Block 1H, SUBTRA */ 236 | eh = saturate(xhigh - s->band[1].s); 237 | 238 | /* Block 1H, QUANTH */ 239 | wd = (eh >= 0) ? eh : -(eh + 1); 240 | wd1 = (564*s->band[1].det) >> 12; 241 | mih = (wd >= wd1) ? 2 : 1; 242 | ihigh = (eh < 0) ? ihn[mih] : ihp[mih]; 243 | 244 | /* Block 2H, INVQAH */ 245 | wd2 = qm2[ihigh]; 246 | dhigh = (s->band[1].det*wd2) >> 15; 247 | 248 | /* Block 3H, LOGSCH */ 249 | ih2 = rh2[ihigh]; 250 | wd = (s->band[1].nb*127) >> 7; 251 | s->band[1].nb = wd + wh[ih2]; 252 | if (s->band[1].nb < 0) 253 | s->band[1].nb = 0; 254 | else if (s->band[1].nb > 22528) 255 | s->band[1].nb = 22528; 256 | 257 | /* Block 3H, SCALEH */ 258 | wd1 = (s->band[1].nb >> 6) & 31; 259 | wd2 = 10 - (s->band[1].nb >> 11); 260 | wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); 261 | s->band[1].det = wd3 << 2; 262 | 263 | block4(&s->band[1], dhigh); 264 | code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample); 265 | } 266 | 267 | if (s->packed) 268 | { 269 | /* Pack the code bits */ 270 | s->out_buffer |= (code << s->out_bits); 271 | s->out_bits += s->bits_per_sample; 272 | if (s->out_bits >= 8) 273 | { 274 | g722_data[g722_bytes++] = (uint8_t) (s->out_buffer & 0xFF); 275 | s->out_bits -= 8; 276 | s->out_buffer >>= 8; 277 | } 278 | } 279 | else 280 | { 281 | g722_data[g722_bytes++] = (uint8_t) code; 282 | } 283 | } 284 | return g722_bytes; 285 | } 286 | /*- End of function --------------------------------------------------------*/ 287 | /*- End of file ------------------------------------------------------------*/ 288 | -------------------------------------------------------------------------------- /g722_encoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * g722.h - The ITU G.722 codec. 3 | * 4 | * Written by Steve Underwood 5 | * 6 | * Copyright (C) 2005 Steve Underwood 7 | * 8 | * Despite my general liking of the GPL, I place my own contributions 9 | * to this code in the public domain for the benefit of all mankind - 10 | * even the slimy ones who might try to proprietize my work and use it 11 | * to my detriment. 12 | * 13 | * Based on a single channel G.722 codec which is: 14 | * 15 | ***** Copyright (c) CMU 1993 ***** 16 | * Computer Science, Speech Group 17 | * Chengxiang Lu and Alex Hauptmann 18 | * 19 | * $Id: g722_encoder.h,v 1.1 2012/08/07 11:33:45 sobomax Exp $ 20 | */ 21 | 22 | 23 | /*! \file */ 24 | 25 | #pragma once 26 | 27 | #include "g722.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #ifndef _G722_ENC_CTX_DEFINED 34 | typedef void G722_ENC_CTX; 35 | #define _G722_ENC_CTX_DEFINED 36 | #endif 37 | 38 | G722_ENC_CTX *g722_encoder_new(int rate, int options); 39 | int g722_encoder_destroy(G722_ENC_CTX *s); 40 | int g722_encode(G722_ENC_CTX *s, const int16_t amp[], int len, uint8_t g722_data[]); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /g722_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * g722.h - The ITU G.722 codec. 3 | * 4 | * Written by Steve Underwood 5 | * 6 | * Copyright (C) 2005 Steve Underwood 7 | * 8 | * Despite my general liking of the GPL, I place my own contributions 9 | * to this code in the public domain for the benefit of all mankind - 10 | * even the slimy ones who might try to proprietize my work and use it 11 | * to my detriment. 12 | * 13 | * Based on a single channel G.722 codec which is: 14 | * 15 | ***** Copyright (c) CMU 1993 ***** 16 | * Computer Science, Speech Group 17 | * Chengxiang Lu and Alex Hauptmann 18 | */ 19 | 20 | 21 | /*! \file */ 22 | 23 | #pragma once 24 | 25 | /*! \page g722_page G.722 encoding and decoding 26 | \section g722_page_sec_1 What does it do? 27 | The G.722 module is a bit exact implementation of the ITU G.722 specification for all three 28 | specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests. 29 | 30 | To allow fast and flexible interworking with narrow band telephony, the encoder and decoder 31 | support an option for the linear audio to be an 8k samples/second stream. In this mode the 32 | codec is considerably faster, and still fully compatible with wideband terminals using G.722. 33 | 34 | \section g722_page_sec_2 How does it work? 35 | ???. 36 | */ 37 | 38 | typedef struct g722_encode_state G722_ENC_CTX; 39 | #define _G722_ENC_CTX_DEFINED 40 | typedef struct g722_decode_state G722_DEC_CTX; 41 | #define _G722_DEC_CTX_DEFINED 42 | 43 | struct g722_band 44 | { 45 | int s; 46 | int sp; 47 | int sz; 48 | int r[3]; 49 | int a[3]; 50 | int ap[3]; 51 | int p[3]; 52 | int d[7]; 53 | int b[7]; 54 | int bp[7]; 55 | int sg[7]; 56 | int nb; 57 | int det; 58 | }; 59 | 60 | struct g722_encode_state 61 | { 62 | /*! TRUE if the operating in the special ITU test mode, with the band split filters 63 | disabled. */ 64 | int itu_test_mode; 65 | /*! TRUE if the G.722 data is packed */ 66 | int packed; 67 | /*! TRUE if encode from 8k samples/second */ 68 | int eight_k; 69 | /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ 70 | int bits_per_sample; 71 | 72 | /*! Signal history for the QMF */ 73 | int x[24]; 74 | 75 | struct g722_band band[2]; 76 | 77 | unsigned int in_buffer; 78 | int in_bits; 79 | unsigned int out_buffer; 80 | int out_bits; 81 | }; 82 | 83 | struct g722_decode_state 84 | { 85 | /*! TRUE if the operating in the special ITU test mode, with the band split filters 86 | disabled. */ 87 | int itu_test_mode; 88 | /*! TRUE if the G.722 data is packed */ 89 | int packed; 90 | /*! TRUE if decode to 8k samples/second */ 91 | int eight_k; 92 | /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ 93 | int bits_per_sample; 94 | 95 | /*! Signal history for the QMF */ 96 | int x[24]; 97 | 98 | struct g722_band band[2]; 99 | 100 | unsigned int in_buffer; 101 | int in_bits; 102 | unsigned int out_buffer; 103 | int out_bits; 104 | }; 105 | -------------------------------------------------------------------------------- /ld_sugar/Symbol.map: -------------------------------------------------------------------------------- 1 | LIBG722_20160729174323 { 2 | g722_encoder_new; 3 | g722_encoder_destroy; 4 | g722_encode; 5 | 6 | g722_decoder_new; 7 | g722_decoder_destroy; 8 | g722_decode; 9 | }; 10 | -------------------------------------------------------------------------------- /ld_sugar/Versions.def: -------------------------------------------------------------------------------- 1 | LIBG722_20160729174323 { 2 | }; 3 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=libg722 2 | version=1.1.1 3 | author=Milton Anderson,Chengxiang Lu, and Alex Hauptmann 4 | maintainer=Phil Schatzmann 5 | sentence=G.722 codec 6 | paragraph=G.722 encoding and decoding 7 | category=Signal Input/Output 8 | url=https://github.com/sippy/libg722 9 | architectures=* 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "numpy"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /python/G722_mod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 6 | #include 7 | 8 | #include "g722_encoder.h" 9 | #include "g722_decoder.h" 10 | 11 | #define MODULE_BASENAME G722 12 | 13 | #define CONCATENATE_DETAIL(x, y) x##y 14 | #define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y) 15 | 16 | #if !defined(DEBUG_MOD) 17 | #define MODULE_NAME MODULE_BASENAME 18 | #else 19 | #define MODULE_NAME CONCATENATE(MODULE_BASENAME, _debug) 20 | #endif 21 | 22 | #define STRINGIFY(x) #x 23 | #define TOSTRING(x) STRINGIFY(x) 24 | 25 | #define MODULE_NAME_STR TOSTRING(MODULE_NAME) 26 | #define PY_INIT_FUNC CONCATENATE(PyInit_, MODULE_NAME) 27 | 28 | typedef struct { 29 | PyObject_HEAD 30 | G722_DEC_CTX *g722_dctx; 31 | G722_ENC_CTX *g722_ectx; 32 | int sample_rate; 33 | int bit_rate; 34 | } PyG722; 35 | 36 | static int PyG722_init(PyG722* self, PyObject* args, PyObject* kwds) { 37 | int sample_rate, bit_rate, options; 38 | static char *kwlist[] = {"sample_rate", "bit_rate", NULL}; 39 | 40 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, &sample_rate, &bit_rate)) { 41 | return -1; 42 | } 43 | 44 | if (sample_rate != 8000 && sample_rate != 16000) { 45 | PyErr_SetString(PyExc_ValueError, "Sample rate must be 8000 or 16000"); 46 | return -1; 47 | } 48 | 49 | if (bit_rate != 48000 && bit_rate != 56000 && bit_rate != 64000) { 50 | PyErr_SetString(PyExc_ValueError, "Bit rate must be 48000, 56000 or 64000"); 51 | return -1; 52 | } 53 | options = (sample_rate == 8000) ? G722_SAMPLE_RATE_8000 : G722_DEFAULT; 54 | self->g722_ectx = g722_encoder_new(bit_rate, options); 55 | if(self->g722_ectx == NULL) { 56 | PyErr_SetString(PyExc_RuntimeError, "Error initializing G.722 encoder"); 57 | return -1; 58 | } 59 | self->g722_dctx = g722_decoder_new(bit_rate, options); 60 | if(self->g722_dctx == NULL) { 61 | g722_encoder_destroy(self->g722_ectx); 62 | PyErr_SetString(PyExc_RuntimeError, "Error initializing G.722 decoder"); 63 | return -1; 64 | } 65 | self->sample_rate = sample_rate; 66 | self->bit_rate = bit_rate; 67 | 68 | return 0; 69 | } 70 | 71 | // The __del__ method for PyG722 objects 72 | static void PyG722_dealloc(PyG722* self) { 73 | g722_encoder_destroy(self->g722_ectx); 74 | g722_decoder_destroy(self->g722_dctx); 75 | Py_TYPE(self)->tp_free((PyObject*)self); 76 | } 77 | 78 | // The encode method for PyG722 objects 79 | static PyObject * 80 | PyG722_encode(PyG722* self, PyObject* args) { 81 | PyObject* item; 82 | PyObject* seq; 83 | int16_t* array; 84 | Py_ssize_t length, i, olength; 85 | 86 | PyObject *rval = NULL; 87 | if (!PyArg_ParseTuple(args, "O", &item)) { 88 | PyErr_SetString(PyExc_TypeError, "Takes exactly one argument"); 89 | goto e0; 90 | } 91 | 92 | if (PyArray_Check(item) && PyArray_TYPE((PyArrayObject*)item) == NPY_INT16) { 93 | array = (int16_t *)PyArray_DATA((PyArrayObject*)item); 94 | length = PyArray_SIZE((PyArrayObject*)item); 95 | } else { 96 | // Convert PyObject to a sequence if possible 97 | seq = PySequence_Fast(item, "Expected a sequence"); 98 | if (seq == NULL) { 99 | PyErr_SetString(PyExc_TypeError, "Expected a sequence"); 100 | goto e0; 101 | } 102 | 103 | // Get the length of the sequence 104 | length = PySequence_Size(seq); 105 | if (length == -1) { 106 | PyErr_SetString(PyExc_TypeError, "Error getting sequence length"); 107 | goto e1; 108 | } 109 | 110 | // Allocate memory for the int array 111 | array = (int16_t*) malloc(length * sizeof(array[0])); 112 | if (!array) { 113 | rval = PyErr_NoMemory(); 114 | goto e1; 115 | } 116 | for (i = 0; i < length; i++) { 117 | PyObject* temp_item = PySequence_Fast_GET_ITEM(seq, i); // Borrowed reference, no need to Py_DECREF 118 | long tv = PyLong_AsLong(temp_item); 119 | if (PyErr_Occurred()) { 120 | goto e2; 121 | } 122 | if (tv < -32768 || tv > 32767) { 123 | PyErr_SetString(PyExc_ValueError, "Value out of range"); 124 | goto e2; 125 | } 126 | array[i] = (int16_t)tv; 127 | } 128 | } 129 | olength = self->sample_rate == 8000 ? length : length / 2; 130 | PyObject *obuf_obj = PyBytes_FromStringAndSize(NULL, olength); 131 | if (obuf_obj == NULL) { 132 | rval = PyErr_NoMemory(); 133 | goto e2; 134 | } 135 | uint8_t *buffer = (uint8_t *)PyBytes_AsString(obuf_obj); 136 | if (!buffer) { 137 | goto e3; 138 | } 139 | int obytes = g722_encode(self->g722_ectx, array, length, buffer); 140 | assert(obytes == olength); 141 | rval = obuf_obj; 142 | goto e2; 143 | e3: 144 | Py_DECREF(obuf_obj); 145 | e2: 146 | if (!PyArray_Check(item)) { 147 | free(array); 148 | } 149 | e1: 150 | if (!PyArray_Check(item)) { 151 | Py_DECREF(seq); 152 | } 153 | e0: 154 | return rval; 155 | } 156 | 157 | typedef struct { 158 | PyObject_HEAD 159 | void *data; // Pointer to the data buffer 160 | } PyDataOwner; 161 | 162 | static void 163 | DataOwner_dealloc(PyDataOwner* self) { 164 | free(self->data); // Free the memory when the object is deallocated 165 | Py_TYPE(self)->tp_free((PyObject*)self); 166 | } 167 | 168 | static PyTypeObject PyDataOwnerType = { 169 | PyVarObject_HEAD_INIT(NULL, 0) 170 | .tp_name = "DataOwner", 171 | .tp_basicsize = sizeof(PyDataOwner), 172 | .tp_flags = Py_TPFLAGS_DEFAULT, 173 | .tp_dealloc = (destructor)DataOwner_dealloc, 174 | }; 175 | 176 | // The get method for PyG722 objects 177 | static PyObject * 178 | PyG722_decode(PyG722* self, PyObject* args) { 179 | PyObject* item; 180 | uint8_t* buffer; 181 | int16_t* array; 182 | Py_ssize_t length, olength; 183 | 184 | // Parse the input tuple to get a bytes object 185 | if (!PyArg_ParseTuple(args, "O", &item)) { 186 | PyErr_SetString(PyExc_TypeError, "Argument must be a bytes object"); 187 | return NULL; 188 | } 189 | 190 | // Ensure the object is a bytes object 191 | if (!PyBytes_Check(item)) { 192 | PyErr_SetString(PyExc_TypeError, "Argument must be a bytes object"); 193 | return NULL; 194 | } 195 | 196 | // Get the buffer and its length from the bytes object 197 | buffer = (uint8_t *)PyBytes_AsString(item); 198 | if (!buffer) { 199 | return NULL; // PyErr_SetString is called by PyBytes_AsString if something goes wrong 200 | } 201 | length = PyBytes_Size(item); 202 | if (length < 0) { 203 | return NULL; // PyErr_SetString is called by PyBytes_Size if something goes wrong 204 | } 205 | olength = self->sample_rate == 8000 ? length : length * 2; 206 | array = (int16_t*) malloc(olength * sizeof(array[0])); 207 | if (array == NULL) { 208 | return PyErr_NoMemory(); 209 | } 210 | g722_decode(self->g722_dctx, buffer, length, array); 211 | 212 | PyDataOwner* owner = PyObject_New(PyDataOwner, &PyDataOwnerType); 213 | if (!owner) { 214 | free(array); 215 | return PyErr_NoMemory(); 216 | } 217 | owner->data = array; 218 | 219 | // Create a new numpy array to hold the integers 220 | npy_intp dims[1] = {olength}; 221 | PyObject* numpy_array = PyArray_SimpleNewFromData(1, dims, NPY_INT16, (void *)array); 222 | if (numpy_array == NULL) goto e1; 223 | PyArray_SetBaseObject((PyArrayObject*)numpy_array, (PyObject*)owner); 224 | return numpy_array; 225 | e1: 226 | Py_DECREF(owner); 227 | return NULL; 228 | } 229 | 230 | static PyMethodDef PyG722_methods[] = { 231 | {"encode", (PyCFunction)PyG722_encode, METH_VARARGS, "Encode signed linear PCM samples to G.722 format"}, 232 | {"decode", (PyCFunction)PyG722_decode, METH_VARARGS, "Decode G.722 format to signed linear PCM samples"}, 233 | {NULL} // Sentinel 234 | }; 235 | 236 | static PyTypeObject PyG722Type = { 237 | PyVarObject_HEAD_INIT(NULL, 0) 238 | .tp_name = MODULE_NAME_STR "." MODULE_NAME_STR, 239 | .tp_doc = "Implementation of ITU-T G.722 audio codec in Python using C extension.", 240 | .tp_basicsize = sizeof(PyG722), 241 | .tp_itemsize = 0, 242 | .tp_flags = Py_TPFLAGS_DEFAULT, 243 | .tp_new = PyType_GenericNew, 244 | .tp_init = (initproc)PyG722_init, 245 | .tp_dealloc = (destructor)PyG722_dealloc, 246 | .tp_methods = PyG722_methods, 247 | }; 248 | 249 | static struct PyModuleDef G722_module = { 250 | PyModuleDef_HEAD_INIT, 251 | .m_name = MODULE_NAME_STR, 252 | .m_doc = "Python interface for the ITU-T G.722 audio codec.", 253 | .m_size = -1, 254 | }; 255 | 256 | // Module initialization function 257 | PyMODINIT_FUNC PY_INIT_FUNC(void) { 258 | PyObject* module; 259 | if (PyType_Ready(&PyG722Type) < 0) 260 | return NULL; 261 | 262 | module = PyModule_Create(&G722_module); 263 | if (module == NULL) 264 | return NULL; 265 | 266 | Py_INCREF(&PyG722Type); 267 | PyModule_AddObject(module, MODULE_NAME_STR, (PyObject*)&PyG722Type); 268 | 269 | import_array(); 270 | 271 | if (PyType_Ready(&PyDataOwnerType) < 0) 272 | return NULL; 273 | 274 | return module; 275 | } 276 | 277 | -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | -------------------------------------------------------------------------------- /python/symbols.map: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | PyInit_G722*; 4 | local: 5 | *; 6 | }; 7 | -------------------------------------------------------------------------------- /scripts/build/install_depends_wheels.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | PYTHON_CMD="${PYTHON_CMD:-"python${PY_VER}"}" 6 | 7 | ${PYTHON_CMD} -m ensurepip --upgrade 8 | ${PYTHON_CMD} -m pip install --upgrade pip 9 | ${PYTHON_CMD} -m pip install --upgrade build setuptools wheel auditwheel 10 | -------------------------------------------------------------------------------- /scripts/do-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | TEST_CMD="${1:-"./test"}" 7 | MDIR="${2:-"`dirname ${0}`/.."}" 8 | TDDIR="${MDIR}/test_data" 9 | 10 | ${TEST_CMD} ${TDDIR}/test.g722 test.raw.out 11 | ${TEST_CMD} --sln16k ${TDDIR}/test.g722 test.raw.16k.out 12 | ${TEST_CMD} --enc --sln16k --bend ${TDDIR}/pcminb.dat pcminb.g722.out 13 | ${TEST_CMD} --sln16k --bend pcminb.g722.out pcminb.raw.16k.out 14 | ${TEST_CMD} --enc test.raw.out test.g722.out 15 | ${TEST_CMD} --sln16k ${TDDIR}/fullscale.g722 fullscale.raw.out 16 | openssl sha256 -r test.raw.out test.raw.16k.out pcminb.g722.out pcminb.raw.16k.out \ 17 | test.g722.out fullscale.raw.out | diff ${TDDIR}/test.checksum - 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import exit, argv 4 | from distutils.core import setup, Extension 5 | from os.path import exists, realpath, dirname, join as path_join 6 | from sys import argv as sys_argv, path as sys_path 7 | 8 | from sysconfig import get_platform 9 | 10 | sys_path.insert(0, realpath(dirname(__file__))) 11 | from build_tools.CheckVersion import CheckVersion 12 | 13 | def main(): 14 | 15 | mod_name = 'G722' 16 | mod_name_dbg = mod_name + '_debug' 17 | 18 | mod_dir = dirname(realpath(sys_argv[0])) 19 | src_dir = './' if exists('g722_decode.c') else '../' 20 | mod_fname = mod_name + '_mod.c' 21 | mod_dir = '' if exists(mod_fname) else 'python/' 22 | 23 | def np_include(): 24 | import numpy as np 25 | return f'-I{np.get_include()}' 26 | 27 | is_win = get_platform().startswith('win') 28 | is_mac = get_platform().startswith('macosx-') 29 | 30 | compile_args = [f'-I{src_dir}', np_include()] 31 | if not is_win: 32 | compile_args.append('-flto') 33 | link_args = ['-flto',] if not is_win else [] 34 | if not is_mac and not is_win: 35 | smap_fname = f'{mod_dir}symbols.map' 36 | link_args.append(f'-Wl,--version-script={smap_fname}') 37 | debug_cflags = ['-g3', '-O0', '-DDEBUG_MOD'] 38 | debug_link_args = ['-g3', '-O0'] 39 | mod_common_args = { 40 | 'sources': [mod_dir + mod_fname, src_dir + 'g722_decode.c', src_dir + 'g722_encode.c'], 41 | 'extra_compile_args': compile_args, 42 | 'extra_link_args': link_args 43 | } 44 | mod_debug_args = mod_common_args.copy() 45 | mod_debug_args['extra_compile_args'] = mod_debug_args['extra_compile_args'] + debug_cflags 46 | mod_debug_args['extra_link_args'] = mod_debug_args['extra_link_args'] + debug_link_args 47 | 48 | module1 = Extension(mod_name, **mod_common_args) 49 | module2 = Extension(mod_name_dbg, **mod_debug_args) 50 | 51 | requirements = [x.strip() for x in open(mod_dir + "requirements.txt", "r").readlines()] 52 | with open(src_dir + "README.md", "r") as fh: 53 | long_description = fh.read() 54 | 55 | kwargs = { 56 | 'name':mod_name, 57 | 'version':'1.2.1', 58 | 'description':'This is a package for G.722 module', 59 | 'long_description': long_description, 60 | 'long_description_content_type': "text/markdown", 61 | 'author':'Maksym Sobolyev', 62 | 'author_email':'sobomax@sippysoft.com', 63 | 'url':'https://github.com/sippy/libg722', 64 | 'ext_modules': [module1, module2], 65 | 'install_requires': requirements, 66 | 'cmdclass': {'checkversion': CheckVersion}, 67 | 'license': 'Public-Domain', 68 | 'classifiers': [ 69 | 'Operating System :: OS Independent', 70 | 'Programming Language :: C', 71 | 'Programming Language :: Python' 72 | ] 73 | } 74 | 75 | setup (**kwargs) 76 | 77 | if __name__ == '__main__': main() 78 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Sippy Software, Inc., http://www.sippysoft.com 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | #if defined(__FreeBSD__) 29 | #include 30 | #else 31 | #include 32 | #endif 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "g722_encoder.h" 39 | #include "g722_decoder.h" 40 | 41 | #define BUFFER_SIZE 10 42 | 43 | static void 44 | usage(const char *argv0) 45 | { 46 | 47 | fprintf(stderr, "usage: %s [--sln16k] [--bend] file.g722 file.raw\n" 48 | " %s --encode [--sln16k] [--bend] file.raw file.g722\n", argv0, 49 | argv0); 50 | exit (1); 51 | } 52 | 53 | int 54 | main(int argc, char **argv) 55 | { 56 | FILE *fi, *fo; 57 | uint8_t ibuf[BUFFER_SIZE]; 58 | int16_t obuf[BUFFER_SIZE * 2]; 59 | G722_DEC_CTX *g722_dctx; 60 | G722_ENC_CTX *g722_ectx; 61 | int i, srate, ch, enc, bend; 62 | int oblen; 63 | 64 | /* options descriptor */ 65 | static struct option longopts[] = { 66 | { "sln16k", no_argument, NULL, 256 }, 67 | { "encode", no_argument, NULL, 257 }, 68 | { "bend", no_argument, NULL, 258 }, 69 | { NULL, 0, NULL, 0 } 70 | }; 71 | 72 | srate = G722_SAMPLE_RATE_8000; 73 | oblen = 1; 74 | enc = bend = 0; 75 | while ((ch = getopt_long(argc, argv, "", longopts, NULL)) != -1) { 76 | switch (ch) { 77 | case 256: 78 | srate &= ~G722_SAMPLE_RATE_8000; 79 | oblen = 2; 80 | break; 81 | case 257: 82 | enc = 1; 83 | break; 84 | case 258: 85 | bend = 1; 86 | break; 87 | default: 88 | usage(argv[0]); 89 | } 90 | } 91 | argc -= optind; 92 | argv += optind; 93 | 94 | if (argc != 2) { 95 | usage(argv[-optind]); 96 | } 97 | 98 | fi = fopen(argv[0], "r"); 99 | if (fi == NULL) { 100 | fprintf(stderr, "cannot open %s\n", argv[0]); 101 | exit (1); 102 | } 103 | fo = fopen(argv[1], "w"); 104 | if (fo == NULL) { 105 | fprintf(stderr, "cannot open %s\n", argv[1]); 106 | exit (1); 107 | } 108 | 109 | int ib; 110 | if (enc == 0) { 111 | g722_dctx = g722_decoder_new(64000, srate); 112 | if (g722_dctx == NULL) { 113 | fprintf(stderr, "g722_decoder_new() failed\n"); 114 | exit (1); 115 | } 116 | while ((ib=fread(ibuf, 1, sizeof(ibuf), fi)) >= 1) { 117 | g722_decode(g722_dctx, ibuf, ib, obuf); 118 | for (i = 0; i < (ib * oblen); i++) { 119 | if (bend == 0) { 120 | obuf[i] = htole16(obuf[i]); 121 | } else { 122 | obuf[i] = htobe16(obuf[i]); 123 | } 124 | } 125 | fwrite(obuf, ib * oblen * sizeof(obuf[0]), 1, fo); 126 | fflush(fo); 127 | } 128 | } else { 129 | g722_ectx = g722_encoder_new(64000, srate); 130 | if (g722_ectx == NULL) { 131 | fprintf(stderr, "g722_encoder_new() failed\n"); 132 | exit (1); 133 | } 134 | int insize = sizeof(obuf) / ((oblen == 1) ? 2 : 1); 135 | while ((ib=fread(obuf, 1, insize, fi)) >= 1) { 136 | int ibnelem = ib / sizeof(obuf[0]); 137 | for (i = 0; i < ibnelem; i++) { 138 | if (bend == 0) { 139 | obuf[i] = le16toh(obuf[i]); 140 | } else { 141 | obuf[i] = be16toh(obuf[i]); 142 | } 143 | } 144 | g722_encode(g722_ectx, obuf, ibnelem, ibuf); 145 | fwrite(ibuf, ibnelem / oblen, 1, fo); 146 | fflush(fo); 147 | } 148 | } 149 | 150 | fclose(fi); 151 | fclose(fo); 152 | 153 | exit(0); 154 | } 155 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import struct 3 | from G722 import G722 4 | 5 | def main(): 6 | parser = argparse.ArgumentParser(description='G.722 codec utility') 7 | parser.add_argument('input_file', type=str, help='Input file path') 8 | parser.add_argument('output_file', type=str, help='Output file path') 9 | parser.add_argument('--sln16k', action='store_true', help='Use 16000 Hz sample rate') 10 | parser.add_argument('--encode', action='store_true', help='Encode mode') 11 | parser.add_argument('--bend', action='store_true', help='Use big-endian byte order') 12 | args = parser.parse_args() 13 | 14 | sample_rate = 16000 if args.sln16k else 8000 15 | bit_rate = 64000 # Example bit rate 16 | 17 | try: 18 | with open(args.input_file, 'rb') as fi, open(args.output_file, 'wb') as fo: 19 | codec = G722(sample_rate, bit_rate) 20 | if args.encode: 21 | while True: 22 | pcm_data = fi.read(2 * 1024) # Read blocks of 2048 bytes 23 | if not pcm_data: 24 | break 25 | # Convert samples to little or big endian based on flag 26 | format_string = '>' if args.bend else '<' 27 | pcm_samples = struct.unpack(format_string + 'h' * (len(pcm_data) // 2), pcm_data) 28 | encoded_data = codec.encode(pcm_samples) 29 | fo.write(encoded_data) 30 | else: 31 | while True: 32 | encoded_data = fi.read(1024) # Read blocks of 1024 bytes 33 | if not encoded_data: 34 | break 35 | decoded_samples = codec.decode(encoded_data) 36 | # Convert samples to little or big endian based on flag 37 | format_string = '>' if args.bend else '<' 38 | output_data = struct.pack(format_string + 'h' * len(decoded_samples), *decoded_samples) 39 | fo.write(output_data) 40 | 41 | except IOError as e: 42 | print(f"File error: {e}", file=sys.stderr) 43 | sys.exit(1) 44 | 45 | if __name__ == "__main__": 46 | main() 47 | -------------------------------------------------------------------------------- /test_data/fullscale.g722: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/libg722/b4c3d4b15f939d8e25cd9af02a72dbab233f366d/test_data/fullscale.g722 -------------------------------------------------------------------------------- /test_data/pcminb.checksum: -------------------------------------------------------------------------------- 1 | #Test Size MD5(encoded) MD5(decoded) 2 | 48000_8000 49602 05d8a9865cd06e8c3c82640089cf026c ceba1bb19b8b890cf9f568e3d6f064e1 3 | 48000_16000 24801 c86ba80766067d05656878bd50feecc1 c230245d5c4896e985c1c023c6a05948 4 | 56000_8000 49602 da5a9a2343a3d3b09cb434047b93920a c923fc53e9834f31ab5bc6c5c09c0b8a 5 | 56000_16000 24801 be0740f34765df5cadc7a7cd71b9ee69 c2560c5b9108f29f491a059dcae45fd8 6 | 64000_8000 49602 612eeec58deab960a5d1ab4e7fb052da ad6b2fbb9ea0c4e58752b9eb11652f33 7 | 64000_16000 24801 7b09168395cd17c69fd4f3c39c288781 11a9661b59beeae7d520c38e1a3524e4 8 | -------------------------------------------------------------------------------- /test_data/pcminb.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/libg722/b4c3d4b15f939d8e25cd9af02a72dbab233f366d/test_data/pcminb.dat -------------------------------------------------------------------------------- /test_data/test.checksum: -------------------------------------------------------------------------------- 1 | 8eb29e8e9feb9709d96c7bbda37cbb2f77510fcfab2d1f48a495178cd2231253 *test.raw.out 2 | a886f22d37ecd356e1963072bc481ed2c5dd12539428188d9779d33a520f3ff8 *test.raw.16k.out 3 | fc0abeb220fc66fc7d5429758dff26b56bdc9e2d64ce701a5b2489ba3a2fb877 *pcminb.g722.out 4 | 6f80f1635a58a9352b1a31357f6f3feea4459953df34d6d275167f4c3e8cc967 *pcminb.raw.16k.out 5 | cb8edfaa7b5a85384dde8e0c9976f5247294732d6caf32c07b94510fc8844403 *test.g722.out 6 | e70c92cd2068d112839db12bfaa787cc0ea9a809b64fbf62fd78b6041cd9fbcc *fullscale.raw.out 7 | -------------------------------------------------------------------------------- /test_data/test.g722: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/libg722/b4c3d4b15f939d8e25cd9af02a72dbab233f366d/test_data/test.g722 -------------------------------------------------------------------------------- /tests/test_decode.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from G722 import G722 4 | 5 | class TestDecoder(unittest.TestCase): 6 | def test_decode_len(self): 7 | bitrates = [48000, 56000, 64000] 8 | sample_rates = [8000, 16000] 9 | 10 | for bitrate in bitrates: 11 | for sample_rate in sample_rates: 12 | with self.subTest(bitrate=bitrate, sample_rate=sample_rate): 13 | g722 = G722(sample_rate, bitrate) 14 | encoded_data = b"0123" # Example encoded data 15 | decoded_data = g722.decode(encoded_data) 16 | got = len(decoded_data) 17 | want = 4 if sample_rate == 8000 else 8 18 | self.assertEqual(want, got) 19 | 20 | 21 | if __name__ == '__main__': 22 | unittest.main() 23 | -------------------------------------------------------------------------------- /tests/test_encode.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import numpy as np 4 | 5 | from G722 import G722 6 | 7 | class TestEncoder(unittest.TestCase): 8 | def test_encode_len(self): 9 | bitrates = (48000, 56000, 64000) 10 | sample_rates = (8000, 16000) 11 | sample_inputs = (b"\x00\x01\x02\x03", np.array((0, 1, 2, 3), dtype=np.int16), (0, 1, 2, 3), ('aa', 'bb', 'cc'), 42) 12 | def do_test(br, sr, id): 13 | with self.subTest(bitrate=br, sample_rate=sr, input_data=id): 14 | g722 = G722(sr, br) 15 | try: 16 | encoded_data = g722.encode(id) 17 | except TypeError: 18 | got = isinstance(id, int) or isinstance(id[0], str) 19 | want = True 20 | self.assertEqual(want, got) 21 | return 22 | else: 23 | got = isinstance(id, int) 24 | want = False 25 | self.assertEqual(want, got) 26 | got = len(encoded_data) 27 | want = 4 if sample_rate == 8000 else 2 28 | self.assertEqual(want, got) 29 | for bitrate in bitrates: 30 | for sample_rate in sample_rates: 31 | for sample_input in sample_inputs: 32 | do_test(bitrate, sample_rate, sample_input) 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/test_encode_decode.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import hashlib 4 | import numpy as np 5 | from sysconfig import get_platform 6 | 7 | from G722 import G722 8 | 9 | IS_S390X = get_platform() == 'linux-s390x' 10 | 11 | @unittest.skipIf(IS_S390X, "G722 encode/decode test is broken on BE; skipping until fixed") 12 | class TestEncodeDecode(unittest.TestCase): 13 | DATA_DIR = os.path.join(os.path.dirname(__file__), '../test_data') 14 | PCM_FILE = os.path.join(DATA_DIR, 'pcminb.dat') 15 | MD5_FILE = os.path.join(DATA_DIR, 'pcminb.checksum') 16 | 17 | def setUp(self): 18 | # load raw PCM as int16 array 19 | with open(self.PCM_FILE, 'rb') as f: 20 | pcm_bytes = f.read() 21 | self.pcm = np.frombuffer(pcm_bytes, dtype='