评论

收藏

[HarmonyOS] HarmonyOS 录音与音频播放功能技术实现

移动开发 移动开发 发布于:2021-07-08 15:12 | 阅读数:308 | 评论:0

引言
  录音和音频播放在app中是一个很常见的功能,本文将介绍在HarmonyOS 如何使用录音和音频播放功能。

功能介绍

  1.录音

1.1 使用录音前需要先申请录音权限
  在 config.json文件中添加权限声明
"reqPermissions": [  
    {
    "name": "ohos.permission.MICROPHONE",
    "reason": "the app need microphone",
    "usedScene": {
      "ability": [
      "com.iflytek.demo.MainAbility"
      ],
      "when": "always"
    }
    }
  ]
  然后在MainAbility中动态申请麦克风权限
private void requestPermission() {
    if (verifySelfPermission("ohos.permission.MICROPHONE") != IBundleManager.PERMISSION_GRANTED) {
      // 应用未被授予权限
      if (canRequestPermission("ohos.permission.MICROPHONE")) {
        // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
        requestPermissionsFromUser(new String[]{"ohos.permission.MICROPHONE"}, REQUEST_MICROPHONE);
      } else {
        // 显示应用需要权限的理由,提示用户进入设置授权
      }
    } else {
      // 权限已被授予
    }
  }

  /**
   * 权限回调
   */
  @Override
  public void onRequestPermissionsFromUserResult(int requestCode,
                           String[] permissions,
                           int[] grantResults) {
    switch (requestCode) {
      case REQUEST_MICROPHONE: {
        // 匹配requestPermissions的requestCode
        if (grantResults.length > 0 && grantResults[0] == IBundleManager.PERMISSION_GRANTED) {
          // 权限被授予
          // 注意:因时间差导致接口权限检查时有无权限,所以对那些因无权限而抛异常的接口进行异常捕获处理
          AppLog.e("MainAbility", "已经获取到录音权限");
        } else {
          // 权限被拒绝
          AppLog.e("MainAbility", "录音权限被拒绝");
        }
      }
    }
  }
  1.2 录音功能使用的是 AudioCapturer类,主要接口如下:

  | 接口名                                                    | 描述                                               |
  | ---------------------------------------------------- | --------------------------------------------- |
  | AudioCapturer(AudioCapturerInfo audioCapturerInfo) throws IllegalArgumentException | 构造函数,设置录音相关音频参数,使用默认录音设备。 |
  | getMinBufferSize(int sampleRate, int channelCount, int audioFormat) | 获取指定参数条件下所需的最小缓冲区大小。           |
  | addSoundEffect(UUID type, String packageName)                | 增加录音的音频音效。                               |
  | start()                                                      | 开始录音。                                         |
  | read(byte[] data, int offset, int size)                      | 读取音频数据。                                     |
  | stop()                                                       | 停止录音。                                         |
  | release()                                                    | 释放录音资源。                                     |
  初始化AudioCapturer,先通过 AudioStreamInfo设置录音音频基本参数,再通过AudioCapturerInfo设置录音源等信息。
/**
   * 创建默认的录音对象
   */
  public void initConfig() {

    if (audioCapturer != null && audioCapturer.getState() != AudioCapturer.State.STATE_STOPPED) {
      audioCapturer.release();
    }
    audioCapturer = null;
    AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder()
        // 音频采样率 16000
        .sampleRate(AUDIO_SAMPLE_RATE)
        // 录音数据格式 16-bit PCM
        .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
        // 声道设置 单声道
        .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_MONO)
        .build();
    AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder()
        .audioStreamInfo(audioStreamInfo)
        // 录音源
        .audioInputSource(AudioCapturerInfo.AudioInputSource.AUDIO_INPUT_SOURCE_MIC)
        .build();
    audioCapturer = new AudioCapturer(audioCapturerInfo);
    bufferSizeInBytes = AudioCapturer.getMinBufferSize(AUDIO_SAMPLE_RATE, 1, 2);
  }
  开始录音,通过 audioCapturer.read() 获取音频。
/**
   * 开始录音
   *
   * @param listener 音频流的监听回调
   */
  public void startRecord(final RecordListener listener) {
    if (audioCapturer.getState() == AudioCapturer.State.STATE_UNINITIALIZED) {
      throw new IllegalStateException("AudioCapturer need init first");
    }
    if (isRecording()) {
      throw new IllegalStateException("AudioCapturer is in recording now");
    }
    // 开始录音
    audioCapturer.start();
    final byte[] audioData = new byte[bufferSizeInBytes];
    while (isRecording()) {
      int size = audioCapturer.read(audioData, 0, bufferSizeInBytes);
      if (size > 0 && listener != null) {
        if (size == bufferSizeInBytes) {
          // 通过回掉回传录音数据
          listener.onRead(audioData);
        } else {
          // 通过回掉回传录音数据
          final byte[] copy = new byte[size];
          System.arraycopy(audioData, 0, copy, 0, size);
          listener.onRead(copy);
        }
      }
    }
    if (finishCallBack != null) {
      finishCallBack.onFinish();
    }
  }
  停止录音
/**
   * 停止录音
   */
  public synchronized void stopRecord() {
    if (isRecording()) {
      audioCapturer.stop();
    }
  }

  /**
   * 释放资源
   */
  public synchronized void release() {
    if (audioCapturer != null) {
      audioCapturer.release();
      audioCapturer = null;
    }
  }
  2. 音频播放

  HarmonyOS 中,播放音频主要有 AudioRenderer 、Player 、SoundPlayer 3个类
AudioRenderer 用于播放pcm音频流  Player 主要用于播放mp3、m4a等格式的音频
SoundPlayer  用于播放短音频  2.1  AudioRenderer播放pcm音频

/**
   * 播放pcm
   *
   * @param file pcm文件
   */
  private void playPcm(File file) {
    if (file == null || !file.exists()) {
      showToast("文件不存在");
      return;
    }
    AudioStreamInfo streamInfo = new AudioStreamInfo.Builder()
        // 16kHz
        .sampleRate(16000)
        // 混音
        .audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK)
        // 16-bit PCM
        .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
        // 单声道输出
        .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_MONO)
        // 媒体类音频
        .streamUsage(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA)
        .build();

    AudioRendererInfo audioRendererInfo = new AudioRendererInfo.Builder().audioStreamInfo(streamInfo)
        // pcm格式的输出流
        .audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
        .bufferSizeInBytes(1280)
        // false表示分段传输buffer并播放,true表示整个音频流一次性传输到HAL层播放
        .isOffload(false)
        .build();

    AudioRenderer renderer = new AudioRenderer(audioRendererInfo, AudioRenderer.PlayMode.MODE_STREAM);
    renderer.start();
    try {
      FileInputStream inputStream = new FileInputStream(file);
      byte[] temp = new byte[1280];
      while (inputStream.available() > temp.length) {
        int read = inputStream.read(temp);
        // 写入pcm到播放器
        renderer.write(temp, 0, read);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

  }
  2.2 Player 播放mp3

/**
   * 播放音频
   *
   * @param file 源文件位置
   */
  private void playMp3(File file) {
    try {
      player = new Player(getContext());
      FileInputStream in = new FileInputStream(file);
      // 从输入流获取FD对象
      FileDescriptor fd = in.getFD();
      player.setSource(new Source(fd));
      player.prepare();
      player.play();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }<span style="background-color: rgb(255, 255, 255);">  </span>

关注下面的标签,发现更多相似文章