android音频之裸数据录制和播放

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_17338093/article/details/79057833
public class MainActivity extends AppCompatActivity {

    private File file=null;
    private boolean isRecording;

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

    public void click(View view){
        switch (view.getId()){
            case R.id.btn_start:
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        StartRecord();
                        Log.e("tag","start");
                    }
                });
                thread.start();
//                ButtonEnabled(false, true, false);
            break;
            case R.id.btn_stop:
                isRecording = false;
                Log.i("Tag","停止录音");
                break;
            case R.id.btn_player:
                PlayRecord();
                break;
        }
    }

    /**
     * Android提供了两个API用于实现录音功能:android.media.AudioRecord、android.media.MediaRecorder。

     1、AudioRecord

     主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理(如会说话的汤姆猫、语音)

     优点:语音的实时处理,可以用代码实现各种音频的封装

     缺点:输出是PCM语音数据,如果保存成音频文件,是不能够被播放器播放的,所以必须先写代码实现数据编码以及压缩

     示例:

     使用AudioRecord类录音,并实现WAV格式封装。录音20s,输出的音频文件大概为3.5M左右

     2、MediaRecorder

     已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp

     优点:大部分以及集成,直接调用相关接口即可,代码量小

     缺点:无法实时处理音频;输出的音频格式不是很多,例如没有输出mp3格式文件

     示例:

     使用MediaRecorder类录音,输出amr格式文件。录音20s,输出的音频文件大概为33K(已写测试代码)

     3、音频格式比较

     WAV格式:录音质量高,但是压缩率小,文件大

     AAC格式:相对于mp3,AAC格式的音质更佳,文件更小;有损压缩;一般苹果或者Android SDK4.1.2(API 16)及以上版本支持播放

     AMR格式:压缩比比较大,但相对其他的压缩格式质量比较差,多用于人声,通话录音

     至于常用的mp3格式,使用MediaRecorder没有该视频格式输出。一些人的做法是使用AudioRecord录音,然后编码成wav格式,再转换成mp3格式
     再贴上一些测试工程。
     */
    public void StartRecord() {
        //16K采集率
        int frequency = 16000;
        //格式
        int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
        //16Bit
        int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
        //生成PCM文件
        file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/reverseme.pcm");
        Log.i("tag","生成文件");
        //如果存在,就先删除再创建
        if (file.exists())
            file.delete();
        Log.i("tag","删除文件");
        try {
            file.createNewFile();
            Log.i("tag","创建文件");
        } catch (IOException e) {
            Log.i("tag","未能创建");
            throw new IllegalStateException("未能创建" + file.toString());
        }

        try {
            //输出流
            OutputStream os = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(os);
            //打开一个输出流,指向创建的文件
            DataOutputStream dos = new DataOutputStream(bos);
            //录制缓冲大小
            int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
            /**
             * 1:录音源 mic:麦克风、default:默认、voice_call:语言播出的与对方说话的声音、voice_communication:摄像头旁边的麦克风、VOICE_RECOGNITION、VOICE_UPLINK
             * 2:录制频率:8000hz或者11025hz等,不同的硬件设备这个值不同;
             * 3:录制通道,可以为AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO
             * 4:录制编码格式,可以为AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的电量和存储空间,16BIT可以兼容更多的手机
             * 5:录制缓冲大小
             */
            AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize);
            short[] buffer = new short[bufferSize];
            audioRecord.startRecording();
            Log.i("tag", "开始录音");
            isRecording = true;
            while (isRecording) {
                int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
                for (int i = 0; i < bufferReadResult; i++) {
                    dos.writeShort(buffer[i]);
                }
            }
            audioRecord.stop();
            dos.close();
        } catch (Throwable t) {
            Log.e("tag", "录音失败");
        }
    }

    /**
     * 播放
     * android中播放声音可以用MediaPlayer和AudioTrack两种方案的,但是两种方案是有很大区别的,MediaPlayer可以播放多种格式的声音文件,
     * 例如MP3,AAC,WAV,OGG,MIDI等。而AudioTrack只能播放PCM数据流。
     * 事实上,两种本质上是没啥区别的,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数据流传递给AudioTrack,
     * 最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过Mediaplayer的解码部分而已。
     * Mediaplayer的解码核心部分是基于OpenCORE 来实现的,支持通用的音视频和图像格式,codec使用的是OpenMAX接口来进行扩展。
     * 因此使用audiotrack播放mp3文件的话,要自己加入一个音频解码器,如libmad。否则只能播放PCM数据,如大多数WAV格式的音频文件。
     */
    public void PlayRecord() {
        if(file == null){
            return;
        }
        //读取文件
        int musicLength = (int) (file.length() / 2);
        short[] music = new short[musicLength];
        try {
            InputStream is = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(is);
            DataInputStream dis = new DataInputStream(bis);
            int i = 0;
            while (dis.available() > 0) {
                music[i] = dis.readShort();
                i++;
            }
            dis.close();
            /**
             * 1:streamType:stream_music:音乐声,RING:铃声,ALARM:警告,SYSTEM:系统提示,VOICE_CALL:电话声
             * 2:sampleRateInHz:采样率 播放的音频每秒会有多少次采样,可选为 8000 16000 22050 24000 32000 44100 48000
             * 3:channelConfig:CHANNEL_CONFIGURATION_MONO:单声道 ;CHANNEL_CONFIGURATION_STEREO:双声道(大多数手机都是伪立体声采集,为了性能考虑所以使用单声道)
             * 4:audioFormat:AudioRecord录制格式同;
             * 5:bufferSizeInBytes:AudioTrack内部音频缓冲区大小,建议使用getMinBufferSize计算出缓冲区并设置
             * 6:mode:播放模式:MODE_STREAM:按照一定的时间间隔不间断的写入音频数据,MODE_STSTIC:需要一次性将所有的数据都写入播放缓冲区,一般用户铃声和提示音;
             */
            AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                    16000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    musicLength * 2,
                    AudioTrack.MODE_STREAM);
            if(null!=audioTrack && audioTrack.getState()!=AudioTrack.STATE_UNINITIALIZED){//audioTrack不为空且当前状态已经初始化完成
                audioTrack.play();//切换到播放状态
            }
            audioTrack.write(music, 0, musicLength); //将数据写入到AudioTrack中
            audioTrack.stop();
        } catch (Throwable t) {
            Log.e("tag", "播放失败");
        }
    }
}


 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/qq_17338093/article/details/79057833