### **引言** 录音和音频播放在app中是一个很常见的功能,本文将介绍在HarmonyOS 如何使用录音和音频播放功能。 ### 功能介绍 #### 1.录音 ##### 1.1 使用录音前需要先申请录音权限 在 `config.json`文件中添加权限声明 ```json "reqPermissions": [ { "name": "ohos.permission.MICROPHONE", "reason": "the app need microphone", "usedScene": { "ability": [ "com.iflytek.demo.MainAbility" ], "when": "always" } } ] ``` 然后在`MainAbility`中动态申请麦克风权限 ```java 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设置录音源等信息。 ```java /** * 创建默认的录音对象 */ 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()` 获取音频。 ```java /** * 开始录音 * * @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(); } } ``` 停止录音 ```java /** * 停止录音 */ 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音频 ```java /** * 播放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 ```java /** * 播放音频 * * @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 [想了解更多关于鸿蒙的内容,请访问:](https://harmonyos.51cto.com/#bkwz) [51CTO和华为官方战略合作共建的鸿蒙技术社区](https://harmonyos.51cto.com/#bkwz) https://harmonyos.51cto.com/#bkwz