refactor(config,audio): merge config into oakcommon, split oakaudio
- config moves into oakcommon as ConfigStore + oakcommon_config_* C API (INI storage, typed entries, error-handler injection); node and render call sites keep OAK_CONFIG() macro shape via a local shim that forwards to the C API; transition config stubs removed - oakaudio: de-Qt all six classes, C ABI in include/audio with refcounted handles (processor/manager/waveform/levelmeter/sync, 48 functions); PreviewAudioDevice moved in from render; recording goes through oakcodec encoder; waveform extract uses probe + ffmpeg_bridge decode (decode_audio needs M8 task system) - fix re_sum_samples min/max init bug (values clamped to 0 for same-sign ranges) - every C API function has positive + error-path tests; suites: oakcommon 193, oakaudio 36, oaknode 96, oakrender 42, oakcodec 18
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_ERROR_H
|
||||
#define OAK_EDITOR_AUDIO_ERROR_H
|
||||
|
||||
/**
|
||||
* @brief Current ABI version stamped into every oakaudio handle.
|
||||
*
|
||||
* Bump whenever a handle layout or the semantics of any exported function
|
||||
* change incompatibly. Consumers should compare a handle's abi_version
|
||||
* field against the value they were compiled with before dereferencing
|
||||
* ctx.
|
||||
*/
|
||||
#define OAKAUDIO_ABI_VERSION 1
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(OAKAUDIO_BUILD)
|
||||
#define OAKAUDIO_API __declspec(dllexport)
|
||||
#else
|
||||
#define OAKAUDIO_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define OAKAUDIO_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Status and error codes shared by all oakaudio C API families.
|
||||
*
|
||||
* Return-code convention (mirrors engine/include/oakengine/init.h):
|
||||
* 0 (OAKAUDIO_OK) on success, a negative OAKAUDIO_E_* error code on
|
||||
* failure. String getters return the required buffer size in bytes
|
||||
* (including the terminating NUL) as a non-negative value instead.
|
||||
*/
|
||||
#define OAKAUDIO_OK 0 /**< Success. */
|
||||
#define OAKAUDIO_E_INVALID (-1) /**< NULL handle or invalid argument. */
|
||||
#define OAKAUDIO_E_STATE (-2) /**< Call not valid in the current state. */
|
||||
#define OAKAUDIO_E_FAILED (-3) /**< The underlying operation failed. */
|
||||
#define OAKAUDIO_E_NOT_FOUND (-4) /**< Index out of range / entry not found. */
|
||||
#define OAKAUDIO_E_NOMEM (-5) /**< Allocation failed. */
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_ERROR_H
|
||||
@@ -0,0 +1,73 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_LEVELMETER_H
|
||||
#define OAK_EDITOR_AUDIO_LEVELMETER_H
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file levelmeter.h
|
||||
* @brief C ABI for the oakaudio level meter (olive::AudioLevelMeter):
|
||||
* stateless peak/RMS/VU/LUFS analysis of planar float audio.
|
||||
*/
|
||||
|
||||
/** Per-channel analysis results. dB fields floor at -200. */
|
||||
typedef struct oakaudio_channel_stats {
|
||||
double peak_linear;
|
||||
double peak_db;
|
||||
double rms_linear;
|
||||
double rms_db;
|
||||
double vu_db;
|
||||
} oakaudio_channel_stats;
|
||||
|
||||
/** Buffer-wide summary. */
|
||||
typedef struct oakaudio_meter_stats {
|
||||
double max_peak_linear;
|
||||
double integrated_lufs; /**< BS.1770-compatible unit (no K-weighting). */
|
||||
int silence; /**< 1 when the buffer is (near-)silent. */
|
||||
} oakaudio_meter_stats;
|
||||
|
||||
/**
|
||||
* @brief Analyze a planar float buffer.
|
||||
*
|
||||
* @param planar Per-channel float planes.
|
||||
* @param channel_count Number of channels (> 0).
|
||||
* @param frame_count Frames per channel (>= 0).
|
||||
* @param channels Receives per-channel stats; may be NULL.
|
||||
* @param channels_capacity Capacity of `channels` (must be >=
|
||||
* channel_count when channels is non-NULL).
|
||||
* @param summary Receives the buffer-wide summary; may be NULL.
|
||||
* @return OAKAUDIO_OK or OAKAUDIO_E_INVALID.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_levelmeter_analyze(const float *const *planar,
|
||||
int channel_count, int frame_count,
|
||||
oakaudio_channel_stats *channels, int channels_capacity,
|
||||
oakaudio_meter_stats *summary);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_LEVELMETER_H
|
||||
@@ -0,0 +1,176 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_MANAGER_H
|
||||
#define OAK_EDITOR_AUDIO_MANAGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "codec/encoder.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file manager.h
|
||||
* @brief C ABI for the oakaudio PortAudio output/input manager
|
||||
* (olive::AudioManager singleton).
|
||||
*
|
||||
* OakAudioManager uses the standard handle layout (see oakcommon's
|
||||
* common/handle.h) but with singleton semantics: ctx points to the
|
||||
* process-wide instance created by oakaudio_manager_create_instance(), so
|
||||
* addref() and release() are intentionally no-ops and never destroy
|
||||
* anything (mirrors oakcommon's OakCurrent). abi_version is always
|
||||
* OAKAUDIO_ABI_VERSION.
|
||||
*
|
||||
* Device indices are PortAudio PaDeviceIndex values (-1 = paNoDevice).
|
||||
* Sample formats are olive::core::SampleFormat::Format values.
|
||||
*/
|
||||
typedef struct OakAudioManager {
|
||||
void *ctx; /**< Opaque pointer to the singleton object. */
|
||||
void (*addref)(void *ctx); /**< No-op (singleton). */
|
||||
void (*release)(void *ctx); /**< No-op (singleton). */
|
||||
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
|
||||
} OakAudioManager;
|
||||
|
||||
/**
|
||||
* @brief Create the process-wide AudioManager (no-op when it exists).
|
||||
*
|
||||
* Initializes PortAudio and picks the configured/default devices.
|
||||
*
|
||||
* @return OAKAUDIO_OK or OAKAUDIO_E_NOMEM.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_create_instance(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy the process-wide AudioManager (no-op when absent).
|
||||
*/
|
||||
OAKAUDIO_API void oakaudio_manager_destroy_instance(void);
|
||||
|
||||
/**
|
||||
* @brief Return a handle to the process-wide AudioManager.
|
||||
*
|
||||
* The returned handle is borrowed; addref/release are no-ops. When no
|
||||
* instance exists the handle is empty (ctx == NULL) and all functions
|
||||
* report OAKAUDIO_E_STATE.
|
||||
*/
|
||||
OAKAUDIO_API OakAudioManager oakaudio_manager_instance(void);
|
||||
|
||||
/**
|
||||
* @brief Release a manager handle. No-op (singleton), safe on NULL/empty.
|
||||
*/
|
||||
OAKAUDIO_API void oakaudio_manager_free(OakAudioManager *self);
|
||||
|
||||
/**
|
||||
* @brief Bytes between output-notify pulses (0 disables).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_set_output_notify_interval(
|
||||
OakAudioManager self, int64_t bytes);
|
||||
|
||||
/**
|
||||
* @brief Push a block of samples to the output device, opening/restarting
|
||||
* the stream when the params changed.
|
||||
*
|
||||
* @param rate/layout/format Stream params (ffmpeg-style layout mask,
|
||||
* SampleFormat::Format int).
|
||||
* @param samples Packed samples in the given format.
|
||||
* @param samples_size Byte count of `samples`.
|
||||
* @param error_buf/error_buf_size Optional human-readable failure detail.
|
||||
* @return OAKAUDIO_OK, OAKAUDIO_E_INVALID, OAKAUDIO_E_STATE (no output
|
||||
* device), or OAKAUDIO_E_FAILED (PortAudio error, see error_buf).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_push_to_output(OakAudioManager self,
|
||||
int rate, uint64_t layout, int format,
|
||||
const char *samples, int64_t samples_size,
|
||||
char *error_buf, int error_buf_size);
|
||||
|
||||
OAKAUDIO_API int oakaudio_manager_clear_buffered_output(OakAudioManager self);
|
||||
OAKAUDIO_API int oakaudio_manager_stop_output(OakAudioManager self);
|
||||
|
||||
/**
|
||||
* @brief Seconds of audio consumed by the output device since the last
|
||||
* reset, compensated for output latency; negative when no stream
|
||||
* is running.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_seconds(OakAudioManager self, double *out);
|
||||
|
||||
OAKAUDIO_API int oakaudio_manager_reset_output_clock(OakAudioManager self);
|
||||
|
||||
/**
|
||||
* @brief Current output device index, paNoDevice (-1), or a negative
|
||||
* OAKAUDIO_E_* code.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_get_output_device(OakAudioManager self);
|
||||
OAKAUDIO_API int oakaudio_manager_set_output_device(OakAudioManager self,
|
||||
int device);
|
||||
OAKAUDIO_API int oakaudio_manager_get_input_device(OakAudioManager self);
|
||||
OAKAUDIO_API int oakaudio_manager_set_input_device(OakAudioManager self,
|
||||
int device);
|
||||
|
||||
/**
|
||||
* @brief Close the output stream and re-initialize PortAudio.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_hard_reset(OakAudioManager self);
|
||||
|
||||
/**
|
||||
* @brief Start recording the input device to a file via the oakcodec
|
||||
* encoder C ABI.
|
||||
*
|
||||
* `params` must describe an audio-enabled encoding; the input stream is
|
||||
* always captured as interleaved 32-bit float (the only format the
|
||||
* oakcodec encoder write path accepts).
|
||||
*
|
||||
* @return OAKAUDIO_OK, OAKAUDIO_E_STATE (no input device), or
|
||||
* OAKAUDIO_E_FAILED (see error_buf).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_start_recording(OakAudioManager self,
|
||||
const oakcodec_encoding_params *params,
|
||||
char *error_buf, int error_buf_size);
|
||||
|
||||
OAKAUDIO_API int oakaudio_manager_stop_recording(OakAudioManager self);
|
||||
|
||||
/**
|
||||
* @brief Device index named by the configuration ("AudioOutput" /
|
||||
* "AudioInput"), or the default device when unset/unmatched.
|
||||
* Static: valid without an instance (PortAudio must be initialized
|
||||
* by an instance first; returns paNoDevice otherwise).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_find_config_device_by_name_s(
|
||||
int is_output_device);
|
||||
|
||||
/**
|
||||
* @brief Device index whose name matches `name` exactly (empty name
|
||||
* matches nothing, falls through to the default device).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_manager_find_device_by_name_s(const char *name,
|
||||
int is_output_device);
|
||||
|
||||
/**
|
||||
* @brief Number of live oakaudio reference-counted objects (leak check).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_debug_alive_count(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_MANAGER_H
|
||||
@@ -0,0 +1,131 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_PROCESSOR_H
|
||||
#define OAK_EDITOR_AUDIO_PROCESSOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file processor.h
|
||||
* @brief C ABI for the oakaudio real-time resampler/format converter
|
||||
* (olive::AudioProcessor).
|
||||
*
|
||||
* OakAudioProcessor follows the neutral by-value handle convention (see
|
||||
* oakcommon's common/handle.h): oakaudio_processor_init() returns a handle
|
||||
* whose underlying object has reference count 1, the addref and release
|
||||
* function pointers adjust that count atomically (release destroys the
|
||||
* object at zero), and abi_version is always OAKAUDIO_ABI_VERSION.
|
||||
* Functions that only use a handle take it BY VALUE; an empty handle
|
||||
* (ctx == NULL) is reported as OAKAUDIO_E_INVALID.
|
||||
*
|
||||
* Sample formats are passed as ints matching the
|
||||
* olive::core::SampleFormat::Format enum values (invalid = -1, u8_p = 0,
|
||||
* s16_p, s32_p, s64_p, f32_p, f64_p, u8, s16, s32, s64, f32, f64,
|
||||
* count). Channel layouts are ffmpeg-style channel masks.
|
||||
*/
|
||||
typedef struct OakAudioProcessor {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
|
||||
} OakAudioProcessor;
|
||||
|
||||
/** oakaudio_processor_convert() delivers planar 32-bit float output. */
|
||||
#define OAKAUDIO_PROCESSOR_OUTPUT_FORMAT 4 /**< SampleFormat::f32_p. */
|
||||
|
||||
/**
|
||||
* @brief Create a closed audio processor (count 1).
|
||||
*
|
||||
* @return Handle with reference count 1; ctx is NULL on allocation
|
||||
* failure.
|
||||
*/
|
||||
OAKAUDIO_API OakAudioProcessor oakaudio_processor_init(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a processor.
|
||||
*
|
||||
* Convenience wrapper around self->release(self->ctx); nulls self->ctx.
|
||||
* No-op when self is NULL or self->ctx is NULL.
|
||||
*/
|
||||
OAKAUDIO_API void oakaudio_processor_free(OakAudioProcessor *self);
|
||||
|
||||
/**
|
||||
* @brief Open the resampling/format-conversion graph.
|
||||
*
|
||||
* out_format is accepted for interface completeness but the conversion
|
||||
* output is always planar 32-bit float (see
|
||||
* OAKAUDIO_PROCESSOR_OUTPUT_FORMAT); passing any other format returns
|
||||
* OAKAUDIO_E_INVALID. A channel layout mask of 0 falls back to the
|
||||
* default layout for the channel count (stereo when unknown), matching
|
||||
* the C++ implementation.
|
||||
*
|
||||
* @param speed Tempo factor (1.0 = unchanged).
|
||||
* @return OAKAUDIO_OK, OAKAUDIO_E_STATE when already open,
|
||||
* OAKAUDIO_E_INVALID for bad arguments, or OAKAUDIO_E_FAILED when
|
||||
* the filter graph could not be created.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_processor_open(OakAudioProcessor self,
|
||||
int in_rate, uint64_t in_layout, int in_format,
|
||||
int out_rate, uint64_t out_layout, int out_format, double speed);
|
||||
|
||||
/**
|
||||
* @brief Close the graph (safe when closed; self must be non-empty).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_processor_close(OakAudioProcessor self);
|
||||
|
||||
/**
|
||||
* @brief 1 when open, 0 when closed, OAKAUDIO_E_INVALID for empty handle.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_processor_is_open(OakAudioProcessor self);
|
||||
|
||||
/**
|
||||
* @brief Push planar float input and pull converted output.
|
||||
*
|
||||
* @param in_planar Per-channel float input planes (in channel count);
|
||||
* NULL with in_frame_count == 0 only pulls pending output.
|
||||
* @param in_frame_count Frames per input channel.
|
||||
* @param out_planar Per-channel float output planes (out channel count);
|
||||
* NULL to discard/pull nothing (returns 0).
|
||||
* @param out_capacity_frames Capacity of each output plane in frames.
|
||||
* @return Number of output frames written (>= 0), or a negative
|
||||
* OAKAUDIO_E_* code. Output is clamped to out_capacity_frames;
|
||||
* remaining frames stay queued in the graph.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_processor_convert(OakAudioProcessor self,
|
||||
const float *const *in_planar, int in_frame_count,
|
||||
float *const *out_planar, int out_capacity_frames);
|
||||
|
||||
/**
|
||||
* @brief Signal end-of-input to the graph (flushes internal delay).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_processor_flush(OakAudioProcessor self);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_PROCESSOR_H
|
||||
@@ -0,0 +1,132 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_SYNC_H
|
||||
#define OAK_EDITOR_AUDIO_SYNC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file sync.h
|
||||
* @brief C ABI for the oakaudio synchronization helpers
|
||||
* (olive::AudioSynchronizer and olive::AudioWaveformSync):
|
||||
* stateless source-time placement and envelope-correlation offset
|
||||
* estimation.
|
||||
*/
|
||||
|
||||
/** Result of an offset estimation. */
|
||||
typedef struct oakaudio_offset_result {
|
||||
int64_t offset_samples;
|
||||
double confidence; /**< 0..1 correlation score. */
|
||||
int valid; /**< 1 when an estimate was found. */
|
||||
} oakaudio_offset_result;
|
||||
|
||||
/** Result of a stretch-plus-offset estimation. */
|
||||
typedef struct oakaudio_stretch_offset_result {
|
||||
double rate; /**< Playback rate aligning the candidate (> 1 = speed up). */
|
||||
int64_t offset_samples;
|
||||
double confidence;
|
||||
int valid;
|
||||
} oakaudio_stretch_offset_result;
|
||||
|
||||
/**
|
||||
* @brief Per-window RMS envelope of a planar float buffer (static).
|
||||
*
|
||||
* @return Number of envelope windows (>= 0) or a negative OAKAUDIO_E_*
|
||||
* code. When out is NULL or too small, the required window count
|
||||
* is returned and nothing is written.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_sync_extract_rms_envelope(
|
||||
const float *const *planar, int channel_count, int frame_count,
|
||||
uint64_t window_samples, double *out, int capacity);
|
||||
|
||||
/**
|
||||
* @brief Estimate the candidate's offset against the reference by
|
||||
* normalized cross-correlation of RMS envelopes.
|
||||
*
|
||||
* @param reference_valid/candidate_valid Optional per-window validity
|
||||
* masks (NULL = all windows valid; when non-NULL the length must
|
||||
* match the corresponding envelope length).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_sync_estimate_envelope_offset(
|
||||
const double *reference, int reference_len,
|
||||
const double *candidate, int candidate_len,
|
||||
const uint8_t *reference_valid, const uint8_t *candidate_valid,
|
||||
uint64_t window_samples, int64_t max_offset_windows,
|
||||
oakaudio_offset_result *out);
|
||||
|
||||
/**
|
||||
* @brief Estimate a playback-rate change plus offset aligning the
|
||||
* candidate to the reference.
|
||||
*
|
||||
* The candidate envelope is resampled at each rate in
|
||||
* [min_rate, max_rate] (step rate_step) and correlated against the
|
||||
* reference. O(rates * lags * overlap); bound max_offset_windows.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_sync_estimate_stretch_and_offset(
|
||||
const double *reference, int reference_len,
|
||||
const double *candidate, int candidate_len,
|
||||
const uint8_t *reference_valid, const uint8_t *candidate_valid,
|
||||
uint64_t window_samples, int64_t max_offset_windows,
|
||||
double min_rate, double max_rate, double rate_step,
|
||||
oakaudio_stretch_offset_result *out);
|
||||
|
||||
/** One clip's source-time metadata (rational seconds). */
|
||||
typedef struct oakaudio_source_clip {
|
||||
int64_t source_start_time_num;
|
||||
int64_t source_start_time_den;
|
||||
int64_t media_in_num;
|
||||
int64_t media_in_den;
|
||||
int has_source_start_time;
|
||||
} oakaudio_source_clip;
|
||||
|
||||
/**
|
||||
* @brief Place the candidate on the timeline so its source time aligns
|
||||
* with the reference clip.
|
||||
*
|
||||
* @param reference_timeline_in_num/den Reference clip's timeline in point.
|
||||
* @param out_num/out_den Receive the candidate's timeline in point.
|
||||
* @param out_valid Receives 1 when placement succeeded.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_sync_place_by_source_time(
|
||||
const oakaudio_source_clip *reference,
|
||||
const oakaudio_source_clip *candidate,
|
||||
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
|
||||
int64_t *out_num, int64_t *out_den, int *out_valid);
|
||||
|
||||
/**
|
||||
* @brief Timeline placement from a measured waveform offset.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_sync_place_by_waveform_offset(
|
||||
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
|
||||
int64_t candidate_offset_samples, int sample_rate,
|
||||
int64_t *out_num, int64_t *out_den, int *out_valid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_SYNC_H
|
||||
@@ -0,0 +1,179 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_AUDIO_WAVEFORM_H
|
||||
#define OAK_EDITOR_AUDIO_WAVEFORM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file waveform.h
|
||||
* @brief C ABI for the oakaudio visual waveform store
|
||||
* (olive::AudioVisualWaveform) and whole-file waveform extraction.
|
||||
*
|
||||
* OakAudioWaveform follows the neutral by-value handle convention (see
|
||||
* oakcommon's common/handle.h). Times are rationals as (num, den) pairs
|
||||
* of int64_t in seconds; den must be non-zero.
|
||||
*
|
||||
* Summaries are stored as channel-interleaved min/max pairs: point p of
|
||||
* channel c lives at pairs[p * channel_count + c]. This matches the
|
||||
* on-disk/cache layout of the engine's waveform data (min/max float
|
||||
* pairs), so the extraction output is drop-in compatible.
|
||||
*/
|
||||
|
||||
/** One summarized waveform point of one channel. */
|
||||
typedef struct oakaudio_min_max {
|
||||
float min;
|
||||
float max;
|
||||
} oakaudio_min_max;
|
||||
|
||||
typedef struct OakAudioWaveform {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
|
||||
} OakAudioWaveform;
|
||||
|
||||
/**
|
||||
* @brief Create an empty waveform (count 1, channel count 0).
|
||||
*/
|
||||
OAKAUDIO_API OakAudioWaveform oakaudio_waveform_init(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference. No-op on NULL/empty handle.
|
||||
*/
|
||||
OAKAUDIO_API void oakaudio_waveform_free(OakAudioWaveform *self);
|
||||
|
||||
/**
|
||||
* @brief Channel count, or a negative OAKAUDIO_E_* code.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_get_channel_count(OakAudioWaveform self);
|
||||
OAKAUDIO_API int oakaudio_waveform_set_channel_count(OakAudioWaveform self,
|
||||
int channels);
|
||||
|
||||
/**
|
||||
* @brief Waveform length in seconds as a rational pair.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_length(OakAudioWaveform self,
|
||||
int64_t *num, int64_t *den);
|
||||
|
||||
/**
|
||||
* @brief Write planar float samples into the waveform at `start` seconds,
|
||||
* expanding it if necessary.
|
||||
*
|
||||
* @param planar Per-channel float planes; channel count is taken from the
|
||||
* waveform (set it first with oakaudio_waveform_set_channel_count).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_overwrite_samples(OakAudioWaveform self,
|
||||
const float *const *planar, int frame_count, int sample_rate,
|
||||
int64_t start_num, int64_t start_den);
|
||||
|
||||
/**
|
||||
* @brief Copy summarized data from another waveform over this one.
|
||||
*
|
||||
* @param dest_num/dest_den Where in `self` the sums start being written.
|
||||
* @param offset_num/offset_den Where in `src` reading starts.
|
||||
* @param length_num/length_den Maximum amount to copy; 0/1 = all of src.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_overwrite_sums(OakAudioWaveform self,
|
||||
OakAudioWaveform src,
|
||||
int64_t dest_num, int64_t dest_den,
|
||||
int64_t offset_num, int64_t offset_den,
|
||||
int64_t length_num, int64_t length_den);
|
||||
|
||||
OAKAUDIO_API int oakaudio_waveform_overwrite_silence(OakAudioWaveform self,
|
||||
int64_t start_num, int64_t start_den,
|
||||
int64_t length_num, int64_t length_den);
|
||||
|
||||
/**
|
||||
* @brief Drop `length` seconds from the front (negative prepends silence).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_trim_in(OakAudioWaveform self,
|
||||
int64_t length_num, int64_t length_den);
|
||||
|
||||
OAKAUDIO_API int oakaudio_waveform_resize(OakAudioWaveform self,
|
||||
int64_t length_num, int64_t length_den);
|
||||
|
||||
OAKAUDIO_API int oakaudio_waveform_trim_range(OakAudioWaveform self,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t length_num, int64_t length_den);
|
||||
|
||||
/**
|
||||
* @brief Summarized min/max pairs covering [start, start+length).
|
||||
*
|
||||
* @param out_pairs Receives points * channel_count channel-interleaved
|
||||
* pairs; may be NULL to query the point count.
|
||||
* @param capacity_points Capacity of out_pairs in points.
|
||||
* @return Number of points (>= 0), or a negative OAKAUDIO_E_* code.
|
||||
* When out_pairs is NULL or too small the required count is
|
||||
* returned and nothing is written.
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_get_summary(OakAudioWaveform self,
|
||||
int64_t start_num, int64_t start_den,
|
||||
int64_t length_num, int64_t length_den,
|
||||
oakaudio_min_max *out_pairs, int capacity_points);
|
||||
|
||||
/**
|
||||
* @brief Min/max of `length` samples starting at `start_index` for every
|
||||
* channel (static, no handle).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_sum_samples_s(const float *const *planar,
|
||||
int channel_count, int start_index, int length,
|
||||
oakaudio_min_max *out);
|
||||
|
||||
/**
|
||||
* @brief Re-summarize channel-interleaved pairs into one point per
|
||||
* channel (static, no handle).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_re_sum_s(const oakaudio_min_max *in,
|
||||
int nb_entries, int nb_channels, oakaudio_min_max *out);
|
||||
|
||||
/**
|
||||
* @brief Extract a whole-file waveform summary from a media file through
|
||||
* the oakcodec decoder C ABI.
|
||||
*
|
||||
* Decodes `filename`'s audio stream `stream_index` (index within the
|
||||
* file's audio stream list) and reduces it to channel-interleaved
|
||||
* min/max pairs, one point per `samples_per_point` source samples.
|
||||
*
|
||||
* @param out_pairs Receives the pairs; may be NULL to query the size.
|
||||
* @param capacity_points Capacity of out_pairs in points.
|
||||
* @param out_channel_count Receives the channel count (may be NULL).
|
||||
* @return Number of points (>= 0); when out_pairs is NULL or too small,
|
||||
* the required count is returned and nothing is written.
|
||||
* Negative OAKAUDIO_E_* code on failure
|
||||
* (OAKAUDIO_E_NOT_FOUND when the file/stream does not exist).
|
||||
*/
|
||||
OAKAUDIO_API int oakaudio_waveform_extract(const char *filename,
|
||||
int stream_index, int samples_per_point,
|
||||
oakaudio_min_max *out_pairs, int capacity_points,
|
||||
int *out_channel_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_AUDIO_WAVEFORM_H
|
||||
@@ -0,0 +1,189 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - 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 OAK_EDITOR_COMMON_CONFIG_H
|
||||
#define OAK_EDITOR_COMMON_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief De-Qt application configuration store, C ABI
|
||||
* (M1-oakcommon.md §2.1, extended for the real consumer surface)
|
||||
*
|
||||
* oakcommon_config is a process-wide singleton key/value store (the de-Qt
|
||||
* replacement for engine/config/config.h's QSettings/QVariant wrapper).
|
||||
* Per the config-wave ruling it is NOT wrapped in the refcounted-handle
|
||||
* convention of common/handle.h: there is exactly one store per process,
|
||||
* so the family is a plain set of functions over that singleton (same
|
||||
* singleton precedent as OakCurrent).
|
||||
*
|
||||
* Keys follow the frozen (group, key) convention of §2.1 and keep the
|
||||
* QSettings INI shape: pass group == NULL (or "") for a top-level key,
|
||||
* otherwise the entry is stored under an INI [group] section and
|
||||
* addressed as "group/key" internally.
|
||||
*
|
||||
* Values are typed (string / int64 / double / bool). Rational settings
|
||||
* are stored as strings in the "num/den" form used by
|
||||
* oakcore_rational_to_string(). Typed getters take a fallback which is
|
||||
* returned when the key is absent or has a different type (§2.1 special
|
||||
* convention: they return values, not error codes).
|
||||
*
|
||||
* Persistence is an INI file at
|
||||
* <FileFunctions::get_configuration_location()>/config.ini. The store
|
||||
* starts up with compiled-in defaults; oakcommon_config_load() re-reads
|
||||
* the file (a missing file is not an error) and oakcommon_config_save()
|
||||
* writes it. The OAK_CONFIG_DIR environment override honored by
|
||||
* get_configuration_location() also redirects this file (tests/tooling).
|
||||
*
|
||||
* NOTE (behavior change): the old Qt implementation persisted to
|
||||
* config.xml (engine XML) — and on macOS QSettings used a plist — so
|
||||
* previously saved settings do NOT carry over; the first run starts from
|
||||
* the compiled-in defaults.
|
||||
*/
|
||||
|
||||
typedef enum OakCommonConfigEntryType {
|
||||
OAKCOMMON_CONFIG_ENTRY_NONE = 0, /**< No entry / null type. */
|
||||
OAKCOMMON_CONFIG_ENTRY_STRING = 1,
|
||||
OAKCOMMON_CONFIG_ENTRY_INT = 2,
|
||||
OAKCOMMON_CONFIG_ENTRY_DOUBLE = 3,
|
||||
OAKCOMMON_CONFIG_ENTRY_BOOL = 4
|
||||
} OakCommonConfigEntryType;
|
||||
|
||||
/**
|
||||
* @brief Handler for configuration errors that should be shown to the user
|
||||
*
|
||||
* The engine layer cannot show dialogs itself. The UI registers a handler
|
||||
* (e.g. QMessageBox-based) at startup; without one, errors go to stderr.
|
||||
* Same injection pattern as the codec task-submit callback.
|
||||
*/
|
||||
typedef void (*OakCommonConfigErrorHandler)(const char *title,
|
||||
const char *message,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Resets the store to compiled-in defaults and loads config.ini
|
||||
*
|
||||
* A missing file leaves the defaults in place and returns OAKCOMMON_OK.
|
||||
* Malformed lines are skipped. An unreadable existing file is reported
|
||||
* through the error handler and returns OAKCOMMON_E_FAILED.
|
||||
*/
|
||||
int oakcommon_config_load(void);
|
||||
|
||||
/**
|
||||
* @brief Writes the current store to config.ini (via a temp file + rename)
|
||||
*
|
||||
* On failure the error handler is invoked and OAKCOMMON_E_FAILED is
|
||||
* returned.
|
||||
*/
|
||||
int oakcommon_config_save(void);
|
||||
|
||||
/**
|
||||
* @brief Resets the store to compiled-in defaults (drops custom keys)
|
||||
*/
|
||||
int oakcommon_config_reset_defaults(void);
|
||||
|
||||
/**
|
||||
* @brief Sets a string entry (§2.1)
|
||||
*
|
||||
* A new key is created as OAKCOMMON_CONFIG_ENTRY_STRING. Setting an
|
||||
* existing typed (INT/DOUBLE/BOOL) entry parses the string into its
|
||||
* declared type; an unparseable value returns OAKCOMMON_E_STATE and
|
||||
* leaves the entry unchanged.
|
||||
*/
|
||||
void oakcommon_config_set(const char *group, const char *key,
|
||||
const char *value_utf8);
|
||||
|
||||
/**
|
||||
* @brief Reads an entry as a string, two-stage buffer (§2.1)
|
||||
*
|
||||
* Numeric/bool entries are formatted (bools as "true"/"false", doubles
|
||||
* with %g).
|
||||
*
|
||||
* @return Required buffer size in bytes (including the terminating NUL),
|
||||
* or a negative OAKCOMMON_E_* error code (OAKCOMMON_E_NOT_FOUND when the
|
||||
* key is absent).
|
||||
*/
|
||||
int oakcommon_config_get(const char *group, const char *key, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Reads an INT entry as int (§2.1)
|
||||
*
|
||||
* @return The stored value, or `fallback` when the key is absent or has
|
||||
* a different type.
|
||||
*/
|
||||
int oakcommon_config_get_int(const char *group, const char *key,
|
||||
int fallback);
|
||||
|
||||
/**
|
||||
* @brief Reads a DOUBLE entry (§2.1), fallback semantics as get_int
|
||||
*/
|
||||
double oakcommon_config_get_double(const char *group, const char *key,
|
||||
double fallback);
|
||||
|
||||
/**
|
||||
* @brief Sets an INT entry (32-bit, §2.1)
|
||||
*/
|
||||
void oakcommon_config_set_int(const char *group, const char *key, int v);
|
||||
|
||||
/**
|
||||
* @brief INT entry as int64 (extension for channel-layout style values)
|
||||
*/
|
||||
int64_t oakcommon_config_get_int64(const char *group, const char *key,
|
||||
int64_t fallback);
|
||||
void oakcommon_config_set_int64(const char *group, const char *key,
|
||||
int64_t v);
|
||||
|
||||
/**
|
||||
* @brief BOOL entry as int 0/1 (extension), fallback semantics as get_int
|
||||
*/
|
||||
int oakcommon_config_get_bool(const char *group, const char *key,
|
||||
int fallback);
|
||||
void oakcommon_config_set_bool(const char *group, const char *key, int v);
|
||||
|
||||
/**
|
||||
* @brief Sets a DOUBLE entry (extension)
|
||||
*/
|
||||
void oakcommon_config_set_double(const char *group, const char *key,
|
||||
double v);
|
||||
|
||||
/**
|
||||
* @brief Returns the OakCommonConfigEntryType of a key, or a negative
|
||||
* OAKCOMMON_E_* error (OAKCOMMON_E_NOT_FOUND when the key is absent)
|
||||
*/
|
||||
int oakcommon_config_entry_type(const char *group, const char *key);
|
||||
|
||||
/**
|
||||
* @brief Registers (or clears, with NULL) the error handler
|
||||
*/
|
||||
int oakcommon_config_set_error_handler(OakCommonConfigErrorHandler handler,
|
||||
void *userdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_COMMON_CONFIG_H
|
||||
Reference in New Issue
Block a user