音频开发——录音与播放、音量调节

来源:互联网 发布:python 迭代器 定义 编辑:程序博客网 时间:2024/05/21 10:17

Android提供了两个API用于实现录音功能:
android.media.AudioRecord和android.media.MediaRecorder,对比如下:

API 应用场景 优点 缺点 AudioRecord 边录边播实时处理 可以用代码实现各种音频的封装 输出PCM语音数据,若保存成音频文件,不能被播放器识别的,必须开发者编写代码实现数据编码以及压缩。 MediaRecorder 普通录音 使用简单,已经集成了录音、编码、压缩等,支持少量音频格式 无法实时处理音频



会说话的汤姆猫,以及其他语音识别与处理的软件(如百度翻译、科大讯飞翻译),获取音源数据肯定是使用了AudioRecord。用AudioRecord录音,并实现WAV格式封装,未经过任何压缩,输出的音频文件很大。MediaRecorder由于已经实现了音频数据的压缩和编码,支持.aac(API = 16) .amr .3gp等,因此,输出的音频文件较小。
当然,这里说的文件大小跟采样率以及采集通道的选择还有直接关系。比如AudioRecord,16000的采用率,单声道,那么录制1秒获取的原始数据量为16000个short,也就是32000个字节;双通道就再乘以2,即64000个字节。

使用MediaRecorder比较简单:

public void startAudioRecord(String saveFilePath){        mediaRecorder = new MediaRecorder();        recordFile = new File(saveFilePath);        // 判断,若当前文件已存在,则删除        if (recordFile.exists()) {            recordFile.delete();        }        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);        mediaRecorder.setAudioChannels(2);        mediaRecorder.setAudioSamplingRate(16000);        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);        mediaRecorder.setOutputFile(recordFile.getAbsolutePath());        mediaRecorder.setMaxDuration(1000 * 60 * 60);        try {            // 准备好开始录音            mediaRecorder.prepare();            mediaRecorder.start();            logd("录音开始,"+recordFile.getAbsolutePath());            TGTConfig.isAudioRecordStart = true;        } catch (IllegalStateException e) {            // TODO Auto-generated catch block            e.printStackTrace();            logd(e);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();            logd(e);        }    }    public void stopAudioRecord(){        if (mediaRecorder != null) {            mediaRecorder.stop();            mediaRecorder.release();            mediaRecorder = null;            logd("录音结束,"+recordFile.getAbsolutePath());            TGTConfig.isAudioRecordStart = false;        }    }

使用AudioRecord稍微复杂一点,网上有现成的代码,这里仅供参考:

第一步,创建对象,获取buffer大小,这里的采样率取16000;44100(44.1kHz)是目前普遍使用的频率,但是某些设备仍然支持22050,16000,11025;

public final static int AUDIO_SAMPLE_RATE = 16000;public final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;private void creatAudioRecord(String savePath) {    // 获取音频文件路径    AudioName = savePath.replace(".wav", ".raw");    NewAudioName = savePath;    // 获得缓冲区字节大小    bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);    // 创建AudioRecord对象    audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);}

AudioFormat.CHANNEL_IN_STEREO是指双通道,还有很多channel选项,但不一定每个都支持,不支持会报错导致crash;另外,双通道获取的数据通常是交替存放的,比如:最终buffer(short类型举例)内存放的数据将会是:short1,short2,short1,short2,……这样的顺序;

第二步,用一个子线程处理音频数据:

private void saveAudioRecord(final String AudioName, final String NewAudioName){        new Thread(new Runnable() {            @Override            public void run() {                writeDateTOFile(AudioName);//往文件中写入裸数据                TGTConfig.audioRecordEnable = true;//裸数据文件写结束,才接受新的录制指令                logd("audio Record Enable again.");                copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件                new File(AudioName).delete();//删除裸数据                logd("录音文件大小:"+ AdbUtil.sendAdb("du -sh "+ NewAudioName, true).replace(NewAudioName,""));            }        }).start();    }//writeDateTOFile方法和copyWaveFile方法请参照其他网上资料

//获取数据的核心方法是
readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);其中audiodata可以是short数组或者byte数组,我认为使用byte数组,方便直接通过FileOutputStream进行文件存储,而使用short数组,方便对音频数据进行处理,比如计算分贝等等。当然short和byte数组的转换也是很方便的,可以自己实现,也可以使用ByteBuffer类:

ByteBuffer.wrap(audiodata).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(buffer2);

这里进行转换的话,有一个大端(高位存高地址)小端(地位存高地址)的概念需要注意。

播放录音采用MediaPlayer,举例如下:

//播放携带的音频资源文件public void startPlayAudio(int resourceId) {        if (mediaPlayer != null) {            mediaPlayer.stop();            mediaPlayer.reset();        }        try {            mediaPlayer = MediaPlayer.create(context, resourceId);            mediaPlayer.start();        } catch (Exception e) {            e.printStackTrace();            logd(e);        }}
//播放音频文件public void startPlayAudio(String filePath){        playFile = new File(filePath);        if (playFile.exists() && playFile != null) {            Uri uri = Uri.fromFile(playFile);            if (mediaPlayer != null) {                mediaPlayer.stop();                mediaPlayer.reset();            }            mediaPlayer = MediaPlayer.create(context, uri);            if(mediaPlayer != null) {                mediaPlayer.start();                TGTConfig.isAudioPlayStart = true;                logd("播放录音文件," + playFile.getAbsolutePath());                //监听MediaPlayer播放完成                mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {                    @Override                    public void onCompletion(MediaPlayer paramMediaPlayer) {                        // TODO Auto-generated method stub                        logd("录音播放结束," + playFile.getAbsolutePath());                        TGTConfig.isAudioPlayStart = false;                    }                });            }else{                logd("播放录音失败");            }        }else{            logd("无法播放录音,文件不存在");        }}

调节音量也很简单,举例如下:

AudioManager audioManager;public boolean setVolume(int volume, int stream) {    if (audioManager != null) {        /*logd("max value is " + audioManager.getStreamMaxVolume(stream)                    + ",min value is " + audioManager.getStreamMinVolume(stream));*/        audioManager.setStreamVolume(stream, volume, 0);        if (audioManager.getStreamVolume(stream) == volume) {            return true;        }    } else {        audioManager = (AudioManager) App.getContext().getSystemService(Context.AUDIO_SERVICE);        return false;    }    return false;}
原创粉丝点击