core: turn olivecore into liboakcore.so with a pure C ABI
liboakcore is now a shared library that exposes only a C ABI: - every value class (Rational, TimeRange, Color, Bezier, AudioParams, SampleBuffer) and the free-function groups (StringUtils, fraction utils, Timecode) is wrapped in an opaque-handle C API under core/include/olive/core/oakcore/ (init/copy/free + self-first functions), implemented in core/src/capi/ - consumers keep the original C++ API unchanged through same-name wrapper classes that hold the handle and forward across the C boundary; original implementations moved to core/src/oliveimpl (namespace olive::core::internal) and are hidden from export - TimeRangeList/TimeRangeListFrameIterator are reimplemented inline over the wrapper (iterators/containers don't cross C ABI) - generic Value container stays internal (unused by consumers) and is no longer part of the public umbrella header - hidden visibility + OAKCORE_BUILD export macro; nm shows zero olive::* symbols exported - install into the platform's standard libdir (GNUInstallDirs); Windows DLLs next to the executables, macOS into the app bundle - TimelineWorkArea::in/out/length now return by value: the wrapped TimeRange getters return values, and forwarding them through const references dangled (found via RenderWorkerFootageTest crash) - tests: 9 new pure C ABI test executables (oakcore_*_test) covering every public C function; 4 stale legacy core tests removed (they targeted a long-renamed API and were never built due to a malformed option() that also kept OLIVECORE_BUILD_TESTS off) - CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the AppImage validation list, build-tree DLL copies on Windows
This commit is contained in:
@@ -67,10 +67,12 @@ jobs:
|
||||
# The FFmpeg bridge DLL is built outside app/ and is not reachable
|
||||
# via PATH, so ntldd cannot discover it from the executables alone
|
||||
cp build/ffmpeg_bridge/bin/ffmpeg_bridge.dll app/packaging/windows/nsis/oak-editor/
|
||||
# Same for the liboakcore DLL
|
||||
cp build/core/oakcore.dll app/packaging/windows/nsis/oak-editor/
|
||||
windeployqt6 app/packaging/windows/nsis/oak-editor/oak-editor.exe
|
||||
# Copy all non-Qt MSYS2 DLLs recursively for every binary we ship
|
||||
cd app/packaging/windows/nsis/oak-editor
|
||||
for binary in oak-editor.exe oak-render-worker.exe oakgl.dll oakvulkan.dll ffmpeg_bridge.dll; do
|
||||
for binary in oak-editor.exe oak-render-worker.exe oakgl.dll oakvulkan.dll ffmpeg_bridge.dll oakcore.dll; do
|
||||
[ -f "$binary" ] || continue
|
||||
for l in $(ntldd -R "$binary" | grep -E 'mingw64|ucrt64|clang64' | sed 's/^[ \t]*//' | cut -d' ' -f3); do
|
||||
cp -v "$l" .
|
||||
@@ -369,9 +371,11 @@ jobs:
|
||||
ls AppDir/usr/lib/liboakvulkan.so || (echo "Missing liboakvulkan.so" && exit 1)
|
||||
ls AppDir/usr/lib/libvulkan.so* || (echo "Missing libvulkan" && exit 1)
|
||||
ls AppDir/usr/lib/libffmpeg_bridge.so || (echo "Missing libffmpeg_bridge.so" && exit 1)
|
||||
ls AppDir/usr/lib/liboakcore.so || (echo "Missing liboakcore.so" && exit 1)
|
||||
# Ensure every shipped binary resolves all of its dependencies
|
||||
! ldd AppDir/usr/bin/oak-editor | grep "not found"
|
||||
! ldd AppDir/usr/bin/oak-render-worker | grep "not found"
|
||||
! ldd AppDir/usr/lib/liboakcore.so | grep "not found"
|
||||
! ldd AppDir/usr/lib/liboakgl.so | grep "not found"
|
||||
! ldd AppDir/usr/lib/liboakvulkan.so | grep "not found"
|
||||
|
||||
|
||||
+10
-1
@@ -295,7 +295,8 @@ elseif (APPLE)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olive-render-worker> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakgl> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:ffmpeg_bridge> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMENT "Copying oak-render-worker, render backends, and ffmpeg_bridge into Oak.app"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olivecore> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMENT "Copying oak-render-worker, render backends, ffmpeg_bridge and liboakcore into Oak.app"
|
||||
)
|
||||
if (TARGET oakvulkan)
|
||||
add_custom_command(TARGET olive-editor POST_BUILD
|
||||
@@ -307,6 +308,14 @@ elseif (UNIX)
|
||||
install(TARGETS olive-editor RUNTIME DESTINATION bin)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# Windows has no RPATH: shared libraries must sit next to the executable
|
||||
add_custom_command(TARGET olive-editor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:ffmpeg_bridge> $<TARGET_FILE_DIR:olive-editor>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olivecore> $<TARGET_FILE_DIR:olive-editor>
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Set link libraries
|
||||
target_link_libraries(olive-editor PRIVATE ${OLIVE_LIBRARIES})
|
||||
target_link_libraries(libolive-editor PRIVATE ${OLIVE_LIBRARIES})
|
||||
|
||||
@@ -106,17 +106,17 @@ void TimelineWorkArea::save(QXmlStreamWriter *writer) const
|
||||
QString::fromStdString(this->out().to_string()));
|
||||
}
|
||||
|
||||
const Rational &TimelineWorkArea::in() const
|
||||
Rational TimelineWorkArea::in() const
|
||||
{
|
||||
return workarea_range_.in();
|
||||
}
|
||||
|
||||
const Rational &TimelineWorkArea::out() const
|
||||
Rational TimelineWorkArea::out() const
|
||||
{
|
||||
return workarea_range_.out();
|
||||
}
|
||||
|
||||
const Rational &TimelineWorkArea::length() const
|
||||
Rational TimelineWorkArea::length() const
|
||||
{
|
||||
return workarea_range_.length();
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ public:
|
||||
bool enabled() const;
|
||||
void set_enabled(bool e);
|
||||
|
||||
const Rational &in() const;
|
||||
const Rational &out() const;
|
||||
const Rational &length() const;
|
||||
Rational in() const;
|
||||
Rational out() const;
|
||||
Rational length() const;
|
||||
const TimeRange &range() const;
|
||||
void set_range(const TimeRange &range);
|
||||
|
||||
|
||||
+55
-8
@@ -19,7 +19,7 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(libolivecore VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
option(OLIVECORE_BUILD_TESTS ON)
|
||||
option(OLIVECORE_BUILD_TESTS "Build liboakcore tests" ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -36,7 +36,20 @@ if(UNIX AND NOT APPLE AND NOT DEFINED OpenGL_GL_PREFERENCE)
|
||||
endif()
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_library(olivecore
|
||||
# liboakcore is a shared library. It exports a pure C ABI (see
|
||||
# include/olive/core/oakcore/): every public function is declared with
|
||||
# OAKCORE_API and all C++ symbols are hidden, so no C++ ABI crosses the
|
||||
# library boundary. Consumers use the wrapper headers in include/olive/core.
|
||||
add_library(olivecore SHARED
|
||||
src/capi/audioparams.cpp
|
||||
src/capi/bezier.cpp
|
||||
src/capi/color.cpp
|
||||
src/capi/fractionutils.cpp
|
||||
src/capi/rational.cpp
|
||||
src/capi/samplebuffer.cpp
|
||||
src/capi/stringutils.cpp
|
||||
src/capi/timecodefunctions.cpp
|
||||
src/capi/timerange.cpp
|
||||
src/render/audioparams.cpp
|
||||
src/render/samplebuffer.cpp
|
||||
src/util/bezier.cpp
|
||||
@@ -50,7 +63,23 @@ add_library(olivecore
|
||||
src/util/value.cpp
|
||||
)
|
||||
|
||||
set_target_properties(olivecore PROPERTIES
|
||||
OUTPUT_NAME oakcore
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
C_VISIBILITY_PRESET hidden
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
)
|
||||
|
||||
# OAKCORE_BUILD marks the library side of the export macros (dllexport)
|
||||
target_compile_definitions(olivecore PRIVATE OAKCORE_BUILD)
|
||||
|
||||
# The library builds against its internal implementation headers
|
||||
# (src/oliveimpl); consumers only ever see the public C API and wrapper
|
||||
# headers (include/olive/core). oliveimpl must come first so that internal
|
||||
# sources never pick up a wrapper header by accident.
|
||||
target_include_directories(olivecore PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/oliveimpl"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/olive/core"
|
||||
"${CMAKE_SOURCE_DIR}/third_party/openfx/include/"
|
||||
)
|
||||
@@ -70,8 +99,19 @@ else()
|
||||
message(" OpenTimelineIO interchange will be disabled.")
|
||||
endif()
|
||||
|
||||
install(TARGETS olivecore)
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/olive" DESTINATION "${CMAKE_INSTALL_PREFIX}/include")
|
||||
# liboakcore installs into the platform's standard library directory
|
||||
# (/usr/lib, /usr/lib64 or the Debian multiarch path), not a custom one.
|
||||
# Windows DLLs go next to the executables in bin.
|
||||
include(GNUInstallDirs)
|
||||
if (WIN32)
|
||||
install(TARGETS olivecore
|
||||
RUNTIME DESTINATION bin
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
else ()
|
||||
install(TARGETS olivecore
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif ()
|
||||
|
||||
if (OLIVECORE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
@@ -82,13 +122,20 @@ if (OLIVECORE_BUILD_TESTS)
|
||||
)
|
||||
target_link_libraries(${name} PRIVATE olivecore)
|
||||
target_include_directories(${name} PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/olive/core"
|
||||
)
|
||||
add_test(${name} ${name})
|
||||
endfunction()
|
||||
|
||||
make_test(rational-test)
|
||||
make_test(stringutils-test)
|
||||
make_test(timecode-test)
|
||||
make_test(timerange-test)
|
||||
# Pure C ABI tests for the liboakcore public interface
|
||||
make_test(oakcore_audioparams_test)
|
||||
make_test(oakcore_bezier_test)
|
||||
make_test(oakcore_color_test)
|
||||
make_test(oakcore_fractionutils_test)
|
||||
make_test(oakcore_rational_test)
|
||||
make_test(oakcore_samplebuffer_test)
|
||||
make_test(oakcore_stringutils_test)
|
||||
make_test(oakcore_timecodefunctions_test)
|
||||
make_test(oakcore_timerange_test)
|
||||
endif()
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "util/tests.h"
|
||||
#include "util/timecodefunctions.h"
|
||||
#include "util/timerange.h"
|
||||
#include "util/value.h"
|
||||
// util/value.h is deliberately not part of the public API: the generic
|
||||
// Value container is unused by consumers and stays internal to the library
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_H
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_AUDIOPARAMS_H
|
||||
#define OAKCORE_AUDIOPARAMS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "export.h"
|
||||
#include "rational.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file audioparams.h
|
||||
* @brief C ABI for the audio stream parameter value type
|
||||
*
|
||||
* Opaque handle + free functions. All returned OakAudioParams* and
|
||||
* OakRational* are owned by the caller and must be released with
|
||||
* oakcore_audioparams_free() / oakcore_rational_free() respectively.
|
||||
*
|
||||
* Sample formats cross the boundary as plain ints carrying the
|
||||
* olive::core::SampleFormat::Format enum values (render/sampleformat.h);
|
||||
* channel layouts are plain 64-bit masks (render/channellayout.h).
|
||||
*/
|
||||
typedef struct OakAudioParams OakAudioParams;
|
||||
|
||||
/**
|
||||
* Creates parameters for the given sample rate, channel layout mask and
|
||||
* sample format. The timebase is initialized to 1/sample_rate.
|
||||
*/
|
||||
OAKCORE_API OakAudioParams *oakcore_audioparams_create(int sample_rate,
|
||||
uint64_t channel_layout,
|
||||
int format);
|
||||
|
||||
/**
|
||||
* Creates default (invalid) parameters: sample_rate=0, empty channel
|
||||
* layout, invalid format.
|
||||
*/
|
||||
OAKCORE_API OakAudioParams *oakcore_audioparams_create_invalid(void);
|
||||
|
||||
OAKCORE_API OakAudioParams *oakcore_audioparams_copy(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_free(OakAudioParams *self);
|
||||
|
||||
OAKCORE_API int oakcore_audioparams_sample_rate(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_sample_rate(OakAudioParams *self,
|
||||
int sample_rate);
|
||||
|
||||
OAKCORE_API uint64_t oakcore_audioparams_channel_layout(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_channel_layout(OakAudioParams *self,
|
||||
uint64_t mask);
|
||||
|
||||
OAKCORE_API OakRational *oakcore_audioparams_time_base(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_time_base(OakAudioParams *self,
|
||||
const OakRational *timebase);
|
||||
|
||||
/**
|
||||
* Returns a new owned handle to Rational(1, sample_rate).
|
||||
*/
|
||||
OAKCORE_API OakRational *oakcore_audioparams_sample_rate_as_time_base(
|
||||
const OakAudioParams *self);
|
||||
|
||||
OAKCORE_API int oakcore_audioparams_format(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_format(OakAudioParams *self, int format);
|
||||
|
||||
OAKCORE_API int oakcore_audioparams_enabled(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_enabled(OakAudioParams *self, int enabled);
|
||||
|
||||
OAKCORE_API int oakcore_audioparams_stream_index(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_stream_index(OakAudioParams *self,
|
||||
int stream_index);
|
||||
|
||||
OAKCORE_API int64_t oakcore_audioparams_duration(const OakAudioParams *self);
|
||||
OAKCORE_API void oakcore_audioparams_set_duration(OakAudioParams *self,
|
||||
int64_t duration);
|
||||
|
||||
/**
|
||||
* Time/byte/sample conversions. All of these require valid parameters
|
||||
* (oakcore_audioparams_is_valid()); calling them on invalid parameters is
|
||||
* an error (the library asserts in debug builds).
|
||||
*/
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_bytes(const OakAudioParams *self,
|
||||
double time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_bytes_rational(
|
||||
const OakAudioParams *self, const OakRational *time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_bytes_per_channel(
|
||||
const OakAudioParams *self, double time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_bytes_per_channel_rational(
|
||||
const OakAudioParams *self, const OakRational *time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_samples(const OakAudioParams *self,
|
||||
double time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_time_to_samples_rational(
|
||||
const OakAudioParams *self, const OakRational *time);
|
||||
OAKCORE_API int64_t oakcore_audioparams_samples_to_bytes(const OakAudioParams *self,
|
||||
int64_t samples);
|
||||
OAKCORE_API int64_t oakcore_audioparams_samples_to_bytes_per_channel(
|
||||
const OakAudioParams *self, int64_t samples);
|
||||
OAKCORE_API OakRational *oakcore_audioparams_samples_to_time(
|
||||
const OakAudioParams *self, int64_t samples);
|
||||
OAKCORE_API int64_t oakcore_audioparams_bytes_to_samples(const OakAudioParams *self,
|
||||
int64_t bytes);
|
||||
OAKCORE_API OakRational *oakcore_audioparams_bytes_to_time(
|
||||
const OakAudioParams *self, int64_t bytes);
|
||||
OAKCORE_API OakRational *oakcore_audioparams_bytes_per_channel_to_time(
|
||||
const OakAudioParams *self, int64_t bytes);
|
||||
|
||||
OAKCORE_API int oakcore_audioparams_channel_count(const OakAudioParams *self);
|
||||
OAKCORE_API int oakcore_audioparams_bytes_per_sample_per_channel(
|
||||
const OakAudioParams *self);
|
||||
OAKCORE_API int oakcore_audioparams_bits_per_sample(const OakAudioParams *self);
|
||||
OAKCORE_API int oakcore_audioparams_is_valid(const OakAudioParams *self);
|
||||
|
||||
/**
|
||||
* Equality like operator==: 1 when equal, 0 otherwise. Compares format,
|
||||
* sample rate, timebase and channel layout (not the footage parameters).
|
||||
*/
|
||||
OAKCORE_API int oakcore_audioparams_equals(const OakAudioParams *self,
|
||||
const OakAudioParams *other);
|
||||
|
||||
/**
|
||||
* Enumerates AudioParams::k_supported_channel_layouts. Out-of-range indices
|
||||
* return 0.
|
||||
*/
|
||||
OAKCORE_API int oakcore_audioparams_supported_channel_layout_count(void);
|
||||
OAKCORE_API uint64_t oakcore_audioparams_supported_channel_layout_at(int index);
|
||||
|
||||
/**
|
||||
* Enumerates AudioParams::k_supported_sample_rates. Out-of-range indices
|
||||
* return 0.
|
||||
*/
|
||||
OAKCORE_API int oakcore_audioparams_supported_sample_rate_count(void);
|
||||
OAKCORE_API int oakcore_audioparams_supported_sample_rate_at(int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_AUDIOPARAMS_H */
|
||||
@@ -0,0 +1,74 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_BEZIER_H
|
||||
#define OAKCORE_BEZIER_H
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file bezier.h
|
||||
* @brief C ABI for the keyframe easing bezier curve value type
|
||||
*
|
||||
* Opaque handle + free functions. All returned OakBezier* are owned by
|
||||
* the caller and must be released with oakcore_bezier_free().
|
||||
*/
|
||||
typedef struct OakBezier OakBezier;
|
||||
|
||||
OAKCORE_API OakBezier *oakcore_bezier_create(void);
|
||||
OAKCORE_API OakBezier *oakcore_bezier_create_xy(double x, double y);
|
||||
OAKCORE_API OakBezier *oakcore_bezier_create_full(double x, double y,
|
||||
double cp1_x, double cp1_y,
|
||||
double cp2_x, double cp2_y);
|
||||
OAKCORE_API OakBezier *oakcore_bezier_copy(const OakBezier *self);
|
||||
OAKCORE_API void oakcore_bezier_free(OakBezier *self);
|
||||
|
||||
OAKCORE_API double oakcore_bezier_x(const OakBezier *self);
|
||||
OAKCORE_API double oakcore_bezier_y(const OakBezier *self);
|
||||
OAKCORE_API double oakcore_bezier_cp1_x(const OakBezier *self);
|
||||
OAKCORE_API double oakcore_bezier_cp1_y(const OakBezier *self);
|
||||
OAKCORE_API double oakcore_bezier_cp2_x(const OakBezier *self);
|
||||
OAKCORE_API double oakcore_bezier_cp2_y(const OakBezier *self);
|
||||
|
||||
OAKCORE_API void oakcore_bezier_set_x(OakBezier *self, double x);
|
||||
OAKCORE_API void oakcore_bezier_set_y(OakBezier *self, double y);
|
||||
OAKCORE_API void oakcore_bezier_set_cp1_x(OakBezier *self, double cp1_x);
|
||||
OAKCORE_API void oakcore_bezier_set_cp1_y(OakBezier *self, double cp1_y);
|
||||
OAKCORE_API void oakcore_bezier_set_cp2_x(OakBezier *self, double cp2_x);
|
||||
OAKCORE_API void oakcore_bezier_set_cp2_y(OakBezier *self, double cp2_y);
|
||||
|
||||
OAKCORE_API double oakcore_bezier_quadratic_xto_t(double x, double a, double b,
|
||||
double c);
|
||||
OAKCORE_API double oakcore_bezier_quadratic_tto_y(double a, double b, double c,
|
||||
double t);
|
||||
OAKCORE_API double oakcore_bezier_cubic_xto_t(double x, double a, double b,
|
||||
double c, double d);
|
||||
OAKCORE_API double oakcore_bezier_cubic_tto_y(double a, double b, double c,
|
||||
double d, double t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_BEZIER_H */
|
||||
@@ -0,0 +1,123 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_COLOR_H
|
||||
#define OAKCORE_COLOR_H
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file color.h
|
||||
* @brief C ABI for the high precision RGBA color value type
|
||||
*
|
||||
* Opaque handle + free functions. All returned OakColor* are owned by the
|
||||
* caller and must be released with oakcore_color_free().
|
||||
*
|
||||
* A color is always 4 float channels in red/green/blue/alpha order. Channel
|
||||
* values are unbounded: values outside [0, 1] are valid (HDR).
|
||||
*
|
||||
* Functions taking a `format` argument expect the same values as
|
||||
* PixelFormat::Format (render/pixelformat.h): invalid = -1, u8 = 0, u10 = 1,
|
||||
* u16 = 2, f16 = 3, f32 = 4.
|
||||
*/
|
||||
typedef struct OakColor OakColor;
|
||||
|
||||
OAKCORE_API OakColor *oakcore_color_create(void);
|
||||
OAKCORE_API OakColor *oakcore_color_create_rgba(float r, float g, float b,
|
||||
float a);
|
||||
OAKCORE_API OakColor *oakcore_color_copy(const OakColor *self);
|
||||
OAKCORE_API void oakcore_color_free(OakColor *self);
|
||||
|
||||
/**
|
||||
* Creates a color from hue/saturation/value.
|
||||
*
|
||||
* Hue expects a value between 0.0 and 360.0. Saturation and value expect a
|
||||
* value between 0.0 and 1.0.
|
||||
*/
|
||||
OAKCORE_API OakColor *oakcore_color_from_hsv(float h, float s, float v);
|
||||
|
||||
/**
|
||||
* Creates a color from raw pixel data in the given pixel format. At most 4
|
||||
* channels are read; missing channels default to 0.
|
||||
*/
|
||||
OAKCORE_API OakColor *oakcore_color_from_data(const char *data, int format,
|
||||
int nb_channels);
|
||||
|
||||
OAKCORE_API float oakcore_color_red(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_green(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_blue(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_alpha(const OakColor *self);
|
||||
|
||||
OAKCORE_API void oakcore_color_set_red(OakColor *self, float red);
|
||||
OAKCORE_API void oakcore_color_set_green(OakColor *self, float green);
|
||||
OAKCORE_API void oakcore_color_set_blue(OakColor *self, float blue);
|
||||
OAKCORE_API void oakcore_color_set_alpha(OakColor *self, float alpha);
|
||||
|
||||
OAKCORE_API void oakcore_color_to_hsv(const OakColor *self, float *hue,
|
||||
float *sat, float *val);
|
||||
OAKCORE_API float oakcore_color_hsv_hue(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_hsv_saturation(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_value(const OakColor *self);
|
||||
|
||||
OAKCORE_API void oakcore_color_to_hsl(const OakColor *self, float *hue,
|
||||
float *sat, float *lightness);
|
||||
OAKCORE_API float oakcore_color_hsl_hue(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_hsl_saturation(const OakColor *self);
|
||||
OAKCORE_API float oakcore_color_lightness(const OakColor *self);
|
||||
|
||||
/**
|
||||
* Borrowed pointer to the 4 float channels (rgba order), valid until
|
||||
* oakcore_color_free().
|
||||
*/
|
||||
OAKCORE_API float *oakcore_color_data(OakColor *self);
|
||||
OAKCORE_API const float *oakcore_color_const_data(const OakColor *self);
|
||||
|
||||
/**
|
||||
* Writes the color as raw pixel data in the given pixel format. At most 4
|
||||
* channels are written; out must have room for nb_channels * bytes-per-channel
|
||||
* (u10 always packs to 4 bytes).
|
||||
*/
|
||||
OAKCORE_API void oakcore_color_to_data(const OakColor *self, char *out,
|
||||
int format, int nb_channels);
|
||||
|
||||
/**
|
||||
* Super rough luminance value mostly used for UI (determining whether to
|
||||
* overlay with black or white text).
|
||||
*/
|
||||
OAKCORE_API float oakcore_color_get_rough_luminance(const OakColor *self);
|
||||
|
||||
OAKCORE_API void oakcore_color_add_assign(OakColor *self,
|
||||
const OakColor *other);
|
||||
OAKCORE_API void oakcore_color_sub_assign(OakColor *self,
|
||||
const OakColor *other);
|
||||
OAKCORE_API void oakcore_color_add_scalar_assign(OakColor *self, float value);
|
||||
OAKCORE_API void oakcore_color_sub_scalar_assign(OakColor *self, float value);
|
||||
OAKCORE_API void oakcore_color_mul_scalar_assign(OakColor *self, float value);
|
||||
OAKCORE_API void oakcore_color_div_scalar_assign(OakColor *self, float value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_COLOR_H */
|
||||
@@ -0,0 +1,45 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_EXPORT_H
|
||||
#define OAKCORE_EXPORT_H
|
||||
|
||||
/**
|
||||
* @file export.h
|
||||
* @brief Symbol visibility macros for liboakcore
|
||||
*
|
||||
* liboakcore exposes a pure C ABI: every public function is declared with
|
||||
* OAKCORE_API and everything else is hidden. No C++ symbols cross the
|
||||
* library boundary.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#ifdef OAKCORE_BUILD
|
||||
#define OAKCORE_API __declspec(dllexport)
|
||||
#else
|
||||
#define OAKCORE_API __declspec(dllimport)
|
||||
#endif
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define OAKCORE_API __attribute__((visibility("default")))
|
||||
#else
|
||||
#define OAKCORE_API
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_EXPORT_H */
|
||||
@@ -0,0 +1,88 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_FRACTIONUTILS_H
|
||||
#define OAKCORE_FRACTIONUTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file fractionutils.h
|
||||
* @brief C ABI for the fraction utility functions
|
||||
*
|
||||
* Pure integer helpers: there is no object and therefore no opaque handle.
|
||||
* In/out values are passed through pointer out-parameters.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Rounding modes for oakcore_fractionutils_rescale_rnd()
|
||||
*
|
||||
* Mirrors the FFmpeg AVRounding modes: NEAR_INF rounds to the nearest value
|
||||
* with halfway cases rounded away from zero, UP rounds toward positive
|
||||
* infinity.
|
||||
*/
|
||||
typedef enum OakFractionRounding {
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF = 0,
|
||||
OAK_FRACTION_ROUNDING_UP = 1
|
||||
} OakFractionRounding;
|
||||
|
||||
/**
|
||||
* @brief Reduces the fraction *num / *den in place so that both fit within max
|
||||
*
|
||||
* Divides out the greatest common divisor and, if the values still do not
|
||||
* fit within max, finds the closest approximation using continued fractions.
|
||||
*
|
||||
* A zero denominator is preserved (with the numerator set to zero).
|
||||
* num and den must not be NULL.
|
||||
*/
|
||||
OAKCORE_API void oakcore_fractionutils_reduce_fraction(int64_t *num,
|
||||
int64_t *den,
|
||||
int64_t max);
|
||||
|
||||
/**
|
||||
* @brief Three-way comparison of the fractions an/ad and bn/bd
|
||||
*
|
||||
* Returns -1 if a < b, 0 if a == b, 1 if a > b, and INT_MIN when the
|
||||
* comparison is meaningless (degenerate zero-denominator fractions).
|
||||
*/
|
||||
OAKCORE_API int oakcore_fractionutils_compare_fractions(int an, int ad,
|
||||
int bn, int bd);
|
||||
|
||||
/**
|
||||
* @brief Rescales a by the fraction b/c: returns a * b / c
|
||||
*
|
||||
* The intermediate product is computed with 128-bit arithmetic where
|
||||
* available so that no precision is lost for large timestamps.
|
||||
*/
|
||||
OAKCORE_API int64_t oakcore_fractionutils_rescale_rnd(int64_t a, int64_t b,
|
||||
int64_t c,
|
||||
OakFractionRounding rnd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_FRACTIONUTILS_H */
|
||||
@@ -0,0 +1,87 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_RATIONAL_H
|
||||
#define OAKCORE_RATIONAL_H
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file rational.h
|
||||
* @brief C ABI for the rational number value type
|
||||
*
|
||||
* Opaque handle + free functions. All returned OakRational* are owned by
|
||||
* the caller and must be released with oakcore_rational_free().
|
||||
*/
|
||||
typedef struct OakRational OakRational;
|
||||
|
||||
OAKCORE_API OakRational *oakcore_rational_create(int numerator);
|
||||
OAKCORE_API OakRational *oakcore_rational_create_nd(int numerator,
|
||||
int denominator);
|
||||
OAKCORE_API OakRational *oakcore_rational_create_nan(void);
|
||||
OAKCORE_API OakRational *oakcore_rational_copy(const OakRational *self);
|
||||
OAKCORE_API void oakcore_rational_free(OakRational *self);
|
||||
|
||||
OAKCORE_API int oakcore_rational_numerator(const OakRational *self);
|
||||
OAKCORE_API int oakcore_rational_denominator(const OakRational *self);
|
||||
OAKCORE_API double oakcore_rational_to_double(const OakRational *self);
|
||||
|
||||
/**
|
||||
* Writes "num/den" into buf (NUL-terminated when buf_size > 0).
|
||||
* Returns the number of characters that would have been written excluding
|
||||
* the NUL, so buf == NULL or a too-small buffer can be used to query the
|
||||
* required size.
|
||||
*/
|
||||
OAKCORE_API int oakcore_rational_to_string(const OakRational *self, char *buf,
|
||||
int buf_size);
|
||||
|
||||
OAKCORE_API OakRational *oakcore_rational_from_double(double value, int *ok);
|
||||
OAKCORE_API OakRational *oakcore_rational_from_string(const char *str, int *ok);
|
||||
|
||||
OAKCORE_API int oakcore_rational_is_null(const OakRational *self);
|
||||
OAKCORE_API int oakcore_rational_is_nan(const OakRational *self);
|
||||
|
||||
OAKCORE_API OakRational *oakcore_rational_flipped(const OakRational *self);
|
||||
OAKCORE_API void oakcore_rational_flip(OakRational *self);
|
||||
|
||||
OAKCORE_API void oakcore_rational_add_assign(OakRational *self,
|
||||
const OakRational *other);
|
||||
OAKCORE_API void oakcore_rational_sub_assign(OakRational *self,
|
||||
const OakRational *other);
|
||||
OAKCORE_API void oakcore_rational_mul_assign(OakRational *self,
|
||||
const OakRational *other);
|
||||
OAKCORE_API void oakcore_rational_div_assign(OakRational *self,
|
||||
const OakRational *other);
|
||||
|
||||
/**
|
||||
* Three-way comparison like compare_fractions: -1, 0 or 1.
|
||||
*/
|
||||
OAKCORE_API int oakcore_rational_compare(const OakRational *self,
|
||||
const OakRational *other);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_RATIONAL_H */
|
||||
@@ -0,0 +1,149 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_SAMPLEBUFFER_H
|
||||
#define OAKCORE_SAMPLEBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "export.h"
|
||||
#include "rational.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file samplebuffer.h
|
||||
* @brief C ABI for the planar audio sample buffer
|
||||
*
|
||||
* Opaque handle + free functions. Every returned OakSampleBuffer or
|
||||
* OakAudioParams handle is owned by the caller and must be released with
|
||||
* oakcore_samplebuffer_free() or oakcore_audioparams_free() respectively.
|
||||
* Samples are always stored planar (one float array per channel).
|
||||
*/
|
||||
typedef struct OakSampleBuffer OakSampleBuffer;
|
||||
typedef struct OakAudioParams OakAudioParams;
|
||||
|
||||
OAKCORE_API OakSampleBuffer *oakcore_samplebuffer_create(void);
|
||||
OAKCORE_API OakSampleBuffer *oakcore_samplebuffer_create_length(
|
||||
const OakAudioParams *params, const OakRational *length);
|
||||
OAKCORE_API OakSampleBuffer *oakcore_samplebuffer_create_samples(
|
||||
const OakAudioParams *params, size_t samples_per_channel);
|
||||
OAKCORE_API OakSampleBuffer *oakcore_samplebuffer_copy(
|
||||
const OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_free(OakSampleBuffer *self);
|
||||
|
||||
OAKCORE_API OakSampleBuffer *oakcore_samplebuffer_rip_channel(
|
||||
const OakSampleBuffer *self, int channel);
|
||||
|
||||
/**
|
||||
* Copies the samples of one channel into out. Returns the total number of
|
||||
* floats in the channel (equal to the sample count), so out == NULL (or a
|
||||
* too-small buffer) can be used to query the required size; at most out_size
|
||||
* floats are written.
|
||||
*/
|
||||
OAKCORE_API int oakcore_samplebuffer_rip_channel_vector(
|
||||
const OakSampleBuffer *self, int channel, float *out, int out_size);
|
||||
|
||||
/**
|
||||
* Returns a copy of the buffer's audio parameters as a new owned handle
|
||||
* (release with oakcore_audioparams_free()).
|
||||
*/
|
||||
OAKCORE_API OakAudioParams *oakcore_samplebuffer_audio_params(
|
||||
const OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_set_audio_params(
|
||||
OakSampleBuffer *self, const OakAudioParams *params);
|
||||
|
||||
OAKCORE_API size_t oakcore_samplebuffer_sample_count(
|
||||
const OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_set_sample_count(OakSampleBuffer *self,
|
||||
size_t sample_count);
|
||||
OAKCORE_API void oakcore_samplebuffer_set_sample_count_length(
|
||||
OakSampleBuffer *self, const OakRational *length);
|
||||
|
||||
/**
|
||||
* Borrowed pointer to the samples of one channel; it becomes invalid when the
|
||||
* handle is destroyed or reallocated and must not be freed by the caller.
|
||||
* Returns NULL when the buffer is not allocated or the channel is out of
|
||||
* range.
|
||||
*/
|
||||
OAKCORE_API float *oakcore_samplebuffer_data(OakSampleBuffer *self,
|
||||
int channel);
|
||||
|
||||
/**
|
||||
* Fills out with one borrowed sample pointer per channel (see
|
||||
* oakcore_samplebuffer_data()). out must have room for
|
||||
* oakcore_samplebuffer_channel_count() entries.
|
||||
*/
|
||||
OAKCORE_API void oakcore_samplebuffer_to_raw_ptrs(OakSampleBuffer *self,
|
||||
float **out);
|
||||
|
||||
OAKCORE_API int oakcore_samplebuffer_channel_count(const OakSampleBuffer *self);
|
||||
OAKCORE_API int oakcore_samplebuffer_is_allocated(const OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_allocate(OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_destroy(OakSampleBuffer *self);
|
||||
|
||||
OAKCORE_API void oakcore_samplebuffer_reverse(OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_speed(OakSampleBuffer *self,
|
||||
double speed);
|
||||
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume(OakSampleBuffer *self,
|
||||
float f);
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume_for_channel(
|
||||
OakSampleBuffer *self, int channel, float volume);
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume_to(
|
||||
float f, const OakSampleBuffer *input, OakSampleBuffer *output);
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume_for_channel_to(
|
||||
int channel, float volume, const OakSampleBuffer *input,
|
||||
OakSampleBuffer *output);
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume_for_sample(
|
||||
OakSampleBuffer *self, size_t sample_index, float volume);
|
||||
OAKCORE_API void oakcore_samplebuffer_transform_volume_for_sample_on_channel(
|
||||
OakSampleBuffer *self, size_t sample_index, int channel, float volume);
|
||||
|
||||
OAKCORE_API void oakcore_samplebuffer_clamp(OakSampleBuffer *self);
|
||||
|
||||
OAKCORE_API void oakcore_samplebuffer_silence(OakSampleBuffer *self);
|
||||
OAKCORE_API void oakcore_samplebuffer_silence_range(OakSampleBuffer *self,
|
||||
size_t start_sample,
|
||||
size_t end_sample);
|
||||
OAKCORE_API void oakcore_samplebuffer_silence_bytes(OakSampleBuffer *self,
|
||||
size_t start_byte,
|
||||
size_t end_byte);
|
||||
|
||||
OAKCORE_API void oakcore_samplebuffer_set(OakSampleBuffer *self, int channel,
|
||||
const float *data,
|
||||
size_t sample_offset,
|
||||
size_t sample_length);
|
||||
|
||||
/**
|
||||
* Copies channel "from" of other into channel "to" of self; from == -1 uses
|
||||
* the same index as to.
|
||||
*/
|
||||
OAKCORE_API void oakcore_samplebuffer_fast_set(OakSampleBuffer *self,
|
||||
const OakSampleBuffer *other,
|
||||
int to, int from);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_SAMPLEBUFFER_H */
|
||||
@@ -0,0 +1,94 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_STRINGUTILS_H
|
||||
#define OAKCORE_STRINGUTILS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file stringutils.h
|
||||
* @brief C ABI for the string utility functions
|
||||
*
|
||||
* StringUtils consists of static functions only, so there are no objects
|
||||
* and no opaque handle. Functions returning string lists hand back a
|
||||
* caller-owned char** that must be released with
|
||||
* oakcore_stringutils_free_string_array().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Splits s on separator and returns a newly allocated array of newly
|
||||
* allocated NUL-terminated strings; *count receives the element count.
|
||||
* Release the result with oakcore_stringutils_free_string_array().
|
||||
* A NULL or empty s yields one empty string, mirroring the C++
|
||||
* implementation. Returns NULL only on allocation failure.
|
||||
*/
|
||||
OAKCORE_API char **oakcore_stringutils_split(const char *s, char separator,
|
||||
int *count);
|
||||
|
||||
/**
|
||||
* Splits s wherever the regular expression pattern matches (std::regex
|
||||
* ECMAScript syntax). Same ownership and NULL semantics as
|
||||
* oakcore_stringutils_split().
|
||||
*/
|
||||
OAKCORE_API char **oakcore_stringutils_split_regex(const char *s,
|
||||
const char *pattern,
|
||||
int *count);
|
||||
|
||||
/**
|
||||
* Releases an array returned by oakcore_stringutils_split() or
|
||||
* oakcore_stringutils_split_regex(). Safe to call with arr == NULL.
|
||||
*/
|
||||
OAKCORE_API void oakcore_stringutils_free_string_array(char **arr, int count);
|
||||
|
||||
/**
|
||||
* Parses an int from s in the given base (usually 10, or 16 for hex).
|
||||
* Returns the parsed value, or 0 on parser error. ok is an optional output
|
||||
* parameter set to 1 on success and 0 on failure.
|
||||
*/
|
||||
OAKCORE_API int oakcore_stringutils_to_int(const char *s, int base, int *ok);
|
||||
|
||||
/**
|
||||
* Formats a string with vsnprintf semantics: writes into buf
|
||||
* (NUL-terminated when buf_size > 0) and returns the number of characters
|
||||
* that would have been written excluding the NUL, so buf == NULL or a
|
||||
* too-small buffer can be used to query the required size.
|
||||
*/
|
||||
OAKCORE_API int oakcore_stringutils_format(char *buf, int buf_size,
|
||||
const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* va_list form of oakcore_stringutils_format(), for forwarding from other
|
||||
* variadic functions.
|
||||
*/
|
||||
OAKCORE_API int oakcore_stringutils_format_v(char *buf, int buf_size,
|
||||
const char *fmt, va_list args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_STRINGUTILS_H */
|
||||
@@ -0,0 +1,145 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_TIMECODEFUNCTIONS_H
|
||||
#define OAKCORE_TIMECODEFUNCTIONS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "export.h"
|
||||
#include "rational.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file timecodefunctions.h
|
||||
* @brief C ABI for the time/timecode/timestamp conversion functions
|
||||
*
|
||||
* Free functions only (the wrapped class is all-static, so there is no
|
||||
* object handle of its own). Times and timebases are passed as borrowed
|
||||
* OakRational handles; every returned OakRational* is owned by the caller
|
||||
* and must be released with oakcore_rational_free().
|
||||
*
|
||||
* Terminology used throughout:
|
||||
*
|
||||
* `time` - time in seconds presented in a Rational form
|
||||
* `timebase` - the base time unit of an audio/video stream in seconds
|
||||
* `timestamp` - an integer representation of a time in timebase units
|
||||
* (in many cases is used like a frame number)
|
||||
* `timecode` - a user-friendly string representation of a time according to OakTimecodeDisplay
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief User-friendly timecode display modes
|
||||
*
|
||||
* Values must stay in sync with the wrapped class's Display enum.
|
||||
*/
|
||||
typedef enum OakTimecodeDisplay {
|
||||
OAK_TIMECODE_DISPLAY_DROP_FRAME = 0,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME = 1,
|
||||
OAK_TIMECODE_DISPLAY_SECONDS = 2,
|
||||
OAK_TIMECODE_DISPLAY_FRAMES = 3,
|
||||
OAK_TIMECODE_DISPLAY_MILLISECONDS = 4
|
||||
} OakTimecodeDisplay;
|
||||
|
||||
/**
|
||||
* @brief Rounding modes for time/timestamp conversions
|
||||
*
|
||||
* Values must stay in sync with the wrapped class's Rounding enum.
|
||||
*/
|
||||
typedef enum OakTimecodeRounding {
|
||||
OAK_TIMECODE_ROUNDING_CEIL = 0,
|
||||
OAK_TIMECODE_ROUNDING_FLOOR = 1,
|
||||
OAK_TIMECODE_ROUNDING_ROUND = 2
|
||||
} OakTimecodeRounding;
|
||||
|
||||
/**
|
||||
* @brief Convert a time (according to a Rational timebase) to a user-friendly string representation
|
||||
*
|
||||
* Writes the timecode into buf (NUL-terminated when buf_size > 0).
|
||||
* Returns the number of characters that would have been written excluding
|
||||
* the NUL, so buf == NULL or a too-small buffer can be used to query the
|
||||
* required size.
|
||||
*/
|
||||
OAKCORE_API int oakcore_timecode_time_to_timecode(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeDisplay display,
|
||||
int show_plus_if_positive,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Convert a user-friendly timecode string to a time in seconds
|
||||
*
|
||||
* Returns a new owned OakRational (free with oakcore_rational_free()).
|
||||
* ok is set to 1 on success and 0 on failure (may be NULL).
|
||||
*/
|
||||
OAKCORE_API OakRational *oakcore_timecode_timecode_to_time(const char *timecode,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeDisplay display,
|
||||
int *ok);
|
||||
|
||||
/**
|
||||
* @brief Convert a millisecond count to an "HH:MM:SS" string
|
||||
*
|
||||
* Same buf/buf_size convention as oakcore_timecode_time_to_timecode().
|
||||
*/
|
||||
OAKCORE_API int oakcore_timecode_time_to_string(int64_t ms, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Snap a time to the nearest timestamp boundary of a timebase
|
||||
*
|
||||
* Returns a new owned OakRational (free with oakcore_rational_free()).
|
||||
*/
|
||||
OAKCORE_API OakRational *oakcore_timecode_snap_time_to_timebase(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding);
|
||||
|
||||
OAKCORE_API int64_t oakcore_timecode_time_to_timestamp(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding);
|
||||
OAKCORE_API int64_t oakcore_timecode_time_to_timestamp_d(double time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding);
|
||||
|
||||
OAKCORE_API int64_t oakcore_timecode_rescale_timestamp(int64_t ts,
|
||||
const OakRational *source,
|
||||
const OakRational *dest);
|
||||
OAKCORE_API int64_t oakcore_timecode_rescale_timestamp_ceil(int64_t ts,
|
||||
const OakRational *source,
|
||||
const OakRational *dest);
|
||||
|
||||
/**
|
||||
* @brief Convert a timestamp in timebase units back to a time in seconds
|
||||
*
|
||||
* Returns a new owned OakRational (free with oakcore_rational_free()).
|
||||
*/
|
||||
OAKCORE_API OakRational *oakcore_timecode_timestamp_to_time(int64_t timestamp,
|
||||
const OakRational *timebase);
|
||||
|
||||
OAKCORE_API int oakcore_timecode_timebase_is_drop_frame(const OakRational *timebase);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_TIMECODEFUNCTIONS_H */
|
||||
@@ -0,0 +1,110 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKCORE_TIMERANGE_H
|
||||
#define OAKCORE_TIMERANGE_H
|
||||
|
||||
#include "export.h"
|
||||
#include "rational.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file timerange.h
|
||||
* @brief C ABI for the time range value type
|
||||
*
|
||||
* Opaque handle + free functions. All returned OakTimeRange and OakRational
|
||||
* pointers are owned by the caller and must be released with
|
||||
* oakcore_timerange_free() and oakcore_rational_free() respectively.
|
||||
*/
|
||||
typedef struct OakTimeRange OakTimeRange;
|
||||
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_create(void);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_create_io(const OakRational *in,
|
||||
const OakRational *out);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_copy(const OakTimeRange *self);
|
||||
OAKCORE_API void oakcore_timerange_free(OakTimeRange *self);
|
||||
|
||||
OAKCORE_API OakRational *oakcore_timerange_in(const OakTimeRange *self);
|
||||
OAKCORE_API OakRational *oakcore_timerange_out(const OakTimeRange *self);
|
||||
OAKCORE_API OakRational *oakcore_timerange_length(const OakTimeRange *self);
|
||||
|
||||
OAKCORE_API void oakcore_timerange_set_in(OakTimeRange *self,
|
||||
const OakRational *in);
|
||||
OAKCORE_API void oakcore_timerange_set_out(OakTimeRange *self,
|
||||
const OakRational *out);
|
||||
OAKCORE_API void oakcore_timerange_set_range(OakTimeRange *self,
|
||||
const OakRational *in,
|
||||
const OakRational *out);
|
||||
|
||||
OAKCORE_API int oakcore_timerange_equal(const OakTimeRange *self,
|
||||
const OakTimeRange *other);
|
||||
|
||||
OAKCORE_API int oakcore_timerange_overlaps_with(const OakTimeRange *self,
|
||||
const OakTimeRange *other,
|
||||
int in_inclusive,
|
||||
int out_inclusive);
|
||||
OAKCORE_API int oakcore_timerange_contains_range(const OakTimeRange *self,
|
||||
const OakTimeRange *other,
|
||||
int in_inclusive,
|
||||
int out_inclusive);
|
||||
OAKCORE_API int oakcore_timerange_contains_time(const OakTimeRange *self,
|
||||
const OakRational *time);
|
||||
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_combined(
|
||||
const OakTimeRange *self, const OakTimeRange *other);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_combine(const OakTimeRange *a,
|
||||
const OakTimeRange *b);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_intersected(
|
||||
const OakTimeRange *self, const OakTimeRange *other);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_intersect(const OakTimeRange *a,
|
||||
const OakTimeRange *b);
|
||||
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_add(const OakTimeRange *self,
|
||||
const OakRational *rhs);
|
||||
OAKCORE_API OakTimeRange *oakcore_timerange_sub(const OakTimeRange *self,
|
||||
const OakRational *rhs);
|
||||
OAKCORE_API void oakcore_timerange_add_assign(OakTimeRange *self,
|
||||
const OakRational *rhs);
|
||||
OAKCORE_API void oakcore_timerange_sub_assign(OakTimeRange *self,
|
||||
const OakRational *rhs);
|
||||
|
||||
/**
|
||||
* Splits the range into chunks of chunk_size (the first and last chunk are
|
||||
* clamped to the range bounds) and writes a newly allocated owned handle per
|
||||
* chunk into out_ranges. Returns the total number of chunks, so
|
||||
* out_ranges == NULL (or a too-small array) can be used to query the
|
||||
* required size; oakcore_timerange_split_count() is the direct equivalent
|
||||
* of that query.
|
||||
*/
|
||||
OAKCORE_API int oakcore_timerange_split_count(const OakTimeRange *self,
|
||||
int chunk_size);
|
||||
OAKCORE_API int oakcore_timerange_split(const OakTimeRange *self,
|
||||
int chunk_size,
|
||||
OakTimeRange **out_ranges,
|
||||
int out_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OAKCORE_TIMERANGE_H */
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,11 +21,12 @@
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
#define OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
#include <cstring>
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
|
||||
#include "channellayout.h"
|
||||
#include "sampleformat.h"
|
||||
#include "../util/rational.h"
|
||||
@@ -36,9 +37,9 @@ namespace olive::core
|
||||
/**
|
||||
* @brief Audio parameters class managing audio stream configuration
|
||||
*
|
||||
* Channel layouts are stored as plain 64-bit masks (see channellayout.h).
|
||||
* Because the mask is a simple value type, AudioParams has value semantics
|
||||
* and can be copied freely.
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakAudioParams handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation.
|
||||
*/
|
||||
class AudioParams {
|
||||
public:
|
||||
@@ -47,12 +48,8 @@ public:
|
||||
* sample_rate=0, channel_layout empty, format=INVALID
|
||||
*/
|
||||
AudioParams()
|
||||
: sample_rate_(0)
|
||||
, channel_layout_mask_(0)
|
||||
, channel_count_(0)
|
||||
, format_(SampleFormat::invalid)
|
||||
: handle_(oakcore_audioparams_create_invalid())
|
||||
{
|
||||
set_default_footage_parameters();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,31 +60,61 @@ public:
|
||||
*/
|
||||
AudioParams(const int &sample_rate, uint64_t channel_layout,
|
||||
const SampleFormat &format)
|
||||
: sample_rate_(sample_rate)
|
||||
, channel_layout_mask_(channel_layout)
|
||||
, channel_count_(0)
|
||||
, format_(format)
|
||||
: handle_(oakcore_audioparams_create(sample_rate, channel_layout, format))
|
||||
{
|
||||
set_default_footage_parameters();
|
||||
timebase_ = sample_rate_as_time_base();
|
||||
calculate_channel_count();
|
||||
}
|
||||
|
||||
AudioParams(const AudioParams &rhs)
|
||||
: handle_(oakcore_audioparams_copy(rhs.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
AudioParams(AudioParams &&rhs) noexcept
|
||||
: handle_(rhs.handle_)
|
||||
{
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
|
||||
~AudioParams()
|
||||
{
|
||||
oakcore_audioparams_free(handle_);
|
||||
}
|
||||
|
||||
AudioParams &operator=(const AudioParams &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_audioparams_free(handle_);
|
||||
handle_ = oakcore_audioparams_copy(rhs.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AudioParams &operator=(AudioParams &&rhs) noexcept
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_audioparams_free(handle_);
|
||||
handle_ = rhs.handle_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
int sample_rate() const
|
||||
{
|
||||
return sample_rate_;
|
||||
return oakcore_audioparams_sample_rate(handle_);
|
||||
}
|
||||
|
||||
void set_sample_rate(int sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
oakcore_audioparams_set_sample_rate(handle_, sample_rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Channel layout as a 64-bit mask (0 if unspecified)
|
||||
*/
|
||||
const uint64_t &channel_layout() const
|
||||
uint64_t channel_layout() const
|
||||
{
|
||||
return channel_layout_mask_;
|
||||
return oakcore_audioparams_channel_layout(handle_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,122 +123,208 @@ public:
|
||||
*/
|
||||
void set_channel_layout(uint64_t mask)
|
||||
{
|
||||
channel_layout_mask_ = mask;
|
||||
calculate_channel_count();
|
||||
oakcore_audioparams_set_channel_layout(handle_, mask);
|
||||
}
|
||||
|
||||
Rational time_base() const
|
||||
{
|
||||
return timebase_;
|
||||
return Rational::from_handle(oakcore_audioparams_time_base(handle_));
|
||||
}
|
||||
|
||||
void set_time_base(const Rational &timebase)
|
||||
{
|
||||
timebase_ = timebase;
|
||||
oakcore_audioparams_set_time_base(handle_, timebase.handle());
|
||||
}
|
||||
|
||||
Rational sample_rate_as_time_base() const
|
||||
{
|
||||
return Rational(1, sample_rate());
|
||||
return Rational::from_handle(
|
||||
oakcore_audioparams_sample_rate_as_time_base(handle_));
|
||||
}
|
||||
|
||||
SampleFormat format() const
|
||||
{
|
||||
return format_;
|
||||
return SampleFormat(
|
||||
static_cast<SampleFormat::Format>(oakcore_audioparams_format(handle_)));
|
||||
}
|
||||
|
||||
void set_format(SampleFormat format)
|
||||
{
|
||||
format_ = format;
|
||||
oakcore_audioparams_set_format(handle_, format);
|
||||
}
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
return oakcore_audioparams_enabled(handle_) != 0;
|
||||
}
|
||||
|
||||
void set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
oakcore_audioparams_set_enabled(handle_, e ? 1 : 0);
|
||||
}
|
||||
|
||||
int stream_index() const
|
||||
{
|
||||
return stream_index_;
|
||||
return oakcore_audioparams_stream_index(handle_);
|
||||
}
|
||||
|
||||
void set_stream_index(int s)
|
||||
{
|
||||
stream_index_ = s;
|
||||
oakcore_audioparams_set_stream_index(handle_, s);
|
||||
}
|
||||
|
||||
int64_t duration() const
|
||||
{
|
||||
return duration_;
|
||||
return oakcore_audioparams_duration(handle_);
|
||||
}
|
||||
|
||||
void set_duration(int64_t duration)
|
||||
{
|
||||
duration_ = duration;
|
||||
oakcore_audioparams_set_duration(handle_, duration);
|
||||
}
|
||||
|
||||
int64_t time_to_bytes(const double &time) const;
|
||||
int64_t time_to_bytes(const Rational &time) const;
|
||||
int64_t time_to_bytes_per_channel(const double &time) const;
|
||||
int64_t time_to_bytes_per_channel(const Rational &time) const;
|
||||
int64_t time_to_samples(const double &time) const;
|
||||
int64_t time_to_samples(const Rational &time) const;
|
||||
int64_t samples_to_bytes(const int64_t &samples) const;
|
||||
int64_t samples_to_bytes_per_channel(const int64_t &samples) const;
|
||||
Rational samples_to_time(const int64_t &samples) const;
|
||||
int64_t bytes_to_samples(const int64_t &bytes) const;
|
||||
Rational bytes_to_time(const int64_t &bytes) const;
|
||||
Rational bytes_per_channel_to_time(const int64_t &bytes) const;
|
||||
int channel_count() const;
|
||||
int bytes_per_sample_per_channel() const;
|
||||
int bits_per_sample() const;
|
||||
bool is_valid() const;
|
||||
int64_t time_to_bytes(const double &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_bytes(handle_, time);
|
||||
}
|
||||
|
||||
bool operator==(const AudioParams &other) const;
|
||||
bool operator!=(const AudioParams &other) const;
|
||||
int64_t time_to_bytes(const Rational &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_bytes_rational(handle_, time.handle());
|
||||
}
|
||||
|
||||
int64_t time_to_bytes_per_channel(const double &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_bytes_per_channel(handle_, time);
|
||||
}
|
||||
|
||||
int64_t time_to_bytes_per_channel(const Rational &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_bytes_per_channel_rational(handle_,
|
||||
time.handle());
|
||||
}
|
||||
|
||||
int64_t time_to_samples(const double &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_samples(handle_, time);
|
||||
}
|
||||
|
||||
int64_t time_to_samples(const Rational &time) const
|
||||
{
|
||||
return oakcore_audioparams_time_to_samples_rational(handle_, time.handle());
|
||||
}
|
||||
|
||||
int64_t samples_to_bytes(const int64_t &samples) const
|
||||
{
|
||||
return oakcore_audioparams_samples_to_bytes(handle_, samples);
|
||||
}
|
||||
|
||||
int64_t samples_to_bytes_per_channel(const int64_t &samples) const
|
||||
{
|
||||
return oakcore_audioparams_samples_to_bytes_per_channel(handle_, samples);
|
||||
}
|
||||
|
||||
Rational samples_to_time(const int64_t &samples) const
|
||||
{
|
||||
return Rational::from_handle(
|
||||
oakcore_audioparams_samples_to_time(handle_, samples));
|
||||
}
|
||||
|
||||
int64_t bytes_to_samples(const int64_t &bytes) const
|
||||
{
|
||||
return oakcore_audioparams_bytes_to_samples(handle_, bytes);
|
||||
}
|
||||
|
||||
Rational bytes_to_time(const int64_t &bytes) const
|
||||
{
|
||||
return Rational::from_handle(
|
||||
oakcore_audioparams_bytes_to_time(handle_, bytes));
|
||||
}
|
||||
|
||||
Rational bytes_per_channel_to_time(const int64_t &bytes) const
|
||||
{
|
||||
return Rational::from_handle(
|
||||
oakcore_audioparams_bytes_per_channel_to_time(handle_, bytes));
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return oakcore_audioparams_channel_count(handle_);
|
||||
}
|
||||
|
||||
int bytes_per_sample_per_channel() const
|
||||
{
|
||||
return oakcore_audioparams_bytes_per_sample_per_channel(handle_);
|
||||
}
|
||||
|
||||
int bits_per_sample() const
|
||||
{
|
||||
return oakcore_audioparams_bits_per_sample(handle_);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return oakcore_audioparams_is_valid(handle_) != 0;
|
||||
}
|
||||
|
||||
bool operator==(const AudioParams &other) const
|
||||
{
|
||||
return oakcore_audioparams_equals(handle_, other.handle_) != 0;
|
||||
}
|
||||
|
||||
bool operator!=(const AudioParams &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static const std::vector<uint64_t> k_supported_channel_layouts;
|
||||
static const std::vector<int> k_supported_sample_rates;
|
||||
|
||||
private:
|
||||
void set_default_footage_parameters()
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakAudioParams *handle() const
|
||||
{
|
||||
enabled_ = true;
|
||||
stream_index_ = 0;
|
||||
duration_ = 0;
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates channel_count_ from the current channel_layout_mask_
|
||||
* Called after any channel layout modification.
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
void calculate_channel_count();
|
||||
static AudioParams from_handle(OakAudioParams *handle)
|
||||
{
|
||||
return AudioParams(handle);
|
||||
}
|
||||
|
||||
int sample_rate_; ///< Audio sample rate in Hz (e.g., 48000)
|
||||
private:
|
||||
explicit AudioParams(OakAudioParams *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Channel layout mask (0 if unspecified)
|
||||
*
|
||||
* Plain value type mirroring FFmpeg's AV_CH_LAYOUT_* masks; no dynamic
|
||||
* memory is involved, so copies are trivially safe.
|
||||
*/
|
||||
uint64_t channel_layout_mask_;
|
||||
|
||||
int channel_count_; ///< Cached channel count from layout
|
||||
|
||||
SampleFormat format_; ///< Audio sample format
|
||||
|
||||
// Footage-specific parameters (serialized with footage metadata)
|
||||
int enabled_; // Using int instead of bool fixes GCC 11 stringop-overflow issue (byte alignment)
|
||||
int stream_index_; ///< Index in the source file's stream list
|
||||
int64_t duration_; ///< Stream duration in timebase units
|
||||
Rational timebase_; ///< Timebase for this audio stream
|
||||
OakAudioParams *handle_;
|
||||
};
|
||||
|
||||
inline const std::vector<uint64_t> AudioParams::k_supported_channel_layouts = [] {
|
||||
std::vector<uint64_t> v;
|
||||
const int n = oakcore_audioparams_supported_channel_layout_count();
|
||||
v.reserve(size_t(n));
|
||||
for (int i = 0; i < n; i++) {
|
||||
v.push_back(oakcore_audioparams_supported_channel_layout_at(i));
|
||||
}
|
||||
return v;
|
||||
}();
|
||||
|
||||
inline const std::vector<int> AudioParams::k_supported_sample_rates = [] {
|
||||
std::vector<int> v;
|
||||
const int n = oakcore_audioparams_supported_sample_rate_count();
|
||||
v.reserve(size_t(n));
|
||||
for (int i = 0; i < n; i++) {
|
||||
v.push_back(oakcore_audioparams_supported_sample_rate_at(i));
|
||||
}
|
||||
return v;
|
||||
}();
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,11 +22,11 @@
|
||||
#ifndef OAK_LIBOLIVECORE_SAMPLEBUFFER_H
|
||||
#define OAK_LIBOLIVECORE_SAMPLEBUFFER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "audioparams.h"
|
||||
#include "../util/rational.h"
|
||||
#include "olive/core/oakcore/samplebuffer.h"
|
||||
#include "olive/core/render/audioparams.h"
|
||||
#include "olive/core/util/rational.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
@@ -34,102 +34,265 @@ namespace olive::core
|
||||
/**
|
||||
* @brief A buffer of audio samples
|
||||
*
|
||||
* Audio samples in this structure are always stored in PLANAR (separated by channel). This is done to simplify audio
|
||||
* rendering code. This replaces the old system of using QByteArrays (containing packed audio) and while SampleBuffer
|
||||
* replaces many of those in the rendering/processing side of things, QByteArrays are currently still in use for
|
||||
* playback, including reading to and from the cache.
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakSampleBuffer handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation, except that
|
||||
* the getters return AudioParams/size_t by value instead of by const
|
||||
* reference.
|
||||
*
|
||||
* Audio samples in this structure are always stored in PLANAR (separated by
|
||||
* channel). This is done to simplify audio rendering code. This replaces the
|
||||
* old system of using QByteArrays (containing packed audio) and while
|
||||
* SampleBuffer replaces many of those in the rendering/processing side of
|
||||
* things, QByteArrays are currently still in use for playback, including
|
||||
* reading to and from the cache.
|
||||
*/
|
||||
class SampleBuffer {
|
||||
public:
|
||||
SampleBuffer();
|
||||
SampleBuffer(const AudioParams &audio_params, const Rational &length);
|
||||
SampleBuffer(const AudioParams &audio_params, size_t samples_per_channel);
|
||||
|
||||
SampleBuffer rip_channel(int channel) const;
|
||||
std::vector<float> rip_channel_vector(int channel) const;
|
||||
|
||||
const AudioParams &audio_params() const;
|
||||
void set_audio_params(const AudioParams ¶ms);
|
||||
|
||||
const size_t &sample_count() const
|
||||
SampleBuffer()
|
||||
: handle_(oakcore_samplebuffer_create())
|
||||
{
|
||||
return sample_count_per_channel_;
|
||||
}
|
||||
void set_sample_count(const size_t &sample_count);
|
||||
|
||||
SampleBuffer(const AudioParams &audio_params, const Rational &length)
|
||||
: handle_(oakcore_samplebuffer_create_length(audio_params.handle(),
|
||||
length.handle()))
|
||||
{
|
||||
}
|
||||
|
||||
SampleBuffer(const AudioParams &audio_params, size_t samples_per_channel)
|
||||
: handle_(oakcore_samplebuffer_create_samples(audio_params.handle(),
|
||||
samples_per_channel))
|
||||
{
|
||||
}
|
||||
|
||||
SampleBuffer(const SampleBuffer &rhs)
|
||||
: handle_(oakcore_samplebuffer_copy(rhs.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
SampleBuffer(SampleBuffer &&rhs) noexcept
|
||||
: handle_(rhs.handle_)
|
||||
{
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
|
||||
~SampleBuffer()
|
||||
{
|
||||
oakcore_samplebuffer_free(handle_);
|
||||
}
|
||||
|
||||
SampleBuffer &operator=(const SampleBuffer &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_samplebuffer_free(handle_);
|
||||
handle_ = oakcore_samplebuffer_copy(rhs.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleBuffer &operator=(SampleBuffer &&rhs) noexcept
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_samplebuffer_free(handle_);
|
||||
handle_ = rhs.handle_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleBuffer rip_channel(int channel) const
|
||||
{
|
||||
return from_handle(oakcore_samplebuffer_rip_channel(handle_, channel));
|
||||
}
|
||||
|
||||
std::vector<float> rip_channel_vector(int channel) const
|
||||
{
|
||||
const int size = oakcore_samplebuffer_rip_channel_vector(
|
||||
handle_, channel, nullptr, 0);
|
||||
std::vector<float> v(static_cast<size_t>(size));
|
||||
if (size > 0) {
|
||||
oakcore_samplebuffer_rip_channel_vector(handle_, channel, v.data(),
|
||||
size);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
AudioParams audio_params() const
|
||||
{
|
||||
return AudioParams::from_handle(
|
||||
oakcore_samplebuffer_audio_params(handle_));
|
||||
}
|
||||
|
||||
void set_audio_params(const AudioParams ¶ms)
|
||||
{
|
||||
oakcore_samplebuffer_set_audio_params(handle_, params.handle());
|
||||
}
|
||||
|
||||
size_t sample_count() const
|
||||
{
|
||||
return oakcore_samplebuffer_sample_count(handle_);
|
||||
}
|
||||
|
||||
void set_sample_count(const size_t &sample_count)
|
||||
{
|
||||
oakcore_samplebuffer_set_sample_count(handle_, sample_count);
|
||||
}
|
||||
|
||||
void set_sample_count(const Rational &length)
|
||||
{
|
||||
set_sample_count(audio_params_.time_to_samples(length));
|
||||
oakcore_samplebuffer_set_sample_count_length(handle_,
|
||||
length.handle());
|
||||
}
|
||||
|
||||
float *data(int channel)
|
||||
{
|
||||
return data_[channel].data();
|
||||
return oakcore_samplebuffer_data(handle_, channel);
|
||||
}
|
||||
|
||||
const float *data(int channel) const
|
||||
{
|
||||
return data_.at(channel).data();
|
||||
return oakcore_samplebuffer_data(handle_, channel);
|
||||
}
|
||||
|
||||
std::vector<float *> to_raw_ptrs()
|
||||
{
|
||||
std::vector<float *> r(data_.size());
|
||||
for (size_t i = 0; i < r.size(); i++) {
|
||||
r[i] = data_[i].data();
|
||||
std::vector<float *> r(static_cast<size_t>(channel_count()));
|
||||
if (!r.empty()) {
|
||||
oakcore_samplebuffer_to_raw_ptrs(handle_, r.data());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return data_.size();
|
||||
return oakcore_samplebuffer_channel_count(handle_);
|
||||
}
|
||||
|
||||
bool is_allocated() const
|
||||
{
|
||||
return !data_.empty();
|
||||
return oakcore_samplebuffer_is_allocated(handle_) != 0;
|
||||
}
|
||||
void allocate();
|
||||
void destroy();
|
||||
|
||||
void reverse();
|
||||
void speed(double speed);
|
||||
void allocate()
|
||||
{
|
||||
oakcore_samplebuffer_allocate(handle_);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
oakcore_samplebuffer_destroy(handle_);
|
||||
}
|
||||
|
||||
void reverse()
|
||||
{
|
||||
oakcore_samplebuffer_reverse(handle_);
|
||||
}
|
||||
|
||||
void speed(double speed)
|
||||
{
|
||||
oakcore_samplebuffer_speed(handle_, speed);
|
||||
}
|
||||
|
||||
void transform_volume(float f)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume(handle_, f);
|
||||
}
|
||||
|
||||
void transform_volume_for_channel(int channel, float volume)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume_for_channel(handle_, channel,
|
||||
volume);
|
||||
}
|
||||
|
||||
void transform_volume(float f);
|
||||
void transform_volume_for_channel(int channel, float volume);
|
||||
static void transform_volume(float f, const SampleBuffer *input,
|
||||
SampleBuffer *output);
|
||||
SampleBuffer *output)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume_to(f, input->handle_,
|
||||
output->handle_);
|
||||
}
|
||||
|
||||
static void transform_volume_for_channel(int channel, float volume,
|
||||
const SampleBuffer *input,
|
||||
SampleBuffer *output);
|
||||
SampleBuffer *output)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume_for_channel_to(
|
||||
channel, volume, input->handle_, output->handle_);
|
||||
}
|
||||
|
||||
void transform_volume_for_sample(size_t sample_index, float volume)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume_for_sample(handle_, sample_index,
|
||||
volume);
|
||||
}
|
||||
|
||||
void transform_volume_for_sample(size_t sample_index, float volume);
|
||||
void transform_volume_for_sample_on_channel(size_t sample_index,
|
||||
int channel, float volume);
|
||||
int channel, float volume)
|
||||
{
|
||||
oakcore_samplebuffer_transform_volume_for_sample_on_channel(
|
||||
handle_, sample_index, channel, volume);
|
||||
}
|
||||
|
||||
void clamp();
|
||||
void clamp()
|
||||
{
|
||||
oakcore_samplebuffer_clamp(handle_);
|
||||
}
|
||||
|
||||
void silence();
|
||||
void silence(size_t start_sample, size_t end_sample);
|
||||
void silence_bytes(size_t start_byte, size_t end_byte);
|
||||
void silence()
|
||||
{
|
||||
oakcore_samplebuffer_silence(handle_);
|
||||
}
|
||||
|
||||
void silence(size_t start_sample, size_t end_sample)
|
||||
{
|
||||
oakcore_samplebuffer_silence_range(handle_, start_sample, end_sample);
|
||||
}
|
||||
|
||||
void silence_bytes(size_t start_byte, size_t end_byte)
|
||||
{
|
||||
oakcore_samplebuffer_silence_bytes(handle_, start_byte, end_byte);
|
||||
}
|
||||
|
||||
void set(int channel, const float *data, size_t sample_offset,
|
||||
size_t sample_length);
|
||||
size_t sample_length)
|
||||
{
|
||||
oakcore_samplebuffer_set(handle_, channel, data, sample_offset,
|
||||
sample_length);
|
||||
}
|
||||
|
||||
void set(int channel, const float *data, size_t sample_length)
|
||||
{
|
||||
set(channel, data, 0, sample_length);
|
||||
}
|
||||
|
||||
void fast_set(const SampleBuffer &other, int to, int from = -1);
|
||||
void fast_set(const SampleBuffer &other, int to, int from = -1)
|
||||
{
|
||||
oakcore_samplebuffer_fast_set(handle_, other.handle_, to, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakSampleBuffer *handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
static SampleBuffer from_handle(OakSampleBuffer *handle)
|
||||
{
|
||||
return SampleBuffer(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
void clamp_channel(int channel);
|
||||
explicit SampleBuffer(OakSampleBuffer *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
size_t sample_count_per_channel_;
|
||||
|
||||
std::vector<std::vector<float>> data_;
|
||||
OakSampleBuffer *handle_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,113 +24,193 @@
|
||||
|
||||
#include <Imath/ImathVec.h>
|
||||
|
||||
#include "olive/core/oakcore/bezier.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Keyframe easing bezier curve value type
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakBezier handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation.
|
||||
*/
|
||||
class Bezier {
|
||||
public:
|
||||
Bezier();
|
||||
Bezier(double x, double y);
|
||||
Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x,
|
||||
double cp2_y);
|
||||
Bezier()
|
||||
: handle_(oakcore_bezier_create())
|
||||
{
|
||||
}
|
||||
|
||||
const double &x() const
|
||||
Bezier(double x, double y)
|
||||
: handle_(oakcore_bezier_create_xy(x, y))
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
const double &y() const
|
||||
|
||||
Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x,
|
||||
double cp2_y)
|
||||
: handle_(
|
||||
oakcore_bezier_create_full(x, y, cp1_x, cp1_y, cp2_x, cp2_y))
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
const double &cp1_x() const
|
||||
|
||||
Bezier(const Bezier &rhs)
|
||||
: handle_(oakcore_bezier_copy(rhs.handle_))
|
||||
{
|
||||
return cp1_x_;
|
||||
}
|
||||
const double &cp1_y() const
|
||||
|
||||
Bezier(Bezier &&rhs) noexcept
|
||||
: handle_(rhs.handle_)
|
||||
{
|
||||
return cp1_y_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
const double &cp2_x() const
|
||||
|
||||
~Bezier()
|
||||
{
|
||||
return cp2_x_;
|
||||
oakcore_bezier_free(handle_);
|
||||
}
|
||||
const double &cp2_y() const
|
||||
|
||||
Bezier &operator=(const Bezier &rhs)
|
||||
{
|
||||
return cp2_y_;
|
||||
if (this != &rhs) {
|
||||
oakcore_bezier_free(handle_);
|
||||
handle_ = oakcore_bezier_copy(rhs.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bezier &operator=(Bezier &&rhs) noexcept
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_bezier_free(handle_);
|
||||
handle_ = rhs.handle_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
double x() const
|
||||
{
|
||||
return oakcore_bezier_x(handle_);
|
||||
}
|
||||
double y() const
|
||||
{
|
||||
return oakcore_bezier_y(handle_);
|
||||
}
|
||||
double cp1_x() const
|
||||
{
|
||||
return oakcore_bezier_cp1_x(handle_);
|
||||
}
|
||||
double cp1_y() const
|
||||
{
|
||||
return oakcore_bezier_cp1_y(handle_);
|
||||
}
|
||||
double cp2_x() const
|
||||
{
|
||||
return oakcore_bezier_cp2_x(handle_);
|
||||
}
|
||||
double cp2_y() const
|
||||
{
|
||||
return oakcore_bezier_cp2_y(handle_);
|
||||
}
|
||||
|
||||
Imath::V2d to_vec() const
|
||||
{
|
||||
return Imath::V2d(x_, y_);
|
||||
return Imath::V2d(x(), y());
|
||||
}
|
||||
|
||||
Imath::V2d control_point_1_to_vec() const
|
||||
{
|
||||
return Imath::V2d(cp1_x_, cp1_y_);
|
||||
return Imath::V2d(cp1_x(), cp1_y());
|
||||
}
|
||||
|
||||
Imath::V2d control_point_2_to_vec() const
|
||||
{
|
||||
return Imath::V2d(cp2_x_, cp2_y_);
|
||||
return Imath::V2d(cp2_x(), cp2_y());
|
||||
}
|
||||
|
||||
void set_x(const double &x)
|
||||
{
|
||||
x_ = x;
|
||||
oakcore_bezier_set_x(handle_, x);
|
||||
}
|
||||
void set_y(const double &y)
|
||||
{
|
||||
y_ = y;
|
||||
oakcore_bezier_set_y(handle_, y);
|
||||
}
|
||||
void set_cp1_x(const double &cp1_x)
|
||||
{
|
||||
cp1_x_ = cp1_x;
|
||||
oakcore_bezier_set_cp1_x(handle_, cp1_x);
|
||||
}
|
||||
void set_cp1_y(const double &cp1_y)
|
||||
{
|
||||
cp1_y_ = cp1_y;
|
||||
oakcore_bezier_set_cp1_y(handle_, cp1_y);
|
||||
}
|
||||
void set_cp2_x(const double &cp2_x)
|
||||
{
|
||||
cp2_x_ = cp2_x;
|
||||
oakcore_bezier_set_cp2_x(handle_, cp2_x);
|
||||
}
|
||||
void set_cp2_y(const double &cp2_y)
|
||||
{
|
||||
cp2_y_ = cp2_y;
|
||||
oakcore_bezier_set_cp2_y(handle_, cp2_y);
|
||||
}
|
||||
|
||||
static double quadratic_xto_t(double x, double a, double b, double c);
|
||||
static double quadratic_xto_t(double x, double a, double b, double c)
|
||||
{
|
||||
return oakcore_bezier_quadratic_xto_t(x, a, b, c);
|
||||
}
|
||||
|
||||
static double quadratic_tto_y(double a, double b, double c, double t);
|
||||
static double quadratic_tto_y(double a, double b, double c, double t)
|
||||
{
|
||||
return oakcore_bezier_quadratic_tto_y(a, b, c, t);
|
||||
}
|
||||
|
||||
static double quadratic_xto_y(double x, const Imath::V2d &a,
|
||||
const Imath::V2d &b, const Imath::V2d &c)
|
||||
const Imath::V2d &b, const Imath::V2d &c)
|
||||
{
|
||||
return quadratic_tto_y(a.y, b.y, c.y, quadratic_xto_t(x, a.x, b.x, c.x));
|
||||
}
|
||||
|
||||
static double cubic_xto_t(double x, double a, double b, double c, double d);
|
||||
static double cubic_xto_t(double x, double a, double b, double c, double d)
|
||||
{
|
||||
return oakcore_bezier_cubic_xto_t(x, a, b, c, d);
|
||||
}
|
||||
|
||||
static double cubic_tto_y(double a, double b, double c, double d, double t);
|
||||
static double cubic_tto_y(double a, double b, double c, double d, double t)
|
||||
{
|
||||
return oakcore_bezier_cubic_tto_y(a, b, c, d, t);
|
||||
}
|
||||
|
||||
static double cubic_xto_y(double x, const Imath::V2d &a, const Imath::V2d &b,
|
||||
const Imath::V2d &c, const Imath::V2d &d)
|
||||
const Imath::V2d &c, const Imath::V2d &d)
|
||||
{
|
||||
return cubic_tto_y(a.y, b.y, c.y, d.y, cubic_xto_t(x, a.x, b.x, c.x, d.x));
|
||||
return cubic_tto_y(a.y, b.y, c.y, d.y,
|
||||
cubic_xto_t(x, a.x, b.x, c.x, d.x));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakBezier *handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
static Bezier from_handle(OakBezier *handle)
|
||||
{
|
||||
return Bezier(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
static double calculate_t_from_x(bool cubic, double x, double a, double b,
|
||||
double c, double d);
|
||||
explicit Bezier(OakBezier *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
double x_;
|
||||
double y_;
|
||||
|
||||
double cp1_x_;
|
||||
double cp1_y_;
|
||||
|
||||
double cp2_x_;
|
||||
double cp2_y_;
|
||||
OakBezier *handle_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef OAK_LIBOLIVECORE_COLOR_H
|
||||
#define OAK_LIBOLIVECORE_COLOR_H
|
||||
|
||||
#include "olive/core/oakcore/color.h"
|
||||
#include "../render/pixelformat.h"
|
||||
|
||||
namespace olive::core
|
||||
@@ -29,6 +30,10 @@ namespace olive::core
|
||||
|
||||
/**
|
||||
* @brief High precision 32-bit DataType based RGBA color value
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakColor handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation.
|
||||
*/
|
||||
class Color {
|
||||
public:
|
||||
@@ -36,22 +41,55 @@ public:
|
||||
static constexpr unsigned int rgba = 4;
|
||||
|
||||
Color()
|
||||
: handle_(oakcore_color_create())
|
||||
{
|
||||
for (unsigned int i = 0; i < rgba; i++) {
|
||||
data_[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Color(const DataType &r, const DataType &g, const DataType &b,
|
||||
const DataType &a = 1.0f)
|
||||
: handle_(oakcore_color_create_rgba(r, g, b, a))
|
||||
{
|
||||
data_[0] = r;
|
||||
data_[1] = g;
|
||||
data_[2] = b;
|
||||
data_[3] = a;
|
||||
}
|
||||
|
||||
Color(const char *data, const PixelFormat &format, int ch_layout);
|
||||
Color(const char *data, const PixelFormat &format, int ch_layout)
|
||||
: handle_(oakcore_color_from_data(data, format, ch_layout))
|
||||
{
|
||||
}
|
||||
|
||||
Color(const Color &rhs)
|
||||
: handle_(oakcore_color_copy(rhs.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
Color(Color &&rhs) noexcept
|
||||
: handle_(rhs.handle_)
|
||||
{
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
|
||||
~Color()
|
||||
{
|
||||
oakcore_color_free(handle_);
|
||||
}
|
||||
|
||||
Color &operator=(const Color &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_color_free(handle_);
|
||||
handle_ = oakcore_color_copy(rhs.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator=(Color &&rhs) noexcept
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_color_free(handle_);
|
||||
handle_ = rhs.handle_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a Color struct from hue/saturation/value
|
||||
@@ -59,78 +97,143 @@ public:
|
||||
* Hue expects a value between 0.0 and 360.0. Saturation and Value expect a value between 0.0 and 1.0.
|
||||
*/
|
||||
static Color from_hsv(const DataType &h, const DataType &s,
|
||||
const DataType &v);
|
||||
|
||||
const DataType &red() const
|
||||
const DataType &v)
|
||||
{
|
||||
return data_[0];
|
||||
}
|
||||
const DataType &green() const
|
||||
{
|
||||
return data_[1];
|
||||
}
|
||||
const DataType &blue() const
|
||||
{
|
||||
return data_[2];
|
||||
}
|
||||
const DataType &alpha() const
|
||||
{
|
||||
return data_[3];
|
||||
return from_handle(oakcore_color_from_hsv(h, s, v));
|
||||
}
|
||||
|
||||
void to_hsv(DataType *hue, DataType *sat, DataType *val) const;
|
||||
DataType hsv_hue() const;
|
||||
DataType hsv_saturation() const;
|
||||
DataType value() const;
|
||||
DataType red() const
|
||||
{
|
||||
return oakcore_color_red(handle_);
|
||||
}
|
||||
DataType green() const
|
||||
{
|
||||
return oakcore_color_green(handle_);
|
||||
}
|
||||
DataType blue() const
|
||||
{
|
||||
return oakcore_color_blue(handle_);
|
||||
}
|
||||
DataType alpha() const
|
||||
{
|
||||
return oakcore_color_alpha(handle_);
|
||||
}
|
||||
|
||||
void to_hsl(DataType *hue, DataType *sat, DataType *lightness) const;
|
||||
DataType hsl_hue() const;
|
||||
DataType hsl_saturation() const;
|
||||
DataType lightness() const;
|
||||
void to_hsv(DataType *hue, DataType *sat, DataType *val) const
|
||||
{
|
||||
oakcore_color_to_hsv(handle_, hue, sat, val);
|
||||
}
|
||||
DataType hsv_hue() const
|
||||
{
|
||||
return oakcore_color_hsv_hue(handle_);
|
||||
}
|
||||
DataType hsv_saturation() const
|
||||
{
|
||||
return oakcore_color_hsv_saturation(handle_);
|
||||
}
|
||||
DataType value() const
|
||||
{
|
||||
return oakcore_color_value(handle_);
|
||||
}
|
||||
|
||||
void to_hsl(DataType *hue, DataType *sat, DataType *lightness) const
|
||||
{
|
||||
oakcore_color_to_hsl(handle_, hue, sat, lightness);
|
||||
}
|
||||
DataType hsl_hue() const
|
||||
{
|
||||
return oakcore_color_hsl_hue(handle_);
|
||||
}
|
||||
DataType hsl_saturation() const
|
||||
{
|
||||
return oakcore_color_hsl_saturation(handle_);
|
||||
}
|
||||
DataType lightness() const
|
||||
{
|
||||
return oakcore_color_lightness(handle_);
|
||||
}
|
||||
|
||||
void set_red(const DataType &red)
|
||||
{
|
||||
data_[0] = red;
|
||||
oakcore_color_set_red(handle_, red);
|
||||
}
|
||||
void set_green(const DataType &green)
|
||||
{
|
||||
data_[1] = green;
|
||||
oakcore_color_set_green(handle_, green);
|
||||
}
|
||||
void set_blue(const DataType &blue)
|
||||
{
|
||||
data_[2] = blue;
|
||||
oakcore_color_set_blue(handle_, blue);
|
||||
}
|
||||
void set_alpha(const DataType &alpha)
|
||||
{
|
||||
data_[3] = alpha;
|
||||
oakcore_color_set_alpha(handle_, alpha);
|
||||
}
|
||||
|
||||
DataType *data()
|
||||
{
|
||||
return data_;
|
||||
return oakcore_color_data(handle_);
|
||||
}
|
||||
const DataType *data() const
|
||||
{
|
||||
return data_;
|
||||
return oakcore_color_const_data(handle_);
|
||||
}
|
||||
|
||||
void to_data(char *out, const PixelFormat &format,
|
||||
unsigned int nb_channels) const;
|
||||
unsigned int nb_channels) const
|
||||
{
|
||||
oakcore_color_to_data(handle_, out, format, int(nb_channels));
|
||||
}
|
||||
|
||||
static Color from_data(const char *in, const PixelFormat &format,
|
||||
unsigned int nb_channels);
|
||||
unsigned int nb_channels)
|
||||
{
|
||||
return from_handle(oakcore_color_from_data(in, format, int(nb_channels)));
|
||||
}
|
||||
|
||||
// Suuuuper rough luminance value mostly used for UI (determining whether to overlay with black
|
||||
// or white text)
|
||||
DataType get_rough_luminance() const;
|
||||
DataType get_rough_luminance() const
|
||||
{
|
||||
return oakcore_color_get_rough_luminance(handle_);
|
||||
}
|
||||
|
||||
// Assignment math operators
|
||||
Color &operator+=(const Color &rhs);
|
||||
Color &operator-=(const Color &rhs);
|
||||
Color &operator+=(const DataType &rhs);
|
||||
Color &operator-=(const DataType &rhs);
|
||||
Color &operator*=(const DataType &rhs);
|
||||
Color &operator/=(const DataType &rhs);
|
||||
Color &operator+=(const Color &rhs)
|
||||
{
|
||||
oakcore_color_add_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator-=(const Color &rhs)
|
||||
{
|
||||
oakcore_color_sub_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator+=(const DataType &rhs)
|
||||
{
|
||||
oakcore_color_add_scalar_assign(handle_, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator-=(const DataType &rhs)
|
||||
{
|
||||
oakcore_color_sub_scalar_assign(handle_, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator*=(const DataType &rhs)
|
||||
{
|
||||
oakcore_color_mul_scalar_assign(handle_, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator/=(const DataType &rhs)
|
||||
{
|
||||
oakcore_color_div_scalar_assign(handle_, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Binary math operators
|
||||
Color operator+(const Color &rhs) const
|
||||
@@ -175,8 +278,29 @@ public:
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakColor *handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
static Color from_handle(OakColor *handle)
|
||||
{
|
||||
return Color(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
DataType data_[rgba];
|
||||
explicit Color(OakColor *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
OakColor *handle_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,14 +24,20 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "olive/core/oakcore/fractionutils.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Rounding modes for RescaleRnd()
|
||||
* @brief Rounding modes for rescale_rnd()
|
||||
*
|
||||
* Mirrors the FFmpeg AVRounding modes that this codebase used before the
|
||||
* FFmpeg dependency was removed from core.
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: every function forwards
|
||||
* across the C boundary. The public API is unchanged from the original
|
||||
* implementation.
|
||||
*/
|
||||
enum class FractionRounding {
|
||||
/**
|
||||
@@ -55,7 +61,10 @@ enum class FractionRounding {
|
||||
*
|
||||
* A zero denominator is preserved (with the numerator set to zero).
|
||||
*/
|
||||
void reduce_fraction(int64_t &num, int64_t &den, int64_t max);
|
||||
inline void reduce_fraction(int64_t &num, int64_t &den, int64_t max)
|
||||
{
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two fractions
|
||||
@@ -64,7 +73,10 @@ void reduce_fraction(int64_t &num, int64_t &den, int64_t max);
|
||||
* 0 if a == b, 1 if a > b, and INT_MIN when the comparison is meaningless
|
||||
* (degenerate zero-denominator fractions).
|
||||
*/
|
||||
int compare_fractions(int an, int ad, int bn, int bd);
|
||||
inline int compare_fractions(int an, int ad, int bn, int bd)
|
||||
{
|
||||
return oakcore_fractionutils_compare_fractions(an, ad, bn, bd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rescale `a` by the fraction b/c: returns a * b / c
|
||||
@@ -73,7 +85,11 @@ int compare_fractions(int an, int ad, int bn, int bd);
|
||||
* product is computed with 128-bit arithmetic where available so that no
|
||||
* precision is lost for large timestamps.
|
||||
*/
|
||||
int64_t rescale_rnd(int64_t a, int64_t b, int64_t c, FractionRounding rnd);
|
||||
inline int64_t rescale_rnd(int64_t a, int64_t b, int64_t c, FractionRounding rnd)
|
||||
{
|
||||
return oakcore_fractionutils_rescale_rnd(a, b, c,
|
||||
static_cast<OakFractionRounding>(rnd));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,131 +24,295 @@
|
||||
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_OTIO
|
||||
#include <opentime/rationalTime.h>
|
||||
#endif
|
||||
|
||||
#include "olive/core/oakcore/rational.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Rational number value type
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakRational handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation.
|
||||
*/
|
||||
class Rational {
|
||||
public:
|
||||
Rational(const int &numerator = 0)
|
||||
: handle_(oakcore_rational_create(numerator))
|
||||
{
|
||||
num_ = numerator;
|
||||
den_ = 1;
|
||||
}
|
||||
|
||||
Rational(const int &numerator, const int &denominator)
|
||||
: handle_(oakcore_rational_create_nd(numerator, denominator))
|
||||
{
|
||||
num_ = numerator;
|
||||
den_ = denominator;
|
||||
|
||||
fix_signs();
|
||||
reduce();
|
||||
}
|
||||
|
||||
Rational(const Rational &rhs) = default;
|
||||
Rational(const Rational &rhs)
|
||||
: handle_(oakcore_rational_copy(rhs.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
static Rational from_double(const double &flt, bool *ok = nullptr);
|
||||
static Rational from_string(const std::string &str, bool *ok = nullptr);
|
||||
Rational(Rational &&rhs) noexcept
|
||||
: handle_(rhs.handle_)
|
||||
{
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
|
||||
~Rational()
|
||||
{
|
||||
oakcore_rational_free(handle_);
|
||||
}
|
||||
|
||||
static Rational from_double(const double &flt, bool *ok = nullptr)
|
||||
{
|
||||
int c_ok = 0;
|
||||
Rational r(from_handle(oakcore_rational_from_double(flt, &c_ok)));
|
||||
if (ok) {
|
||||
*ok = (c_ok != 0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static Rational from_string(const std::string &str, bool *ok = nullptr)
|
||||
{
|
||||
int c_ok = 0;
|
||||
Rational r(
|
||||
from_handle(oakcore_rational_from_string(str.c_str(), &c_ok)));
|
||||
if (ok) {
|
||||
*ok = (c_ok != 0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static const Rational na_n;
|
||||
|
||||
//Assignment Operators
|
||||
const Rational &operator=(const Rational &rhs);
|
||||
const Rational &operator+=(const Rational &rhs);
|
||||
const Rational &operator-=(const Rational &rhs);
|
||||
const Rational &operator/=(const Rational &rhs);
|
||||
const Rational &operator*=(const Rational &rhs);
|
||||
Rational &operator=(const Rational &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_rational_free(handle_);
|
||||
handle_ = oakcore_rational_copy(rhs.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational &operator=(Rational &&rhs) noexcept
|
||||
{
|
||||
if (this != &rhs) {
|
||||
oakcore_rational_free(handle_);
|
||||
handle_ = rhs.handle_;
|
||||
rhs.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational &operator+=(const Rational &rhs)
|
||||
{
|
||||
oakcore_rational_add_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational &operator-=(const Rational &rhs)
|
||||
{
|
||||
oakcore_rational_sub_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational &operator/=(const Rational &rhs)
|
||||
{
|
||||
oakcore_rational_div_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational &operator*=(const Rational &rhs)
|
||||
{
|
||||
oakcore_rational_mul_assign(handle_, rhs.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Binary math operators
|
||||
Rational operator+(const Rational &rhs) const;
|
||||
Rational operator-(const Rational &rhs) const;
|
||||
Rational operator/(const Rational &rhs) const;
|
||||
Rational operator*(const Rational &rhs) const;
|
||||
Rational operator+(const Rational &rhs) const
|
||||
{
|
||||
Rational answer(*this);
|
||||
answer += rhs;
|
||||
return answer;
|
||||
}
|
||||
|
||||
Rational operator-(const Rational &rhs) const
|
||||
{
|
||||
Rational answer(*this);
|
||||
answer -= rhs;
|
||||
return answer;
|
||||
}
|
||||
|
||||
Rational operator/(const Rational &rhs) const
|
||||
{
|
||||
Rational answer(*this);
|
||||
answer /= rhs;
|
||||
return answer;
|
||||
}
|
||||
|
||||
Rational operator*(const Rational &rhs) const
|
||||
{
|
||||
Rational answer(*this);
|
||||
answer *= rhs;
|
||||
return answer;
|
||||
}
|
||||
|
||||
//Relational and equality operators
|
||||
bool operator<(const Rational &rhs) const;
|
||||
bool operator<=(const Rational &rhs) const;
|
||||
bool operator>(const Rational &rhs) const;
|
||||
bool operator>=(const Rational &rhs) const;
|
||||
bool operator==(const Rational &rhs) const;
|
||||
bool operator!=(const Rational &rhs) const;
|
||||
bool operator<(const Rational &rhs) const
|
||||
{
|
||||
return oakcore_rational_compare(handle_, rhs.handle_) < 0;
|
||||
}
|
||||
|
||||
bool operator<=(const Rational &rhs) const
|
||||
{
|
||||
return oakcore_rational_compare(handle_, rhs.handle_) <= 0;
|
||||
}
|
||||
|
||||
bool operator>(const Rational &rhs) const
|
||||
{
|
||||
return oakcore_rational_compare(handle_, rhs.handle_) > 0;
|
||||
}
|
||||
|
||||
bool operator>=(const Rational &rhs) const
|
||||
{
|
||||
return oakcore_rational_compare(handle_, rhs.handle_) >= 0;
|
||||
}
|
||||
|
||||
bool operator==(const Rational &rhs) const
|
||||
{
|
||||
return oakcore_rational_compare(handle_, rhs.handle_) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Rational &rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
//Unary operators
|
||||
const Rational &operator+() const
|
||||
Rational operator+() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator-() const
|
||||
{
|
||||
return Rational(num_, -den_);
|
||||
return Rational(numerator(), -denominator());
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !num_;
|
||||
return numerator() == 0;
|
||||
}
|
||||
|
||||
//Function: convert to double
|
||||
double to_double() const;
|
||||
double to_double() const
|
||||
{
|
||||
return oakcore_rational_to_double(handle_);
|
||||
}
|
||||
|
||||
#ifdef USE_OTIO
|
||||
static Rational fromRationalTime(const opentime::RationalTime &t)
|
||||
{
|
||||
// Is this the best way to do this?
|
||||
return fromDouble(t.to_seconds());
|
||||
return from_double(t.to_seconds());
|
||||
}
|
||||
|
||||
// Convert Olive rationals to opentime rationals with the given framerate (defaults to 24)
|
||||
opentime::RationalTime toRationalTime(double framerate = 24) const;
|
||||
opentime::RationalTime toRationalTime(double framerate = 24) const
|
||||
{
|
||||
// Olive can store rationals as 0/0 which causes errors in OTIO
|
||||
const int den = denominator();
|
||||
opentime::RationalTime time(numerator(), den == 0 ? 1 : den);
|
||||
return time.rescaled_to(framerate);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Produce "flipped" version
|
||||
Rational flipped() const;
|
||||
void flip();
|
||||
Rational flipped() const
|
||||
{
|
||||
return from_handle(oakcore_rational_flipped(handle_));
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
oakcore_rational_flip(handle_);
|
||||
}
|
||||
|
||||
// Returns whether the Rational is valid but equal to zero or not
|
||||
//
|
||||
// A NaN is always a null, but a null is not always a NaN
|
||||
bool isNull() const
|
||||
{
|
||||
return num_ == 0;
|
||||
return oakcore_rational_is_null(handle_) != 0;
|
||||
}
|
||||
|
||||
// Returns whether this Rational is not a valid number (denominator == 0)
|
||||
bool isNaN() const
|
||||
{
|
||||
return den_ == 0;
|
||||
return oakcore_rational_is_nan(handle_) != 0;
|
||||
}
|
||||
|
||||
const int &numerator() const
|
||||
int numerator() const
|
||||
{
|
||||
return num_;
|
||||
}
|
||||
const int &denominator() const
|
||||
{
|
||||
return den_;
|
||||
return oakcore_rational_numerator(handle_);
|
||||
}
|
||||
|
||||
std::string to_string() const;
|
||||
int denominator() const
|
||||
{
|
||||
return oakcore_rational_denominator(handle_);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const int size = oakcore_rational_to_string(handle_, nullptr, 0);
|
||||
std::string s(size_t(size) + 1, '\0');
|
||||
oakcore_rational_to_string(handle_, s.data(), size + 1);
|
||||
s.resize(size_t(size));
|
||||
return s;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, const Rational &value)
|
||||
{
|
||||
out << value.num_ << '/' << value.den_;
|
||||
out << value.numerator() << '/' << value.denominator();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
void fix_signs();
|
||||
void reduce();
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakRational *handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
int num_;
|
||||
int den_;
|
||||
/**
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
static Rational from_handle(OakRational *handle)
|
||||
{
|
||||
return Rational(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Rational(OakRational *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
OakRational *handle_;
|
||||
};
|
||||
|
||||
inline const Rational Rational::na_n =
|
||||
Rational::from_handle(oakcore_rational_create_nan());
|
||||
|
||||
#define RATIONAL_MIN Rational(INT_MIN)
|
||||
#define RATIONAL_MAX Rational(INT_MAX)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,13 +23,24 @@
|
||||
#define OAK_LIBOLIVECORE_STRINGUTILS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "olive/core/oakcore/stringutils.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief String utility functions
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: every non-inlined call
|
||||
* is forwarded across the C boundary. The public API is unchanged from the
|
||||
* original implementation. The class has static members only, so there is
|
||||
* no opaque handle.
|
||||
*/
|
||||
class StringUtils {
|
||||
public:
|
||||
/**
|
||||
@@ -47,7 +58,20 @@ public:
|
||||
*
|
||||
* A vector of strings split by the specified delimiter.
|
||||
*/
|
||||
static std::vector<std::string> split(const std::string &s, char separator);
|
||||
static std::vector<std::string> split(const std::string &s, char separator)
|
||||
{
|
||||
int count = 0;
|
||||
char **arr = oakcore_stringutils_split(s.c_str(), separator, &count);
|
||||
std::vector<std::string> output;
|
||||
if (arr) {
|
||||
output.reserve(size_t(count));
|
||||
for (int i = 0; i < count; i++) {
|
||||
output.emplace_back(arr[i]);
|
||||
}
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Splits a string into a list of strings using regular expressions.
|
||||
@@ -65,10 +89,26 @@ public:
|
||||
* A vector of strings split wherever the regular expression matched.
|
||||
*/
|
||||
static std::vector<std::string> split_regex(const std::string &s,
|
||||
const std::regex ®ex);
|
||||
const std::regex ®ex)
|
||||
{
|
||||
// std::regex cannot cross the C ABI, so this overload is implemented
|
||||
// inline here with only standard library facilities, exactly like the
|
||||
// original. The C ABI exposes the same functionality as
|
||||
// oakcore_stringutils_split_regex() taking a pattern string.
|
||||
std::vector<std::string> output;
|
||||
|
||||
std::sregex_token_iterator iter(s.begin(), s.end(), regex, -1);
|
||||
std::sregex_token_iterator end;
|
||||
for (; iter != end; iter++) {
|
||||
output.push_back(*iter);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a string to int using a bool pointer to determine success rather than an exception
|
||||
* @brief Convert a string to int using a bool pointer to determine
|
||||
* success rather than an exception
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
@@ -86,7 +126,15 @@ public:
|
||||
*
|
||||
* Either the int parsed from the string, or 0 (with *ok set to false) on parser error.
|
||||
*/
|
||||
static int to_int(const std::string &s, int base, bool *ok = nullptr);
|
||||
static int to_int(const std::string &s, int base, bool *ok = nullptr)
|
||||
{
|
||||
int c_ok = 0;
|
||||
const int x = oakcore_stringutils_to_int(s.c_str(), base, &c_ok);
|
||||
if (ok) {
|
||||
*ok = (c_ok != 0);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overloaded function
|
||||
@@ -157,7 +205,28 @@ public:
|
||||
*
|
||||
* A formatted string in std::string form.
|
||||
*/
|
||||
static std::string format(const char *fmt, ...);
|
||||
static std::string format(const char *fmt, ...)
|
||||
{
|
||||
va_list ap1, ap2;
|
||||
va_start(ap1, fmt);
|
||||
|
||||
// Need to duplicate because the va_list is consumed by each C API call
|
||||
va_copy(ap2, ap1);
|
||||
|
||||
const int size = oakcore_stringutils_format_v(nullptr, 0, fmt, ap1);
|
||||
|
||||
// Create string with size, adding 1 for the null terminator
|
||||
std::string r(size_t(size) + 1, '\0');
|
||||
oakcore_stringutils_format_v(r.data(), size + 1, fmt, ap2);
|
||||
|
||||
va_end(ap2);
|
||||
va_end(ap1);
|
||||
|
||||
// Pop null terminator
|
||||
r.pop_back();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// trim from start (in place)
|
||||
static inline void ltrim(std::string &s)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "rational.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "olive/core/oakcore/timecodefunctions.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
@@ -32,11 +35,17 @@ namespace olive::core
|
||||
/**
|
||||
* @brief Functions for converting times/timecodes/timestamps
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the class is all-static
|
||||
* and every call is forwarded across the C boundary (times and timebases
|
||||
* cross it as borrowed Rational handles). The public API is unchanged from
|
||||
* the original implementation.
|
||||
*
|
||||
* Olive uses the following terminology through its code:
|
||||
*
|
||||
* `time` - time in seconds presented in a Rational form
|
||||
* `timebase` - the base time unit of an audio/video stream in seconds
|
||||
* `timestamp` - an integer representation of a time in timebase units (in many cases is used like a frame number)
|
||||
* `timestamp` - an integer representation of a time in timebase units
|
||||
* (in many cases is used like a frame number)
|
||||
* `timecode` a user-friendly string representation of a time according to Timecode::Display
|
||||
*/
|
||||
class Timecode {
|
||||
@@ -52,40 +61,104 @@ public:
|
||||
enum Rounding { k_ceil, k_floor, k_round };
|
||||
|
||||
/**
|
||||
* @brief Convert a timestamp (according to a Rational timebase) to a user-friendly string representation
|
||||
* @brief Convert a timestamp (according to a Rational timebase) to a
|
||||
* user-friendly string representation
|
||||
*/
|
||||
static std::string time_to_timecode(const Rational &time,
|
||||
const Rational &timebase,
|
||||
const Display &display,
|
||||
bool show_plus_if_positive = false);
|
||||
bool show_plus_if_positive = false)
|
||||
{
|
||||
const OakTimecodeDisplay c_display =
|
||||
static_cast<OakTimecodeDisplay>(display);
|
||||
const int c_show_plus = show_plus_if_positive ? 1 : 0;
|
||||
const int size = oakcore_timecode_time_to_timecode(
|
||||
time.handle(), timebase.handle(), c_display, c_show_plus, nullptr,
|
||||
0);
|
||||
std::string s(size_t(size) + 1, '\0');
|
||||
oakcore_timecode_time_to_timecode(time.handle(), timebase.handle(),
|
||||
c_display, c_show_plus, s.data(),
|
||||
size + 1);
|
||||
s.resize(size_t(size));
|
||||
return s;
|
||||
}
|
||||
|
||||
static Rational timecode_to_time(std::string timecode,
|
||||
const Rational &timebase,
|
||||
const Display &display,
|
||||
bool *ok = nullptr);
|
||||
bool *ok = nullptr)
|
||||
{
|
||||
int c_ok = 0;
|
||||
Rational r = Rational::from_handle(oakcore_timecode_timecode_to_time(
|
||||
timecode.c_str(), timebase.handle(),
|
||||
static_cast<OakTimecodeDisplay>(display), &c_ok));
|
||||
if (ok) {
|
||||
*ok = (c_ok != 0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static std::string time_to_string(int64_t ms);
|
||||
static std::string time_to_string(int64_t ms)
|
||||
{
|
||||
const int size = oakcore_timecode_time_to_string(ms, nullptr, 0);
|
||||
std::string s(size_t(size) + 1, '\0');
|
||||
oakcore_timecode_time_to_string(ms, s.data(), size + 1);
|
||||
s.resize(size_t(size));
|
||||
return s;
|
||||
}
|
||||
|
||||
static Rational snap_time_to_timebase(const Rational &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
Rounding floor = k_round)
|
||||
{
|
||||
return Rational::from_handle(oakcore_timecode_snap_time_to_timebase(
|
||||
time.handle(), timebase.handle(),
|
||||
static_cast<OakTimecodeRounding>(floor)));
|
||||
}
|
||||
|
||||
static int64_t time_to_timestamp(const Rational &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
Rounding floor = k_round)
|
||||
{
|
||||
return oakcore_timecode_time_to_timestamp(
|
||||
time.handle(), timebase.handle(),
|
||||
static_cast<OakTimecodeRounding>(floor));
|
||||
}
|
||||
|
||||
static int64_t time_to_timestamp(const double &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
Rounding floor = k_round)
|
||||
{
|
||||
return oakcore_timecode_time_to_timestamp_d(
|
||||
time, timebase.handle(), static_cast<OakTimecodeRounding>(floor));
|
||||
}
|
||||
|
||||
static int64_t rescale_timestamp(const int64_t &ts, const Rational &source,
|
||||
const Rational &dest);
|
||||
const Rational &dest)
|
||||
{
|
||||
return oakcore_timecode_rescale_timestamp(ts, source.handle(),
|
||||
dest.handle());
|
||||
}
|
||||
|
||||
static int64_t rescale_timestamp_ceil(const int64_t &ts,
|
||||
const Rational &source,
|
||||
const Rational &dest);
|
||||
const Rational &dest)
|
||||
{
|
||||
return oakcore_timecode_rescale_timestamp_ceil(ts, source.handle(),
|
||||
dest.handle());
|
||||
}
|
||||
|
||||
static Rational timestamp_to_time(const int64_t ×tamp,
|
||||
const Rational &timebase);
|
||||
const Rational &timebase)
|
||||
{
|
||||
return Rational::from_handle(oakcore_timecode_timestamp_to_time(
|
||||
timestamp, timebase.handle()));
|
||||
}
|
||||
|
||||
static bool timebase_is_drop_frame(const Rational &timebase);
|
||||
static bool timebase_is_drop_frame(const Rational &timebase)
|
||||
{
|
||||
return oakcore_timecode_timebase_is_drop_frame(timebase.handle()) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,67 +22,223 @@
|
||||
#ifndef OAK_LIBOLIVECORE_TIMERANGE_H
|
||||
#define OAK_LIBOLIVECORE_TIMERANGE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "olive/core/oakcore/timerange.h"
|
||||
#include "rational.h"
|
||||
#include "timecodefunctions.h"
|
||||
|
||||
namespace olive::core
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A range of time, normalized so that in() <= out()
|
||||
*
|
||||
* Consumer-side wrapper over the liboakcore C ABI: the object only holds an
|
||||
* opaque OakTimeRange handle and forwards every call across the C boundary.
|
||||
* The public API is unchanged from the original implementation, except that
|
||||
* the getters return Rational by value instead of by const reference.
|
||||
*/
|
||||
class TimeRange {
|
||||
public:
|
||||
TimeRange() = default;
|
||||
TimeRange(const Rational &in, const Rational &out);
|
||||
TimeRange(const TimeRange &r)
|
||||
: TimeRange(r.in(), r.out())
|
||||
TimeRange()
|
||||
: handle_(oakcore_timerange_create())
|
||||
{
|
||||
}
|
||||
|
||||
TimeRange(const Rational &in, const Rational &out)
|
||||
: handle_(oakcore_timerange_create_io(in.handle(), out.handle()))
|
||||
{
|
||||
}
|
||||
|
||||
TimeRange(const TimeRange &r)
|
||||
: handle_(oakcore_timerange_copy(r.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
TimeRange(TimeRange &&r) noexcept
|
||||
: handle_(r.handle_)
|
||||
{
|
||||
r.handle_ = nullptr;
|
||||
}
|
||||
|
||||
~TimeRange()
|
||||
{
|
||||
oakcore_timerange_free(handle_);
|
||||
}
|
||||
|
||||
TimeRange &operator=(const TimeRange &r)
|
||||
{
|
||||
set_range(r.in(), r.out());
|
||||
if (this != &r) {
|
||||
oakcore_timerange_free(handle_);
|
||||
handle_ = oakcore_timerange_copy(r.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Rational &in() const;
|
||||
const Rational &out() const;
|
||||
const Rational &length() const;
|
||||
TimeRange &operator=(TimeRange &&r) noexcept
|
||||
{
|
||||
if (this != &r) {
|
||||
oakcore_timerange_free(handle_);
|
||||
handle_ = r.handle_;
|
||||
r.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_in(const Rational &in);
|
||||
void set_out(const Rational &out);
|
||||
void set_range(const Rational &in, const Rational &out);
|
||||
Rational in() const
|
||||
{
|
||||
return Rational::from_handle(oakcore_timerange_in(handle_));
|
||||
}
|
||||
|
||||
bool operator==(const TimeRange &r) const;
|
||||
bool operator!=(const TimeRange &r) const;
|
||||
Rational out() const
|
||||
{
|
||||
return Rational::from_handle(oakcore_timerange_out(handle_));
|
||||
}
|
||||
|
||||
Rational length() const
|
||||
{
|
||||
return Rational::from_handle(oakcore_timerange_length(handle_));
|
||||
}
|
||||
|
||||
void set_in(const Rational &in)
|
||||
{
|
||||
oakcore_timerange_set_in(handle_, in.handle());
|
||||
}
|
||||
|
||||
void set_out(const Rational &out)
|
||||
{
|
||||
oakcore_timerange_set_out(handle_, out.handle());
|
||||
}
|
||||
|
||||
void set_range(const Rational &in, const Rational &out)
|
||||
{
|
||||
oakcore_timerange_set_range(handle_, in.handle(), out.handle());
|
||||
}
|
||||
|
||||
bool operator==(const TimeRange &r) const
|
||||
{
|
||||
return oakcore_timerange_equal(handle_, r.handle_) != 0;
|
||||
}
|
||||
|
||||
bool operator!=(const TimeRange &r) const
|
||||
{
|
||||
return !(*this == r);
|
||||
}
|
||||
|
||||
bool overlaps_with(const TimeRange &a, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
bool out_inclusive = true) const
|
||||
{
|
||||
return oakcore_timerange_overlaps_with(handle_, a.handle_,
|
||||
in_inclusive ? 1 : 0,
|
||||
out_inclusive ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
bool contains(const TimeRange &a, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
bool contains(const Rational &r) const;
|
||||
bool out_inclusive = true) const
|
||||
{
|
||||
return oakcore_timerange_contains_range(handle_, a.handle_,
|
||||
in_inclusive ? 1 : 0,
|
||||
out_inclusive ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
TimeRange combined(const TimeRange &a) const;
|
||||
static TimeRange combine(const TimeRange &a, const TimeRange &b);
|
||||
TimeRange intersected(const TimeRange &a) const;
|
||||
static TimeRange intersect(const TimeRange &a, const TimeRange &b);
|
||||
bool contains(const Rational &r) const
|
||||
{
|
||||
return oakcore_timerange_contains_time(handle_, r.handle()) != 0;
|
||||
}
|
||||
|
||||
TimeRange operator+(const Rational &rhs) const;
|
||||
TimeRange operator-(const Rational &rhs) const;
|
||||
TimeRange combined(const TimeRange &a) const
|
||||
{
|
||||
return from_handle(oakcore_timerange_combined(handle_, a.handle_));
|
||||
}
|
||||
|
||||
const TimeRange &operator+=(const Rational &rhs);
|
||||
const TimeRange &operator-=(const Rational &rhs);
|
||||
static TimeRange combine(const TimeRange &a, const TimeRange &b)
|
||||
{
|
||||
return from_handle(oakcore_timerange_combine(a.handle_, b.handle_));
|
||||
}
|
||||
|
||||
std::list<TimeRange> split(const int &chunk_size) const;
|
||||
TimeRange intersected(const TimeRange &a) const
|
||||
{
|
||||
return from_handle(oakcore_timerange_intersected(handle_, a.handle_));
|
||||
}
|
||||
|
||||
static TimeRange intersect(const TimeRange &a, const TimeRange &b)
|
||||
{
|
||||
return from_handle(oakcore_timerange_intersect(a.handle_, b.handle_));
|
||||
}
|
||||
|
||||
TimeRange operator+(const Rational &rhs) const
|
||||
{
|
||||
return from_handle(oakcore_timerange_add(handle_, rhs.handle()));
|
||||
}
|
||||
|
||||
TimeRange operator-(const Rational &rhs) const
|
||||
{
|
||||
return from_handle(oakcore_timerange_sub(handle_, rhs.handle()));
|
||||
}
|
||||
|
||||
const TimeRange &operator+=(const Rational &rhs)
|
||||
{
|
||||
oakcore_timerange_add_assign(handle_, rhs.handle());
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TimeRange &operator-=(const Rational &rhs)
|
||||
{
|
||||
oakcore_timerange_sub_assign(handle_, rhs.handle());
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::list<TimeRange> split(const int &chunk_size) const
|
||||
{
|
||||
const int count = oakcore_timerange_split_count(handle_, chunk_size);
|
||||
std::vector<OakTimeRange *> handles{size_t(count)};
|
||||
oakcore_timerange_split(handle_, chunk_size, handles.data(), count);
|
||||
|
||||
std::list<TimeRange> ranges;
|
||||
for (OakTimeRange *h : handles) {
|
||||
ranges.push_back(from_handle(h));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The wrapped C handle, for cross-type wrappers and direct C API use
|
||||
*/
|
||||
OakTimeRange *handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps an owned C handle (takes ownership)
|
||||
*/
|
||||
static TimeRange from_handle(OakTimeRange *handle)
|
||||
{
|
||||
return TimeRange(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
void normalize();
|
||||
explicit TimeRange(OakTimeRange *handle)
|
||||
: handle_(handle)
|
||||
{
|
||||
}
|
||||
|
||||
Rational in_;
|
||||
Rational out_;
|
||||
Rational length_;
|
||||
OakTimeRange *handle_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A list of TimeRanges, kept merged and non-overlapping on insert()
|
||||
*
|
||||
* Consumer-side inline reimplementation over the wrapped TimeRange: every
|
||||
* operation is expressed through the public API of olive::core::TimeRange
|
||||
* and olive::core::Rational, so a container like this needs no C ABI surface
|
||||
* of its own. The public API is unchanged from the original implementation.
|
||||
*/
|
||||
class TimeRangeList {
|
||||
public:
|
||||
TimeRangeList() = default;
|
||||
@@ -92,11 +248,46 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void insert(const TimeRangeList &list_to_add);
|
||||
void insert(TimeRange range_to_add);
|
||||
void insert(const TimeRangeList &list_to_add)
|
||||
{
|
||||
for (auto it = list_to_add.cbegin(); it != list_to_add.cend(); it++) {
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void remove(const TimeRange &remove);
|
||||
void remove(const TimeRangeList &list);
|
||||
void insert(TimeRange range_to_add)
|
||||
{
|
||||
// See if list contains this range
|
||||
if (contains(range_to_add)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Does not contain range, so we'll almost certainly be adding it in some way
|
||||
for (auto it = array_.begin(); it != array_.end();) {
|
||||
const TimeRange &compare = *it;
|
||||
|
||||
if (compare.overlaps_with(range_to_add)) {
|
||||
range_to_add = TimeRange::combine(range_to_add, compare);
|
||||
it = array_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
array_.push_back(range_to_add);
|
||||
}
|
||||
|
||||
void remove(const TimeRange &remove)
|
||||
{
|
||||
util_remove(&array_, remove);
|
||||
}
|
||||
|
||||
void remove(const TimeRangeList &list)
|
||||
{
|
||||
for (const TimeRange &r : list) {
|
||||
remove(r);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void util_remove(std::vector<T> *list, const TimeRange &remove)
|
||||
@@ -138,7 +329,16 @@ public:
|
||||
}
|
||||
|
||||
bool contains(const TimeRange &range, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
bool out_inclusive = true) const
|
||||
{
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (array_.at(i).contains(range, in_inclusive, out_inclusive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool contains(const Rational &r) const
|
||||
{
|
||||
@@ -178,13 +378,62 @@ public:
|
||||
return array_.size();
|
||||
}
|
||||
|
||||
void shift(const Rational &diff);
|
||||
void shift(const Rational &diff)
|
||||
{
|
||||
for (int i = 0; i < array_.size(); i++) {
|
||||
array_[i] += diff;
|
||||
}
|
||||
}
|
||||
|
||||
void trim_in(const Rational &diff);
|
||||
void trim_in(const Rational &diff)
|
||||
{
|
||||
// Re-do list since we want to handle overlaps
|
||||
TimeRangeList temp = *this;
|
||||
|
||||
void trim_out(const Rational &diff);
|
||||
clear();
|
||||
|
||||
TimeRangeList intersects(const TimeRange &range) const;
|
||||
for (auto it = temp.array_.begin(); it != temp.array_.end(); it++) {
|
||||
TimeRange &r = *it;
|
||||
r.set_in(r.in() + diff);
|
||||
insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
void trim_out(const Rational &diff)
|
||||
{
|
||||
// Re-do list since we want to handle overlaps
|
||||
TimeRangeList temp = *this;
|
||||
|
||||
clear();
|
||||
|
||||
for (auto it = temp.array_.begin(); it != temp.array_.end(); it++) {
|
||||
TimeRange &r = *it;
|
||||
r.set_out(r.out() + diff);
|
||||
insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
TimeRangeList intersects(const TimeRange &range) const
|
||||
{
|
||||
TimeRangeList intersect_list;
|
||||
|
||||
for (int i = 0; i < size(); i++) {
|
||||
const TimeRange &compare = array_.at(i);
|
||||
|
||||
if (compare.out() <= range.in() || compare.in() >= range.out()) {
|
||||
// No intersect
|
||||
continue;
|
||||
} else {
|
||||
// Crop the time range to the range and add it to the list
|
||||
TimeRange cropped(std::max(range.in(), compare.in()),
|
||||
std::min(range.out(), compare.out()));
|
||||
|
||||
intersect_list.insert(cropped);
|
||||
}
|
||||
}
|
||||
|
||||
return intersect_list;
|
||||
}
|
||||
|
||||
using const_iterator = std::vector<TimeRange>::const_iterator;
|
||||
|
||||
@@ -237,17 +486,70 @@ private:
|
||||
std::vector<TimeRange> array_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Steps through a TimeRangeList frame by frame at a given timebase
|
||||
*
|
||||
* Consumer-side inline reimplementation: frame snapping and timestamp
|
||||
* conversion go through the wrapped olive::core::Timecode functions, the
|
||||
* range storage through the wrapped olive::core::TimeRangeList above. The
|
||||
* public API is unchanged from the original implementation.
|
||||
*/
|
||||
class TimeRangeListFrameIterator {
|
||||
public:
|
||||
TimeRangeListFrameIterator();
|
||||
TimeRangeListFrameIterator()
|
||||
: TimeRangeListFrameIterator(TimeRangeList(), Rational::na_n)
|
||||
{
|
||||
}
|
||||
|
||||
TimeRangeListFrameIterator(const TimeRangeList &list,
|
||||
const Rational &timebase);
|
||||
const Rational &timebase)
|
||||
: list_(list)
|
||||
, timebase_(timebase)
|
||||
, range_index_(-1)
|
||||
, size_(-1)
|
||||
, frame_index_(0)
|
||||
, custom_range_(false)
|
||||
{
|
||||
if (!list_.isEmpty() && timebase_.isNull()) {
|
||||
std::cerr
|
||||
<< "TimeRangeListFrameIterator created with null timebase but "
|
||||
"non-empty list, this will likely lead to infinite loops"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
Rational snap(const Rational &r) const;
|
||||
update_index_if_necessary();
|
||||
}
|
||||
|
||||
bool get_next(Rational *out);
|
||||
Rational snap(const Rational &r) const
|
||||
{
|
||||
return Timecode::snap_time_to_timebase(r, timebase_, Timecode::k_floor);
|
||||
}
|
||||
|
||||
bool has_next() const;
|
||||
bool get_next(Rational *out)
|
||||
{
|
||||
if (!has_next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Output current value
|
||||
*out = current_;
|
||||
|
||||
// Determine next value by adding timebase
|
||||
current_ += timebase_;
|
||||
|
||||
// If this time is outside the current range, jump to the next one
|
||||
update_index_if_necessary();
|
||||
|
||||
// Increment frame index
|
||||
frame_index_++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_next() const
|
||||
{
|
||||
return range_index_ < list_.size();
|
||||
}
|
||||
|
||||
std::vector<Rational> to_vector() const
|
||||
{
|
||||
@@ -260,7 +562,31 @@ public:
|
||||
return times;
|
||||
}
|
||||
|
||||
int size();
|
||||
int size()
|
||||
{
|
||||
if (size_ == -1) {
|
||||
// Size isn't calculated automatically for optimization, so we'll calculate it now
|
||||
size_ = 0;
|
||||
|
||||
for (const TimeRange &range : list_) {
|
||||
Rational start = snap(range.in());
|
||||
Rational end = Timecode::snap_time_to_timebase(
|
||||
range.out(), timebase_, Timecode::k_floor);
|
||||
|
||||
if (end == range.out()) {
|
||||
end -= timebase_;
|
||||
}
|
||||
|
||||
int64_t start_ts =
|
||||
Timecode::time_to_timestamp(start, timebase_);
|
||||
int64_t end_ts = Timecode::time_to_timestamp(end, timebase_);
|
||||
|
||||
size_ += 1 + (end_ts - start_ts);
|
||||
}
|
||||
}
|
||||
|
||||
return size_;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
@@ -293,7 +619,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void update_index_if_necessary();
|
||||
void update_index_if_necessary()
|
||||
{
|
||||
while (range_index_ < list_.size() &&
|
||||
(range_index_ == -1 ||
|
||||
current_ >= list_.at(range_index_).out())) {
|
||||
range_index_++;
|
||||
|
||||
if (range_index_ < list_.size()) {
|
||||
current_ = snap(list_.at(range_index_).in());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeRangeList list_;
|
||||
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/audioparams.h"
|
||||
|
||||
#include "render/audioparams.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::AudioParams *impl(OakAudioParams *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::AudioParams *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::AudioParams *impl(const OakAudioParams *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::AudioParams *>(h);
|
||||
}
|
||||
|
||||
OakAudioParams *wrap(olive::core::internal::AudioParams *p)
|
||||
{
|
||||
return reinterpret_cast<OakAudioParams *>(p);
|
||||
}
|
||||
|
||||
const olive::core::internal::Rational *impl(const OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
OakRational *wrap(olive::core::internal::Rational *r)
|
||||
{
|
||||
return reinterpret_cast<OakRational *>(r);
|
||||
}
|
||||
|
||||
olive::core::SampleFormat to_format(int f)
|
||||
{
|
||||
return olive::core::SampleFormat(
|
||||
static_cast<olive::core::SampleFormat::Format>(f));
|
||||
}
|
||||
|
||||
// The supported channel layouts / sample rates as constant data. These
|
||||
// mirror AudioParams::k_supported_channel_layouts /
|
||||
// k_supported_sample_rates (src/render/audioparams.cpp) and must be kept in
|
||||
// sync with them. The C API serves these instead of reading the
|
||||
// implementation's std::vector statics directly so that the functions are
|
||||
// safe to call during a consumer's static initialization even when
|
||||
// liboakcore is linked statically (cross-TU static init order is
|
||||
// unspecified; the implementation's vectors may not be constructed yet).
|
||||
constexpr uint64_t k_supported_channel_layouts[] = {
|
||||
olive::core::k_channel_layout_mono,
|
||||
olive::core::k_channel_layout_stereo,
|
||||
olive::core::k_channel_layout2_1,
|
||||
olive::core::k_channel_layout5_point1,
|
||||
olive::core::k_channel_layout7_point1,
|
||||
};
|
||||
|
||||
constexpr int k_supported_sample_rates[] = {
|
||||
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000,
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
T at_or_zero(const T (&arr)[N], int index)
|
||||
{
|
||||
if (index < 0 || size_t(index) >= N) {
|
||||
return 0;
|
||||
}
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakAudioParams *oakcore_audioparams_create(int sample_rate,
|
||||
uint64_t channel_layout, int format)
|
||||
{
|
||||
return wrap(new olive::core::internal::AudioParams(
|
||||
sample_rate, channel_layout, to_format(format)));
|
||||
}
|
||||
|
||||
OakAudioParams *oakcore_audioparams_create_invalid(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::AudioParams());
|
||||
}
|
||||
|
||||
OakAudioParams *oakcore_audioparams_copy(const OakAudioParams *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::AudioParams(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_audioparams_free(OakAudioParams *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
int oakcore_audioparams_sample_rate(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->sample_rate();
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_sample_rate(OakAudioParams *self, int sample_rate)
|
||||
{
|
||||
impl(self)->set_sample_rate(sample_rate);
|
||||
}
|
||||
|
||||
uint64_t oakcore_audioparams_channel_layout(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->channel_layout();
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_channel_layout(OakAudioParams *self, uint64_t mask)
|
||||
{
|
||||
impl(self)->set_channel_layout(mask);
|
||||
}
|
||||
|
||||
OakRational *oakcore_audioparams_time_base(const OakAudioParams *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(impl(self)->time_base()));
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_time_base(OakAudioParams *self,
|
||||
const OakRational *timebase)
|
||||
{
|
||||
impl(self)->set_time_base(*impl(timebase));
|
||||
}
|
||||
|
||||
OakRational *oakcore_audioparams_sample_rate_as_time_base(
|
||||
const OakAudioParams *self)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::Rational(impl(self)->sample_rate_as_time_base()));
|
||||
}
|
||||
|
||||
int oakcore_audioparams_format(const OakAudioParams *self)
|
||||
{
|
||||
return int(impl(self)->format());
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_format(OakAudioParams *self, int format)
|
||||
{
|
||||
impl(self)->set_format(to_format(format));
|
||||
}
|
||||
|
||||
int oakcore_audioparams_enabled(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->enabled() ? 1 : 0;
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_enabled(OakAudioParams *self, int enabled)
|
||||
{
|
||||
impl(self)->set_enabled(enabled != 0);
|
||||
}
|
||||
|
||||
int oakcore_audioparams_stream_index(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->stream_index();
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_stream_index(OakAudioParams *self, int stream_index)
|
||||
{
|
||||
impl(self)->set_stream_index(stream_index);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_duration(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->duration();
|
||||
}
|
||||
|
||||
void oakcore_audioparams_set_duration(OakAudioParams *self, int64_t duration)
|
||||
{
|
||||
impl(self)->set_duration(duration);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_bytes(const OakAudioParams *self, double time)
|
||||
{
|
||||
return impl(self)->time_to_bytes(time);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_bytes_rational(const OakAudioParams *self,
|
||||
const OakRational *time)
|
||||
{
|
||||
return impl(self)->time_to_bytes(*impl(time));
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_bytes_per_channel(const OakAudioParams *self,
|
||||
double time)
|
||||
{
|
||||
return impl(self)->time_to_bytes_per_channel(time);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_bytes_per_channel_rational(
|
||||
const OakAudioParams *self, const OakRational *time)
|
||||
{
|
||||
return impl(self)->time_to_bytes_per_channel(*impl(time));
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_samples(const OakAudioParams *self,
|
||||
double time)
|
||||
{
|
||||
return impl(self)->time_to_samples(time);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_time_to_samples_rational(const OakAudioParams *self,
|
||||
const OakRational *time)
|
||||
{
|
||||
return impl(self)->time_to_samples(*impl(time));
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_samples_to_bytes(const OakAudioParams *self,
|
||||
int64_t samples)
|
||||
{
|
||||
return impl(self)->samples_to_bytes(samples);
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_samples_to_bytes_per_channel(
|
||||
const OakAudioParams *self, int64_t samples)
|
||||
{
|
||||
return impl(self)->samples_to_bytes_per_channel(samples);
|
||||
}
|
||||
|
||||
OakRational *oakcore_audioparams_samples_to_time(const OakAudioParams *self,
|
||||
int64_t samples)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::Rational(impl(self)->samples_to_time(samples)));
|
||||
}
|
||||
|
||||
int64_t oakcore_audioparams_bytes_to_samples(const OakAudioParams *self,
|
||||
int64_t bytes)
|
||||
{
|
||||
return impl(self)->bytes_to_samples(bytes);
|
||||
}
|
||||
|
||||
OakRational *oakcore_audioparams_bytes_to_time(const OakAudioParams *self,
|
||||
int64_t bytes)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::Rational(impl(self)->bytes_to_time(bytes)));
|
||||
}
|
||||
|
||||
OakRational *oakcore_audioparams_bytes_per_channel_to_time(
|
||||
const OakAudioParams *self, int64_t bytes)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(
|
||||
impl(self)->bytes_per_channel_to_time(bytes)));
|
||||
}
|
||||
|
||||
int oakcore_audioparams_channel_count(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->channel_count();
|
||||
}
|
||||
|
||||
int oakcore_audioparams_bytes_per_sample_per_channel(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->bytes_per_sample_per_channel();
|
||||
}
|
||||
|
||||
int oakcore_audioparams_bits_per_sample(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->bits_per_sample();
|
||||
}
|
||||
|
||||
int oakcore_audioparams_is_valid(const OakAudioParams *self)
|
||||
{
|
||||
return impl(self)->is_valid() ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakcore_audioparams_equals(const OakAudioParams *self,
|
||||
const OakAudioParams *other)
|
||||
{
|
||||
return (*impl(self) == *impl(other)) ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakcore_audioparams_supported_channel_layout_count(void)
|
||||
{
|
||||
return int(sizeof(k_supported_channel_layouts) /
|
||||
sizeof(k_supported_channel_layouts[0]));
|
||||
}
|
||||
|
||||
uint64_t oakcore_audioparams_supported_channel_layout_at(int index)
|
||||
{
|
||||
return at_or_zero(k_supported_channel_layouts, index);
|
||||
}
|
||||
|
||||
int oakcore_audioparams_supported_sample_rate_count(void)
|
||||
{
|
||||
return int(sizeof(k_supported_sample_rates) /
|
||||
sizeof(k_supported_sample_rates[0]));
|
||||
}
|
||||
|
||||
int oakcore_audioparams_supported_sample_rate_at(int index)
|
||||
{
|
||||
return at_or_zero(k_supported_sample_rates, index);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,157 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/bezier.h"
|
||||
|
||||
#include "util/bezier.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::Bezier *impl(OakBezier *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::Bezier *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::Bezier *impl(const OakBezier *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Bezier *>(h);
|
||||
}
|
||||
|
||||
OakBezier *wrap(olive::core::internal::Bezier *b)
|
||||
{
|
||||
return reinterpret_cast<OakBezier *>(b);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakBezier *oakcore_bezier_create(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::Bezier());
|
||||
}
|
||||
|
||||
OakBezier *oakcore_bezier_create_xy(double x, double y)
|
||||
{
|
||||
return wrap(new olive::core::internal::Bezier(x, y));
|
||||
}
|
||||
|
||||
OakBezier *oakcore_bezier_create_full(double x, double y, double cp1_x,
|
||||
double cp1_y, double cp2_x, double cp2_y)
|
||||
{
|
||||
return wrap(new olive::core::internal::Bezier(x, y, cp1_x, cp1_y, cp2_x,
|
||||
cp2_y));
|
||||
}
|
||||
|
||||
OakBezier *oakcore_bezier_copy(const OakBezier *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::Bezier(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_bezier_free(OakBezier *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
double oakcore_bezier_x(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->x();
|
||||
}
|
||||
|
||||
double oakcore_bezier_y(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->y();
|
||||
}
|
||||
|
||||
double oakcore_bezier_cp1_x(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->cp1_x();
|
||||
}
|
||||
|
||||
double oakcore_bezier_cp1_y(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->cp1_y();
|
||||
}
|
||||
|
||||
double oakcore_bezier_cp2_x(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->cp2_x();
|
||||
}
|
||||
|
||||
double oakcore_bezier_cp2_y(const OakBezier *self)
|
||||
{
|
||||
return impl(self)->cp2_y();
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_x(OakBezier *self, double x)
|
||||
{
|
||||
impl(self)->set_x(x);
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_y(OakBezier *self, double y)
|
||||
{
|
||||
impl(self)->set_y(y);
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_cp1_x(OakBezier *self, double cp1_x)
|
||||
{
|
||||
impl(self)->set_cp1_x(cp1_x);
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_cp1_y(OakBezier *self, double cp1_y)
|
||||
{
|
||||
impl(self)->set_cp1_y(cp1_y);
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_cp2_x(OakBezier *self, double cp2_x)
|
||||
{
|
||||
impl(self)->set_cp2_x(cp2_x);
|
||||
}
|
||||
|
||||
void oakcore_bezier_set_cp2_y(OakBezier *self, double cp2_y)
|
||||
{
|
||||
impl(self)->set_cp2_y(cp2_y);
|
||||
}
|
||||
|
||||
double oakcore_bezier_quadratic_xto_t(double x, double a, double b, double c)
|
||||
{
|
||||
return olive::core::internal::Bezier::quadratic_xto_t(x, a, b, c);
|
||||
}
|
||||
|
||||
double oakcore_bezier_quadratic_tto_y(double a, double b, double c, double t)
|
||||
{
|
||||
return olive::core::internal::Bezier::quadratic_tto_y(a, b, c, t);
|
||||
}
|
||||
|
||||
double oakcore_bezier_cubic_xto_t(double x, double a, double b, double c,
|
||||
double d)
|
||||
{
|
||||
return olive::core::internal::Bezier::cubic_xto_t(x, a, b, c, d);
|
||||
}
|
||||
|
||||
double oakcore_bezier_cubic_tto_y(double a, double b, double c, double d,
|
||||
double t)
|
||||
{
|
||||
return olive::core::internal::Bezier::cubic_tto_y(a, b, c, d, t);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,220 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/color.h"
|
||||
|
||||
#include "util/color.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::Color *impl(OakColor *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::Color *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::Color *impl(const OakColor *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Color *>(h);
|
||||
}
|
||||
|
||||
OakColor *wrap(olive::core::internal::Color *c)
|
||||
{
|
||||
return reinterpret_cast<OakColor *>(c);
|
||||
}
|
||||
|
||||
olive::core::PixelFormat to_format(int f)
|
||||
{
|
||||
return olive::core::PixelFormat(
|
||||
static_cast<olive::core::PixelFormat::Format>(f));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakColor *oakcore_color_create(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::Color());
|
||||
}
|
||||
|
||||
OakColor *oakcore_color_create_rgba(float r, float g, float b, float a)
|
||||
{
|
||||
return wrap(new olive::core::internal::Color(r, g, b, a));
|
||||
}
|
||||
|
||||
OakColor *oakcore_color_copy(const OakColor *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::Color(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_color_free(OakColor *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
OakColor *oakcore_color_from_hsv(float h, float s, float v)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::Color(olive::core::internal::Color::from_hsv(h, s, v)));
|
||||
}
|
||||
|
||||
OakColor *oakcore_color_from_data(const char *data, int format, int nb_channels)
|
||||
{
|
||||
return wrap(new olive::core::internal::Color(
|
||||
olive::core::internal::Color::from_data(data, to_format(format),
|
||||
static_cast<unsigned int>(nb_channels))));
|
||||
}
|
||||
|
||||
float oakcore_color_red(const OakColor *self)
|
||||
{
|
||||
return impl(self)->red();
|
||||
}
|
||||
|
||||
float oakcore_color_green(const OakColor *self)
|
||||
{
|
||||
return impl(self)->green();
|
||||
}
|
||||
|
||||
float oakcore_color_blue(const OakColor *self)
|
||||
{
|
||||
return impl(self)->blue();
|
||||
}
|
||||
|
||||
float oakcore_color_alpha(const OakColor *self)
|
||||
{
|
||||
return impl(self)->alpha();
|
||||
}
|
||||
|
||||
void oakcore_color_set_red(OakColor *self, float red)
|
||||
{
|
||||
impl(self)->set_red(red);
|
||||
}
|
||||
|
||||
void oakcore_color_set_green(OakColor *self, float green)
|
||||
{
|
||||
impl(self)->set_green(green);
|
||||
}
|
||||
|
||||
void oakcore_color_set_blue(OakColor *self, float blue)
|
||||
{
|
||||
impl(self)->set_blue(blue);
|
||||
}
|
||||
|
||||
void oakcore_color_set_alpha(OakColor *self, float alpha)
|
||||
{
|
||||
impl(self)->set_alpha(alpha);
|
||||
}
|
||||
|
||||
void oakcore_color_to_hsv(const OakColor *self, float *hue, float *sat, float *val)
|
||||
{
|
||||
impl(self)->to_hsv(hue, sat, val);
|
||||
}
|
||||
|
||||
float oakcore_color_hsv_hue(const OakColor *self)
|
||||
{
|
||||
return impl(self)->hsv_hue();
|
||||
}
|
||||
|
||||
float oakcore_color_hsv_saturation(const OakColor *self)
|
||||
{
|
||||
return impl(self)->hsv_saturation();
|
||||
}
|
||||
|
||||
float oakcore_color_value(const OakColor *self)
|
||||
{
|
||||
return impl(self)->value();
|
||||
}
|
||||
|
||||
void oakcore_color_to_hsl(const OakColor *self, float *hue, float *sat,
|
||||
float *lightness)
|
||||
{
|
||||
impl(self)->to_hsl(hue, sat, lightness);
|
||||
}
|
||||
|
||||
float oakcore_color_hsl_hue(const OakColor *self)
|
||||
{
|
||||
return impl(self)->hsl_hue();
|
||||
}
|
||||
|
||||
float oakcore_color_hsl_saturation(const OakColor *self)
|
||||
{
|
||||
return impl(self)->hsl_saturation();
|
||||
}
|
||||
|
||||
float oakcore_color_lightness(const OakColor *self)
|
||||
{
|
||||
return impl(self)->lightness();
|
||||
}
|
||||
|
||||
float *oakcore_color_data(OakColor *self)
|
||||
{
|
||||
return impl(self)->data();
|
||||
}
|
||||
|
||||
const float *oakcore_color_const_data(const OakColor *self)
|
||||
{
|
||||
return impl(self)->data();
|
||||
}
|
||||
|
||||
void oakcore_color_to_data(const OakColor *self, char *out, int format,
|
||||
int nb_channels)
|
||||
{
|
||||
impl(self)->to_data(out, to_format(format),
|
||||
static_cast<unsigned int>(nb_channels));
|
||||
}
|
||||
|
||||
float oakcore_color_get_rough_luminance(const OakColor *self)
|
||||
{
|
||||
return impl(self)->get_rough_luminance();
|
||||
}
|
||||
|
||||
void oakcore_color_add_assign(OakColor *self, const OakColor *other)
|
||||
{
|
||||
*impl(self) += *impl(other);
|
||||
}
|
||||
|
||||
void oakcore_color_sub_assign(OakColor *self, const OakColor *other)
|
||||
{
|
||||
*impl(self) -= *impl(other);
|
||||
}
|
||||
|
||||
void oakcore_color_add_scalar_assign(OakColor *self, float value)
|
||||
{
|
||||
*impl(self) += value;
|
||||
}
|
||||
|
||||
void oakcore_color_sub_scalar_assign(OakColor *self, float value)
|
||||
{
|
||||
*impl(self) -= value;
|
||||
}
|
||||
|
||||
void oakcore_color_mul_scalar_assign(OakColor *self, float value)
|
||||
{
|
||||
*impl(self) *= value;
|
||||
}
|
||||
|
||||
void oakcore_color_div_scalar_assign(OakColor *self, float value)
|
||||
{
|
||||
*impl(self) /= value;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,53 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/fractionutils.h"
|
||||
|
||||
#include "util/fractionutils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::FractionRounding impl(OakFractionRounding rnd)
|
||||
{
|
||||
return static_cast<olive::core::internal::FractionRounding>(rnd);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void oakcore_fractionutils_reduce_fraction(int64_t *num, int64_t *den, int64_t max)
|
||||
{
|
||||
olive::core::internal::reduce_fraction(*num, *den, max);
|
||||
}
|
||||
|
||||
int oakcore_fractionutils_compare_fractions(int an, int ad, int bn, int bd)
|
||||
{
|
||||
return olive::core::internal::compare_fractions(an, ad, bn, bd);
|
||||
}
|
||||
|
||||
int64_t oakcore_fractionutils_rescale_rnd(int64_t a, int64_t b, int64_t c, OakFractionRounding rnd)
|
||||
{
|
||||
return olive::core::internal::rescale_rnd(a, b, c, impl(rnd));
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,172 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/rational.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util/rational.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::Rational *impl(OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::Rational *impl(const OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
OakRational *wrap(olive::core::internal::Rational *r)
|
||||
{
|
||||
return reinterpret_cast<OakRational *>(r);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakRational *oakcore_rational_create(int numerator)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(numerator));
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_create_nd(int numerator, int denominator)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(numerator, denominator));
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_create_nan(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(olive::core::internal::Rational::na_n));
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_copy(const OakRational *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_rational_free(OakRational *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
int oakcore_rational_numerator(const OakRational *self)
|
||||
{
|
||||
return impl(self)->numerator();
|
||||
}
|
||||
|
||||
int oakcore_rational_denominator(const OakRational *self)
|
||||
{
|
||||
return impl(self)->denominator();
|
||||
}
|
||||
|
||||
double oakcore_rational_to_double(const OakRational *self)
|
||||
{
|
||||
return impl(self)->to_double();
|
||||
}
|
||||
|
||||
int oakcore_rational_to_string(const OakRational *self, char *buf, int buf_size)
|
||||
{
|
||||
const std::string s = impl(self)->to_string();
|
||||
if (buf && buf_size > 0) {
|
||||
snprintf(buf, size_t(buf_size), "%s", s.c_str());
|
||||
}
|
||||
return int(s.size());
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_from_double(double value, int *ok)
|
||||
{
|
||||
bool b = false;
|
||||
olive::core::internal::Rational *r =
|
||||
new olive::core::internal::Rational(olive::core::internal::Rational::from_double(value, &b));
|
||||
if (ok) {
|
||||
*ok = b ? 1 : 0;
|
||||
}
|
||||
return wrap(r);
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_from_string(const char *str, int *ok)
|
||||
{
|
||||
bool b = false;
|
||||
olive::core::internal::Rational *r = new olive::core::internal::Rational(
|
||||
olive::core::internal::Rational::from_string(str ? str : "", &b));
|
||||
if (ok) {
|
||||
*ok = b ? 1 : 0;
|
||||
}
|
||||
return wrap(r);
|
||||
}
|
||||
|
||||
int oakcore_rational_is_null(const OakRational *self)
|
||||
{
|
||||
return impl(self)->isNull() ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakcore_rational_is_nan(const OakRational *self)
|
||||
{
|
||||
return impl(self)->isNaN() ? 1 : 0;
|
||||
}
|
||||
|
||||
OakRational *oakcore_rational_flipped(const OakRational *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(impl(self)->flipped()));
|
||||
}
|
||||
|
||||
void oakcore_rational_flip(OakRational *self)
|
||||
{
|
||||
impl(self)->flip();
|
||||
}
|
||||
|
||||
void oakcore_rational_add_assign(OakRational *self, const OakRational *other)
|
||||
{
|
||||
*impl(self) += *impl(other);
|
||||
}
|
||||
|
||||
void oakcore_rational_sub_assign(OakRational *self, const OakRational *other)
|
||||
{
|
||||
*impl(self) -= *impl(other);
|
||||
}
|
||||
|
||||
void oakcore_rational_mul_assign(OakRational *self, const OakRational *other)
|
||||
{
|
||||
*impl(self) *= *impl(other);
|
||||
}
|
||||
|
||||
void oakcore_rational_div_assign(OakRational *self, const OakRational *other)
|
||||
{
|
||||
*impl(self) /= *impl(other);
|
||||
}
|
||||
|
||||
int oakcore_rational_compare(const OakRational *self, const OakRational *other)
|
||||
{
|
||||
if (*impl(self) < *impl(other)) {
|
||||
return -1;
|
||||
}
|
||||
if (*impl(other) < *impl(self)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,272 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/samplebuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "render/audioparams.h"
|
||||
#include "render/samplebuffer.h"
|
||||
#include "util/rational.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::SampleBuffer *impl(OakSampleBuffer *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::SampleBuffer *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::SampleBuffer *impl(const OakSampleBuffer *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::SampleBuffer *>(h);
|
||||
}
|
||||
|
||||
OakSampleBuffer *wrap(olive::core::internal::SampleBuffer *b)
|
||||
{
|
||||
return reinterpret_cast<OakSampleBuffer *>(b);
|
||||
}
|
||||
|
||||
const olive::core::internal::AudioParams *apimpl(const OakAudioParams *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::AudioParams *>(h);
|
||||
}
|
||||
|
||||
OakAudioParams *apwrap(olive::core::internal::AudioParams *p)
|
||||
{
|
||||
return reinterpret_cast<OakAudioParams *>(p);
|
||||
}
|
||||
|
||||
const olive::core::internal::Rational *rimpl(const OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakSampleBuffer *oakcore_samplebuffer_create(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::SampleBuffer());
|
||||
}
|
||||
|
||||
OakSampleBuffer *oakcore_samplebuffer_create_length(
|
||||
const OakAudioParams *params, const OakRational *length)
|
||||
{
|
||||
return wrap(new olive::core::internal::SampleBuffer(*apimpl(params),
|
||||
*rimpl(length)));
|
||||
}
|
||||
|
||||
OakSampleBuffer *oakcore_samplebuffer_create_samples(
|
||||
const OakAudioParams *params, size_t samples_per_channel)
|
||||
{
|
||||
return wrap(new olive::core::internal::SampleBuffer(*apimpl(params),
|
||||
samples_per_channel));
|
||||
}
|
||||
|
||||
OakSampleBuffer *oakcore_samplebuffer_copy(const OakSampleBuffer *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::SampleBuffer(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_free(OakSampleBuffer *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
OakSampleBuffer *oakcore_samplebuffer_rip_channel(const OakSampleBuffer *self,
|
||||
int channel)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::SampleBuffer(impl(self)->rip_channel(channel)));
|
||||
}
|
||||
|
||||
int oakcore_samplebuffer_rip_channel_vector(const OakSampleBuffer *self,
|
||||
int channel, float *out,
|
||||
int out_size)
|
||||
{
|
||||
const std::vector<float> v = impl(self)->rip_channel_vector(channel);
|
||||
if (out && out_size > 0) {
|
||||
const size_t n = std::min(v.size(), size_t(out_size));
|
||||
std::copy(v.begin(), v.begin() + n, out);
|
||||
}
|
||||
return int(v.size());
|
||||
}
|
||||
|
||||
OakAudioParams *oakcore_samplebuffer_audio_params(const OakSampleBuffer *self)
|
||||
{
|
||||
return apwrap(
|
||||
new olive::core::internal::AudioParams(impl(self)->audio_params()));
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_set_audio_params(OakSampleBuffer *self,
|
||||
const OakAudioParams *params)
|
||||
{
|
||||
impl(self)->set_audio_params(*apimpl(params));
|
||||
}
|
||||
|
||||
size_t oakcore_samplebuffer_sample_count(const OakSampleBuffer *self)
|
||||
{
|
||||
return impl(self)->sample_count();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_set_sample_count(OakSampleBuffer *self,
|
||||
size_t sample_count)
|
||||
{
|
||||
impl(self)->set_sample_count(sample_count);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_set_sample_count_length(OakSampleBuffer *self,
|
||||
const OakRational *length)
|
||||
{
|
||||
impl(self)->set_sample_count(*rimpl(length));
|
||||
}
|
||||
|
||||
float *oakcore_samplebuffer_data(OakSampleBuffer *self, int channel)
|
||||
{
|
||||
if (!impl(self)->is_allocated() || channel < 0 ||
|
||||
channel >= impl(self)->channel_count()) {
|
||||
return nullptr;
|
||||
}
|
||||
return impl(self)->data(channel);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_to_raw_ptrs(OakSampleBuffer *self, float **out)
|
||||
{
|
||||
if (!out) {
|
||||
return;
|
||||
}
|
||||
const std::vector<float *> ptrs = impl(self)->to_raw_ptrs();
|
||||
std::copy(ptrs.begin(), ptrs.end(), out);
|
||||
}
|
||||
|
||||
int oakcore_samplebuffer_channel_count(const OakSampleBuffer *self)
|
||||
{
|
||||
return impl(self)->channel_count();
|
||||
}
|
||||
|
||||
int oakcore_samplebuffer_is_allocated(const OakSampleBuffer *self)
|
||||
{
|
||||
return impl(self)->is_allocated() ? 1 : 0;
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_allocate(OakSampleBuffer *self)
|
||||
{
|
||||
impl(self)->allocate();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_destroy(OakSampleBuffer *self)
|
||||
{
|
||||
impl(self)->destroy();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_reverse(OakSampleBuffer *self)
|
||||
{
|
||||
impl(self)->reverse();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_speed(OakSampleBuffer *self, double speed)
|
||||
{
|
||||
impl(self)->speed(speed);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume(OakSampleBuffer *self, float f)
|
||||
{
|
||||
impl(self)->transform_volume(f);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume_for_channel(OakSampleBuffer *self,
|
||||
int channel,
|
||||
float volume)
|
||||
{
|
||||
impl(self)->transform_volume_for_channel(channel, volume);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume_to(float f,
|
||||
const OakSampleBuffer *input,
|
||||
OakSampleBuffer *output)
|
||||
{
|
||||
olive::core::internal::SampleBuffer::transform_volume(f, impl(input),
|
||||
impl(output));
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume_for_channel_to(
|
||||
int channel, float volume, const OakSampleBuffer *input,
|
||||
OakSampleBuffer *output)
|
||||
{
|
||||
olive::core::internal::SampleBuffer::transform_volume_for_channel(
|
||||
channel, volume, impl(input), impl(output));
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume_for_sample(OakSampleBuffer *self,
|
||||
size_t sample_index,
|
||||
float volume)
|
||||
{
|
||||
impl(self)->transform_volume_for_sample(sample_index, volume);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_transform_volume_for_sample_on_channel(
|
||||
OakSampleBuffer *self, size_t sample_index, int channel, float volume)
|
||||
{
|
||||
impl(self)->transform_volume_for_sample_on_channel(sample_index, channel,
|
||||
volume);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_clamp(OakSampleBuffer *self)
|
||||
{
|
||||
impl(self)->clamp();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_silence(OakSampleBuffer *self)
|
||||
{
|
||||
impl(self)->silence();
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_silence_range(OakSampleBuffer *self,
|
||||
size_t start_sample,
|
||||
size_t end_sample)
|
||||
{
|
||||
impl(self)->silence(start_sample, end_sample);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_silence_bytes(OakSampleBuffer *self,
|
||||
size_t start_byte, size_t end_byte)
|
||||
{
|
||||
impl(self)->silence_bytes(start_byte, end_byte);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_set(OakSampleBuffer *self, int channel,
|
||||
const float *data, size_t sample_offset,
|
||||
size_t sample_length)
|
||||
{
|
||||
impl(self)->set(channel, data, sample_offset, sample_length);
|
||||
}
|
||||
|
||||
void oakcore_samplebuffer_fast_set(OakSampleBuffer *self,
|
||||
const OakSampleBuffer *other, int to,
|
||||
int from)
|
||||
{
|
||||
impl(self)->fast_set(*impl(other), to, from);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,123 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/stringutils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "util/stringutils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
char **copy_string_vector(const std::vector<std::string> &v, int *count)
|
||||
{
|
||||
char **arr = static_cast<char **>(malloc(sizeof(char *) * v.size()));
|
||||
if (!arr) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
arr[i] = static_cast<char *>(malloc(v[i].size() + 1));
|
||||
if (!arr[i]) {
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
free(arr[j]);
|
||||
}
|
||||
free(arr);
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(arr[i], v[i].c_str(), v[i].size() + 1);
|
||||
}
|
||||
*count = int(v.size());
|
||||
return arr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
char **oakcore_stringutils_split(const char *s, char separator, int *count)
|
||||
{
|
||||
const std::vector<std::string> v =
|
||||
olive::core::internal::StringUtils::split(s ? s : "", separator);
|
||||
return copy_string_vector(v, count);
|
||||
}
|
||||
|
||||
char **oakcore_stringutils_split_regex(const char *s, const char *pattern,
|
||||
int *count)
|
||||
{
|
||||
const std::vector<std::string> v =
|
||||
olive::core::internal::StringUtils::split_regex(
|
||||
s ? s : "", std::regex(pattern ? pattern : ""));
|
||||
return copy_string_vector(v, count);
|
||||
}
|
||||
|
||||
void oakcore_stringutils_free_string_array(char **arr, int count)
|
||||
{
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
free(arr[i]);
|
||||
}
|
||||
free(arr);
|
||||
}
|
||||
|
||||
int oakcore_stringutils_to_int(const char *s, int base, int *ok)
|
||||
{
|
||||
bool b = false;
|
||||
const int x = olive::core::internal::StringUtils::to_int(s ? s : "", base, &b);
|
||||
if (ok) {
|
||||
*ok = b ? 1 : 0;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int oakcore_stringutils_format(char *buf, int buf_size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int r = oakcore_stringutils_format_v(buf, buf_size, fmt, args);
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
|
||||
int oakcore_stringutils_format_v(char *buf, int buf_size, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
// The implementation class only exposes a variadic format(), so the
|
||||
// va_list form applies the same vsnprintf semantics directly here.
|
||||
va_list copy;
|
||||
va_copy(copy, args);
|
||||
const int needed =
|
||||
vsnprintf(buf, buf_size > 0 ? size_t(buf_size) : 0, fmt, copy);
|
||||
va_end(copy);
|
||||
return needed;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,157 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/timecodefunctions.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/rational.h"
|
||||
#include "util/timecodefunctions.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const olive::core::internal::Rational *impl(const OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
OakRational *wrap(olive::core::internal::Rational *r)
|
||||
{
|
||||
return reinterpret_cast<OakRational *>(r);
|
||||
}
|
||||
|
||||
olive::core::internal::Timecode::Display to_display(OakTimecodeDisplay d)
|
||||
{
|
||||
return static_cast<olive::core::internal::Timecode::Display>(d);
|
||||
}
|
||||
|
||||
olive::core::internal::Timecode::Rounding to_rounding(OakTimecodeRounding r)
|
||||
{
|
||||
return static_cast<olive::core::internal::Timecode::Rounding>(r);
|
||||
}
|
||||
|
||||
int write_string(const std::string &s, char *buf, int buf_size)
|
||||
{
|
||||
if (buf && buf_size > 0) {
|
||||
snprintf(buf, size_t(buf_size), "%s", s.c_str());
|
||||
}
|
||||
return int(s.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
int oakcore_timecode_time_to_timecode(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeDisplay display,
|
||||
int show_plus_if_positive, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
const std::string s = olive::core::internal::Timecode::time_to_timecode(
|
||||
*impl(time), *impl(timebase), to_display(display),
|
||||
show_plus_if_positive != 0);
|
||||
return write_string(s, buf, buf_size);
|
||||
}
|
||||
|
||||
OakRational *oakcore_timecode_timecode_to_time(const char *timecode,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeDisplay display,
|
||||
int *ok)
|
||||
{
|
||||
bool b = false;
|
||||
olive::core::internal::Rational *r = new olive::core::internal::Rational(
|
||||
olive::core::internal::Timecode::timecode_to_time(
|
||||
timecode ? timecode : "", *impl(timebase), to_display(display),
|
||||
&b));
|
||||
if (ok) {
|
||||
*ok = b ? 1 : 0;
|
||||
}
|
||||
return wrap(r);
|
||||
}
|
||||
|
||||
int oakcore_timecode_time_to_string(int64_t ms, char *buf, int buf_size)
|
||||
{
|
||||
return write_string(olive::core::internal::Timecode::time_to_string(ms),
|
||||
buf, buf_size);
|
||||
}
|
||||
|
||||
OakRational *oakcore_timecode_snap_time_to_timebase(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(
|
||||
olive::core::internal::Timecode::snap_time_to_timebase(
|
||||
*impl(time), *impl(timebase), to_rounding(rounding))));
|
||||
}
|
||||
|
||||
int64_t oakcore_timecode_time_to_timestamp(const OakRational *time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding)
|
||||
{
|
||||
return olive::core::internal::Timecode::time_to_timestamp(
|
||||
*impl(time), *impl(timebase), to_rounding(rounding));
|
||||
}
|
||||
|
||||
int64_t oakcore_timecode_time_to_timestamp_d(double time,
|
||||
const OakRational *timebase,
|
||||
OakTimecodeRounding rounding)
|
||||
{
|
||||
return olive::core::internal::Timecode::time_to_timestamp(
|
||||
time, *impl(timebase), to_rounding(rounding));
|
||||
}
|
||||
|
||||
int64_t oakcore_timecode_rescale_timestamp(int64_t ts,
|
||||
const OakRational *source,
|
||||
const OakRational *dest)
|
||||
{
|
||||
return olive::core::internal::Timecode::rescale_timestamp(
|
||||
ts, *impl(source), *impl(dest));
|
||||
}
|
||||
|
||||
int64_t oakcore_timecode_rescale_timestamp_ceil(int64_t ts,
|
||||
const OakRational *source,
|
||||
const OakRational *dest)
|
||||
{
|
||||
return olive::core::internal::Timecode::rescale_timestamp_ceil(
|
||||
ts, *impl(source), *impl(dest));
|
||||
}
|
||||
|
||||
OakRational *oakcore_timecode_timestamp_to_time(int64_t timestamp,
|
||||
const OakRational *timebase)
|
||||
{
|
||||
return wrap(new olive::core::internal::Rational(
|
||||
olive::core::internal::Timecode::timestamp_to_time(timestamp,
|
||||
*impl(timebase))));
|
||||
}
|
||||
|
||||
int oakcore_timecode_timebase_is_drop_frame(const OakRational *timebase)
|
||||
{
|
||||
return olive::core::internal::Timecode::timebase_is_drop_frame(
|
||||
*impl(timebase))
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,221 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/timerange.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "util/rational.h"
|
||||
#include "util/timerange.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
olive::core::internal::TimeRange *impl(OakTimeRange *h)
|
||||
{
|
||||
return reinterpret_cast<olive::core::internal::TimeRange *>(h);
|
||||
}
|
||||
|
||||
const olive::core::internal::TimeRange *impl(const OakTimeRange *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::TimeRange *>(h);
|
||||
}
|
||||
|
||||
OakTimeRange *wrap(olive::core::internal::TimeRange *r)
|
||||
{
|
||||
return reinterpret_cast<OakTimeRange *>(r);
|
||||
}
|
||||
|
||||
const olive::core::internal::Rational *rimpl(const OakRational *h)
|
||||
{
|
||||
return reinterpret_cast<const olive::core::internal::Rational *>(h);
|
||||
}
|
||||
|
||||
OakRational *rwrap(olive::core::internal::Rational *r)
|
||||
{
|
||||
return reinterpret_cast<OakRational *>(r);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
OakTimeRange *oakcore_timerange_create(void)
|
||||
{
|
||||
return wrap(new olive::core::internal::TimeRange());
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_create_io(const OakRational *in,
|
||||
const OakRational *out)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::TimeRange(*rimpl(in), *rimpl(out)));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_copy(const OakTimeRange *self)
|
||||
{
|
||||
return wrap(new olive::core::internal::TimeRange(*impl(self)));
|
||||
}
|
||||
|
||||
void oakcore_timerange_free(OakTimeRange *self)
|
||||
{
|
||||
delete impl(self);
|
||||
}
|
||||
|
||||
OakRational *oakcore_timerange_in(const OakTimeRange *self)
|
||||
{
|
||||
return rwrap(new olive::core::internal::Rational(impl(self)->in()));
|
||||
}
|
||||
|
||||
OakRational *oakcore_timerange_out(const OakTimeRange *self)
|
||||
{
|
||||
return rwrap(new olive::core::internal::Rational(impl(self)->out()));
|
||||
}
|
||||
|
||||
OakRational *oakcore_timerange_length(const OakTimeRange *self)
|
||||
{
|
||||
return rwrap(new olive::core::internal::Rational(impl(self)->length()));
|
||||
}
|
||||
|
||||
void oakcore_timerange_set_in(OakTimeRange *self, const OakRational *in)
|
||||
{
|
||||
impl(self)->set_in(*rimpl(in));
|
||||
}
|
||||
|
||||
void oakcore_timerange_set_out(OakTimeRange *self, const OakRational *out)
|
||||
{
|
||||
impl(self)->set_out(*rimpl(out));
|
||||
}
|
||||
|
||||
void oakcore_timerange_set_range(OakTimeRange *self, const OakRational *in,
|
||||
const OakRational *out)
|
||||
{
|
||||
impl(self)->set_range(*rimpl(in), *rimpl(out));
|
||||
}
|
||||
|
||||
int oakcore_timerange_equal(const OakTimeRange *self,
|
||||
const OakTimeRange *other)
|
||||
{
|
||||
return *impl(self) == *impl(other) ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakcore_timerange_overlaps_with(const OakTimeRange *self,
|
||||
const OakTimeRange *other,
|
||||
int in_inclusive, int out_inclusive)
|
||||
{
|
||||
return impl(self)
|
||||
->overlaps_with(*impl(other), in_inclusive != 0,
|
||||
out_inclusive != 0) ?
|
||||
1 :
|
||||
0;
|
||||
}
|
||||
|
||||
int oakcore_timerange_contains_range(const OakTimeRange *self,
|
||||
const OakTimeRange *other,
|
||||
int in_inclusive, int out_inclusive)
|
||||
{
|
||||
return impl(self)
|
||||
->contains(*impl(other), in_inclusive != 0,
|
||||
out_inclusive != 0) ?
|
||||
1 :
|
||||
0;
|
||||
}
|
||||
|
||||
int oakcore_timerange_contains_time(const OakTimeRange *self,
|
||||
const OakRational *time)
|
||||
{
|
||||
return impl(self)->contains(*rimpl(time)) ? 1 : 0;
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_combined(const OakTimeRange *self,
|
||||
const OakTimeRange *other)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::TimeRange(impl(self)->combined(*impl(other))));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_combine(const OakTimeRange *a,
|
||||
const OakTimeRange *b)
|
||||
{
|
||||
return wrap(new olive::core::internal::TimeRange(
|
||||
olive::core::internal::TimeRange::combine(*impl(a), *impl(b))));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_intersected(const OakTimeRange *self,
|
||||
const OakTimeRange *other)
|
||||
{
|
||||
return wrap(new olive::core::internal::TimeRange(
|
||||
impl(self)->intersected(*impl(other))));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_intersect(const OakTimeRange *a,
|
||||
const OakTimeRange *b)
|
||||
{
|
||||
return wrap(new olive::core::internal::TimeRange(
|
||||
olive::core::internal::TimeRange::intersect(*impl(a), *impl(b))));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_add(const OakTimeRange *self,
|
||||
const OakRational *rhs)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::TimeRange(*impl(self) + *rimpl(rhs)));
|
||||
}
|
||||
|
||||
OakTimeRange *oakcore_timerange_sub(const OakTimeRange *self,
|
||||
const OakRational *rhs)
|
||||
{
|
||||
return wrap(
|
||||
new olive::core::internal::TimeRange(*impl(self) - *rimpl(rhs)));
|
||||
}
|
||||
|
||||
void oakcore_timerange_add_assign(OakTimeRange *self, const OakRational *rhs)
|
||||
{
|
||||
*impl(self) += *rimpl(rhs);
|
||||
}
|
||||
|
||||
void oakcore_timerange_sub_assign(OakTimeRange *self, const OakRational *rhs)
|
||||
{
|
||||
*impl(self) -= *rimpl(rhs);
|
||||
}
|
||||
|
||||
int oakcore_timerange_split_count(const OakTimeRange *self, int chunk_size)
|
||||
{
|
||||
return int(impl(self)->split(chunk_size).size());
|
||||
}
|
||||
|
||||
int oakcore_timerange_split(const OakTimeRange *self, int chunk_size,
|
||||
OakTimeRange **out_ranges, int out_size)
|
||||
{
|
||||
const std::list<olive::core::internal::TimeRange> ranges =
|
||||
impl(self)->split(chunk_size);
|
||||
int n = 0;
|
||||
for (const olive::core::internal::TimeRange &r : ranges) {
|
||||
if (out_ranges && n < out_size) {
|
||||
out_ranges[n] =
|
||||
wrap(new olive::core::internal::TimeRange(r));
|
||||
}
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,217 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
#define OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
#include <cstring>
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
#include "render/channellayout.h"
|
||||
#include "render/sampleformat.h"
|
||||
#include "../util/rational.h"
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Audio parameters class managing audio stream configuration
|
||||
*
|
||||
* Channel layouts are stored as plain 64-bit masks (see channellayout.h).
|
||||
* Because the mask is a simple value type, AudioParams has value semantics
|
||||
* and can be copied freely.
|
||||
*/
|
||||
class AudioParams {
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor creates invalid AudioParams
|
||||
* sample_rate=0, channel_layout empty, format=INVALID
|
||||
*/
|
||||
AudioParams()
|
||||
: sample_rate_(0)
|
||||
, channel_layout_mask_(0)
|
||||
, channel_count_(0)
|
||||
, format_(SampleFormat::invalid)
|
||||
{
|
||||
set_default_footage_parameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor from channel layout mask
|
||||
* @param sample_rate Audio sample rate
|
||||
* @param channel_layout Channel layout mask (e.g., kChannelLayoutStereo)
|
||||
* @param format Sample format
|
||||
*/
|
||||
AudioParams(const int &sample_rate, uint64_t channel_layout,
|
||||
const SampleFormat &format)
|
||||
: sample_rate_(sample_rate)
|
||||
, channel_layout_mask_(channel_layout)
|
||||
, channel_count_(0)
|
||||
, format_(format)
|
||||
{
|
||||
set_default_footage_parameters();
|
||||
timebase_ = sample_rate_as_time_base();
|
||||
calculate_channel_count();
|
||||
}
|
||||
int sample_rate() const
|
||||
{
|
||||
return sample_rate_;
|
||||
}
|
||||
|
||||
void set_sample_rate(int sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Channel layout as a 64-bit mask (0 if unspecified)
|
||||
*/
|
||||
const uint64_t &channel_layout() const
|
||||
{
|
||||
return channel_layout_mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set channel layout from mask
|
||||
* @param mask Channel layout mask (e.g., kChannelLayoutStereo)
|
||||
*/
|
||||
void set_channel_layout(uint64_t mask)
|
||||
{
|
||||
channel_layout_mask_ = mask;
|
||||
calculate_channel_count();
|
||||
}
|
||||
Rational time_base() const
|
||||
{
|
||||
return timebase_;
|
||||
}
|
||||
|
||||
void set_time_base(const Rational &timebase)
|
||||
{
|
||||
timebase_ = timebase;
|
||||
}
|
||||
|
||||
Rational sample_rate_as_time_base() const
|
||||
{
|
||||
return Rational(1, sample_rate());
|
||||
}
|
||||
|
||||
SampleFormat format() const
|
||||
{
|
||||
return format_;
|
||||
}
|
||||
|
||||
void set_format(SampleFormat format)
|
||||
{
|
||||
format_ = format;
|
||||
}
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
int stream_index() const
|
||||
{
|
||||
return stream_index_;
|
||||
}
|
||||
|
||||
void set_stream_index(int s)
|
||||
{
|
||||
stream_index_ = s;
|
||||
}
|
||||
|
||||
int64_t duration() const
|
||||
{
|
||||
return duration_;
|
||||
}
|
||||
|
||||
void set_duration(int64_t duration)
|
||||
{
|
||||
duration_ = duration;
|
||||
}
|
||||
|
||||
int64_t time_to_bytes(const double &time) const;
|
||||
int64_t time_to_bytes(const Rational &time) const;
|
||||
int64_t time_to_bytes_per_channel(const double &time) const;
|
||||
int64_t time_to_bytes_per_channel(const Rational &time) const;
|
||||
int64_t time_to_samples(const double &time) const;
|
||||
int64_t time_to_samples(const Rational &time) const;
|
||||
int64_t samples_to_bytes(const int64_t &samples) const;
|
||||
int64_t samples_to_bytes_per_channel(const int64_t &samples) const;
|
||||
Rational samples_to_time(const int64_t &samples) const;
|
||||
int64_t bytes_to_samples(const int64_t &bytes) const;
|
||||
Rational bytes_to_time(const int64_t &bytes) const;
|
||||
Rational bytes_per_channel_to_time(const int64_t &bytes) const;
|
||||
int channel_count() const;
|
||||
int bytes_per_sample_per_channel() const;
|
||||
int bits_per_sample() const;
|
||||
bool is_valid() const;
|
||||
|
||||
bool operator==(const AudioParams &other) const;
|
||||
bool operator!=(const AudioParams &other) const;
|
||||
|
||||
static const std::vector<uint64_t> k_supported_channel_layouts;
|
||||
static const std::vector<int> k_supported_sample_rates;
|
||||
|
||||
private:
|
||||
void set_default_footage_parameters()
|
||||
{
|
||||
enabled_ = true;
|
||||
stream_index_ = 0;
|
||||
duration_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates channel_count_ from the current channel_layout_mask_
|
||||
* Called after any channel layout modification.
|
||||
*/
|
||||
void calculate_channel_count();
|
||||
|
||||
int sample_rate_; ///< Audio sample rate in Hz (e.g., 48000)
|
||||
|
||||
/**
|
||||
* @brief Channel layout mask (0 if unspecified)
|
||||
*
|
||||
* Plain value type mirroring FFmpeg's AV_CH_LAYOUT_* masks; no dynamic
|
||||
* memory is involved, so copies are trivially safe.
|
||||
*/
|
||||
uint64_t channel_layout_mask_;
|
||||
|
||||
int channel_count_; ///< Cached channel count from layout
|
||||
|
||||
SampleFormat format_; ///< Audio sample format
|
||||
|
||||
// Footage-specific parameters (serialized with footage metadata)
|
||||
int enabled_; // Using int instead of bool fixes GCC 11 stringop-overflow issue (byte alignment)
|
||||
int stream_index_; ///< Index in the source file's stream list
|
||||
int64_t duration_; ///< Stream duration in timebase units
|
||||
Rational timebase_; ///< Timebase for this audio stream
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_AUDIOPARAMS_H
|
||||
@@ -0,0 +1,137 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_SAMPLEBUFFER_H
|
||||
#define OAK_LIBOLIVECORE_SAMPLEBUFFER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "audioparams.h"
|
||||
#include "../util/rational.h"
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A buffer of audio samples
|
||||
*
|
||||
* Audio samples in this structure are always stored in PLANAR (separated by channel). This is done to simplify audio
|
||||
* rendering code. This replaces the old system of using QByteArrays (containing packed audio) and while SampleBuffer
|
||||
* replaces many of those in the rendering/processing side of things, QByteArrays are currently still in use for
|
||||
* playback, including reading to and from the cache.
|
||||
*/
|
||||
class SampleBuffer {
|
||||
public:
|
||||
SampleBuffer();
|
||||
SampleBuffer(const AudioParams &audio_params, const Rational &length);
|
||||
SampleBuffer(const AudioParams &audio_params, size_t samples_per_channel);
|
||||
|
||||
SampleBuffer rip_channel(int channel) const;
|
||||
std::vector<float> rip_channel_vector(int channel) const;
|
||||
|
||||
const AudioParams &audio_params() const;
|
||||
void set_audio_params(const AudioParams ¶ms);
|
||||
|
||||
const size_t &sample_count() const
|
||||
{
|
||||
return sample_count_per_channel_;
|
||||
}
|
||||
void set_sample_count(const size_t &sample_count);
|
||||
void set_sample_count(const Rational &length)
|
||||
{
|
||||
set_sample_count(audio_params_.time_to_samples(length));
|
||||
}
|
||||
|
||||
float *data(int channel)
|
||||
{
|
||||
return data_[channel].data();
|
||||
}
|
||||
|
||||
const float *data(int channel) const
|
||||
{
|
||||
return data_.at(channel).data();
|
||||
}
|
||||
|
||||
std::vector<float *> to_raw_ptrs()
|
||||
{
|
||||
std::vector<float *> r(data_.size());
|
||||
for (size_t i = 0; i < r.size(); i++) {
|
||||
r[i] = data_[i].data();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
bool is_allocated() const
|
||||
{
|
||||
return !data_.empty();
|
||||
}
|
||||
void allocate();
|
||||
void destroy();
|
||||
|
||||
void reverse();
|
||||
void speed(double speed);
|
||||
|
||||
void transform_volume(float f);
|
||||
void transform_volume_for_channel(int channel, float volume);
|
||||
static void transform_volume(float f, const SampleBuffer *input,
|
||||
SampleBuffer *output);
|
||||
static void transform_volume_for_channel(int channel, float volume,
|
||||
const SampleBuffer *input,
|
||||
SampleBuffer *output);
|
||||
|
||||
void transform_volume_for_sample(size_t sample_index, float volume);
|
||||
void transform_volume_for_sample_on_channel(size_t sample_index,
|
||||
int channel, float volume);
|
||||
|
||||
void clamp();
|
||||
|
||||
void silence();
|
||||
void silence(size_t start_sample, size_t end_sample);
|
||||
void silence_bytes(size_t start_byte, size_t end_byte);
|
||||
|
||||
void set(int channel, const float *data, size_t sample_offset,
|
||||
size_t sample_length);
|
||||
void set(int channel, const float *data, size_t sample_length)
|
||||
{
|
||||
set(channel, data, 0, sample_length);
|
||||
}
|
||||
|
||||
void fast_set(const SampleBuffer &other, int to, int from = -1);
|
||||
|
||||
private:
|
||||
void clamp_channel(int channel);
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
size_t sample_count_per_channel_;
|
||||
|
||||
std::vector<std::vector<float>> data_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_SAMPLEBUFFER_H
|
||||
@@ -0,0 +1,138 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_BEZIER_H
|
||||
#define OAK_LIBOLIVECORE_BEZIER_H
|
||||
|
||||
#include <Imath/ImathVec.h>
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
class Bezier {
|
||||
public:
|
||||
Bezier();
|
||||
Bezier(double x, double y);
|
||||
Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x,
|
||||
double cp2_y);
|
||||
|
||||
const double &x() const
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
const double &y() const
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
const double &cp1_x() const
|
||||
{
|
||||
return cp1_x_;
|
||||
}
|
||||
const double &cp1_y() const
|
||||
{
|
||||
return cp1_y_;
|
||||
}
|
||||
const double &cp2_x() const
|
||||
{
|
||||
return cp2_x_;
|
||||
}
|
||||
const double &cp2_y() const
|
||||
{
|
||||
return cp2_y_;
|
||||
}
|
||||
|
||||
Imath::V2d to_vec() const
|
||||
{
|
||||
return Imath::V2d(x_, y_);
|
||||
}
|
||||
|
||||
Imath::V2d control_point_1_to_vec() const
|
||||
{
|
||||
return Imath::V2d(cp1_x_, cp1_y_);
|
||||
}
|
||||
|
||||
Imath::V2d control_point_2_to_vec() const
|
||||
{
|
||||
return Imath::V2d(cp2_x_, cp2_y_);
|
||||
}
|
||||
|
||||
void set_x(const double &x)
|
||||
{
|
||||
x_ = x;
|
||||
}
|
||||
void set_y(const double &y)
|
||||
{
|
||||
y_ = y;
|
||||
}
|
||||
void set_cp1_x(const double &cp1_x)
|
||||
{
|
||||
cp1_x_ = cp1_x;
|
||||
}
|
||||
void set_cp1_y(const double &cp1_y)
|
||||
{
|
||||
cp1_y_ = cp1_y;
|
||||
}
|
||||
void set_cp2_x(const double &cp2_x)
|
||||
{
|
||||
cp2_x_ = cp2_x;
|
||||
}
|
||||
void set_cp2_y(const double &cp2_y)
|
||||
{
|
||||
cp2_y_ = cp2_y;
|
||||
}
|
||||
|
||||
static double quadratic_xto_t(double x, double a, double b, double c);
|
||||
|
||||
static double quadratic_tto_y(double a, double b, double c, double t);
|
||||
|
||||
static double quadratic_xto_y(double x, const Imath::V2d &a,
|
||||
const Imath::V2d &b, const Imath::V2d &c)
|
||||
{
|
||||
return quadratic_tto_y(a.y, b.y, c.y, quadratic_xto_t(x, a.x, b.x, c.x));
|
||||
}
|
||||
|
||||
static double cubic_xto_t(double x, double a, double b, double c, double d);
|
||||
|
||||
static double cubic_tto_y(double a, double b, double c, double d, double t);
|
||||
|
||||
static double cubic_xto_y(double x, const Imath::V2d &a, const Imath::V2d &b,
|
||||
const Imath::V2d &c, const Imath::V2d &d)
|
||||
{
|
||||
return cubic_tto_y(a.y, b.y, c.y, d.y, cubic_xto_t(x, a.x, b.x, c.x, d.x));
|
||||
}
|
||||
|
||||
private:
|
||||
static double calculate_t_from_x(bool cubic, double x, double a, double b,
|
||||
double c, double d);
|
||||
|
||||
double x_;
|
||||
double y_;
|
||||
|
||||
double cp1_x_;
|
||||
double cp1_y_;
|
||||
|
||||
double cp2_x_;
|
||||
double cp2_y_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_BEZIER_H
|
||||
@@ -0,0 +1,184 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_COLOR_H
|
||||
#define OAK_LIBOLIVECORE_COLOR_H
|
||||
|
||||
#include "render/pixelformat.h"
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief High precision 32-bit DataType based RGBA color value
|
||||
*/
|
||||
class Color {
|
||||
public:
|
||||
using DataType = float;
|
||||
static constexpr unsigned int rgba = 4;
|
||||
|
||||
Color()
|
||||
{
|
||||
for (unsigned int i = 0; i < rgba; i++) {
|
||||
data_[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Color(const DataType &r, const DataType &g, const DataType &b,
|
||||
const DataType &a = 1.0f)
|
||||
{
|
||||
data_[0] = r;
|
||||
data_[1] = g;
|
||||
data_[2] = b;
|
||||
data_[3] = a;
|
||||
}
|
||||
|
||||
Color(const char *data, const PixelFormat &format, int ch_layout);
|
||||
|
||||
/**
|
||||
* @brief Creates a Color struct from hue/saturation/value
|
||||
*
|
||||
* Hue expects a value between 0.0 and 360.0. Saturation and Value expect a value between 0.0 and 1.0.
|
||||
*/
|
||||
static Color from_hsv(const DataType &h, const DataType &s,
|
||||
const DataType &v);
|
||||
|
||||
const DataType &red() const
|
||||
{
|
||||
return data_[0];
|
||||
}
|
||||
const DataType &green() const
|
||||
{
|
||||
return data_[1];
|
||||
}
|
||||
const DataType &blue() const
|
||||
{
|
||||
return data_[2];
|
||||
}
|
||||
const DataType &alpha() const
|
||||
{
|
||||
return data_[3];
|
||||
}
|
||||
|
||||
void to_hsv(DataType *hue, DataType *sat, DataType *val) const;
|
||||
DataType hsv_hue() const;
|
||||
DataType hsv_saturation() const;
|
||||
DataType value() const;
|
||||
|
||||
void to_hsl(DataType *hue, DataType *sat, DataType *lightness) const;
|
||||
DataType hsl_hue() const;
|
||||
DataType hsl_saturation() const;
|
||||
DataType lightness() const;
|
||||
|
||||
void set_red(const DataType &red)
|
||||
{
|
||||
data_[0] = red;
|
||||
}
|
||||
void set_green(const DataType &green)
|
||||
{
|
||||
data_[1] = green;
|
||||
}
|
||||
void set_blue(const DataType &blue)
|
||||
{
|
||||
data_[2] = blue;
|
||||
}
|
||||
void set_alpha(const DataType &alpha)
|
||||
{
|
||||
data_[3] = alpha;
|
||||
}
|
||||
|
||||
DataType *data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
const DataType *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
void to_data(char *out, const PixelFormat &format,
|
||||
unsigned int nb_channels) const;
|
||||
|
||||
static Color from_data(const char *in, const PixelFormat &format,
|
||||
unsigned int nb_channels);
|
||||
|
||||
// Suuuuper rough luminance value mostly used for UI (determining whether to overlay with black
|
||||
// or white text)
|
||||
DataType get_rough_luminance() const;
|
||||
|
||||
// Assignment math operators
|
||||
Color &operator+=(const Color &rhs);
|
||||
Color &operator-=(const Color &rhs);
|
||||
Color &operator+=(const DataType &rhs);
|
||||
Color &operator-=(const DataType &rhs);
|
||||
Color &operator*=(const DataType &rhs);
|
||||
Color &operator/=(const DataType &rhs);
|
||||
|
||||
// Binary math operators
|
||||
Color operator+(const Color &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c += rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator-(const Color &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c -= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator+(const DataType &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c += rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator-(const DataType &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c -= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator*(const DataType &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c *= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator/(const DataType &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c /= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
DataType data_[rgba];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_COLOR_H
|
||||
@@ -0,0 +1,80 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_FRACTIONUTILS_H
|
||||
#define OAK_LIBOLIVECORE_FRACTIONUTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Rounding modes for RescaleRnd()
|
||||
*
|
||||
* Mirrors the FFmpeg AVRounding modes that this codebase used before the
|
||||
* FFmpeg dependency was removed from core.
|
||||
*/
|
||||
enum class FractionRounding {
|
||||
/**
|
||||
* Round to the nearest value; halfway cases are rounded away from zero.
|
||||
* Equivalent to FFmpeg's AV_ROUND_NEAR_INF.
|
||||
*/
|
||||
k_near_inf,
|
||||
|
||||
/**
|
||||
* Round toward positive infinity. Equivalent to FFmpeg's AV_ROUND_UP.
|
||||
*/
|
||||
k_up
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reduce a fraction so that numerator and denominator fit within `max`
|
||||
*
|
||||
* Native re-implementation of FFmpeg's av_reduce(): divides out the greatest
|
||||
* common divisor and, if the values still do not fit within `max`, finds the
|
||||
* closest approximation using continued fractions.
|
||||
*
|
||||
* A zero denominator is preserved (with the numerator set to zero).
|
||||
*/
|
||||
void reduce_fraction(int64_t &num, int64_t &den, int64_t max);
|
||||
|
||||
/**
|
||||
* @brief Compare two fractions
|
||||
*
|
||||
* Native re-implementation of FFmpeg's av_cmp_q(): returns -1 if a < b,
|
||||
* 0 if a == b, 1 if a > b, and INT_MIN when the comparison is meaningless
|
||||
* (degenerate zero-denominator fractions).
|
||||
*/
|
||||
int compare_fractions(int an, int ad, int bn, int bd);
|
||||
|
||||
/**
|
||||
* @brief Rescale `a` by the fraction b/c: returns a * b / c
|
||||
*
|
||||
* Native re-implementation of FFmpeg's av_rescale_rnd(). The intermediate
|
||||
* product is computed with 128-bit arithmetic where available so that no
|
||||
* precision is lost for large timestamps.
|
||||
*/
|
||||
int64_t rescale_rnd(int64_t a, int64_t b, int64_t c, FractionRounding rnd);
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_FRACTIONUTILS_H
|
||||
@@ -0,0 +1,157 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_RATIONAL_H
|
||||
#define OAK_LIBOLIVECORE_RATIONAL_H
|
||||
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef USE_OTIO
|
||||
#include <opentime/rationalTime.h>
|
||||
#endif
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
class Rational {
|
||||
public:
|
||||
Rational(const int &numerator = 0)
|
||||
{
|
||||
num_ = numerator;
|
||||
den_ = 1;
|
||||
}
|
||||
|
||||
Rational(const int &numerator, const int &denominator)
|
||||
{
|
||||
num_ = numerator;
|
||||
den_ = denominator;
|
||||
|
||||
fix_signs();
|
||||
reduce();
|
||||
}
|
||||
|
||||
Rational(const Rational &rhs) = default;
|
||||
|
||||
static Rational from_double(const double &flt, bool *ok = nullptr);
|
||||
static Rational from_string(const std::string &str, bool *ok = nullptr);
|
||||
|
||||
static const Rational na_n;
|
||||
|
||||
//Assignment Operators
|
||||
const Rational &operator=(const Rational &rhs);
|
||||
const Rational &operator+=(const Rational &rhs);
|
||||
const Rational &operator-=(const Rational &rhs);
|
||||
const Rational &operator/=(const Rational &rhs);
|
||||
const Rational &operator*=(const Rational &rhs);
|
||||
|
||||
//Binary math operators
|
||||
Rational operator+(const Rational &rhs) const;
|
||||
Rational operator-(const Rational &rhs) const;
|
||||
Rational operator/(const Rational &rhs) const;
|
||||
Rational operator*(const Rational &rhs) const;
|
||||
|
||||
//Relational and equality operators
|
||||
bool operator<(const Rational &rhs) const;
|
||||
bool operator<=(const Rational &rhs) const;
|
||||
bool operator>(const Rational &rhs) const;
|
||||
bool operator>=(const Rational &rhs) const;
|
||||
bool operator==(const Rational &rhs) const;
|
||||
bool operator!=(const Rational &rhs) const;
|
||||
|
||||
//Unary operators
|
||||
const Rational &operator+() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
Rational operator-() const
|
||||
{
|
||||
return Rational(num_, -den_);
|
||||
}
|
||||
bool operator!() const
|
||||
{
|
||||
return !num_;
|
||||
}
|
||||
|
||||
//Function: convert to double
|
||||
double to_double() const;
|
||||
|
||||
#ifdef USE_OTIO
|
||||
static Rational fromRationalTime(const opentime::RationalTime &t)
|
||||
{
|
||||
// Is this the best way to do this?
|
||||
return fromDouble(t.to_seconds());
|
||||
}
|
||||
|
||||
// Convert Olive rationals to opentime rationals with the given framerate (defaults to 24)
|
||||
opentime::RationalTime toRationalTime(double framerate = 24) const;
|
||||
#endif
|
||||
|
||||
// Produce "flipped" version
|
||||
Rational flipped() const;
|
||||
void flip();
|
||||
|
||||
// Returns whether the Rational is valid but equal to zero or not
|
||||
//
|
||||
// A NaN is always a null, but a null is not always a NaN
|
||||
bool isNull() const
|
||||
{
|
||||
return num_ == 0;
|
||||
}
|
||||
|
||||
// Returns whether this Rational is not a valid number (denominator == 0)
|
||||
bool isNaN() const
|
||||
{
|
||||
return den_ == 0;
|
||||
}
|
||||
|
||||
const int &numerator() const
|
||||
{
|
||||
return num_;
|
||||
}
|
||||
const int &denominator() const
|
||||
{
|
||||
return den_;
|
||||
}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, const Rational &value)
|
||||
{
|
||||
out << value.num_ << '/' << value.den_;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
void fix_signs();
|
||||
void reduce();
|
||||
|
||||
int num_;
|
||||
int den_;
|
||||
};
|
||||
|
||||
#define RATIONAL_MIN Rational(INT_MIN)
|
||||
#define RATIONAL_MAX Rational(INT_MAX)
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_RATIONAL_H
|
||||
@@ -0,0 +1,211 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_STRINGUTILS_H
|
||||
#define OAK_LIBOLIVECORE_STRINGUTILS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
class StringUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Split a string into a list of strings using a specific delimiter
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The string to split.
|
||||
*
|
||||
* @param separator
|
||||
*
|
||||
* The character to split the string on.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A vector of strings split by the specified delimiter.
|
||||
*/
|
||||
static std::vector<std::string> split(const std::string &s, char separator);
|
||||
|
||||
/**
|
||||
* @brief Splits a string into a list of strings using regular expressions.
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The string to split.
|
||||
*
|
||||
* @param regex
|
||||
*
|
||||
* The regular expression to split the string on.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A vector of strings split wherever the regular expression matched.
|
||||
*/
|
||||
static std::vector<std::string> split_regex(const std::string &s,
|
||||
const std::regex ®ex);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to int using a bool pointer to determine success rather than an exception
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The string to parse an int from.
|
||||
*
|
||||
* @param base
|
||||
*
|
||||
* The base of the number in the string (usually 10, or 16 for hex).
|
||||
*
|
||||
* @param ok
|
||||
*
|
||||
* (Optional) a boolean output parameter specifying whether the conversion was successful or not.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Either the int parsed from the string, or 0 (with *ok set to false) on parser error.
|
||||
*/
|
||||
static int to_int(const std::string &s, int base, bool *ok = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Overloaded function
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The string to parse an int from.
|
||||
*
|
||||
* @param ok
|
||||
*
|
||||
* (Optional) a boolean output parameter specifying whether the conversion was successful or not.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Either the int parsed from the string, or 0 (with *ok set to false) on parser error.
|
||||
*/
|
||||
static int to_int(const std::string &s, bool *ok = nullptr)
|
||||
{
|
||||
return to_int(s, 10, ok);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a number to a string with left padding
|
||||
*
|
||||
* Usually used for converting a number to a string with leading zeroes.
|
||||
*
|
||||
* @param val
|
||||
*
|
||||
* The number to convert. This is a templated function and will accept any type, e.g.
|
||||
* int/long/float/double/etc.
|
||||
*
|
||||
* @param padding
|
||||
*
|
||||
* Total desired length of the string. For example, setting this to `2` will ensure the string
|
||||
* is at least 2 characters in size, using `c` to pad the left side where necessary.
|
||||
*
|
||||
* @param c
|
||||
*
|
||||
* The character to pad with. This defaults to `0` assuming you'll be using this function to
|
||||
* create leading zeroes.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The padded string.
|
||||
*/
|
||||
template <typename T>
|
||||
static std::string to_string_leftpad(T val, size_t padding, char c = '0')
|
||||
{
|
||||
std::string s = std::to_string(val);
|
||||
|
||||
if (s.size() < padding) {
|
||||
s.insert(0, padding - s.size(), c);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format a string
|
||||
*
|
||||
* A sprintf wrapper that returns a std::string.
|
||||
*
|
||||
* @param fmt
|
||||
*
|
||||
* The format to use.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A formatted string in std::string form.
|
||||
*/
|
||||
static std::string format(const char *fmt, ...);
|
||||
|
||||
// trim from start (in place)
|
||||
static inline void ltrim(std::string &s)
|
||||
{
|
||||
s.erase(s.begin(),
|
||||
std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
}
|
||||
|
||||
// trim from end (in place)
|
||||
static inline void rtrim(std::string &s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||
[](unsigned char ch) { return !std::isspace(ch); })
|
||||
.base(),
|
||||
s.end());
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
static inline void trim(std::string &s)
|
||||
{
|
||||
rtrim(s);
|
||||
ltrim(s);
|
||||
}
|
||||
|
||||
// trim from start (copying)
|
||||
static inline std::string ltrimmed(std::string s)
|
||||
{
|
||||
ltrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from end (copying)
|
||||
static inline std::string rtrimmed(std::string s)
|
||||
{
|
||||
rtrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from both ends (copying)
|
||||
static inline std::string trimmed(std::string s)
|
||||
{
|
||||
trim(s);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_STRINGUTILS_H
|
||||
@@ -0,0 +1,93 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
|
||||
#define OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
|
||||
|
||||
#include "rational.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Functions for converting times/timecodes/timestamps
|
||||
*
|
||||
* Olive uses the following terminology through its code:
|
||||
*
|
||||
* `time` - time in seconds presented in a Rational form
|
||||
* `timebase` - the base time unit of an audio/video stream in seconds
|
||||
* `timestamp` - an integer representation of a time in timebase units (in many cases is used like a frame number)
|
||||
* `timecode` a user-friendly string representation of a time according to Timecode::Display
|
||||
*/
|
||||
class Timecode {
|
||||
public:
|
||||
enum Display {
|
||||
k_timecode_drop_frame,
|
||||
k_timecode_non_drop_frame,
|
||||
k_timecode_seconds,
|
||||
k_frames,
|
||||
k_milliseconds
|
||||
};
|
||||
|
||||
enum Rounding { k_ceil, k_floor, k_round };
|
||||
|
||||
/**
|
||||
* @brief Convert a timestamp (according to a Rational timebase) to a user-friendly string representation
|
||||
*/
|
||||
static std::string time_to_timecode(const Rational &time,
|
||||
const Rational &timebase,
|
||||
const Display &display,
|
||||
bool show_plus_if_positive = false);
|
||||
static Rational timecode_to_time(std::string timecode,
|
||||
const Rational &timebase,
|
||||
const Display &display,
|
||||
bool *ok = nullptr);
|
||||
|
||||
static std::string time_to_string(int64_t ms);
|
||||
|
||||
static Rational snap_time_to_timebase(const Rational &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
|
||||
static int64_t time_to_timestamp(const Rational &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
static int64_t time_to_timestamp(const double &time,
|
||||
const Rational &timebase,
|
||||
Rounding floor = k_round);
|
||||
|
||||
static int64_t rescale_timestamp(const int64_t &ts, const Rational &source,
|
||||
const Rational &dest);
|
||||
static int64_t rescale_timestamp_ceil(const int64_t &ts,
|
||||
const Rational &source,
|
||||
const Rational &dest);
|
||||
|
||||
static Rational timestamp_to_time(const int64_t ×tamp,
|
||||
const Rational &timebase);
|
||||
|
||||
static bool timebase_is_drop_frame(const Rational &timebase);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
|
||||
@@ -0,0 +1,315 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_LIBOLIVECORE_TIMERANGE_H
|
||||
#define OAK_LIBOLIVECORE_TIMERANGE_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "rational.h"
|
||||
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
class TimeRange {
|
||||
public:
|
||||
TimeRange() = default;
|
||||
TimeRange(const Rational &in, const Rational &out);
|
||||
TimeRange(const TimeRange &r)
|
||||
: TimeRange(r.in(), r.out())
|
||||
{
|
||||
}
|
||||
|
||||
TimeRange &operator=(const TimeRange &r)
|
||||
{
|
||||
set_range(r.in(), r.out());
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Rational &in() const;
|
||||
const Rational &out() const;
|
||||
const Rational &length() const;
|
||||
|
||||
void set_in(const Rational &in);
|
||||
void set_out(const Rational &out);
|
||||
void set_range(const Rational &in, const Rational &out);
|
||||
|
||||
bool operator==(const TimeRange &r) const;
|
||||
bool operator!=(const TimeRange &r) const;
|
||||
|
||||
bool overlaps_with(const TimeRange &a, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
bool contains(const TimeRange &a, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
bool contains(const Rational &r) const;
|
||||
|
||||
TimeRange combined(const TimeRange &a) const;
|
||||
static TimeRange combine(const TimeRange &a, const TimeRange &b);
|
||||
TimeRange intersected(const TimeRange &a) const;
|
||||
static TimeRange intersect(const TimeRange &a, const TimeRange &b);
|
||||
|
||||
TimeRange operator+(const Rational &rhs) const;
|
||||
TimeRange operator-(const Rational &rhs) const;
|
||||
|
||||
const TimeRange &operator+=(const Rational &rhs);
|
||||
const TimeRange &operator-=(const Rational &rhs);
|
||||
|
||||
std::list<TimeRange> split(const int &chunk_size) const;
|
||||
|
||||
private:
|
||||
void normalize();
|
||||
|
||||
Rational in_;
|
||||
Rational out_;
|
||||
Rational length_;
|
||||
};
|
||||
|
||||
class TimeRangeList {
|
||||
public:
|
||||
TimeRangeList() = default;
|
||||
|
||||
TimeRangeList(std::initializer_list<TimeRange> r)
|
||||
: array_(r)
|
||||
{
|
||||
}
|
||||
|
||||
void insert(const TimeRangeList &list_to_add);
|
||||
void insert(TimeRange range_to_add);
|
||||
|
||||
void remove(const TimeRange &remove);
|
||||
void remove(const TimeRangeList &list);
|
||||
|
||||
template <typename T>
|
||||
static void util_remove(std::vector<T> *list, const TimeRange &remove)
|
||||
{
|
||||
std::vector<T> additions;
|
||||
|
||||
for (auto it = list->begin(); it != list->end();) {
|
||||
T &compare = *it;
|
||||
|
||||
if (remove.contains(compare)) {
|
||||
// This element is entirely encompassed in this range, remove it
|
||||
it = list->erase(it);
|
||||
} else {
|
||||
if (compare.contains(remove, false, false)) {
|
||||
// The remove range is within this element, only choice is to split the element into two
|
||||
T new_range = compare;
|
||||
new_range.set_in(remove.out());
|
||||
compare.set_out(remove.in());
|
||||
|
||||
additions.push_back(new_range);
|
||||
break;
|
||||
} else {
|
||||
if (compare.in() < remove.in() &&
|
||||
compare.out() > remove.in()) {
|
||||
// This element's out point overlaps the range's in, we'll trim it
|
||||
compare.set_out(remove.in());
|
||||
} else if (compare.in() < remove.out() &&
|
||||
compare.out() > remove.out()) {
|
||||
// This element's in point overlaps the range's out, we'll trim it
|
||||
compare.set_in(remove.out());
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list->insert(list->end(), additions.begin(), additions.end());
|
||||
}
|
||||
|
||||
bool contains(const TimeRange &range, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const;
|
||||
|
||||
bool contains(const Rational &r) const
|
||||
{
|
||||
for (const TimeRange &range : array_) {
|
||||
if (range.contains(r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool overlaps_with(const TimeRange &r, bool in_inclusive = true,
|
||||
bool out_inclusive = true) const
|
||||
{
|
||||
for (const TimeRange &range : array_) {
|
||||
if (range.overlaps_with(r, in_inclusive, out_inclusive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return array_.empty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
array_.clear();
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return array_.size();
|
||||
}
|
||||
|
||||
void shift(const Rational &diff);
|
||||
|
||||
void trim_in(const Rational &diff);
|
||||
|
||||
void trim_out(const Rational &diff);
|
||||
|
||||
TimeRangeList intersects(const TimeRange &range) const;
|
||||
|
||||
using const_iterator = std::vector<TimeRange>::const_iterator;
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return array_.cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return array_.cend();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
const TimeRange &first() const
|
||||
{
|
||||
return array_.front();
|
||||
}
|
||||
|
||||
const TimeRange &last() const
|
||||
{
|
||||
return array_.back();
|
||||
}
|
||||
|
||||
const TimeRange &at(int index) const
|
||||
{
|
||||
return array_.at(index);
|
||||
}
|
||||
|
||||
const std::vector<TimeRange> &internal_array() const
|
||||
{
|
||||
return array_;
|
||||
}
|
||||
|
||||
bool operator==(const TimeRangeList &rhs) const
|
||||
{
|
||||
return array_ == rhs.array_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<TimeRange> array_;
|
||||
};
|
||||
|
||||
class TimeRangeListFrameIterator {
|
||||
public:
|
||||
TimeRangeListFrameIterator();
|
||||
TimeRangeListFrameIterator(const TimeRangeList &list,
|
||||
const Rational &timebase);
|
||||
|
||||
Rational snap(const Rational &r) const;
|
||||
|
||||
bool get_next(Rational *out);
|
||||
|
||||
bool has_next() const;
|
||||
|
||||
std::vector<Rational> to_vector() const
|
||||
{
|
||||
TimeRangeListFrameIterator copy(list_, timebase_);
|
||||
std::vector<Rational> times;
|
||||
Rational r;
|
||||
while (copy.get_next(&r)) {
|
||||
times.push_back(r);
|
||||
}
|
||||
return times;
|
||||
}
|
||||
|
||||
int size();
|
||||
|
||||
void reset()
|
||||
{
|
||||
*this = TimeRangeListFrameIterator();
|
||||
}
|
||||
|
||||
void insert(const TimeRange &range)
|
||||
{
|
||||
list_.insert(range);
|
||||
}
|
||||
|
||||
void insert(const TimeRangeList &list)
|
||||
{
|
||||
list_.insert(list);
|
||||
}
|
||||
|
||||
bool is_custom_range() const
|
||||
{
|
||||
return custom_range_;
|
||||
}
|
||||
|
||||
void set_custom_range(bool e)
|
||||
{
|
||||
custom_range_ = e;
|
||||
}
|
||||
|
||||
int frame_index() const
|
||||
{
|
||||
return frame_index_;
|
||||
}
|
||||
|
||||
private:
|
||||
void update_index_if_necessary();
|
||||
|
||||
TimeRangeList list_;
|
||||
|
||||
Rational timebase_;
|
||||
|
||||
Rational current_;
|
||||
|
||||
int range_index_;
|
||||
|
||||
int size_;
|
||||
|
||||
int frame_index_;
|
||||
|
||||
bool custom_range_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LIBOLIVECORE_TIMERANGE_H
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
const std::vector<int> AudioParams::k_supported_sample_rates = {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "util/cpuoptimize.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
SampleBuffer::SampleBuffer()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
Bezier::Bezier()
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
Color Color::from_hsv(const DataType &h, const DataType &s, const DataType &v)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
namespace
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "util/fractionutils.h"
|
||||
#include "util/stringutils.h"
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
const Rational Rational::na_n = Rational(0, 0);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
std::vector<std::string> StringUtils::split(const std::string &s,
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "util/fractionutils.h"
|
||||
#include "util/stringutils.h"
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
std::string Timecode::time_to_timecode(const Rational &time,
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "util/timecodefunctions.h"
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
TimeRange::TimeRange(const Rational &in, const Rational &out)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "util/value.h"
|
||||
|
||||
namespace olive::core
|
||||
namespace olive::core::internal
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
// Pure C API test for oakcore/audioparams.h: no gtest, no C++ wrappers.
|
||||
// oakcore/audioparams.h includes oakcore/rational.h for the OakRational
|
||||
// handles crossing the boundary.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
|
||||
// Mirror of render/channellayout.h values, spelled out so this test only
|
||||
// includes the C API header.
|
||||
static const uint64_t k_layout_mono = 0x4;
|
||||
static const uint64_t k_layout_stereo = 0x3;
|
||||
static const uint64_t k_layout_2_1 = 0x103;
|
||||
static const uint64_t k_layout_5_point1 = 0x60F;
|
||||
static const uint64_t k_layout_7_point1 = 0x63F;
|
||||
|
||||
// Mirror of render/sampleformat.h enum values.
|
||||
static const int k_format_invalid = -1;
|
||||
static const int k_format_u8_p = 0;
|
||||
static const int k_format_f32_p = 4;
|
||||
static const int k_format_s16 = 7;
|
||||
static const int k_format_f64 = 11;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Default constructor: invalid parameters with footage defaults
|
||||
{
|
||||
OakAudioParams *p = oakcore_audioparams_create_invalid();
|
||||
assert(p != nullptr);
|
||||
assert(oakcore_audioparams_is_valid(p) == 0);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 0);
|
||||
assert(oakcore_audioparams_channel_layout(p) == 0);
|
||||
assert(oakcore_audioparams_channel_count(p) == 0);
|
||||
assert(oakcore_audioparams_format(p) == k_format_invalid);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 0);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 0);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
assert(oakcore_audioparams_stream_index(p) == 0);
|
||||
assert(oakcore_audioparams_duration(p) == 0);
|
||||
|
||||
OakRational *tb = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_is_null(tb) == 1);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
oakcore_audioparams_free(p);
|
||||
}
|
||||
|
||||
// Full constructor: 48000 Hz stereo s16
|
||||
OakAudioParams *p =
|
||||
oakcore_audioparams_create(48000, k_layout_stereo, k_format_s16);
|
||||
assert(p != nullptr);
|
||||
assert(oakcore_audioparams_is_valid(p) == 1);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 48000);
|
||||
assert(oakcore_audioparams_channel_layout(p) == k_layout_stereo);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
assert(oakcore_audioparams_format(p) == k_format_s16);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 2);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 16);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
assert(oakcore_audioparams_stream_index(p) == 0);
|
||||
assert(oakcore_audioparams_duration(p) == 0);
|
||||
|
||||
// Timebase defaults to 1/sample_rate
|
||||
{
|
||||
OakRational *tb = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_numerator(tb) == 1);
|
||||
assert(oakcore_rational_denominator(tb) == 48000);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
OakRational *srtb = oakcore_audioparams_sample_rate_as_time_base(p);
|
||||
assert(oakcore_rational_numerator(srtb) == 1);
|
||||
assert(oakcore_rational_denominator(srtb) == 48000);
|
||||
oakcore_rational_free(srtb);
|
||||
}
|
||||
|
||||
// Setters / getters
|
||||
oakcore_audioparams_set_sample_rate(p, 44100);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 44100);
|
||||
oakcore_audioparams_set_sample_rate(p, 48000);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 48000);
|
||||
|
||||
// Changing the layout recalculates the channel count
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_mono);
|
||||
assert(oakcore_audioparams_channel_layout(p) == k_layout_mono);
|
||||
assert(oakcore_audioparams_channel_count(p) == 1);
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_5_point1);
|
||||
assert(oakcore_audioparams_channel_count(p) == 6);
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_stereo);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
|
||||
{
|
||||
OakRational *tb = oakcore_rational_create_nd(1, 1000);
|
||||
oakcore_audioparams_set_time_base(p, tb);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
OakRational *got = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_numerator(got) == 1);
|
||||
assert(oakcore_rational_denominator(got) == 1000);
|
||||
oakcore_rational_free(got);
|
||||
|
||||
// Restore the default 1/48000 timebase
|
||||
tb = oakcore_rational_create_nd(1, 48000);
|
||||
oakcore_audioparams_set_time_base(p, tb);
|
||||
oakcore_rational_free(tb);
|
||||
}
|
||||
|
||||
oakcore_audioparams_set_format(p, k_format_f32_p);
|
||||
assert(oakcore_audioparams_format(p) == k_format_f32_p);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 4);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 32);
|
||||
oakcore_audioparams_set_format(p, k_format_s16);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 2);
|
||||
|
||||
oakcore_audioparams_set_enabled(p, 0);
|
||||
assert(oakcore_audioparams_enabled(p) == 0);
|
||||
oakcore_audioparams_set_enabled(p, 1);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
|
||||
oakcore_audioparams_set_stream_index(p, 3);
|
||||
assert(oakcore_audioparams_stream_index(p) == 3);
|
||||
oakcore_audioparams_set_stream_index(p, 0);
|
||||
|
||||
const int64_t big_duration = int64_t(1) << 40;
|
||||
oakcore_audioparams_set_duration(p, big_duration);
|
||||
assert(oakcore_audioparams_duration(p) == big_duration);
|
||||
oakcore_audioparams_set_duration(p, 0);
|
||||
|
||||
// Time/sample/byte conversions (48000 Hz, 2 channels, 2 bytes/sample)
|
||||
assert(oakcore_audioparams_time_to_samples(p, 1.0) == 48000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, 0.5) == 24000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, -1.0) == -48000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, 0.0) == 0);
|
||||
|
||||
assert(oakcore_audioparams_samples_to_bytes_per_channel(p, 100) == 200);
|
||||
assert(oakcore_audioparams_samples_to_bytes(p, 100) == 400);
|
||||
assert(oakcore_audioparams_samples_to_bytes(p, 0) == 0);
|
||||
|
||||
assert(oakcore_audioparams_time_to_bytes_per_channel(p, 1.0) == 96000);
|
||||
assert(oakcore_audioparams_time_to_bytes(p, 1.0) == 192000);
|
||||
|
||||
assert(oakcore_audioparams_bytes_to_samples(p, 400) == 100);
|
||||
assert(oakcore_audioparams_bytes_to_samples(p, 0) == 0);
|
||||
|
||||
// Rational-taking overloads
|
||||
{
|
||||
OakRational *half = oakcore_rational_create_nd(1, 2);
|
||||
assert(oakcore_audioparams_time_to_samples_rational(p, half) == 24000);
|
||||
assert(oakcore_audioparams_time_to_bytes_rational(p, half) == 96000);
|
||||
assert(oakcore_audioparams_time_to_bytes_per_channel_rational(p, half) ==
|
||||
48000);
|
||||
oakcore_rational_free(half);
|
||||
}
|
||||
|
||||
// Rational-returning conversions
|
||||
{
|
||||
OakRational *t = oakcore_audioparams_samples_to_time(p, 48000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_samples_to_time(p, 24000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 2);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_bytes_to_time(p, 192000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_bytes_per_channel_to_time(p, 96000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
}
|
||||
|
||||
// Copy + equality
|
||||
{
|
||||
OakAudioParams *copy = oakcore_audioparams_copy(p);
|
||||
assert(copy != nullptr);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 1);
|
||||
assert(oakcore_audioparams_equals(copy, copy) == 1);
|
||||
|
||||
oakcore_audioparams_set_sample_rate(copy, 96000);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 0);
|
||||
oakcore_audioparams_set_sample_rate(copy, 48000);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 1);
|
||||
|
||||
OakAudioParams *invalid = oakcore_audioparams_create_invalid();
|
||||
assert(oakcore_audioparams_equals(p, invalid) == 0);
|
||||
assert(oakcore_audioparams_equals(invalid, invalid) == 1);
|
||||
oakcore_audioparams_free(invalid);
|
||||
|
||||
oakcore_audioparams_free(copy);
|
||||
}
|
||||
|
||||
// Supported channel layouts / sample rates
|
||||
{
|
||||
assert(oakcore_audioparams_supported_channel_layout_count() == 5);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(0) == k_layout_mono);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(1) ==
|
||||
k_layout_stereo);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(2) == k_layout_2_1);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(3) ==
|
||||
k_layout_5_point1);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(4) ==
|
||||
k_layout_7_point1);
|
||||
// Out-of-range indices return 0
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(-1) == 0);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(5) == 0);
|
||||
|
||||
assert(oakcore_audioparams_supported_sample_rate_count() == 10);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(0) == 8000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(6) == 44100);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(7) == 48000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(9) == 96000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(-1) == 0);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(10) == 0);
|
||||
}
|
||||
|
||||
oakcore_audioparams_free(p);
|
||||
|
||||
std::printf("oakcore_audioparams_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/bezier.h"
|
||||
|
||||
static int double_eq(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < 1e-9;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Default constructor: everything zeroed
|
||||
OakBezier *b = oakcore_bezier_create();
|
||||
assert(b != NULL);
|
||||
assert(double_eq(oakcore_bezier_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_y(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(b), 0.0));
|
||||
|
||||
// Setters write through to the getters
|
||||
oakcore_bezier_set_x(b, 1.5);
|
||||
oakcore_bezier_set_y(b, -2.25);
|
||||
oakcore_bezier_set_cp1_x(b, 0.1);
|
||||
oakcore_bezier_set_cp1_y(b, 0.2);
|
||||
oakcore_bezier_set_cp2_x(b, 0.3);
|
||||
oakcore_bezier_set_cp2_y(b, 0.4);
|
||||
assert(double_eq(oakcore_bezier_x(b), 1.5));
|
||||
assert(double_eq(oakcore_bezier_y(b), -2.25));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(b), 0.1));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(b), 0.2));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(b), 0.3));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(b), 0.4));
|
||||
|
||||
// x/y constructor: control points stay zeroed
|
||||
OakBezier *xy = oakcore_bezier_create_xy(3.0, 4.0);
|
||||
assert(double_eq(oakcore_bezier_x(xy), 3.0));
|
||||
assert(double_eq(oakcore_bezier_y(xy), 4.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(xy), 0.0));
|
||||
|
||||
// Full constructor
|
||||
OakBezier *full =
|
||||
oakcore_bezier_create_full(1.0, 2.0, 0.25, 0.5, 0.75, 1.0);
|
||||
assert(double_eq(oakcore_bezier_x(full), 1.0));
|
||||
assert(double_eq(oakcore_bezier_y(full), 2.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(full), 0.25));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(full), 0.5));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(full), 0.75));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(full), 1.0));
|
||||
|
||||
// Copy: same values, independent storage
|
||||
OakBezier *dup = oakcore_bezier_copy(full);
|
||||
assert(double_eq(oakcore_bezier_x(dup), 1.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(dup), 1.0));
|
||||
oakcore_bezier_set_x(dup, 9.0);
|
||||
oakcore_bezier_set_cp1_y(dup, 8.0);
|
||||
assert(double_eq(oakcore_bezier_x(full), 1.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(full), 0.5));
|
||||
|
||||
// Quadratic evaluation: endpoints and x->t->y round trip
|
||||
assert(double_eq(oakcore_bezier_quadratic_tto_y(0.0, 0.5, 1.0, 0.0), 0.0));
|
||||
assert(double_eq(oakcore_bezier_quadratic_tto_y(0.0, 0.5, 1.0, 1.0), 1.0));
|
||||
{
|
||||
const double x = 0.3;
|
||||
const double t =
|
||||
oakcore_bezier_quadratic_xto_t(x, 0.0, 0.25, 1.0);
|
||||
const double y =
|
||||
oakcore_bezier_quadratic_tto_y(0.0, 0.75, 1.0, t);
|
||||
assert(t >= 0.0 && t <= 1.0);
|
||||
// xto_t solves on the x curve; feeding the same x back must hold
|
||||
assert(fabs(oakcore_bezier_quadratic_tto_y(0.0, 0.25, 1.0, t) - x) <
|
||||
1e-5);
|
||||
(void) y;
|
||||
}
|
||||
// xto_t clamps x into [a, c] instead of diverging
|
||||
{
|
||||
const double t_lo = oakcore_bezier_quadratic_xto_t(-5.0, 0.0, 0.5, 1.0);
|
||||
const double t_hi = oakcore_bezier_quadratic_xto_t(5.0, 0.0, 0.5, 1.0);
|
||||
assert(t_lo >= 0.0 && t_lo <= 1.0);
|
||||
assert(t_hi >= 0.0 && t_hi <= 1.0);
|
||||
}
|
||||
|
||||
// Cubic evaluation: endpoints and x->t->y round trip
|
||||
assert(double_eq(oakcore_bezier_cubic_tto_y(0.0, 0.25, 0.75, 1.0, 0.0),
|
||||
0.0));
|
||||
assert(double_eq(oakcore_bezier_cubic_tto_y(0.0, 0.25, 0.75, 1.0, 1.0),
|
||||
1.0));
|
||||
{
|
||||
const double x = 0.6;
|
||||
const double t =
|
||||
oakcore_bezier_cubic_xto_t(x, 0.0, 0.1, 0.9, 1.0);
|
||||
const double y =
|
||||
oakcore_bezier_cubic_tto_y(0.0, 0.8, 0.2, 1.0, t);
|
||||
assert(t >= 0.0 && t <= 1.0);
|
||||
assert(fabs(oakcore_bezier_cubic_tto_y(0.0, 0.1, 0.9, 1.0, t) - x) <
|
||||
1e-5);
|
||||
(void) y;
|
||||
}
|
||||
// xto_t clamps x into [a, d] instead of diverging
|
||||
{
|
||||
const double t_lo =
|
||||
oakcore_bezier_cubic_xto_t(-5.0, 0.0, 0.25, 0.75, 1.0);
|
||||
const double t_hi =
|
||||
oakcore_bezier_cubic_xto_t(5.0, 0.0, 0.25, 0.75, 1.0);
|
||||
assert(t_lo >= 0.0 && t_lo <= 1.0);
|
||||
assert(t_hi >= 0.0 && t_hi <= 1.0);
|
||||
}
|
||||
|
||||
// Ownership: every handle released exactly once
|
||||
oakcore_bezier_free(b);
|
||||
oakcore_bezier_free(xy);
|
||||
oakcore_bezier_free(full);
|
||||
oakcore_bezier_free(dup);
|
||||
oakcore_bezier_free(NULL);
|
||||
|
||||
printf("oakcore_bezier_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_color_test.cpp
|
||||
* @brief Pure C API test for the OakColor ABI
|
||||
*
|
||||
* Exercises every function of oakcore/color.h through the C boundary only:
|
||||
* no C++ wrapper, no test framework, just main() + assert().
|
||||
*/
|
||||
|
||||
#include "olive/core/oakcore/color.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* PixelFormat::Format values (render/pixelformat.h) */
|
||||
enum { PF_U8 = 0, PF_U10 = 1, PF_U16 = 2, PF_F16 = 3, PF_F32 = 4 };
|
||||
|
||||
static int feq(float a, float b)
|
||||
{
|
||||
return fabsf(a - b) < 1e-5f;
|
||||
}
|
||||
|
||||
static void test_create_and_channels(void)
|
||||
{
|
||||
/* Default construction: all channels zero */
|
||||
OakColor *c = oakcore_color_create();
|
||||
assert(c != NULL);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 0.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* RGBA construction + getters */
|
||||
c = oakcore_color_create_rgba(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 0.25f));
|
||||
assert(feq(oakcore_color_green(c), 0.5f));
|
||||
assert(feq(oakcore_color_blue(c), 0.75f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
|
||||
/* Setters, including out-of-gamut HDR values */
|
||||
oakcore_color_set_red(c, -0.5f);
|
||||
oakcore_color_set_green(c, 2.0f);
|
||||
oakcore_color_set_blue(c, 0.0f);
|
||||
oakcore_color_set_alpha(c, 0.125f);
|
||||
assert(feq(oakcore_color_red(c), -0.5f));
|
||||
assert(feq(oakcore_color_green(c), 2.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 0.125f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_copy_and_ownership(void)
|
||||
{
|
||||
OakColor *c = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
OakColor *copy = oakcore_color_copy(c);
|
||||
assert(copy != NULL);
|
||||
assert(feq(oakcore_color_red(copy), 0.1f));
|
||||
assert(feq(oakcore_color_green(copy), 0.2f));
|
||||
assert(feq(oakcore_color_blue(copy), 0.3f));
|
||||
assert(feq(oakcore_color_alpha(copy), 0.4f));
|
||||
|
||||
/* The copy is independent from the original */
|
||||
oakcore_color_set_red(copy, 0.9f);
|
||||
assert(feq(oakcore_color_red(copy), 0.9f));
|
||||
assert(feq(oakcore_color_red(c), 0.1f));
|
||||
|
||||
oakcore_color_free(copy);
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Releasing a NULL handle must be safe */
|
||||
oakcore_color_free(NULL);
|
||||
}
|
||||
|
||||
static void test_hsv(void)
|
||||
{
|
||||
/* Primary colors from HSV */
|
||||
OakColor *c = oakcore_color_from_hsv(0.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 1.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_from_hsv(120.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 1.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_from_hsv(240.0f, 1.0f, 0.5f);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* to_hsv of pure red and gray */
|
||||
c = oakcore_color_create_rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
float h = -1.0f, s = -1.0f, v = -1.0f;
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 1.0f));
|
||||
assert(feq(v, 1.0f));
|
||||
assert(feq(oakcore_color_hsv_hue(c), h));
|
||||
assert(feq(oakcore_color_hsv_saturation(c), s));
|
||||
assert(feq(oakcore_color_value(c), v));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 0.0f));
|
||||
assert(feq(v, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Roundtrip: color -> hsv -> color */
|
||||
c = oakcore_color_create_rgba(0.2f, 0.4f, 0.8f, 1.0f);
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 220.0f));
|
||||
assert(feq(s, 0.75f));
|
||||
assert(feq(v, 0.8f));
|
||||
OakColor *rt = oakcore_color_from_hsv(h, s, v);
|
||||
assert(feq(oakcore_color_red(rt), 0.2f));
|
||||
assert(feq(oakcore_color_green(rt), 0.4f));
|
||||
assert(feq(oakcore_color_blue(rt), 0.8f));
|
||||
oakcore_color_free(rt);
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_hsl(void)
|
||||
{
|
||||
/* Pure red: l = 0.5, s = 1, h = 0 */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
float h = -1.0f, s = -1.0f, l = -1.0f;
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 1.0f));
|
||||
assert(feq(l, 0.5f));
|
||||
assert(feq(oakcore_color_hsl_hue(c), h));
|
||||
assert(feq(oakcore_color_hsl_saturation(c), s));
|
||||
assert(feq(oakcore_color_lightness(c), l));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Gray: zero saturation */
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 0.0f));
|
||||
assert(feq(l, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Arbitrary color */
|
||||
c = oakcore_color_create_rgba(0.2f, 0.4f, 0.8f, 1.0f);
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 220.0f));
|
||||
assert(feq(s, 0.6f));
|
||||
assert(feq(l, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_data_access(void)
|
||||
{
|
||||
OakColor *c = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
|
||||
/* Const read access */
|
||||
const float *cd = oakcore_color_const_data(c);
|
||||
assert(cd != NULL);
|
||||
assert(feq(cd[0], 0.1f));
|
||||
assert(feq(cd[1], 0.2f));
|
||||
assert(feq(cd[2], 0.3f));
|
||||
assert(feq(cd[3], 0.4f));
|
||||
|
||||
/* Mutable write access */
|
||||
float *d = oakcore_color_data(c);
|
||||
assert(d != NULL);
|
||||
d[0] = 0.125f;
|
||||
d[3] = 1.0f;
|
||||
assert(feq(oakcore_color_red(c), 0.125f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_pixel_data(void)
|
||||
{
|
||||
char buf[16];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* u8 roundtrip, 4 channels */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_U8, 4);
|
||||
assert((uint8_t)buf[0] == 255);
|
||||
assert((uint8_t)buf[1] == 0);
|
||||
assert((uint8_t)buf[2] == 255);
|
||||
assert((uint8_t)buf[3] == 255);
|
||||
OakColor *back = oakcore_color_from_data(buf, PF_U8, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u8 with only 3 channels: alpha stays 0 */
|
||||
oakcore_color_to_data(c, buf, PF_U8, 3);
|
||||
back = oakcore_color_from_data(buf, PF_U8, 3);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 0.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u16 roundtrip */
|
||||
oakcore_color_to_data(c, buf, PF_U16, 4);
|
||||
assert(((uint16_t *)buf)[0] == 65535);
|
||||
assert(((uint16_t *)buf)[1] == 0);
|
||||
back = oakcore_color_from_data(buf, PF_U16, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* f32 roundtrip, exact */
|
||||
c = oakcore_color_create_rgba(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_F32, 4);
|
||||
assert(feq(((float *)buf)[0], 0.25f));
|
||||
assert(feq(((float *)buf)[3], 1.0f));
|
||||
back = oakcore_color_from_data(buf, PF_F32, 4);
|
||||
assert(feq(oakcore_color_red(back), 0.25f));
|
||||
assert(feq(oakcore_color_green(back), 0.5f));
|
||||
assert(feq(oakcore_color_blue(back), 0.75f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* f16 roundtrip: 1.0 = 0x3C00, 0.5 = 0x3800 in IEEE half */
|
||||
c = oakcore_color_create_rgba(1.0f, 0.5f, 0.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_F16, 4);
|
||||
assert(((uint16_t *)buf)[0] == 0x3C00);
|
||||
assert(((uint16_t *)buf)[1] == 0x3800);
|
||||
back = oakcore_color_from_data(buf, PF_F16, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.5f));
|
||||
assert(feq(oakcore_color_blue(back), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u10 packed 4-channel roundtrip: all ones packs to 0xFFFFFFFF */
|
||||
c = oakcore_color_create_rgba(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_U10, 4);
|
||||
assert(((uint32_t *)buf)[0] == 0xFFFFFFFFu);
|
||||
back = oakcore_color_from_data(buf, PF_U10, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 1.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_luminance(void)
|
||||
{
|
||||
/* (2r + b + 3g) / 6 */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c), 1.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c), 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.6f, 0.2f, 0.4f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c),
|
||||
(2.0f * 0.6f + 0.4f + 3.0f * 0.2f) / 6.0f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_math(void)
|
||||
{
|
||||
OakColor *a = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
OakColor *b = oakcore_color_create_rgba(0.4f, 0.3f, 0.2f, 0.1f);
|
||||
|
||||
/* Color +=/-= Color */
|
||||
oakcore_color_add_assign(a, b);
|
||||
assert(feq(oakcore_color_red(a), 0.5f));
|
||||
assert(feq(oakcore_color_green(a), 0.5f));
|
||||
assert(feq(oakcore_color_blue(a), 0.5f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.5f));
|
||||
oakcore_color_sub_assign(a, b);
|
||||
assert(feq(oakcore_color_red(a), 0.1f));
|
||||
assert(feq(oakcore_color_green(a), 0.2f));
|
||||
assert(feq(oakcore_color_blue(a), 0.3f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.4f));
|
||||
|
||||
/* Scalar assign operators (apply to all channels) */
|
||||
oakcore_color_add_scalar_assign(a, 1.0f);
|
||||
assert(feq(oakcore_color_red(a), 1.1f));
|
||||
assert(feq(oakcore_color_alpha(a), 1.4f));
|
||||
oakcore_color_sub_scalar_assign(a, 0.6f);
|
||||
assert(feq(oakcore_color_red(a), 0.5f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.8f));
|
||||
oakcore_color_mul_scalar_assign(a, 2.0f);
|
||||
assert(feq(oakcore_color_red(a), 1.0f));
|
||||
assert(feq(oakcore_color_green(a), 1.2f));
|
||||
assert(feq(oakcore_color_blue(a), 1.4f));
|
||||
assert(feq(oakcore_color_alpha(a), 1.6f));
|
||||
oakcore_color_div_scalar_assign(a, 4.0f);
|
||||
assert(feq(oakcore_color_red(a), 0.25f));
|
||||
assert(feq(oakcore_color_green(a), 0.3f));
|
||||
assert(feq(oakcore_color_blue(a), 0.35f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.4f));
|
||||
|
||||
oakcore_color_free(b);
|
||||
oakcore_color_free(a);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_create_and_channels();
|
||||
test_copy_and_ownership();
|
||||
test_hsv();
|
||||
test_hsl();
|
||||
test_data_access();
|
||||
test_pixel_data();
|
||||
test_luminance();
|
||||
test_math();
|
||||
|
||||
printf("oakcore_color_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/fractionutils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @file oakcore_fractionutils_test.cpp
|
||||
* @brief Pure C API test for the fraction utility functions
|
||||
*
|
||||
* Exercises every oakcore_fractionutils_* function directly, without the
|
||||
* C++ wrapper and without a test framework.
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
// reduce_fraction: greatest common divisor is divided out
|
||||
{
|
||||
int64_t num = 6, den = 9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 2 && den == 3);
|
||||
}
|
||||
|
||||
// reduce_fraction: the sign is normalized into the numerator
|
||||
{
|
||||
int64_t num = -6, den = 9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == -2 && den == 3);
|
||||
}
|
||||
{
|
||||
int64_t num = 6, den = -9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == -2 && den == 3);
|
||||
}
|
||||
|
||||
// reduce_fraction: a zero denominator is preserved, numerator zeroed
|
||||
{
|
||||
int64_t num = 42, den = 0;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 0 && den == 0);
|
||||
}
|
||||
|
||||
// reduce_fraction: a zero numerator reduces to 0/1
|
||||
{
|
||||
int64_t num = 0, den = 7;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 0 && den == 1);
|
||||
}
|
||||
|
||||
// reduce_fraction: exact reduction well within max
|
||||
{
|
||||
int64_t num = 1000000, den = 1000000;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, 100);
|
||||
assert(num == 1 && den == 1);
|
||||
}
|
||||
|
||||
// reduce_fraction: continued-fraction approximation respects max
|
||||
{
|
||||
int64_t num = 1048576, den = 1000000;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, 10000);
|
||||
assert(num > 0 && num <= 10000 && den > 0 && den <= 10000);
|
||||
const double approx = double(num) / double(den);
|
||||
assert(fabs(approx - 1.048576) < 0.001);
|
||||
}
|
||||
|
||||
// compare_fractions: three-way result -1 / 0 / 1
|
||||
assert(oakcore_fractionutils_compare_fractions(1, 3, 1, 2) == -1);
|
||||
assert(oakcore_fractionutils_compare_fractions(1, 2, 2, 4) == 0);
|
||||
assert(oakcore_fractionutils_compare_fractions(2, 3, 1, 2) == 1);
|
||||
|
||||
// compare_fractions: meaningless degenerate comparison returns INT_MIN
|
||||
assert(oakcore_fractionutils_compare_fractions(0, 0, 0, 5) == INT_MIN);
|
||||
|
||||
// rescale_rnd: exact division needs no rounding
|
||||
assert(oakcore_fractionutils_rescale_rnd(100, 3, 4,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 75);
|
||||
|
||||
// rescale_rnd: round to nearest, halfway cases away from zero
|
||||
assert(oakcore_fractionutils_rescale_rnd(5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(-5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == -3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(7, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 4);
|
||||
|
||||
// rescale_rnd: round toward positive infinity
|
||||
assert(oakcore_fractionutils_rescale_rnd(5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == 3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(-5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == -2);
|
||||
assert(oakcore_fractionutils_rescale_rnd(4, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == 2);
|
||||
|
||||
// rescale_rnd: a negative divisor is normalized into the multiplier
|
||||
assert(oakcore_fractionutils_rescale_rnd(10, 1, -2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == -5);
|
||||
|
||||
// rescale_rnd: large intermediates stay exact (128-bit path)
|
||||
assert(oakcore_fractionutils_rescale_rnd(INT64_MAX / 2, 4, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF)
|
||||
== INT64_MAX - 1);
|
||||
|
||||
printf("oakcore_fractionutils_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/rational.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// create_nd reduces and normalizes signs
|
||||
OakRational *r = oakcore_rational_create_nd(2, 4);
|
||||
assert(r != NULL);
|
||||
assert(oakcore_rational_numerator(r) == 1);
|
||||
assert(oakcore_rational_denominator(r) == 2);
|
||||
|
||||
OakRational *neg = oakcore_rational_create_nd(1, -2);
|
||||
assert(oakcore_rational_numerator(neg) == -1);
|
||||
assert(oakcore_rational_denominator(neg) == 2);
|
||||
oakcore_rational_free(neg);
|
||||
|
||||
// create defaults to n/1
|
||||
OakRational *five = oakcore_rational_create(5);
|
||||
assert(oakcore_rational_numerator(five) == 5);
|
||||
assert(oakcore_rational_denominator(five) == 1);
|
||||
oakcore_rational_free(five);
|
||||
|
||||
// NaN
|
||||
OakRational *nan = oakcore_rational_create_nan();
|
||||
assert(oakcore_rational_is_nan(nan) == 1);
|
||||
assert(oakcore_rational_is_null(nan) == 1);
|
||||
assert(isnan(oakcore_rational_to_double(nan)));
|
||||
oakcore_rational_free(nan);
|
||||
|
||||
// copy is deep
|
||||
OakRational *copy = oakcore_rational_copy(r);
|
||||
assert(copy != r);
|
||||
assert(oakcore_rational_compare(copy, r) == 0);
|
||||
oakcore_rational_add_assign(copy, r);
|
||||
assert(oakcore_rational_numerator(copy) == 1);
|
||||
assert(oakcore_rational_denominator(copy) == 1);
|
||||
assert(oakcore_rational_numerator(r) == 1);
|
||||
assert(oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(copy);
|
||||
|
||||
// to_double / to_string
|
||||
assert(fabs(oakcore_rational_to_double(r) - 0.5) < 1e-12);
|
||||
char buf[32];
|
||||
int needed = oakcore_rational_to_string(r, NULL, 0);
|
||||
assert(needed == 3);
|
||||
assert(oakcore_rational_to_string(r, buf, sizeof(buf)) == 3);
|
||||
assert(strcmp(buf, "1/2") == 0);
|
||||
assert(oakcore_rational_to_string(r, buf, 2) == 3); // truncated write
|
||||
assert(strcmp(buf, "1") == 0);
|
||||
|
||||
// from_double / from_string
|
||||
int ok = 0;
|
||||
OakRational *half = oakcore_rational_from_double(0.5, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_compare(half, r) == 0);
|
||||
oakcore_rational_free(half);
|
||||
|
||||
OakRational *bad = oakcore_rational_from_double(NAN, &ok);
|
||||
assert(ok == 0);
|
||||
assert(oakcore_rational_is_nan(bad));
|
||||
oakcore_rational_free(bad);
|
||||
|
||||
OakRational *parsed = oakcore_rational_from_string("3/4", &ok);
|
||||
assert(ok == 1);
|
||||
assert(fabs(oakcore_rational_to_double(parsed) - 0.75) < 1e-12);
|
||||
oakcore_rational_free(parsed);
|
||||
|
||||
OakRational *unparsed = oakcore_rational_from_string("1/2/3", &ok);
|
||||
assert(ok == 0);
|
||||
oakcore_rational_free(unparsed);
|
||||
|
||||
// flip
|
||||
OakRational *flip = oakcore_rational_flipped(r);
|
||||
assert(oakcore_rational_numerator(flip) == 2);
|
||||
assert(oakcore_rational_denominator(flip) == 1);
|
||||
oakcore_rational_flip(flip);
|
||||
assert(oakcore_rational_compare(flip, r) == 0);
|
||||
oakcore_rational_free(flip);
|
||||
|
||||
// arithmetic assignments
|
||||
OakRational *a = oakcore_rational_create_nd(1, 3);
|
||||
OakRational *b = oakcore_rational_create_nd(1, 6);
|
||||
oakcore_rational_add_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 2);
|
||||
oakcore_rational_sub_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 3);
|
||||
oakcore_rational_mul_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 18);
|
||||
oakcore_rational_div_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 3);
|
||||
|
||||
// compare ordering
|
||||
assert(oakcore_rational_compare(b, a) < 0);
|
||||
assert(oakcore_rational_compare(a, b) > 0);
|
||||
|
||||
oakcore_rational_free(a);
|
||||
oakcore_rational_free(b);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
printf("oakcore_rational_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/samplebuffer.h"
|
||||
|
||||
/* These functions are declared in oakcore/audioparams.h (a parallel
|
||||
* delivery); they are re-declared here so that this test only includes the
|
||||
* samplebuffer C header it is meant to exercise. */
|
||||
extern "C" {
|
||||
OakAudioParams *oakcore_audioparams_create(int sample_rate,
|
||||
uint64_t channel_layout,
|
||||
int format);
|
||||
void oakcore_audioparams_free(OakAudioParams *self);
|
||||
int oakcore_audioparams_sample_rate(const OakAudioParams *self);
|
||||
int oakcore_audioparams_channel_count(const OakAudioParams *self);
|
||||
}
|
||||
|
||||
/* Values mirror render/channellayout.h (k_channel_layout_stereo) and
|
||||
* render/sampleformat.h (SampleFormat::f32_p) on the library side. */
|
||||
#define SAMPLE_RATE 48000
|
||||
#define CHANNEL_LAYOUT_STEREO 0x3
|
||||
#define FORMAT_F32P 4
|
||||
|
||||
static int float_eq(float a, float b)
|
||||
{
|
||||
return fabsf(a - b) < 1e-6f;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* --- Default construction: empty, unallocated --- */
|
||||
OakSampleBuffer *def = oakcore_samplebuffer_create();
|
||||
assert(def != NULL);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
assert(oakcore_samplebuffer_sample_count(def) == 0);
|
||||
assert(oakcore_samplebuffer_channel_count(def) == 0);
|
||||
assert(oakcore_samplebuffer_data(def, 0) == NULL);
|
||||
|
||||
/* audio_params() hands back an owned copy of the (invalid) defaults */
|
||||
OakAudioParams *def_params = oakcore_samplebuffer_audio_params(def);
|
||||
assert(def_params != NULL);
|
||||
assert(oakcore_audioparams_sample_rate(def_params) == 0);
|
||||
oakcore_audioparams_free(def_params);
|
||||
|
||||
/* Operations that need an allocation warn and leave the buffer alone */
|
||||
oakcore_samplebuffer_allocate(def);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
oakcore_samplebuffer_silence(def);
|
||||
oakcore_samplebuffer_reverse(def);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
oakcore_samplebuffer_free(def);
|
||||
|
||||
/* --- Construction from audio params + sample count --- */
|
||||
OakAudioParams *stereo = oakcore_audioparams_create(
|
||||
SAMPLE_RATE, CHANNEL_LAYOUT_STEREO, FORMAT_F32P);
|
||||
assert(stereo != NULL);
|
||||
|
||||
OakSampleBuffer *buf = oakcore_samplebuffer_create_samples(stereo, 100);
|
||||
assert(oakcore_samplebuffer_is_allocated(buf) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(buf) == 100);
|
||||
assert(oakcore_samplebuffer_channel_count(buf) == 2);
|
||||
|
||||
/* A fresh allocation is silent */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(buf, ch);
|
||||
assert(d != NULL);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(d[i], 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/* audio_params() copy reflects the construction parameters */
|
||||
OakAudioParams *p = oakcore_samplebuffer_audio_params(buf);
|
||||
assert(oakcore_audioparams_sample_rate(p) == SAMPLE_RATE);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
oakcore_audioparams_free(p);
|
||||
|
||||
/* data() rejects out-of-range channels */
|
||||
assert(oakcore_samplebuffer_data(buf, -1) == NULL);
|
||||
assert(oakcore_samplebuffer_data(buf, 2) == NULL);
|
||||
|
||||
/* --- Construction from audio params + rational length (0.5s) --- */
|
||||
OakRational *half_sec = oakcore_rational_create_nd(1, 2);
|
||||
OakSampleBuffer *timed =
|
||||
oakcore_samplebuffer_create_length(stereo, half_sec);
|
||||
assert(oakcore_samplebuffer_is_allocated(timed) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(timed) == 24000);
|
||||
oakcore_rational_free(half_sec);
|
||||
oakcore_samplebuffer_free(timed);
|
||||
|
||||
/* --- Manual lifecycle: params + count + allocate, destroy, repeat --- */
|
||||
OakSampleBuffer *manual = oakcore_samplebuffer_create();
|
||||
oakcore_samplebuffer_set_audio_params(manual, stereo);
|
||||
oakcore_samplebuffer_set_sample_count(manual, 50);
|
||||
oakcore_samplebuffer_allocate(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(manual) == 50);
|
||||
assert(oakcore_samplebuffer_channel_count(manual) == 2);
|
||||
|
||||
oakcore_samplebuffer_destroy(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 0);
|
||||
assert(oakcore_samplebuffer_channel_count(manual) == 0);
|
||||
assert(oakcore_samplebuffer_data(manual, 0) == NULL);
|
||||
|
||||
oakcore_samplebuffer_set_sample_count(manual, 30);
|
||||
oakcore_samplebuffer_allocate(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(manual) == 30);
|
||||
oakcore_samplebuffer_free(manual);
|
||||
|
||||
/* set_sample_count via a rational length: 0.001s at 48kHz = 48 samples */
|
||||
OakSampleBuffer *manual2 = oakcore_samplebuffer_create();
|
||||
oakcore_samplebuffer_set_audio_params(manual2, stereo);
|
||||
OakRational *one_ms = oakcore_rational_create_nd(1, 1000);
|
||||
oakcore_samplebuffer_set_sample_count_length(manual2, one_ms);
|
||||
oakcore_rational_free(one_ms);
|
||||
oakcore_samplebuffer_allocate(manual2);
|
||||
assert(oakcore_samplebuffer_sample_count(manual2) == 48);
|
||||
oakcore_samplebuffer_free(manual2);
|
||||
|
||||
/* --- data() direct access, set(), to_raw_ptrs() --- */
|
||||
float *ch0 = oakcore_samplebuffer_data(buf, 0);
|
||||
float *ch1 = oakcore_samplebuffer_data(buf, 1);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ch0[i] = (float)i; /* ramp 0..99 on channel 0 */
|
||||
ch1[i] = (float)(i * 10); /* ramp 0..990 on channel 1 */
|
||||
}
|
||||
|
||||
const float ins[4] = { -1.0f, -2.0f, -3.0f, -4.0f };
|
||||
oakcore_samplebuffer_set(buf, 0, ins, 10, 4); /* write at offset 10 */
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(ch0[10 + k], ins[k]));
|
||||
}
|
||||
assert(float_eq(ch0[9], 9.0f)); /* neighbours untouched */
|
||||
assert(float_eq(ch0[14], 14.0f));
|
||||
|
||||
oakcore_samplebuffer_set(buf, 1, ins, 0, 4); /* write at offset 0 */
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(ch1[k], ins[k]));
|
||||
}
|
||||
for (int k = 0; k < 4; k++) { /* restore the channel 1 ramp */
|
||||
ch1[k] = (float)(k * 10);
|
||||
}
|
||||
|
||||
float *ptrs[2] = { NULL, NULL };
|
||||
oakcore_samplebuffer_to_raw_ptrs(buf, ptrs);
|
||||
assert(ptrs[0] == ch0);
|
||||
assert(ptrs[1] == ch1);
|
||||
ptrs[1][50] = 123.0f; /* writes through the raw pointer land in the buffer */
|
||||
assert(float_eq(oakcore_samplebuffer_data(buf, 1)[50], 123.0f));
|
||||
ch1[50] = 500.0f; /* restore ramp value (50 * 10) */
|
||||
|
||||
/* --- copy: an independent deep copy --- */
|
||||
OakSampleBuffer *cp = oakcore_samplebuffer_copy(buf);
|
||||
assert(oakcore_samplebuffer_is_allocated(cp) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(cp) == 100);
|
||||
assert(oakcore_samplebuffer_channel_count(cp) == 2);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 9.0f));
|
||||
|
||||
/* --- transform_volume (in place, all channels) --- */
|
||||
oakcore_samplebuffer_transform_volume(cp, 2.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 18.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 1)[9], 180.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(buf, 0)[9], 9.0f)); /* source keeps its data */
|
||||
|
||||
/* --- transform_volume_for_channel (in place, one channel) --- */
|
||||
oakcore_samplebuffer_transform_volume_for_channel(cp, 1, 0.5f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 1)[9], 90.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 18.0f)); /* other channel untouched */
|
||||
oakcore_samplebuffer_free(cp);
|
||||
|
||||
/* --- static-style transforms: input -> output --- */
|
||||
OakSampleBuffer *tin = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
OakSampleBuffer *tout = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(tin, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(tin, 1)[i] = (float)(100 + i);
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_transform_volume_to(0.5f, tin, tout);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 0)[i], (float)i * 0.5f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 1)[i],
|
||||
(float)(100 + i) * 0.5f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[i], (float)i)); /* input unchanged */
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence(tout);
|
||||
oakcore_samplebuffer_transform_volume_for_channel_to(1, 2.0f, tin, tout);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 1)[i],
|
||||
(float)(100 + i) * 2.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 0)[i], 0.0f)); /* channel 0 untouched */
|
||||
}
|
||||
|
||||
/* --- per-sample volume transforms --- */
|
||||
oakcore_samplebuffer_transform_volume_for_sample(tin, 3, 10.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[3], 30.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 1)[3], 1030.0f));
|
||||
|
||||
oakcore_samplebuffer_transform_volume_for_sample_on_channel(tin, 4, 0,
|
||||
100.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[4], 400.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 1)[4], 104.0f)); /* channel 1 untouched */
|
||||
oakcore_samplebuffer_free(tin);
|
||||
oakcore_samplebuffer_free(tout);
|
||||
|
||||
/* --- clamp() limits every channel to [-1, 1] --- */
|
||||
OakSampleBuffer *cl = oakcore_samplebuffer_create_samples(stereo, 4);
|
||||
oakcore_samplebuffer_data(cl, 0)[0] = 2.5f;
|
||||
oakcore_samplebuffer_data(cl, 0)[1] = -2.5f;
|
||||
oakcore_samplebuffer_data(cl, 1)[0] = 42.0f;
|
||||
oakcore_samplebuffer_data(cl, 1)[1] = 0.25f;
|
||||
oakcore_samplebuffer_clamp(cl);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 0)[0], 1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 0)[1], -1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 1)[0], 1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 1)[1], 0.25f));
|
||||
oakcore_samplebuffer_free(cl);
|
||||
|
||||
/* --- silence / silence_range / silence_bytes --- */
|
||||
OakSampleBuffer *si = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(si, ch)[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence_range(si, 2, 5); /* samples [2, 5) */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
assert(float_eq(d[1], 1.0f));
|
||||
assert(float_eq(d[2], 0.0f));
|
||||
assert(float_eq(d[4], 0.0f));
|
||||
assert(float_eq(d[5], 1.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence_bytes(si, 0, 2 * sizeof(float)); /* samples [0, 2) */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
assert(float_eq(d[0], 0.0f));
|
||||
assert(float_eq(d[1], 0.0f));
|
||||
assert(float_eq(d[5], 1.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence(si); /* everything */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(d[i], 0.0f));
|
||||
}
|
||||
}
|
||||
oakcore_samplebuffer_free(si);
|
||||
|
||||
/* --- reverse() flips every channel --- */
|
||||
OakSampleBuffer *rv = oakcore_samplebuffer_create_samples(stereo, 5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
oakcore_samplebuffer_data(rv, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(rv, 1)[i] = (float)(10 * i);
|
||||
}
|
||||
oakcore_samplebuffer_reverse(rv);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(rv, 0)[i], (float)(4 - i)));
|
||||
assert(float_eq(oakcore_samplebuffer_data(rv, 1)[i],
|
||||
(float)(10 * (4 - i))));
|
||||
}
|
||||
oakcore_samplebuffer_free(rv);
|
||||
|
||||
/* --- speed(2.0) halves the sample count, sampling the ramp exactly --- */
|
||||
OakSampleBuffer *sp = oakcore_samplebuffer_create_samples(stereo, 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
oakcore_samplebuffer_data(sp, 0)[i] = (float)i;
|
||||
}
|
||||
oakcore_samplebuffer_speed(sp, 2.0);
|
||||
assert(oakcore_samplebuffer_sample_count(sp) == 50);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(sp, 0)[i], (float)(2 * i)));
|
||||
}
|
||||
oakcore_samplebuffer_free(sp);
|
||||
|
||||
/* --- fast_set(): channel copy between buffers --- */
|
||||
OakSampleBuffer *fa = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
OakSampleBuffer *fb = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(fa, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(fa, 1)[i] = (float)(100 + i);
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_fast_set(fb, fa, 1, 0); /* fb channel 1 <- fa channel 0 */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 1)[i], (float)i));
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 0)[i], 0.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_fast_set(fb, fa, 0, -1); /* from == -1 mirrors to */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 0)[i], (float)i));
|
||||
}
|
||||
oakcore_samplebuffer_free(fa);
|
||||
oakcore_samplebuffer_free(fb);
|
||||
|
||||
/* --- rip_channel(): a new mono buffer with one channel's samples --- */
|
||||
OakSampleBuffer *rip = oakcore_samplebuffer_rip_channel(buf, 1);
|
||||
assert(oakcore_samplebuffer_is_allocated(rip) == 1);
|
||||
assert(oakcore_samplebuffer_channel_count(rip) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(rip) == 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(rip, 0)[i], (float)(i * 10)));
|
||||
}
|
||||
OakAudioParams *rip_params = oakcore_samplebuffer_audio_params(rip);
|
||||
assert(oakcore_audioparams_sample_rate(rip_params) == SAMPLE_RATE);
|
||||
assert(oakcore_audioparams_channel_count(rip_params) == 1);
|
||||
oakcore_audioparams_free(rip_params);
|
||||
oakcore_samplebuffer_free(rip);
|
||||
|
||||
/* --- rip_channel_vector(): query size, partial copy, full copy --- */
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, NULL, 0) == 100);
|
||||
|
||||
float partial[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, partial, 4) == 100);
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(partial[k], (float)(k * 10)));
|
||||
}
|
||||
|
||||
float full[100];
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, full, 100) == 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(full[i], (float)(i * 10)));
|
||||
}
|
||||
|
||||
/* --- Ownership: everything released --- */
|
||||
oakcore_samplebuffer_free(buf);
|
||||
oakcore_audioparams_free(stereo);
|
||||
|
||||
printf("oakcore_samplebuffer_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_stringutils_test.cpp
|
||||
* @brief Pure C API test for oakcore/stringutils.h
|
||||
*
|
||||
* Exercises every C function directly (no C++ wrapper, no test framework).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/stringutils.h"
|
||||
|
||||
static void test_split()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Basic split
|
||||
char **arr = oakcore_stringutils_split("a,b,c", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 3);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "b") == 0);
|
||||
assert(strcmp(arr[2], "c") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// No separator present: single element equal to the whole string
|
||||
arr = oakcore_stringutils_split("abc", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "abc") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Trailing separator yields a trailing empty string
|
||||
arr = oakcore_stringutils_split("a,", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 2);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Empty string yields one empty string
|
||||
arr = oakcore_stringutils_split("", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// NULL string behaves like an empty string
|
||||
arr = oakcore_stringutils_split(NULL, ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
}
|
||||
|
||||
static void test_split_regex()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Split on runs of digits
|
||||
char **arr = oakcore_stringutils_split_regex("a1b22c", "[0-9]+", &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 3);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "b") == 0);
|
||||
assert(strcmp(arr[2], "c") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Pattern that never matches yields the whole string
|
||||
arr = oakcore_stringutils_split_regex("abc", "[0-9]+", &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "abc") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Freeing NULL is safe
|
||||
oakcore_stringutils_free_string_array(NULL, 0);
|
||||
}
|
||||
|
||||
static void test_to_int()
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
// Base 10
|
||||
assert(oakcore_stringutils_to_int("42", 10, &ok) == 42);
|
||||
assert(ok == 1);
|
||||
|
||||
// Negative
|
||||
assert(oakcore_stringutils_to_int("-17", 10, &ok) == -17);
|
||||
assert(ok == 1);
|
||||
|
||||
// Base 16
|
||||
assert(oakcore_stringutils_to_int("ff", 16, &ok) == 255);
|
||||
assert(ok == 1);
|
||||
|
||||
// Parser error: returns 0 and reports failure
|
||||
assert(oakcore_stringutils_to_int("xyz", 10, &ok) == 0);
|
||||
assert(ok == 0);
|
||||
|
||||
// ok output parameter is optional
|
||||
assert(oakcore_stringutils_to_int("7", 10, NULL) == 7);
|
||||
}
|
||||
|
||||
/* Variadic forwarder to exercise oakcore_stringutils_format_v() */
|
||||
static int format_forward(char *buf, int buf_size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int r = oakcore_stringutils_format_v(buf, buf_size, fmt, args);
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void test_format()
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
// Format with mixed argument types
|
||||
const int needed = oakcore_stringutils_format(buf, sizeof(buf), "%d-%s-%.2f",
|
||||
42, "mid", 1.5);
|
||||
assert(needed == 11);
|
||||
assert(strcmp(buf, "42-mid-1.50") == 0);
|
||||
|
||||
// NULL buffer queries the required size
|
||||
assert(oakcore_stringutils_format(NULL, 0, "%d-%s-%.2f", 42, "mid",
|
||||
1.5) == needed);
|
||||
|
||||
// Too-small buffer truncates but still reports the full required size
|
||||
char small[5];
|
||||
const int needed2 =
|
||||
oakcore_stringutils_format(small, sizeof(small), "%s", "abcdefgh");
|
||||
assert(needed2 == 8);
|
||||
assert(strcmp(small, "abcd") == 0);
|
||||
|
||||
// va_list form produces identical results
|
||||
char buf2[64];
|
||||
const int needed3 = format_forward(buf2, sizeof(buf2), "%d-%s-%.2f", 42,
|
||||
"mid", 1.5);
|
||||
assert(needed3 == needed);
|
||||
assert(strcmp(buf2, buf) == 0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_split();
|
||||
test_split_regex();
|
||||
test_to_int();
|
||||
test_format();
|
||||
|
||||
printf("oakcore_stringutils_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/timecodefunctions.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
char buf[64];
|
||||
int ok = 0;
|
||||
OakRational *r = NULL;
|
||||
|
||||
/* Common timebases: 30 fps non-drop, 29.97 drop-frame, 60 fps */
|
||||
OakRational *tb_30 = oakcore_rational_create_nd(1, 30);
|
||||
OakRational *tb_df = oakcore_rational_create_nd(1001, 30000);
|
||||
OakRational *tb_60 = oakcore_rational_create_nd(1, 60);
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: drop-frame ( Olive's own test case ) */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create(1);
|
||||
const int size = oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, NULL, 0);
|
||||
assert(size == 11);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 11);
|
||||
assert(strcmp(buf, "00:00:01;00") == 0);
|
||||
/* Truncating buffer still reports the full required size */
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
6) == 11);
|
||||
assert(strcmp(buf, "00:00") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: invalid timebase */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create(0);
|
||||
OakRational *bizarre = oakcore_rational_create(156632219);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, bizarre, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 16);
|
||||
assert(strcmp(buf, "INVALID TIMEBASE") == 0);
|
||||
oakcore_rational_free(bizarre);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: non-drop, plus sign, negative */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 11);
|
||||
assert(strcmp(buf, "00:00:01:15") == 0);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 1, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "+00:00:01:15") == 0);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(-3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "-00:00:01:15") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: seconds / frames / milliseconds */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(123, 2); /* 61.5 s */
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_SECONDS, 0, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "00:01:01.500") == 0);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_FRAMES, 0, buf,
|
||||
sizeof(buf)) == 2);
|
||||
assert(strcmp(buf, "45") == 0);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_MILLISECONDS, 0, buf,
|
||||
sizeof(buf)) == 4);
|
||||
assert(strcmp(buf, "1500") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: non-drop */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01:15", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: drop-frame */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01;00", tb_df,
|
||||
OAK_TIMECODE_DISPLAY_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 1001 &&
|
||||
oakcore_rational_denominator(r) == 1000);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: seconds / frames / milliseconds */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01.5", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_SECONDS, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time("45", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_FRAMES, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time("1500", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_MILLISECONDS,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: invalid and NULL input */
|
||||
r = oakcore_timecode_timecode_to_time("abc", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 0);
|
||||
assert(oakcore_rational_numerator(r) == 0);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time(NULL, tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 0);
|
||||
oakcore_rational_free(r);
|
||||
/* ok pointer itself may be NULL */
|
||||
r = oakcore_timecode_timecode_to_time("45", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_FRAMES, NULL);
|
||||
assert(oakcore_rational_to_double(r) == 1.5);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_time_to_string */
|
||||
assert(oakcore_timecode_time_to_string(3661000, NULL, 0) == 8);
|
||||
assert(oakcore_timecode_time_to_string(3661000, buf, sizeof(buf)) == 8);
|
||||
assert(strcmp(buf, "01:01:01") == 0);
|
||||
assert(oakcore_timecode_time_to_string(0, buf, sizeof(buf)) == 8);
|
||||
assert(strcmp(buf, "00:00:00") == 0);
|
||||
|
||||
/* oakcore_timecode_snap_time_to_timebase */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(102, 100); /* 1.02 s */
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND);
|
||||
assert(oakcore_rational_numerator(r) == 31 &&
|
||||
oakcore_rational_denominator(r) == 30);
|
||||
oakcore_rational_free(r);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(151, 100); /* 1.51 s -> 45.3 frames */
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_FLOOR);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_CEIL);
|
||||
assert(oakcore_rational_numerator(r) == 23 &&
|
||||
oakcore_rational_denominator(r) == 15);
|
||||
oakcore_rational_free(r);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timestamp (Rational) */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND) == 45);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(151, 100); /* 45.3 frames */
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND) == 45);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_FLOOR) == 45);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_CEIL) == 46);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timestamp_d (double) */
|
||||
assert(oakcore_timecode_time_to_timestamp_d(1.5, tb_30,
|
||||
OAK_TIMECODE_ROUNDING_ROUND) ==
|
||||
45);
|
||||
assert(oakcore_timecode_time_to_timestamp_d(NAN, tb_30,
|
||||
OAK_TIMECODE_ROUNDING_ROUND) ==
|
||||
0);
|
||||
|
||||
/* oakcore_timecode_rescale_timestamp */
|
||||
assert(oakcore_timecode_rescale_timestamp(30, tb_30, tb_60) == 60);
|
||||
assert(oakcore_timecode_rescale_timestamp(30, tb_30, tb_30) == 30);
|
||||
{
|
||||
/* 1 * (1*4) / (5*3) = 0.2666... -> nearest is 0 */
|
||||
OakRational *src = oakcore_rational_create_nd(1, 5);
|
||||
OakRational *dst = oakcore_rational_create_nd(3, 4);
|
||||
assert(oakcore_timecode_rescale_timestamp(1, src, dst) == 0);
|
||||
/* ... but ceil rounds up to 1 */
|
||||
assert(oakcore_timecode_rescale_timestamp_ceil(1, src, dst) == 1);
|
||||
oakcore_rational_free(dst);
|
||||
oakcore_rational_free(src);
|
||||
}
|
||||
assert(oakcore_timecode_rescale_timestamp_ceil(30, tb_30, tb_60) == 60);
|
||||
|
||||
/* oakcore_timecode_timestamp_to_time */
|
||||
r = oakcore_timecode_timestamp_to_time(45, tb_30);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timestamp_to_time(0, tb_30);
|
||||
assert(oakcore_rational_to_double(r) == 0.0);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timebase_is_drop_frame */
|
||||
assert(oakcore_timecode_timebase_is_drop_frame(tb_df) == 1);
|
||||
assert(oakcore_timecode_timebase_is_drop_frame(tb_30) == 0);
|
||||
|
||||
oakcore_rational_free(tb_60);
|
||||
oakcore_rational_free(tb_df);
|
||||
oakcore_rational_free(tb_30);
|
||||
|
||||
printf("oakcore_timecodefunctions_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_timerange_test.cpp
|
||||
* @brief Smoke test for the OakTimeRange C ABI
|
||||
*
|
||||
* Pure C API test: no gtest, no C++ wrapper classes. Every allocated handle
|
||||
* is released with the matching free function.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/timerange.h"
|
||||
|
||||
static OakRational *mk_time(int n)
|
||||
{
|
||||
return oakcore_rational_create(n);
|
||||
}
|
||||
|
||||
static OakTimeRange *mk_range(int in, int out)
|
||||
{
|
||||
OakRational *ri = mk_time(in);
|
||||
OakRational *ro = mk_time(out);
|
||||
OakTimeRange *r = oakcore_timerange_create_io(ri, ro);
|
||||
oakcore_rational_free(ri);
|
||||
oakcore_rational_free(ro);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void expect_int_time(const OakRational *r, int expected)
|
||||
{
|
||||
assert(oakcore_rational_numerator(r) == expected);
|
||||
assert(oakcore_rational_denominator(r) == 1);
|
||||
}
|
||||
|
||||
static void expect_range(const OakTimeRange *r, int in, int out)
|
||||
{
|
||||
OakRational *ri = oakcore_timerange_in(r);
|
||||
OakRational *ro = oakcore_timerange_out(r);
|
||||
expect_int_time(ri, in);
|
||||
expect_int_time(ro, out);
|
||||
oakcore_rational_free(ri);
|
||||
oakcore_rational_free(ro);
|
||||
}
|
||||
|
||||
static void test_create_default(void)
|
||||
{
|
||||
OakTimeRange *r = oakcore_timerange_create();
|
||||
expect_range(r, 0, 0);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 0);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_create_and_getters(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(10, 20);
|
||||
expect_range(r, 10, 20);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_normalize_swaps_in_out(void)
|
||||
{
|
||||
// out earlier than in must be swapped
|
||||
OakTimeRange *r = mk_range(20, 10);
|
||||
expect_range(r, 10, 20);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_length_nan_on_extremes(void)
|
||||
{
|
||||
// RATIONAL_MIN/RATIONAL_MAX endpoints make the length NaN
|
||||
OakTimeRange *r = mk_range(INT_MIN, 0);
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
assert(oakcore_rational_is_nan(length) == 1);
|
||||
oakcore_rational_free(length);
|
||||
oakcore_timerange_free(r);
|
||||
|
||||
r = mk_range(0, INT_MAX);
|
||||
length = oakcore_timerange_length(r);
|
||||
assert(oakcore_rational_is_nan(length) == 1);
|
||||
oakcore_rational_free(length);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_copy_and_equal(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(3, 7);
|
||||
OakTimeRange *copy = oakcore_timerange_copy(r);
|
||||
|
||||
assert(oakcore_timerange_equal(r, copy) == 1);
|
||||
expect_range(copy, 3, 7);
|
||||
|
||||
// Mutating the copy must not affect the original (deep ownership)
|
||||
OakRational *nine = mk_time(9);
|
||||
oakcore_timerange_set_in(copy, nine);
|
||||
oakcore_rational_free(nine);
|
||||
|
||||
// set_in(9) with out==7 normalizes by swapping to (7, 9)
|
||||
expect_range(copy, 7, 9);
|
||||
assert(oakcore_timerange_equal(r, copy) == 0);
|
||||
expect_range(r, 3, 7);
|
||||
|
||||
oakcore_timerange_free(copy);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_setters(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *t = mk_time(5);
|
||||
|
||||
oakcore_timerange_set_in(r, t);
|
||||
expect_range(r, 5, 10);
|
||||
|
||||
oakcore_timerange_set_out(r, t);
|
||||
// set_out(5) with in==5 collapses to a zero-length range
|
||||
expect_range(r, 5, 5);
|
||||
|
||||
{
|
||||
OakRational *in = mk_time(30);
|
||||
OakRational *out = mk_time(20);
|
||||
oakcore_timerange_set_range(r, in, out);
|
||||
oakcore_rational_free(in);
|
||||
oakcore_rational_free(out);
|
||||
}
|
||||
// set_range also normalizes
|
||||
expect_range(r, 20, 30);
|
||||
|
||||
oakcore_rational_free(t);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_overlaps_with(void)
|
||||
{
|
||||
OakTimeRange *a = mk_range(0, 10);
|
||||
OakTimeRange *b = mk_range(10, 20);
|
||||
OakTimeRange *c = mk_range(5, 15);
|
||||
|
||||
// Touching ranges (0,10) vs (10,20): in_inclusive governs the
|
||||
// other.out-vs-self.in comparison, out_inclusive the other.in-vs-self.out
|
||||
// one, so the results are asymmetric for mixed flags
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 1, 1) == 1);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 0, 0) == 0);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 1, 0) == 0);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 0, 1) == 1);
|
||||
|
||||
// Genuinely overlapping ranges overlap under any inclusivity
|
||||
assert(oakcore_timerange_overlaps_with(a, c, 1, 1) == 1);
|
||||
assert(oakcore_timerange_overlaps_with(a, c, 0, 0) == 1);
|
||||
|
||||
oakcore_timerange_free(c);
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
}
|
||||
|
||||
static void test_contains_range(void)
|
||||
{
|
||||
OakTimeRange *outer = mk_range(0, 30);
|
||||
OakTimeRange *inner = mk_range(5, 10);
|
||||
OakTimeRange *same = mk_range(0, 30);
|
||||
OakTimeRange *partial = mk_range(5, 40);
|
||||
|
||||
assert(oakcore_timerange_contains_range(outer, inner, 1, 1) == 1);
|
||||
assert(oakcore_timerange_contains_range(outer, inner, 0, 0) == 1);
|
||||
|
||||
// Identical ranges: contained inclusively, not exclusively
|
||||
assert(oakcore_timerange_contains_range(outer, same, 1, 1) == 1);
|
||||
assert(oakcore_timerange_contains_range(outer, same, 0, 0) == 0);
|
||||
|
||||
assert(oakcore_timerange_contains_range(outer, partial, 1, 1) == 0);
|
||||
assert(oakcore_timerange_contains_range(inner, outer, 1, 1) == 0);
|
||||
|
||||
oakcore_timerange_free(partial);
|
||||
oakcore_timerange_free(same);
|
||||
oakcore_timerange_free(inner);
|
||||
oakcore_timerange_free(outer);
|
||||
}
|
||||
|
||||
static void test_contains_time(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *inside = mk_time(5);
|
||||
OakRational *edge_in = mk_time(0);
|
||||
OakRational *edge_out = mk_time(10);
|
||||
OakRational *outside = mk_time(-1);
|
||||
|
||||
// contains(time) is in-inclusive but out-exclusive
|
||||
assert(oakcore_timerange_contains_time(r, inside) == 1);
|
||||
assert(oakcore_timerange_contains_time(r, edge_in) == 1);
|
||||
assert(oakcore_timerange_contains_time(r, edge_out) == 0);
|
||||
assert(oakcore_timerange_contains_time(r, outside) == 0);
|
||||
|
||||
oakcore_rational_free(outside);
|
||||
oakcore_rational_free(edge_out);
|
||||
oakcore_rational_free(edge_in);
|
||||
oakcore_rational_free(inside);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_combine_and_intersect(void)
|
||||
{
|
||||
OakTimeRange *a = mk_range(0, 10);
|
||||
OakTimeRange *b = mk_range(20, 30);
|
||||
|
||||
OakTimeRange *combined = oakcore_timerange_combined(a, b);
|
||||
expect_range(combined, 0, 30);
|
||||
oakcore_timerange_free(combined);
|
||||
|
||||
combined = oakcore_timerange_combine(b, a);
|
||||
expect_range(combined, 0, 30);
|
||||
oakcore_timerange_free(combined);
|
||||
|
||||
// Disjoint ranges intersect to (20, 10), which normalize() swaps to (10, 20)
|
||||
OakTimeRange *intersected = oakcore_timerange_intersected(a, b);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
|
||||
a = mk_range(0, 20);
|
||||
b = mk_range(10, 30);
|
||||
|
||||
intersected = oakcore_timerange_intersected(a, b);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
intersected = oakcore_timerange_intersect(b, a);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
}
|
||||
|
||||
static void test_arithmetic(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *five = mk_time(5);
|
||||
|
||||
OakTimeRange *shifted = oakcore_timerange_add(r, five);
|
||||
expect_range(shifted, 5, 15);
|
||||
|
||||
OakTimeRange *back = oakcore_timerange_sub(shifted, five);
|
||||
expect_range(back, 0, 10);
|
||||
oakcore_timerange_free(back);
|
||||
oakcore_timerange_free(shifted);
|
||||
|
||||
oakcore_timerange_add_assign(r, five);
|
||||
expect_range(r, 5, 15);
|
||||
|
||||
oakcore_timerange_sub_assign(r, five);
|
||||
expect_range(r, 0, 10);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_rational_free(five);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_split(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
|
||||
// Size query, both directly and through the NULL-buffer form
|
||||
assert(oakcore_timerange_split_count(r, 4) == 3);
|
||||
assert(oakcore_timerange_split(r, 4, NULL, 0) == 3);
|
||||
|
||||
// Full split: (0,4), (4,8), (8,10)
|
||||
OakTimeRange *chunks[3];
|
||||
assert(oakcore_timerange_split(r, 4, chunks, 3) == 3);
|
||||
expect_range(chunks[0], 0, 4);
|
||||
expect_range(chunks[1], 4, 8);
|
||||
expect_range(chunks[2], 8, 10);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
oakcore_timerange_free(chunks[i]);
|
||||
}
|
||||
|
||||
// A too-small buffer receives a prefix, the return value is the total
|
||||
OakTimeRange *prefix[2];
|
||||
assert(oakcore_timerange_split(r, 4, prefix, 2) == 3);
|
||||
expect_range(prefix[0], 0, 4);
|
||||
expect_range(prefix[1], 4, 8);
|
||||
oakcore_timerange_free(prefix[0]);
|
||||
oakcore_timerange_free(prefix[1]);
|
||||
|
||||
// Zero-length range splits into exactly one chunk
|
||||
OakTimeRange *zero = mk_range(5, 5);
|
||||
assert(oakcore_timerange_split_count(zero, 4) == 1);
|
||||
OakTimeRange *single = NULL;
|
||||
assert(oakcore_timerange_split(zero, 4, &single, 1) == 1);
|
||||
expect_range(single, 5, 5);
|
||||
oakcore_timerange_free(single);
|
||||
oakcore_timerange_free(zero);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_create_default();
|
||||
test_create_and_getters();
|
||||
test_normalize_swaps_in_out();
|
||||
test_length_nan_on_extremes();
|
||||
test_copy_and_equal();
|
||||
test_setters();
|
||||
test_overlaps_with();
|
||||
test_contains_range();
|
||||
test_contains_time();
|
||||
test_combine_and_intersect();
|
||||
test_arithmetic();
|
||||
test_split();
|
||||
|
||||
printf("oakcore_timerange_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/rational.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool rational_to_from_string_test()
|
||||
{
|
||||
Rational r(1, 30);
|
||||
|
||||
std::string s = r.toString();
|
||||
|
||||
Rational r2 = Rational::fromString(s);
|
||||
|
||||
return r == r2;
|
||||
}
|
||||
|
||||
bool rational_to_from_string_test2()
|
||||
{
|
||||
Rational r(69, 420);
|
||||
|
||||
std::string s = r.toString();
|
||||
|
||||
Rational r2 = Rational::fromString(s);
|
||||
|
||||
return r == r2;
|
||||
}
|
||||
|
||||
bool rational_defaults()
|
||||
{
|
||||
// By default, rationals are valid 0/1
|
||||
Rational basic_constructor;
|
||||
|
||||
if (!basic_constructor.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (basic_constructor.isNaN()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rational_nan()
|
||||
{
|
||||
// Create a NaN with a 0 denominator
|
||||
Rational nan = Rational(0, 0);
|
||||
if (!nan.isNaN())
|
||||
return false;
|
||||
if (!nan.isNull())
|
||||
return false;
|
||||
|
||||
// Create a non-NaN with a zero numerator
|
||||
Rational zero_nonnan(0, 999);
|
||||
if (!zero_nonnan.isNull())
|
||||
return false;
|
||||
if (zero_nonnan.isNaN())
|
||||
return false;
|
||||
|
||||
// Create a non-NaN with a non-zero numerator
|
||||
Rational nonzer_nonnan(1, 30);
|
||||
if (nonzer_nonnan.isNull())
|
||||
return false;
|
||||
if (nonzer_nonnan.isNaN())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rational_nan_constant()
|
||||
{
|
||||
return Rational::NaN.isNaN();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("Rational::defaults", rational_defaults);
|
||||
t.add("Rational::NaN", rational_nan);
|
||||
t.add("Rational::NaN_constant", rational_nan_constant);
|
||||
t.add("Rational::toString/fromString", rational_to_from_string_test);
|
||||
t.add("Rational::toString/fromString2", rational_to_from_string_test2);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/stringutils.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool stringutils_format_test()
|
||||
{
|
||||
const char *expected = "Hello, world!";
|
||||
std::string f = StringUtils::format("%s, %s!", "Hello", "world");
|
||||
if (strcmp(f.c_str(), expected) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("StringUtils::format", stringutils_format_test);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/timecodefunctions.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool timecodefunctions_time_to_timecode_test()
|
||||
{
|
||||
Rational drop_frame_30(1001, 30000);
|
||||
|
||||
std::string timecode = Timecode::time_to_timecode(
|
||||
Rational(1), drop_frame_30, Timecode::kTimecodeDropFrame);
|
||||
if (strcmp(timecode.c_str(), "00:00:01;00") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timecodefunctions_time_to_timecode_test2()
|
||||
{
|
||||
Rational bizarre_timebase(156632219);
|
||||
|
||||
std::string timecode = Timecode::time_to_timecode(
|
||||
Rational(0), bizarre_timebase, Timecode::kTimecodeDropFrame);
|
||||
if (strcmp(timecode.c_str(), "INVALID TIMEBASE") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("Timecode::time_to_timecode",
|
||||
timecodefunctions_time_to_timecode_test);
|
||||
t.add("Timecode::time_to_timecode2",
|
||||
timecodefunctions_time_to_timecode_test2);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/timerange.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool timerangelist_remove_test()
|
||||
{
|
||||
TimeRangeList t;
|
||||
|
||||
t.insert(TimeRange(0, 30));
|
||||
t.remove(TimeRange(2, 5));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timerangelist_mergeadjacent_test()
|
||||
{
|
||||
TimeRangeList t;
|
||||
|
||||
// TimeRangeList should merge 1 and 3 together since they're adjacent
|
||||
t.insert(TimeRange(0, 6));
|
||||
t.insert(TimeRange(20, 30));
|
||||
t.insert(TimeRange(6, 10));
|
||||
|
||||
if (!(t.size() == 2)) {
|
||||
return false;
|
||||
}
|
||||
if (!(t.first() == TimeRange(20, 30))) {
|
||||
return false;
|
||||
}
|
||||
if (!(t.at(1) == TimeRange(0, 10))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TimeRangeList should ignore these because it's already contained
|
||||
TimeRangeList noop_test = t;
|
||||
|
||||
noop_test.insert(TimeRange(4, 7));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
noop_test.insert(TimeRange(0, 3));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
noop_test.insert(TimeRange(25, 30));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TimeRangeList should combine all these together
|
||||
TimeRangeList combine_test_no_overlap = t;
|
||||
combine_test_no_overlap.insert(TimeRange(10, 20));
|
||||
if (!(combine_test_no_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_no_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_in_overlap = t;
|
||||
combine_test_in_overlap.insert(TimeRange(9, 20));
|
||||
if (!(combine_test_in_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_in_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_out_overlap = t;
|
||||
combine_test_out_overlap.insert(TimeRange(10, 21));
|
||||
if (!(combine_test_out_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_out_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_both_overlap = t;
|
||||
combine_test_both_overlap.insert(TimeRange(9, 21));
|
||||
if (!(combine_test_both_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_both_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("TimeRangeList::remove", timerangelist_remove_test);
|
||||
t.add("TimeRangeList::merge_adjacent", timerangelist_mergeadjacent_test);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -198,8 +198,11 @@ else()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# Windows has no RPATH: ffmpeg_bridge.dll must sit next to the executable
|
||||
# Windows has no RPATH: shared libraries must sit next to the executable
|
||||
add_custom_command(TARGET olive-gtest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:ffmpeg_bridge> $<TARGET_FILE_DIR:olive-gtest>)
|
||||
add_custom_command(TARGET olive-gtest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:olivecore> $<TARGET_FILE_DIR:olive-gtest>)
|
||||
endif()
|
||||
|
||||
@@ -70,6 +70,14 @@ endif ()
|
||||
# copies, Linux install() into the same bin directory)
|
||||
add_dependencies(olive-editor olive-render-worker)
|
||||
|
||||
if (WIN32)
|
||||
# Windows has no RPATH: shared libraries must sit next to the executable
|
||||
add_custom_command(TARGET olive-render-worker POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:ffmpeg_bridge> $<TARGET_FILE_DIR:olive-render-worker>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olivecore> $<TARGET_FILE_DIR:olive-render-worker>
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Install the render worker alongside the editor on Linux.
|
||||
if (UNIX AND NOT APPLE)
|
||||
install(TARGETS olive-render-worker RUNTIME DESTINATION bin)
|
||||
|
||||
Reference in New Issue
Block a user