赞
踩
应用开发过程中很多场景都有音频采集需求,比如聊天功能的发送语音功能,实时语音转文本功能,实时语音通话,实时视频通话等。在Android和iOS端,系统提供了两种形式:
系统还提供了不同形式的API,比如Android:
在鸿蒙化适配的过程中也有音频采集的需求,本文我们一步一步实现音频采集功能。
HarmonyOS 提供了TS与C++两种音频采集接口:
分别介绍这两种语言的API。
使用AudioCapturer录制音频涉及到AudioCapturer实例的创建、音频采集参数的配置、采集的开始与停止、资源的释放等,下面官方给出的状态示意图将方法和状态切换标记的很清晰:
创建capture主要涉及到参数配置:
import { audio } from '@kit.AudioKit'; let audioStreamInfo: audio.AudioStreamInfo = { samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率 channels: audio.AudioChannel.CHANNEL_2, // 通道 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式 }; let audioCapturerInfo: audio.AudioCapturerInfo = { source: audio.SourceType.SOURCE_TYPE_MIC, capturerFlags: 0 }; let audioCapturerOptions: audio.AudioCapturerOptions = { streamInfo: audioStreamInfo, capturerInfo: audioCapturerInfo }; audio.createAudioCapturer(audioCapturerOptions, (err, data) => { if (err) { } else { let audioCapturer = data; } });
参数包含两大块:
on(‘readData’)方法用来订阅监听音频数据读入回调:
let readDataCallback = (buffer: ArrayBuffer) => {
//处理音频流
}
audioCapturer.on('readData', readDataCallback);
start方法用来开始录制:
import { BusinessError } from '@kit.BasicServicesKit';
audioCapturer.start((err: BusinessError) => {
if (err) {
} else {
}
});
stop用来停止录制:
import { BusinessError } from '@kit.BasicServicesKit';
audioCapturer.stop((err: BusinessError) => {
if (err) {
} else {
}
});
release销毁实例,释放资源
import { BusinessError } from '@kit.BasicServicesKit';
audioCapturer.release((err: BusinessError) => {
if (err) {
} else {
}
});
OHAudio是系统在API version 10中引入的一套C API,此API在设计上实现归一,同时支持普通音频通路和低时延通路。仅支持PCM格式,适用于依赖Native层实现音频输入功能的场景。很多音频编码库都是C/C++实现的,在迁移到鸿蒙平台后,采集侧也使用OHAudio C++接口,可以减少数据在TS层与C++层传递的消耗,提高效率。
OHAudio依赖libohaudio.so动态库,通过引入<native_audiostreambuilder.h>
和<native_audiocapturer.h)>头文件,使用音频录制相关API。
OH_AudioStreamBuilder* builder;
OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER);
可参考如下示例:
// 设置音频采样率
OH_AudioStreamBuilder_SetSamplingRate(builder, 48000);
// 设置音频声道
OH_AudioStreamBuilder_SetChannelCount(builder, 2);
// 设置音频采样格式
OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
// 设置音频流的编码类型
OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
// 设置输入音频流的工作场景
OH_AudioStreamBuilder_SetCapturerInfo(builder, AUDIOSTREAM_SOURCE_TYPE_MIC);
参数作用于AudioCapture类似。
// 自定义写入数据函数 int32_t MyOnReadData( OH_AudioCapturer* capturer, void* userData, void* buffer, int32_t length) { // 从buffer中取出length长度的录音数据 return 0; } // 自定义音频流事件函数 int32_t MyOnStreamEvent( OH_AudioCapturer* capturer, void* userData, OH_AudioStream_Event event) { // 根据event表示的音频流事件信息,更新播放器状态和界面 return 0; } // 自定义音频中断事件函数 int32_t MyOnInterruptEvent( OH_AudioCapturer* capturer, void* userData, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint) { // 根据type和hint表示的音频中断信息,更新录制器状态和界面 return 0; } // 自定义异常回调函数 int32_t MyOnError( OH_AudioCapturer* capturer, void* userData, OH_AudioStream_Result error) { // 根据error表示的音频异常信息,做出相应的处理 return 0; } OH_AudioCapturer_Callbacks callbacks; // 配置回调函数 callbacks.OH_AudioCapturer_OnReadData = MyOnReadData; callbacks.OH_AudioCapturer_OnStreamEvent = MyOnStreamEvent; callbacks.OH_AudioCapturer_OnInterruptEvent = MyOnInterruptEvent; callbacks.OH_AudioCapturer_OnError = MyOnError; // 设置音频输入流的回调 OH_AudioStreamBuilder_SetCapturerCallback(builder, callbacks, nullptr);
通过OH_AudioStreamBuilder_SetCapturerCallback函数配置回调函数。
OH_AudioCapturer* audioCapturer;
OH_AudioStreamBuilder_GenerateCapturer(builder, &audioCapturer);
OH_AudioStreamBuilder_Destroy(builder);
我们以录制MP3为例来实现音频采集的全流程实践。
音频采集需要动态申请权限,现在module.json5中声明权限:
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:reason",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
}
],
动态申请权限:
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 atManager.requestPermissionsFromUser(context, permissions).then((data) => { let grantStatus: Array<number> = data.authResults; let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] === 0) { // 用户授权,可以继续访问目标操作 } else { // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限 return; } } // 授权成功 }).catch((err: BusinessError) => { console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); }) }
在aboutToAppera中调用申请权限方法,在授权成功后启动录音
const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
reqPermissionsFromUser(permissions, context);
}
创建C++模块后,配置ohaudio动态库依赖:
cmake_minimum_required(VERSION 3.5.0)
project(audiorecorderdemo)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(capture SHARED napi_init.cpp)
target_link_libraries(capture PUBLIC libace_napi.z.so)
target_link_libraries(capture PUBLIC libohaudio.so)
配置napi方法:
static napi_value start(napi_env env, napi_callback_info info) { return nullptr; } static napi_value stop(napi_env env, napi_callback_info info) { return nullptr; } EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { { "start", nullptr, start, nullptr, nullptr, nullptr, napi_default, nullptr }, { "stop", nullptr, stop, nullptr, nullptr, nullptr, napi_default, nullptr } }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; }
// 自定义写入数据函数 int32_t MyOnReadData( OH_AudioCapturer* capturer, void* userData, void* buffer, int32_t length) { //TODO 从buffer中取出length长度的录音数据 return 0; } // 自定义音频流事件函数 int32_t MyOnStreamEvent( OH_AudioCapturer* capturer, void* userData, OH_AudioStream_Event event) { //TODO 根据event表示的音频流事件信息,更新播放器状态和界面 return 0; } // 自定义音频中断事件函数 int32_t MyOnInterruptEvent( OH_AudioCapturer* capturer, void* userData, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint) { //TODO 根据type和hint表示的音频中断信息,更新录制器状态和界面 return 0; } // 自定义异常回调函数 int32_t MyOnError( OH_AudioCapturer* capturer, void* userData, OH_AudioStream_Result error) { //TODO 根据error表示的音频异常信息,做出相应的处理 return 0; } static napi_value start(napi_env env, napi_callback_info info) { OH_AudioStreamBuilder* builder; OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER); // 设置音频采样率 OH_AudioStreamBuilder_SetSamplingRate(builder, 48000); // 设置音频声道 OH_AudioStreamBuilder_SetChannelCount(builder, 2); // 设置音频采样格式 OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE); // 设置音频流的编码类型 OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW); // 设置输入音频流的工作场景 OH_AudioStreamBuilder_SetCapturerInfo(builder, AUDIOSTREAM_SOURCE_TYPE_MIC); OH_AudioCapturer_Callbacks callbacks; // 配置回调函数 callbacks.OH_AudioCapturer_OnReadData = MyOnReadData; callbacks.OH_AudioCapturer_OnStreamEvent = MyOnStreamEvent; callbacks.OH_AudioCapturer_OnInterruptEvent = MyOnInterruptEvent; callbacks.OH_AudioCapturer_OnError = MyOnError; // 设置音频输入流的回调 OH_AudioStreamBuilder_SetCapturerCallback(builder, callbacks, nullptr); OH_AudioCapturer* audioCapturer; OH_AudioStreamBuilder_GenerateCapturer(builder, &audioCapturer); return nullptr; }
最佳实践一:
为了避免不可预期的行为,在设置音频回调函数时,请确保OH_AudioCapturer_Callbacks的每一个回调都被自定义的回调方法或空指针初始化,比如:
OH_AudioCapturer_Callbacks callbacks;
// 配置回调函数,如果需要监听,则赋值
callbacks.OH_AudioCapturer_OnReadData = MyOnReadData;
callbacks.OH_AudioCapturer_OnInterruptEvent = MyOnInterruptEvent;
// (必选)如果不需要监听,使用空指针初始化
callbacks.OH_AudioCapturer_OnStreamEvent = nullptr;
callbacks.OH_AudioCapturer_OnError = nullptr;
最佳实践二:
对于支持低延时模式的设备,对于延时要求比较高的场景(比如语音通话)可以使用低时延模式创建音频录制构造器,获得更高质量的音频体验:
OH_AudioStream_LatencyMode latencyMode = AUDIOSTREAM_LATENCY_MODE_FAST;
OH_AudioStreamBuilder_SetLatencyMode(builder, latencyMode);
在音频回调中我们对音频数据就行处理,可以交给ASR也可以直接写入文件,下一篇我们实现编码成mp3并写入文件的实践。
OH_AudioCapturer_Stop(builder, &audioCapturer);
OH_AudioStreamBuilder_Destroy(builder);
本文介绍了HarmonyOS 提供的两种音频采集方式:TS层的AudioCapture和C++层的OHAudio,并以OHAudio接口实现了实时音频采集功能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。