HarmonyOS开发音频效果:均衡器、低音增强与虚拟环绕声实战
一、背景与动机
你有没有注意到,同一个耳机在不同手机上听同一首歌,效果可能天差地别?有的手机低音浑厚有力,有的则干瘪单薄;有的手机看视频环绕感十足,有的就像从罐子里传出来的声音。这背后的秘密,就是音频效果处理。
音频效果就像给声音加滤镜。就像修图软件有亮度、对比度、饱和度调节一样,音频效果有均衡器(调节各频段音量)、低音增强(让低频更有冲击力)、虚拟环绕声(模拟多声道空间感)等。这些效果叠加在一起,就构成了音频效果链。
为什么需要了解音频效果?
- 音乐播放器:用户期望能自定义 EQ,调节出自己喜欢的音色
- 视频播放:虚拟环绕声让手机也能有影院般的沉浸感
- 游戏音频:低音增强让爆炸声更有冲击力,3D 音效让定位更精准
- 语音通话:降噪、回声消除让通话更清晰
- 品牌差异化:好的音频调校是产品的重要卖点
今天咱们就来深入 HarmonyOS 的音频效果体系。
二、核心原理
2.1 音频效果框架总览
HarmonyOS 的音频效果处理框架由以下几层构成:
flowchart TB
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
A[应用层] --> B[AudioEffect API]
B --> C[音频效果链]
C --> D[均衡器 Equalizer]
C --> E[低音增强 BassBoost]
C --> F[虚拟环绕声 VirtualSurround]
C --> G[降噪 NoiseSuppression]
C --> H[回声消除 AEC]
D --> D1[频段增益调节]
D --> D2[预设模式切换]
E --> E1[低频增强强度]
F --> F1[空间模拟算法]
G --> G1[背景噪声过滤]
H --> H1[回声信号消除]
I[AudioRenderer] --> C
C --> J[音频输出设备]
class A primary
class B info
class C warning
class D primary
class E purple
class F info
class G error
class H error
class I primary
class J primary
2.2 均衡器(Equalizer)原理
均衡器是音频效果中最核心的工具。它的工作原理是将音频信号按频率分成多个频段,然后分别调节每个频段的增益(音量大小)。
打个比方:均衡器就像一个调色板。低频(Bass)是暖色调,中频(Mid)是中间色调,高频(Treble)是冷色调。你可以让暖色调更浓(增强低音),让冷色调更亮(增强高音),调出你喜欢的"声音色彩"。
典型频段划分:
| 频段 | 频率范围 | 听感描述 | 典型应用 |
|---|---|---|---|
| 超低频 | 20-60 Hz | 震撼感、轰鸣 | 电影音效、电子乐 |
| 低频 | 60-250 Hz | 厚度、力度 | 鼓声、贝斯 |
| 中低频 | 250-500 Hz | 温暖感 | 人声基频 |
| 中频 | 500-2000 Hz | 清晰度 | 语音、主旋律 |
| 中高频 | 2000-4000 Hz | 亮度 | 弦乐、钢琴 |
| 高频 | 4000-8000 Hz | 空气感 | 镲片、齿音 |
| 超高频 | 8000-20000 Hz | 细节、泛音 | 环境声、空间感 |
2.3 音频效果链
多个音频效果可以串联起来,形成效果链。信号从源头流经每个效果处理器,最终输出到设备:
flowchart LR
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
A[原始音频] --> B[均衡器]
B --> C[低音增强]
C --> D[虚拟环绕声]
D --> E[输出]
class A primary
class B info
class C warning
class D purple
class E primary
效果链顺序很重要:先均衡再增强低音,和先增强低音再均衡,效果可能完全不同。一般推荐:均衡器 → 低音增强 → 虚拟环绕声。
三、代码实战
3.1 均衡器控制面板
实现一个完整的均衡器,支持频段调节和预设切换:
import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 均衡器频段信息
interface BandInfo {
centerFreq: number; // 中心频率 Hz
bandIndex: number; // 频段索引
gain: number; // 当前增益 dB
minGain: number; // 最小增益
maxGain: number; // 最大增益
}
// 均衡器预设
interface EQPreset {
name: string;
gains: number[]; // 各频段增益值
}
@Entry
@Component
struct EqualizerPanel {
// 均衡器效果实例
private equalizer: audio.AudioEffectEqualizer | null = null;
// 音频渲染器(效果需要绑定到渲染器)
private audioRenderer: audio.AudioRenderer | null = null;
// 频段列表
@State bands: BandInfo[] = [];
// 当前预设名称
@State currentPreset: string = '默认';
// 是否已初始化
@State isInitialized: boolean = false;
// 预设列表
private presets: EQPreset[] = [
{ name: '默认', gains: [0, 0, 0, 0, 0] },
{ name: '流行', gains: [1, 3, 5, 3, 1] },
{ name: '摇滚', gains: [4, 2, -1, 2, 4] },
{ name: '古典', gains: [3, 1, 0, 1, 3] },
{ name: '爵士', gains: [2, 0, -1, 1, 3] },
{ name: '电子', gains: [5, 3, 0, 2, 4] },
{ name: '人声', gains: [-1, 0, 4, 2, -1] },
{ name: '低音增强', gains: [5, 4, 1, 0, 0] },
{ name: '高音增强', gains: [0, 0, 1, 3, 5] },
];
aboutToAppear(): void {
this.initEqualizer();
}
aboutToDisappear(): void {
this.releaseEqualizer();
}
/**
* 初始化均衡器
*/
async initEqualizer(): Promise<void> {
try {
// 创建音频渲染器(均衡器需要绑定到渲染器)
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
rendererFlags: 0
}
};
this.audioRenderer = await audio.createAudioRenderer(rendererOptions);
// 创建均衡器效果
this.equalizer = await audio.createAudioEffectEqualizer(this.audioRenderer);
// 获取频段信息
const bandCount = this.equalizer.getNumberOfBands();
const bandList: BandInfo[] = [];
for (let i = 0; i < bandCount; i++) {
const centerFreq = this.equalizer.getCenterFreq(i);
const bandRange = this.equalizer.getBandFreqRange(i);
const minGain = this.equalizer.getMinGain(i);
const maxGain = this.equalizer.getMaxGain(i);
bandList.push({
centerFreq: centerFreq,
bandIndex: i,
gain: 0,
minGain: minGain,
maxGain: maxGain
});
}
this.bands = bandList;
this.isInitialized = true;
console.info(`[EQ] 初始化成功,共 ${bandCount} 个频段`);
} catch (err) {
const error = err as BusinessError;
console.error(`[EQ] 初始化失败: ${error.message}`);
}
}
/**
* 设置指定频段的增益
*/
setBandGain(bandIndex: number, gainDb: number): void {
if (!this.equalizer) return;
try {
this.equalizer.setBandGain(bandIndex, gainDb);
// 更新本地状态
this.bands[bandIndex].gain = gainDb;
console.info(`[EQ] 频段${bandIndex} 增益设为 ${gainDb}dB`);
} catch (err) {
const error = err as BusinessError;
console.error(`[EQ] 设置增益失败: ${error.message}`);
}
}
/**
* 应用预设
*/
applyPreset(preset: EQPreset): void {
if (!this.equalizer || this.bands.length === 0) return;
const gainCount = Math.min(preset.gains.length, this.bands.length);
for (let i = 0; i < gainCount; i++) {
const clampedGain = Math.max(this.bands[i].minGain, Math.min(this.bands[i].maxGain, preset.gains[i]));
this.setBandGain(i, clampedGain);
}
this.currentPreset = preset.name;
console.info(`[EQ] 应用预设: ${preset.name}`);
}
/**
* 重置所有频段为0
*/
resetBands(): void {
for (let i = 0; i < this.bands.length; i++) {
this.setBandGain(i, 0);
}
this.currentPreset = '默认';
}
/**
* 格式化频率显示
*/
formatFrequency(hz: number): string {
if (hz >= 1000) {
return `${(hz / 1000).toFixed(hz >= 10000 ? 0 : 1)}k`;
}
return `${hz}`;
}
/**
* 释放资源
*/
async releaseEqualizer(): Promise<void> {
try {
if (this.equalizer) {
this.equalizer.release();
this.equalizer = null;
}
if (this.audioRenderer) {
await this.audioRenderer.release();
this.audioRenderer = null;
}
} catch (err) {
console.error('[EQ] 释放失败');
}
}
build() {
Column({ space: 16 }) {
// 标题
Text('🎛️ 均衡器')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text(`当前预设: ${this.currentPreset}`)
.fontSize(14)
.fontColor('#4CAF50')
// 预设选择
Scroll(Scroller) {
Row({ space: 8 }) {
ForEach(this.presets, (preset: EQPreset) => {
Text(preset.name)
.fontSize(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.currentPreset === preset.name ? '#4CAF50' : '#333')
.fontColor(this.currentPreset === preset.name ? '#fff' : '#aaa')
.onClick(() => this.applyPreset(preset))
})
}
}
.scrollable(ScrollDirection.Horizontal)
.width('90%')
// 频段调节
if (this.bands.length > 0) {
Row({ space: 4 }) {
ForEach(this.bands, (band: BandInfo) => {
Column({ space: 8 }) {
// 增益值显示
Text(`${band.gain > 0 ? '+' : ''}${band.gain.toFixed(0)}`)
.fontSize(10)
.fontColor(band.gain > 0 ? '#4CAF50' : band.gain < 0 ? '#F44336' : '#888')
.height(20)
// 竖向滑块
Slider({
value: band.gain,
min: band.minGain,
max: band.maxGain,
step: 1,
style: SliderStyle.InSet
})
.width(40)
.height(180)
.vertical(true)
.blockColor('#4CAF50')
.trackColor('#333')
.onChange((value: number) => {
this.setBandGain(band.bandIndex, Math.round(value));
})
// 频率标签
Text(this.formatFrequency(band.centerFreq))
.fontSize(10)
.fontColor('#888')
.height(20)
}
.layoutWeight(1)
})
}
.width('95%')
.height(260)
}
// 重置按钮
Button('重置')
.width(120)
.backgroundColor('#666')
.onClick(() => this.resetBands())
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
.backgroundColor('#1a1a2e')
}
}
3.2 低音增强与虚拟环绕声
低音增强和虚拟环绕声是音乐播放器的标配效果。下面实现一个组合效果控制器:
import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct AudioEffectController {
// 音频渲染器
private audioRenderer: audio.AudioRenderer | null = null;
// 低音增强效果
private bassBoost: audio.AudioEffectBassBoost | null = null;
// 虚拟环绕声效果
private virtualSurround: audio.AudioEffectVirtualSurround | null = null;
// 低音增强强度(0-1000)
@State bassStrength: number = 0;
// 虚拟环绕声开关
@State surroundEnabled: boolean = false;
// 虚拟环绕声模式
@State surroundMode: string = '关闭';
// 整体效果开关
@State effectEnabled: boolean = true;
// 状态文本
@State statusText: string = '未初始化';
aboutToAppear(): void {
this.initAudioEffects();
}
aboutToDisappear(): void {
this.releaseEffects();
}
/**
* 初始化音频效果
*/
async initAudioEffects(): Promise<void> {
try {
// 创建音频渲染器
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
channels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
rendererFlags: 0
}
};
this.audioRenderer = await audio.createAudioRenderer(rendererOptions);
// 创建低音增强效果
this.bassBoost = await audio.createAudioEffectBassBoost(this.audioRenderer);
// 创建虚拟环绕声效果
this.virtualSurround = await audio.createAudioEffectVirtualSurround(this.audioRenderer);
this.statusText = '效果已就绪';
console.info('[效果] 初始化成功');
} catch (err) {
const error = err as BusinessError;
console.error(`[效果] 初始化失败: ${error.message}`);
this.statusText = '初始化失败';
}
}
/**
* 设置低音增强强度
* @param strength 强度值 0-1000
*/
setBassBoostStrength(strength: number): void {
if (!this.bassBoost) return;
try {
// 检查低音增强是否支持
if (!this.bassBoost.isBassBoostSupported()) {
console.warn('[效果] 设备不支持低音增强');
return;
}
// 设置强度
this.bassBoost.setStrength(strength);
this.bassStrength = strength;
console.info(`[效果] 低音增强强度: ${strength}`);
} catch (err) {
const error = err as BusinessError;
console.error(`[效果] 设置低音增强失败: ${error.message}`);
}
}
/**
* 切换虚拟环绕声
*/
toggleVirtualSurround(enabled: boolean): void {
if (!this.virtualSurround) return;
try {
if (enabled) {
this.virtualSurround.enable();
this.surroundEnabled = true;
this.surroundMode = '开启';
console.info('[效果] 虚拟环绕声已开启');
} else {
this.virtualSurround.disable();
this.surroundEnabled = false;
this.surroundMode = '关闭';
console.info('[效果] 虚拟环绕声已关闭');
}
} catch (err) {
const error = err as BusinessError;
console.error(`[效果] 切换虚拟环绕声失败: ${error.message}`);
}
}
/**
* 切换整体效果开关
*/
toggleEffect(enabled: boolean): void {
this.effectEnabled = enabled;
if (!enabled) {
// 关闭所有效果
this.setBassBoostStrength(0);
this.toggleVirtualSurround(false);
}
}
/**
* 获取低音增强描述
*/
getBassDescription(): string {
if (this.bassStrength === 0) return '关闭';
if (this.bassStrength < 300) return '轻微';
if (this.bassStrength < 600) return '中等';
if (this.bassStrength < 900) return '强烈';
return '极致';
}
/**
* 释放所有效果资源
*/
async releaseEffects(): Promise<void> {
try {
if (this.bassBoost) {
this.bassBoost.release();
this.bassBoost = null;
}
if (this.virtualSurround) {
this.virtualSurround.release();
this.virtualSurround = null;
}
if (this.audioRenderer) {
await this.audioRenderer.release();
this.audioRenderer = null;
}
} catch (err) {
console.error('[效果] 释放失败');
}
}
build() {
Column({ space: 24 }) {
// 标题
Row() {
Text('🔊 音频效果控制器')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
// 总开关
Toggle({ type: ToggleType.Switch, isOn: this.effectEnabled })
.onChange((isOn: boolean) => this.toggleEffect(isOn))
.selectedColor('#4CAF50')
}
.width('90%')
Text(this.statusText)
.fontSize(14)
.fontColor('#888')
// 低音增强控制
Column({ space: 12 }) {
Row() {
Text('🎵 低音增强')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
Text(this.getBassDescription())
.fontSize(14)
.fontColor('#FF9800')
}
.width('100%')
// 强度滑块
Slider({
value: this.bassStrength,
min: 0,
max: 1000,
step: 10,
style: SliderStyle.OutSet
})
.width('100%')
.blockColor('#FF9800')
.trackColor('#333')
.onChange((value: number) => {
if (this.effectEnabled) {
this.setBassBoostStrength(Math.round(value));
}
})
// 预设按钮
Row({ space: 8 }) {
Text('预设:')
.fontSize(14)
.fontColor('#888')
ForEach([
{ name: '关闭', value: 0 },
{ name: '轻微', value: 200 },
{ name: '中等', value: 500 },
{ name: '强烈', value: 800 }
], (item: { name: string; value: number }) => {
Text(item.name)
.fontSize(12)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(12)
.backgroundColor(this.bassStrength === item.value ? '#FF9800' : '#333')
.fontColor(this.bassStrength === item.value ? '#fff' : '#aaa')
.onClick(() => {
if (this.effectEnabled) {
this.setBassBoostStrength(item.value);
}
})
})
}
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
// 虚拟环绕声控制
Column({ space: 12 }) {
Row() {
Text('🎧 虚拟环绕声')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.surroundEnabled })
.onChange((isOn: boolean) => {
if (this.effectEnabled) {
this.toggleVirtualSurround(isOn);
}
})
.selectedColor('#9C27B0')
}
.width('100%')
Text('模拟多声道空间感,让声音更有沉浸感')
.fontSize(12)
.fontColor('#888')
// 环绕声模式说明
Row({ space: 8 }) {
Text('💡 提示: 戴耳机效果最佳')
.fontSize(12)
.fontColor('#9C27B0')
}
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
// 效果链预览
Column({ space: 8 }) {
Text('效果链')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%')
Row({ space: 8 }) {
Text('原始音频')
.fontSize(12)
.padding(6)
.borderRadius(8)
.backgroundColor('#2196F3')
Text('→')
.fontSize(16)
.fontColor('#666')
Text(`低音${this.getBassDescription()}`)
.fontSize(12)
.padding(6)
.borderRadius(8)
.backgroundColor(this.bassStrength > 0 ? '#FF9800' : '#333')
Text('→')
.fontSize(16)
.fontColor('#666')
Text(`环绕${this.surroundMode}`)
.fontSize(12)
.padding(6)
.borderRadius(8)
.backgroundColor(this.surroundEnabled ? '#9C27B0' : '#333')
Text('→')
.fontSize(16)
.fontColor('#666')
Text('输出')
.fontSize(12)
.padding(6)
.borderRadius(8)
.backgroundColor('#4CAF50')
}
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
.backgroundColor('#1a1a2e')
}
}
3.3 音频效果链管理器
将均衡器、低音增强、虚拟环绕声整合到一个统一的效果链管理器中,支持效果开关、参数保存与恢复:
import { audio } from '@kit.AudioKit';
import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
// 效果配置模型
interface EffectConfig {
eqGains: number[]; // 均衡器各频段增益
eqPresetName: string; // 当前预设名称
bassStrength: number; // 低音增强强度
surroundEnabled: boolean; // 虚拟环绕声开关
}
@Entry
@Component
struct EffectChainManager {
// 音频渲染器
private audioRenderer: audio.AudioRenderer | null = null;
// 效果实例
private equalizer: audio.AudioEffectEqualizer | null = null;
private bassBoost: audio.AudioEffectBassBoost | null = null;
private virtualSurround: audio.AudioEffectVirtualSurround | null = null;
// 状态
@State eqGains: number[] = [0, 0, 0, 0, 0];
@State eqPresetName: string = '默认';
@State bassStrength: number = 0;
@State surroundEnabled: boolean = false;
@State masterEnabled: boolean = true;
@State isInitialized: boolean = false;
// 均衡器预设
private eqPresets: Map<string, number[]> = new Map([
['默认', [0, 0, 0, 0, 0]],
['流行', [1, 3, 5, 3, 1]],
['摇滚', [4, 2, -1, 2, 4]],
['古典', [3, 1, 0, 1, 3]],
['电子', [5, 3, 0, 2, 4]],
['人声', [-1, 0, 4, 2, -1]],
]);
// 频段名称
private bandNames: string[] = ['60Hz', '230Hz', '910Hz', '3.6kHz', '14kHz'];
aboutToAppear(): void {
this.initEffectChain();
this.loadConfig();
}
aboutToDisappear(): void {
this.saveConfig();
this.releaseAll();
}
/**
* 初始化完整效果链
*/
async initEffectChain(): Promise<void> {
try {
// 创建渲染器
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
channels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
rendererFlags: 0
}
};
this.audioRenderer = await audio.createAudioRenderer(rendererOptions);
// 按顺序创建效果:均衡器 → 低音增强 → 虚拟环绕声
this.equalizer = await audio.createAudioEffectEqualizer(this.audioRenderer);
this.bassBoost = await audio.createAudioEffectBassBoost(this.audioRenderer);
this.virtualSurround = await audio.createAudioEffectVirtualSurround(this.audioRenderer);
this.isInitialized = true;
console.info('[效果链] 初始化完成');
} catch (err) {
const error = err as BusinessError;
console.error(`[效果链] 初始化失败: ${error.message}`);
}
}
/**
* 应用完整效果配置
*/
applyConfig(config: EffectConfig): void {
// 应用均衡器
if (this.equalizer) {
const bandCount = this.equalizer.getNumberOfBands();
for (let i = 0; i < Math.min(config.eqGains.length, bandCount); i++) {
try {
this.equalizer.setBandGain(i, config.eqGains[i]);
} catch (err) {
console.warn(`[效果链] EQ频段${i}设置失败`);
}
}
}
// 应用低音增强
if (this.bassBoost) {
try {
this.bassBoost.setStrength(config.bassStrength);
} catch (err) {
console.warn('[效果链] 低音增强设置失败');
}
}
// 应用虚拟环绕声
if (this.virtualSurround) {
try {
if (config.surroundEnabled) {
this.virtualSurround.enable();
} else {
this.virtualSurround.disable();
}
} catch (err) {
console.warn('[效果链] 虚拟环绕声设置失败');
}
}
// 更新状态
this.eqGains = [...config.eqGains];
this.eqPresetName = config.eqPresetName;
this.bassStrength = config.bassStrength;
this.surroundEnabled = config.surroundEnabled;
}
/**
* 获取当前效果配置
*/
getCurrentConfig(): EffectConfig {
return {
eqGains: [...this.eqGains],
eqPresetName: this.eqPresetName,
bassStrength: this.bassStrength,
surroundEnabled: this.surroundEnabled
};
}
/**
* 保存配置到本地
*/
async saveConfig(): Promise<void> {
try {
const context = getContext(this);
const prefs = await preferences.getPreferences(context, 'audio_effect_config');
const config = this.getCurrentConfig();
await prefs.put('eqGains', JSON.stringify(config.eqGains));
await prefs.put('eqPresetName', config.eqPresetName);
await prefs.put('bassStrength', config.bassStrength);
await prefs.put('surroundEnabled', config.surroundEnabled);
await prefs.flush();
console.info('[效果链] 配置已保存');
} catch (err) {
console.error('[效果链] 保存配置失败');
}
}
/**
* 从本地加载配置
*/
async loadConfig(): Promise<void> {
try {
const context = getContext(this);
const prefs = await preferences.getPreferences(context, 'audio_effect_config');
const eqGainsStr = await prefs.get('eqGains', '[0,0,0,0,0]') as string;
const eqPresetName = await prefs.get('eqPresetName', '默认') as string;
const bassStrength = await prefs.get('bassStrength', 0) as number;
const surroundEnabled = await prefs.get('surroundEnabled', false) as boolean;
const config: EffectConfig = {
eqGains: JSON.parse(eqGainsStr),
eqPresetName: eqPresetName,
bassStrength: bassStrength,
surroundEnabled: surroundEnabled
};
this.applyConfig(config);
console.info('[效果链] 配置已加载');
} catch (err) {
console.warn('[效果链] 加载配置失败,使用默认值');
}
}
/**
* 重置所有效果
*/
resetAll(): void {
this.applyConfig({
eqGains: [0, 0, 0, 0, 0],
eqPresetName: '默认',
bassStrength: 0,
surroundEnabled: false
});
}
/**
* 释放所有资源
*/
async releaseAll(): Promise<void> {
try {
this.equalizer?.release();
this.bassBoost?.release();
this.virtualSurround?.release();
await this.audioRenderer?.release();
this.equalizer = null;
this.bassBoost = null;
this.virtualSurround = null;
this.audioRenderer = null;
} catch (err) {
console.error('[效果链] 释放失败');
}
}
build() {
Scroll() {
Column({ space: 20 }) {
// 标题栏
Row() {
Text('🎛️ 效果链管理器')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.masterEnabled })
.onChange((isOn: boolean) => {
this.masterEnabled = isOn;
if (!isOn) this.resetAll();
})
.selectedColor('#4CAF50')
}
.width('90%')
// 均衡器区域
Column({ space: 12 }) {
Text('📊 均衡器')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.width('100%')
// 预设选择
Scroll(Scroller) {
Row({ space: 6 }) {
ForEach(Array.from(this.eqPresets.entries()), ([name, gains]: [string, number[]]) => {
Text(name)
.fontSize(12)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(12)
.backgroundColor(this.eqPresetName === name ? '#4CAF50' : '#333')
.fontColor(this.eqPresetName === name ? '#fff' : '#aaa')
.onClick(() => {
this.eqPresetName = name;
this.eqGains = [...gains];
if (this.equalizer) {
gains.forEach((g, i) => this.equalizer?.setBandGain(i, g));
}
})
})
}
}
.scrollable(ScrollDirection.Horizontal)
// 频段调节
Row({ space: 4 }) {
ForEach(this.eqGains, (gain: number, index: number) => {
Column({ space: 4 }) {
Text(`${gain > 0 ? '+' : ''}${gain}`)
.fontSize(9)
.fontColor(gain > 0 ? '#4CAF50' : gain < 0 ? '#F44336' : '#666')
Slider({
value: gain,
min: -10,
max: 10,
step: 1,
style: SliderStyle.InSet
})
.width(36)
.height(120)
.vertical(true)
.blockColor('#4CAF50')
.onChange((value: number) => {
this.eqGains[index] = Math.round(value);
this.eqPresetName = '自定义';
this.equalizer?.setBandGain(index, Math.round(value));
})
Text(this.bandNames[index] || `${index}`)
.fontSize(8)
.fontColor('#888')
}
.layoutWeight(1)
})
}
.width('100%')
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
// 低音增强区域
Column({ space: 12 }) {
Row() {
Text('🎵 低音增强')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
Text(this.bassStrength > 0 ? `${Math.round(this.bassStrength / 10)}%` : '关闭')
.fontSize(14)
.fontColor('#FF9800')
}
Slider({
value: this.bassStrength,
min: 0,
max: 1000,
step: 10,
style: SliderStyle.OutSet
})
.width('100%')
.blockColor('#FF9800')
.onChange((value: number) => {
this.bassStrength = Math.round(value);
this.bassBoost?.setStrength(Math.round(value));
})
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
// 虚拟环绕声区域
Column({ space: 12 }) {
Row() {
Text('🎧 虚拟环绕声')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.surroundEnabled })
.onChange((isOn: boolean) => {
this.surroundEnabled = isOn;
if (isOn) {
this.virtualSurround?.enable();
} else {
this.virtualSurround?.disable();
}
})
.selectedColor('#9C27B0')
}
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#252540')
// 操作按钮
Row({ space: 12 }) {
Button('保存配置')
.layoutWeight(1)
.backgroundColor('#4CAF50')
.onClick(() => this.saveConfig())
Button('重置')
.layoutWeight(1)
.backgroundColor('#666')
.onClick(() => this.resetAll())
}
.width('90%')
}
.width('100%')
.alignItems(HorizontalAlign.Center)
.padding({ top: 20, bottom: 40 })
}
.width('100%')
.height('100%')
.backgroundColor('#1a1a2e')
}
}
四、踩坑与注意事项
4.1 效果实例必须绑定到 AudioRenderer
问题:直接创建 AudioEffectEqualizer 不传 renderer,导致效果不生效。
原因:音频效果必须绑定到一个正在工作的音频渲染器上,否则效果无处施加。
解决方案:先创建 AudioRenderer,再基于它创建效果实例。
// ❌ 错误写法:没有绑定渲染器
const eq = await audio.createAudioEffectEqualizer(); // 参数缺失!
// ✅ 正确写法:绑定到渲染器
const renderer = await audio.createAudioRenderer(options);
const eq = await audio.createAudioEffectEqualizer(renderer);
4.2 效果参数范围超限
问题:设置均衡器增益为 20dB,但设备只支持 -10 ~ +10dB,导致设置失败。
解决方案:设置前先查询设备支持的范围。
// 查询频段支持的增益范围
const minGain = this.equalizer.getMinGain(bandIndex);
const maxGain = this.equalizer.getMaxGain(bandIndex);
// 限制在有效范围内
const clampedGain = Math.max(minGain, Math.min(maxGain, desiredGain));
this.equalizer.setBandGain(bandIndex, clampedGain);
4.3 效果链顺序影响音质
问题:先加虚拟环绕声再调均衡器,结果低音增强效果变差了。
原因:虚拟环绕声会重新分配频谱能量,之后再调均衡器可能效果不如预期。
解决方案:遵循标准效果链顺序——均衡器 → 低音增强 → 虚拟环绕声。先调整基础音色,再增强低频,最后添加空间效果。
4.4 效果与蓝牙耳机兼容性
问题:某些蓝牙耳机自带 EQ 和音效,和系统效果叠加后声音失真。
解决方案:检测输出设备类型,蓝牙设备时提供"关闭系统效果"选项。
import { audio } from '@kit.AudioKit';
// 获取当前输出设备
const audioManager = audio.getAudioManager();
const routingManager = audioManager.getRoutingManager();
const devices = routingManager.getDevices(audio.DeviceFlag.OUTPUT_DEVICES_FLAG);
// 检测是否为蓝牙设备
const isBluetooth = devices.some(d =>
d.deviceType === audio.DeviceType.BLUETOOTH_A2DP ||
d.deviceType === audio.DeviceType.BLUETOOTH_SCO
);
if (isBluetooth) {
// 提示用户蓝牙设备可能自带音效
console.info('[效果] 检测到蓝牙设备,建议关闭系统效果');
}
4.5 效果配置持久化
问题:用户调好的 EQ 每次重启 App 都重置了。
解决方案:使用 @ohos.data.preferences 保存效果配置,启动时恢复。
(示例见 3.3 节的 saveConfig / loadConfig 方法)
五、HarmonyOS 6 适配
5.1 API 变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| 效果创建 | createAudioEffectXxx(renderer) |
保持一致,新增 createAudioEffectReverb(混响) |
| 均衡器频段 | 通常 5 频段 | 新增 10 频段模式支持 |
| 低音增强 | setStrength(0-1000) |
新增 setBassBoostMode() 预设模式 |
| 虚拟环绕声 | enable/disable | 新增 setSurroundScene() 场景模式(影院/音乐/游戏) |
| 混响效果 | 无 | 新增 AudioEffectReverb(房间/大厅/教堂等混响) |
5.2 迁移指南
// HarmonyOS 5 写法
this.virtualSurround.enable();
// HarmonyOS 6 写法(支持场景模式)
this.virtualSurround.enable();
this.virtualSurround.setSurroundScene(audio.SurroundScene.SCENE_CINEMA); // 影院模式
5.3 新特性
- 混响效果:新增房间、大厅、教堂等空间混响模拟
- 10 频段均衡器:更精细的频率调节
- 场景化预设:虚拟环绕声支持影院、音乐、游戏等场景模式
- 自适应效果:根据输出设备自动调整效果参数
六、总结
mindmap
root((音频效果))
均衡器 Equalizer
频段调节
超低频 20-60Hz
低频 60-250Hz
中频 500-2kHz
高频 4k-8kHz
预设模式
流行/摇滚/古典
人声/电子/自定义
API
createAudioEffectEqualizer
setBandGain
getNumberOfBands
低音增强 BassBoost
强度调节 0-1000
预设等级
轻微/中等/强烈
API
createAudioEffectBassBoost
setStrength
isBassBoostSupported
虚拟环绕声 VirtualSurround
空间模拟
多声道虚拟化
头相关传递函数
开关控制
enable / disable
API
createAudioEffectVirtualSurround
enable / disable
效果链
顺序原则
均衡器 → 低音增强 → 虚拟环绕声
绑定要求
必须绑定AudioRenderer
配置持久化
preferences保存/加载
注意事项
参数范围检查
蓝牙设备兼容
效果链顺序
资源释放
核心要点回顾:
- 均衡器是基础:通过调节各频段增益来塑造音色,预设模式方便快速切换
- 低音增强简单有效:一个强度参数就能让低频更有冲击力,但别开太大
- 虚拟环绕声提升沉浸感:戴耳机效果最佳,适合电影和游戏场景
- 效果链顺序有讲究:均衡器 → 低音增强 → 虚拟环绕声,先调基础再调空间
- 配置要持久化:用户调好的效果要保存下来,别让用户每次都重新调
- 点赞
- 收藏
- 关注作者
评论(0)