I am trying to play a .wav file, that I recorded using AudioRecord Class, from external storage on my android device. I have two buttons Play/Pause and Stop. I read the other SO posts but I could not solve my problem.
My code is as follows:
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);
}
}
});
My code for record button:
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
Header:
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);
}
I am getting the following error when I press play after selecting one of the files I just recorded:
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)
I know that the (-38,0) is related to state but I am having trouble fixing it. The same code was working fine on API 19 but is throwing this error on API 23. I read on a few SO posts that the error (1, -2147483648) may be because the app doesn't have proper permission to read the file, however, I could not solve the problem. I don't know where I am going wrong or if I need to add anything for API 23. Could this be related to runtime permission as mediaPlayer reads the file from external storage? If so, where should I request permission. The wav file was recorded properly as I transferred it and played it on my computer.
UPDATE: I used another wav file to check if the mediaRecord class and the conditions were working properly, they were. I now realize the problem is only the files that the app is recording and saving as wav cannot be read (but can be played fine on computer). I cannot figure it out as I was recording the same way on API 19 without checking for runtime permissions.
UPDATE 2: I had to call the reset
method on the stop button onClickListener
and onCompletionListener
to reset data source.
I also copied one of the recorded files on my computer and renamed it from 2016-07-10 22:39:40_Recording.wav
to blabla.wav
and put it back. It played fine. But I can still read other types of files with the similar date and time name format. Not sure if the problem is only with the name.
Any help regarding this issue will be appreciated!
I solved the problem. Removing the colons in the name of the file solved error (1, -2147483648). MediaPlayer was not able to recognize and read the files. Changing the names to a different format solved this problem.
Error (-38, 0) was related to states and took a while to figure out where the problem was. The State Diagram was a great help. The following code worked:
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();
if(!fileName.endsWith(".3gp") && !fileName.endsWith(".wav")){
}
else if ( (!isPlaying && !isPaused)) {
isPlaying = true;
isPaused = false;
int hasReadFilesPermission = ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE);
if (hasReadFilesPermission != PackageManager.PERMISSION_GRANTED) {
if (!shouldShowRequestPermissionRationale(permission.READ_EXTERNAL_STORAGE)) {
showMessageOKCancel("You must give permission to read from storage.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(new String[] {permission.READ_EXTERNAL_STORAGE},
223);
}
});
return ;
}
requestPermissions(new String[] {permission.READ_EXTERNAL_STORAGE},
223);
return ;
}
fileName = getFileSelected();
displayResult(getActivity().getApplicationContext().getExternalFilesDir(null).toString() + File.separator + fileName);
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();
}
try {
m.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
buttonPlay.setText("Pause");
m.seekTo(0);
m.start();
buttonStop.setEnabled(true);
Toast.makeText(getActivity().getApplicationContext(), "Playing audio", Toast.LENGTH_LONG).show();
m.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
m.reset();
isPlaying = false;
isPaused = 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);
}
}
});