我正在尝试从我的 android 设备上的外部存储播放我使用 AudioRecord 类录制的 .wav 文件。我有两个按钮播放/暂停和停止。我阅读了其他 SO 帖子,但我无法解决我的问题。
我的代码如下:
final MediaPlayer m = new MediaPlayer();
final Button buttonStop = (Button) mView.findViewById(R.id.btnStop);
buttonStop.setEnabled(false);
final Button buttonPlay = (Button) mView.findViewById(R.id.btnPlay);
buttonPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String fileName = getFileSelected();
Log.i("Info: ", "Play pressed");
if ( (!isPlaying && !isPaused) && (getFileSelected().endsWith(".3gp") || getFileSelected().endsWith(".wav"))) {
isPlaying = true;
isPaused = false;
try {
m.setDataSource(getActivity().getApplicationContext().getExternalFilesDir(null).toString() + File.separator + fileName);
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.i("Info: ", "DataSource set");
try {
m.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.i("Info: ", "Media Player prepare");
// m.setOnPreparedListener(new OnPreparedListener() {
// @Override
// public void onPrepared(MediaPlayer mediaPlayer) {
// m.seekTo(0);
// m.start();
// }
// });
buttonPlay.setText("Pause");
Log.i("Info: ", "Button Set to Pause");
m.seekTo(0);
Log.i("Info: ", "SeektoZero");
m.start();
Log.i("Info: ", "MesiaPlayer Started");
buttonStop.setEnabled(true);
Log.i("Info: ", "Stop Button enabled");
Toast.makeText(getActivity().getApplicationContext(), "Playing audio", Toast.LENGTH_LONG).show();
m.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
isPlaying = false;
buttonPlay.setEnabled(true);
buttonPlay.setText("Play Selected Recording");
buttonStop.setEnabled(false);
}
});
}
//paused
else if (isPaused){
m.seekTo(songPos);
m.start();
buttonStop.setEnabled(true);
buttonPlay.setText("Pause");
isPaused = false;
isPlaying = true;
}
//isPlaying and not paused
else{
songPos = m.getCurrentPosition();
m.pause();
isPaused = true;
isPlaying = false;
buttonPlay.setText("Play");
buttonStop.setEnabled(false);
}
}
});
buttonStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isPlaying && !isPaused){
isPlaying = false;
isPaused = false;
m.pause();
buttonPlay.setEnabled(true);
buttonPlay.setText("Play Selected Recording");
buttonStop.setEnabled(false);
}
}
});
我的记录按钮代码:
btnStart.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
if (!isRecordingCheckButton) {
startRecording();
isRecordingCheckButton = true;
setButtonLabel(R.id.btnStart, "Stop Recording");
Toast.makeText(getActivity().getApplicationContext(), "Recording started", Toast.LENGTH_LONG).show();
}
else{
stopRecording();
}
}
private void startRecording(){
int hasRecordAudioPermission = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(), permission.RECORD_AUDIO);
if (hasRecordAudioPermission != PackageManager.PERMISSION_GRANTED) {
if (!shouldShowRequestPermissionRationale(permission.RECORD_AUDIO)) {
showMessageOKCancel("You must give permission to write to storage.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(new String[] {permission.RECORD_AUDIO},
111);
}
});
return;
}
requestPermissions(new String[] {permission.RECORD_AUDIO},
111);
return;
}
recorder = new AudioRecord(AudioSource.VOICE_RECOGNITION,
RECORDER_SAMPLERATE, RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
int i = recorder.getState();
if(i==1)
recorder.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
writeAudioDataToFile();
}
},"AudioRecorder Thread");
recordingThread.start();
}
private void writeAudioDataToFile(){
int hasWriteFilePermission = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteFilePermission != PackageManager.PERMISSION_GRANTED) {
if (!shouldShowRequestPermissionRationale(permission.WRITE_EXTERNAL_STORAGE)) {
showMessageOKCancel("You must give permission to write to storage.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(new String[] {permission.WRITE_EXTERNAL_STORAGE},
111);
}
});
return;
}
requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
111);
return;
}
byte data[] = new byte[bufferSize];
String filename = getTempFilename();
FileOutputStream os = null;
try {
os = new FileOutputStream(filename);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int read = 0;
if(null != os){
while(isRecording){
read = recorder.read(data, 0, bufferSize);
if(AudioRecord.ERROR_INVALID_OPERATION != read){
try {
os.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Wave File
标题:
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';
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] = RECORDER_BPP; // bits per sample
header[35] = 0;
header[36] = 'd';
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);
}
选择刚刚录制的文件之一后按播放时出现以下错误:
07-11 02:59:56.571 20450-20450/com.ibm.watson.developer_cloud.android.examples I/Info:: Media Player prepare
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples I/Info:: Button Set to Pause
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: Attempt to perform seekTo in wrong state: mPlayer=0x7f6521e3c0, mCurrentState=0
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: error (-38, 0)
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples I/Info:: SeektoZero
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: start called in state 0
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: error (-38, 0)
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples I/Info:: MesiaPlayer Started
07-11 02:59:56.581 20450-20450/com.ibm.watson.developer_cloud.android.examples I/Info:: Stop Button enabled
07-11 02:59:56.611 20450-20450/com.ibm.watson.developer_cloud.android.examples I/MediaPlayer: send context aware event
07-11 02:59:56.611 20450-20450/com.ibm.watson.developer_cloud.android.examples I/MediaPlayer: sendBroadcast CONTEXT_AWARE_MUSIC_INFO - type(error) - id (261)
07-11 02:59:56.611 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: Error (-38,0)
07-11 02:59:56.621 20450-20450/com.ibm.watson.developer_cloud.android.examples E/MediaPlayer: Error (-38,0)
07-11 02:59:56.671 20450-20450/com.ibm.watson.developer_cloud.android.examples W/DisplayListCanvas: DisplayListCanvas is started on unbinded RenderNode (without mOwningView)
我知道 (-38,0) 与状态有关,但我无法修复它。相同的代码在 API 19 上运行良好,但在 API 23 上抛出了这个错误。我在一些 SO 帖子上读到错误(1,-2147483648)可能是因为应用程序没有适当的权限来读取文件,但是,我无法解决问题。我不知道我哪里出错了,或者我是否需要为 API 23 添加任何内容。这是否与运行时权限有关,因为 mediaPlayer 从外部存储读取文件?如果是这样,我应该在哪里请求许可。wav 文件在我传输并在我的计算机上播放时被正确记录。
更新:我使用另一个 wav 文件来检查 mediaRecord 类和条件是否正常工作,它们是。我现在意识到问题只是应用程序正在录制并保存为 wav 的文件无法读取(但可以在计算机上正常播放)。我无法弄清楚,因为我在 API 19 上以相同的方式录制,而不检查运行时权限。
更新 2:我不得不调用reset
停止按钮上的方法onClickListener
并onCompletionListener
重置数据源。我还在我的计算机上复制了一个录制的文件,并将其重命名为2016-07-10 22:39:40_Recording.wav
toblabla.wav
并将其放回原处。它玩得很好。但我仍然可以读取具有类似日期和时间名称格式的其他类型的文件。不确定问题是否仅与名称有关。
任何有关此问题的帮助将不胜感激!