android中是否有任何声音过滤库

时间:2011-05-18 08:50:11

标签: android

我想捕获音频并将其转换为字节数组 但是当我录制音频时,会加入噪音。

android中是否有任何过滤库,以便我可以删除其中的噪音。

的问候,
斯纳

1 个答案:

答案 0 :(得分:5)

使用以下课程分析音频,我在这里为您提供三个课程。我已经使用这个类来识别信号电平(音频Db级别)。我想您可以了解如何过滤Audio中的噪音。以下类使用AudioRecoder记录音频并将其转换为字节并测量信号强度。

AudioAnalyser.java

package com.indianic.audioanalyzer;

import android.util.Log;





/**
 * An {@link Instrument} which analyses an audio stream in various ways.
 * 
 * <p>To use this class, your application must have permission RECORD_AUDIO.
 */
public class AudioAnalyser
{

    // ******************************************************************** //
    // Constructor.
    // ******************************************************************** //

    /**
     * Create a WindMeter instance.
     * 
     * @param   parent          Parent surface.
     */
    public AudioAnalyser() {

        audioReader = new AudioReader();


        biasRange = new float[2];
    }


    // ******************************************************************** //
    // Configuration.
    // ******************************************************************** //

    /**
     * Set the sample rate for this instrument.
     * 
     * @param   rate        The desired rate, in samples/sec.
     */
    public void setSampleRate(int rate) {
        sampleRate = rate;
    }


    /**
     * Set the input block size for this instrument.
     * 
     * @param   size        The desired block size, in samples.  Typical
     *                      values would be 256, 512, or 1024.  Larger block
     *                      sizes will mean more work to analyse the spectrum.
     */
    public void setBlockSize(int size) {
        inputBlockSize = size;


        // Allocate the spectrum data.
        spectrumData = new float[inputBlockSize / 2];
        spectrumHist = new float[inputBlockSize / 2][historyLen];
    }




    /**
     * Set the decimation rate for this instrument.
     * 
     * @param   rate        The desired decimation.  Only 1 in rate blocks
     *                      will actually be processed.
     */
    public void setDecimation(int rate) {
        sampleDecimate = rate;
    }


    /**
     * Set the histogram averaging window for this instrument.
     * 
     * @param   len         The averaging interval.  1 means no averaging.
     */
    public void setAverageLen(int len) {
        historyLen = len;

        // Set up the history buffer.
        spectrumHist = new float[inputBlockSize / 2][historyLen];
        spectrumIndex = 0;
    }


    // ******************************************************************** //
    // Run Control.
    // ******************************************************************** //

    /**
     * The application is starting.  Perform any initial set-up prior to
     * starting the application.  We may not have a screen size yet,
     * so this is not a good place to allocate resources which depend on
     * that.
     */
    public void appStart() {
    }


    /**
     * We are starting the main run; start measurements.
     */
    public void measureStart() {
        audioProcessed = audioSequence = 0;
        readError = AudioReader.Listener.ERR_OK;

        audioReader.startReader(sampleRate, inputBlockSize * sampleDecimate, new AudioReader.Listener() {
            @Override
            public final void onReadComplete(short[] buffer) {
                receiveAudio(buffer);
            }
            @Override
            public void onReadError(int error) {
                handleError(error);
            }
        });
    }


    /**
     * We are stopping / pausing the run; stop measurements.
     */
    public void measureStop() {
        audioReader.stopReader();
    }


    /**
     * The application is closing down.  Clean up any resources.
     */
    public void appStop() {
    }








    // ******************************************************************** //
    // Audio Processing.
    // ******************************************************************** //

    /**
     * Handle audio input.  This is called on the thread of the audio
     * reader.
     * 
     * @param   buffer      Audio data that was just read.
     */
    private final void receiveAudio(short[] buffer) {
        // Lock to protect updates to these local variables.  See run().
        synchronized (this) {
            audioData = buffer;
            ++audioSequence;
        }
    }


    /**
     * An error has occurred.  The reader has been terminated.
     * 
     * @param   error       ERR_XXX code describing the error.
     */
    private void handleError(int error) {
        synchronized (this) {
            readError = error;
        }
    }


    // ******************************************************************** //
    // Main Loop.
    // ******************************************************************** //

    /**
     * Update the state of the instrument for the current frame.
     * This method must be invoked from the doUpdate() method of the
     * application's {@link SurfaceRunner}.
     * 
     * <p>Since this is called frequently, we first check whether new
     * audio data has actually arrived.
     * 
     * @param   now         Nominal time of the current frame in ms.
     */
    public final void doUpdate() {
        short[] buffer = null;
        synchronized (this) {
            if (audioData != null && audioSequence > audioProcessed) {
                audioProcessed = audioSequence;
                buffer = audioData;
            }
        }

        // If we got data, process it without the lock.
        if (buffer != null)
            processAudio(buffer);

        if (readError != AudioReader.Listener.ERR_OK)
            processError(readError);
    }


    /**
     * Handle audio input.  This is called on the thread of the
     * parent surface.
     * 
     * @param   buffer      Audio data that was just read.
     */
    private final void processAudio(short[] buffer) {
        // Process the buffer.  While reading it, it needs to be locked.
        synchronized (buffer) {
            // Calculate the power now, while we have the input
            // buffer; this is pretty cheap.
            final int len = buffer.length;


            // If we have a power gauge, calculate the signal power.

                setCurrentPower(SignalPower.calculatePowerDb(buffer, 0, len));

            // If we have a spectrum analyser, set up the FFT input data.
          Log.i(this.getClass().getSimpleName(), "dB = " + getCurrentPower() + "\t\t" + Math.round(getCurrentPower()));

            // Tell the reader we're done with the buffer.
            buffer.notify();
        }




    }


    /**
     * Handle an audio input error.
     * 
     * @param   error       ERR_XXX code describing the error.
     */
    private final void processError(int error) {

    }





    // ******************************************************************** //
    // Class Data.
    // ******************************************************************** //

    public void setCurrentPower(double currentPower) {
        this.currentPower = currentPower;
    }


    public double getCurrentPower() {
        return currentPower;
    }





    // Debugging tag.
    @SuppressWarnings("unused")
    private static final String TAG = "instrument";




    // The desired sampling rate for this analyser, in samples/sec.
    private int sampleRate = 8000;

    // Audio input block size, in samples.
    private int inputBlockSize = 256;


    // The desired decimation rate for this analyser.  Only 1 in
    // sampleDecimate blocks will actually be processed.
    private int sampleDecimate = 1;

    // The desired histogram averaging window.  1 means no averaging.
    private int historyLen = 4;

    // Our audio input device.
    private final AudioReader audioReader;


    // Buffered audio data, and sequence number of the latest block.
    private short[] audioData;
    private long audioSequence = 0;

    // If we got a read error, the error code.
    private int readError = AudioReader.Listener.ERR_OK;

    // Sequence number of the last block we processed.
    private long audioProcessed = 0;

    // Analysed audio spectrum data; history data for each frequency
    // in the spectrum; index into the history data; and buffer for
    // peak frequencies.
    private float[] spectrumData;
    private float[][] spectrumHist;
    private int spectrumIndex;

    // Current signal power level, in dB relative to max. input power.
    private double currentPower = 0f;

    // Temp. buffer for calculated bias and range.
    private float[] biasRange = null;

}

================== AudioReader.java

    package com.indianic.audioanalyzer;


    import android.media.AudioFormat;
    import android.media.AudioRecord;
    import android.media.MediaRecorder;
    import android.util.Log;


    /**
     * A class which reads audio input from the mic in a background thread and
     * passes it to the caller when ready.
     * 
     * <p>To use this class, your application must have permission RECORD_AUDIO.
     */
    public class AudioReader
    {

        // ******************************************************************** //
        // Public Classes.
        // ******************************************************************** //

        /**
         * Listener for audio reads.
         */
        public static abstract class Listener {
            /**
             * Audio read error code: no error.
             */
            public static final int ERR_OK = 0;

            /**
             * Audio read error code: the audio reader failed to initialise.
             */
            public static final int ERR_INIT_FAILED = 1;

            /**
             * Audio read error code: an audio read failed.
             */
            public static final int ERR_READ_FAILED = 2;

            /**
             * An audio read has completed.
             * @param   buffer      Buffer containing the data.
             */
            public abstract void onReadComplete(short[] buffer);

            /**
             * An error has occurred.  The reader has been terminated.
             * @param   error       ERR_XXX code describing the error.
             */
            public abstract void onReadError(int error);
        }


        // ******************************************************************** //
        // Constructor.
        // ******************************************************************** //

        /**
         * Create an AudioReader instance.
         */
        public AudioReader() {
    //        audioManager = (AudioManager) app.getSystemService(Context.AUDIO_SERVICE);
        }


        // ******************************************************************** //
        // Run Control.
        // ******************************************************************** //

        /**
         * Start this reader.
         * 
         * @param   rate        The audio sampling rate, in samples / sec.
         * @param   block       Number of samples of input to read at a time.
         *                      This is different from the system audio
         *                      buffer size.
         * @param   listener    Listener to be notified on each completed read.
         */
        public void startReader(int rate, int block, Listener listener) {
            Log.i(TAG, "Reader: Start Thread");
            synchronized (this) {
                // Calculate the required I/O buffer size.
                int audioBuf = AudioRecord.getMinBufferSize(rate,
                                             AudioFormat.CHANNEL_CONFIGURATION_MONO,
                                             AudioFormat.ENCODING_PCM_16BIT) * 2;

                // Set up the audio input.
                audioInput = new AudioRecord(MediaRecorder.AudioSource.MIC,
                                             rate,
                                             AudioFormat.CHANNEL_CONFIGURATION_MONO,
                                             AudioFormat.ENCODING_PCM_16BIT,
                                             audioBuf);
                inputBlockSize = block;
                sleepTime = (long) (1000f / ((float) rate / (float) block));
                inputBuffer = new short[2][inputBlockSize];
                inputBufferWhich = 0;
                inputBufferIndex = 0;
                inputListener = listener;
                running = true;
                readerThread = new Thread(new Runnable() {
                    public void run() { readerRun(); }
                }, "Audio Reader");
                readerThread.start();
            }
        }


        /**
         * Stop this reader.
         */
        public void stopReader() {
            Log.i(TAG, "Reader: Signal Stop");
            synchronized (this) {
                running = false;
            }
            try {
                if (readerThread != null)
                    readerThread.join();
            } catch (InterruptedException e) {
                ;
            }
            readerThread = null;

            // Kill the audio input.
            synchronized (this) {
                if (audioInput != null) {
                    audioInput.release();
                    audioInput = null;
                }
            }

            Log.i(TAG, "Reader: Thread Stopped");
        }


        // ******************************************************************** //
        // Main Loop.
        // ******************************************************************** //

        /**
         * Main loop of the audio reader.  This runs in our own thread.
         */
        private void readerRun() {
            short[] buffer;
            int index, readSize;

            int timeout = 200;
            try {
                while (timeout > 0 && audioInput.getState() != AudioRecord.STATE_INITIALIZED) {
                    Thread.sleep(50);
                    timeout -= 50;
                }
            } catch (InterruptedException e) { }

            if (audioInput.getState() != AudioRecord.STATE_INITIALIZED) {
                Log.e(TAG, "Audio reader failed to initialize");
                readError(Listener.ERR_INIT_FAILED);
                running = false;
                return;
            }

            try {
                Log.i(TAG, "Reader: Start Recording");
                audioInput.startRecording();
                while (running) {
                    long stime = System.currentTimeMillis();

                    if (!running)
                        break;

                    readSize = inputBlockSize;
                    int space = inputBlockSize - inputBufferIndex;
                    if (readSize > space)
                        readSize = space;
                    buffer = inputBuffer[inputBufferWhich];
                    index = inputBufferIndex;

                    synchronized (buffer) {
                        int nread = audioInput.read(buffer, index, readSize);

                        boolean done = false;
                        if (!running)
                            break;

                        if (nread < 0) {
                            Log.e(TAG, "Audio read failed: error " + nread);
                            readError(Listener.ERR_READ_FAILED);
                            running = false;
                            break;
                        }
                        int end = inputBufferIndex + nread;
                        if (end >= inputBlockSize) {
                            inputBufferWhich = (inputBufferWhich + 1) % 2;
                            inputBufferIndex = 0;
                            done = true;
                        } else
                            inputBufferIndex = end;

                        if (done) {
                            readDone(buffer);

                            // Because our block size is way smaller than the audio
                            // buffer, we get blocks in bursts, which messes up
                            // the audio analyzer.  We don't want to be forced to
                            // wait until the analysis is done, because if
                            // the analysis is slow, lag will build up.  Instead
                            // wait, but with a timeout which lets us keep the
                            // input serviced.
                            long etime = System.currentTimeMillis();
                            long sleep = sleepTime - (etime - stime);
                            if (sleep < 5)
                                sleep = 5;
                            try {
                                buffer.wait(sleep);
                            } catch (InterruptedException e) { }
                        }
                    }
                }
            } finally {
                Log.i(TAG, "Reader: Stop Recording");
                if (audioInput.getState() == AudioRecord.RECORDSTATE_RECORDING)
                    audioInput.stop();
            }
        }


        /**
         * Notify the client that a read has completed.
         * 
         * @param   buffer      Buffer containing the data.
         */
        private void readDone(short[] buffer) {
            inputListener.onReadComplete(buffer);
        }


        /**
         * Notify the client that an error has occurred.  The reader has been
         * terminated.
         * 
         * @param   error       ERR_XXX code describing the error.
         */
        private void readError(int code) {
            inputListener.onReadError(code);
        }


        // ******************************************************************** //
        // Class Data.
        // ******************************************************************** //

        // Debugging tag.
        @SuppressWarnings("unused")
        private static final String TAG = "WindMeter";


        // ******************************************************************** //
        // Private Data.
        // ******************************************************************** //

        // Our audio input device.
        private AudioRecord audioInput;

        // Our audio input buffer, and the index of the next item to go in.
        private short[][] inputBuffer = null;
        private int inputBufferWhich = 0;
        private int inputBufferIndex = 0;

        // Size of the block to read each time.
        private int inputBlockSize = 0;

        // Time in ms to sleep between blocks, to meter the supply rate.
        private long sleepTime = 0;

        // Listener for input.
        private Listener inputListener = null;

        // Flag whether the thread should be running.
        private boolean running = false;

        // The thread, if any, which is currently reading.  Null if not running.
        private Thread readerThread = null;

    }


===================

SignalPower.java

/**
 * dsp: various digital signal processing algorithms
 * <br>Copyright 2009 Ian Cameron Smith
 *
 * <p>This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation (see COPYING).
 * 
 * <p>This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


package com.indianic.audioanalyzer;


/**
 * A power metering algorithm.
 */
public final class SignalPower {

    // ******************************************************************** //
    // Constructor.
    // ******************************************************************** //

    /**
     * Only static methods are provided in this class.
     */
    private SignalPower() {
    }


    // ******************************************************************** //
    // Algorithm.
    // ******************************************************************** //

    /**
     * Calculate the bias and range of the given input signal.
     * 
     * @param   sdata       Buffer containing the input samples to process.
     * @param   off         Offset in sdata of the data of interest.
     * @param   samples     Number of data samples to process.
     * @param   out         A float array in which the results will be placed
     *                      Must have space for two entries, which will be
     *                      set to:
     *                      <ul>
     *                      <li>The bias, i.e. the offset of the average
     *                      signal value from zero.
     *                      <li>The range, i.e. the absolute value of the largest
     *                      departure from the bias level.
     *                      </ul>
     * @throws  NullPointerException    Null output array reference.
     * @throws  ArrayIndexOutOfBoundsException  Output array too small.
     */
    public final static void biasAndRange(short[] sdata, int off, int samples,
                                          float[] out)
    {
        // Find the max and min signal values, and calculate the bias.
        short min =  32767;
        short max = -32768;
        int total = 0;
        for (int i = off; i < off + samples; ++i) {
            final short val = sdata[i];
            total += val;
            if (val < min)
                min = val;
            if (val > max)
                max = val;
        }
        final float bias = (float) total / (float) samples;
        final float bmin = min + bias;
        final float bmax = max - bias;
        final float range = Math.abs(bmax - bmin) / 2f;

        out[0] = bias;
        out[1] = range;
    }


    /**
     * Calculate the power of the given input signal.
     * 
     * @param   sdata       Buffer containing the input samples to process.
     * @param   off         Offset in sdata of the data of interest.
     * @param   samples     Number of data samples to process.
     * @return              The calculated power in dB relative to the maximum
     *                      input level; hence 0dB represents maximum power,
     *                      and minimum power is about -95dB.  Particular
     *                      cases of interest:
     *                      <ul>
     *                      <li>A non-clipping full-range sine wave input is
     *                          about -2.41dB.
     *                      <li>Saturated input (heavily clipped) approaches
     *                          0dB.
     *                      <li>A low-frequency fully saturated input can
     *                          get above 0dB, but this would be pretty
     *                          artificial.
     *                      <li>A really tiny signal, which only occasionally
     *                          deviates from zero, can get below -100dB.
     *                      <li>A completely zero input will produce an
     *                          output of -Infinity.
     *                      </ul>
     *                      <b>You must be prepared to handle this infinite
     *                      result and results greater than zero,</b> although
     *                      clipping them off would be quite acceptable in
     *                      most cases.
     */
    public final static double calculatePowerDb(short[] sdata, int off, int samples) {
        // Calculate the sum of the values, and the sum of the squared values.
        // We need longs to avoid running out of bits.
        double sum = 0;
        double sqsum = 0;
        for (int i = 0; i < samples; i++) {
            final long v = sdata[off + i];
            sum += v;
            sqsum += v * v;
        }

        // sqsum is the sum of all (signal+bias)², so
        //     sqsum = sum(signal²) + samples * bias²
        // hence
        //     sum(signal²) = sqsum - samples * bias²
        // Bias is simply the average value, i.e.
        //     bias = sum / samples
        // Since power = sum(signal²) / samples, we have
        //     power = (sqsum - samples * sum² / samples²) / samples
        // so
        //     power = (sqsum - sum² / samples) / samples
        double power = (sqsum - sum * sum / samples) / samples;

        // Scale to the range 0 - 1.
        power /= MAX_16_BIT * MAX_16_BIT;

        // Convert to dB, with 0 being max power.  Add a fudge factor to make
        // a "real" fully saturated input come to 0 dB.
        return Math.log10(power) * 10f + FUDGE;
    }


    // ******************************************************************** //
    // Constants.
    // ******************************************************************** //

    // Maximum signal amplitude for 16-bit data.
    private static final float MAX_16_BIT = 32768;

    // This fudge factor is added to the output to make a realistically
    // fully-saturated signal come to 0dB.  Without it, the signal would
    // have to be solid samples of -32768 to read zero, which is not
    // realistic.  This really is a fudge, because the best value depends
    // on the input frequency and sampling rate.  We optimise here for
    // a 1kHz signal at 16,000 samples/sec.
    private static final float FUDGE = 0.6f;

}

==============================