AudioRecord 和 AudioTrack

637 查看

(一) 简单介绍

1.1 AudioRecord

AudioRecord是android捕捉音频的其中的一个工具。
另外一个工具是MediaRecorder
关于这两个工具的比较网上有很多,大家可以看看。

1.2 AudioTrack

AudioTrack是播放PCM流的,android其实还有,

  1. SoundPool 类

  2. MediaPlayer 类

  3. JetPlayer 类

  4. AudioTrack 类

1.3 PCM格式

AudioRecord捕捉到的是PCM的格式:
PCM: 模拟音频信号经过模数转换(AD变换)直接形成的二进制序列,该文件没有附加的文件头和文件结束标志。
PCM文件格式简介:
http://wenku.baidu.com/link?url=78-ueL3a...

1.4 音频有关的术语:

采用频率(the sampling rate):模拟信息转成数字信号的采样率。
采样位数:8位 或者 16位 去存储每一次的采样结果。
声道数:单声道,立体声道(2)。
比特率(Bit rate )/位率:声音中的比特率是指将模拟声音信号转换成数字声音信号后,单位时间内的二进制数据量,是间接衡量音频质量的一个指标。
比特率(bps) = 采样频率(HZ) 采样位数(Bit) 声道数

(二) AudioRecord的使用

 private void _record(String path) {

        //  录音调用的方法。如果文件已经存在,则覆盖。
        File file = new File(path);

        //      创建输出流 , 缓存输出流。
        try {
            OutputStream os = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(os);
            DataOutputStream dos = new DataOutputStream(bos);

            int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
            /**
             *      1.  录音来源
             *      2.  采样频率
             *      3.  STEREO 双声道,MONO 单声道
             *      4.  编码制式和采样的位数。ENCODING_PCM_16BIT
             *      5.  采集数据需要的缓冲区大小。
             */
            AudioRecord audioRecord = new AudioRecord(
                    MediaRecorder.AudioSource.MIC,
                    frequency,
                    channelConfiguration,
                    audioEncoding,
                    bufferSize);

            short[] buffer = new short[bufferSize];
            audioRecord.startRecording();

            isRecording = true;

            //listener
            if (onRecordStartedLister!=null) {
                onRecordStartedLister.onStarted();
            }

            while (isRecording) {
                int bufferReadResult = audioRecord.read(buffer,0,bufferSize);   //  从 AudioRecord中读取 0 - bufferSize
                for (int i=0;i<bufferReadResult;i++) {
                    dos.writeShort(buffer[i]);          //  写入 dos 中
                }
            }

            audioRecord.stop();
            audioRecord.release();
            dos.close();
            os.close();
            bos.close();

            //listener
            if (onRecordStoppedLister!=null) {
                onRecordStoppedLister.onStopped();
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e("FileNotFoundException", e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("IOException2", e.getMessage());
        }

    }

步骤:

1.首先,你得有个路径 Path:例如,(存储卡的根目录)
String file = Environment.getExternalStorageDirectory().getAbsolutePath() + "/wang_ni_ma.pcm";
2.创建输出流:
        OutputStream os = new FileOutputStream(file);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        DataOutputStream dos = new DataOutputStream(bos);
3.新建 AudioRecord 的对象:
AudioRecord audioRecord = new AudioRecord(
                MediaRecorder.AudioSource.MIC,
                11025,
                AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufferSize);
参数的说明:
① 音频的来源,就是你使用什么进行捕捉,这里的MIC是指麦克风
② 音频的采样频率,有些低端机型支持的频率可能不能太高。
③ 音频的声道数,AudioRecord只支持,单双声道。MONO单声道。STEREO 双声道
④ 音频的采样位数:有8,16Bit。
⑤ 采集数据需要的缓冲区大小
    这个大小最好用函数计算出来:
    其也需要三个参数:采样率,声道数,采样位数。
int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
    
4.这里BB几句。
while (isRecording) {
           int bufferReadResult = audioRecord.read(buffer,0,bufferSize);   //  从 AudioRecord中读取 0 - bufferSize
           for (int i=0;i<bufferReadResult;i++) {
               dos.writeShort(buffer[i]);          //  写入 dos 中
           }
       }
 while里面是核心,audioRecord.read方法,从0到bufferSize读取,放到buffer中,
 然后就dos.writeShort(buffer[i])写入 dos 中。
 isRecording,Boolen变量,我们可以通过button的触发,把它变成false,就结束录音了。

(三) AudioTrack的使用

代码例子如下:

private void _play(String path) {

        //  1.  PCM文件所在位置
        File file = new File(path);

        //  2.  计算 Buff 大小
        int buffSize = AudioTrack.getMinBufferSize(frequency,channelConfiguration,audioEncoding);

        try {

        //  3.  创建一个输入流     DataInputStream

            InputStream is = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(is);
            DataInputStream dis = new DataInputStream(bis);

        //  4.  创建播放器   audioTrack
            /**
             1)指定在流的类型
                STREAM_ALARM:警告声
                STREAM_MUSCI:音乐声,例如music等
                STREAM_RING:铃声
                STREAM_SYSTEM:系统声音
                STREAM_VOCIE_CALL:电话声音

            2)音频的采样率

            3)音频 声道数
                CHANNEL_CONFIGURATION_MONO      单声道
                CHANNEL_CONFIGURATION_STEREO    双声道

            4)采样位数
                AudioFormat.ENCODING_PCM_16BIT  16 bit
                AudioFormat.ENCODING_PCM_8BIT   8 bit

            5) 缓存大小
                由函数直接得出。
                AudioTrack.getMinBufferSize

             6) 模式
                AudioTrack.MODE_STREAM
                AudioTrack.MODE_STATIC

                     AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。

                          STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。
                          这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
                          这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。

                          而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,
                          后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
                          这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
             */

            AudioTrack audioTrack = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    frequency,
                    channelConfiguration,
                    audioEncoding,
                    buffSize,
                    AudioTrack.MODE_STREAM
            );

            //  5.  读取 file 到 music array 中
            /**
             *      1)建立缓存数组  music[]
             *      2)从 PCM 流文件读出,每 20 次,
             *      3)放到 audioTrack.write 中,播放。
             */
            int i=0;
            short[] music = new short[20];
            audioTrack.play();

            // listener
            if (onPlayStartedLister != null) {
                onPlayStartedLister.onStarted();
            }

            while (dis.available()>0) {         //  判断PCM流文件是否结束
                music[i] = dis.readShort();     //  从PCM文件中,以流的形式读出存放在 music 中

                if (i==19) {                    //  定义每次读出 20 的数据后,播放。
                    audioTrack.write(music, 0, 20);
                    i = 0;
                }
                else {
                    i++;
                }
            }

            dis.close();
            bis.close();
            is.close();
            audioTrack.stop();

            //  listener
            if (onPlayStoppedListener!=null) {
                onPlayStoppedListener.onPlayStop();
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

其中,对象AudioTrack的参数需要和AudioRecord的录音格式一致。
其他需要BB的,在注释里面也挺清楚的了。

(四) 总结

第一次写文章,也许,很多地方需要改进,或者是理解错了,希望大家指出来,那也是大家共同进步了。今天是搞了一下午,有点成果了,Audio系统,其实算是挺底层的了,有大神已经说得很好了:http://www.cnblogs.com/innost/archive/20...,有时间也可以去研究一下。