HarmonyOS鸿蒙Next中应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据
HarmonyOS鸿蒙Next中应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据 【问题描述】: 应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据
【问题现象】: NA
【版本信息】: NA
【复现代码】: NA
【尝试解决方案】: NA
开发者你好,若是需要将其他设备作为录音输入源可以参考下以下方案:
【背景知识】
- AVInputCastPicker是录音设备选择组件,可用于切换音频输入设备,仅在PC/2in1设备可用,且该接口在API version 20之后支持。
- 音频流类型是定义音频数据播放和录制方式的关键属性,在实际的使用过程中,不同的音频流,在音量大小、设备路由、并发策略上存在差异。系统可以通过音频流所附带的使用场景信息,为不同的音频流制定合适的处理策略,以达到更好的用户体验。音频流录制场景的信息,可以通过SourceType进行描述。
【解决方案】
-
在PC/2in1设备上,在需要切换设备的通话界面创建AVInputCastPicker组件,实现音频输入设备的选择。 参考代码如下:
import { AVInputCastPicker } from '@kit.AVSessionKit'; build() { Row() { Column() { // 创建AVInputCastPicker组件,并设置大小。 AVInputCastPicker() .size({ height:45, width:45 }) } } } -
非PC/2in1设备,无法指定录音时的音频输入设备,系统会根据音频流类型自动选择音频输入设备:
- 单独录音时,当录制流的SourceType选择为SOURCE_TYPE_VOICE_RECOGNITION(语音识别源)时,连接蓝牙会自动使用蓝牙作为音频输入设备。
- 语音通话时,当录制流的SourceType选择为SOURCE_TYPE_VOICE_COMMUNICATION,播放流的StreamUsage设置为STREAM_USAGE_VOICE_COMMUNICATION,连接蓝牙会自动使用蓝牙作为音频输入设备。
- 在其他场景下,系统使用内置麦克风作为音频输入设备。
系统提供了多样化的API,来帮助开发者完成音频录制的开发,不同的API适用于不同录音输出格式、音频使用场景或不同开发语言。 需要录制pcm的音频数据可以使用以下方式:
AudioCapturer:用于音频输入的ArkTS/JS API,仅支持PCM格式,需要应用持续读取音频数据进行工作。应用可以在音频输出后添加数据处理,要求开发者具备音频处理的基础知识,适用于更专业、更多样化的媒体录制应用开发。
OHAudio:用于音频输入的Native API,此API在设计上实现归一,同时支持普通音频通路和低时延通路。仅支持PCM格式,适用于依赖Native层实现音频输入功能的场景。
以AudioCapturer录制音频为例:
-
配置音频采集参数并创建AudioCapturer实例,音频采集参数的详细信息可以查看AudioCapturerOptions。注意:当设置Mic音频源时,需要申请麦克风权限ohos.permission.MICROPHONE,参考示例代码如下:
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, // 音源类型:Mic音频源。根据业务场景配置,参考SourceType。 capturerFlags: 0 // 音频采集器标志。 }; let audioCapturerOptions: audio.AudioCapturerOptions = { streamInfo: audioStreamInfo, capturerInfo: audioCapturerInfo }; audio.createAudioCapturer(audioCapturerOptions, (err, data) => { if (err) { console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`); } else { console.info('Invoke createAudioCapturer succeeded.'); let audioCapturer = data; } }); -
调用on(‘readData’)方法,订阅监听音频数据读入回调,参考示例代码如下:
import { BusinessError } from '@kit.BasicServicesKit'; import { fileIo as fs } from '@kit.CoreFileKit'; import { common } from '@kit.AbilityKit'; class Options { offset?: number; length?: number; } let bufferSize: number = 0; // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext。 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; let path = context.cacheDir; let filePath = path + '/StarWars10s-2C-48000-4SW.pcm'; let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); let readDataCallback = (buffer: ArrayBuffer) => { let options: Options = { offset: bufferSize, length: buffer.byteLength }; fs.writeSync(file.fd, buffer, options); bufferSize += buffer.byteLength; }; audioCapturer.on('readData', readDataCallback); -
调用start()方法进入running状态,开始录制音频,参考示例代码如下:
audioCapturer.start((err: BusinessError) => { if (err) { console.error(`Capturer start failed, code is ${err.code}, message is ${err.message}`); } else { console.info('Capturer start success.'); } }); -
调用release()方法销毁实例,释放资源,参考示例代码如下:
audioCapturer.release((err: BusinessError) => { if (err) { console.error(`capturer release failed, code is ${err.code}, message is ${err.message}`); } else { console.info('capturer released.'); } });
更多关于HarmonyOS鸿蒙Next中应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
Q1: PCM 必须要先写入文件再读取,不能实时获取 PCM 流吗?
A: 可以使用 AudioCapturer API 实时获取 PCM 流。
相关文档:
如果此API不符合您的需求,可以参考下面获取音频流的方法:
1. 使用 AudioCapturer 开发音频录制功能
可以在调用 createAudioCapturer 接口时,传入对应的 SourceType。
createAudioCapturer 的参数 options 类型为 AudioCapturerOptions,包含 AudioCapturerInfo 采集器信息,使用 AudioCapturerInfo.source 可指定 SourceType 音源类型。
2. 使用 OHAudio 开发音频录制功能
可以在调用 OH_AudioStreamBuilder_SetCapturerInfo 接口时,传入对应的 OH_AudioStream_SourceType 指定音源类型。
3. 使用 AVRecorder 开发音频录制功能
可以在调用 AVRecorder.prepare 接口时,传入对应的 AudioSourceType。
AVRecorder.prepare 的参数 config 类型为 AVRecorderConfig,使用 AVRecorderConfig.audioSourceType 可指定音源类型。
Q2: 音频源只能来自麦克风吗?
A: 不是的。可以通过设置 AudioCapturerInfo 中的 SourceType 进行调整。
代码示例:
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, // 音源类型:Mic音频源。根据业务场景配置,参考SourceType
capturerFlags: 0 // 音频采集器标志
};
let audioCapturerOptions: audio.AudioCapturerOptions = {
streamInfo: audioStreamInfo,
capturerInfo: audioCapturerInfo
};
audio.createAudioCapturer(audioCapturerOptions, (err, data) => {
if (err) {
console.error(`Invoke createAudioCapturer failed, code is ${err.code}, message is ${err.message}`);
} else {
console.info('Invoke createAudioCapturer succeeded.');
let audioCapturer = data;
}
});
说明:
source字段可以设置为不同的SourceType值,如SOURCE_TYPE_MIC(麦克风)、SOURCE_TYPE_VOICE_COMMUNICATION(语音通话)等- 根据业务场景选择合适的音源类型,参考 SourceType 枚举值
参考文档
希望HarmonyOS能继续推出更多实用的功能,满足用户的不同需求。
pcm必须要先写入文件再读取,不能实时获取pcm流嘛? 然后是音频源只能来自麦克风?
开发者你好,可以通过 AVRecorder API 来录制音频并获取 PCM 数据。
解决方案
方案一:使用 AVRecorder 录制音频并获取 PCM 数据
方案说明:HarmonyOS 提供了完整的 AVRecorder API,支持录制音频并保存为文件,然后可以从文件中读取 PCM 数据。这是当前推荐的标准方式。详细使用指南请参考:使用 AVRecorder 进行音视频录制。
实现步骤:
- 配置应用权限
在 module.json5 文件中添加音频录制权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "需要麦克风权限进行音频录制",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
}
}
- 导入 AVRecorder 模块
import { media } from '@kit.MediaKit'
- 创建 AVRecorder 实例并配置参数
import { media } from '@kit.MediaKit'
import { fileIo } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
@ComponentV2
struct AudioRecordPage {
private avRecorder: media.AVRecorder | null = null
@Local isRecording: boolean = false
private pcmFilePath: string = '' // PCM文件路径
/**
* 初始化音频录制器
*/
async initAVRecorder(): Promise<void> {
try {
// 创建AVRecorder实例
this.avRecorder = await media.createAVRecorder()
// 准备输出文件路径
const context = getContext(this) as common.UIAbilityContext
const filesDir = context.filesDir
this.pcmFilePath = `${filesDir}/audio_recording.pcm`
// 配置音频录制参数
const avRecorderConfig: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频源:麦克风
audioEncoder: media.AudioEncoder.AUDIO_ENCODER_PCM, // 音频编码器:PCM格式
audioSampleRate: media.AudioSampleRate.SAMPLE_RATE_44100, // 采样率:44.1kHz
audioChannels: media.AudioChannel.CHANNEL_2, // 声道:立体声
audioBitrate: 32000, // 比特率:32kbps
outputFormat: media.ContainerFormatType.CFT_PCM, // 输出格式:PCM
url: `file://${this.pcmFilePath}` // 输出文件路径
}
// 配置录制器
await this.avRecorder.prepare(avRecorderConfig)
// 注册状态变化回调
this.avRecorder.on('stateChange', (state: media.AVRecorderState, reason: media.StateChangeReason) => {
console.info(`AVRecorder状态变化: ${state}, 原因: ${reason}`)
})
// 注册错误回调
this.avRecorder.on('error', (error: BusinessError) => {
console.error(`AVRecorder错误: ${error.message}`)
})
console.info('音频录制器初始化成功')
} catch (error) {
console.error(`初始化音频录制器失败: ${error.message}`)
}
}
/**
* 开始录制
*/
async startRecording(): Promise<void> {
if (!this.avRecorder) {
await this.initAVRecorder()
}
try {
// 开始录制
await this.avRecorder.start()
this.isRecording = true
console.info('开始录制音频')
} catch (error) {
console.error(`开始录制失败: ${error.message}`)
}
}
/**
* 停止录制并读取PCM数据
*/
async stopRecording(): Promise<void> {
if (!this.avRecorder || !this.isRecording) {
return
}
try {
// 停止录制
await this.avRecorder.stop()
this.isRecording = false
console.info('停止录制')
// 从文件中读取PCM数据
await this.readPCMDataFromFile()
} catch (error) {
console.error(`停止录制失败: ${error.message}`)
}
}
/**
* 从文件中读取PCM数据
*/
private async readPCMDataFromFile(): Promise<void> {
try {
const file = await fileIo.open(this.pcmFilePath, fileIo.OpenMode.READ_ONLY)
const stat = await file.stat()
const fileSize = stat.size
// 读取整个PCM文件
const buffer = new ArrayBuffer(fileSize)
await file.read(buffer)
await file.close()
console.info(`读取PCM数据,大小: ${fileSize} 字节`)
// 处理PCM数据
this.processPCMData(buffer)
} catch (error) {
console.error(`读取PCM文件失败: ${error.message}`)
}
}
/**
* 处理PCM数据
*/
private processPCMData(buffer: ArrayBuffer): void {
// 将ArrayBuffer转换为Int16Array进行处理(16位PCM)
const pcmData = new Int16Array(buffer)
// 示例:计算音频数据的平均值(简单的音量检测)
let sum = 0
for (let i = 0; i < pcmData.length; i++) {
sum += Math.abs(pcmData[i])
}
const average = sum / pcmData.length
console.info(`PCM数据平均值: ${average}`)
console.info(`PCM样本数: ${pcmData.length}`)
// 您可以在这里进行其他处理,如:
// - 音频分析
// - 音频可视化
// - 音频特效处理
// - 转换为其他格式
}
/**
* 释放资源
*/
async release(): Promise<void> {
if (this.avRecorder) {
// 释放AVRecorder资源
await this.avRecorder.release()
this.avRecorder = null
}
}
build() {
Column() {
Text('音频录制示例')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Text(this.isRecording ? '正在录制...' : '未录制')
.fontSize(16)
.fontColor(this.isRecording ? '#FF0000' : '#666666')
.margin({ bottom: 20 })
Button(this.isRecording ? '停止录制' : '开始录制')
.onClick(() => {
if (this.isRecording) {
this.stopRecording()
} else {
this.startRecording()
}
})
.width(200)
.height(50)
.margin({ bottom: 20 })
Text('点击按钮开始录制音频')
.fontSize(14)
.fontColor('#999999')
}
.width('100%')
.height('100%')
.padding(20)
.onDisappear(() => {
this.release()
})
}
}
关键API说明:
- media.createAVRecorder():创建音视频录制器实例
- AVRecorderConfig:配置音频录制参数,包括采样率、声道、编码器等
- prepare():准备录制器,配置参数
- start() / stop():开始和停止录制
- AudioEncoder.AUDIO_ENCODER_PCM:指定音频编码器为PCM格式
- ContainerFormatType.CFT_PCM:指定容器格式为PCM
方案二:实时读取 PCM 文件数据
方案说明:如果需要实时处理 PCM 数据,可以在录制过程中定期读取文件,实现类似实时处理的效果。这种方式适用于需要实时音频分析的场景。
实现步骤:
import { media } from '@kit.MediaKit'
import { fileIo } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
@ComponentV2
struct RealTimeAudioPage {
private avRecorder: media.AVRecorder | null = null
private pcmFilePath: string = ''
@Local isRecording: boolean = false
private readTimer: number | null = null // 定时器用于定期读取
/**
* 实时录制并读取PCM数据
*/
async startRecordingWithRealTimeRead(): Promise<void> {
try {
this.avRecorder = await media.createAVRecorder()
const context = getContext(this) as common.UIAbilityContext
this.pcmFilePath = `${context.filesDir}/audio_real_time.pcm`
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
audioEncoder: media.AudioEncoder.AUDIO_ENCODER_PCM,
audioSampleRate: media.AudioSampleRate.SAMPLE_RATE_44100,
audioChannels: media.AudioChannel.CHANNEL_2,
audioBitrate: 32000,
outputFormat: media.ContainerFormatType.CFT_PCM,
url: `file://${this.pcmFilePath}`
}
await this.avRecorder.prepare(config)
await this.avRecorder.start()
this.isRecording = true
// 开始定时读取PCM数据(每100ms读取一次)
this.startReadingPCMData()
console.info('开始录制并实时读取PCM数据')
} catch (error) {
console.error(`录制失败: ${error.message}`)
}
}
/**
* 开始定时读取PCM数据
*/
private startReadingPCMData(): void {
let lastPosition = 0
this.readTimer = setInterval(async () => {
if (!this.isRecording) {
return
}
try {
const file = await fileIo.open(this.pcmFilePath, fileIo.OpenMode.READ_ONLY)
const stat = await file.stat()
const currentSize = stat.size
if (currentSize > lastPosition) {
// 读取新增的数据
await file.seek(lastPosition, fileIo.SeekMode.SEEK_START)
const newDataSize = currentSize - lastPosition
const buffer = new ArrayBuffer(newDataSize)
await file.read(buffer)
await file.close()
// 处理新读取的PCM数据
this.realTimeProcess(buffer)
lastPosition = currentSize
} else {
await file.close()
}
} catch (error) {
console.error(`读取PCM数据失败: ${error.message}`)
}
}, 100) // 每100ms读取一次
}
/**
* 停止录制
*/
async stopRecording(): Promise<void> {
if (this.readTimer !== null) {
clearInterval(this.readTimer)
this.readTimer = null
}
if (this.avRecorder && this.isRecording) {
await this.avRecorder.stop()
this.isRecording = false
}
}
/**
* 实时处理PCM数据
*/
private realTimeProcess(buffer: ArrayBuffer): void {
const pcmData = new Int16Array(buffer)
// 示例:实时音量检测
let maxAmplitude = 0
for (let i = 0; i < pcmData.length; i++) {
const amplitude = Math.abs(pcmData[i])
if (amplitude > maxAmplitude) {
maxAmplitude = amplitude
}
}
// 转换为分贝值(简化计算)
const db = 20 * Math.log10(maxAmplitude / 32768)
console.info(`实时音量: ${db.toFixed(2)} dB`)
}
build() {
Column() {
// UI内容
}
}
}
关键点:
- 使用定时器定期读取PCM文件,实现实时处理效果
- 记录上次读取位置,只读取新增的数据
- 适用于需要实时音频分析的场景
- 注意性能影响,读取频率不宜过高
注意事项
- 权限申请:必须在
module.json5中声明ohos.permission.MICROPHONE权限 - PCM数据格式:PCM数据是原始音频数据,需要根据采样率、声道数、位深度来正确解析
- 资源释放:使用完毕后要调用
release()释放AVRecorder资源 - 文件路径:确保文件路径正确,使用
getContext()获取应用上下文路径 - 数据格式:ArrayBuffer 格式的PCM数据可以转换为 Int16Array(16位)或 Int8Array(8位)进行处理
- 采样率选择:常用采样率有 44100Hz(CD质量)和 48000Hz(专业音频)
参考文档
如果以上方案对您有帮助,欢迎采纳答案,也欢迎继续提问交流!🙏
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
pcm必须要先写入文件再读取,不能实时获取pcm流嘛? 然后是音频源只能来自麦克风?
详情请查看 4楼 回帖。回复无法使用 markdown 格式排版 😂,
在HarmonyOS Next中,应用的音频录制权限已开放,可通过AudioCapturer API实现。使用createAudioCapturer()方法初始化音频采集器,配置参数包括采样率、声道数和采样格式(如PCM)。调用start()开始采集后,通过read()方法循环读取PCM数据流。需在module.json5中声明ohos.permission.MICROPHONE权限。
在HarmonyOS Next中,应用可通过AudioCapturer类实现音频录制并获取PCM数据,无需依赖麦克风权限。以下是关键步骤:
-
配置音频参数:
使用AudioCapturerOptions设置采样率、声道数、位深等参数。示例:let options = { streamUsage: audio.StreamUsage.STREAM_USAGE_MEDIA, sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100, channelCount: 1, sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW }; -
初始化AudioCapturer:
创建实例并启动录制:let audioCapturer = await audio.createAudioCapturer(options); await audioCapturer.start(); -
读取PCM数据:
通过read方法获取原始音频数据(直接为PCM格式):let buffer = await audioCapturer.read(bufferSize, isBlocking); // buffer即为PCM数据,可进一步处理或保存 -
释放资源:
录制完成后调用stop()和release()。
注意事项:
- 确保在
module.json5中声明ohos.permission.MICROPHONE权限(仅用于系统权限校验,实际数据来源不限于麦克风)。 - PCM数据格式由
sampleFormat定义,如S16LE表示16位有符号整数小端序。 - 实时处理时需循环读取缓冲区,避免数据积压。
此方案适用于系统级音频流捕获,包括应用内音频或混音输出。

