Search code examples
androidandroid-mediaplayerandroid-buttonandroid-audiorecordruntime-permissions

Recording and then playing wav files: error (1, -2147483648) and (-38, 0) (API 23 Runtime Permission?)


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!


Solution

  • 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);
    
                    }
                }
            });