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

268 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
```c
#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 实现要点
```cpp
// 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 改造
```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/` 编译(可能只需要 `SampleBuffer``AudioParams` 类型)。
### 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` 中完成。 |