HarmonyNote.TOP鸿蒙开发笔记

AudioCapturer录音与AudioRenderer播放音频实践

开发手机APP应用,录音与播放音频是很常见的需求,在鸿蒙应用开发中,@kit.AudioKit组件提供支持PCM格式录音的工具AudioCapturer,以及用于播放PCM格式音频的工具AudioRenderer。要想用AudioCapturer实现录音功能,需要先声明一个音频流配置实例,代码如下:

一、AudioCapturer实现录音功能第一步

audioConfig: audio.AudioStreamInfo = {
  // 采样率,这里设置为44100Hz(标准的CD质量采样率)
  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
  // 声道数,这里设置为1(单声道)
  channels: audio.AudioChannel.CHANNEL_1,
  // 采样格式,这里设置为16位有符号整数,小端序(SAMPLE_FORMAT_S16LE)
  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
  // 编码类型,这里设置为原始音频(ENCODING_TYPE_RAW)
  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
}

samplingRate参数根据实际录音场景建议:语音通话(16000Hz、节省带宽),音乐录制(44100Hz 或 48000Hz),Hi-Res音频(48000Hz 或更高)。完整samplingRate采样率参数可选项有如下几种:

enum AudioSamplingRate {
  SAMPLE_RATE_8000 = 8000,    // 8kHz
  SAMPLE_RATE_11025 = 11025,  // 11.025kHz
  SAMPLE_RATE_12000 = 12000,  // 12kHz
  SAMPLE_RATE_16000 = 16000,  // 16kHz
  SAMPLE_RATE_22050 = 22050,  // 22.05kHz
  SAMPLE_RATE_24000 = 24000,  // 24kHz
  SAMPLE_RATE_32000 = 32000,  // 32kHz
  SAMPLE_RATE_44100 = 44100,  // 44.1kHz (CD标准)
  SAMPLE_RATE_48000 = 48000,  // 48kHz (专业音频标准)
  SAMPLE_RATE_64000 = 64000,  // 64kHz
  SAMPLE_RATE_96000 = 96000   // 96kHz (高保真)
}

channels参数根据实际录音场景建议:语音记录(单声道、减少文件体积),音乐播放(立体声),影院体验(5.1/7.1环绕声)。完整channels声道数参数可选项有如下几种:

enum AudioChannel {
  CHANNEL_1 = 1,   // 单声道
  CHANNEL_2 = 2,   // 立体声
  CHANNEL_3 = 3,
  CHANNEL_4 = 4,
  CHANNEL_5 = 5,
  CHANNEL_6 = 6,   // 5.1环绕声
  CHANNEL_7 = 7,
  CHANNEL_8 = 8    // 7.1环绕声
}

sampleFormat参数根据实际录音场景建议:S16LE​(兼容性最好,适合大部分场景),F32LE​(专业音频处理保留更高精度),设备兼容性(Android设备主要支持S16LE)。完整sampleFormat采样格式参数可选性有如下几种:

enum AudioSampleFormat {
  SAMPLE_FORMAT_INVALID = -1,      // 无效格式
  SAMPLE_FORMAT_U8 = 1,             // 8位无符号整型
  SAMPLE_FORMAT_S16LE = 2,          // 16位有符号整型小端序
  SAMPLE_FORMAT_S24LE = 3,          // 24位有符号整型小端序
  SAMPLE_FORMAT_S32LE = 4,          // 32位有符号整型小端序
  SAMPLE_FORMAT_F32LE = 5,          // 32位浮点小端序
  SAMPLE_FORMAT_S24_32LE = 6,       // 24位存储为32位小端序
  SAMPLE_FORMAT_S16BE = 7,          // 16位有符号整型大端序
  SAMPLE_FORMAT_S24BE = 8,          // 24位有符号整型大端序
  SAMPLE_FORMAT_S32BE = 9,          // 32位有符号整型大端序
  SAMPLE_FORMAT_F32BE = 10,         // 32位浮点大端序
  SAMPLE_FORMAT_S24_32BE = 11       // 24位存储为32位大端序
}

encodingType参数根据实际录音场景对比PCM格式和MP3格式如下:

RAW(PCM)
        优点:原始质量,无延迟
        缺点:文件体积大
        使用场景:专业音频处理、实时传输
压缩格式(MP3/AAC)
        优点:体积小
        缺点:有损压缩
        使用场景:音乐存储、网络传输。

完整encodingType编码类型参数有如下几种可选:

enum AudioEncodingType {
  ENCODING_TYPE_INVALID = -1,   // 无效类型
  ENCODING_TYPE_DEFAULT = 0,    // 默认
  ENCODING_TYPE_RAW = 1,        // 原始PCM数据
  ENCODING_TYPE_MP3 = 2,        // MP3编码
  ENCODING_TYPE_AAC = 3,        // AAC编码
  ENCODING_TYPE_AAC_LC = 4,     // AAC-LC
  ENCODING_TYPE_AAC_HE = 5,     // AAC-HE
  ENCODING_TYPE_FLAC = 6        // FLAC无损
}

二、AudioCapturer实现录音功能第二步

声明一个音频源信息和采集标志的配置实例,示例代码如下:

audioSourceConfig: audio.AudioCapturerInfo = {
  // 音频源类型
  source: audio.SourceType.SOURCE_TYPE_MIC,
  // 采集标志位
  capturerFlags: 0
}

source音频源类型参数可选项有如下几种:

enum SourceType {
  SOURCE_TYPE_INVALID = -1,         // 无效音源
  SOURCE_TYPE_MIC = 0,               // 麦克风
  SOURCE_TYPE_VOICE_COMMUNICATION,   // 语音通话
  SOURCE_TYPE_VOICE_RECOGNITION,     // 语音识别
  SOURCE_TYPE_VOICE_CALL,            // 语音呼叫
  SOURCE_TYPE_CAMCORDER,             // 相机录音(同步视频录制)
  SOURCE_TYPE_VOICE_PERFORMANCE,      // 语音表演(如K歌)
  SOURCE_TYPE_ULTRASONIC,            // 超声波(特殊设备)
  SOURCE_TYPE_ECHO_REFERENCE,        // 回声参考源
  SOURCE_TYPE_REMOTE_SUBMIX,         // 远程混音
  SOURCE_TYPE_UNPROCESSED,           // 未处理的原始音频
  SOURCE_TYPE_VOIP,                  // VoIP音频(网络电话)
  SOURCE_TYPE_HOTWORD,               // 唤醒词采集(常亮麦克风)
  SOURCE_TYPE_ECHO,                  // 回声消除参考
  SOURCE_TYPE_FM_TUNER               // FM调谐器
}

source参数根据实际录音场景建议如下:

SOURCE_TYPE_MIC: 普通麦克风采集(默认)
SOURCE_TYPE_VOICE_RECOGNITION: 语音识别(设备会优化降噪)
SOURCE_TYPE_VOIP: 网络电话(优化网络传输延迟)
SOURCE_TYPE_CAMCORDER: 视频录制同步音频
SOURCE_TYPE_HOTWORD: 唤醒词检测(常亮低功耗麦克风)

完整capturerFlags采集标志参数有如下几种可选:

enum AudioCapturerFlags {
  AUDIO_CAPTURER_FLAG_NONE = 0,           // 无标志
  AUDIO_CAPTURER_FLAG_PREFER_LOW_LATENCY = 1, // 优先低延迟
  AUDIO_CAPTURER_FLAG_STREAMING = 2,       // 流式传输(持续采集)
  AUDIO_CAPTURER_FLAG_DEEP_BUFFER = 4,    // 深层缓存(优化功耗)
  AUDIO_CAPTURER_FLAG_FAST = 8            // 快速模式(低延迟路径)
}

三、AudioCapturer实现录音功能第三步

有了上述两个配置实例,就可以初始化一个AudioCapturer录音实例了,示例代码如下:

async initAudioHandler() {
  this.audioHandler = await audio.createAudioCapturer({
    streamInfo: this.audioConfig,
    capturerInfo: this.audioSourceConfig
  })
}

四、AudioCapturer实现录音功能第四步

有了AudioCapturer录音实例后,就可以在开始录音按钮的点击事件里调用AudioCapturer实例的start方法开始录音,正常执行的话会在手机右上角显示正在录音的标志,然后AudioCapturer实例的readData监听事件里,通过将接收到的录音buffer数据写到cache沙箱目录下的xxx.wav文件里。示例代码如下:

Button('开始录音')
.onClick(() => {
  try {
    this.isRecordAudio = true
    this.fileHandler = SandboxFileUtil.cache.create(util.generateRandomUUID() + '.wav')
    this.audioAbsPath = this.fileHandler.path
    this.audioHandler?.start()
    this.audioHandler?.on('readData', (audioBuffer: ArrayBuffer) => {
      if (this.isRecordAudio) {
        SandboxFileUtil.cache.appendWithHandler(this.fileHandler!, audioBuffer)
      }
    })
  } catch (err) {
    showDebugV2(err)
  }
})

①、停止录音

当录音完成后,点击停止录音按钮,在停止录音按钮的点击事件里通过调用AudioCapturer录音实例的stop方法实现停止录音。需要注意的是不要调用AudioCapturer实例的off('readData')方法,因为经过真机测试,第二次再次点击开始录音按钮,on('readData', callback)方法的callback无法接收录音buffer。示例代码如下:

Button('停止录音')
.onClick(() => {
  this.isRecordAudio = false
  this.audioHandler?.stop()
  fileIo.closeSync(this.fileHandler)
  // 停止录音不要调用off('readData')方法
  // this.audioHandler?.off('readData')
})

五、第五步:AudioRenderer实例播放PCM格式音频

声明AudioRenderer实例的第一个参数复用第一步中的audioConfig,第二个参数需要声明一个音频输出参数的配置实例,代码如下:

audioRendererConfig: audio.AudioRendererInfo = {、
  // 渲染器标志
  rendererFlags: 0,
  // 音频用途场景
  usage: audio.StreamUsage.STREAM_USAGE_MOVIE
}

其中rendererFlags(渲染器标志)参数可选值:0(普通模式),1(低延迟模式);低延迟模式(标志为1)仅在某些支持低延迟的硬件设备上有效,若不支持则会回退到普通模式。

usage(音频用途)参数定义了音频流的用途,系统根据用途进行不同的音频处理(如音效、重采样策略等)。完整usage可选项有如下几种:

enum StreamUsage {
    STREAM_USAGE_DEFAULT = 0,               // 默认用途
    STREAM_USAGE_MUSIC = 1,                 // 音乐播放
    STREAM_USAGE_VOICE_COMMUNICATION = 2,    // 语音通话
    STREAM_USAGE_VOICE_ASSISTANT = 3,       // 语音助手
    STREAM_USAGE_ALARM = 4,                 // 闹钟
    STREAM_USAGE_NOTIFICATION = 5,          // 通知音
    STREAM_USAGE_RINGTONE = 6,              // 铃声
    STREAM_USAGE_SYSTEM = 7,                // 系统声音
    STREAM_USAGE_MOVIE = 8,                 // 电影(视频播放)
    STREAM_USAGE_GAME = 9,                  // 游戏音效
}

usage参数根据实际播放场景建议如下:

​普通音频播放​:STREAM_USAGE_MUSIC
​视频播放​:STREAM_USAGE_MOVIE
​游戏音效​:STREAM_USAGE_GAME
​语音通话​:STREAM_USAGE_VOICE_COMMUNICATION

六、第六步声明一个用于播放PCM格式音频的AudioRenderer实例

复用第一步中的audioConfig,结合第五步中声明的audioRendererConfig,声明一个用于播放PCM格式音频的AudioRenderer实例。示例代码如下:

audioRendererOptions: audio.AudioRendererOptions = {
  streamInfo: this.audioConfig,
  rendererInfo: this.audioRendererConfig
}
renderHandler = await audio.createAudioRenderer(this.audioRendererOptions)

点击播放录音按钮,在点击事件里调用renderHandler的start方法开始播放PCM格式的录音音频文件。示例代码如下:

Button('播放录音')
.onClick(() => {
  this.renderHandler.start(this.audioAbsPath)
})

一定要确保声明AudioCapturer录音实例和AudioRenderer播放音频实例时的第一个参数一定要用相同的audioConfig,否则会出现播放杂音等现象。