Android音频开发之录制音频(WAV及MP3格式)

目录
  • 一、音频录制权限:
  • 二、录音文件的配置:
  • 三、音频录制:
    • 1、录音对象初始化:
    • 2、录制wav音频文件:
    • 3、录制MP3音频文件
  • 四、音频录制管理【AudioRecordManager】:

附GitHub源码:MultimediaExplore

首先看下音频录制跟播放效果简图:

上面是录音:长按即可录音,支持声波动画,右滑删除等。支持录制pcm、wav、mp3格式音频。

下面是播放:点击左边扬声器icon,开始播放刚录制的本地音频文件【也支持在线音频播放】,支持播放进度,支持切换播放模式(听筒/扬声器/耳机)等。

一、音频录制权限:

无论在做开发任何功能之前,总得先添加及申请相关权限,后续的工作才能正常进行下去。音频录制所需权限如下,而且要在代码中动态申请这些敏感权限,同意后才能正常录制:

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

二、录音文件的配置:

通过第一节讲到音频的基础概念可知,在录制音频前应先进行录制的相关配置,它直接决定了录音文件的音频质量、文件大小、音频格式等。

    /**
     * 录音音频的相关配置
     */
    private void initConfig() {
        recordConfig = new RecordConfig();
        //采样位宽
        recordConfig.setEncodingConfig(AudioFormat.ENCODING_PCM_16BIT);
        //录音格式
        recordConfig.setFormat(RecordConfig.RecordFormat.MP3);
        // recordConfig.setFormat(RecordConfig.RecordFormat.WAV);
        //采样频率
        recordConfig.setSampleRate(16000);
        String recordDir = String.format(Locale.getDefault(), "%s/Record/zhongyao/",
                Environment.getExternalStorageDirectory().getAbsolutePath());
        //存储目录
        recordConfig.setRecordDir(recordDir);
        AudioRecordManager.getInstance().setCurrentConfig(recordConfig);
    }

三、音频录制:

音频录制类主要有两个封装类:分别是AudioRecorder 、AudioRecordManager。

AudioRecorder:主要是使用系统的AudioRecord来进行录音。并把录制好的音频文件进行合并,转码等,生成我们所需的音频文件。该文件是全局单例的,保证音频录制类只有一个实例。

AudioRecordManager:对AudioRecorder的封装管理,与外界交互均通过此类来完成,包括录音的各种生命周期控制调用等。减少了外界与AudioRecorder的直接交互,已达到对录音类的更好的管理,此类也是一个全局单例类。

1、录音对象初始化:

这里主要根据之前的录音配置,生成 bufferSizeInBytes【缓冲区字节大小】,和audioRecord对象。

    /**
     * 创建默认的录音对象
     */
    public void prepareRecord() {
        // 获得缓冲区字节大小
        if (bufferSizeInBytes == 0) {
            bufferSizeInBytes = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(),
                    currentConfig.getChannelConfig(), currentConfig.getEncodingConfig());
        }
        if (audioRecord == null) {
            audioRecord = new AudioRecord(AUDIO_INPUT, currentConfig.getSampleRate(),
                    currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSizeInBytes);
        }

        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_PREPARE;
    }

2、录制wav音频文件:

wav音频文件是无损的,所以音质会接近原生,但也正是因为是无损的,所以wav音频文件几乎没有压缩,相对来说会比较大。

录制wav音频得先进行录制采用,获得pcm文件,然后把pcm文件合并,最后再转成wav音频文件。

(1)开始录制pcm文件:

    private void startPcmRecorder() {
        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START;
        notifyState();
        Logger.d(TAG, "开始录制 Pcm");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(tmpFile);
            audioRecord.startRecording();
            byte[] byteBuffer = new byte[bufferSizeInBytes];

            while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) {
                int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
                notifyData(byteBuffer);
                fos.write(byteBuffer, 0, end);
                fos.flush();
            }
            audioRecord.stop();
            files.add(tmpFile);
            if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_STOP) {
                makeFile();
            } else {
                Logger.d(TAG, "取消录制...");
            }
        } catch (Exception e) {
            Logger.e(e, TAG, e.getMessage());
            notifyError("录音失败");
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) {
            audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_IDLE;
            notifyState();
            Logger.d(TAG, "录音结束");
        }
    }

(2)合并生成的多个pcm文件:

    /**
     * 合并pcm文件
     */
    private void mergePcmFile() {
        boolean mergeSuccess = mergePcmFiles(resultFile, files);
        if (!mergeSuccess) {
            notifyError("合并失败");
        }
    }

(3)将合并好的pcm文件转成wav文件:

    /**
     * 添加Wav头文件
     */
    private void makeWav() {
        if (!FileUtil.isFile(resultFile) || resultFile.length() == 0) {
            return;
        }
        byte[] header = WavUtils.generateWavFileHeader((int) resultFile.length(), currentConfig.getSampleRate(), currentConfig.getChannelCount(), currentConfig.getEncoding());
        WavUtils.writeHeader(resultFile, header);
    }

3、录制MP3音频文件

相比WAV音频文件而言,MP3音频文件,就更加常见,商业上使用的也比较多,就是因为MP3音频时经过压缩的,文件大小只有WAV的十二分之一,但是音质上几乎没有较大的差异性。当对音质没有极高要求的情况下,如录音文件,MP3格式是极好的选择。

(1)开始录制音频缓存:

这里有开启一个线程Mp3EncodeThread,将录音产生的字节数组byteBuffer不断的进行编解码生成MP3文件。

    private void startMp3Recorder() {
        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START;
        notifyState();

        try {
            audioRecord.startRecording();
            short[] byteBuffer = new short[bufferSizeInBytes];

            while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) {
                int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
                if (mp3EncodeThread != null) {
                    mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end));
                }
                notifyData(ByteUtils.toBytes(byteBuffer));
            }
            audioRecord.stop();
        } catch (Exception e) {
            Logger.e(e, TAG, e.getMessage());
            notifyError("录音失败");
        }
        if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) {
            if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_CANCEL) {
                deleteMp3Encoded();
            } else {
                stopMp3Encoded();
            }
        } else {
            Logger.d(TAG, "暂停");
        }
    }

(2)MP3音频编解码:

Android原生的音频录制,支持直接生成WAV文件,但其实是不支持直接生成MP3文件的。这里对应MP3编解码,主要用到了开源的 libmp3lame.so 这个音频编解码库。以下是lame编解码方法及Mp3Encoder类:

MP3编解码方法:

    private void lameData(ChangeBuffer changeBuffer) {
        if (changeBuffer == null) {
            return;
        }
        short[] buffer = changeBuffer.getData();
        int readSize = changeBuffer.getReadSize();
        if (readSize > 0) {
            int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer);
            if (encodedSize < 0) {
                Logger.e(TAG, "Lame encoded size: " + encodedSize);
            }
            try {
                os.write(mp3Buffer, 0, encodedSize);
            } catch (IOException e) {
                Logger.e(e, TAG, "Unable to write to file");
            }
        }
    }

Mp3Encoder类:

public class Mp3Encoder {

    static {
        System.loadLibrary("mp3lame");
    }

    public native static void close();

    public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf);

    public native static int flush(byte[] mp3buf);

    public native static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality);

    public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) {
        init(inSampleRate, outChannel, outSampleRate, outBitrate, 7);
    }
}

(3)生成libmp3lame.so:

本项目提供的源码中有lame的jni源码,如果想生成libmp3lame.so文件,供自己的项目使用,那么需要对Mp3Encoder.c 和Mp3Encoder.h文件进行修改,然后通过ndk build 生成该so文件。

修改的内容主要是把文件中的Mp3Encoder路径改成自己项目中的Mp3Encoder的路径,否则会找不到相关的native方法。

另外一般情况下,cpu类型支持 armeabi-v7a 、arm64-v8a 两种即可,如果想支持其他的可在Application.mk中添加。

上面文件修改完毕,通过ndk-build指令即可编译生成对应的 libmp3lame.so 文件。

将so不同CPU类型的文件放置 jniLibs 下,或者通过sourceSets配置的路径下,如:

    sourceSets.main {
        jni.srcDirs = []//disable automatic ndk-build call
        jniLibs.srcDirs = ['libs']
    }

即可进行MP3音频格式的录制。

四、音频录制管理【AudioRecordManager】:

通过全局单例模式的AudioRecorderManager与业务进行交互,即保证了音频录制实例的单一性,又能较好的对音频生命周期等进行较好的管理。 

/**
 * Create by zhongyao on 2021/8/18
 * Description:音频录制管理类
 */
public class AudioRecordManager {

    private static final String TAG = "AudioRecordManager";

    private AudioRecordManager() {
    }

    public static AudioRecordManager getInstance() {
        return AudioRecordManagerHolder.instance;
    }

    public static class AudioRecordManagerHolder {
        public static AudioRecordManager instance = new AudioRecordManager();
    }

    public void setCurrentConfig(RecordConfig recordConfig) {
        AudioRecorder.getInstance().setCurrentConfig(recordConfig);
    }

    public RecordConfig getCurrentConfig() {
        return AudioRecorder.getInstance().getCurrentConfig();
    }

    /**
     * 录音状态监听回调
     */
    public void setRecordStateListener(RecordStateListener listener) {
        AudioRecorder.getInstance().setRecordStateListener(listener);
    }

    /**
     * 录音数据监听回调
     */
    public void setRecordDataListener(RecordDataListener listener) {
        AudioRecorder.getInstance().setRecordDataListener(listener);
    }

    /**
     * 录音可视化数据回调,傅里叶转换后的频域数据
     */
    public void setRecordFftDataListener(RecordFftDataListener recordFftDataListener) {
        AudioRecorder.getInstance().setRecordFftDataListener(recordFftDataListener);
    }

    /**
     * 录音文件转换结束回调
     */
    public void setRecordResultListener(RecordResultListener listener) {
        AudioRecorder.getInstance().setRecordResultListener(listener);
    }

    /**
     * 录音音量监听回调
     */
    public void setRecordSoundSizeListener(RecordSoundSizeListener listener) {
        AudioRecorder.getInstance().setRecordSoundSizeListener(listener);
    }

    public void setStatus(AudioRecordStatus curAudioRecordStatus) {
        switch (curAudioRecordStatus) {
            case AUDIO_RECORD_IDLE:

                break;

            case AUDIO_RECORD_PREPARE:
                AudioRecorder.getInstance().prepareRecord();
                break;

            case AUDIO_RECORD_START:
                AudioRecorder.getInstance().startRecord();
                break;

            case AUDIO_RECORD_PAUSE:
                AudioRecorder.getInstance().pauseRecord();
                break;

            case AUDIO_RECORD_STOP:
                AudioRecorder.getInstance().stopRecord();
                break;

            case AUDIO_RECORD_CANCEL:
                AudioRecorder.getInstance().cancelRecord();
                break;

            case AUDIO_RECORD_RELEASE:
                AudioRecorder.getInstance().releaseRecord();
                break;

            default:
                break;
        }
    }

    public AudioRecordStatus getStatus() {
        return AudioRecorder.getInstance().getAudioRecordStatus();
    }

    public String getAudioPath() {
        return AudioRecorder.getInstance().getAudioPath();
    }
}

以上就是Android音频开发之录制音频(WAV及MP3格式)的详细内容,更多关于Android 音频录制的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android利用AudioRecord类实现音频录制程序

    AudioRecord类相对于MediaRecorder来说,更加接近底层,为我们封装的方法也更少.然而实现一个AudioRecord的音频录制程序也很简单.本实例代码如下: package demo.camera; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; imp

  • Android音频处理之通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能

    音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下Android他提供的录音类,实际上他有两个,一个是MediaRecorder,还有一个就是我们今天要用到的AudioRecord,那他们有什么区别呢? 一.区别 MediaRecorder和AudioRecord都可以录制音频,区别是MediaRecorder录制的音频文件是经过压缩后的,需要设置编码器.并且录制的音频文件可以用系统自带的Music播放器播放. 而AudioRecord录制的是P

  • Android使用MediaRecorder类实现视频和音频录制功能

    一.前期基础知识储备 Android提供了MediaRecorder这一个类来实现视频和音频的录制. 由官方配图可知,MediaRecorder用于录制视频时需要调用一系列的API来设置和录制相关的配置,而且调用方法的顺序是固定的,必须按照这个顺序进行API调用才能正确利用手机摄像头实现录像功能. 调用MediaRecorder的录像API顺序如下: 1)Open Camera - Use the Camera.open() to get an instance of the camera ob

  • Android音频录制MediaRecorder之简易的录音软件实现代码

    使用MediaRecorder的步骤:1.创建MediaRecorder对象2.调用MediRecorder对象的setAudioSource()方法设置声音的来源,一般传入MediaRecorder.MIC3.调用MediaRecorder对象的setOutputFormat()设置所录制的音频文件的格式4.调用MediaRecorder对象的setAudioRncoder().setAudioEncodingBitRate(int bitRate).setAudioSamlingRate(i

  • Android录制声音文件(音频)并播放

    本文实例为大家分享了Android录制音频文件的具体代码,供大家参考,具体内容如下 1.这个demo中没有对多次点击同一个声音文件做详细处理,偶尔会有崩溃,用的时候需要注意. 2.按住录音按钮录音过程中,只对竖直方向处理了一下,水平方向没写: 3.没有做删除某个声音文件的操作,但是测试的时候实现了功能,需要用到的话,在MainActivity->onItemClick中的TODO中有详细说明: 4.这只是个demo,如果要在项目中使用,先写出demo,没问题了,再引入项目,在写成demo后,在真

  • Android仿微信、录制音频并发送功能

    MyRecorder(仿微信,录制音频并发送功能) ①布局实现(activity_main.xml) 布局采用线性布局,上面使用的一个ListView,下面使用的是一个自定义的Button(会在下面进行介绍) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • Android音频开发之录制音频(WAV及MP3格式)

    目录 一.音频录制权限: 二.录音文件的配置: 三.音频录制: 1.录音对象初始化: 2.录制wav音频文件: 3.录制MP3音频文件 四.音频录制管理[AudioRecordManager]: 附GitHub源码:MultimediaExplore 首先看下音频录制跟播放效果简图: 上面是录音:长按即可录音,支持声波动画,右滑删除等.支持录制pcm.wav.mp3格式音频. 下面是播放:点击左边扬声器icon,开始播放刚录制的本地音频文件[也支持在线音频播放],支持播放进度,支持切换播放模式(

  • Android录制mp3格式文件

    前言 最近做一个即时通信类的项目,由于要保证pc端,iOS端和Android端的通用性,最终统一为MP3格式,一直担心MP3格式会不会很大,但是实测还是可以接受的.下面来看看具体步骤: 工具 MP3格式是用一个开源项目转的,MP3lame,由于该项目用到了jni,所以需要大家配置好ndk环境,环境配置在此就不多说了,大家可以自行百度,最新的应该很好配置. 创建jni 拷贝文件 下载好后(我下载的是3.98.4版本)打开,找到libmp3lame文件,将里面的.h和.c拷贝下来,在自己的工程里创建

  • Android音频开发之音频采集的实现示例

    在 Android 系统中,一般使用 AudioRecord 或者 MediaRecord 来采集音频. AudioRecord 是一个比较偏底层的API,它可以获取到一帧帧 PCM 数据,之后可以对这些数据进行处理. 而 MediaRecorder 是基于 AudioRecorder 的 API(最终还是会创建AudioRecord用来与AudioFlinger进行交互) ,它可以直接将采集到的音频数据转化为执行的编码格式,并保存. 直播技术采用的就是 AudioRecorder 采集音频数据

  • Android开发基础实现音频文件的播放详解

    目录 前言 实现方法 最终效果 总结 前言 上一篇(安卓开发基础——实现最简单的视频播放我们简单的实现了一个播放视频的功能,这一节我们来实现App对音频文件的播放功能,本文主要是依靠MediaPlayer类去实现Android播放音乐的. 实现方法 和上一篇的播放功能实现类似,我们首先需要一个文件夹去放我们的音频文件,我们在main文件夹下新建一个assets文件夹放入我们的音频文件 然后我们在布局中添加一张图片,下面加上三个处理播放控制的按钮,播放,暂停(暂停播放),停止(正在播放就停止播放,

  • Android录制语音文件wav转mp3的方法示例

    1.Android 使用AudioRecord而实现录音暂停以及wav文件转mp3文件.因为android系统开源的原因,导致许多生产厂商乱改系统源码,此处小米最为恶心,使用android原生的AudioRecord录制只能保存wav和pcm格式的语音文件,但是小米手机录制的wav语音文件系统本身不支持,所以使用MediaPlayer不能播放wav格式的文件,其它手机可以.此时有一万匹草泥马从旁边疾驰而过... 2.针对这个问题解决方案目前有两种: 方案1: 将录制完的wav文件转化成mp3格式

  • Android开发之MediaPlayer多媒体(音频,视频)播放工具类

    本文实例讲述了Android开发之MediaPlayer多媒体(音频,视频)播放工具类.分享给大家供大家参考,具体如下: package com.android.imooc.chat; import java.io.IOException; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; impor

  • Android实现音频条形图效果(仿音频动画无监听音频输入)

    音频条形图 如下图所示就是这次的音频条形图: 由于只是自定义View的用法,我们就不去真实地监听音频输入了,随机模拟一些数字即可. 如果要实现一个如上图的静态音频条形图,相信大家应该可以很快找到思路,也就是绘制一个个的矩形,每个矩形之间稍微偏移一点距离即可.如下代码就展示了一种计算坐标的方法. for (int i = 0; i < mRectCount; i++) { // 矩形的绘制是从左边开始到上.右.下边(左右边距离左边画布边界的距离,上下边距离上边画布边界的距离) canvas.dra

  • python pcm音频添加头转成Wav格式文件的方法

    如下所示: ''''' add Head Infomation for pcm file ''' import sys import struct import os __author__ = 'bob_hu, hewitt924@gmail.com' __date__ = 'Dec 19,2011' __update__ = 'Dec 19,2011' def geneHeadInfo(sampleRate,bits,sampleNum): ''''' 生成头信息,需要采样率,每个采样的位数,

  • Android 判断网络状态对音频静音的实现方法

    在实际应用中,我们不希望在教室网络,打开游戏就显示较大的声音,进而影响上课质量.因此,就需要让app变得智能,让app可以根据使用者当前网络状态,自动进行静音等操作. 本次内容分为两部分:1. 识别网络环境 2. 实现app自动静音. 自动静音 /** * 实现静音功能 */ private void silentSwitchOn() { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVI

随机推荐