代码之家  ›  专栏  ›  技术社区  ›  jalal

32位wav PCM中的Android录制不工作

  •  2
  • jalal  · 技术社区  · 7 年前

    我一直在尝试使用32位深度的录音在android上录制人声,并将其写入wav文件。我知道32位精度的录制仅在API级别23或更高级别上可用(根据文档所述),使用 AudioFormat.ENCODING_PCM_FLOAT . 我在API级别为23的设备上做了一些测试,但由于某种原因,产生的音频已损坏(我只能听到完全的噪音)。下面是我的代码:

    private AudioRecord mRecorder;
    private int mBufferSize;
    
    private Thread mRecordingThread;
    private boolean mIsRecording;
    
    private String tempPath = "/some/path/tempFile.wav";
    private String outputPath = "/some/path/recording.wav";
    
    @TargetApi(23)
    public void startRecording() {
        mBufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT);
        mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_FLOAT, mBufferSize);
    
        if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED
                && mBufferSize != AudioRecord.ERROR_BAD_VALUE) {
            return;
        }
    
        mRecordingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
                try {
                    FileOutputStream outputStream = new FileOutputStream(tempPath);
                    float[] audioData = new float[mBufferSize / 4];
    
                    mRecorder.startRecording();
                    mIsRecording = true;
    
                    while (mIsRecording) {
                        int readSize = mRecorder.read(audioData, 0, audioData.length, AudioRecord.READ_BLOCKING);
                        if (readSize == AudioRecord.ERROR_INVALID_OPERATION || readSize == AudioRecord.ERROR_BAD_VALUE) {
                            break;
                        }
    
                        // convert float to byte
                        byte[] bytes = new byte[audioData.length * 4];
                        ByteBuffer.wrap(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer().put(audioData);
                        try {
                            outputStream.write(bytes);
                        } catch (IOException e) {
                            Log.e("Recorder", e.getMessage(), e);
                        }
                    }
                    outputStream.close();
    
                } catch (IOException e) {
                    Log.e("Recorder", e.getMessage(), e);
                } catch (Exception e) {
                    Log.e("Recorder", e.getMessage(), e);
                }
            }
        });
        mRecordingThread.start();
    }
    
    
    public void stopRecording() {
        if (mIsRecording) {
            mIsRecording = false;
            if (mRecordingThread != null && mRecordingThread.getState() != Thread.State.NEW
                    && mRecordingThread.getState() != Thread.State.TERMINATED) {
                try {
                    mRecordingThread.join();
                } catch (InterruptedException e) {
                    // ...
                }
            }
            if (mRecorder.getState() == AudioRecord.RECORDSTATE_RECORDING) {
                mRecorder.stop();
            }
            mRecordingThread = null;
            generateFinalAudio();
        }
    }
    
    public void generateFinalAudio() {
        CopyAudioTask task = new CopyAudioTask();
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, tempPath, outputPath);
    }
    
    private class CopyAudioTask extends AsyncTask<String, String, Void> {
        @Override
        protected Void doInBackground(String... params) {
            String tmpPath = params[0];
            String outPath = params[1];
    
            try {
                FileInputStream in = new FileInputStream(tmpPath);
                FileOutputStream out = new FileOutputStream(outPath);
                byte[] data = new byte[mBufferSize];
    
                writeWaveFileHeader(in, out);
                while (in.read(data) != -1) {
                    out.write(data);
                }
                in.close();
                out.close();
    
            } catch (Exception e) {
                Log.e("Recorder", e.getMessage(), e);
            }
            return null;
        }
    }
    
    private void writeWaveFileHeader(FileInputStream in, FileOutputStream out) throws IOException {
        int bitWidth = 32;
        long longSampleRate = mRecorder.getSampleRate();
        int channels = mRecorder.getChannelCount();
        long totalAudioLen = in.getChannel().size();
        long totalDataLen = totalAudioLen + 36;
        long byteRate = bitWidth * longSampleRate * channels / 8;
    
        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) (channels * bitWidth / 8); // block align
        header[33] = 0;
        header[34] = (byte) bitWidth; // 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);
    }
    

    有人知道我做错了什么吗?我试过使用16位( ENCODING_PCM_16BIT ,并已使用 short[] 作为数据缓冲区),并且工作正常。

    任何帮助都将不胜感激。谢谢

    2 回复  |  直到 7 年前
        1
  •  4
  •   jalal    7 年前

    好了,我现在明白了。问题出在收割台上。的价值 header[20] 应该是 3 0x0003 对于本节中指出的32位浮点PCM数据 page .

        2
  •  0
  •   Isao Nokarikashi    3 年前

    对于将问题中的代码用作模板的任何其他人(表示Ctrl+C Ctrl+V),不要忘记将==更改为!=在这里的第一行。似乎是复制粘贴错误或其他什么。无论如何,换了这条线后它就会开始工作了。

    if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED
            && mBufferSize != AudioRecord.ERROR_BAD_VALUE) {
        return;
    }