Android 录音中的那些坑

来源:互联网 发布:linux网络设置 编辑:程序博客网 时间:2024/05/22 08:27
每一个看似简单的功能背后,都是攻城狮们用汗水填起的坑,尤其在这么热的天。


最近又邂逅了Android的录音模块,由于是前同事留下的代码,bug改得宝宝真是欲仙欲死。首先看下原始的实现:


其中RecorderHelp中封装了MediaRecorder,录音这种耗时操作放在了AsyncTask中进行。其实并说不上这种设计有什么缺点,但在使用的过程确实虐心,下边是自己的实现。


一个比较恶心的问题,如何在开启录音前增加权限检测。在Google原生SDK上,当然可以使用CompatContext.checkSelfPermission来做,但碰到小米华为等手机,这个方法就gg了,无论用户是否禁止了录音权限,都会返回给PERMISSION_GRANTED。没办法,果断google,大概有如下几个方案:
1.try catch MediaRecorder在start的时候,如果没有权限会抛出状态异常,但尝试后发现华为手机是不抛任何异常的,小米会启动失败。
2.预录制一段音频,判断麦克风振幅。但这个方案在小米上貌似是可以的,他妈的华为的又gg了。
3.接着2,华为的虽然靠振幅不能判断,但P7上是不会生成录音文件的,大喜。结果提交后,QA反馈honor上在权限被禁的情况下可以录制1s的文件。。。。要去日几只狗。


后边老大提醒看下微信是如何做的,看了下微信,体验还真不错,但突然想到微信是可以语音转文字的啊,那肯定是用AudioRecorder了。随手Google下AudioRecorder,有哥们给出了如下方案


public static int getRecordState() {        int minBuffer = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);        AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, (minBuffer * 100));        short[] point = new short[minBuffer];        int readSize = 0;        try {            audioRecord.startRecording();//检测是否可以进入初始化状态        } catch (Exception e) {            if (audioRecord != null) {                audioRecord.release();                audioRecord = null;                LogUtils.d("CheckAudioPermission", "无法进入录音初始状态");            }            return STATE_NO_PERMISSION;        }        if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {            //6.0以下机型都会返回此状态,故使用时需要判断bulid版本            //检测是否在录音中            if (audioRecord != null) {                audioRecord.stop();                audioRecord.release();                audioRecord = null;                LogUtils.d("CheckAudioPermission", "录音机被占用");            }            return STATE_RECORDING;        } else {            //检测是否可以获取录音结果            readSize = audioRecord.read(point, 0, point.length);            if (readSize <= 0) {                if (audioRecord != null) {                    audioRecord.stop();                    audioRecord.release();                    audioRecord = null;                }                LogUtils.d("CheckAudioPermission", "录音的结果为空");                return STATE_NO_PERMISSION;            } else {                if (audioRecord != null) {                    audioRecord.stop();                    audioRecord.release();                    audioRecord = null;                }                return STATE_SUCCESS;            }        }    }




这个在测试后发现可以兼容大部分的手机。收工。



---------------------------------------------------------------------------------------------------


上述代码发现在米3上仍不能使用,不管录音权限是否打开,米3返回的state始终为failed,该型号的手机至今没有找到解决方案,求大神们路过告知。

接下来是监听语音播放时耳机插入与拔出事件,不多说先上代码

package com.didapinche.booking.common.voice;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.media.AudioManager;import android.media.MediaPlayer;import android.net.Uri;import android.os.Build;import com.apkfuns.logutils.LogUtils;import com.didapinche.booking.app.CarpoolApplication;import java.io.File;import java.io.IOException;/** * Created by leeks on 2016/8/22. */public class VoicePlayer {    /**     * 外放模式     */    public static final int MODE_SPEAKER = 0;    /**     * 耳机模式     */    public static final int MODE_HEADSET = 1;    /**     * 听筒模式     */    public static final int MODE_EARPIECE = 2;    private AudioManager audioManager;    private MediaPlayer mediaPlayer;    private PlayCallback callback;    private Context context;    private HeadsetReceiver receiver;    private IntentFilter filter;    private boolean isPause = false;    private int currentMode = MODE_SPEAKER;    private boolean isRegisterReceiver = false;    private static class Singleton {        public static VoicePlayer instance = new VoicePlayer();    }    public static VoicePlayer getInstance() {        return Singleton.instance;    }    private VoicePlayer() {        this.context = CarpoolApplication.context;        initMediaPlayer();        initAudioManager();        initReceiver();    }    public void initReceiver() {        receiver = new HeadsetReceiver();        filter = new IntentFilter();        filter.addAction(Intent.ACTION_HEADSET_PLUG);        filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);    }    /**     * 初始化播放器     */    private void initMediaPlayer() {        mediaPlayer = new MediaPlayer();        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);    }    /**     * 初始化音频管理器     */    private void initAudioManager() {        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {            audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);        } else {            audioManager.setMode(AudioManager.MODE_IN_CALL);        }        audioManager.setSpeakerphoneOn(true);            //默认为扬声器播放    }    /**     * 播放回调接口     */    public interface PlayCallback {        /**         * 音乐准备完毕         */        void onPrepared();        /**         * 音乐播放完成         */        void onComplete();        /**         * 音乐停止播放         */        void onStop();    }    public void play(String path, final PlayCallback callback) {        play(Uri.parse(path), callback);    }    public void play(File file, PlayCallback callback) {        play(Uri.fromFile(file), callback);    }    private void play(Uri uri, final PlayCallback callback) {        this.callback = callback;        if (!isRegisterReceiver) {            context.registerReceiver(receiver, filter);            isRegisterReceiver = true;        }        if (mediaPlayer == null) {            initMediaPlayer();        }        try {            mediaPlayer.reset();            mediaPlayer.setDataSource(context, uri);            mediaPlayer.prepareAsync();            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {                @Override                public void onPrepared(MediaPlayer mp) {                    if (callback != null) {                        callback.onPrepared();                    }                    mediaPlayer.start();                }            });            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {                @Override                public void onCompletion(MediaPlayer mp) {                    if (callback != null) {                        callback.onComplete();                    }                    resetPlayMode();                }            });        } catch (IOException e) {            e.printStackTrace();        }    }    public boolean isPause() {        return isPause;    }    public void pause() {        if (isPlaying()) {            isPause = true;            mediaPlayer.pause();        }    }    public void resume() {        if (isPause) {            isPause = false;            mediaPlayer.start();        }    }    /**     * 获取当前播放模式     *     * @return     */    public int getCurrentMode() {        return currentMode;    }    /**     * 切换到听筒模式     */    public void changeToEarpieceMode() {        currentMode = MODE_EARPIECE;        audioManager.setSpeakerphoneOn(false);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,                    audioManager.getStreamMaxVolume(AudioManager.MODE_IN_COMMUNICATION), AudioManager.FX_KEY_CLICK);        } else {            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,                    audioManager.getStreamMaxVolume(AudioManager.MODE_IN_CALL), AudioManager.FX_KEY_CLICK);        }    }    /**     * 切换到耳机模式     */    public void changeToHeadsetMode() {        currentMode = MODE_HEADSET;        audioManager.setSpeakerphoneOn(false);    }    /**     * 切换到外放模式     */    public void changeToSpeakerMode() {        currentMode = MODE_SPEAKER;        audioManager.setSpeakerphoneOn(true);    }    public void resetPlayMode() {        if (audioManager.isWiredHeadsetOn()) {            changeToHeadsetMode();        } else {            changeToSpeakerMode();        }    }    /**     * 调大音量     */    public void raiseVolume() {        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);        if (currentVolume < audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) {            audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,                    AudioManager.ADJUST_RAISE, AudioManager.FX_FOCUS_NAVIGATION_UP);        }    }    /**     * 调小音量     */    public void lowerVolume() {        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);        if (currentVolume > 0) {            audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,                    AudioManager.ADJUST_LOWER, AudioManager.FX_FOCUS_NAVIGATION_UP);        }    }    /**     * 停止播放     */    public void stop() {        if (isPlaying()) {            try {                mediaPlayer.stop();                callback.onStop();            } catch (IllegalStateException e) {                e.printStackTrace();            }        }    }    public void release() {        stop();        try {            if (mediaPlayer != null) {                mediaPlayer.release();                mediaPlayer = null;            }        } finally {            if (isRegisterReceiver) {                try {                    context.unregisterReceiver(receiver);                } catch (Exception e) {                    e.printStackTrace();                }                isRegisterReceiver = false;            }        }    }    /**     * 是否正在播放     *     * @return 正在播放返回true, 否则返回false     */    public boolean isPlaying() {        return mediaPlayer != null && mediaPlayer.isPlaying();    }    public int getDuration() {        if (mediaPlayer != null) {            return mediaPlayer.getDuration();        }        return 0;    }    public class HeadsetReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            switch (action) {                //插入和拔出耳机会触发此广播                case Intent.ACTION_HEADSET_PLUG:                    int state = intent.getIntExtra("state", 0);                    if (state == 1) {                        LogUtils.e("耳机已插入");                        changeToHeadsetMode();                    } else if (state == 0) {                        LogUtils.e("音乐恢复播放");                        resume();                    }                    break;                //拔出耳机会触发此广播,拔出不会触发,且此广播比上一个早,故可在此暂停播放,收到上一个广播时在恢复播放                case AudioManager.ACTION_AUDIO_BECOMING_NOISY:                    pause();                    LogUtils.e("耳机已拔出");                    if (isPause()) {                        LogUtils.e("音乐已暂停");                    }                    changeToSpeakerMode();                    break;                default:                    break;            }        }    }}


0 0
原创粉丝点击