1. 程式人生 > >訊飛語音合成Wav,以及MediaPlayer的一些坑,SeekBar相關知識點

訊飛語音合成Wav,以及MediaPlayer的一些坑,SeekBar相關知識點

專案中遇到訊飛語音轉成WAV,我是這樣做的,首先生成.pcm檔案,然後再.pcm與.wav互換,最終由MediaPlayer播放,還有進度條之類的小知識點
首先上佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height
="fill_parent" >
<EditText android:layout_width="match_parent" android:layout_height="100dp" android:id="@+id/edit"/> <LinearLayout android:layout_marginBottom="15dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation
="horizontal">
<Button android:id="@+id/play" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="播放" /> <Button android:id="@+id/pause" android:layout_weight="1" android:layout_width
="0dp" android:layout_height="wrap_content" android:text="暫停" />
</LinearLayout> <SeekBar android:id="@+id/sb" android:layout_width="fill_parent" android:layout_height="wrap_content" android:max="100" /> </LinearLayout>

然後是.pcm與.WAV互換工具類

public class PcmToWavUtil {
    private int mBufferSize;  //快取的音訊大小
    private int mSampleRate = 8000;// 8000|16000
    private int mChannel = AudioFormat.CHANNEL_IN_STEREO;   //立體聲
    private int mEncoding = AudioFormat.ENCODING_PCM_16BIT;

    public PcmToWavUtil() {
        this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, mEncoding);
    }

    /**
     * @param sampleRate sample rate、取樣率
     * @param channel    channel、聲道
     * @param encoding   Audio data format、音訊格式
     */
    public PcmToWavUtil(int sampleRate, int channel, int encoding) {
        this.mSampleRate = sampleRate;
        this.mChannel = channel;
        this.mEncoding = encoding;
        this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, mEncoding);
    }

    /**
     * pcm檔案轉wav檔案
     *
     * @param inFilename  原始檔路徑
     * @param outFilename 目標檔案路徑
     */
    public void pcmToWav(String inFilename, String outFilename) {
        FileInputStream in;
        FileOutputStream out;
        long totalAudioLen;
        long totalDataLen;
        long longSampleRate = mSampleRate;
        int channels = 2;
        long byteRate = 16 * mSampleRate * channels / 8;
        byte[] data = new byte[mBufferSize];
        try {
            in = new FileInputStream(inFilename);
            out = new FileOutputStream(outFilename);
            totalAudioLen = in.getChannel().size();
            totalDataLen = totalAudioLen + 36;

            writeWaveFileHeader(out, totalAudioLen, totalDataLen,
                    longSampleRate, channels, byteRate);
            while (in.read(data) != -1) {
                out.write(data);
            }
            in.close();
            out.close();
        }  catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加入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';  //WAVE
        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'; //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);
    }
}

最後看大布局

public class Voice_Activity extends Activity {
    /** Called when the activity is first created. */
    private Voice_BroadcaseUtil voice_broadcaseUtil;
    private EditText edit;
    private Context context=this;
    private  ListView list;
    private  Button play,pause,resume;
    private MediaPlayer mp;
    private   SeekBar sb;
    private Handler handler=new Handler();
    private  int Duration;
    private String content;
   private PcmToWavUtil pcmToWavUtil = new PcmToWavUtil();
    public Voice_Activity voice;
    private List<Voice_Data> voiceData = new ArrayList<>();
    public static String SDPATH ="/sdcard/" ;//獲取資料夾
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_voice_test);
        voice=new Voice_Activity();
        initView();
        initData();
        voice_broadcaseUtil=new Voice_BroadcaseUtil(context);
           }


    private void initView() {
        edit=(EditText)findViewById(R.id.edit);
        play=(Button)findViewById(R.id.play);
        pause=(Button)findViewById(R.id.pause);
        sb=(SeekBar)findViewById(R.id.sb);
         mp = new MediaPlayer();
        //找到相應View

        //後面的引數必須是URI形式的,所以要把相應路徑轉換成URI
        play.setOnClickListener(playlis);
        pause.setOnClickListener(pauselis);
        sb.setOnSeekBarChangeListener(sbLis);
        adapter=new GridAdapter(context,ar);
        list.setAdapter(adapter);
        testRandom1();
    }






    public static void delFile(String fileName){
        File file = new File(SDPATH + fileName);
        if(file.isFile()){
            file.delete();
        }
        file.exists();
    }

    private OnClickListener playlis=new OnClickListener(){
        @Override
        public void onClick(View v) {
            System.out.println(edit.getText().toString().length());
           delFile("speak_result.pcm");//這兩行一定要否則,一直都會讀上一個檔案的內容,導致重新整理不及時,點的不是讀的
            delFile("speak_result.wav");
            voice_broadcaseUtil.toVoiceStartSave(edit.getText().toString());
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    final String path = "/sdcard/speak_result.pcm" ;
                    final String outpath = path.replace(".pcm", ".wav");
                    pcmToWavUtil.pcmToWav(path, outpath);//轉換
                    Toast.makeText(context, "正在拉取資源。請稍後...", Toast.LENGTH_SHORT).show();
                }
            },edit.getText().toString().length()*100);


            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    try {
                        mp.reset();//這句很關鍵,沒有這句會報狀態異常錯誤,而且很難排查
                        mp.setDataSource(context, Uri.parse("/sdcard/speak_result.wav"));
                        mp.prepare();//這句也很重要,沒有這句無法正常播放
                        System.out.println("執行了啊");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    Duration=mp.getDuration();//時間
                    sb.setMax(Duration);//進度條最大時間
                    handler.post(start);
                    Toast.makeText(context, "請稍後...", Toast.LENGTH_LONG).show();
                }
            },edit.getText().toString().length()*100);
            //呼叫handler播放
            }
    };
    Runnable start=new Runnable(){

        @Override
        public void run() {
            mp.start();
            handler.post(updatesb);
            //用一個handler更新SeekBar
        }
    };
    Runnable updatesb =new Runnable(){
        @Override
        public void run() {

                    sb.setProgress(mp.getCurrentPosition());
                    // sb.setProgress();
                    handler.postDelayed(updatesb, 100);//每100毫秒修改一次進度條

              }
    };
    private OnClickListener pauselis=new OnClickListener(){

        @Override
        public void onClick(View v) {
            if(mp.isPlaying()){
                mp.pause();//暫停播放
                pause.setText("繼續");
            }else{
                mp.start();//繼續播放
                pause.setText("暫停");

            }

        }

    };
    private OnSeekBarChangeListener sbLis=new OnSeekBarChangeListener(){

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,
                                      boolean fromUser) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
            mp.seekTo(sb.getProgress());
            //SeekBar確定位置後,跳到指定位置
        }

    };
    public void refresh() {
        onCreate(null);
    }
    private void showTip(String string){
        Toast.makeText(context, string, Toast.LENGTH_SHORT).show();
    }


}

快進和快退

 case R.id.next_up:
                System.out.println("快退");
                mp.seekTo(mp.getCurrentPosition() - 5000);
                break;
            case R.id.next_down:
                mp.seekTo(mp.getCurrentPosition() + 5000);

上幾張圖
這裡寫圖片描述