Recording .Wav with Android AudioRecorder

前端 未结 5 581
无人共我
无人共我 2020-12-05 11:58

I have read a lot of pages about Android\'s AudioRecorder. You can see a list of them below the question.

I\'m trying to record audio with AudioRecorder, but it\'s n

相关标签:
5条回答
  • 2020-12-05 12:13

    PCMAudioHelper solved my problem. I'll modify this answer and explain it but firstly i have to do some tests over this class.

    0 讨论(0)
  • 2020-12-05 12:16

    You might find this OMRECORDER helpful for recording .WAV format.

    In case if .aac works with you then check out this WhatsappAudioRecorder:

    On startRecording button click :

    1. Initialise new thread.
    2. Create file with .aac extension.
    3. Create output stream of file.
    4. Set output
    5. SetListener and execute thread.

    OnStopClick :

    1. Interrupt the thread and audio will be saved in file.

    Here is full gist of for reference :

    import android.media.AudioFormat;
    import android.media.AudioRecord;
    import android.media.MediaCodec;
    import android.media.MediaCodecInfo;
    import android.media.MediaFormat;
    import android.media.MediaRecorder;
    import android.os.Build;
    import android.util.Log;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.ByteBuffer;
    
    public class AudioRecordThread implements Runnable {
    
        private static final String TAG = AudioRecordThread.class.getSimpleName();
    
        private static final int SAMPLE_RATE = 44100;
        private static final int SAMPLE_RATE_INDEX = 4;
        private static final int CHANNELS = 1;
        private static final int BIT_RATE = 32000;
    
        private final int bufferSize;
        private final MediaCodec mediaCodec;
        private final AudioRecord audioRecord;
        private final OutputStream outputStream;
    
        private OnRecorderFailedListener onRecorderFailedListener;
    
    
        AudioRecordThread(OutputStream outputStream, OnRecorderFailedListener onRecorderFailedListener) throws IOException {
    
            this.bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
            this.audioRecord = createAudioRecord(this.bufferSize);
            this.mediaCodec = createMediaCodec(this.bufferSize);
            this.outputStream = outputStream;
            this.onRecorderFailedListener = onRecorderFailedListener;
    
            this.mediaCodec.start();
    
            try {
                audioRecord.startRecording();
            } catch (Exception e) {
                Log.w(TAG, e);
                mediaCodec.release();
                throw new IOException(e);
            }
        }
    
        @Override
        public void run() {
            if (onRecorderFailedListener != null) {
                Log.d(TAG, "onRecorderStarted");
                onRecorderFailedListener.onRecorderStarted();
            }
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers();
    
            try {
                while (!Thread.interrupted()) {
    
                    boolean success = handleCodecInput(audioRecord, mediaCodec, codecInputBuffers, Thread.currentThread().isAlive());
                    if (success)
                        handleCodecOutput(mediaCodec, codecOutputBuffers, bufferInfo, outputStream);
                }
            } catch (IOException e) {
                Log.w(TAG, e);
            } finally {
                mediaCodec.stop();
                audioRecord.stop();
    
                mediaCodec.release();
                audioRecord.release();
    
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        private boolean handleCodecInput(AudioRecord audioRecord,
                                         MediaCodec mediaCodec, ByteBuffer[] codecInputBuffers,
                                         boolean running) throws IOException {
            byte[] audioRecordData = new byte[bufferSize];
            int length = audioRecord.read(audioRecordData, 0, audioRecordData.length);
    
            if (length == AudioRecord.ERROR_BAD_VALUE ||
                    length == AudioRecord.ERROR_INVALID_OPERATION ||
                    length != bufferSize) {
    
                if (length != bufferSize) {
                    if (onRecorderFailedListener != null) {
                        Log.d(TAG, "length != BufferSize calling onRecordFailed");
                        onRecorderFailedListener.onRecorderFailed();
                    }
                    return false;
                }
            }
    
            int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10 * 1000);
    
            if (codecInputBufferIndex >= 0) {
                ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex];
                codecBuffer.clear();
                codecBuffer.put(audioRecordData);
                mediaCodec.queueInputBuffer(codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            }
    
            return true;
        }
    
        private void handleCodecOutput(MediaCodec mediaCodec,
                                       ByteBuffer[] codecOutputBuffers,
                                       MediaCodec.BufferInfo bufferInfo,
                                       OutputStream outputStream)
                throws IOException {
            int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
    
            while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
                if (codecOutputBufferIndex >= 0) {
                    ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex];
    
                    encoderOutputBuffer.position(bufferInfo.offset);
                    encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);
    
                    if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
                        byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset);
    
    
                        outputStream.write(header);
    
                        byte[] data = new byte[encoderOutputBuffer.remaining()];
                        encoderOutputBuffer.get(data);
                        outputStream.write(data);
                    }
    
                    encoderOutputBuffer.clear();
    
                    mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
                } else if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    codecOutputBuffers = mediaCodec.getOutputBuffers();
                }
    
                codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        }
    
    
        private byte[] createAdtsHeader(int length) {
            int frameLength = length + 7;
            byte[] adtsHeader = new byte[7];
    
            adtsHeader[0] = (byte) 0xFF; // Sync Word
            adtsHeader[1] = (byte) 0xF1; // MPEG-4, Layer (0), No CRC
            adtsHeader[2] = (byte) ((MediaCodecInfo.CodecProfileLevel.AACObjectLC - 1) << 6);
            adtsHeader[2] |= (((byte) SAMPLE_RATE_INDEX) << 2);
            adtsHeader[2] |= (((byte) CHANNELS) >> 2);
            adtsHeader[3] = (byte) (((CHANNELS & 3) << 6) | ((frameLength >> 11) & 0x03));
            adtsHeader[4] = (byte) ((frameLength >> 3) & 0xFF);
            adtsHeader[5] = (byte) (((frameLength & 0x07) << 5) | 0x1f);
            adtsHeader[6] = (byte) 0xFC;
    
            return adtsHeader;
        }
    
        private AudioRecord createAudioRecord(int bufferSize) {
            AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10);
    
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                Log.d(TAG, "Unable to initialize AudioRecord");
                throw new RuntimeException("Unable to initialize AudioRecord");
            }
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (android.media.audiofx.NoiseSuppressor.isAvailable()) {
                    android.media.audiofx.NoiseSuppressor noiseSuppressor = android.media.audiofx.NoiseSuppressor
                            .create(audioRecord.getAudioSessionId());
                    if (noiseSuppressor != null) {
                        noiseSuppressor.setEnabled(true);
                    }
                }
            }
    
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (android.media.audiofx.AutomaticGainControl.isAvailable()) {
                    android.media.audiofx.AutomaticGainControl automaticGainControl = android.media.audiofx.AutomaticGainControl
                            .create(audioRecord.getAudioSessionId());
                    if (automaticGainControl != null) {
                        automaticGainControl.setEnabled(true);
                    }
                }
            }
    
    
            return audioRecord;
        }
    
        private MediaCodec createMediaCodec(int bufferSize) throws IOException {
            MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
            MediaFormat mediaFormat = new MediaFormat();
    
            mediaFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
            mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
            mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
            mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
            mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    
            try {
                mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            } catch (Exception e) {
                Log.w(TAG, e);
                mediaCodec.release();
                throw new IOException(e);
            }
    
            return mediaCodec;
        }
    
        interface OnRecorderFailedListener {
            void onRecorderFailed();
    
            void onRecorderStarted();
        }
    }
    
    0 讨论(0)
  • 2020-12-05 12:23

    I wrote a simple (by which you should read, not to professional standards) class to do this yesterday, and it works.

    private class Wave {
        private final int LONGINT = 4;
        private final int SMALLINT = 2;
        private final int INTEGER = 4;
        private final int ID_STRING_SIZE = 4;
        private final int WAV_RIFF_SIZE = LONGINT + ID_STRING_SIZE;
        private final int WAV_FMT_SIZE = (4 * SMALLINT) + (INTEGER * 2) + LONGINT + ID_STRING_SIZE;
        private final int WAV_DATA_SIZE = ID_STRING_SIZE + LONGINT;
        private final int WAV_HDR_SIZE = WAV_RIFF_SIZE + ID_STRING_SIZE + WAV_FMT_SIZE + WAV_DATA_SIZE;
        private final short PCM = 1;
        private final int SAMPLE_SIZE = 2;
        int cursor, nSamples;
        byte[] output;
    
        public Wave(int sampleRate, short nChannels, short[] data, int start, int end) {
            nSamples = end - start + 1;
            cursor = 0;
            output = new byte[nSamples * SMALLINT + WAV_HDR_SIZE];
            buildHeader(sampleRate, nChannels);
            writeData(data, start, end);
        }
    
        // ------------------------------------------------------------
        private void buildHeader(int sampleRate, short nChannels) {
            write("RIFF");
            write(output.length);
            write("WAVE");
            writeFormat(sampleRate, nChannels);
        }
    
        // ------------------------------------------------------------
        public void writeFormat(int sampleRate, short nChannels) {
            write("fmt ");
            write(WAV_FMT_SIZE - WAV_DATA_SIZE);
            write(PCM);
            write(nChannels);
            write(sampleRate);
            write(nChannels * sampleRate * SAMPLE_SIZE);
            write((short) (nChannels * SAMPLE_SIZE));
            write((short) 16);
        }
    
        // ------------------------------------------------------------
        public void writeData(short[] data, int start, int end) {
            write("data");
            write(nSamples * SMALLINT);
            for (int i = start; i <= end; write(data[i++])) ;
        }
    
        // ------------------------------------------------------------
        private void write(byte b) {
            output[cursor++] = b;
        }
    
        // ------------------------------------------------------------
        private void write(String id) {
            if (id.length() != ID_STRING_SIZE)
                Utils.logError("String " + id + " must have four characters.");
            else {
                for (int i = 0; i < ID_STRING_SIZE; ++i) write((byte) id.charAt(i));
            }
        }
    
        // ------------------------------------------------------------
        private void write(int i) {
            write((byte) (i & 0xFF));
            i >>= 8;
            write((byte) (i & 0xFF));
            i >>= 8;
            write((byte) (i & 0xFF));
            i >>= 8;
            write((byte) (i & 0xFF));
        }
    
        // ------------------------------------------------------------
        private void write(short i) {
            write((byte) (i & 0xFF));
            i >>= 8;
            write((byte) (i & 0xFF));
        }
    
        // ------------------------------------------------------------
        public boolean wroteToFile(String filename) {
            boolean ok = false;
    
            try {
                File path = new File(getFilesDir(), filename);
                FileOutputStream outFile = new FileOutputStream(path);
                outFile.write(output);
                outFile.close();
                ok = true;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ok = false;
            } catch (IOException e) {
                ok = false;
                e.printStackTrace();
            }
            return ok;
        }
    }
    

    Hope this helps

    0 讨论(0)
  • 2020-12-05 12:25

    I would add this as a comment but I don't yet have enough Stackoverflow rep points...

    Opiatefuchs's link takes you to sample code that shows you the exact header formatting necessary to create a .wav file. I've been all over that code myself. Very helpful.

    0 讨论(0)
  • 2020-12-05 12:30

    First you need know that wav file has its format -- header. so you can't just write the pure data to the .wav file.

    Second the wav file header include the length of file . so you need write the header after recording.

    My solution is , user AudioRecorder record the pcm file .

        byte[] audiodata = new byte[bufferSizeInBytes];
    
        FileOutputStream fos = null;
        int readsize = 0;
        try {
            fos = new FileOutputStream(pcmFileName, true);
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    
        status = Status.STATUS_START;
        while (status == Status.STATUS_START && audioRecord != null) {
            readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
            if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {
    
                if (readsize > 0 && readsize <= audiodata.length)
                        fos.write(audiodata, 0, readsize);
                } catch (IOException e) {
                    Log.e("AudioRecorder", e.getMessage());
                }
            }
        }
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    

    then convert it to wav file.

        byte buffer[] = null;
        int TOTAL_SIZE = 0;
        File file = new File(pcmPath);
        if (!file.exists()) {
            return false;
        }
        TOTAL_SIZE = (int) file.length();
    
        WaveHeader header = new WaveHeader();
    
        header.fileLength = TOTAL_SIZE + (44 - 8);
        header.FmtHdrLeth = 16;
        header.BitsPerSample = 16;
        header.Channels = 1;
        header.FormatTag = 0x0001;
        header.SamplesPerSec = 8000;
        header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
        header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
        header.DataHdrLeth = TOTAL_SIZE;
    
        byte[] h = null;
        try {
            h = header.getHeader();
        } catch (IOException e1) {
            Log.e("PcmToWav", e1.getMessage());
            return false;
        }
    
        if (h.length != 44) 
            return false;
    
    
        File destfile = new File(destinationPath);
        if (destfile.exists())
            destfile.delete();
    
    
        try {
            buffer = new byte[1024 * 4]; // Length of All Files, Total Size
            InputStream inStream = null;
            OutputStream ouStream = null;
    
            ouStream = new BufferedOutputStream(new FileOutputStream(
                    destinationPath));
            ouStream.write(h, 0, h.length);
            inStream = new BufferedInputStream(new FileInputStream(file));
            int size = inStream.read(buffer);
            while (size != -1) {
                ouStream.write(buffer);
                size = inStream.read(buffer);
            }
            inStream.close();
            ouStream.close();
        } catch (FileNotFoundException e) {
            Log.e("PcmToWav", e.getMessage());
            return false;
        } catch (IOException ioe) {
            Log.e("PcmToWav", ioe.getMessage());
            return false;
        }
        if (deletePcmFile) {
            file.delete();
        }
        Log.i("PcmToWav", "makePCMFileToWAVFile  success!" + new SimpleDateFormat("yyyy-MM-dd hh:mm").format(new Date()));
        return true;
    
    0 讨论(0)
提交回复
热议问题