Initial commit
All checks were successful
CI / build-and-analyze (push) Successful in 1m19s

This commit is contained in:
2026-08-19 09:22:12 +03:00
commit 072d068dcd
11 changed files with 693 additions and 0 deletions

89
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,89 @@
name: CI
on:
push:
branches: [trunk]
pull_request:
branches: [trunk]
jobs:
build-and-analyze:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install tools
run: |
sudo apt-get update -qq
sudo apt-get install -y build-essential clang clang-tools clang-tidy cppcheck gcc-mingw-w64-x86-64 cmake
- name: Build with GCC
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=gcc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with Clang
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=clang CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with MinGW
run: |
make -f Makefile.posix clean
make -f Makefile.mingw CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with CMake
run: |
cmake -S . -B build
cmake --build build
- name: Linting checks
run: |
echo "Running all linting checks..."
FAILED=0
# cppcheck
echo "=== Check 1/4: cppcheck ==="
if make -f Makefile.lint cppcheck; then
echo "cppcheck: PASSED"
else
echo "cppcheck: FAILED"
FAILED=1
fi
# clang-tidy
echo "=== Check 2/4: clang-tidy ==="
if make -f Makefile.lint clang-tidy; then
echo "clang-tidy: PASSED"
else
echo "clang-tidy: FAILED"
FAILED=1
fi
# scan-build
echo "=== Check 3/4: scan-build ==="
if make -f Makefile.lint scan-build; then
echo "scan-build: PASSED"
else
echo "scan-build: FAILED"
FAILED=1
fi
# security-check
echo "=== Check 4/4: security-check ==="
if make -f Makefile.lint security-check; then
echo "security-check: PASSED"
else
echo "security-check: FAILED"
FAILED=1
fi
# Final result
if [ $FAILED -ne 0 ]; then
echo "One or more linting checks failed."
exit 1
else
echo "All linting checks passed."
fi

68
.gitignore vendored Normal file
View File

@@ -0,0 +1,68 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json

25
CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.10)
project(CgeSample LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(SOURCES
Sample.c
)
set(HEADERS
CgeSample.h
)
add_library(CgeSample STATIC ${SOURCES} ${HEADERS})
target_include_directories(CgeSample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS CgeSample
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES ${HEADERS} DESTINATION include)

46
CgeSample.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef CGE_SAMPLE_H
#define CGE_SAMPLE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
enum CgeSampleFormat {
CGE_SAMPLE_UNKNOWN, /* Invalid or unknown format */
CGE_SAMPLE_S8, /* 8-bit signed integer, range: -128 to 127 */
CGE_SAMPLE_U8, /* 8-bit unsigned integer, range: 0 to 255 */
CGE_SAMPLE_S16, /* 16-bit signed integer, range: -32768 to 32767 */
CGE_SAMPLE_U16, /* 16-bit unsigned integer, range: 0 to 65535 */
CGE_SAMPLE_S24, /* 24-bit signed integer */
CGE_SAMPLE_U24, /* 24-bit unsigned integer */
CGE_SAMPLE_S32, /* 32-bit signed integer */
CGE_SAMPLE_U32, /* 32-bit unsigned integer */
CGE_SAMPLE_F32, /* 32-bit IEEE 754 float */
CGE_SAMPLE_F64, /* 64-bit IEEE 754 double */
};
typedef struct CgeSampleBuf {
void *data;
int format;
size_t count;
size_t stride;
} CgeSampleBuf;
size_t CgeSampleInitLayout(CgeSampleBuf *buf, int format, size_t count);
float CgeSampleGetF32(const CgeSampleBuf *buf, size_t index);
void CgeSampleSetF32(CgeSampleBuf *buf, size_t index, float value);
int16_t CgeSampleGetS16(const CgeSampleBuf *buf, size_t index);
void CgeSampleSetS16(CgeSampleBuf *buf, size_t index, int16_t value);
void CgeSampleConvert(void *src, int srcFormat, size_t srcStride,
void *dst, int dstFormat, size_t dstStride,
size_t count);
#ifdef __cplusplus
}
#endif
#endif /* CGE_SAMPLE_H */

12
LICENSE Normal file
View File

@@ -0,0 +1,12 @@
Copyright (C) 2026 by blankhex me@blankhex.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

29
Makefile.lint Normal file
View File

@@ -0,0 +1,29 @@
# Makefile.lint - Individual linting targets
.PHONY: all
all: cppcheck clang-tidy scan-build security-check
@echo "All linting scheduled"
.PHONY: cppcheck
cppcheck:
@echo "Running cppcheck..."
@cppcheck --enable=warning,performance,portability --std=c99 --quiet -Iinclude -I. .
.PHONY: clang-tidy
clang-tidy:
@echo "Running clang-tidy..."
@for file in $$(find . -name "*.c" -type f); do \
clang-tidy "$$file" -- -I. -Iinclude -std=c99; \
done
.PHONY: scan-build
scan-build:
@echo "Running scan-build..."
@scan-build --status-bugs make -f Makefile.posix clean all CC=clang
.PHONY: security-check
security-check:
@echo "Scanning for unsafe C functions..."
@grep -rnE '\b(strcpy|strcat|sprintf|gets|scanf|sscanf|realpath|mktemp|tempnam|tmpnam|getwd|getlogin)\b' \
. --include="*.c" --include="*.h" | grep -v "^Binary file" && \
exit 1 || exit 0

23
Makefile.mingw Normal file
View File

@@ -0,0 +1,23 @@
# MinGW Makefile for CgeSample
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra
ARFLAGS = rcs
TARGET = libCgeSample.a
SOURCES = Sample.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeSample.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
del $(OBJECTS) $(TARGET) 2>nul || exit 0

33
Makefile.posix Normal file
View File

@@ -0,0 +1,33 @@
# POSIX Makefile for CgeSample
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra -fPIC
ARFLAGS = rcs
TARGET = libCgeSample.a
SOURCES = Sample.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean install
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeSample.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/lib/
cp CgeSample.h /usr/local/include/
ldconfig || echo "Run ldconfig manually if needed"
uninstall:
rm -f /usr/local/lib/libCgeSample.a
rm -f /usr/local/include/CgeSample.h
ldconfig || true

23
Makefile.win32 Normal file
View File

@@ -0,0 +1,23 @@
# Makefile.win32 for MSVC (NMake)
# Usage: Open "x86 Native Tools Command Prompt", then:
# nmake -f Makefile.win32
CC = cl
LIB = lib
CFLAGS = /c /nologo /W3 /O2
LIBFLAGS = /nologo
TARGET = CgeSample.lib
SOURCES = Sample.c
OBJECTS = $(SOURCES:.c=.obj)
$(TARGET): $(OBJECTS)
$(LIB) $(LIBFLAGS) /OUT:$(TARGET) $(OBJECTS)
{.}.c{}.obj:
$(CC) $(CFLAGS) /Fo$@ $<
clean:
del $(OBJECTS) $(TARGET) 2>nul
.PHONY: clean

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# CgeSample
TODO
## License
0BSD - a permissive license with no attribution required.

338
Sample.c Normal file
View File

@@ -0,0 +1,338 @@
#include "CgeSample.h"
#include <string.h>
static size_t bytesPerSample(int format) {
switch (format) {
default:
return 0;
case CGE_SAMPLE_U8:
case CGE_SAMPLE_S8:
return 1;
case CGE_SAMPLE_U16:
case CGE_SAMPLE_S16:
return 2;
case CGE_SAMPLE_U24:
case CGE_SAMPLE_S24:
case CGE_SAMPLE_U32:
case CGE_SAMPLE_S32:
case CGE_SAMPLE_F32:
return 4;
case CGE_SAMPLE_F64:
return 8;
}
}
size_t CgeSampleInitLayout(CgeSampleBuf *buf, int format, size_t count) {
buf->count = count;
buf->format = format;
buf->data = NULL;
buf->stride = bytesPerSample(format);
return buf->stride * count;
}
float CgeSampleGetF32(const CgeSampleBuf *buf, size_t index) {
void *data;
data = (char *)buf->data + index * buf->stride;
switch (buf->format) {
default:
case CGE_SAMPLE_UNKNOWN: return 0.0f;
case CGE_SAMPLE_S8: return (float)(*(int8_t*)data) / 128.0f;
case CGE_SAMPLE_U8: return ((float)(*(uint8_t*)data) - 128.0f) / 128.0f;
case CGE_SAMPLE_S16: return (float)(*(int16_t*)data) / 32768.0f;
case CGE_SAMPLE_U16: return ((float)(*(uint16_t*)data) - 32768.0f) / 32768.0f;
case CGE_SAMPLE_S24: return (float)(*(int32_t*)data) / 8388608.0f;
case CGE_SAMPLE_U24: return ((float)(*(uint32_t*)data) - 8388608.0f) / 8388608.0f;
case CGE_SAMPLE_S32: return (float)(*(int32_t*)data) / 2147483648.0f;
case CGE_SAMPLE_U32: return ((float)(*(uint32_t*)data) - 2147483648.0f) / 2147483648.0f;
case CGE_SAMPLE_F32: return (*(float*)data);
case CGE_SAMPLE_F64: return (*(double*)data);
}
}
void CgeSampleSetF32(CgeSampleBuf *buf, size_t index, float value) {
void *data;
data = (char *)buf->data + index * buf->stride;
switch (buf->format) {
default:
case CGE_SAMPLE_UNKNOWN:
break;
case CGE_SAMPLE_S8:
value *= 128.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 127.0f) value = 127.0f;
if (value < -128.0f) value = -128.0f;
*(int8_t*)data = value;
break;
case CGE_SAMPLE_U8:
value = value * 128.0f + 128.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 255.0f) value = 255.0f;
if (value < 0.0f) value = 0.0f;
*(uint8_t*)data = value;
break;
case CGE_SAMPLE_S16:
value *= 32768.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 32767.0f) value = 32767.0f;
if (value < -32768.0f) value = -32768.0f;
*(int16_t*)data = value;
break;
case CGE_SAMPLE_U16:
value = value * 32768.0f + 32768.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 65535.0f) value = 65535.0f;
if (value < 0.0f) value = 0.0f;
*(uint16_t*)data = value;
break;
case CGE_SAMPLE_S24:
value *= 8388608.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 8388607.0f) value = 8388607.0f;
if (value < -8388608.0f) value = -8388608.0f;
*(int32_t*)data = value;
break;
case CGE_SAMPLE_U24:
value = value * 8388608.0f + 8388608.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 16777215.0f) value = 16777215.0f;
if (value < 0.0f) value = 0.0f;
*(uint32_t*)data = value;
break;
case CGE_SAMPLE_S32: {
double scaled = value;
scaled *= 2147483648.0;
scaled += (scaled >= 0.0) ? 0.5 : -0.5;
if (scaled > 2147483647.0) scaled = 2147483647.0;
if (scaled < -2147483648.0) scaled = -2147483648.0;
*(int32_t*)data = scaled;
break;
}
case CGE_SAMPLE_U32: {
double scaled = value;
scaled = scaled * 2147483648.0 + 2147483648.0;
scaled += (scaled >= 0.0) ? 0.5 : -0.5;
if (scaled > 4294967295.0) scaled = 4294967295.0;
if (scaled < 0.0) scaled = 0.0;
*(uint32_t*)data = scaled;
break;
}
case CGE_SAMPLE_F32:
*(float*)data = value;
break;
case CGE_SAMPLE_F64:
*(double*)data = (double)value;
break;
}
}
int16_t CgeSampleGetS16(const CgeSampleBuf *buf, size_t index) {
void *data;
data = (char *)buf->data + index * buf->stride;
switch (buf->format) {
default:
case CGE_SAMPLE_UNKNOWN: return 0;
case CGE_SAMPLE_S8: return (int16_t)*(int8_t*)data * 256;
case CGE_SAMPLE_U8: return ((int16_t)*(uint8_t*)data - 128) * 256;
case CGE_SAMPLE_S16: return *(int16_t*)data;
case CGE_SAMPLE_U16: return ((uint32_t)*(uint16_t*)data - 32768);
case CGE_SAMPLE_S24: return (*(int32_t*)data / 256);
case CGE_SAMPLE_U24: return ((*(uint32_t*)data / 256) - 32768);
case CGE_SAMPLE_S32: return (*(int32_t*)data / 65536);
case CGE_SAMPLE_U32: return ((*(uint32_t*)data / 65536) - 32768);
case CGE_SAMPLE_F32: {
float value = *(float*)data;
value *= 32768.0f;
value += (value >= 0.0f) ? 0.5f : -0.5f;
if (value > 32767.0f) value = 32767.0f;
if (value < -32768.0f) value = -32768.0f;
return (int16_t)value;
}
case CGE_SAMPLE_F64: {
float value = (float)(*(double*)data);
value *= 32768.0;
value += (value >= 0.0) ? 0.5 : -0.5;
if (value > 32767.0) value = 32767.0;
if (value < -32768.0) value = -32768.0;
return (int16_t)value;
}
}
}
void CgeSampleSetS16(CgeSampleBuf *buf, size_t index, int16_t value) {
void *data;
int32_t scaled;
uint32_t unscaled;
data = (char *)buf->data + index * buf->stride;
switch (buf->format) {
default:
case CGE_SAMPLE_UNKNOWN:
break;
case CGE_SAMPLE_S8:
*(int8_t*)data = (int8_t)(value / 256);
break;
case CGE_SAMPLE_U8:
*(uint8_t*)data = (uint8_t)(value / 256 + 128);
break;
case CGE_SAMPLE_S16:
*(int16_t*)data = value;
break;
case CGE_SAMPLE_U16:
*(uint16_t*)data = (uint16_t)((int32_t)value + 32768);
break;
case CGE_SAMPLE_S24:
scaled = (int32_t)value * 256;
if (scaled > 8388607) scaled = 8388607;
if (scaled < -8388608) scaled = -8388608;
*(int32_t*)data = scaled;
break;
case CGE_SAMPLE_U24:
scaled = (((int32_t)value + 32768) & 0xFFFF) * 256;
if (scaled > 16777215) scaled = 16777215;
*(uint32_t*)data = (uint32_t)scaled;
break;
case CGE_SAMPLE_S32:
*(int32_t*)data = (int32_t)value * 65536;
break;
case CGE_SAMPLE_U32:
unscaled = (((uint32_t)((int32_t)value + 32768)) & 0xFFFFu) * 65536u;
*(uint32_t*)data = unscaled;
break;
case CGE_SAMPLE_F32:
*(float*)data = (float)value / 32768.0f;
break;
case CGE_SAMPLE_F64:
*(double*)data = (double)value / 32768.0;
break;
}
}
static void fastCopy(void *src, size_t srcStride, void *dst,
size_t dstStride, size_t count, size_t size) {
size_t i;
for (i = 0; i < count; i++) {
memcpy(dst, src, size);
src = (char*)src + srcStride;
dst = (char*)dst + dstStride;
}
}
static void fastS16ToF32(void *src, size_t srcStride, void *dst,
size_t dstStride, size_t count) {
const float scale = 1.0f / 32768.0f;
size_t i;
for (i = 0; i < count; i++) {
*(float *)dst = *(int16_t *)src * scale;
src = (char *)src + srcStride;
dst = (char *)dst + dstStride;
}
}
static void fastF32ToS16(void *src, size_t srcStride, void *dst,
size_t dstStride, size_t count) {
size_t i;
for (i = 0; i < count; i++) {
int32_t v = (int32_t)(*(float *)src * 32768.0f);
if (v > 32767)
v = 32767;
else if (v < -32768)
v = -32768;
*(int16_t *)dst = (int16_t)v;
src = (void *)((char *)src + srcStride);
dst = (void *)((char *)dst + dstStride);
}
}
static void fastS24ToF32(void *src, size_t srcStride, void *dst,
size_t dstStride, size_t count) {
const float scale = 1.0f / 8388608.0f;
size_t i;
for (i = 0; i < count; i++) {
*(float *)dst = *(int32_t *)src * scale;
src = (void *)((char *)src + srcStride);
dst = (void *)((char *)dst + dstStride);
}
}
static void fastF32ToS24(void *src, size_t srcStride, void *dst,
size_t dstStride, size_t count) {
size_t i;
for (i = 0; i < count; i++) {
int32_t v = (int32_t)(*(float *)src * 8388608.0f);
if (v > 8388607)
v = 8388607;
else if (v < -8388608)
v = -8388608;
*(int32_t *)dst = (int32_t)v;
src = (void *)((char *)src + srcStride);
dst = (void *)((char *)dst + dstStride);
}
}
void CgeSampleConvert(void *src, int srcFormat, size_t srcStride,
void *dst, int dstFormat, size_t dstStride,
size_t count) {
CgeSampleBuf srcBuf, dstBuf;
size_t i;
srcStride = (srcStride) ? (srcStride) : bytesPerSample(srcFormat);
dstStride = (dstStride) ? (dstStride) : bytesPerSample(dstFormat);
if (srcFormat == dstFormat) {
fastCopy(src, srcStride, dst, dstStride, count, bytesPerSample(srcFormat));
} else if (srcFormat == CGE_SAMPLE_S16 && dstFormat == CGE_SAMPLE_F32) {
fastS16ToF32(src, srcStride, dst, dstStride, count);
} else if (srcFormat == CGE_SAMPLE_F32 && dstFormat == CGE_SAMPLE_S16) {
fastF32ToS16(src, srcStride, dst, dstStride, count);
} else if (srcFormat == CGE_SAMPLE_F32 && dstFormat == CGE_SAMPLE_S24) {
fastF32ToS24(src, srcStride, dst, dstStride, count);
} else if (srcFormat == CGE_SAMPLE_S24 && dstFormat == CGE_SAMPLE_F32) {
fastS24ToF32(src, srcStride, dst, dstStride, count);
} else {
CgeSampleInitLayout(&srcBuf, srcFormat, count);
CgeSampleInitLayout(&dstBuf, dstFormat, count);
srcBuf.stride = srcStride;
dstBuf.stride = dstStride;
srcBuf.data = src;
dstBuf.data = dst;
for (i = 0; i < count; i++)
CgeSampleSetF32(&dstBuf, i, CgeSampleGetF32(&srcBuf, i));
}
}