Android多媒体学习十:利用AudioRecord类实现自己的音频录制程序

2023-05-16

AudioRecord类相对于MediaRecorder来说,更加接近底层,为我们封装的方法也更少。然而实现一个AudioRecord的音频录制程序也很

简单。本实例代码如下:

 

可惜,本实例测试时有个问题,在录制的时候,会出现buffer over。缓存泄露,待解决。

 

package demo.camera;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.hardware.Camera.AutoFocusCallback;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/**
 * 该实例中,我们使用AudioRecord类来完成我们的音频录制程序
 * AudioRecord类,我们可以使用三种不同的read方法来完成录制工作,
 * 每种方法都有其实用的场合
 * 一、实例化一个AudioRecord类我们需要传入几种参数
 * 1、AudioSource:这里可以是MediaRecorder.AudioSource.MIC
 * 2、SampleRateInHz:录制频率,可以为8000hz或者11025hz等,不同的硬件设备这个值不同
 * 3、ChannelConfig:录制通道,可以为AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO
 * 4、AudioFormat:录制编码格式,可以为AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的电量和存储空间
 * 5、BufferSize:录制缓冲大小:可以通过getMinBufferSize来获取
 * 这样我们就可以实例化一个AudioRecord对象了
 * 二、创建一个文件,用于保存录制的内容
 * 同上篇
 * 三、打开一个输出流,指向创建的文件
 * DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))
 * 四、现在就可以开始录制了,我们需要创建一个字节数组来存储从AudioRecorder中返回的音频数据,但是
 * 注意,我们定义的数组要小于定义AudioRecord时指定的那个BufferSize
 * short[]buffer = new short[BufferSize/4];
 * startRecording();
 * 然后一个循环,调用AudioRecord的read方法实现读取
 * 另外使用MediaPlayer是无法播放使用AudioRecord录制的音频的,为了实现播放,我们需要
 * 使用AudioTrack类来实现
 * AudioTrack类允许我们播放原始的音频数据
 * 
 * 
 * 一、实例化一个AudioTrack同样要传入几个参数
 * 1、StreamType:在AudioManager中有几个常量,其中一个是STREAM_MUSIC;
 * 2、SampleRateInHz:最好和AudioRecord使用的是同一个值
 * 3、ChannelConfig:同上
 * 4、AudioFormat:同上
 * 5、BufferSize:通过AudioTrack的静态方法getMinBufferSize来获取
 * 6、Mode:可以是AudioTrack.MODE_STREAM和MODE_STATIC,关于这两种不同之处,可以查阅文档
 * 二、打开一个输入流,指向刚刚录制内容保存的文件,然后开始播放,边读取边播放
 * 
 * 实现时,音频的录制和播放分别使用两个AsyncTask来完成 
 */
public class MyAudioRecord2 extends Activity{
    
    private TextView stateView;
    
    private Button btnStart,btnStop,btnPlay,btnFinish;
    
    private RecordTask recorder;
    private PlayTask player;
    
    private File audioFile;
    
    private boolean isRecording=true, isPlaying=false; //标记
    
    private int frequence = 8000; //录制频率,单位hz.这里的值注意了,写的不好,可能实例化AudioRecord对象的时候,会出错。我开始写成11025就不行。这取决于硬件设备
    private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
    
    
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_audio_record);
        
        stateView = (TextView)this.findViewById(R.id.view_state);
        stateView.setText("准备开始");
        btnStart = (Button)this.findViewById(R.id.btn_start);
        btnStop = (Button)this.findViewById(R.id.btn_stop);
        btnPlay = (Button)this.findViewById(R.id.btn_play);
        btnFinish = (Button)this.findViewById(R.id.btn_finish);
        btnFinish.setText("停止播放");
        btnStop.setEnabled(false);
        btnPlay.setEnabled(false);
        btnFinish.setEnabled(false);
        
        //在这里我们创建一个文件,用于保存录制内容
        File fpath = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/files/");
        fpath.mkdirs();//创建文件夹
        try {
            //创建临时文件,注意这里的格式为.pcm
            audioFile = File.createTempFile("recording", ".pcm", fpath);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }       
    }
    
    
    public void onClick(View v){
        int id = v.getId();
        switch(id){
        case R.id.btn_start:
            //开始录制
            
            //这里启动录制任务
            recorder = new RecordTask();
            recorder.execute();
            
            break;
        case R.id.btn_stop:
            //停止录制
            this.isRecording = false;
            //更新状态
            //在录制完成时设置,在RecordTask的onPostExecute中完成
            break;
        case R.id.btn_play:
            
            player = new PlayTask();
            player.execute();
            break;
        case R.id.btn_finish:
            //完成播放
            this.isPlaying = false;
            break;
            
        }
    }
    
    class RecordTask extends AsyncTask<Void, Integer, Void>{
        @Override
        protected Void doInBackground(Void... arg0) {
            isRecording = true;
            try {
                //开通输出流到指定的文件
                DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile)));
                //根据定义好的几个配置,来获取合适的缓冲大小
                int bufferSize = AudioRecord.getMinBufferSize(frequence, channelConfig, audioEncoding);
                //实例化AudioRecord
                AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelConfig, audioEncoding, bufferSize);
                //定义缓冲
                short[] buffer = new short[bufferSize];
                
                //开始录制
                record.startRecording();
                
                int r = 0; //存储录制进度
                //定义循环,根据isRecording的值来判断是否继续录制
                while(isRecording){
                    //从bufferSize中读取字节,返回读取的short个数
                    //这里老是出现buffer overflow,不知道是什么原因,试了好几个值,都没用,TODO:待解决
                    int bufferReadResult = record.read(buffer, 0, buffer.length);
                    //循环将buffer中的音频数据写入到OutputStream中
                    for(int i=0; i<bufferReadResult; i++){
                        dos.writeShort(buffer[i]);
                    }
                    publishProgress(new Integer(r)); //向UI线程报告当前进度
                    r++; //自增进度值
                }
                //录制结束
                record.stop();
                Log.v("The DOS available:", "::"+audioFile.length());
                dos.close();
            } catch (Exception e) {
                // TODO: handle exception
            }
            return null;
        }
        
        //当在上面方法中调用publishProgress时,该方法触发,该方法在UI线程中被执行
        protected void onProgressUpdate(Integer...progress){
            stateView.setText(progress[0].toString());
        }
        
        protected void onPostExecute(Void result){
            btnStop.setEnabled(false);
            btnStart.setEnabled(true);
            btnPlay.setEnabled(true);
            btnFinish.setEnabled(false);
        }
        
        protected void onPreExecute(){
            //stateView.setText("正在录制");
            btnStart.setEnabled(false);
            btnPlay.setEnabled(false);
            btnFinish.setEnabled(false);
            btnStop.setEnabled(true);       
        }
        
    }
    
    class PlayTask extends AsyncTask<Void, Integer, Void>{
        @Override
        protected Void doInBackground(Void... arg0) {
            isPlaying = true;
            int bufferSize = AudioTrack.getMinBufferSize(frequence, channelConfig, audioEncoding);
            short[] buffer = new short[bufferSize/4];
            try {
                //定义输入流,将音频写入到AudioTrack类中,实现播放
                DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(audioFile)));
                //实例AudioTrack
                AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);
                //开始播放
                track.play();
                //由于AudioTrack播放的是流,所以,我们需要一边播放一边读取
                while(isPlaying && dis.available()>0){
                    int i = 0;
                    while(dis.available()>0 && i<buffer.length){
                        buffer[i] = dis.readShort();
                        i++;
                    }
                    //然后将数据写入到AudioTrack中
                    track.write(buffer, 0, buffer.length);
                    
                }
                
                //播放结束
                track.stop();
                dis.close();
            } catch (Exception e) {
                // TODO: handle exception
            }
            return null;
        }
        
        protected void onPostExecute(Void result){
            btnPlay.setEnabled(true);
            btnFinish.setEnabled(false);
            btnStart.setEnabled(true);
            btnStop.setEnabled(false);
        }
        
        protected void onPreExecute(){  
            
            //stateView.setText("正在播放");
            btnStart.setEnabled(false);
            btnStop.setEnabled(false);
            btnPlay.setEnabled(false);
            btnFinish.setEnabled(true);         
        }
        
    }
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android多媒体学习十:利用AudioRecord类实现自己的音频录制程序 的相关文章

  • Docker安装Kafka消息队列

    文章目录 1 安装zookeeper2 安装kafka3 安装kafka map xff08 可选 xff09 1 安装zookeeper span class token function docker span run span cla
  • 【Spring Boot实战与进阶】集成Kafka消息队列

    汇总目录链接 xff1a Spring Boot实战与进阶 学习目录 文章目录 一 简介二 集成Kafka消息队列1 引入依赖2 配置文件3 测试生产消息4 测试消费消息 一 简介 Kafka是由Apache软件基金会开发的一个开源流处理平
  • Hutool工具类之excel导入导出

    文章目录 1 导入excel2 导出excel 1 导入excel span class token class name ExcelReader span reader span class token operator 61 span
  • Docker安装RockerMQ消息队列

    文章目录 1 安装namesrv2 安装broker3 安装console xff08 可选 xff09 1 安装namesrv namesrv就类似于消息队列的注册中心 span class token function docker s
  • 【Spring Boot实战与进阶】集成RockerMQ消息队列

    汇总目录链接 xff1a Spring Boot实战与进阶 学习目录 文章目录 一 简介二 集成RockerMQ消息队列1 引入依赖2 配置文件3 测试生产消息4 测试消费消息 一 简介 RocketMQ 是阿里巴巴在2012年开源的分布式
  • linux下elasticsearch 安装、配置及示例

    简介 开始学es xff0c 我习惯边学边记 xff0c 总结出现的问题和解决方法 本文是在两台linux虚拟机下 xff0c 安装了三个节点 本次搭建es同时实践了两种模式 单机模式和分布式模式 条件允许的话 xff0c 可以在多台机器上
  • Java服务器热部署的实现原理

    今天发现早年在大象笔记中写的一篇笔记 xff0c 之前放在ijavaboy上的 xff0c 现在它已经访问不了了 前几天又有同事在讨论这个问题 这里拿来分享一下 在web应用开发或者游戏服务器开发的过程中 xff0c 我们时时刻刻都在使用热
  • win10打开热点提示:我们无法设置移动热点

    解决方案 xff1a 1 右键我的电脑 xff0c 打开管理 2 双击带有下载标记wi fi 适配器 xff0c 点击启用设备 xff0c 确认即可
  • Docker容器打包迁移

    有时在一个docker内部署的容器不想在其它服务器容器上重新部署 xff0c 再配置配置文件 这时直接将当前的容器打包然后直接部署到新的服务器上即可 xff0c 免了再配置一遍 1 将容器保存为镜像 先把容器停止运行不然会有问题 容器保存为
  • 四旋翼飞行器的原理研究和建模

    四旋翼飞行器的原理研究和建模 对四旋翼飞行器的工作原理进行了简单介绍 xff0c 对其飞行姿态角进行描述 xff0c 并在此基础上建立数学模型 四旋翼飞行器的原理 根据四旋翼飞行器的运动方式的特点将其飞行控制划分为四种基本的飞行控制方式 1
  • 数组名和函数名是什么东西

    数组名和函数名的本质都是一个 指向数组首地址或函数体的指针常量 的名字 规则1 xff1a 数组 61 指向数组首地址的指针常量 43 数组元素 简单说就是 xff0c 数组 61 指针常量 43 数组内容 xff0c 数组名就是这个指针常
  • RT-Thread进阶之低功耗PM组件应用笔记

    电源管理组件 嵌入式系统低功耗管理的目的在于满足用户对性能需求的前提下 xff0c 尽可能降低系统能耗以延长设备待机时间 高性能与有限的电池能量在嵌入式系统中矛盾最为突出 xff0c 硬件低功耗设计与软件低功耗管理的联合应用成为解决矛盾的有
  • 【STM32H750】玩转ART-Pi(一)——使用STM32CUBMX生成TouchGFX工程

    目录 STM32H750 玩转ART Pi xff08 一 xff09 使用STM32CUBMX生成TouchGFX工程 STM32H750 玩转ART Pi xff08 二 xff09 制作MDK的外部QSPI FLASH烧录算法 STM
  • 【STM32H750】玩转ART-Pi(二)——制作MDK的外部QSPI-FLASH烧录算法

    目录 STM32H750 玩转ART Pi xff08 一 xff09 使用STM32CUBMX生成TouchGFX工程 STM32H750 玩转ART Pi xff08 二 xff09 制作MDK的外部QSPI FLASH烧录算法 STM
  • linux驱动开发篇(四)—— platform平台设备驱动

    linux系列目录 xff1a linux基础篇 xff08 一 xff09 GCC和Makefile编译过程 linux基础篇 xff08 二 xff09 静态和动态链接 ARM裸机篇 xff08 一 xff09 i MX6ULL介绍 A
  • C语言变参函数和可变参数宏

    文章目录 一 变参函数的设计与实现1 变参函数初体验2 变参函数改进版3 变参函数 V3 0 版本3 变参函数 V4 0 版本4 变参函数 V5 0 版本 二 可变参数宏的设计与实现1 什么是可变参数宏2 宏连接符 的作用3 可变参数宏的另
  • 利用MDK的FLM文件生成通用flash驱动

    文章目录 前言一 FLM文件是什么 xff1f 二 FLM文件结构1 FlashPrg c2 FlashPrg c 三 解析FLM文件1 解析flm文件 四 设计flash驱动抽象层五 快速使用 前言 在进行Flash操作时 xff0c 一
  • 游标的概念和作用

    游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制 游标充当指针的作用 尽管游标能遍历结果中的所有行 xff0c 但他一次只指向一行 概括来讲 xff0c SQL的游标是一种临时的数据库对象 xff0c 即可以用来存放在数
  • 【STM32H750】从零编写MDK的FLM烧录算法

    文章目录 前言一 将代码中的图片资源下载到外部flash1 修改分散加载文件 二 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中擦除操作执行流程3 制作FLM文件步骤 三 使用STM32CubeMX新建工程1 新建
  • 使用MDK开发树莓派pico RP2040之外部 flash下载算法

    文章目录 前言一 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中操作执行流程3 创建MDK下载算法通用流程 二 树莓派pico下载算法制作步骤1 下载树莓派的MDK的程序模板2 修改输出算法文件的名字3 修改编程算

随机推荐

  • 单片机堆栈分配

    Code xff1a 表示程序所占用 FLASH 的大小 xff08 FLASH xff09 RO data xff1a 即 Read Only data xff0c 表示程序定义的常量 xff0c 如 const 类型 xff08 FLA
  • 【STM32F429】通过STM32CubeMX移植TouchGFX

    目录 STM32F429 移植TouchGFX到RT Thread系统 xff08 1 xff09 STM32F429 使用TouchGFX的MVP架构来实现GUI和硬件的双向交互 xff08 2 xff09 STM32F429 RT Th
  • TouchGFX使用MVP架构来实现GUI和硬件的双向交互

    目录 xff1a 实战 xff1a 1 STM32F767移植touchGFX 使用RT Thread系统实现DIY数字仪表 xff08 完成 xff09 2STM32F429移植touchGFX 使用RT Thread系统实现DIY数字仪
  • python 微信自动回复机器人

    python 微信自动回复机器人 导入wxauto https github com cluic wxauto span class token comment python3 span span class token comment c
  • 《python+opencv实践》一、基于颜色的物体追踪(上)

    点击打开链接 本文主要参考国外一大牛博客 xff0c 然后自己修改得来 相关知识点在这里 实现功能 xff1a 追踪红颜色瓶盖 xff0c 并画出瓶盖轮廓和运动轨迹 from collections import deque import
  • 《python+opencv实践》一、基于颜色的物体追踪(下)

    本文对 python 43 opencv实践 一 基于颜色的物体追踪 xff08 上 xff09 做了功能上的强化 xff0c 强化如下 xff1a xff08 1 xff09 加了pts清空 xff0c 即当没有检测到目标时 xff0c
  • Ubuntu16.04 编译出错c++: internal compiler error: Killed (program cc1plus)

    最近在使用github上的一个模拟器 xff0c 需要自己对其中文件进行make编译 但是中间遇到了不知道多少个错误 xff0c 吐血 想了想还是记录一下 xff0c 错误 compiling moc moc qwt plot panner
  • C++创建信号量 CreateSemaphore

    一 定义 Semaphore也是一个线程同步的辅助类 xff0c 可以维护当前访问自身的线程个数 xff0c 并提供了同步机制 使用Semaphore可以控制同时访问资源的线程个数 xff0c 例如 xff0c 实现一个文件允许的并发访问数
  • OpenGL ES之GLSurfaceView学习一:介绍

    原文地址 http 120 132 134 205 cmdn supesite uid 5358 action viewspace itemid 6527 GLSurfaceView是一个视图 xff0c 继承至SurfaceView xf
  • C++之STL迭代器

    一 背景 迭代器 iterator 是一种抽象的设计理念 xff0c 即迭代器模式 xff0c 通过迭代器可以在不了解容器内部原理的情况下遍历容器 除此之外 xff0c STL中迭代器一个最重要的作用就是作为容器 vector queue
  • C++make_shared的使用

    一 使用 make shared是标准库函数 xff0c 此函数在动态内存中分配一个对象并初始化它 xff0c 返回指向此对象的shared ptr 由于是通过shared ptr管理内存 xff0c 因此这是一种安全分配和使用动态内存的方
  • cmkae命令set_target_properties

    一 介绍 命令的格式如下 set target properties target1 target2 PROPERTIES prop1 value1 prop2 value2 Sets properties on targets The s
  • cmake的install

    一 介绍 一般使用cmake xff0c 常用命令就是 mkdir build amp amp cd build cmake make make install install命令为项目生成一系列的安装规则 在执行make install时
  • cmake命令之list

    一 介绍 cmake的list命令即对列表的一系列操作 xff0c cmake中的列表变量是用分号 分隔的一组字符串 xff0c 创建列表可以使用set命令 xff08 参考set命令 xff09 xff0c 例如 xff1a set va
  • Cmake之ExternalProject_Add

    一 介绍 ExternalProject命令可以很好的解决项目中使用第三方库 提高项目的可用性 ExternalProject Add 函数创建一个外部工程可以驱动下载 更新 补丁 配置 构建 安装和测试流程的自定义目标 语法 xff1a
  • POI导入Excel,获取公式的值

    直接POI导入Excel中的数据的时候 xff0c 直接获取表中的值 xff0c 如果表中单元格的值时由公式计算得出的话 xff0c 获取到的会是公式 所以需要对获取的单元格的值进行处理 xff1a 导入数字时 导入公式的计算结果而非公式
  • navicate连接远程数据库

    远程主机的3306端口一般是不允许外网直接访问的 xff0c 但是开发过程中 xff0c 使用navicate工具进行数据库操作会方便超级多 xff0c 那么要怎么配置navicate连接远程数据库呢 超简单两步走 xff1a 1 使用se
  • idea中Gradle项目控制台中文乱码

    我使用的是IEDA2021 xff0c 之前跑maven项目一切正常 今天导入了一个Gradle项目 xff0c debug的时候控制台中文乱码了 之前直接用idea控制台中文乱码做关键词搜索 xff0c 改了file settings e
  • @RequestMapping value值置为““

    我们通常用 64 RequestMapping来映射请求 xff0c 比如 xff0c 写一个方法 xff1a span class token annotation punctuation 64 RequestMapping span s
  • Android多媒体学习十:利用AudioRecord类实现自己的音频录制程序

    AudioRecord类相对于MediaRecorder来说 xff0c 更加接近底层 xff0c 为我们封装的方法也更少 然而实现一个AudioRecord的音频录制程序也很 简单 本实例代码如下 xff1a 可惜 xff0c 本实例测试