Android多媒体应用——AudioTrack, AudioRecord

本文介绍了Android的AudioRecord和AudioTrack工具类。AudioRecord用于录音,管理音频资源,记录音频输入,给出了其实现录音的流程。由于其输出为PCM原始数据,需用AudioTrack处理播放或转换格式。还介绍了AudioTrack的两种工作模式及特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

github源码:https://github.com/heinsven/Test5.git

 

AudioRecord 是Android提供的用于录音的工具类。

AudioRecord管理应用的各类音频资源,记录平台的各类输入设备的音频输入。

AudioRecord实现Android录音的流程:

  • 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败
  • 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小
  • 调用startRecording函数,开始录音
  • 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流,生成PCM格式文件
  • 录音结束时,关闭数据流, 停止录音

 

AudioRecord由于输出的数据是原始数据PCM,MediaPlayer播放器是不能识别播放的,需要通过AudioTrack处理播放,或者把PCM转化为可以播放的格式类似wav,MP3等。

public class Test5 extends AppCompatActivity {
    private ProgressBar mProgressBar;
    private Button mBtn1;
    private Button mBtn2;
    private Button mBtn3;

    private AudioRecord mAudioRecord;
    private boolean isRecording = false;

    private byte[] audioData;
    int laiyuan = MediaRecorder.AudioSource.MIC;//来源
    int samHz = 16000;//采样频率
    int shengdao = AudioFormat.CHANNEL_IN_MONO;//声道
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//格式
    int bufferSize = 2 * AudioRecord.getMinBufferSize(samHz, shengdao, audioFormat);//缓冲区大小

    private String voicePath = Environment.getExternalStorageDirectory().getPath() + "/audio";
    private String voiceName = "/test.pcm";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test5);
        init();
    }

    private void init() {
        verifyStoragePermissions(this);
        mProgressBar = findViewById(R.id.progressbar);
        mProgressBar.setVisibility(View.GONE);
        mBtn1 = findViewById(R.id.btn1);
        mBtn2 = findViewById(R.id.btn2);
        mBtn3 = findViewById(R.id.btn3);

        mBtn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mAudioRecord == null) {
                    startRecord();
                }
            }
        });

        mBtn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mAudioRecord != null) {
                    stopRecord();
                }
            }
        });

        mBtn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    InputStream in = new FileInputStream(voicePath + voiceName);
                    try {
                        ByteArrayOutputStream out = new ByteArrayOutputStream(264848);
                        for (int b; (b = in.read()) != -1; ) {
                            out.write(b);
                        }
                        audioData = out.toByteArray();
                    } finally {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, samHz,
                        AudioFormat.CHANNEL_OUT_MONO, audioFormat,
                        audioData.length, AudioTrack.MODE_STATIC);
                audioTrack.write(audioData, 0, audioData.length);
                audioTrack.play();
            }
        });
    }

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.RECORD_AUDIO"};

    public static void verifyStoragePermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission1 = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            int permission2 = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.RECORD_AUDIO");
            if (permission1 != PackageManager.PERMISSION_GRANTED || permission2 != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void startRecord() {
        mAudioRecord = new AudioRecord(laiyuan, samHz, shengdao, audioFormat, bufferSize);

        final byte data[] = new byte[bufferSize];
        final File file = new File(voicePath);
        final File fileaudio = new File(voicePath + voiceName);

        if (fileaudio.exists()) {
            fileaudio.delete();
        }
        if (!file.exists()) {
            file.mkdirs();
        }
        mAudioRecord.startRecording();
        isRecording = true;
        mProgressBar.setVisibility(View.VISIBLE);

        new Thread(new Runnable() {
            @Override
            public void run() {
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(fileaudio);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                if (null != fos) {
                    while (isRecording) {
                        int read = mAudioRecord.read(data, 0, bufferSize);
                        //返回正确时才读取数据
                        if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                            try {
                                fos.write(data);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }


    public void stopRecord() {
        isRecording = false;
        if (mAudioRecord != null) {
            mProgressBar.setVisibility(View.GONE);
            mAudioRecord.stop();
            mAudioRecord.release();
            //调用release之后必须置为null
            mAudioRecord = null;
        }
    }
}

AudioTrack只能播放已经解码的PCM流,对于文件,也支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件

AudioTrack可以在两种模式下工作,一种是静态(static),一种是流(streaming)

MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。

MODE_STATIC:在play之前,只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值