Android如何将采集到的音频PCM文件转为WAV并保存

1.Android音频采集

添加权限

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

动态申请权限

引入权限申请库

implementation 'com.permissionx.guolindev:permissionx:1.4.0'

申请权限的部分代码

PermissionX.init(this).permissions(Manifest.permission.RECORD_AUDIO)
            .request { _, _, _ -> 
    //TODO 申请成功之后的代码写在这里
 }

初始化AudioRecorder

需要设置的参数有:音频源,采样率,声道数,数据类型,最小缓冲区

最小缓冲区的大小用AudioRecord.getMinBufferSize接口,根据采样率,声道数,数据类型返回该值;

private const val SAMPLE_RATE_IN_HZ_16K = 16000
private var minBufferSize = 1280
private var audioRecord: AudioRecord? = null
private var captureThread: Thread? = null
private var isRunning = false
private var isFloats = false

fun start(isFloats: Boolean) {
    this.isFloats = isFloats
    minBufferSize = AudioRecord.getMinBufferSize(
        SAMPLE_RATE_IN_HZ_16K,
        AudioFormat.CHANNEL_IN_MONO,
        if (isFloats) AudioFormat.ENCODING_PCM_FLOAT else AudioFormat.ENCODING_PCM_16BIT
    )
    audioRecord = AudioRecord(
        MediaRecorder.AudioSource.VOICE_COMMUNICATION,
        SAMPLE_RATE_IN_HZ_16K,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_FLOAT,
        minBufferSize
    )
    audioRecord!!.startRecording()
    isRunning = true
}

音频采集时可以设置的格式有

public static final int ENCODING_DEFAULT = 1;
// These values must be kept in sync with core/jni/android_
// Also sync av/services/audiopolicy/managerdefault/ConfigP
/** Audio data format: PCM 16 bit per sample. Guaranteed to
public static final int ENCODING_PCM_16BIT = 2;
/** Audio data format: PCM 8 bit per sample. Not guaranteed
public static final int ENCODING_PCM_8BIT = 3;
/** Audio data format: single-precision floating-point per 
public static final int ENCODING_PCM_FLOAT = 4;
/** Audio data format: AC-3 compressed, also known as Dolby
public static final int ENCODING_AC3 = 5;
/** Audio data format: E-AC-3 compressed, also known as Dol
public static final int ENCODING_E_AC3 = 6;
    /** Audio data format: DTS compressed */
public static final int ENCODING_DTS = 7;
    /** Audio data format: DTS HD compressed */
public static final int ENCODING_DTS_HD = 8;
    /** Audio data format: MP3 compressed */
public static final int ENCODING_MP3 = 9;
    /** Audio data format: AAC LC compressed */
public static final int ENCODING_AAC_LC = 10;
    /** Audio data format: AAC HE V1 compressed */
public static final int ENCODING_AAC_HE_V1 = 11;
    /** Audio data format: AAC HE V2 compressed */
public static final int ENCODING_AAC_HE_V2 = 12;

开始采集数据

fun capture(listener: OnReadListener) {
    captureThread = Thread {
        while (isRunning) {
            var nReadBytes: Int
            if (isFloats) {
                val data = FloatArray(minBufferSize)
                nReadBytes =
                    audioRecord!!.read(data, 0, minBufferSize, AudioRecord.READ_BLOCKING)
                listener.read(nReadBytes, data)
            } else {
                val data = ByteArray(minBufferSize)
                nReadBytes =
                    audioRecord!!.read(data, 0, minBufferSize)
                listener.read(nReadBytes, data)
            }
        }
    }
    captureThread!!.start()
}

数据回调接口

interface OnReadListener {
    fun read(readSize: Int, floatArray: FloatArray)
    fun read(readSize: Int, byteArray: ByteArray)
}

结束采集,关闭AudioRecorder

fun stop() {
    isRunning = false
    captureThread!!.interrupt()
    try {
        audioRecord!!.stop()
        audioRecord!!.release()
    } catch (e : Exception) {
        e.printStackTrace()
    }
}

采集到的数据后将PCM数据保存

AudioCapture.capture(object : AudioCapture.OnReadListener {
    override fun read(readSize: Int, floatArray: FloatArray) {
        val savePath = getDir(
            "spanner", Context.MODE_PRIVATE
        ).absolutePath + File.separator + "/Music/"
        //保存浮点数据到本地文件
        SaveBytesAsFile.writeFile(floatArray, savePath + "record.pcm")
    }
    override fun read(readSize: Int, byteArray: ByteArray) {
        
    }
})



public class SaveBytesAsFile {

    public SaveBytesAsFile() {
    }

    public void writeFile(byte[] bytes) {
        long time = System.currentTimeMillis();
        String path = Environment.getExternalStorageDirectory() + File.separator + "g711ToAAC.aac";
        try {
            FileOutputStream out = new FileOutputStream(path, true);//指定写到哪个路径中
            FileChannel fileChannel = out.getChannel();
            fileChannel.write(ByteBuffer.wrap(bytes)); //将字节流写入文件中
            fileChannel.force(true);//强制刷新
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 将流写入口自定义路径
     *
     * @param bytes
     * @param path  ps:快捷回复AAC转PCM之后写入到指定路径   请勿修改
     */
    public static void writeFile(byte[] bytes, String path) {

        try {
            FileOutputStream out = new FileOutputStream(path, true);//指定写到哪个路径中
            FileChannel fileChannel = out.getChannel();
            fileChannel.write(ByteBuffer.wrap(bytes)); //将字节流写入文件中
            fileChannel.force(true);//强制刷新
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void writeFile(float[] floats, String path) {
        try {
            FileOutputStream fos = new FileOutputStream(path, true);
            DataOutputStream dos = new DataOutputStream(fos);
            for (float f : floats) {
                dos.writeFloat(f);
            }
            dos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.PCM文件转WAV

播放浮点型音频的指令

ffplay -f f32be  -ar 16000 -ac 1 -i record.pcm

使用FFMPEG

ffmpeg -f 音频格式 -ar 采样率 -ac 频道数 -i pcm文件名 -ar 采样率 -ac 频道数 wav文件名

此处把浮点型的PCM数据转化

//浮点型音频PCM转WAV格式音频指令
ffmpeg -f f32be -ar 16000 -ac 1 -i record.pcm -ar 16000 -ac 1 record.wav

查看ffmepg可以解析的格式指令

扫描二维码关注公众号,回复: 14988485 查看本文章
ffmpeg -formats

DE f32be           PCM 32-bit floating-point big-endian
DE f32le           PCM 32-bit floating-point little-endian
DE f64be           PCM 64-bit floating-point big-endian
DE f64le           PCM 64-bit floating-point little-endian
DE s16be           PCM signed 16-bit big-endian
DE s16le           PCM signed 16-bit little-endian
DE s24be           PCM signed 24-bit big-endian
DE s24le           PCM signed 24-bit little-endian
DE s32be           PCM signed 32-bit big-endian
DE s32le           PCM signed 32-bit little-endian
DE u16be           PCM unsigned 16-bit big-endian
DE u16le           PCM unsigned 16-bit little-endian
DE u24be           PCM unsigned 24-bit big-endian
DE u24le           PCM unsigned 24-bit little-endian
DE u32be           PCM unsigned 32-bit big-endian
DE u32le           PCM unsigned 32-bit little-endian

猜你喜欢

转载自blog.csdn.net/mozushixin_1/article/details/128580848