(オリジナル)speexとwav形式オーディオファイルの相互変換(二)

63663 ワード

前にspeexとwav形式のオーディオをどのように変換するかを書いたことがありますが、見たことがない場合は接続を見てください.
http://www.cnblogs.com/dongweiq/p/4515186.html
自分で関連する圧縮アルゴリズムを実現したが,gaussの圧縮割合と若干差があり,一部はパラメータ設定の問題であり,もう一部はoggを使用していない問題であることが分かった.
もともとgaussのoggアルゴリズムを研究して、彼が録画したオーディオをwavフォーマットに変えて、後のスペクトル描画などを続けたいと思っていました.
後続のgaussの復号過程では,oggのフォーマットを先に解いてセグメント化し,speexのヘッダを除去し,セグメントのspeexデータをpcmの元データに復号し,audiotrackセグメントを用いて再生した.audiotrackは放送中はブロックされていたので、どのくらい放送したら解けたのか.
彼が1段のpcmの元のデータを得た以上、私はこの段の元のデータをつづることができて、最后に復号が完成した全体のpcmのデータを得て、最后にwavの头をプラスして直接wavフォーマットのオーディオに変换することができますか??
おとといここを思い出して、すぐに直しました.
SpeexDecoderはgaussのdemoの主要な復号クラスで、私たちは1部コピーして、SpeexFileDecoderと改名します
再生に関連するaudiotrack変数を削除します.再生に関係なく復号データが得られるからです.
修正後のコードは以下の通りです.
  1 package com.sixin.speex;

  2 

  3 import java.io.File;

  4 import java.io.FileOutputStream;

  5 import java.io.IOException;

  6 import java.io.RandomAccessFile;

  7 import java.util.ArrayList;

  8 import java.util.List;

  9 

 10 import android.media.AudioFormat;

 11 import android.media.AudioManager;

 12 import android.media.AudioTrack;

 13 import android.os.RecoverySystem.ProgressListener;

 14 import android.util.Log;

 15 

 16 /**

 17  *   Jspeex  ,    , ogg     ,    speex decode speex  wav       

 18  * 

 19  * @author Honghe

 20  */

 21 public class SpeexFileDecoder {

 22 

 23     protected Speex speexDecoder;

 24     private String errmsg = null;

 25     private List<ProgressListener> listenerList = new ArrayList<ProgressListener>();

 26     private File srcPath;

 27     private File dstPath;

 28 

 29     public SpeexFileDecoder(File srcPath, File dstPath) throws Exception {

 30         this.srcPath = srcPath;

 31         this.dstPath = dstPath;

 32     }

 33 

 34     private void initializeAndroidAudio(int sampleRate) throws Exception {

 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 void addOnMetadataListener(ProgressListener l) {

 43         listenerList.add(l);

 44     }

 45 

 46     public String getErrmsg() {

 47         return errmsg;

 48     }

 49 

 50     public void decode() throws Exception {

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

 64         speexDecoder.init();

 65         // open the input stream

 66         RandomAccessFile dis = new RandomAccessFile(srcPath, "r");

 67         FileOutputStream fos = new FileOutputStream(dstPath);

 68 

 69         int origchksum;

 70         int chksum;

 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      * <pre>

155      *  0 -  7: speex_string: "Speex   "

156      *  8 - 27: speex_version: "speex-1.0"

157      * 28 - 31: speex_version_id: 1

158      * 32 - 35: header_size: 80

159      * 36 - 39: rate

160      * 40 - 43: mode: 0=narrowband, 1=wb, 2=uwb

161      * 44 - 47: mode_bitstream_version: 4

162      * 48 - 51: nb_channels

163      * 52 - 55: bitrate: -1

164      * 56 - 59: frame_size: 160

165      * 60 - 63: vbr

166      * 64 - 67: frames_per_packet

167      * 68 - 71: extra_headers: 0

168      * 72 - 75: reserved1

169      * 76 - 79: reserved2

170      * </pre>

171      * 

172      * @param packet

173      * @param offset

174      * @param bytes

175      * @return

176      * @throws Exception

177      */

178     private boolean readSpeexHeader(final byte[] packet, final int offset, final int bytes, boolean init) throws Exception {

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=" + channels

191         //                + "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 int offset) {

203         /*

204          * no 0xff on the last one to keep the sign

205          */

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 int offset) {

210         /*

211          * no 0xff on the last one to keep the sign

212          */

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 int offset) {

218         /*

219          * no 0xff on the last one to keep the sign

220          */

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

222     }

223 

224 }

上はspeexが160個のshortタイプの配列を復号したため、javaがファイルを書くにはbyte配列を書き込む必要があるので、shortがbyte配列を回転する方法shortArray 2 ByteArrayを使ってクラスをパッケージしました.下にも貼って
 1 package com.sixin.speex;

 2 

 3 public class ShortAndByte {

 4     /** 

 5     * @             

 6     * @param     

 7     * @return         

 8     */

 9     public static byte[] shortToByte(short number) {

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         return b;

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         return s;

31     }

32 

33     /** 

34      * @              ,          short   

35      * @param b 

36      */

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 < b.length;) {

43             buf[0] = b[i];

44             buf[1] = b[i + 1];

45             short st = byteToShort(buf);

46             re[index] = st;

47             index++;

48             i += 2;

49         }

50         return re;

51     }

52 

53     /** 

54      * @              ,   short          

55      * @param b 

56      */

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         return rebt;

68     }

69 }

読み込んだ元のデータをdstPathのファイルに入れて、どうやって操作したのか見てみましょう.その中でgaussのspeexplayerメソッドを修正しました.
speexPlayerメソッドをコピーし、SpeexFileDecoderHelperと名前を変更し、次のように変更します.
  1 /**

  2  * 

  3  */

  4 package com.sixin.speex;

  5 

  6 import java.io.File;

  7 

  8 import android.os.Handler;

  9 

 10 /**

 11  * @author honghe

 12  * 

 13  */

 14 public class SpeexFileDecoderHelper {

 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 = new Handler() {

 23         public void handleMessage(android.os.Message msg) {

 24             int what = msg.what;

 25             switch (what) {

 26             case speexdecode_completion:

 27                 if (speexListener != null) {

 28                     speexListener.onCompletion(speexdec);

 29                 } else {

 30                     System.out.println("  ---------null===speexListener");

 31                 }

 32                 break;

 33             case speexdecode_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     public SpeexFileDecoderHelper(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 void startDecode() {

 64         RecordDecodeThread rpt = new RecordDecodeThread();

 65         Thread th = new Thread(rpt);

 66         th.start();

 67     }

 68 

 69     public boolean isDecoding = false;

 70 

 71     class RecordDecodeThread extends Thread {

 72 

 73         public void run() {

 74             try {

 75                 if (speexdec != null) {

 76                     isDecoding = true;

 77                     speexdec.decode();

 78                     if (null != speexdec.getErrmsg()) {

 79                         throw new Exception(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 void stopDecode() {

100         isDecoding = false;

101     }

102 

103     public String getSpxFileName() {

104         return this.srcName;

105     };

106 }

この方法は、既存の復号化をオンにし、復号化が完了するとhandlerを送信し、コールバックメソッドを呼び出し、復号化に失敗したか成功したかを通知する.OnSpeexFileCompletionListenerこれは簡単ですが、貼る必要がありますか?やはり貼っておきましょう.
 1 package com.sixin.speex;

 2 

 3 /**

 4  * Speex        

 5  * @author honghe

 6  *

 7  */

 8 public interface OnSpeexFileCompletionListener {

 9     void onCompletion(SpeexFileDecoder speexdecoder);

10     void onError(Exception ex);

11 }

このコードは全部貼ってあります.なに?!まだ使えないの?ああ、私はまだwavの頭を加える方法を書いていません.じゃ、もう一つ方法を書きましょう.
 1 /**

 2      *     

 3      * 

 4      * @param name

 5      * @param srcFileName spx   

 6      * @param dstFileName            

 7      */

 8     public static void decodeSpx(Context context, String srcFileName, final String 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, new OnSpeexFileCompletionListener() {

18 

19                         @Override

20                         public void onError(Exception ex) {

21                             System.out.println("    ");

22                         }

23 

24                         @Override

25                         public void onCompletion(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を歓迎します.何か問題があったらメールで連絡してください[email protected] qq714094450