Android音视频开发-AudioRecord

Android音视频开发-AudioRecord

本篇文章主要讲下AudioRecord.

1: 简介

AudioRecord是Android平台上的一个类,用于实时录制音频数据。它提供了一种方便的方式来捕获和处理音频流。

以下是关于AudioRecord的一些介绍:

  1. 音频源:Record可以从多种音频源中录制音频数据例如麦克风、电话线路、语音识别等。
  2. 音频格式:可以选择不同的音频格式来录制音频数据,如PCM(脉冲编码调制)、AAC(级音频编码)等。
  3. 缓冲区AudioRecord使用一个缓冲区来存储录制的音频数据。开发者可以指定缓冲区的大小,以适应不同的录制需求。
  4. 录制状态:通过调用start()方法开始录制音频数据,并可以通过stop()方法停止录制。还可以使用getState()方法获取当前的录制状态。
  5. 回调函数:可以注册一个回调函数,当有新的音频数据可用时,系统会自动调用回调函数进行处理。
  6. 音频参数:可以设置采样率、声道数和位深度等参数,以满足不同的录制需求。
  7. 权限要求:需要在AndroidManifest.xml文件中添加相应的权限声明android.permission.RECORD_AUDIO权限。

2: AudioRecord对象

首先看下AudioRecord的构造函数:

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes)
throws IllegalArgumentException {this((new AudioAttributes.Builder()).setInternalCapturePreset(audioSource).build(),(new AudioFormat.Builder()).setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,true/*allow legacy configurations*/)).setEncoding(audioFormat).setSampleRate(sampleRateInHz).build(),bufferSizeInBytes,AudioManager.AUDIO_SESSION_ID_GENERATE);
}

参数如下:

  1. audioSource:录制源,具体的参数为MediaRecorder.AudioSource.
  2. sampleRateInHz:采样率,单位赫兹.44100Hz是目前唯一保证在所有设备上工作的速率.
  3. channelConfig:音频通道的配置.
  4. audioFormat:音频数据的格式
  5. bufferSizeInBytes:缓冲区大小.

创建AudioRecord对象:

int audioSource = MediaRecorder.AudioSource.MIC; // 设置音频源为麦克风
int sampleRateInHz = 44100; // 设置采样率为44100Hz
int channelConfig = AudioFormat.CHANNEL_IN_MONO; // 设置通道配置为单声道
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 设置音频格式为16位PCM
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 获取缓冲区大小
audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);

3: 开启录制

开启录制很简单,调用audioRecord的startRecording()方法即可.

audioRecord.startRecording();

另外为了写入文件,我们这里设置个录制状态:

isRecording = true;

创建缓冲区,存储录音文件.

byte[] buffer = new byte[bufferSizeInBytes];

循环读取,写入文件:

new Thread(() -> writeAudioPcm()).start();

writeAudioPcm()详细代码如下:

private void writeAudioPcm() {byte[] bytes = new byte[bufferSizeInBytes];FileOutputStream fos = null;try {File file = new File("sdcard/audioRecord.pcm");if (!file.exists()) {file.createNewFile();}fos = new FileOutputStream(file);while (isRecording) {int read = audioRecord.read(bytes, 0, bytes.length);if (read > 0) {fos.write(read);}}} catch (Throwable e) {Log.e(TAG, "writeAudioPcm: ", e);} finally {try {fos.close();} catch (IOException e) {e.printStackTrace();}}
}

4: 结束录制

按需停止录制音频并释放资源.

private void stopAudio() {isRecording = false;if (audioRecord != null) {audioRecord.stop();audioRecord.release();}pcmToWav();
}

5: pcm转Wav.

最后就是将pcm转为WAV格式.

首先我们先根据音频的采样率,声道等参数,获取wav的header信息.

/*** 头部信息共44字节* @param sampleRate* @param channels* @param bitDepth* @param dataSize* @return* @throws IOException*/public byte[] getWavHeader(int sampleRate, int channels, int bitDepth, long dataSize) {byte[] header = new byte[44];// ChunkID,RIFF标识header[0] = 'R';header[1] = 'I';header[2] = 'F';header[3] = 'F';// ChunkSize,文件长度long totalSize = dataSize + 36;header[4] = (byte) (totalSize & 0xff);header[5] = (byte) ((totalSize >> 8) & 0xff);header[6] = (byte) ((totalSize >> 16) & 0xff);header[7] = (byte) ((totalSize >> 24) & 0xff);// Format,WAVE标识header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';// Subchunk1ID,fmt标识header[12] = 'f';header[13] = 'm';header[14] = 't';header[15] = ' ';// Subchunk1Size,格式信息长度header[16] = 16;header[17] = 0;header[18] = 0;header[19] = 0;// AudioFormat,音频格式(PCM为1)header[20] = 1;header[21] = 0;// NumChannels,声道数header[22] = (byte) channels;header[23] = 0;// SampleRate,采样率header[24] = (byte) (sampleRate & 0xff);header[25] = (byte) ((sampleRate >> 8) & 0xff);header[26] = (byte) ((sampleRate >> 16) & 0xff);header[27] = (byte) ((sampleRate >> 24) & 0xff);// ByteRate,比特率int byteRate = sampleRate * channels * bitDepth / 8;header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);// BlockAlign,块对齐int blockAlign = channels * bitDepth / 8;header[32] = (byte) blockAlign;header[33] = 0;// BitsPerSample,采样位深度header[34] = (byte) bitDepth;header[35] = 0;// Subchunk2ID,data标识header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';// Subchunk2Size,音频数据长度 dataHdrLengthheader[40] = (byte) (dataSize & 0xff);header[41] = (byte) ((dataSize >> 8) & 0xff);header[42] = (byte) ((dataSize >> 16) & 0xff);header[43] = (byte) ((dataSize >> 24) & 0xff);return header;}

转换方法如下:

private void pcmToWav() {File pcmFile = new File("sdcard/audioRecord.pcm");File wavFile = new File("sdcard/audioRecord.wav");// 创建WAV文件头short i = (short) (channelConfig == AudioFormat.CHANNEL_IN_MONO ? 1 : 2);byte[] header = getWavHeader(sampleRateInHz,i,16,pcmFile.length());// 写入WAV文件头FileOutputStream wavOutputStream = null;try {wavOutputStream = new FileOutputStream(wavFile);wavOutputStream.write(header);// 写入PCM数据FileInputStream pcmInputStream = new FileInputStream(pcmFile);byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = pcmInputStream.read(buffer)) != -1) {wavOutputStream.write(buffer, 0, bytesRead);}// 关闭文件流pcmInputStream.close();wavOutputStream.close();} catch (Throwable e) {Log.e(TAG, "pcmToWav: ", e);}}

6:机型适配.

在华为mate50上测试时,授予权限开启录音后崩溃:

2024-04-09 12:00:09.763 1438-1438/? E/ServiceUtilities: Request requires android.permission.MODIFY_AUDIO_SETTINGS
2024-04-09 12:00:09.767 1438-1438/? E/AudioPolicyIntefaceImpl: getInputForAttr permission denied: recording not allowed for AttributionSourceState{pid: 11582, uid: 10157, packageName: com.test.media, attributionTag: (null), token: , renouncedPermissions: [], next: [], uidPidOrigin: -1}
2024-04-09 12:00:09.767 1438-1438/? E/AudioFlinger: createRecord() getInputForAttr return error -1
2024-04-09 12:00:09.767 11582-11582/com.test.media E/AudioRecord: createRecord_l(-1): AudioFlinger could not create record track, status: -1
2024-04-09 12:00:09.767 11582-11582/com.test.media E/AudioRecord-JNI: Error creating AudioRecord instance: initialization check failed with status -1.
2024-04-09 12:00:09.768 11582-11582/com.test.media E/android.media.AudioRecord: Error code -20 when initializing native AudioRecord object.
2024-04-09 12:00:09.770 11582-11582/com.test.media E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.test.media, PID: 11582java.lang.IllegalStateException: startRecording() called on an uninitialized AudioRecord.at android.media.AudioRecord.startRecording(AudioRecord.java:1326)at com.test.media.AudioRecordActivity.startAudio(AudioRecordActivity.java:98)at com.test.media.AudioRecordActivity.onClick(AudioRecordActivity.java:41)at android.view.View.performClick(View.java:7682)at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:967)at android.view.View.performClickInternal(View.java:7651)at android.view.View.access$3700(View.java:886)at android.view.View$PerformClick.run(View.java:30173)at android.os.Handler.handleCallback(Handler.java:966)at android.os.Handler.dispatchMessage(Handler.java:110)at android.os.Looper.loopOnce(Looper.java:205)at android.os.Looper.loop(Looper.java:293)at android.app.ActivityThread.loopProcess(ActivityThread.java:9934)at android.app.ActivityThread.main(ActivityThread.java:9923)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)
  1. 根据日志输出,授予:android.permission.MODIFY_AUDIO_SETTINGS权限.增加权限无效,经过测试无效.

  2. Error code -20 when initializing native AudioRecord object.

    错误码-20:AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED.

    怀疑是配置有问题,尝试切换声道,采样率等,仍然无效.

  3. AudioPolicyIntefaceImpl: getInputForAttr permission denied: recording not allowed for AttributionSourceState{pid: 11582, uid: 10157, packageName: com.test.media, attributionTag: (null), token: , renouncedPermissions: [], next: [], uidPidOrigin: -1}

    最后发现还是权限问题,由于我未动态申请权限,是通过应用程序直接授予的麦克风权限.

    添加动态申请权限代码:

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1101);
    }
    

    问题解决.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/804053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux虚拟内存简介

Linux,像多数现代内核一样,采用了虚拟内存管理技术。该技术利用了大多数程序的一个典型特征,即访问局部性(locality of reference),以求高效使用CPU和RAM(物理内存)资源。大多数程序…

网络安全---非对称数据加密签名验证

一、课题描述 三位同学一组完成数据的非对称加密和数字签名验证传输。 三位同学分别扮演图中 Alice、Bob 和 CA 三个角色,Bob 和 Alice 从 CA 中获得数字证书、Bob 向 Alice 发送秘密发送一段加密并签名后的信息,Alice 获取 Bob 发送的加密信息&#x…

网络安全---Packet Tracer - 配置扩展 ACL

一、实验目的 在Windows环境下利用Cisco Packet Tracer进行 配置防火墙操作。 二、实验环境 1.Windows10、Cisco Packet Tracer 8.2 2.相关的环境设置 在最初的时候,我们已经得到了搭建好的拓扑模型,利用已经搭建好的拓扑模型,进行后续的…

[蓝桥杯 2019 国 B] 解谜游戏

[蓝桥杯 2019 国 B] 解谜游戏 题目背景 题目描述 小明正在玩一款解谜游戏。谜题由 24 24 24 根塑料棒组成,其中黄色塑料棒 4 4 4 根,红色 8 8 8 根,绿色 12 12 12 根 (后面用 Y 表示黄色、R 表示红色、G 表示绿色)。初始时这些塑料棒排…

华科大发布多模态大模型Monkey:低成本扩大输入分辨率,部分性能超越GPT-4V

前言 近年来,基于Transformer架构的多模态大语言模型(MLLM)在视觉理解和多模态推理任务中展现了出色的潜力。但这些模型通常需要大量的训练资源,限制了它们在更广泛研究和应用领域的普及。一种直接的解决方案是使用更小规模的预训练视觉和语言模型&…

Harmony鸿蒙南向驱动开发-MIPI CSI

CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接口标准。CSI-2是MIPI CSI第二版,主要由应用层、协议层、物理层组成,最大支持4通道数据传输、单线传输速度高达1Gb/s。 物理层支持HS(High Speed&…

【御控物联】 2、物联网构成

文章目录 一、前言二、物联网的架构三、物联网的参与角色四、物联网的软硬件五、技术资料 一、前言 物联网的定义,前一章节我们已经做了大体介绍。本章我们说一下物联网的构成,物联网的构成相对来说比较难以总结,主要是因其应用场景特别的多…

UVA12538 Version Controlled IDE 题解 crope

Version Controlled IDE 传送门 题面翻译 维护一种数据结构,资磁三种操作。 1.在p位置插入一个字符串s 2.从p位置开始删除长度为c的字符串 3.输出第v个历史版本中从p位置开始的长度为c的字符串 1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000,所…

【案例分享】如何通过甘特图管理项目进度?

我将通过一个实际案例来具体说明我是如何通过甘特图来管理项目进度的。 案例背景: 我负责过一个软件开发项目:一款在线学习APP。项目团队包括项目经理、开发人员、测试人员、UI设计师等多个角色,预计项目周期为6个月。 案例实施过程&…

使用阿里云试用Elasticsearch学习:4. 聚合——1

在这之前,本书致力于搜索。 通过搜索,如果我们有一个查询并且希望找到匹配这个查询的文档集,就好比在大海捞针。 通过聚合,我们会得到一个数据的概览。我们需要的是分析和总结全套的数据而不是寻找单个文档: 在大海里…

vue快速入门(十二)v-key索引标志

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-key的使用场景数组筛选器的使用 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, i…

【Redis】持久化

文章目录 一、RDB1.1、RDB的自动备份与手动备份1.1.1、自动备份1.1.2、手动备份 1.2、RDB优点1.3、RDB缺点1.4、RDB快照1.5、RDB优化配置项 二、AOF2.1、AOF工作流程2.2、AOF写回策略2.3、MP-AOF实现2.4、AOF优缺点2.5、AOF重写机制 三、RDBAOF混合持久化3.1、数据恢复顺序和加…

【JavaSE】接口 详解(下)

前言 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 接口实例运用 代码举例理解 比较对象的年龄 比较对象的姓名 利用冒泡排序实现Array.sort 年龄比较器 姓名比较器 比较示例测试 clone接口 浅拷贝和深拷贝 浅…

C语言 | Leetcode C语言题解之第17题电话号码的字母组合

题目&#xff1a; 题解&#xff1a; char phoneMap[11][5] {"\0", "\0", "abc\0", "def\0", "ghi\0", "jkl\0", "mno\0", "pqrs\0", "tuv\0", "wxyz\0"};char* digits…

BERT论文解读及情感分类实战

文章目录 简介BERT文章主要贡献BERT模型架构技术细节任务1 Masked LM&#xff08;MLM&#xff09;任务2 Next Sentence Prediction (NSP)模型输入 下游任务微调GLUE数据集SQuAD v1.1 和 v2.0NER 情感分类实战IMDB影评情感数据集数据集构建模型构建超参数设置训练结果注意事项 简…

python把视频按帧转化为图片并保存

参考链接&#xff1a;pythonopencv 将.mp4视频每一帧转为jpg图片_将mp4每一帧转化为图片-CSDN博客 from cv2 import VideoCapture from cv2 import imwrite# 定义保存图片函数 # image:要保存的图片名字 # addr&#xff1b;图片地址与相片名字的前部分 # num: 相片&#xff0c…

系统架构最佳实践 -- 智慧图书管理系统架构设计

随着数字化时代的到来&#xff0c;智慧图书管理系统在图书馆和机构中扮演着重要的角色。一个优秀的图书管理系统不仅需要满足基本的借阅管理需求&#xff0c;还需要具备高效的性能、良好的扩展性和稳定的安全性。本文将讨论智慧图书管理系统的架构设计与实现&#xff0c;以满足…

Debian安装1panel管理面板教程-最新

1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。 1Panel面板是一个强大的服务器管理工具&#xff0c;它通过提供一站式管理、易于使用的界面、高度的可定制性、安全可靠的性能、强大的扩展性以及活跃的社区支持&#xff0c;为用户提供了一个高效、便捷的管理解决方案…

test4101

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

Vue项目自动注入less、sass、scss、stylus全局变量

一、Vue2项目 // vue.config.js const path require(path) module.exports {css: {loaderOptions: {// 给 sass-loader 传递选项sass: {// / 是 src/ 的别名// 所以这里假设有 src/assets/style/var.sass 这个文件// 注意&#xff1a;在 sass-loader v8 中&#xff0c;这个选…