Init commit

This commit is contained in:
2026-07-15 10:10:01 +03:00
commit 2d5cb02421
5 changed files with 243 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# Out-of-source CMake/Jenkins builds when this directory is used as WORKSPACE.
/build/
/build-*/
/install/
/install-*/
/deps/
# Accidental in-source CMake builds.
/CMakeCache.txt
/CMakeFiles/
/cmake_install.cmake
/install_manifest.txt
/Makefile
/CTestTestfile.cmake
/compile_commands.json
/.ninja_*
/*.ninja
# Native build products created directly in this directory.
/*.dll
/*.dll.a
/*.so
/*.so.*
/*.dylib
/*.a
/*.o
/*.obj
/*.pdb
+28
View File
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.10)
project(ewsdr_dmr C)
find_path(MBELIB_INCLUDE_DIR mbelib.h REQUIRED)
find_library(MBELIB_LIBRARY NAMES mbe REQUIRED)
add_library(ewsdr_dmr SHARED ewsdr_dmr.c)
target_compile_definitions(ewsdr_dmr PRIVATE EWSDR_DMR_BUILD)
target_include_directories(ewsdr_dmr
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${MBELIB_INCLUDE_DIR})
target_link_libraries(ewsdr_dmr PRIVATE ${MBELIB_LIBRARY})
set_target_properties(ewsdr_dmr PROPERTIES
C_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES)
# DMRBindings.pas loads this exact name. MinGW otherwise adds the Unix-style
# "lib" prefix to DLLs (libewsdr_dmr.dll).
if(WIN32)
set_target_properties(ewsdr_dmr PROPERTIES PREFIX "")
endif()
include(GNUInstallDirs)
install(TARGETS ewsdr_dmr
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ewsdr_dmr.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+48
View File
@@ -0,0 +1,48 @@
# EWSDR DMR native adapter
This optional shared library isolates `mbelib` and the imported DMR core from
the Lazarus application. It intentionally does not depend on PulseAudio,
ncurses, libsndfile or the DSD-FME command-line frontend.
Build on Linux:
```sh
cmake -S native/dmr -B build/dmr -DCMAKE_BUILD_TYPE=Release
cmake --build build/dmr
```
The Pascal burst/FEC layer extracts three row-major `char[4][24]` AMBE+2
frames from each selected voice-slot burst and feeds them through this ABI.
Decoded 8 kHz mono PCM is resampled to EWSDR's 48 kHz speaker/web route.
For a build-tree run, either copy `libewsdr_dmr.so` beside the EWSDR executable
or add `build/dmr` to `LD_LIBRARY_PATH`. `cmake --install build/dmr` installs the
adapter system-wide. The GUI and daemon remain usable (with metadata but no
voice) when this optional library is absent.
## Jenkins cross-build for Windows x64
Use `jenkins/build-mbelib-win64.sh` in a Jenkins job that checks out mbelib.
It creates `install-win64` with `mbelib.h`, the import library, `libmbe.dll`,
and the MinGW runtime DLLs.
Configure the mbelib job to archive `install-win64/**`. In the EWSDR job, copy
that artifact to `deps/mbelib` and then run
`native/dmr/jenkins/build-ewsdr-dmr-win64.sh` before `lazbuild`. The script
expects the resulting prefix at
`$WORKSPACE/deps/mbelib/install-win64`, builds from
`$WORKSPACE/native/dmr`, and stages the DLL bundle in
`$WORKSPACE/bin/x86_64-win64` beside the future `ewsdr.exe`. All paths can be
overridden with environment variables.
Declarative/Scripted Pipeline example for the artifact transfer:
```groovy
copyArtifacts(
projectName: 'mbelib',
selector: lastSuccessful(),
filter: 'install-win64/**',
target: 'deps/mbelib'
)
sh 'native/dmr/jenkins/build-ewsdr-dmr-win64.sh'
```
+86
View File
@@ -0,0 +1,86 @@
#include "ewsdr_dmr.h"
#include <mbelib.h>
#include <stdlib.h>
#include <string.h>
struct ewsdr_dmr_mbe {
mbe_parms current;
mbe_parms previous;
mbe_parms previous_enhanced;
int uv_quality;
};
uint32_t ewsdr_dmr_abi_version(void)
{
return EWSDR_DMR_ABI_VERSION;
}
void ewsdr_dmr_mbe_reset(ewsdr_dmr_mbe *decoder)
{
if (decoder == NULL)
return;
mbe_initMbeParms(&decoder->current, &decoder->previous,
&decoder->previous_enhanced);
}
ewsdr_dmr_mbe *ewsdr_dmr_mbe_create(int uv_quality)
{
ewsdr_dmr_mbe *decoder = calloc(1, sizeof(*decoder));
if (decoder == NULL)
return NULL;
if (uv_quality < 1)
uv_quality = 1;
if (uv_quality > 64)
uv_quality = 64;
decoder->uv_quality = uv_quality;
ewsdr_dmr_mbe_reset(decoder);
return decoder;
}
void ewsdr_dmr_mbe_destroy(ewsdr_dmr_mbe *decoder)
{
if (decoder == NULL)
return;
memset(decoder, 0, sizeof(*decoder));
free(decoder);
}
int ewsdr_dmr_mbe_decode(ewsdr_dmr_mbe *decoder,
const uint8_t ambe_bits[EWSDR_DMR_AMBE_BITS],
float pcm_out[EWSDR_DMR_PCM_SAMPLES],
ewsdr_dmr_mbe_result *result)
{
char frame[4][24];
char data[49];
char error_text[64];
int errors_c0 = 0;
int errors_total = 0;
size_t row;
size_t col;
if (decoder == NULL || ambe_bits == NULL || pcm_out == NULL)
return -1;
for (row = 0; row < 4; ++row) {
for (col = 0; col < 24; ++col) {
const uint8_t bit = ambe_bits[row * 24 + col];
if (bit > 1)
return -2;
frame[row][col] = (char)bit;
}
}
memset(data, 0, sizeof(data));
memset(error_text, 0, sizeof(error_text));
mbe_processAmbe3600x2450Framef(
pcm_out, &errors_c0, &errors_total, error_text, frame, data,
&decoder->current, &decoder->previous, &decoder->previous_enhanced,
decoder->uv_quality);
if (result != NULL) {
result->errors_c0 = errors_c0;
result->errors_total = errors_total;
}
return 0;
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef EWSDR_DMR_H
#define EWSDR_DMR_H
#include <stddef.h>
#include <stdint.h>
#if defined(_WIN32)
# if defined(EWSDR_DMR_BUILD)
# define EWSDR_DMR_API __declspec(dllexport)
# else
# define EWSDR_DMR_API __declspec(dllimport)
# endif
#else
# define EWSDR_DMR_API __attribute__((visibility("default")))
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define EWSDR_DMR_ABI_VERSION 1u
#define EWSDR_DMR_AMBE_BITS 96u
#define EWSDR_DMR_PCM_SAMPLES 160u
typedef struct ewsdr_dmr_mbe ewsdr_dmr_mbe;
typedef struct ewsdr_dmr_mbe_result {
int errors_c0;
int errors_total;
} ewsdr_dmr_mbe_result;
EWSDR_DMR_API uint32_t ewsdr_dmr_abi_version(void);
EWSDR_DMR_API ewsdr_dmr_mbe *ewsdr_dmr_mbe_create(int uv_quality);
EWSDR_DMR_API void ewsdr_dmr_mbe_destroy(ewsdr_dmr_mbe *decoder);
EWSDR_DMR_API void ewsdr_dmr_mbe_reset(ewsdr_dmr_mbe *decoder);
/*
* Decode one already deinterleaved DMR AMBE+2 frame. ambe_bits is a row-major
* char[4][24] matrix containing only 0/1 values. pcm_out receives 160 float
* samples at 8 kHz. Frame extraction/deinterleaving remains in the DMR burst
* layer, keeping mbelib and its state behind this stable ABI.
*/
EWSDR_DMR_API int ewsdr_dmr_mbe_decode(
ewsdr_dmr_mbe *decoder,
const uint8_t ambe_bits[EWSDR_DMR_AMBE_BITS],
float pcm_out[EWSDR_DMR_PCM_SAMPLES],
ewsdr_dmr_mbe_result *result);
#ifdef __cplusplus
}
#endif
#endif