当前位置:首页 > 青鸟知识 > android

Android系统wav和amr的互转

来源:长沙it培训 发布日期:2017-04-17

  1、wav和amr文件都有头文件,AudioRecord录制出来的文件是raw格式的就不能播放,加上wav头文件就变成wav文件就可以播放。

  给raw文件添加wav头文件

/** 
     * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。 
     * 为我为啥插入这44个字节,这个还真没深入研究,不过你随便打开一个wav 
     * 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有 
     * 自己特有的头文件。 
     */ 
    private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate) throws IOException { 
        byte[] header = new byte[44]; 
        header[0] = 'R'; // RIFF/WAVE header   
        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'; 
        header[9] = 'A'; 
        header[10] = 'V'; 
        header[11] = 'E'; 
        header[12] = 'f'; // 'fmt ' chunk   
        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; 
        header[20] = 1; // format = 1   
        header[21] = 0; 
        header[22] = (byte) channels; 
        header[23] = 0; 
        header[24] = (byte) (longSampleRate & 0xff); 
        header[25] = (byte) ((longSampleRate >> 8) & 0xff); 
        header[26] = (byte) ((longSampleRate >> 16) & 0xff); 
        header[27] = (byte) ((longSampleRate >> 24) & 0xff); 
        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) (2 * 16 / 8); // block align   
        header[33] = 0; 
        header[34] = 16; // bits per sample   
        header[35] = 0; 
        header[36] = 'd'; 
        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); 
    } 

  amr头文件

[java] view plain copy
final private static byte[] header = new byte[]{0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A}; 

 

  2、wav或raw转amr

  2.1 通过Android系统自带的AmrInputStream类,因为它被隐藏了,只有通过反射来操作。

public class AmrInputStream extends InputStream 
{     
    static { 
        System.loadLibrary("media"); 
    } 
     
    private final static String TAG = "AmrInputStream"; 
     
    // frame is 20 msec at 8.000 khz 
    private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; 
     
    // pcm input stream 
    private InputStream mInputStream; 
     
    // native handle 
    private int mGae; 
     
    // result amr stream 
    private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2]; 
    private int mBufIn = 0; 
    private int mBufOut = 0; 
     
    // helper for bytewise read() 
    private byte[] mOneByte = new byte[1]; 
     
    /**
     * Create a new AmrInputStream, which converts 16 bit PCM to AMR
     * @param inputStream InputStream containing 16 bit PCM.
     */ 
    public AmrInputStream(InputStream inputStream) { 
        mInputStream = inputStream; 
        mGae = GsmAmrEncoderNew(); 
        GsmAmrEncoderInitialize(mGae); 
    } 
 
    @Override 
    public int read() throws IOException { 
        int rtn = read(mOneByte, 0, 1); 
        return rtn == 1 ? (0xff & mOneByte[0]) : -1; 
    } 
     
    @Override 
    public int read(byte[] b) throws IOException { 
        return read(b, 0, b.length); 
    } 
 
    @Override 
    public int read(byte[] b, int offset, int length) throws IOException { 
        if (mGae == 0) throw new IllegalStateException("not open"); 
         
        // local buffer of amr encoded audio empty 
        if (mBufOut >= mBufIn) { 
            // reset the buffer 
            mBufOut = 0; 
            mBufIn = 0; 
             
            // fetch a 20 msec frame of pcm 
            for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) { 
                int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i); 
                if (n == -1) return -1; 
                i += n; 
            } 
             
            // encode it 
            mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0); 
        } 
         
        // return encoded audio to user 
        if (length > mBufIn - mBufOut) length = mBufIn - mBufOut; 
        System.arraycopy(mBuf, mBufOut, b, offset, length); 
        mBufOut += length; 
         
        return length; 
    } 
 
    @Override 
    public void close() throws IOException { 
        try { 
            if (mInputStream != null) mInputStream.close(); 
        } finally { 
            mInputStream = null; 
            try { 
                if (mGae != 0) GsmAmrEncoderCleanup(mGae); 
            } finally { 
                try { 
                    if (mGae != 0) GsmAmrEncoderDelete(mGae); 
                } finally { 
                    mGae = 0; 
                } 
            } 
        } 
    } 
 
    @Override 
    protected void finalize() throws Throwable { 
        if (mGae != 0) { 
            close(); 
            throw new IllegalStateException("someone forgot to close AmrInputStream"); 
        } 
    } 
     
    // 
    // AudioRecord JNI interface 
    // 
    public static native int GsmAmrEncoderNew(); 
    public static native void GsmAmrEncoderInitialize(int gae); 
    public static native int GsmAmrEncoderEncode(int gae, 
            byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException; 
    public static native void GsmAmrEncoderCleanup(int gae); 
    public static native void GsmAmrEncoderDelete(int gae); 
 

  JNI位于源代码下的frameworks\base\media\jni目录,对应的libmedia_jni.so文件文件在system/lib,打开File Exploer就可以看到。

  下面是通过反射进行文件转换

/**
    * 通过反射调用android系统自身AmrInputStream类进行转换
    * @param inPath 源文件
    * @param outPath 目标文件
    */ 
   public void systemWav2Amr(String inPath,String outPath){ 
    try { 
        FileInputStream fileInputStream = new FileInputStream(inPath); 
        FileOutputStream fileoutputStream = new FileOutputStream(outPath); 
        // 获得Class 
        Class<?> cls = Class.forName("android.media.AmrInputStream"); 
        // 通过Class获得所对应对象的方法 
        Method[] methods = cls.getMethods(); 
        // 输出每个方法名 
        fileoutputStream.write(header); 
        Constructor<?> con = cls.getConstructor(InputStream.class); 
        Object obj = con.newInstance(fileInputStream); 
        for (Method method : methods) { 
            Class<?>[] parameterTypes = method.getParameterTypes(); 
            if ("read".equals(method.getName()) 
                    && parameterTypes.length == 3) { 
                byte[] buf = new byte[1024]; 
                int len = 0; 
                while ((len = (int) method.invoke(obj, buf, 0, 1024)) > 0) { 
                    fileoutputStream.write(buf, 0, len); 
                } 
                break; 
            } 
        } 
        for (Method method : methods) { 
            if ("close".equals(method.getName())) { 
                method.invoke(obj); 
                break; 
            } 
        } 
        fileoutputStream.close(); 
    } catch (Exception e) { 
        e.printStackTrace(); 
    } 
   } 

  2.2 通过开源库opencore进行转换,下面是jni部分

public class AmrEncoder { 
 
    public enum Mode { 
        MR475,/* 4.75 kbps */ 
        MR515,    /* 5.15 kbps */ 
        MR59,     /* 5.90 kbps */ 
        MR67,     /* 6.70 kbps */ 
        MR74,     /* 7.40 kbps */ 
        MR795,    /* 7.95 kbps */ 
        MR102,    /* 10.2 kbps */ 
        MR122,    /* 12.2 kbps */ 
        MRDTX,    /* DTX       */ 
        N_MODES   /* Not Used  */ 
    } 
 
    public static native void init(int dtx); 
 
    public static native int encode(int mode, short[] in, byte[] out); 
 
    public static native void reset(); 
 
    public static native void exit(); 
 
    static { 
        System.loadLibrary("amr-codec"); 
    } 

  转换操作,里面也有解码的部分

private void wav2amr(final String inpath,final String outpath){ 
        //  Random random = new Random(); 
//      File file = new File(root + "/RawAudio.raw"); 
//      file.getAbsolutePath(),root + "/test" + random.nextInt(120) + ".amr" 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                convertAMR(inpath,outpath); 
            } 
        }).start(); 
    } 
    /**
     * 将wav或raw文件转换成amr
     * @param inpath 源文件
     * @param outpath 目标文件
     */ 
    private void convertAMR(String inpath,String outpath){ 
        try { 
            AmrEncoder.init(0); 
            File inFile = new File(inpath); 
            List<short[]> armsList = new ArrayList<short[]>(); 
            FileInputStream inputStream = new FileInputStream(inFile); 
            FileOutputStream outStream = new FileOutputStream(outpath); 
            //写入Amr头文件 
            outStream.write(header); 
 
            int byteSize = 320; 
            byte[] buff = new byte[byteSize]; 
            int rc = 0; 
            while ((rc = inputStream.read(buff, 0, byteSize)) > 0) { 
                short[] shortTemp = new short[160]; 
                //将byte[]转换成short[] 
                ByteBuffer.wrap(buff).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortTemp);   
                //将short[]转换成byte[]  
//              ByteBuffer.wrap(buff).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortTemp);   
                armsList.add(shortTemp); 
            } 
              
            for (int i = 0; i < armsList.size(); i++) { 
                int size = armsList.get(i).length; 
                byte[] encodedData = new byte[size*2]; 
                int len = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), armsList.get(i), encodedData); 
                if (len>0) { 
                    byte[] tempBuf = new byte[len]; 
                    System.arraycopy(encodedData, 0, tempBuf, 0, len); 
                    outStream.write(tempBuf, 0, len); 
                } 
            } 
            AmrEncoder.reset(); 
            AmrEncoder.exit(); 
             
            outStream.close(); 
            inputStream.close(); 
            System.out.println("convert success ... "+outpath); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 

 


拒绝套路 试听有礼

数据已加密保证您的信息安全