鸿蒙6.1 audio坑:createAudioRenderer工厂非new+AudioSampleFormat enum非字符串
本文是「鸿蒙 6.1 API 23 开发坑系列」第 15 篇(非 UI 系第 9 篇)。本篇讲
@ohos.multimedia.audionamespace(API 9+,鸿蒙 6.1 API 23 基座)—— 音频管理audio.createAudioRenderer(options)/audio.createAudioCapturer(options)工厂造AudioRenderer/AudioCapturer实例 +AudioRenderer/AudioCapturer/AudioManagerinterface 不能 new +AudioStreamInfo/AudioRendererInfo/AudioCapturerInfo/AudioRendererOptions/AudioCapturerOptionsinterface +AudioSampleFormat/AudioChannel/AudioEncodingType/AudioVolumeType/AudioSampleRate/AudioStreamUsage/AudioSourceUsage/RendererState/CapturerStateenum 常量不是字符串 enum 值数字。鸿蒙坑根因:①audio.createAudioRenderer(options: AudioRendererOptions, callback?): Promise<AudioRenderer>是工厂函数造AudioRenderer实例——AudioRenderer是 interface 不能new audio.AudioRenderer()(跟篇 7HttpRequestinterface 不能 new、篇 13ImageSourceinterface 不能 new、篇 14CameraManagerinterface 不能 new 同理);②AudioSampleFormatenum 常量SAMPLE_FORMAT_U8=0/SAMPLE_FORMAT_S16LE=1/SAMPLE_FORMAT_S24LE=2/SAMPLE_FORMAT_S32LE=3/SAMPLE_FORMAT_F32LE=4不是字符串'SAMPLE_FORMAT_S16LE'(传字符串编译错,enum 值是数字 0/1/2/3/4 不是字符串);③AudioChannelenum 常量CHANNEL_1=1/CHANNEL_2=2/CHANNEL_3=3/CHANNEL_4=4/CHANNEL_5=5/CHANNEL_6=6/CHANNEL_7=7/CHANNEL_8=8不是字符串'CHANNEL_2'(enum 值是数字 1~8 不是字符串,对应 mono/stereo/5.1/7.1 声道);④AudioRendererOptionsinterface 含streamInfo: AudioStreamInfo+rendererInfo: AudioRendererInfo两必填对象——AudioStreamInfo含sampleRate: AudioSampleRate+audioChannels: AudioChannel+sampleFormat: AudioSampleFormat+encodingType: AudioEncodingType四必填 enum 常量;⑤AudioRenderer.writeData(buffer: ArrayBuffer)异步写入音频数据到播放器——buffer 是ArrayBuffer原始字节(PCM 裸数据),不是 React WebAudioAudioBuffer对象(含getChannelData()返回Float32Array);⑥AudioRenderer状态机new→initialize()→prepare()→start()→pause()→stop()→release()——每个状态转换都是异步Promise<void>,release()必须在aboutToDisappear生命周期调释放音频硬件资源。
一、开篇:鸿蒙 audio 不是浏览器 Web Audio API,是「createAudioRenderer 工厂造 AudioRenderer」
你写 Web 前端时,音频播放用 new AudioContext() + audioContext.decodeAudioData(arrayBuffer) 返回 Promise<AudioBuffer> + audioContext.createBufferSource() 造 AudioBufferSourceNode + source.buffer = audioBuffer + source.connect(audioContext.destination) + source.start():
// Web:Web Audio API decodeAudioData + createBufferSource 播放音频
const audioContext: AudioContext = new AudioContext() // ✅ new AudioContext() 造上下文
const response: Response = await fetch('audio.mp3')
const arrayBuffer: ArrayBuffer = await response.arrayBuffer()
// ✅ decodeAudioData 异步解码 mp3 返回 Promise<AudioBuffer>
const audioBuffer: AudioBuffer = await audioContext.decodeAudioData(arrayBuffer)
// ✅ createBufferSource 工厂造 AudioBufferSourceNode
const source: AudioBufferSourceNode = audioContext.createBufferSource()
source.buffer = audioBuffer // ✅ AudioBuffer 对象(含 getChannelData 返回 Float32Array)
source.connect(audioContext.destination) // ✅ 连接扬声器
source.start() // ✅ 开始播放
你写鸿蒙 ArkTS 时,音频播放用 audio.createAudioRenderer(options) 工厂造 AudioRenderer 实例 + AudioRendererOptions 配置(AudioStreamInfo + AudioRendererInfo)+ AudioRenderer 状态机 initialize()→prepare()→start()→pause()→stop()→release() + writeData(buffer) 写入 PCM 裸数据:
// ArkTS audio.createAudioRenderer:工厂造 AudioRenderer(interface 不能 new)
import audio from '@ohos.multimedia.audio' // ✅ default import(audio 是 namespace)
// ✅ AudioRendererOptions 配置(streamInfo + rendererInfo 两必填对象)
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, // ✅ AudioSampleRate enum 常量
audioChannels: audio.AudioChannel.CHANNEL_2, // ✅ AudioChannel enum 常量(stereo)
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // ✅ AudioSampleFormat enum 常量
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM // ✅ AudioEncodingType enum 常量
} as audio.AudioStreamInfo,
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MEDIA, // ✅ StreamUsage enum 常量
rendererFlags: 0 // ✅ rendererFlags number
} as audio.AudioRendererInfo
}
// ✅ createAudioRenderer 工厂造 AudioRenderer 实例(interface 不能 new audio.AudioRenderer)
const audioRenderer: audio.AudioRenderer = await audio.createAudioRenderer(rendererOptions)
await audioRenderer.initialize() // ✅ 状态机 initialize
await audioRenderer.prepare() // ✅ 状态机 prepare
await audioRenderer.start() // ✅ 状态机 start 开始播放
// 鸿蒙坑根因:createAudioRenderer 工厂造 AudioRenderer(interface 不能 new),AudioSampleFormat enum 非字符串
Web vs 鸿蒙 audio 的区别:Web 把音频当 new AudioContext() + decodeAudioData() 返回 AudioBuffer 对象(含 getChannelData() 返回 Float32Array 归一化浮点 PCM [-1.0, 1.0])+ createBufferSource() 造 AudioBufferSourceNode + source.start() 播放(一步到位,浏览器内部状态机),ArkTS 把音频当 audio.createAudioRenderer(options) 工厂造 AudioRenderer 实例 + AudioRenderer 状态机 initialize()→prepare()→start()→pause()→stop()→release()(每个状态转换异步 Promise<void>,Android AudioTrack 风格状态机)+ writeData(buffer: ArrayBuffer) 写入 PCM 裸数据(原始字节,S16LE 每采样 2 字节小端有符号整数)。根因不是 Promise 是工厂——鸿蒙 AudioRenderer 是 interface 不能 new audio.AudioRenderer()(用 audio.createAudioRenderer(options) 工厂造实例,跟篇 7 HttpRequest interface 不能 new 用 http.createHttp() 工厂造实例、篇 13 ImageSource interface 不能 new 用 image.createImageSource() 工厂造实例、篇 14 CameraManager interface 不能 new 用 camera.getCameraManager(context) 工厂造实例同理),AudioSampleFormat / AudioChannel / AudioEncodingType / AudioSampleRate / StreamUsage enum 常量不是字符串(传字符串触发 Type 'string' is not assignable to type 'AudioSampleFormat' 编译错,enum 值是数字不是字符串)。
二、根因:鸿蒙 @ohos.multimedia.audio 的六个绑定机制
鸿蒙 @ohos.multimedia.audio namespace(API 9+)核心导出 audio.createAudioRenderer(options) / audio.createAudioCapturer(options) / audio.getAudioManager() 工厂函数(造 AudioRenderer / AudioCapturer / AudioManager 实例)+ AudioRenderer / AudioCapturer / AudioManager interface(不能 new)+ AudioStreamInfo / AudioRendererInfo / AudioCapturerInfo / AudioRendererOptions / AudioCapturerOptions interface(配置对象)+ AudioSampleFormat / AudioChannel / AudioEncodingType / AudioVolumeType / AudioSampleRate / AudioStreamUsage / AudioSourceUsage / RendererState / CapturerState enum 常量不是字符串。绑定机制来自六重根因。
机制 1:audio.createAudioRenderer(options) 工厂造 AudioRenderer——AudioRenderer interface 不能 new
鸿蒙坑根因:audio.createAudioRenderer(options: AudioRendererOptions, callback?): Promise<AudioRenderer> 是工厂函数造 AudioRenderer 实例——AudioRenderer 是 interface 不能 new audio.AudioRenderer():
// ❌ 鸿蒙坑:AudioRenderer 是 interface 不能 new audio.AudioRenderer()
import audio from '@ohos.multimedia.audio'
// ❌ new audio.AudioRenderer() 编译错(AudioRenderer 是 interface 不是 class,没有 constructor)
const renderer1 = new audio.AudioRenderer() // ❌ 'AudioRenderer' only refers to a type
// ❌ new audio.AudioRenderer(options) 编译错(interface 没有 constructor 带参)
const renderer2 = new audio.AudioRenderer(rendererOptions) // ❌ interface 不能 new
// ✅ 正确用法:audio.createAudioRenderer 工厂函数造 AudioRenderer 实例
// ✅ 重载 1:createAudioRenderer(options, callback) 带 callback
audio.createAudioRenderer(rendererOptions, (err, renderer) => {
if (err) {
console.error('创建 AudioRenderer 失败: ' + err.message)
return
}
// ✅ renderer 是 AudioRenderer 实例
})
// ✅ 重载 2:createAudioRenderer(options) 返回 Promise<AudioRenderer>
const audioRenderer: audio.AudioRenderer = await audio.createAudioRenderer(rendererOptions)
// 鸿蒙坑根因:createAudioRenderer 工厂造 AudioRenderer(interface 不能 new,options 必传)
createAudioRenderer 工厂 AudioRenderer interface 不能 new 坑根因:鸿蒙 audio.createAudioRenderer(options: AudioRendererOptions, callback?: AsyncCallback<AudioRenderer>): Promise<AudioRenderer> 是工厂函数造 AudioRenderer 实例(AudioRenderer 是 interface 不是 class,没有 constructor——new audio.AudioRenderer() 触发 'AudioRenderer' only refers to a type, but is being used as a value here 编译错)。鸿蒙坑:options: AudioRendererOptions 必传(不传 options 编译错 Expected 1 arguments, but got 0),AudioRendererOptions 含 streamInfo: AudioStreamInfo + rendererInfo: AudioRendererInfo 两必填对象(缺一编译错)。React new AudioContext() 造音频上下文(class + constructor,一步到位),鸿蒙 createAudioRenderer(options) 工厂造 AudioRenderer 实例(interface + 工厂模式,需传 AudioRendererOptions 配置)。createAudioRenderer 有两种重载——callback 风格(createAudioRenderer(options, (err, renderer) => { ... }))和 Promise 风格(const renderer = await createAudioRenderer(options)),推荐 Promise 风格(async/await 更清晰)。
机制 2:AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0/S16LE=1/S24LE=2/S32LE=3/F32LE=4 不是字符串
鸿蒙坑根因:AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0 / SAMPLE_FORMAT_S16LE=1 / SAMPLE_FORMAT_S24LE=2 / SAMPLE_FORMAT_S32LE=3 / SAMPLE_FORMAT_F32LE=4 不是字符串 'SAMPLE_FORMAT_S16LE'(传字符串编译错,enum 值是数字 0/1/2/3/4 不是字符串):
// ❌ 鸿蒙坑:AudioSampleFormat enum 常量不是字符串'SAMPLE_FORMAT_S16LE'
import audio from '@ohos.multimedia.audio'
// ❌ 传字符串'SAMPLE_FORMAT_S16LE'编译错(AudioSampleFormat 类型是 enum 不是 string)
const format1: audio.AudioSampleFormat = 'SAMPLE_FORMAT_S16LE' // ❌ Type 'string' is not assignable to type 'AudioSampleFormat'
// ❌ 传简写字符串'S16LE'编译错(AudioSampleFormat enum 没有'S16LE'常量)
const format2: audio.AudioSampleFormat = 'S16LE' // ❌ Type 'string' is not assignable to type 'AudioSampleFormat'
// ❌ 传数字 1 编译错(enum 值是数字但类型必须 enum 常量不是 number)
const format3: audio.AudioSampleFormat = 1 // ❌ Type 'number' is not assignable to type 'AudioSampleFormat'
// ✅ 正确用法:audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE enum 常量(不是字符串,不是数字)
const format4: audio.AudioSampleFormat = audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE // ✅ enum 常量 = 1
// ✅ AudioSampleFormat enum 常量语义:
// SAMPLE_FORMAT_U8=0:8 位无符号整数 PCM(每采样 1 字节,动态范围小)
// SAMPLE_FORMAT_S16LE=1:16 位有符号小端 PCM(每采样 2 字节,最常用 CD 音质)
// SAMPLE_FORMAT_S24LE=2:24 位有符号小端 PCM(每采样 3 字节,高保真音频)
// SAMPLE_FORMAT_S32LE=3:32 位有符号小端 PCM(每采样 4 字节,专业音频)
// SAMPLE_FORMAT_F32LE=4:32 位浮点小端 PCM(每采样 4 字节,归一化 [-1.0, 1.0])
// 鸿蒙坑根因:AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8/S16LE/S24LE/S32LE/F32LE 不是字符串
AudioSampleFormat enum 常量不是字符串坑根因:鸿蒙 AudioSampleFormat enum 的五个常量是 SAMPLE_FORMAT_U8=0(8 位无符号整数 PCM,每采样 1 字节,动态范围小)/SAMPLE_FORMAT_S16LE=1(16 位有符号小端 PCM,每采样 2 字节,最常用 CD 音质)/SAMPLE_FORMAT_S24LE=2(24 位有符号小端 PCM,每采样 3 字节,高保真音频)/SAMPLE_FORMAT_S32LE=3(32 位有符号小端 PCM,每采样 4 字节,专业音频)/SAMPLE_FORMAT_F32LE=4(32 位浮点小端 PCM,每采样 4 字节,归一化 [-1.0, 1.0])。鸿蒙坑:传字符串 'SAMPLE_FORMAT_S16LE'/'S16LE' 触发 Type 'string' is not assignable to type 'AudioSampleFormat' 编译错——必须传 audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE enum 常量(enum 值是数字 1 不是字符串)。传数字 1 也编译错(Type 'number' is not assignable to type 'AudioSampleFormat',ArkTS 严格模式 enum 类型不接受裸数字)。React WebAudio AudioBuffer.getChannelData() 返回 Float32Array 归一化浮点 PCM [-1.0, 1.0](固定 SAMPLE_FORMAT_F32LE 格式),鸿蒙 AudioSampleFormat enum 提供 5 种 PCM 格式可选(SAMPLE_FORMAT_S16LE 最常用 CD 音质,SAMPLE_FORMAT_F32LE 对应 React WebAudio 浮点格式)。
机制 3:AudioChannel enum 常量 CHANNEL_1=1/CHANNEL_2=2/…/CHANNEL_8=8 不是字符串’CHANNEL_2’
鸿蒙坑根因:AudioChannel enum 常量 CHANNEL_1=1 / CHANNEL_2=2 / CHANNEL_3=3 / CHANNEL_4=4 / CHANNEL_5=5 / CHANNEL_6=6 / CHANNEL_7=7 / CHANNEL_8=8 不是字符串 'CHANNEL_2'(enum 值是数字 1~8 不是字符串,对应 mono/stereo/5.1/7.1 声道):
// ❌ 鸿蒙坑:AudioChannel enum 常量不是字符串'CHANNEL_2'
import audio from '@ohos.multimedia.audio'
// ❌ 传字符串'CHANNEL_2'编译错(AudioChannel 类型是 enum 不是 string)
const channel1: audio.AudioChannel = 'CHANNEL_2' // ❌ Type 'string' is not assignable to type 'AudioChannel'
// ❌ 传简写字符串'stereo'编译错(AudioChannel enum 没有'stereo'常量)
const channel2: audio.AudioChannel = 'stereo' // ❌ Type 'string' is not assignable to type 'AudioChannel'
// ❌ 传数字 2 编译错(enum 值是数字但类型必须 enum 常量不是 number)
const channel3: audio.AudioChannel = 2 // ❌ Type 'number' is not assignable to type 'AudioChannel'
// ✅ 正确用法:audio.AudioChannel.CHANNEL_2 enum 常量(不是字符串,不是数字)
const channel4: audio.AudioChannel = audio.AudioChannel.CHANNEL_2 // ✅ enum 常量 = 2
// ✅ AudioChannel enum 常量语义:
// CHANNEL_1=1:单声道(mono)
// CHANNEL_2=2:立体声(stereo,最常用)
// CHANNEL_3=3:3 声道(左/右/中)
// CHANNEL_4=4:4 声道(左/右/中/低频)
// CHANNEL_5=5:5 声道(5.0 环绕声)
// CHANNEL_6=6:6 声道(5.1 环绕声,含低频效果声道 LFE)
// CHANNEL_7=7:7 声道(6.1 环绕声)
// CHANNEL_8=8:8 声道(7.1 环绕声)
// 鸿蒙坑根因:AudioChannel enum 常量 CHANNEL_1~CHANNEL_8 不是字符串 enum 值数字
AudioChannel enum 常量不是字符串坑根因:鸿蒙 AudioChannel enum 的八个常量是 CHANNEL_1=1(单声道 mono)/CHANNEL_2=2(立体声 stereo,最常用)/CHANNEL_3=3(3 声道左/右/中)/CHANNEL_4=4(4 声道左/右/中/低频)/CHANNEL_5=5(5 声道 5.0 环绕声)/CHANNEL_6=6(6 声道 5.1 环绕声含低频效果声道 LFE)/CHANNEL_7=7(7 声道 6.1 环绕声)/CHANNEL_8=8(8 声道 7.1 环绕声)。鸿蒙坑:传字符串 'CHANNEL_2'/'stereo' 触发 Type 'string' is not assignable to type 'AudioChannel' 编译错——必须传 audio.AudioChannel.CHANNEL_2 enum 常量(enum 值是数字 2 不是字符串)。传数字 2 也编译错(Type 'number' is not assignable to type 'AudioChannel',ArkTS 严格模式 enum 类型不接受裸数字)。React WebAudio AudioBuffer.numberOfChannels 是 number(1=mono, 2=stereo),鸿蒙 AudioChannel enum 常量指定声道数(enum 值 1~8 对应 mono/stereo/5.1/7.1)。
机制 4:AudioRendererOptions 含 streamInfo + rendererInfo 两必填对象——AudioStreamInfo 四必填 enum 常量
鸿蒙坑根因:AudioRendererOptions interface 含 streamInfo: AudioStreamInfo + rendererInfo: AudioRendererInfo 两必填对象——AudioStreamInfo 含 sampleRate: AudioSampleRate + audioChannels: AudioChannel + sampleFormat: AudioSampleFormat + encodingType: AudioEncodingType 四必填 enum 常量:
// ❌ 鸿蒙坑:AudioRendererOptions 含 streamInfo + rendererInfo 两必填对象
import audio from '@ohos.multimedia.audio'
// ❌ 缺 streamInfo 编译错(AudioRendererOptions.streamInfo 必填)
const opts1: audio.AudioRendererOptions = {
rendererInfo: { usage: audio.StreamUsage.STREAM_USAGE_MEDIA, rendererFlags: 0 } as audio.AudioRendererInfo
} // ❌ 缺 streamInfo
// ❌ 缺 rendererInfo 编译错(AudioRendererOptions.rendererInfo 必填)
const opts2: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100,
audioChannels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM
} as audio.AudioStreamInfo
} // ❌ 缺 rendererInfo
// ❌ AudioStreamInfo 缺 sampleRate 编译错(AudioStreamInfo.sampleRate 必填 AudioSampleRate enum 常量)
const opts3: audio.AudioRendererOptions = {
streamInfo: {
audioChannels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM
} as audio.AudioStreamInfo, // ❌ 缺 sampleRate
rendererInfo: { usage: audio.StreamUsage.STREAM_USAGE_MEDIA, rendererFlags: 0 } as audio.AudioRendererInfo
}
// ✅ 正确用法:AudioRendererOptions streamInfo + rendererInfo 两必填对象
// ✅ AudioStreamInfo 四必填 enum 常量 sampleRate + audioChannels + sampleFormat + encodingType
// ✅ AudioRendererInfo 两必填 usage + rendererFlags
const opts4: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, // ✅ AudioSampleRate enum 常量(44100Hz CD 音质)
audioChannels: audio.AudioChannel.CHANNEL_2, // ✅ AudioChannel enum 常量(stereo)
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // ✅ AudioSampleFormat enum 常量(16 位 PCM)
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM // ✅ AudioEncodingType enum 常量(PCM 裸数据)
} as audio.AudioStreamInfo,
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MEDIA, // ✅ StreamUsage enum 常量(媒体播放)
rendererFlags: 0 // ✅ rendererFlags number(0=默认)
} as audio.AudioRendererInfo
}
// 鸿蒙坑根因:AudioRendererOptions streamInfo + rendererInfo 两必填,AudioStreamInfo 四必填 enum 常量
AudioRendererOptions + AudioStreamInfo 四必填 enum 常量坑根因:鸿蒙 AudioRendererOptions interface 含两必填对象 streamInfo: AudioStreamInfo(音频流信息)+ rendererInfo: AudioRendererInfo(渲染器信息),缺任何一个触发 Property 'xxx' is missing in type 编译错。鸿蒙坑:AudioStreamInfo interface 含四必填 enum 常量字段 sampleRate: AudioSampleRate(采样率 enum 常量,SAMPLE_RATE_8000=8000/SAMPLE_RATE_44100=44100/SAMPLE_RATE_48000=48000 等,enum 值是数字 8000/44100/48000 不是字符串)/audioChannels: AudioChannel(声道数 enum 常量,CHANNEL_1=1/CHANNEL_2=2 等)/sampleFormat: AudioSampleFormat(采样格式 enum 常量,SAMPLE_FORMAT_U8=0/SAMPLE_FORMAT_S16LE=1 等)/encodingType: AudioEncodingType(编码类型 enum 常量,ENCODING_TYPE_PCM=0/ENCODING_TYPE_MP3=1/ENCODING_TYPE_AAC=2 等),缺任何一个编译错。AudioRendererInfo interface 含两必填字段 usage: StreamUsage(流用途 enum 常量,STREAM_USAGE_MEDIA=1/STREAM_USAGE_VOICE_COMMUNICATION=2/STREAM_USAGE_NOTIFICATION_RINGTONE=5 等)/rendererFlags: number(渲染器标志位数字)。React WebAudio AudioContext() 无需配置(默认 44100Hz stereo float32),鸿蒙 AudioRendererOptions 需显式配置 AudioStreamInfo 四必填 enum 常量(声明式配置,编译期检查字段名和 enum 常量)。
机制 5:AudioRenderer.writeData(buffer: ArrayBuffer) 异步写入 PCM 裸数据——不是 React WebAudio AudioBuffer
鸿蒙坑根因:AudioRenderer.writeData(buffer: ArrayBuffer) 异步写入音频数据到播放器——buffer 是 ArrayBuffer 原始字节(PCM 裸数据),不是 React WebAudio AudioBuffer 对象(含 getChannelData() 返回 Float32Array):
// ❌ 鸿蒙坑:AudioRenderer.writeData 写入 ArrayBuffer PCM 裸数据(不是 React WebAudio AudioBuffer)
import audio from '@ohos.multimedia.audio'
// ✅ 正确用法:writeData 写入 ArrayBuffer PCM 裸数据
// ✅ SAMPLE_FORMAT_S16LE 每采样 2 字节,CHANNEL_2 立体声 2 声道
// ✅ buffer 大小 = 采样数 × 2 字节/采样 × 2 声道 = 采样数 × 4 字节
const bufferSize: number = 4410 * 4 // ✅ 4410 采样 × 4 字节 = 17640 字节(0.1 秒 44100Hz stereo S16LE)
const pcmBuffer: ArrayBuffer = new ArrayBuffer(bufferSize)
const pcmBytes: Uint8Array = new Uint8Array(pcmBuffer)
// ✅ 填充 PCM 裸数据(正弦波 440Hz A4 音符)
for (let i: number = 0; i < 4410; i++) {
const sample: number = Math.sin(2 * Math.PI * 440 * i / 44100) * 32767 // ✅ S16LE 范围 [-32768, 32767]
// ✅ 立体声交错存储:左声道 2 字节 + 右声道 2 字节
pcmBytes[i * 4] = sample & 0xFF // ✅ 低字节
pcmBytes[i * 4 + 1] = (sample >> 8) & 0xFF // ✅ 高字节
pcmBytes[i * 4 + 2] = sample & 0xFF // ✅ 右声道低字节
pcmBytes[i * 4 + 3] = (sample >> 8) & 0xFF // ✅ 右声道高字节
}
// ✅ writeData 异步写入 PCM 裸数据到播放器
await audioRenderer.writeData(pcmBuffer)
// ❌ 用 AudioBuffer 对象编译错(鸿蒙没有 AudioBuffer 类型)
// const audioBuffer: audio.AudioBuffer = new audio.AudioBuffer() // ❌ Property 'AudioBuffer' does not exist on type 'audio'
// ❌ 用 getChannelData 编译错(鸿蒙 writeData 接收 ArrayBuffer 不是 AudioBuffer)
// const channelData: Float32Array = audioBuffer.getChannelData(0) // ❌ 鸿蒙没有 getChannelData 方法
// ❌ 用 Float32Array 归一化浮点 PCM 编译错(SAMPLE_FORMAT_S16LE 是 16 位整数 PCM 不是浮点)
// const float32Buffer: Float32Array = new Float32Array(bufferSize) // ❌ SAMPLE_FORMAT_S16LE 需要 Int16Array 不是 Float32Array
// 鸿蒙坑根因:writeData 写入 ArrayBuffer PCM 裸数据(不是 React WebAudio AudioBuffer)
AudioRenderer.writeData + ArrayBuffer PCM 裸数据坑根因:鸿蒙 AudioRenderer.writeData(buffer: ArrayBuffer): Promise<void> 异步写入 PCM 裸数据到播放器,返回 Promise<void>(写完后播放器开始播放写入的数据)。鸿蒙坑:buffer 是 ArrayBuffer 原始字节(PCM 裸数据,需用 Uint8Array 或 Int16Array 访问),不是 React WebAudio AudioBuffer 对象(含 getChannelData() 返回 Float32Array 归一化浮点 PCM [-1.0, 1.0])。React WebAudio AudioBuffer.getChannelData(channel) 返回 Float32Array 归一化浮点 PCM [-1.0, 1.0](固定 SAMPLE_FORMAT_F32LE 格式),鸿蒙 writeData(buffer) 接收 ArrayBuffer PCM 裸数据(格式由 AudioStreamInfo.sampleFormat enum 常量决定,SAMPLE_FORMAT_S16LE 每采样 2 字节小端有符号整数范围 [-32768, 32767],SAMPLE_FORMAT_F32LE 每采样 4 字节浮点范围 [-1.0, 1.0])。React source.start() 一步到位播放完整 AudioBuffer,鸿蒙 writeData(buffer) 需循环写入 PCM 裸数据(类似 Android AudioTrack write(byte[], offset, size) 方法,流式播放需持续 writeData 喂数据避免 underrun 播放断续)。
机制 6:AudioRenderer 状态机 initialize→prepare→start→pause→stop→release——release 必须调
鸿蒙坑根因:AudioRenderer 状态机 new→initialize()→prepare()→start()→pause()→stop()→release()——每个状态转换都是异步 Promise<void>,release() 必须在 aboutToDisappear 生命周期调释放音频硬件资源:
// ✅ 鸿蒙坑:AudioRenderer 状态机 initialize→prepare→start→pause→stop→release
import audio from '@ohos.multimedia.audio'
@Entry
@Component
struct Index {
private audioRenderer: audio.AudioRenderer | null = null
async createAndPlayRenderer() {
// ✅ step 1:createAudioRenderer 工厂造 AudioRenderer 实例(interface 不能 new)
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100,
audioChannels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM
} as audio.AudioStreamInfo,
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MEDIA,
rendererFlags: 0
} as audio.AudioRendererInfo
}
this.audioRenderer = await audio.createAudioRenderer(rendererOptions)
// ✅ step 2:initialize 状态机初始化(异步 Promise<void>)
await this.audioRenderer.initialize()
// ✅ step 3:prepare 状态机准备(异步 Promise<void>,分配音频硬件资源)
await this.audioRenderer.prepare()
// ✅ step 4:start 状态机开始播放(异步 Promise<void>,开始播放写入的 PCM 数据)
await this.audioRenderer.start()
// ✅ step 5:writeData 写入 PCM 裸数据(循环写入流式播放)
const pcmBuffer: ArrayBuffer = new ArrayBuffer(17640) // ✅ 0.1 秒 PCM 数据
await this.audioRenderer.writeData(pcmBuffer)
// ✅ step 6:pause 状态机暂停(异步 Promise<void>,暂停播放但保留资源)
// await this.audioRenderer.pause()
// ✅ step 7:stop 状态机停止(异步 Promise<void>,停止播放释放部分资源)
// await this.audioRenderer.stop()
}
// ✅ aboutToDisappear 生命周期释放资源(避免内存泄漏和音频硬件占用)
async aboutToDisappear() {
if (this.audioRenderer) {
await this.audioRenderer.stop() // ✅ stop 停止播放
await this.audioRenderer.release() // ✅ release 释放音频硬件资源(必须调)
}
}
build() {
Column({ space: 8 }) {
Button('创建并播放').onClick(() => this.createAndPlayRenderer())
}
}
}
// AudioRenderer 状态机 initialize→prepare→start→pause→stop→release 严格顺序异步
AudioRenderer 状态机 + release 必须调坑根因:鸿蒙 AudioRenderer 状态机严格 7 步——new(createAudioRenderer 工厂造实例)→ initialize(): Promise<void>(状态机初始化,分配播放器内部资源)→ prepare(): Promise<void>(状态机准备,分配音频硬件资源)→ start(): Promise<void>(状态机开始播放,开始播放写入的 PCM 数据)→ pause(): Promise<void>(状态机暂停,暂停播放但保留资源,可 start() 恢复)→ stop(): Promise<void>(状态机停止,停止播放释放部分资源,可 start() 恢复)→ release(): Promise<void>(状态机释放,释放所有资源,不可恢复)。鸿蒙坑:必须严格按 initialize → prepare → start → pause/stop → release 顺序调用,颠倒顺序或跳步会报错(如 start 前 prepare 报「未准备好」错,release 后 start 报「已释放」错)。release() 必须在 aboutToDisappear 生命周期调(释放音频硬件资源,不释放会导致后续应用无法播放音频,类似篇 14 CaptureSession.release() 释放相机硬件资源)。React WebAudio source.start() 一步到位播放(浏览器内部状态机,无需显式状态转换),鸿蒙 AudioRenderer 状态机 Android AudioTrack 风格(initialize/prepare/start/pause/stop/release 对应 Android AudioTrack 的 setNotificationMarkerPosition/play/pause/stop/release 方法)。
三、真机配图:鸿蒙 @ohos.multimedia.audio 音频坑——createAudioRenderer 工厂非 new + AudioSampleFormat enum 非字符串

真机配图展示鸿蒙 @ohos.multimedia.audio 音频坑:
- audio 初始态:鸿蒙 6.1 @ohos.multimedia.audio 音频坑标题,5 个验证按钮(① createAudioRenderer 工厂非 new / ② AudioSampleFormat enum 非字符串 / ③ AudioChannel enum 非字符串 / ④ writeData 写入 PCM 裸数据 / ⑤ AudioRenderer 状态机 7 步),要点说明 7 条
- createAudioRenderer 工厂态:点击「① 验证 createAudioRenderer 工厂非 new」按钮,显示「✅ audio.createAudioRenderer(options) 工厂造 AudioRenderer 实例(interface 不能 new audio.AudioRenderer)」+ audioRenderer 值——createAudioRenderer 工厂造 AudioRenderer(interface 不能 new)验证
- AudioSampleFormat enum 态:点击「② 验证 AudioSampleFormat enum 非字符串」按钮,显示「✅ AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0/S16LE=1/S24LE=2/S32LE=3/F32LE=4 不是字符串」+ format 值——AudioSampleFormat enum 常量非字符串验证
- AudioChannel enum 态:点击「③ 验证 AudioChannel enum 非字符串」按钮,显示「✅ AudioChannel enum 常量 CHANNEL_1=1/CHANNEL_2=2/…/CHANNEL_8=8 不是字符串 enum 值数字」+ channel 值——AudioChannel enum 常量非字符串验证
- AudioRenderer 状态机态:点击「⑤ 验证 AudioRenderer 状态机 7 步」按钮,显示「✅ initialize→prepare→start→pause→stop→release 状态机 7 步严格顺序异步 + aboutToDisappear 生命周期 release 释放音频硬件资源」+ state 值——AudioRenderer 状态机 7 步 + release 必须调验证
四、真解法:鸿蒙 @ohos.multimedia.audio 的四个场景
场景 1:createAudioRenderer + AudioRendererOptions 配置——90% 场景首选
基础音频播放用 audio.createAudioRenderer(options) 工厂造 AudioRenderer 实例 + AudioRendererOptions 配置(AudioStreamInfo + AudioRendererInfo 两必填对象):
// ✅ 场景 1:createAudioRenderer + AudioRendererOptions 配置(API 9,90% 场景首选)
import audio from '@ohos.multimedia.audio' // ✅ default import(audio 是 namespace)
async function createAudioRenderer(): Promise<audio.AudioRenderer> {
// ✅ AudioRendererOptions streamInfo + rendererInfo 两必填对象
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
// ✅ AudioStreamInfo 四必填 enum 常量 sampleRate + audioChannels + sampleFormat + encodingType
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, // ✅ AudioSampleRate enum 常量(44100Hz CD 音质)
audioChannels: audio.AudioChannel.CHANNEL_2, // ✅ AudioChannel enum 常量(stereo 立体声)
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // ✅ AudioSampleFormat enum 常量(16 位 PCM)
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM // ✅ AudioEncodingType enum 常量(PCM 裸数据)
} as audio.AudioStreamInfo,
rendererInfo: {
// ✅ AudioRendererInfo 两必填 usage + rendererFlags
usage: audio.StreamUsage.STREAM_USAGE_MEDIA, // ✅ StreamUsage enum 常量(媒体播放)
rendererFlags: 0 // ✅ rendererFlags number(0=默认)
} as audio.AudioRendererInfo
}
// ✅ createAudioRenderer 工厂造 AudioRenderer 实例(interface 不能 new audio.AudioRenderer)
const audioRenderer: audio.AudioRenderer = await audio.createAudioRenderer(rendererOptions)
return audioRenderer
}
// createAudioRenderer 工厂造 AudioRenderer(interface 不能 new)+ AudioRendererOptions 配置
鸿蒙 @ohos.multimedia.audio API 真名坑:import audio from '@ohos.multimedia.audio'(default import,audio 是 namespace);audio.createAudioRenderer(options: AudioRendererOptions, callback?: AsyncCallback<AudioRenderer>): Promise<AudioRenderer>(工厂函数造 AudioRenderer 实例,AudioRenderer 是 interface 不能 new audio.AudioRenderer(),options 必传);AudioRendererOptions interface 含两必填对象 streamInfo: AudioStreamInfo(音频流信息)+ rendererInfo: AudioRendererInfo(渲染器信息);AudioStreamInfo interface 含四必填 enum 常量字段 sampleRate: AudioSampleRate / audioChannels: AudioChannel / sampleFormat: AudioSampleFormat / encodingType: AudioEncodingType;AudioRendererInfo interface 含两必填字段 usage: StreamUsage(流用途 enum 常量)/rendererFlags: number(渲染器标志位数字);AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0/SAMPLE_FORMAT_S16LE=1/SAMPLE_FORMAT_S24LE=2/SAMPLE_FORMAT_S32LE=3/SAMPLE_FORMAT_F32LE=4 不是字符串;AudioChannel enum 常量 CHANNEL_1=1/CHANNEL_2=2/…/CHANNEL_8=8 不是字符串;AudioSampleRate enum 常量 SAMPLE_RATE_8000=8000/SAMPLE_RATE_44100=44100/SAMPLE_RATE_48000=48000 等不是字符串;AudioEncodingType enum 常量 ENCODING_TYPE_PCM=0/ENCODING_TYPE_MP3=1/ENCODING_TYPE_AAC=2 不是字符串;StreamUsage enum 常量 STREAM_USAGE_MEDIA=1/STREAM_USAGE_VOICE_COMMUNICATION=2/STREAM_USAGE_NOTIFICATION_RINGTONE=5 不是字符串;SysCap SystemCapability.Multimedia.Audio.Core / SystemCapability.Multimedia.Audio.Renderer / SystemCapability.Multimedia.Audio.Capturer;@atomicservice 原子化服务(API 11+)。
场景 2:AudioRenderer 状态机 initialize→prepare→start→pause→stop→release
音频播放状态机用 AudioRenderer.initialize() → prepare() → start() → pause() / stop() → release() 严格顺序异步状态转换:
// ✅ 场景 2:AudioRenderer 状态机 initialize→prepare→start→pause→stop→release(API 9)
import audio from '@ohos.multimedia.audio'
@Entry
@Component
struct Index {
private audioRenderer: audio.AudioRenderer | null = null
@State stateText: string = '未创建'
async createRenderer() {
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100,
audioChannels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM
} as audio.AudioStreamInfo,
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MEDIA,
rendererFlags: 0
} as audio.AudioRendererInfo
}
// ✅ createAudioRenderer 工厂造 AudioRenderer 实例(interface 不能 new)
this.audioRenderer = await audio.createAudioRenderer(rendererOptions)
// ✅ initialize 状态机初始化(异步 Promise<void>)
await this.audioRenderer.initialize()
this.stateText = '已初始化'
// ✅ prepare 状态机准备(异步 Promise<void>,分配音频硬件资源)
await this.audioRenderer.prepare()
this.stateText = '已准备'
// ✅ start 状态机开始播放(异步 Promise<void>)
await this.audioRenderer.start()
this.stateText = '正在播放'
}
async pauseRenderer() {
if (this.audioRenderer) {
// ✅ pause 状态机暂停(异步 Promise<void>,暂停播放但保留资源)
await this.audioRenderer.pause()
this.stateText = '已暂停'
}
}
async stopRenderer() {
if (this.audioRenderer) {
// ✅ stop 状态机停止(异步 Promise<void>,停止播放释放部分资源)
await this.audioRenderer.stop()
this.stateText = '已停止'
}
}
// ✅ aboutToDisappear 生命周期释放资源(避免内存泄漏和音频硬件占用)
async aboutToDisappear() {
if (this.audioRenderer) {
await this.audioRenderer.stop() // ✅ stop 停止播放
await this.audioRenderer.release() // ✅ release 释放音频硬件资源(必须调)
}
}
build() {
Column({ space: 8 }) {
Button('创建播放器').onClick(() => this.createRenderer())
Button('暂停').onClick(() => this.pauseRenderer())
Button('停止').onClick(() => this.stopRenderer())
Text(this.stateText).fontSize(16).margin({ top: 20 })
}
}
}
// AudioRenderer 状态机 initialize→prepare→start→pause→stop→release 严格顺序异步
鸿蒙 AudioRenderer 状态机 API 真名坑:AudioRenderer.initialize(): Promise<void>(状态机初始化,分配播放器内部资源)→ AudioRenderer.prepare(): Promise<void>(状态机准备,分配音频硬件资源)→ AudioRenderer.start(): Promise<void>(状态机开始播放)→ AudioRenderer.pause(): Promise<void>(状态机暂停,可 start() 恢复)→ AudioRenderer.stop(): Promise<void>(状态机停止,可 start() 恢复)→ AudioRenderer.release(): Promise<void>(状态机释放,释放所有资源,不可恢复);鸿蒙坑:必须严格按 initialize → prepare → start → pause/stop → release 顺序调用,颠倒顺序或跳步会报错;release() 必须在 aboutToDisappear 生命周期调(释放音频硬件资源,不释放会导致后续应用无法播放音频);React WebAudio source.start() 一步到位播放(浏览器内部状态机),鸿蒙 AudioRenderer 状态机 Android AudioTrack 风格(7 步状态转换,每步异步 Promise<void>)。
场景 3:writeData 写入 PCM 裸数据——SAMPLE_FORMAT_S16LE 每采样 2 字节
播放 PCM 裸数据用 AudioRenderer.writeData(buffer: ArrayBuffer) 异步写入——SAMPLE_FORMAT_S16LE 每采样 2 字节小端有符号整数,CHANNEL_2 立体声 2 声道交错存储:
// ✅ 场景 3:writeData 写入 PCM 裸数据(SAMPLE_FORMAT_S16LE 每采样 2 字节)
import audio from '@ohos.multimedia.audio'
@Entry
@Component
struct Index {
private audioRenderer: audio.AudioRenderer | null = null
async playSineWave() {
// ✅ 创建 AudioRenderer(工厂造实例,interface 不能 new)
const rendererOptions: audio.AudioRendererOptions = {
streamInfo: {
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, // ✅ 44100Hz CD 音质
audioChannels: audio.AudioChannel.CHANNEL_2, // ✅ stereo 立体声
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // ✅ 16 位 PCM 每采样 2 字节
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM // ✅ PCM 裸数据
} as audio.AudioStreamInfo,
rendererInfo: {
usage: audio.StreamUsage.STREAM_USAGE_MEDIA,
rendererFlags: 0
} as audio.AudioRendererInfo
}
this.audioRenderer = await audio.createAudioRenderer(rendererOptions)
// ✅ 状态机 initialize→prepare→start 严格顺序异步
await this.audioRenderer.initialize()
await this.audioRenderer.prepare()
await this.audioRenderer.start()
// ✅ writeData 写入 PCM 裸数据(SAMPLE_FORMAT_S16LE 每采样 2 字节,CHANNEL_2 立体声 2 声道交错存储)
// ✅ buffer 大小 = 采样数 × 2 字节/采样 × 2 声道 = 采样数 × 4 字节
const sampleCount: number = 4410 // ✅ 4410 采样(0.1 秒 44100Hz)
const bufferSize: number = sampleCount * 4 // ✅ 17640 字节(0.1 秒 stereo S16LE)
const pcmBuffer: ArrayBuffer = new ArrayBuffer(bufferSize)
const pcmBytes: Uint8Array = new Uint8Array(pcmBuffer)
// ✅ 填充 PCM 裸数据(正弦波 440Hz A4 音符)
for (let i: number = 0; i < sampleCount; i++) {
// ✅ S16LE 范围 [-32768, 32767],正弦波振幅 32767(最大音量)
const sample: number = Math.sin(2 * Math.PI * 440 * i / 44100) * 32767
// ✅ 立体声交错存储:左声道 2 字节 + 右声道 2 字节(小端 low byte first)
pcmBytes[i * 4] = sample & 0xFF // ✅ 左声道低字节
pcmBytes[i * 4 + 1] = (sample >> 8) & 0xFF // ✅ 左声道高字节
pcmBytes[i * 4 + 2] = sample & 0xFF // ✅ 右声道低字节
pcmBytes[i * 4 + 3] = (sample >> 8) & 0xFF // ✅ 右声道高字节
}
// ✅ writeData 异步写入 PCM 裸数据到播放器
await this.audioRenderer.writeData(pcmBuffer)
console.info('正弦波 440Hz 播放中')
}
async aboutToDisappear() {
if (this.audioRenderer) {
await this.audioRenderer.stop()
await this.audioRenderer.release() // ✅ release 释放音频硬件资源(必须调)
}
}
build() {
Column({ space: 8 }) {
Button('播放正弦波').onClick(() => this.playSineWave())
}
}
}
// writeData 写入 ArrayBuffer PCM 裸数据(SAMPLE_FORMAT_S16LE 每采样 2 字节小端有符号整数)
鸿蒙 writeData + PCM 裸数据 API 真名坑:AudioRenderer.writeData(buffer: ArrayBuffer): Promise<void> 异步写入 PCM 裸数据到播放器;SAMPLE_FORMAT_S16LE 每采样 2 字节小端有符号整数范围 [-32768, 32767];CHANNEL_2 立体声 2 声道交错存储(左声道 2 字节 + 右声道 2 字节,小端 low byte first);buffer 大小 = 采样数 × 每采样字节数 × 声道数(SAMPLE_FORMAT_S16LE + CHANNEL_2 每采样 4 字节 = 2 字节/采样 × 2 声道);鸿蒙坑:buffer 是 ArrayBuffer 原始字节(PCM 裸数据),不是 React WebAudio AudioBuffer 对象(含 getChannelData() 返回 Float32Array);React AudioBuffer.getChannelData(0) 返回 Float32Array 归一化浮点 PCM [-1.0, 1.0](固定 SAMPLE_FORMAT_F32LE 格式),鸿蒙 writeData(buffer) 接收 ArrayBuffer PCM 裸数据(格式由 AudioStreamInfo.sampleFormat enum 常量决定);React source.start() 一步到位播放完整 AudioBuffer,鸿蒙 writeData(buffer) 需循环写入 PCM 裸数据(流式播放需持续 writeData 喂数据避免 underrun 播放断续)。
场景 4:createAudioCapturer 录音——AudioCapturerOptions 含 streamInfo + capturerInfo
录音用 audio.createAudioCapturer(options) 工厂造 AudioCapturer 实例 + AudioCapturerOptions 配置(AudioStreamInfo + AudioCapturerInfo 两必填对象)+ AudioCapturer.readData(buffer) 异步读取 PCM 裸数据:
// ✅ 场景 4:createAudioCapturer 录音——AudioCapturerOptions 含 streamInfo + capturerInfo
import audio from '@ohos.multimedia.audio'
@Entry
@Component
struct Index {
private audioCapturer: audio.AudioCapturer | null = null
async startRecording() {
// ✅ AudioCapturerOptions streamInfo + capturerInfo 两必填对象
const capturerOptions: audio.AudioCapturerOptions = {
streamInfo: {
// ✅ AudioStreamInfo 四必填 enum 常量 sampleRate + audioChannels + sampleFormat + encodingType
sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, // ✅ AudioSampleRate enum 常量(44100Hz CD 音质)
audioChannels: audio.AudioChannel.CHANNEL_1, // ✅ AudioChannel enum 常量(mono 单声道录音)
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // ✅ AudioSampleFormat enum 常量(16 位 PCM)
encodingType: audio.AudioEncodingType.ENCODING_TYPE_PCM // ✅ AudioEncodingType enum 常量(PCM 裸数据)
} as audio.AudioStreamInfo,
capturerInfo: {
// ✅ AudioCapturerInfo 两必填 source + capturerFlags
source: audio.SourceType.SOURCE_TYPE_MIC, // ✅ SourceType enum 常量(麦克风录音)
capturerFlags: 0 // ✅ capturerFlags number(0=默认)
} as audio.AudioCapturerInfo
}
// ✅ createAudioCapturer 工厂造 AudioCapturer 实例(interface 不能 new audio.AudioCapturer)
this.audioCapturer = await audio.createAudioCapturer(capturerOptions)
// ✅ AudioCapturer 状态机 initialize→prepare→start 严格顺序异步
await this.audioCapturer.initialize()
await this.audioCapturer.prepare()
await this.audioCapturer.start()
console.info('录音开始')
// ✅ readData 异步读取 PCM 裸数据(SAMPLE_FORMAT_S16LE 每采样 2 字节,CHANNEL_1 单声道)
// ✅ buffer 大小 = 采样数 × 2 字节/采样 × 1 声道 = 采样数 × 2 字节
const bufferSize: number = 4410 * 2 // ✅ 8820 字节(0.1 秒 44100Hz mono S16LE)
const pcmBuffer: ArrayBuffer = new ArrayBuffer(bufferSize)
await this.audioCapturer.readData(pcmBuffer) // ✅ readData 异步读取 PCM 裸数据
console.info('读取 PCM 数据: ' + pcmBuffer.byteLength + ' 字节')
}
async aboutToDisappear() {
if (this.audioCapturer) {
await this.audioCapturer.stop()
await this.audioCapturer.release() // ✅ release 释放音频硬件资源(必须调)
}
}
build() {
Column({ space: 8 }) {
Button('开始录音').onClick(() => this.startRecording())
}
}
}
// createAudioCapturer 工厂造 AudioCapturer(interface 不能 new)+ readData 异步读取 PCM 裸数据
鸿蒙 createAudioCapturer + readData API 真名坑:audio.createAudioCapturer(options: AudioCapturerOptions, callback?: AsyncCallback<AudioCapturer>): Promise<AudioCapturer> 工厂函数造 AudioCapturer 实例(AudioCapturer 是 interface 不能 new audio.AudioCapturer());AudioCapturerOptions interface 含两必填对象 streamInfo: AudioStreamInfo(音频流信息,同 AudioRendererOptions.streamInfo)+ capturerInfo: AudioCapturerInfo(录音器信息);AudioCapturerInfo interface 含两必填字段 source: SourceType(音频源 enum 常量,SOURCE_TYPE_MIC=0 麦克风 /SOURCE_TYPE_VOICE_RECOGNITION=3 语音识别 /SOURCE_TYPE_VOICE_COMMUNICATION=4 语音通信)/capturerFlags: number(录音器标志位数字);AudioCapturer.readData(buffer: ArrayBuffer): Promise<void> 异步读取 PCM 裸数据到 buffer(与 AudioRenderer.writeData(buffer) 反向操作);AudioCapturer 状态机 initialize → prepare → start → pause/stop → release(同 AudioRenderer 状态机);鸿蒙坑:SourceType enum 常量 SOURCE_TYPE_MIC=0/SOURCE_TYPE_VOICE_RECOGNITION=3/SOURCE_TYPE_VOICE_COMMUNICATION=4 不是字符串(enum 值是数字 0/3/4);录音需 ohos.permission.MICROPHONE 权限(module.json5 的 requestPermissions 配置 ohos.permission.MICROPHONE);React navigator.mediaDevices.getUserMedia({ audio: true }) 返回 Promise<MediaStream>(浏览器处理权限),鸿蒙 createAudioCapturer 需手动配置 ohos.permission.MICROPHONE 权限。
五、一句话哲学
写鸿蒙 ArkTS 记住:audio 不是浏览器 Web Audio API 是「createAudioRenderer 工厂造 AudioRenderer」——鸿蒙 6.1 API 23
@ohos.multimedia.audionamespace(API 9+,鸿蒙 6.1 API 23 基座,audio.createAudioRenderer(options)/audio.createAudioCapturer(options)/audio.getAudioManager()工厂造AudioRenderer/AudioCapturer/AudioManager实例 +AudioRenderer/AudioCapturer/AudioManagerinterface 不能 new +AudioStreamInfo/AudioRendererInfo/AudioCapturerInfo/AudioRendererOptions/AudioCapturerOptionsinterface 配置对象 +AudioSampleFormat/AudioChannel/AudioEncodingType/AudioVolumeType/AudioSampleRate/AudioStreamUsage/AudioSourceUsage/RendererState/CapturerStateenum 常量不是字符串 enum 值数字,SysCap SystemCapability.Multimedia.Audio.Core / SystemCapability.Multimedia.Audio.Renderer / SystemCapability.Multimedia.Audio.Capturer,@atomicservice)。根因不是 Promise 是工厂——audio.createAudioRenderer(options: AudioRendererOptions, callback?: AsyncCallback<AudioRenderer>): Promise<AudioRenderer>工厂函数造AudioRenderer实例(✅const audioRenderer: audio.AudioRenderer = await audio.createAudioRenderer(rendererOptions)工厂造实例,❌new audio.AudioRenderer()触发'AudioRenderer' only refers to a type, but is being used as a value here编译错,AudioRenderer是 interface 不是 class 没有 constructor,options: AudioRendererOptions必传不传 options 编译错Expected 1 arguments, but got 0,跟篇 7HttpRequestinterface 不能 new 用http.createHttp()工厂造实例、篇 13ImageSourceinterface 不能 new 用image.createImageSource()工厂造实例、篇 14CameraManagerinterface 不能 new 用camera.getCameraManager(context)工厂造实例同理,Reactnew AudioContext()造音频上下文 class + constructor 一步到位差异,鸿蒙createAudioRenderer(options)工厂造AudioRenderer实例 interface + 工厂模式需传AudioRendererOptions配置),AudioSampleFormatenum 常量SAMPLE_FORMAT_U8=0/SAMPLE_FORMAT_S16LE=1/SAMPLE_FORMAT_S24LE=2/SAMPLE_FORMAT_S32LE=3/SAMPLE_FORMAT_F32LE=4不是字符串'SAMPLE_FORMAT_S16LE'(❌'SAMPLE_FORMAT_S16LE'触发Type 'string' is not assignable to type 'AudioSampleFormat'编译错,❌1触发Type 'number' is not assignable to type 'AudioSampleFormat'编译错 ArkTS 严格模式 enum 类型不接受裸数字,✅audio.AudioSampleFormat.SAMPLE_FORMAT_S16LEenum 常量 enum 值是数字1不是字符串,React WebAudioAudioBuffer.getChannelData()返回Float32Array归一化浮点 PCM [-1.0, 1.0] 固定SAMPLE_FORMAT_F32LE格式差异,鸿蒙AudioSampleFormatenum 提供 5 种 PCM 格式可选),AudioChannelenum 常量CHANNEL_1=1/CHANNEL_2=2/ … /CHANNEL_8=8不是字符串'CHANNEL_2'(❌'CHANNEL_2'触发Type 'string' is not assignable to type 'AudioChannel'编译错,❌2触发Type 'number' is not assignable to type 'AudioChannel'编译错,✅audio.AudioChannel.CHANNEL_2enum 常量 enum 值是数字2不是字符串,React WebAudioAudioBuffer.numberOfChannels是number差异,鸿蒙AudioChannelenum 常量指定声道数 enum 值 1~8 对应 mono/stereo/5.1/7.1),AudioRendererOptionsinterface 含streamInfo: AudioStreamInfo+rendererInfo: AudioRendererInfo两必填对象(缺一编译错Property 'xxx' is missing in type),AudioStreamInfointerface 含四必填 enum 常量字段sampleRate: AudioSampleRate采样率 enum 常量SAMPLE_RATE_8000=8000/SAMPLE_RATE_44100=44100/SAMPLE_RATE_48000=48000等不是字符串 enum 值是数字 8000/44100/48000,audioChannels: AudioChannel声道数 enum 常量,sampleFormat: AudioSampleFormat采样格式 enum 常量,encodingType: AudioEncodingType编码类型 enum 常量ENCODING_TYPE_PCM=0/ENCODING_TYPE_MP3=1/ENCODING_TYPE_AAC=2不是字符串 enum 值是数字 0/1/2,缺任何一个编译错),AudioRendererInfointerface 含两必填字段usage: StreamUsage流用途 enum 常量STREAM_USAGE_MEDIA=1/STREAM_USAGE_VOICE_COMMUNICATION=2/STREAM_USAGE_NOTIFICATION_RINGTONE=5不是字符串 enum 值是数字 1/2/5,rendererFlags: number渲染器标志位数字,React WebAudioAudioContext()无需配置默认 44100Hz stereo float32 差异,鸿蒙AudioRendererOptions需显式配置AudioStreamInfo四必填 enum 常量声明式配置编译期检查字段名和 enum 常量),AudioRenderer.writeData(buffer: ArrayBuffer): Promise<void>异步写入 PCM 裸数据到播放器——buffer 是ArrayBuffer原始字节 PCM 裸数据不是 React WebAudioAudioBuffer对象(❌const audioBuffer: audio.AudioBuffer = new audio.AudioBuffer()触发Property 'AudioBuffer' does not exist on type 'audio'编译错,❌audioBuffer.getChannelData(0)触发Property 'getChannelData' does not exist编译错 React WebAudioAudioBuffer才有getChannelData方法,✅await audioRenderer.writeData(pcmBuffer)写入ArrayBufferPCM 裸数据,React WebAudioAudioBuffer.getChannelData(0)返回Float32Array归一化浮点 PCM [-1.0, 1.0] 固定SAMPLE_FORMAT_F32LE格式差异,鸿蒙writeData(buffer)接收ArrayBufferPCM 裸数据格式由AudioStreamInfo.sampleFormatenum 常量决定),AudioRenderer状态机严格 7 步——newcreateAudioRenderer工厂造实例 →initialize(): Promise<void>状态机初始化分配播放器内部资源 →prepare(): Promise<void>状态机准备分配音频硬件资源 →start(): Promise<void>状态机开始播放开始播放写入的 PCM 数据 →pause(): Promise<void>状态机暂停暂停播放但保留资源可start()恢复 →stop(): Promise<void>状态机停止停止播放释放部分资源可start()恢复 →release(): Promise<void>状态机释放释放所有资源不可恢复(必须严格按initialize→prepare→start→pause/stop→release顺序调用颠倒顺序或跳步会报错如start前prepare报「未准备好」错release后start报「已释放」错,release()必须在aboutToDisappear生命周期调释放音频硬件资源不释放会导致后续应用无法播放音频类似篇 14CaptureSession.release()释放相机硬件资源,React WebAudiosource.start()一步到位播放浏览器内部状态机无需显式状态转换差异,鸿蒙AudioRenderer状态机 Android AudioTrack 风格)。createAudioRenderer 工厂造 AudioRenderer(interface 不能 new,options 必传)+ AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0/S16LE=1/S24LE=2/S32LE=3/F32LE=4 不是字符串 enum 值数字 + AudioChannel enum 常量 CHANNEL_1=1/CHANNEL_2=2/…/CHANNEL_8=8 不是字符串 enum 值数字 + AudioRendererOptions streamInfo + rendererInfo 两必填对象 AudioStreamInfo 四必填 enum 常量 sampleRate + audioChannels + sampleFormat + encodingType + AudioRendererInfo 两必填 usage + rendererFlags + writeData 写入 ArrayBuffer PCM 裸数据(不是 React WebAudio AudioBuffer)SAMPLE_FORMAT_S16LE 每采样 2 字节小端有符号整数 + AudioRenderer 状态机 initialize→prepare→start→pause→stop→release 严格顺序异步 + aboutToDisappear 生命周期 release 释放音频硬件资源 + createAudioCapturer 录音 AudioCapturerOptions streamInfo + capturerInfo 两必填对象 AudioCapturerInfo 两必填 source: SourceType enum 常量 SOURCE_TYPE_MIC=0/VOICE_RECOGNITION=3/VOICE_COMMUNICATION=4 不是字符串 + capturerFlags + readData 异步读取 PCM 裸数据 + 录音需 ohos.permission.MICROPHONE 权限 是鸿蒙 6.1 @ohos.multimedia.audio 音频坑核心!
能力系列回链
- 鸿蒙 7.0 新特性篇 1~17(沉浸式毛玻璃/Component3D/智能体框架/方舟引擎/星盾安全/星河互联/空间音频/可变字体/游戏快启/分布式数据盾/LTPO 可变帧率/AI 文档识别/多形态服务窗口/AI 反诈/机密计算/空间计算/小艺全面进化)
- 鸿蒙 6.1 API 23 开发坑系列篇 1~14 见前文回链(ArkUI.modifier/componentSnapshot/node/UIContext/observer 装饰器坑 + animator/http/fs/router/promptAction/measure/curves/image/camera 非 UI 系坑)
- 鸿蒙 6.1 API 23 开发坑系列篇 15「@ohos.multimedia.audio 音频坑」——createAudioRenderer 工厂造 AudioRenderer(interface 不能 new,options 必传)+ AudioSampleFormat enum 常量 SAMPLE_FORMAT_U8=0/S16LE=1/S24LE=2/S32LE=3/F32LE=4 不是字符串 + AudioChannel enum 常量 CHANNEL_1=1/CHANNEL_2=2/…/CHANNEL_8=8 不是字符串 + AudioRendererOptions streamInfo + rendererInfo 两必填对象 AudioStreamInfo 四必填 enum 常量 + AudioRendererInfo 两必填 usage + rendererFlags + writeData 写入 ArrayBuffer PCM 裸数据(不是 React WebAudio AudioBuffer)+ AudioRenderer 状态机 initialize→prepare→start→pause→stop→release 严格顺序异步 + aboutToDisappear 生命周期 release 释放音频硬件资源 + createAudioCapturer 录音 AudioCapturerOptions streamInfo + capturerInfo 两必填对象 + readData 异步读取 PCM 裸数据 + 录音需 ohos.permission.MICROPHONE 权限(本文)
更多推荐




所有评论(0)