我想捕获音频并将其转换为字节数组 但是当我录制音频时,会加入噪音。
android中是否有任何过滤库,以便我可以删除其中的噪音。
的问候,
斯纳
答案 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;
}
==============================