訊飛語音合成Wav,以及MediaPlayer的一些坑,SeekBar相關知識點
阿新 • • 發佈:2019-01-08
專案中遇到訊飛語音轉成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);
上幾張圖