Andorid pcm转码wav

参考文章:https://blog.csdn.net/hesong1120/article/details/79043482

最近工作开发中,碰到一个BUG,由于WebApp开发,一套H5代码适用于Android和IOS,但是发语音时,Android无法打开IOS的pcm格式文件,而IOS同样无法打开Android的wav格式文件。很尴尬,尝试了直接改文件后缀名强转,可惜会损坏文件。那么只有转码这一条路可以走了。

Android中,想要播放,那么就需要单独判断pcm文件,转码给wav,再使用mediaplayer播放。

先上代码:

  /**
     * PCM文件转WAV文件
     * @param inPcmFilePath 输入PCM文件路径
     * @param outWavFilePath 输出WAV文件路径
     * @param sampleRate 采样率,例如15000
     * @param channels 声道数 单声道:1或双声道:2
     * @param bitNum 采样位数,8或16
     */
    public static void convertPcmToWav(String inPcmFilePath, String outWavFilePath, int sampleRate,
                                      int channels, int bitNum) {
        FileInputStream in = null;
        FileOutputStream out = null;
        byte[] data = new byte[1024];

        try {
            //采样字节byte率
            long byteRate = sampleRate * channels * bitNum / 8;

            in = new FileInputStream(inPcmFilePath);
            out = new FileOutputStream(outWavFilePath);

            //PCM文件大小
            long totalAudioLen = in.getChannel().size();

            //总大小,由于不包括RIFF和WAV,所以是44 - 8 = 36,在加上PCM文件大小
            long totalDataLen = totalAudioLen + 36;

            writeWaveFileHeader(out, totalAudioLen, totalDataLen, sampleRate, channels, byteRate);

            int length = 0;
            while ((length = in.read(data)) > 0) {
                out.write(data, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 输出WAV文件
     * @param out WAV输出文件流
     * @param totalAudioLen 整个音频PCM数据大小
     * @param totalDataLen 整个数据大小
     * @param sampleRate 采样率
     * @param channels 声道数
     * @param byteRate 采样字节byte率
     * @throws IOException
     */
    private static void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,
                                            long totalDataLen, int sampleRate, int channels, long byteRate) throws IOException {
        byte[] header = new byte[44];
        header[0] = 'R'; // RIFF
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);//数据大小
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';//WAVE
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        //FMT Chunk
        header[12] = 'f'; // 'fmt '
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';//过渡字节
        //数据大小
        header[16] = 16; // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        //编码方式 10H为PCM编码格式
        header[20] = 1; // format = 1
        header[21] = 0;
        //通道数
        header[22] = (byte) channels;
        header[23] = 0;
        //采样率,每个通道的播放速度
        header[24] = (byte) (sampleRate & 0xff);
        header[25] = (byte) ((sampleRate >> 8) & 0xff);
        header[26] = (byte) ((sampleRate >> 16) & 0xff);
        header[27] = (byte) ((sampleRate >> 24) & 0xff);
        //音频数据传送速率,采样率*通道数*采样深度/8
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        // 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
        header[32] = (byte) (channels * 16 / 8);
        header[33] = 0;
        //每个样本的数据位数
        header[34] = 16;
        header[35] = 0;
        //Data chunk
        header[36] = 'd';//data
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        out.write(header, 0, 44);
    }

详细的音频知识笔者一知半解,想要了解的朋友可以查看上面的参考文章,有详细介绍。调用convertPcmToWav方法可以将pcm文件转化成wav文件。下面是笔者在项目中的使用方法:

 if(end.equals("pcm")){
                String pcmPath=f.getPath();
                String wavPath=f.getPath().replace("pcm","wav");
                AudioRecoderUtils audioRecoderUtils=AudioRecoderUtils.getInstance();
                audioRecoderUtils.convertPcmToWav(pcmPath,wavPath,15000,1,8);
                audioRecoderUtils.playerStart(wavPath);
            } 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏DannyHoo的专栏

ios开发中使用Masonry对多个视图进行均匀布局

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

1592
来自专栏小鄧子的技术博客专栏

【译】占位图与渐变效果

我们甚至不用解释或者讨论:一个空的ImageView在视觉体验上真的很差,因为这是毋庸置疑的。如果你使用Picasso,你可能更倾向于通过网络连接来加载图像。依...

1092
来自专栏吉浦迅科技

DAY46:阅读Surface Reference API

reads the CUDA array bound to the one-dimensional surface reference surfRef usin...

1105
来自专栏生信技能树

转录组数据拼接之应用篇

前前后后接触了一些基因组和转录组拼接的工作,而且后期还会持续进行。期间遇到了各种各样莫名其妙的坑,也尝试了一些不同的方法和软件,简单做一个阶段性小结。上周的今天...

5656
来自专栏Python

Python任务调度模块 – APScheduler,Flask-APScheduler实现定时任务

  看代码,定义一个函数,然后定义一个scheduler类型,添加一个job,然后执行,就可以了,代码是不是超级简单,而且非常清晰。看看结果吧。

8890
来自专栏轮子工厂

教你用翻译软件快速阅读大量英文文献

对于一些引用的英文文献,我们需要快速地了解整篇文献讲了什么内容,来判断是否可以作为“国内外研究现状”来进行详细分析。

2004
来自专栏YoungGy

MMD_3b_StreamAlgorithms

Stream概述 DBMS的区别 Stream模型 Query种类 应用 Sliding Windows 简介 例子 Bloom Filter motivati...

1699
来自专栏落影的专栏

OpenGLES进阶教程8-obj文件和mtl文件解析

教程 距离上一篇教程已经有两个月了,这两个月详细阅读GPUImage的源码,并写了详细解析,发现对OpenGLES的深入了解很有帮助。 上周一个简书的朋友问我...

4097
来自专栏逍遥剑客的游戏开发

Untiy Native Render Plugin在VR中的绘制(二): 透明排序

2409
来自专栏生信技能树

rMATS这款差异可变剪切分析软件的使用体验

rMATS最近刚现在出了rMATS 4.0.1版,相比之间的rMATS 3.2.5版,其用C,Python,Cython重写了该软件,运算速度提升了100倍,并...

1.1K4

扫码关注云+社区

领取腾讯云代金券