Запись и последующее воспроизведение файлов wav: ошибка (1, -2147483648) и (-38, 0) (разрешение среды выполнения API 23?)

Я пытаюсь воспроизвести файл .wav, записанный с помощью класса AudioRecord, из внешнего хранилища на моем устройстве Android. У меня есть две кнопки Play/Pause и Stop. Я прочитал другие сообщения 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 на blabla.wav и вернул обратно. Играл нормально. Но я все еще могу читать другие типы файлов с аналогичным форматом имени даты и времени. Не уверен, что проблема только в названии.

Любая помощь по этому вопросу будет оценена по достоинству!


comment
я не уверен, что это решит вашу проблему, но я мог найти потенциальную ошибку, в buttonStop.setOnClickListener для isPlaying и isPaused установлено значение false. посмотри на это   -  person hello_world    schedule 11.07.2016
comment
Я получаю ошибки, когда я нажимаю Play/Pause. Я использовал isPlaying и isPaused в качестве проверок, поэтому после нажатия кнопки остановки mediaPlayer останавливается (приостанавливается без отслеживания положения), поэтому при следующем нажатии кнопки воспроизведения MediaPlayer начинает воспроизведение с самого начала.   -  person skbrhmn    schedule 11.07.2016
comment
Вы можете проверить свой первый оператор if. ваши условия выглядят неверными, я имею в виду (!isPlaying && !isPaused) выглядят подозрительно! Кроме того, добавьте несколько операторов журнала и проверьте поток приложения.   -  person hello_world    schedule 11.07.2016
comment
Условия, хотя и не самые эффективные, работают, поскольку я тестировал их на другом устройстве с API 19. Я получаю эту ошибку после того, как установил для компиляции и целевого SDK значение 23. Я добавил несколько журналов и обновил ошибки. Я не уверен, в чем проблема. Несмотря на то, что для текста кнопки задано значение «Пауза» и код выполняется, текст кнопки остается прежним. Я не уверен, должен ли я запрашивать разрешения во время выполнения для MediaPlayer.   -  person skbrhmn    schedule 11.07.2016
comment
Привет. Проблема в том, что когда вы запрашиваете разрешение в то время, вы также продолжаете свой код, которому требуется разрешение, поэтому, пока пользователь не предоставит разрешение, вы не должны выполнять какие-либо другие задачи, проверьте мой ответ здесь: stackoverflow.com/questions/37725699/   -  person Vickyexpert    schedule 11.07.2016
comment
Но вы видите, что файлы «записываются» в хранилище, а файлы «читаются» из хранилища (поскольку я поместил созданный извне файл для тестирования, который отлично воспроизводился). Звук также записывается правильно, когда я воспроизводил файлы на своем компьютере. Единственная проблема в том, что я не могу воспроизводить файлы, записанные в приложении. Этого не должно было случиться, если бы была проблема с разрешениями, верно?   -  person skbrhmn    schedule 11.07.2016


Ответы (1)


Я решил проблему. Удаление двоеточий в имени файла решило ошибку (1, -2147483648). MediaPlayer не смог распознать и прочитать файлы. Изменение имен на другой формат решило эту проблему.

Ошибка (-38, 0) была связана с состояниями, и потребовалось некоторое время, чтобы выяснить, в чем проблема. диаграмма состояний очень помогла. Работал следующий код:

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

                }
            }
        });
person skbrhmn    schedule 11.07.2016