mbelib-neo
June 6, 2026 · View on GitHub
Performance‑enhanced IMBE/AMBE vocoder primitives with a modern CMake build, installable headers, and pkg-config/CMake package integration.
Project homepage: https://github.com/arancormonk/mbelib-neo
Patent Notice
This source code is provided for educational purposes only. It
is a written description of how certain voice encoding/decoding
algorithms could be implemented. Executable objects compiled or
derived from this package may be covered by one or more patents.
Readers are strongly advised to check for any patent restrictions
or licensing requirements before compiling or using this source code.
This notice is advisory and does not modify the license. See LICENSE for terms.
License
- mbelib-neo is distributed under GPL-2.0-or-later (
LICENSE). All compiled binaries and source distributions fall under that license. - Portions originate from the original ISC-licensed mbelib; the retained ISC notice is in
docs/upstream-mbelib-attribution.mdfor attribution and clarity. Those contributions are redistributed under GPL-2.0-or-later as permitted by the ISC terms. - Project-maintained source files carry SPDX identifiers reflecting their license; vendored sources under
src/external/pffft/retain upstream license headers.
Overview
- A performance‑enhanced fork of lwvmobile/mbelib, which is a fork of szechyjs/mbelib
- Supports IMBE 7200x4400 (P25 Phase 1), IMBE 7100x4400 (ProVoice), AMBE (D‑STAR), and AMBE+2 (DMR, NXDN, P25 Phase 2, dPMR, etc.).
- Public API in
#include <mbelib-neo/mbelib.h>with version macroMBELIB_VERSION. - v2.0.0 is an ABI-breaking release that adds strict bit-array validation, soft-decision ECC/frame decode, staged frame-decode helpers, and result-structured synthesis APIs; use older tags for the v1 API.
- Ships as shared and static libraries:
libmbe-neo.{so|dylib}on Unix-like platforms, andmbe-neo.dll+ import lib /mbe-neo-static.libon Windows. - Installable CMake package (
mbe_neo::mbe_shared/mbe_neo::mbe_static) and pkg-config file (libmbe-neo).
Build From Source
Requirements
- C compiler with C99 support.
- CMake ≥ 3.20.
- On non‑Windows platforms, links against
-lmautomatically.
Using CMake presets (recommended)
# From the repository root:
# Debug build with tests/examples
cmake --preset dev-debug
cmake --build --preset dev-debug -j
ctest --preset dev-debug -V
# Release build (SIMD + fast-math + LTO)
cmake --preset dev-release
cmake --build --preset dev-release -j
# Optional sanitizers (Linux/Clang/GCC)
cmake --preset asan-ubsan-debug
cmake --build --preset asan-ubsan-debug -j
ctest --preset asan-ubsan-debug -V
# Disable tone synthesis (AMBE tones)
cmake --preset notones-debug
cmake --build --preset notones-debug -j
ctest --preset notones-debug -V
Notes
- Presets create out-of-source builds under
build/<preset>/. Run the above from the repo root. - Alternatively, you can run from anywhere using
cmake -S <repo> -B <builddir>.
Manual configure/build
# From the repository root (single-config generators like Ninja/Make):
cmake -S . -B build/manual-debug -DCMAKE_BUILD_TYPE=Debug -DMBELIB_BUILD_TESTS=ON -DMBELIB_BUILD_EXAMPLES=ON
cmake --build build/manual-debug -j
ctest --test-dir build/manual-debug -V
# From anywhere (equivalent):
cmake -S <repo-path> -B <build-dir> -DCMAKE_BUILD_TYPE=Debug -DMBELIB_BUILD_TESTS=ON -DMBELIB_BUILD_EXAMPLES=ON
cmake --build <build-dir> -j
ctest --test-dir <build-dir> -V
Install / Uninstall
# Single-config generators (Unix Makefiles/Ninja):
cmake --install build/dev-release
# Multi-config generators (Visual Studio/Xcode):
cmake --install build/dev-release --config Release
# Uninstall from the same build directory
cmake --build build/dev-release --target uninstall
Tagged Releases
GitHub Actions builds release package artifacts when you push a tag matching vX.Y.Z.
The tag must match the project version declared in CMakeLists.txt and verify as an annotated tag signed by the trusted release key, or the release workflow fails before packaging.
On a matching tag, CI rebuilds dev-release, reruns the release preset tests, packages the installed library tree for Linux, macOS, and Windows, generates SPDX SBOMs, creates GitHub artifact attestations, and creates or overwrites the matching GitHub Release assets.
Release verification instructions are in docs/release-verification.md.
Typical release flow:
# Update project(mbelib-neo VERSION X.Y.Z ...) first, commit, and push.
git tag -a vX.Y.Z -m "mbelib-neo X.Y.Z"
git push origin vX.Y.Z
Configuration Options
-DNOTONES=ON— Disable AMBE/AMBE+2 tone synthesis (adds-DDISABLE_AMBE_TONES).-DMBELIB_BUILD_TESTS=ON— Build CTest executables (default ON).-DMBELIB_BUILD_EXAMPLES=ON— Buildexamples/(default ON).-DMBELIB_ENABLE_WARNINGS=ON— Enable common warnings (default ON).-DMBELIB_WARNINGS_AS_ERRORS=ON— Treat warnings as errors (default ON).-DMBELIB_ENABLE_ASAN=ON— Enable AddressSanitizer in Debug builds.-DMBELIB_ENABLE_UBSAN=ON— Enable UndefinedBehaviorSanitizer in Debug builds.-DMBE_ENABLE_DEBUG_LOGS=ON— Verbose debug logging in codec sources.-DMBELIB_BUILD_DOCS=ON— Adddocstarget (requires Doxygen).-DMBELIB_ENABLE_FAST_MATH=ON— Enable fast-math (-ffast-math//fp:fast) on library targets.-DMBELIB_ENABLE_LTO=ON— Enable IPO/LTO in Release builds when supported.-DMBELIB_ENABLE_HARDENING=ON— Enable supported Release-like compiler/linker hardening (default ON).-DMBELIB_ENABLE_SIMD=ON— Enable SIMD-accelerated routines in hot paths (SSE2 on x86_64, NEON on ARM64, and SSE2-targeted builds on 32-bit x86). On 32-bit x86, this option compiles the library for SSE2 and therefore requires an SSE2-capable CPU; leave it OFF for baseline i386 portability.- Note: the
dev-releasepreset enables SIMD, fast-math, and LTO by default when supported. -DMBELIB_BUILD_BENCHMARKS=ON— Build optional local micro‑benchmarks (not run in CI):bench_synth,bench_unvoiced, andbench_convert.
Using The Library
Headers and linking
- Public header:
#include <mbelib-neo/mbelib.h> - pkg-config:
pkg-config --cflags --libs libmbe-neo - CMake package:
find_package(mbe-neo CONFIG REQUIRED) target_link_libraries(your_target PRIVATE mbe_neo::mbe_shared) # or mbe_neo::mbe_static
Minimal example
#include <stdio.h>
#include <mbelib-neo/mbelib.h>
int main(void) {
printf("mbelib version: %s\n", mbe_versionString());
return 0;
}
You can also build and run the bundled example:
cmake --build build/dev-debug --target example_print_version
./build/dev-debug/example_print_version
Frame/Data API Quick Reference
| Codec path | Hard frame input | Soft frame input | Data API input | Scratch/output bits |
|---|---|---|---|---|
| AMBE 3600x2400 | char ambe_fr[4][24] | mbe_soft_bit ambe_fr[4][24] | char ambe_d[49] | ambe_d[49] |
| AMBE 3600x2450 | char ambe_fr[4][24] | mbe_soft_bit ambe_fr[4][24] | char ambe_d[49] | ambe_d[49] |
| IMBE 7200x4400 | char imbe_fr[8][23] | mbe_soft_bit imbe_fr[8][23] | char imbe_d[88] | imbe_d[88] |
| IMBE 7100x4400 | char imbe_fr[7][24] | mbe_soft_bit imbe_fr[7][24] | char imbe_d[88] | imbe_d[88] (converted to 7200 layout) |
Use hard-frame mbe_process*Frame* APIs when you want one-call decode+synthesis from interleaved codec frames. These APIs accept const frame arrays, run demod/ECC, populate mbe_process_result, and write the scratch/output parameter bits.
Use mbe_decode*Frame() / mbe_decode*SoftFrame() when you need staged decode. These helpers accept const frame input, fill hard parameter bits, optionally populate mbe_process_result, and return the corrected error total without synthesizing audio. Use mbe_process*Data* for later synthesis when you want to carry that result context forward.
Use mbe_process*Data* when you already have unpacked parameter bits.
IMBE 7100x4400 frame decoders convert their imbe_d[88] output to the 7200x4400/IMBE 4400 layout; synthesize converted data with the IMBE 4400 data APIs.
Stateful Decode Workflow
- Keep one
mbe_parmsstate triplet per audio stream/thread:cur_mp,prev_mp, andprev_mp_enhanced. - Initialize once before decoding with
mbe_initMbeParms(&cur_mp, &prev_mp, &prev_mp_enhanced). - Prefer
mbe_process*Frame*APIs when you have raw hard-decision vocoder frames and do not need staged parameter handling. - For soft-decision input, fill
mbe_soft_bit.bitwith the hard decision andmbe_soft_bit.reliabilitywith confidence (0is erasure-like,255is highly reliable), then callmbe_decode*SoftFrame()ormbe_process*SoftFrame*(). - Helper constructors are available for common inputs:
mbe_softBitsFromHard()assigns a fixed reliability and validates hard bits, whilembe_softBitsFromLlr()maps positive signed LLRs to bit 1 with magnitude clamped to0..255. - Processing APIs report frame state through
mbe_process_result:c0_errors,protected_errors, IMBE-specificc4_errors,total_errors, and flags such asMBE_PROCESS_FLAG_SOFT_INPUT,MBE_PROCESS_FLAG_C0_VALID,MBE_PROCESS_FLAG_C4_VALID,MBE_PROCESS_FLAG_TONE,MBE_PROCESS_FLAG_ERASURE,MBE_PROCESS_FLAG_REPEAT, andMBE_PROCESS_FLAG_MUTE. - Use
mbe_formatProcessResult()when you need a compact status string from a result. It writes'='repeatedtotal_errorstimes, then anyE,T,R, andMflags in that order, truncated to the supplied buffer size. - Hard input bits must be exactly
0or1; softmbe_soft_bit.bitvalues must also be exactly0or1. Invalid pointers/counters returnMBE_STATUS_INVALID_ARGUMENT; invalid bit values returnMBE_STATUS_INVALID_BITS. - For parameter-only processing, seed
mbe_process_result.total_errorsand any valid C0/C4 context before callingmbe_process*Data*. - Use
mbe_versionString()for the static NUL-terminated library version string.
Audio Sample Scaling (Float vs int16)
- The library synthesizes 8 kHz audio in 160-sample frames.
- The
shortentry points write 16-bit PCM with soft clipping (~95% full-scale). - The
*fentry points return mbelib’s historical float scale (not normalized[-1, +1]).mbe_floattoshort()applies the same* 7.0scaling and clipping used by theshortAPIs. - To feed a normalized float pipeline, scale each float sample by
(7.0f / 32768.0f)(range is approximately[-0.95, +0.95]after soft clipping).
Windows (MSVC) Quickstart
- Open the "x64 Native Tools Command Prompt for VS".
- From the repository root:
cmake --preset dev-release
cmake --build --preset dev-release --config Release -j
ctest --preset dev-release -C Release
cmake --install build/dev-release --config Release
Consuming with CMake on Windows (link static to avoid DLL path issues):
cmake_minimum_required(VERSION 3.20)
project(consumer C)
find_package(mbe-neo CONFIG REQUIRED)
add_executable(consumer consumer.c)
target_link_libraries(consumer PRIVATE mbe_neo::mbe_static) # or mbe_neo::mbe_shared
pkg-config on Windows (MSYS2/MinGW)
- Install MSYS2 and open a MinGW64 shell (e.g., UCRT64 or MINGW64).
- Install toolchain and pkg-config:
pacman -S --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-pkg-config
- Configure, build, and install using the MinGW generator (example to the default /mingw64 prefix):
cmake -G "MinGW Makefiles" -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build --prefix /mingw64
- Compile a consumer using pkg-config:
cat > consumer.c << 'EOF'
#include <stdio.h>
#include <mbelib-neo/mbelib.h>
int main(void) { printf("%s\n", mbe_versionString()); }
EOF
cc consumer.c $(pkg-config --cflags --libs libmbe-neo) -o consumer.exe
Notes
- If you install to a custom prefix, set
PKG_CONFIG_PATH="<prefix>/lib/pkgconfig:$PKG_CONFIG_PATH". - For shared builds, ensure
<prefix>/binis onPATH(so the DLL is found) or copy the DLL next to your.exe. - To prefer static linking via pkg-config, you can try
pkg-config --static --libs libmbe-neo(requires static libs available) or use the CMake package and linkmbe_neo::mbe_static.
Audio Quality Improvements
mbelib-neo implements JMBE-compatible audio synthesis algorithms for improved audio quality:
-
FFT-based unvoiced synthesis (Algorithms #117-126): Uses 256-point FFT with band-level scaling instead of the legacy oscillator bank approach. Provides cleaner, more natural unvoiced sounds with proper spectral shaping.
-
Weighted Overlap-Add (WOLA) (Algorithm #126): Smooth frame transitions for unvoiced synthesis using a 211-element trapezoidal synthesis window. Eliminates audible discontinuities between frames.
-
Adaptive smoothing (Algorithms #111-116): Error-rate-based parameter smoothing that gracefully handles corrupted frames. Includes local energy tracking, adaptive voicing thresholds, and amplitude scaling.
-
Voiced phase/amplitude interpolation (Algorithms #134-138): Smooth interpolation of pitch and amplitude for low-frequency harmonics during stable pitch periods. Reduces "buzzy" artifacts in voiced speech.
-
Codec-specific frame repeat/muting parity: Matches JMBE behavior where IMBE mutes on max repeats or error-rate threshold, while AMBE muting is repeat-driven in the synth path. IMBE prolonged repeat headroom resets to a default model state.
-
AMBE tone/erasure fallback parity: AMBE 3600x2450 tone classification uses JMBE-style verification plus BER gating (
errorCount < 6). Unverified/high-BER tone candidates fall back to erasure/repeat behavior, and invalid tone IDs reuse prior voice model until repeat max is reached. -
LCG noise generator with buffer overlap: JMBE-compatible Linear Congruential Generator for deterministic noise, with 96-sample overlap for smooth continuity between frames.
-
Comfort-noise parity model: Muted-frame noise follows JMBE’s low-level uniform white-noise model (
0.003gain semantics) using JavaRandom-compatible per-thread RNG behavior.
These improvements bring mbelib-neo's audio quality closer to the reference JMBE (Java Multi-Band Excitation) implementation.
API Notes
- Public API is prefixed
mbe_and declared inmbelib.h. - Float and 16‑bit PCM variants are provided (e.g.,
mbe_processAmbe3600x2400Framefandmbe_processAmbe3600x2400Frame). - Version macro
MBELIB_VERSIONis defined in the generated headermbelib-neo/version.h. mbe_versionString()returns a const pointer to the version string.- Processing APIs return an
interror total and usembe_process_resultfor decode/synthesis status. - Soft-decision ECC helpers are public for Golay(23,12), Hamming(15,11), and the IMBE 7100x4400 Hamming mapping.
- Unvoiced synthesis uses a JMBE-style LCG state carried in
mbe_parms(noiseSeed/noiseOverlap), with per-thread cold-start seeding viambe_setThreadRngSeed(uint32_t). - ABI: v2.0.0 breaks ABI from v1. The shared library
SOVERSIONfollows the project major version; minor updates within a major version aim to remain ABI compatible.
Determinism & RNG
mbe_setThreadRngSeed(seed)seeds both thread-local comfort-noise RNG state and the next unvoiced LCG cold start. For reproducible output, set it per thread before synthesis.mbe_setThreadRngSeed(0)is accepted and remapped internally to a non-zero seed. Use an explicit non-zero seed when exact reproducibility matters across builds.- Unvoiced noise progression after cold start is driven by the per-frame LCG state in
mbe_parms, so deterministic playback requires carrying frame state forward consistently. - Runtime synthesis helpers (RNG state and FFT plan) are thread-local; do not share mutable decode state (
mbe_parms) across threads unless you synchronize externally. - AMBE/IMBE frame handling intentionally differs for JMBE parity: IMBE uses error-rate muting and repeat-headroom reset behavior, while AMBE tone/erasure/repeat transitions follow AMBE-specific JMBE gating rules.
- Enabling
MBELIB_ENABLE_SIMD=ONselects vectorized math on supported CPUs. This can change floating‑point rounding at the bit level. Tests enforce exactness for int16 on x86 in Debug, and sanity bounds elsewhere.
Benchmarks (Optional)
Build micro-benchmarks:
cmake --preset dev-release -DMBELIB_BUILD_BENCHMARKS=ON
cmake --build --preset dev-release -j --target bench_synth bench_unvoiced bench_convert
Run benchmark executables:
./build/dev-release/bench_synth
./build/dev-release/bench_unvoiced
./build/dev-release/bench_convert
Quick scalar-vs-SIMD comparison helper (builds ad hoc comparison trees under build/bench-compare-scalar/ and build/bench-compare-simd/):
tools/bench_compare.sh
Tests and Examples
- Run tests with
ctest --preset dev-debug -V(orctest -Vfrom the build directory). - Included tests:
test_api(version/header/result helpers),test_ecc(hard and soft Golay/Hamming),test_noise_determinism(unvoiced RNG/frame-state determinism),test_params(parameter/synthesis behavior, soft frame decode, and v2 wrappers),test_floattoshort_parity(exact float-to-int16 conversion parity),test_golden_pcm(golden hash regression checks). - Example:
examples/print_version.cshows linking and header usage. - Golden hash helper:
gen_golden(available whenMBELIB_BUILD_TESTS=ON, default) prints current FNV-1a reference values for synthesis/conversion regression workflows (cmake --build build/dev-debug --target gen_golden && ./build/dev-debug/gen_golden).
Documentation
Optional Doxygen documentation can be generated:
cmake --preset dev-debug -DMBELIB_BUILD_DOCS=ON
cmake --build --preset dev-debug --target docs
# Output in build/docs/html
The generated site focuses on the public API (include/) and bundled examples.
Project documentation and policies:
- Contribution process, DCO, coding standards, review requirements, and test policy:
CONTRIBUTING.md - Defect reporting:
docs/issue-reporting.md - Architecture and trust boundaries:
docs/architecture.md - Build and installation policy:
docs/build-installation.md - Security requirements and design:
docs/security-requirements.md - Dependency management and monitoring:
docs/dependencies.md - OpenSSF OSPS Baseline evidence:
docs/openssf-baseline.md - Testing, CI, coverage target, and dynamic analysis:
docs/testing.md - Release verification and signing:
docs/release-verification.md - Supply-chain guardrails:
docs/supply-chain-guardrails.md - Code-quality guardrails:
docs/code-quality-guardrails.md
Project Layout
- Public headers:
include/mbelib-neo/ - Sources:
src/core/,src/ecc/,src/ambe/,src/imbe/ - Internal headers:
src/internal/ - External libraries:
src/external/pffft/(bundled PFFFT + FFTPACK sources, BSD-like license) - Tests:
tests/• Examples:examples/• Benchmarks:bench/ - Developer tooling and CI parity scripts:
tools/
Contributing
- Read
CONTRIBUTING.mdbefore opening a pull request. - Follow
.clang-format(LLVM style, 4‑space indent, 120 cols). You can runtools/format.sh. - Static analysis scripts (CI-aligned; CI and pre-push/preflight use strict mode where applicable):
tools/clang_tidy.sh(promotes broad bugprone/performance/portability findings; targeted TUs supported).tools/cppcheck.sh(strict uses exhaustive value-flow, inconclusive findings, and targeted TUs).tools/iwyu.sh(strict include hygiene via include-what-you-use, including project headers; excludessrc/external).tools/gcc_fanalyzer.sh(strict GCC-fanalyzerplus additional high-signal GCC warnings; excludessrc/external).tools/scan_build.sh(strict Clang Static Analyzer with extra security/portability checkers; excludessrc/external).tools/semgrep.sh(strict SAST with default, C, and security-audit rule packs).tools/lizard.sh(strict complexity/length/argument budgets; excludessrc/external).
- Git hooks:
tools/install-git-hooks.shenables auto-format on commit and CI-style pre-push checks. - Manual preflight runner:
tools/preflight_ci.shruns the local CI quality gates without pushing. - Prefer keeping internal symbols
staticand declarations in headers where shared. - Before sending changes: build locally, run
ctest -V, and ensure examples still link.