【讯飞开放平台】HarmonyOS 录音与音频播放 原创 精华

天生要墙
发布于 2021-7-1 16:46
浏览
7收藏

引言

录音和音频播放在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 中,播放音频主要有 AudioRendererPlayerSoundPlayer 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();
        }
    }

代码地址

本文所涉及的代码已上传 gitee : https://gitee.com/hong1861/hmos_demo

参考文档

音频采集开发指导 https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-audio-recording-0000000000040903

音频播放开发指导 https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-audio-playback-0000000000031734

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-7-1 16:56:12修改
13
收藏 7
回复
举报
6条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

欢迎新大佬来社区发帖。

回复
2021-7-1 17:29:10
Whyalone
Whyalone

com.iflytek.demo.MainAbility

 

回复
2021-7-1 18:39:37
Anzia
Anzia

讯飞真的不错,我手机的输入法就是用讯飞的。有时候我的塑料普通话都能识别清楚。这个案例也不错

1
回复
2021-7-1 20:09:02
SummerRic
SummerRic

欢迎新大佬来社区发帖。+1

回复
2021-7-13 14:14:00
wx5f82ca430a05d
wx5f82ca430a05d

暂时不支持鸿蒙正式版,包名不是OHOS

 

 

2
回复
2021-8-4 15:18:10
雪建到底
雪建到底

请问老哥如何使用 player 播放网络音频?

回复
2021-10-28 17:32:07
回复
    相关推荐