消除Android中录制的音频中的嗡嗡声

时间:2018-08-02 12:14:51

标签: java android audio-recording aac encoder

当我录制手机的麦克风时,我的Mediarecorder给了我PCM File作为输出。然后,我尝试将收到的PCM File和另一个PCM audiomixPCMFileSignals()相加(如下所示)。它把音频放在一起,但是当我收听输出的音频时,我可以听到录制的声音不清楚,并且有很多嗡嗡声(我希望这是正确的词)或静态的。

我像这样通过麦克风录音来制作output File

public class UAudioRecorder{

    private static final int CHANNEL = AudioFormat.CHANNEL_IN_STEREO;
    private static final int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    private static final int SAMPLE_RATE = 22050; //44.1kHz
    private static final int BUFFER_SIZE = 2048;

    public Status status = Status.IDLE;
    private AudioRecordingHandler arh;
    private File outputFile;
    private Context context;

    AudioRecord record;
    AACEncoder aacEncoder;

    enum Status {
        IDLE,
        RECORDING;
    }

    public UAudioRecorder(Context context) {
        this.context = context;
        init();
    }

    private void init() {
        record = new AudioRecord(1, SAMPLE_RATE,
                CHANNEL, AUDIO_ENCODING, BUFFER_SIZE*2 );

        try {
            outputFile = createOutputFile();
        } catch (Exception ex) {
            Log.e(TAG, "Failed to init AudioRecorder!");
            ex.printStackTrace();
        }
    }


    /**
     * Creates new temp folder and temp output file.
     * If temp file exists, overrides it.
     * Returns temp file
     */
    private File createOutputFile() throws IOException {
        File tempDir = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR);
        File file = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR + "/temp.aac");
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }
        if (file.exists()) {
            file.delete();
        }
        file.createNewFile();
        return file;
    }

    /**
     * Starts recording user mic input. Activate thread for recording.
     * Takes in AudioRecordingHandler. Thread cannot start if outputFile
     * is still not set.
     * @param arh
     */
    public void record(AudioRecordingHandler arh) {
        this.arh = arh;
        status = Status.RECORDING;
        start();
    }


    /**
     * Starts script for running. Needs output file to work!
     */
    public void start() {
        if (outputFile == null) { return; }
        System.out.println("Start reading stream...");
        aacEncoder = new AACEncoder(SAMPLE_RATE, outputFile);
        new Thread(new Runnable() {
            @Override
            public void run() {
                record.startRecording();
                byte[] data = new byte[BUFFER_SIZE];
                float[] audioFloatBuffer = new float[BUFFER_SIZE/2];
                Yin y = new Yin(SAMPLE_RATE, BUFFER_SIZE/2);

                while(status == Status.RECORDING) {
                    record.read(data, 0, BUFFER_SIZE);
                    audioFloatBuffer = ConverterUtil.toFloatArray(data, 0, audioFloatBuffer,
                            0, audioFloatBuffer.length);
                    PitchDetectionResult pdr = y.getPitch(audioFloatBuffer);
                    arh.handlePitch(pdr.getPitch());
                    aacEncoder.writeIntoOutputfile(data);

                }
                aacEncoder.stopEncoding();
            }
        }).start();
    }

    /**
     * Stops script
     */
    public void stop() {
        status = Status.IDLE;
        record.stop();
        arh.finishedRecording(outputFile);
    }
}

现在,output file通过将它们转换为byte[]并将它们合并在一起来添加另一种声音。

SimpleAACEncoder.mixPCMFileSignals(micOutputPCM, backgroundAudio, outfile);

还有SimpleAACEncoder,这是我将两个信号加在一起的地方:

public class SimpleAACEncoder {

    public static File mixPCMFileSignals(File pcmFile1, File pcmFile2,
               File output) throws IOException {

        FileInputStream fis1 = new FileInputStream(pcmFile1);
        FileInputStream fis2 = new FileInputStream(pcmFile2);

        int bufferSize = 2048;

        byte[] buf1 = new byte[bufferSize];
        byte[] buf2 = new byte[bufferSize];

        int state = 1;
        int longerFile = (pcmFile1.length() > pcmFile2.length()) ? 0 : 1;

        if (!output.exists()) {
            output.createNewFile();
        }

        AACEncoder encoder = new AACEncoder(44100, output);

        while (state == 1) {
            int bytesread1 = fis1.read(buf1, 0, bufferSize);
            int bytesread2 = fis2.read(buf2, 0, bufferSize);
            byte[] byteStream = mixSignals(buf1, buf2);

            encoder.encodeAudioFrameToAAC(byteStream);

            /*fos.write(byteStream, 0, bufferSize);*/
            if (longerFile == 1) {
                if (bytesread1 == -1) {
                    state = 0;
                }
            } else {
                if (bytesread2 == -1) {
                    state = 0;
                }
            }
        }


        return output;
    }

    /**
     * Mixes bytes stream values into one. Designed to use with PCM 16bit data.
     * @param signalOne - signal 1
     * @param signalTwo - signal 2
     * @return mixed signals. Clips away values that are not fitting (means lossy quality (rarely happens))
     */
    public static byte[] mixSignals(byte[] signalOne, byte[] signalTwo) {

        byte[] final_arrayb = new byte[2048];

        for (int i = 0; i < final_arrayb.length; i++) {
            float samplef1 = signalOne[i] / 128.0f * 3.5f; //2^7
            float samplef2 = signalTwo[i] / 128.0f;
            float mixed    = samplef1 + samplef2;

            // volume change -- currently works on both tracks togethers;
            mixed *= 1;

            // hard clipping.. removes values that does not fit into scale -1...1
            if (mixed > 1.0f)  mixed = 1.0f;
            if (mixed < -1.0f) mixed = -1.0f;

            byte outputSample  = (byte) (mixed * 128.0f);
            final_arrayb[i] = outputSample;
        }
        return final_arrayb;
    }
}

最后是我的AACEncoder

public class AACEncoder {
    final String TAG = "UEncoder Processor";

    final int sampleRate;
    File outputFile;

    FileOutputStream fos;

    final int TIMEOUT_USEC = 10000 ;

    MediaCodec encoder;
    boolean isEncoderRunning = false;
    boolean outputDone = false;

    MediaCodec.BufferInfo info;

    public AACEncoder(final int sampleRate, File outputFile) {
        this.sampleRate = sampleRate;
        this.info = new MediaCodec.BufferInfo();
        this.outputFile = outputFile;

        openFileStream();
        initEncoder();
    }

    /**
     * Initializes encoder for AAC-LC (Low complexity)
     * @throws Exception
     */
    public void initEncoder() {
        try {
            encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
            MediaFormat format = new MediaFormat();
            format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
            format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
            format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
            format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        } catch (IOException ex) {
            Log.e(TAG, "Failed to create encoder");
            ex.printStackTrace();
        }
    }

    int generateIndex = 0;

    public void encodeAudioFrameToAAC(byte[] frameData) {

        if (encoder == null) return;

        if (!isEncoderRunning) {
            encoder.start();
            isEncoderRunning = true;
        }

        ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
        if (fos != null) {
            int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);

            if (inputBufIndex >= 0) {
                long ptsUsec = (System.currentTimeMillis() * 1000) / 10000;
                if (outputDone) {
                    encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                } else {
                    ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
                    inputBuf.clear();
                    inputBuf.put(frameData);
                    encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);
                }
                generateIndex++;
            }
            tryEncodeOutputBuffer();
        }
        checkIfOutputDone();
    }

    /**
     * Gets data from output buffer and encodes it to
     * AAC-LC encoding with ADTS header attached before every frame
     */
    private void tryEncodeOutputBuffer() {
        ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
        //If >= 0 then valid response
        int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
        if (encoderStatus >= 0) {
            ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];

            encodedData.position(info.offset);
            encodedData.limit(info.offset + info.size + 7);

            byte[] data = new byte[info.size + 7];
            addADTStoPacket(data, info.size + 7);
            encodedData.get(data, 7, info.size);

            encodedData.position(info.offset);
            writeIntoOutputfile(data);
            encoder.releaseOutputBuffer(encoderStatus, false);
        }
    }

    private void checkIfOutputDone() {
        if (outputDone) {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ioe) {
                    Log.w(TAG, "failed closing debug file");
                    throw new RuntimeException(ioe);
                }
                fos = null;
            }
        }
    }

    /**
     *  Add ADTS header at the beginning of each and every AAC packet.
     *  This is needed as MediaCodec encoder generates a packet of raw
     *  AAC data.
     *
     *  Note the packetLen must count in the ADTS header itself.
     **/
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC
        //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
        int freqIdx = 4;  //44.1KHz
        int chanCfg = 2;  //CPE

        // fill in ADTS data
        packet[0] = (byte)0xFF;
        packet[1] = (byte)0xF9;
        packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
        packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
        packet[4] = (byte)((packetLen&0x7FF) >> 3);
        packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
        packet[6] = (byte)0xFC;
    }

    private void openFileStream() {
        fos = null;
        try {
            fos = new FileOutputStream(outputFile, false);
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }

    /**
     * Writes data into file
     * @param data
     */
    public void writeIntoOutputfile(byte[] data) {
        try {
            fos.write(data);
        } catch (IOException ioe) {
            Log.w(TAG, "failed writing debug data to file");
            throw new RuntimeException(ioe);
        }
    }

    public void stopEncoding() {
        isEncoderRunning = false;
        encoder.stop();
        closeStream();
    }

    private void closeStream() {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }
}

我的问题是,如何消除音频中的嗡嗡声/静态噪声。

0 个答案:

没有答案