Files
oak-editor/docs/zh/modularization-plan/04-oliveaudio.md
T

8.8 KiB
Raw Blame History

liboliveaudio.so — 音频播放与处理

依赖libolivecore.so
外部依赖PortAudioQt::Core
包含源码app/audio/
当前状态:单体 OBJECT 库的一部分
改造难度(较简单)


1. 当前状态分析

app/audio/ 负责音频播放管理、音频处理管线和波形可视化数据。

组件 说明
audiomanager.h/cpp 音频播放管理器(单例),基于 PortAudio
audioprocessor.h/cpp 音频处理管线
audiovisualwaveform.h/cpp 音频波形数据(用于 UI 显示)
audiohybriddevice.h/cpp 音频混合设备

特点

  • 相对独立,不直接依赖 node/render/(通过回调或数据缓冲区交互)。
  • AudioManager 是单例,C API 中需要妥善处理单例的生命周期。
  • 音频数据量较小,实时性要求高。

2. C API 设计

2.1 头文件:c_api/include/olive/audio_api.h

#ifndef OLIVE_AUDIO_API_H
#define OLIVE_AUDIO_API_H

#ifdef __cplusplus
extern "C" {
#endif

#include "core_api.h"

#define OLIVE_AUDIO_API_VERSION 1

#ifdef OLIVE_BUILDING_AUDIO
#  define OLIVE_AUDIO_API __attribute__((visibility("default")))
#else
#  define OLIVE_AUDIO_API
#endif

/* ========== 类型前向声明 ========== */
typedef struct OliveAudioManager     OliveAudioManager;
typedef struct OliveAudioProcessor   OliveAudioProcessor;
typedef struct OliveAudioWaveform    OliveAudioWaveform;

/* ========== API 版本 ========== */
OLIVE_AUDIO_API int olive_audio_api_version(void);

/* ========== AudioManager(播放控制) ========== */
OLIVE_AUDIO_API OliveAudioManager* olive_audio_manager_get_instance(void);
OLIVE_AUDIO_API void olive_audio_manager_release_instance(OliveAudioManager* mgr);

OLIVE_AUDIO_API int olive_audio_manager_init(OliveAudioManager* mgr, OliveAudioParams params);
OLIVE_AUDIO_API void olive_audio_manager_shutdown(OliveAudioManager* mgr);

// 播放控制
OLIVE_AUDIO_API int olive_audio_manager_play(OliveAudioManager* mgr);
OLIVE_AUDIO_API int olive_audio_manager_pause(OliveAudioManager* mgr);
OLIVE_AUDIO_API int olive_audio_manager_stop(OliveAudioManager* mgr);
OLIVE_AUDIO_API int olive_audio_manager_is_playing(OliveAudioManager* mgr);

// 推入待播放的音频缓冲区(主进程渲染后推入)
OLIVE_AUDIO_API int olive_audio_manager_push_buffer(OliveAudioManager* mgr,
                                                       OliveSampleBuffer* buffer);

// 获取当前播放时间
OLIVE_AUDIO_API OliveRational olive_audio_manager_get_playback_time(OliveAudioManager* mgr);

/* ========== AudioProcessor(处理管线) ========== */
OLIVE_AUDIO_API OliveAudioProcessor* olive_audio_processor_create(OliveAudioParams params);
OLIVE_AUDIO_API void olive_audio_processor_destroy(OliveAudioProcessor* proc);

// 处理一帧音频(应用音量、声像等)
OLIVE_AUDIO_API int olive_audio_processor_process(OliveAudioProcessor* proc,
                                                     OliveSampleBuffer* input,
                                                     OliveSampleBuffer** output);

/* ========== AudioWaveform(波形数据) ========== */
OLIVE_AUDIO_API OliveAudioWaveform* olive_audio_waveform_create(OliveAudioParams params,
                                                                 OliveRational duration);
OLIVE_AUDIO_API void olive_audio_waveform_destroy(OliveAudioWaveform* wf);

// 从采样缓冲区生成波形数据
OLIVE_AUDIO_API int olive_audio_waveform_generate(OliveAudioWaveform* wf,
                                                   OliveSampleBuffer* buffer,
                                                   OliveRational start_time);

// 获取指定时间点的波形峰值(用于 UI 绘制)
OLIVE_AUDIO_API float olive_audio_waveform_get_peak(OliveAudioWaveform* wf,
                                                      OliveRational time,
                                                      int channel);

// 获取波形数据数组(用于批量绘制)
OLIVE_AUDIO_API const float* olive_audio_waveform_get_peaks(OliveAudioWaveform* wf,
                                                               int channel,
                                                               int* out_count);

#ifdef __cplusplus
}
#endif

#endif  // OLIVE_AUDIO_API_H

2.2 实现要点

// c_api/src/audio_api.cpp

#include "olive/audio_api.h"
#include "audio/audiomanager.h"
#include "audio/audioprocessor.h"
#include "audio/audiovisualwaveform.h"

struct OliveAudioManager {
    // AudioManager 是单例,此处不持有所有权,只作为句柄
    olive::AudioManager* impl;
};

extern "C" {

OliveAudioManager* olive_audio_manager_get_instance(void) {
    static OliveAudioManager mgr;
    mgr.impl = olive::AudioManager::instance();
    return &mgr;
}

void olive_audio_manager_release_instance(OliveAudioManager* mgr) {
    // 单例不在这里销毁
    (void)mgr;
}

int olive_audio_manager_init(OliveAudioManager* mgr, OliveAudioParams params) {
    if (!mgr || !mgr->impl) return OLIVE_ERROR_INVALID;
    try {
        olive::AudioParams cpp_params;
        cpp_params.set_sample_rate(params.sample_rate);
        cpp_params.set_channel_layout(params.channel_layout);
        // ... 转换 format ...
        mgr->impl->SetParameters(cpp_params);
        return OLIVE_OK;
    } catch (...) {
        return OLIVE_ERROR_GENERIC;
    }
}

int olive_audio_manager_push_buffer(OliveAudioManager* mgr, OliveSampleBuffer* buffer) {
    if (!mgr || !mgr->impl || !buffer) return OLIVE_ERROR_INVALID;
    try {
        mgr->impl->PushBuffer(buffer->impl);
        return OLIVE_OK;
    } catch (...) {
        return OLIVE_ERROR_GENERIC;
    }
}

// ... 其他函数类似封装 ...

}  // extern "C"

3. CMake 改造

# app/audio/CMakeLists.txt

set(AUDIO_INTERNAL_SOURCES
  audiomanager.cpp audiomanager.h
  audioprocessor.cpp audioprocessor.h
  audiovisualwaveform.cpp audiovisualwaveform.h
  audiohybriddevice.cpp audiohybriddevice.h
)

set(AUDIO_API_SOURCES
  ${CMAKE_SOURCE_DIR}/c_api/src/audio_api.cpp
)

add_library(oliveaudio SHARED
  ${AUDIO_INTERNAL_SOURCES}
  ${AUDIO_API_SOURCES}
)

target_compile_definitions(oliveaudio PRIVATE OLIVE_BUILDING_AUDIO)

target_include_directories(oliveaudio
  PRIVATE
    ${CMAKE_SOURCE_DIR}/app
    ${CMAKE_SOURCE_DIR}/c_api/include
  PUBLIC
    $<INSTALL_INTERFACE:include>
)

target_link_libraries(oliveaudio
  PUBLIC
    olivecore
    ${PORTAUDIO_LIBRARIES}
    Qt${QT_VERSION_MAJOR}::Core
)

set_target_properties(oliveaudio PROPERTIES
  CXX_VISIBILITY_PRESET hidden
  VISIBILITY_INLINES_HIDDEN YES
)

install(TARGETS oliveaudio DESTINATION lib)
install(FILES ${CMAKE_SOURCE_DIR}/c_api/include/olive/audio_api.h DESTINATION include/olive)

4. 小步快跑实施步骤

Step 0: 分析音频数据流(半天)

  • 梳理 AudioManager 的数据流:谁调用 PushBuffer?谁消费?
  • 确认 audio/node/ 的依赖情况(AudioProcessor 是否直接操作 Node?)。

验收标准:确认 audio/ 可以独立于 node/ 编译(可能只需要 SampleBufferAudioParams 类型)。

Step 1: 独立编译 liboliveaudio.so1 天)

  • app/audio/ 从主 OBJECT 库移出,单独构建为 oliveaudio SHARED
  • 确保 AudioManager 单例的初始化顺序正确(Qt 的 Q_GLOBAL_STATIC 或延迟初始化)。

验收标准liboliveaudio.so 编译成功。

Step 2: 最小 C API1 天)

  • 实现播放控制:init, play, pause, stop
  • 实现 push_buffer(关键接口,主进程渲染音频后推入播放队列)。

验收标准:可以通过 C API 初始化音频、播放一段静音缓冲区。

Step 3: 波形数据接口(1 天)

  • 实现 olive_audio_waveform_create/generate/get_peak
  • 此接口供 UI 层调用以绘制音频波形。

验收标准:给定一个 OliveSampleBuffer*,可以生成波形并查询任意时间点的峰值。

Step 4: 处理器接口(1 天)

  • 实现 olive_audio_processor_create/process
  • 用于节点图中的音频处理链(音量、声像)。

5. 风险与回退

风险 对策
AudioManager 单例在动态库卸载后仍被引用 主进程退出前先停止播放并释放 AudioManager,再卸载动态库。
PortAudio 回调线程与主线程的交互 C API 中 push_buffer 是线程安全的(内部用 QMutex 保护队列),C API 调用者无需额外同步。
实时音频延迟要求 C API 不增加额外拷贝:push_buffer 内部直接传递 SampleBuffer 的共享指针。
音频处理需要节点图信息 AudioProcessor 的参数(如音量值)通过 C API 直接设置,不涉及节点图遍历。节点图到音频参数的映射在 libolivenode.so 中完成。