HarmonyOS鸿蒙Next中应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据

HarmonyOS鸿蒙Next中应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据 【问题描述】: 应用的音频录制权限有开放出来嘛(不是麦克风) 如果有的话我应该怎么去获音频的pcm数据

【问题现象】: NA

【版本信息】: NA

【复现代码】: NA

【尝试解决方案】: NA

10 回复

开发者你好,若是需要将其他设备作为录音输入源可以参考下以下方案:

【背景知识】

  • AVInputCastPicker是录音设备选择组件,可用于切换音频输入设备,仅在PC/2in1设备可用,且该接口在API version 20之后支持。
  • 音频流类型是定义音频数据播放和录制方式的关键属性,在实际的使用过程中,不同的音频流,在音量大小、设备路由、并发策略上存在差异。系统可以通过音频流所附带的使用场景信息,为不同的音频流制定合适的处理策略,以达到更好的用户体验。音频流录制场景的信息,可以通过SourceType进行描述。

【解决方案】

  1. 在PC/2in1设备上,在需要切换设备的通话界面创建AVInputCastPicker组件,实现音频输入设备的选择。 参考代码如下:

    import { AVInputCastPicker } from '@kit.AVSessionKit';
     
    build() {
      Row() {
        Column() {
          // 创建AVInputCastPicker组件,并设置大小。
          AVInputCastPicker()
            .size({ height:45, width:45 })
        }
      }
    }
    
  2. 非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录制音频为例:

  1. 配置音频采集参数并创建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;
      }
    });
    
  2. 调用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);
    
  3. 调用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.');
      }
    });
    
  4. 调用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 进行音视频录制

实现步骤

  1. 配置应用权限

module.json5 文件中添加音频录制权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "需要麦克风权限进行音频录制",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      }
    ]
  }
}
  1. 导入 AVRecorder 模块
import { media } from '@kit.MediaKit'
  1. 创建 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数据,无需依赖麦克风权限。以下是关键步骤:

  1. 配置音频参数
    使用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
    };
    
  2. 初始化AudioCapturer
    创建实例并启动录制:

    let audioCapturer = await audio.createAudioCapturer(options);
    await audioCapturer.start();
    
  3. 读取PCM数据
    通过read方法获取原始音频数据(直接为PCM格式):

    let buffer = await audioCapturer.read(bufferSize, isBlocking);
    // buffer即为PCM数据,可进一步处理或保存
    
  4. 释放资源
    录制完成后调用stop()release()

注意事项:

  • 确保在module.json5中声明ohos.permission.MICROPHONE权限(仅用于系统权限校验,实际数据来源不限于麦克风)。
  • PCM数据格式由sampleFormat定义,如S16LE表示16位有符号整数小端序。
  • 实时处理时需循环读取缓冲区,避免数据积压。

此方案适用于系统级音频流捕获,包括应用内音频或混音输出。

回到顶部