项目中遇到讯飞语音转成WAV,我是这样做的,首先生成.pcm文件,然后再.pcm与.wav互换,最终由MediaPlayer播放,还有进度条之类的小知识点
首先上布局
<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 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);