amr转wav java,Android系统wav和amr的互转

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 armsList = new ArrayList();

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();

}

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值