之前写过了如何将speex与wav格式的音频互相转换,如果没有看过的请看一下连接

http://www.cnblogs.com/dongweiq/p/4515186.html

虽然自己实现了相关的压缩算法,但是发现还是与gauss的压缩比例差了一些,一部分是参数设置的问题,另外一部分是没有使用ogg的问题。

本来想研究一下gauss的ogg算法,然后将他录制的音频转为wav格式,再继续进行后面的频谱绘制之类的。

在后续的研究gauss的解码过程,他是先解了ogg的格式,然后分段,然后去掉speex的头,然后把一段段的speex数据再解码成pcm的原数据,最后使用audiotrack一段段播放出来了。audiotrack播放的时候是阻塞的,所以播了多久就解了多久。

既然他得到了一段段pcm的原数据,那我就可以去将这一段段的原数据拼起来,最后得到解码完成的整个的pcm数据,最后加上wav的头不就可以直接转换成wav格式的音频了么???

前天的时候想到这里,立马就去改了。

SpeexDecoder是gauss的demo里主要的解码类,我们复制一份,改名为SpeexFileDecoder

去掉里面跟播放相关的audiotrack变量,因为我们要得到的是解码数据,跟播放无关。

修改后代码如下

1 packagecom.sixin.speex;2

3 importjava.io.File;4 importjava.io.FileOutputStream;5 importjava.io.IOException;6 importjava.io.RandomAccessFile;7 importjava.util.ArrayList;8 importjava.util.List;9

10 importandroid.media.AudioFormat;11 importandroid.media.AudioManager;12 importandroid.media.AudioTrack;13 importandroid.os.RecoverySystem.ProgressListener;14 importandroid.util.Log;15

16 /**

17 * 采用Jspeex方案,首先解包,从ogg里面接出来,然后使用speex decode将speex转为wav数据并进行播放18 *19 *@authorHonghe20 */

21 public classSpeexFileDecoder {22

23 protectedSpeex speexDecoder;24 private String errmsg = null;25 private List listenerList = new ArrayList();26 privateFile srcPath;27 privateFile dstPath;28

29 public SpeexFileDecoder(File srcPath, File dstPath) throwsException {30 this.srcPath =srcPath;31 this.dstPath =dstPath;32 }33

34 private void initializeAndroidAudio(int sampleRate) throwsException {35 int minBufferSize =AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);36

37 if (minBufferSize < 0) {38 throw new Exception("Failed to get minimum buffer size: " +Integer.toString(minBufferSize));39 }40 }41

42 public voidaddOnMetadataListener(ProgressListener l) {43 listenerList.add(l);44 }45

46 publicString getErrmsg() {47 returnerrmsg;48 }49

50 public void decode() throwsException {51 errmsg = null;52 byte[] header = new byte[2048];53 byte[] payload = new byte[65536];54 final int OGG_HEADERSIZE = 27;55 final int OGG_SEGOFFSET = 26;56 final String OGGID = "OggS";57 int segments = 0;58 int curseg = 0;59 int bodybytes = 0;60 int decsize = 0;61 int packetNo = 0;62 //construct a new decoder

63 speexDecoder = newSpeex();64 speexDecoder.init();65 //open the input stream

66 RandomAccessFile dis = new RandomAccessFile(srcPath, "r");67 FileOutputStream fos = newFileOutputStream(dstPath);68

69 intorigchksum;70 intchksum;71 try{72

73 //read until we get to EOF

74 while (true) {75 if(Thread.interrupted()) {76 dis.close();77 return;78 }79

80 //read the OGG header

81 dis.readFully(header, 0, OGG_HEADERSIZE);82 origchksum = readInt(header, 22);83 readLong(header, 6);84 header[22] = 0;85 header[23] = 0;86 header[24] = 0;87 header[25] = 0;88 chksum = OggCrc.checksum(0, header, 0, OGG_HEADERSIZE);89

90 //make sure its a OGG header

91 if (!OGGID.equals(new String(header, 0, 4))) {92 System.err.println("missing ogg id!");93 errmsg = "missing ogg id!";94 return;95 }96

97 /*how many segments are there?*/

98 segments = header[OGG_SEGOFFSET] & 0xFF;99 dis.readFully(header, OGG_HEADERSIZE, segments);100 chksum =OggCrc.checksum(chksum, header, OGG_HEADERSIZE, segments);101

102 /*decode each segment, writing output to wav*/

103 for (curseg = 0; curseg < segments; curseg++) {104

105 if(Thread.interrupted()) {106 dis.close();107 return;108 }109

110 /*get the number of bytes in the segment*/

111 bodybytes = header[OGG_HEADERSIZE + curseg] & 0xFF;112 if (bodybytes == 255) {113 System.err.println("sorry, don't handle 255 sizes!");114 return;115 }116 dis.readFully(payload, 0, bodybytes);117 chksum = OggCrc.checksum(chksum, payload, 0, bodybytes);118

119 /*decode the segment*/

120 /*if first packet, read the Speex header*/

121 if (packetNo == 0) {122 if (readSpeexHeader(payload, 0, bodybytes, true)) {123

124 packetNo++;125 } else{126 packetNo = 0;127 }128 } else if (packetNo == 1) { //Ogg Comment packet

129 packetNo++;130 } else{131

132 /*get the amount of decoded data*/

133 short[] decoded = new short[160];134 if ((decsize = speexDecoder.decode(payload, decoded, 160)) > 0) {135 //把边解边播改为写文件

136 fos.write(ShortAndByte.shortArray2ByteArray(decoded), 0, decsize * 2);137 }138 packetNo++;139 }140 }141 if (chksum !=origchksum)142 throw new IOException("Ogg CheckSums do not match");143 }144 } catch(Exception e) {145 e.printStackTrace();146 }147 fos.close();148 dis.close();149 }150

151 /**

152 * Reads the header packet.153 *154 *

155 *  0 -  7: speex_string: "Speex   "156 *  8 - 27: speex_version: "speex-1.0"157 * 28 - 31: speex_version_id: 1158 * 32 - 35: header_size: 80159 * 36 - 39: rate160 * 40 - 43: mode: 0=narrowband, 1=wb, 2=uwb161 * 44 - 47: mode_bitstream_version: 4162 * 48 - 51: nb_channels163 * 52 - 55: bitrate: -1164 * 56 - 59: frame_size: 160165 * 60 - 63: vbr166 * 64 - 67: frames_per_packet167 * 68 - 71: extra_headers: 0168 * 72 - 75: reserved1169 * 76 - 79: reserved2170 * 

171 *172 *@parampacket173 *@paramoffset174 *@parambytes175 *@return

176 *@throwsException177 */

178 private boolean readSpeexHeader(final byte[] packet, final int offset, final int bytes, boolean init) throwsException {179 if (bytes != 80) {180 return false;181 }182 if (!"Speex ".equals(new String(packet, offset, 8))) {183 return false;184 }185 //int mode = packet[40 + offset] & 0xFF;

186 int sampleRate = readInt(packet, offset + 36);187 //int channels = readInt(packet, offset + 48);188 //int nframes = readInt(packet, offset + 64);189 //int frameSize = readInt(packet, offset + 56);190 //RongXinLog.SystemOut("mode=" + mode + " sampleRate==" + sampleRate + " channels=" + channels191 //+ "nframes=" + nframes + "framesize=" + frameSize);

192 initializeAndroidAudio(sampleRate);193

194 if(init) {195 //return speexDecoder.init(mode, sampleRate, channels, enhanced);

196 return true;197 } else{198 return true;199 }200 }201

202 protected static int readInt(final byte[] data, final intoffset) {203 /*

204 * no 0xff on the last one to keep the sign205 */

206 return (data[offset] & 0xff) | ((data[offset + 1] & 0xff) << 8) | ((data[offset + 2] & 0xff) << 16) | (data[offset + 3] << 24);207 }208

209 protected static long readLong(final byte[] data, final intoffset) {210 /*

211 * no 0xff on the last one to keep the sign212 */

213 return (data[offset] & 0xff) | ((data[offset + 1] & 0xff) << 8) | ((data[offset + 2] & 0xff) << 16) | ((data[offset + 3] & 0xff) << 24) | ((data[offset + 4] & 0xff) << 32)214 | ((data[offset + 5] & 0xff) << 40) | ((data[offset + 6] & 0xff) << 48) | (data[offset + 7] << 56);215 }216

217 protected static int readShort(final byte[] data, final intoffset) {218 /*

219 * no 0xff on the last one to keep the sign220 */

221 return (data[offset] & 0xff) | (data[offset + 1] << 8);222 }223

224 }

注意上面因为speex解码出来的是160个short类型的数组,而java写文件要求写入的是byte数组,所以我们还是用到了short转byte数组的方法shortArray2ByteArray,我封装了一个类。也贴在下边

1 packagecom.sixin.speex;2

3 public classShortAndByte {4 /**

5 * @功能 短整型与字节的转换6 *@param短整型7 *@return两位的字节数组8 */

9 public static byte[] shortToByte(shortnumber) {10 int temp =number;11 byte[] b = new byte[2];12 for (int i = 0; i < b.length; i++) {13 b[i] = new Integer(temp & 0xff).byteValue();//将最低位保存在最低位

14 temp = temp >> 8; //向右移8位

15 }16 returnb;17 }18

19 /**

20 * @功能 字节的转换与短整型21 *@param两位的字节数组22 *@return短整型23 */

24 public static short byteToShort(byte[] b) {25 short s = 0;26 short s0 = (short) (b[0] & 0xff);//最低位

27 short s1 = (short) (b[1] & 0xff);28 s1 <<= 8;29 s = (short) (s0 |s1);30 returns;31 }32

33 /**

34 * @说明 主要是为解析静态数据包,将一个字节数组转换为short数组35 *@paramb36 */

37 public static short[] byteArray2ShortArray(byte[] b) {38 int len = b.length / 2;39 int index = 0;40 short[] re = new short[len];41 byte[] buf = new byte[2];42 for (int i = 0; i

53 /**

54 * @说明 主要是为解析静态数据包,将一个short数组反转为字节数组55 *@paramb56 */

57 public static byte[] shortArray2ByteArray(short[] b) {58 byte[] rebt = new byte[b.length * 2];59 int index = 0;60 for (int i = 0; i < b.length; i++) {61 short st =b[i];62 byte[] bt =shortToByte(st);63 rebt[index] = bt[0];64 rebt[index + 1] = bt[1];65 index += 2;66 }67 returnrebt;68 }69 }

读出来的原数据我们放入到了dstPath的文件,再来看看是怎么操作的呢?其中我还是修改了gauss的speexplayer方法。

我们复制speexPlayer方法,改名为SpeexFileDecoderHelper,按照如下方法修改

1 /**

2 *3 */

4 packagecom.sixin.speex;5

6 importjava.io.File;7

8 importandroid.os.Handler;9

10 /**

11 *@authorhonghe12 *13 */

14 public classSpeexFileDecoderHelper {15 private String srcName = null;16 private String dstName = null;17 private SpeexFileDecoder speexdec = null;18 private OnSpeexFileCompletionListener speexListener = null;19 private static final int speexdecode_completion = 1001;20 private static final int speexdecode_error = 1002;21

22 public Handler handler = newHandler() {23 public voidhandleMessage(android.os.Message msg) {24 int what =msg.what;25 switch(what) {26 casespeexdecode_completion:27 if (speexListener != null) {28 speexListener.onCompletion(speexdec);29 } else{30 System.out.println("司信---------null===speexListener");31 }32 break;33 casespeexdecode_error:34 if (speexListener != null) {35 File file = new File(SpeexFileDecoderHelper.this.srcName);36 if (null != file &&file.exists()) {37 file.delete();38 }39 speexListener.onError(null);40 }41 break;42 default:43 break;44 }45 };46 };47

48 publicSpeexFileDecoderHelper(String fileName,String dstName, OnSpeexFileCompletionListener splistener) {49 this.speexListener =splistener;50 this.srcName =fileName;51 this.dstName =dstName;52 try{53 speexdec = new SpeexFileDecoder(new File(this.srcName),new File(this.dstName));54 } catch(Exception e) {55 e.printStackTrace();56 File file = new File(SpeexFileDecoderHelper.this.srcName);57 if (null != file &&file.exists()) {58 file.delete();59 }60 }61 }62

63 public voidstartDecode() {64 RecordDecodeThread rpt = newRecordDecodeThread();65 Thread th = newThread(rpt);66 th.start();67 }68

69 public boolean isDecoding = false;70

71 class RecordDecodeThread extendsThread {72

73 public voidrun() {74 try{75 if (speexdec != null) {76 isDecoding = true;77 speexdec.decode();78 if (null !=speexdec.getErrmsg()) {79 throw newException(speexdec.getErrmsg());80 }81 }82 System.out.println("RecordPlayThread 文件转换完成");83 if(isDecoding) {84 handler.sendEmptyMessage(speexdecode_completion);85 }86 isDecoding = false;87 } catch(Exception t) {88 t.printStackTrace();89 System.out.println("RecordPlayThread 文件转换出错");90 handler.sendEmptyMessage(speexdecode_error);91 isDecoding = false;92 }93 }94 }95

96 /**

97 * 结束播放98 */

99 public voidstopDecode() {100 isDecoding = false;101 }102

103 publicString getSpxFileName() {104 return this.srcName;105 };106 }

这个方法是开启了一个现成去解码,然后解码完成后会发送handler,调用回调方法,通知解码失败还是成功。OnSpeexFileCompletionListener这个很简单,我需要贴吗?还是贴上吧,省的被骂娘。

1 packagecom.sixin.speex;2

3 /**

4 * Speex音频解码完成监听5 *@authorhonghe6 *7 */

8 public interfaceOnSpeexFileCompletionListener {9 voidonCompletion(SpeexFileDecoder speexdecoder);10 voidonError(Exception ex);11 }

到此代码都贴出来了。什么?!还不会用?哦,我还没写怎么加wav头呢,那再写个方法吧

1 /**

2 * 语音转换3 *4 *@paramname5 *@paramsrcFileName spx文件名6 *@paramdstFileName 转换后得到文件的文件名7 */

8 public static void decodeSpx(Context context, String srcFileName, finalString dstFileName) {9 final String temppath = AudioFileFunc.getFilePathByName("temp.raw");10 try{11 //如果是speex录音

12 if (srcFileName != null && srcFileName.endsWith(".spx")) {13 if (mSpeexFileDecoderHelper != null &&mSpeexFileDecoderHelper.isDecoding) {14 stopMusic(context);15 } else{16 muteAudioFocus(context, true);17 mSpeexFileDecoderHelper = new SpeexFileDecoderHelper(srcFileName, temppath, newOnSpeexFileCompletionListener() {18

19 @Override20 public voidonError(Exception ex) {21 System.out.println("转换错误");22 }23

24 @Override25 public voidonCompletion(SpeexFileDecoder speexdecoder) {26 System.out.println("转换完成");27 WaveJoin.copyWaveFile(temppath, dstFileName);28 }29 });30 mSpeexFileDecoderHelper.startDecode();31 }32 } else{33 System.out.println("音频文件格式不正确");34 }35

36 } catch(Exception e) {37 e.printStackTrace();38 }39 }

copyWaveFile这个方法在哪里?去看开头的那个链接吧,上面有。不过在加wav头的时候要注意,加的头要和你录制音频的时候设置的参数一致,比如samplerate,声道数,framesize这些,我就设置错了一次,gauss录制音频的时候使用的是单声道,我加入的wav头的channel设置成了2,结果放出来的声音老搞笑了,就跟快放一样。有兴趣你可以试试。

代码已更新

代码链接如下:

https://github.com/dongweiq/study/tree/master/Record

我的github地址:https://github.com/dongweiq/study

欢迎关注,欢迎star o(∩_∩)o 。有什么问题请邮箱联系 dongweiqmail@gmail.com qq714094450

speex java_(原创)speex与wav格式音频文件的互相转换(二)相关推荐

  1. (原创)speex与wav格式音频文件的互相转换(二)

    之前写过了如何将speex与wav格式的音频互相转换,如果没有看过的请看一下连接 http://www.cnblogs.com/dongweiq/p/4515186.html 虽然自己实现了相关的压缩 ...

  2. (原创)speex与wav格式音频文件的互相转换

    我们的司信项目又有了新的需求,就是要做会议室.然而需求却很纠结,要继续按照原来发语音消息那样的形式来实现这个会议的功能,还要实现语音播放的计时,暂停,语音的拼接,还要绘制频谱图等等. 如果是wav,m ...

  3. Android 手机录制wav格式音频文件实现

    上一篇文章已经实现了在Android手机上使用MediaRecorder录音,但是后期在处理这些音频文件的时候发现3gp格式的音频不大方便处理,使用wav格式的音频处理起来更方便一些! 这里需要用到A ...

  4. Qt播放WAV格式音频文件的两种方法

    这两种方法都需要在.pro文件中加入multimedia模块. 方法一.使用QAudioOutput #include <QApplication> #include <QFile& ...

  5. c语言 输出音频 单片机,单片机播放WAV格式音频的理解

    CSDN账号注册了3年,一直没有上来过,更不用说写博客了.我不知道博客的具体用途,我只想把它当做一种心得来发表,可能是一些技术上的理解或者生活上的小故事.好了,下面我将记录我对WAV播放器的理解. 很 ...

  6. 单片机播放WAV格式音频的理解

    CSDN账号注册了3年,一直没有上来过,更不用说写博客了.我不知道博客的具体用途,我只想把它当做一种心得来发表,可能是一些技术上的理解或者生活上的小故事.好了,下面我将记录我对WAV播放器的理解. 很 ...

  7. python按固定采样点个数分割wav格式音频

    最近开始做实验需要绘制音频的语谱图,绘制语谱图的过程中需要FFT过程,FFT需要采样点个数是2的整数倍,所以为了生成语谱图的大小合适,那么总长65536是个比较合适的数,对于采样率32kHz的wav音 ...

  8. 【音视频数据数据处理 10】【PCM篇】将PCM转为WAV格式音频

    [音视频数据数据处理 10][PCM篇]将PCM转为WAV格式音频 一.WAV头信息 1.1 RIFF区块 1.2 FORMAT区块 1.3 DATA区块 二.PCM 转 WAV 代码实现 PCM转为 ...

  9. matlab wav格式音频去除人声

    matlab wav格式音频去除人声(原理自查) 先设立Hbs带阻函数(matlab2018a为例) 选择右上view可以查看函数效果如下 应用函数 代码块 代码块语法遵循标准markdown代码,例 ...

  10. wav格式音频怎么转换mp3

    对于一些音乐的爱好者们来说音频格式运用的很广发,大家是否发现有这样一个问题,每次自己在电脑上下载的歌曲最后在手机或者其他设备上准备播放的时候都会显示无法播放,你们有找过这是什么原因造成的吗?小编今天告 ...

最新文章

  1. Hadoop-调优剖析
  2. python工程师-Python工程师学习之旅
  3. 【ASP.NET】 【防止连续多次点击提交按钮 导致页面重复提交】
  4. python与Java线程实现方式的对比
  5. Java黑皮书课后题第8章:8.18(打乱行)编写一个方法,使用下面的方法头打乱一个二维int型数组的行。编写一个测试程序,打乱下面的矩阵
  6. 【STM32】SPI简介
  7. eclipse创建maven多模块项目(单个类似)
  8. php数组验证用户名密码,求个php数组验证问题,在线等
  9. Github Page 绑定域名
  10. Linux调用可执行程序
  11. 前端用户忘记密码,手机验证码修改密码功能
  12. 属性变量,实例变量,全局变量
  13. 学计算机信息管理专业的感谢,2014年计算机信息管理专业自荐信
  14. WordPress 全方位优化指南(上)
  15. 海信LINUX系统改安卓,如何获取海信电视的root许可一键单击海信Smart TV的root卸载内置软件...
  16. 【Ansys】什么软件模块是DS,它和workbench、mechanical的区别在哪里?
  17. 把手机摄像头或智能电视摄像头数据推送到另一台手机或智能电视上的方法
  18. anyconnect免密码登录
  19. Google Reader“寿终正寝”,哪些软件可取而代之?
  20. 苹果手机应用分身_G胖串流应用上架App Store,苹果手机能玩电脑游戏了

热门文章

  1. linux打补丁教程,Linux下patch打补丁命令
  2. resin那些事之resin.conf
  3. 车路协同应用场景分析
  4. UCGUI使用的24位颜色RGB数值对照表
  5. snb处理器hd3000显卡专用extra_最强i9-9900K处理器造就最强主机!华硕ROG GL12CX评测...
  6. Nginx安全配置手册
  7. Qt功能优化:Qt 3D画廊
  8. 计算机专业答辩 ppt模板 免费,计算机毕业论文答辩(完整版).ppt
  9. Qt知识回顾(九)——2D绘画
  10. CS 3:威胁情报解决方案峰会——数据是威胁情报的基础