[Android][Kotlin] 使用 AudioRecord 实现带通滤波器并写入 16BIT PCM 文件
[Android][Kotlin] Implement Bandpass Filter with AudioRecord and write to 16BIT PCM file
总结:
使用 Android 的 AudioRecord,我正在尝试实现带通滤波器并将过滤后的音频写入 PCM 文件。
到目前为止,我的图书馆已经尝试过:
https://github.com/berndporr/iirj/blob/master/src/test/java/uk/me/berndporr/iirj/ButterworthTest.java
https://github.com/psambit9791/jDSP/blob/master/src/main/java/com/github/psambit9791/jdsp/filter/Butterworth.java
结果:
在查看过滤后的 PCM 文件时,两者都会导致过滤后的音频输出失真。
当前使用 jDSP 巴特沃斯带通滤波器的实现。我认为我没有正确地从 ByteArray 转换为 Double,反之亦然。
以下是用于此实施的主要信息。
/**
* This constructor initialises the prerequisites
* required to use Butterworth filter.
* @param s Signal to be filtered
* @param Fs Sampling frequency of input signal
*/
public Butterworth(double[] s, double Fs) {
this.signal = s;
this.samplingFreq = Fs;
}
/**
* This method implements a band pass filter with given parameters, filters the signal and returns it.
* @param order Order of the filter
* @param lowCutoff The lower cutoff frequency for the filter in Hz
* @param highCutoff The upper cutoff frequency for the filter in Hz
* @throws java.lang.IllegalArgumentException The lower cutoff frequency is greater than the higher cutoff frequency
* @return double[] Filtered signal
*/
public double[] bandPassFilter(int order, double lowCutoff, double highCutoff) throws IllegalArgumentException{
if (lowCutoff >= highCutoff) {
throw new IllegalArgumentException("Lower Cutoff Frequency cannot be more than the Higher Cutoff Frequency");
}
double centreFreq = (highCutoff + lowCutoff)/2.0;
double width = Math.abs(highCutoff - lowCutoff);
this.output = new double[this.signal.length];
uk.me.berndporr.iirj.Butterworth bp = new uk.me.berndporr.iirj.Butterworth();
bp.bandPass(order, this.samplingFreq, centreFreq, width);
for (int i=0; i<this.output.length; i++) {
this.output[i] = bp.filter(this.signal[i]);
}
return this.output;
}
private val AudioSource = MediaRecorder.AudioSource.MIC
private val SampleRate = 44100
private val Channel = AudioFormat.CHANNEL_IN_MONO
private val EncodingType = AudioFormat.ENCODING_PCM_16BIT
private var bufferSizeInByte: Int = 0
private fun writeDatatoFile() {
var audioData = ByteArray(bufferSizeInByte)
val file1 = UNFILTEREDPCM
val file2 = FILTEREDPCM
file1.createNewFile()
file2.createNewFile()
val out1 = BufferedOutputStream(FileOutputStream(file1))
val out2 = BufferedOutputStream(FileOutputStream(file2))
var length = 0
while (isRecord && audioRecorder != null) {
length = audioRecorder!!.read(audioData, 0, bufferSizeInByte) // get audio data
val butterworth = Butterworth(doubleArrayOf(ByteBuffer.wrap(audioData).getDouble()),44100.0)
val result = butterworth.bandPassFilter(2, 1525.0, 1625.0)
if (AudioRecord.ERROR_INVALID_OPERATION != length) {
out1.write(audioData, 0, length) // write file
out1.flush()
for (i in 0 until result.size) {
val newBuffer = ByteBuffer.allocate(bufferSizeInByte).putDouble(result[i]).array()
out2.write(newBuffer, 0, length)
}
out2.flush()
}
}
out1.close()
out2.close()
}
我认为这个项目可以帮助你:
https://github.com/soygabimoreno/RT
具体来说,您可以深入研究这个 class:
https://github.com/soygabimoreno/RT/blob/master/app/src/main/java/com/appacoustic/rt/domain/calculator/processing/FilterIIR.kt
fun DoubleArray.filterIIR(
butterworthCoefficients: ButterworthCoefficients
): DoubleArray {
val nOrder = butterworthCoefficients.nOrder
val bufferSize = nOrder * 2 + 1
val buffer = DoubleArray(bufferSize)
val out = DoubleArray(size)
for (n in indices) {
for (i in 0 until bufferSize - 1) {
buffer[i] = buffer[i + 1]
}
buffer[bufferSize - 1] = 0.0
for (i in 0 until bufferSize) {
buffer[i] += this[n] * butterworthCoefficients.numeratorDenominator.b[i]
}
for (i in 0 until bufferSize - 1) {
buffer[i + 1] += -buffer[0] * butterworthCoefficients.numeratorDenominator.a[i + 1]
}
out[n] = buffer[0]
}
return out
}
我真的想通了。使用https://github.com/JorenSix/TarsosDSP,库过滤后无需转换。它还包含在 FFT 库中。
总结: 使用 Android 的 AudioRecord,我正在尝试实现带通滤波器并将过滤后的音频写入 PCM 文件。
到目前为止,我的图书馆已经尝试过: https://github.com/berndporr/iirj/blob/master/src/test/java/uk/me/berndporr/iirj/ButterworthTest.java https://github.com/psambit9791/jDSP/blob/master/src/main/java/com/github/psambit9791/jdsp/filter/Butterworth.java
结果: 在查看过滤后的 PCM 文件时,两者都会导致过滤后的音频输出失真。 当前使用 jDSP 巴特沃斯带通滤波器的实现。我认为我没有正确地从 ByteArray 转换为 Double,反之亦然。 以下是用于此实施的主要信息。
/**
* This constructor initialises the prerequisites
* required to use Butterworth filter.
* @param s Signal to be filtered
* @param Fs Sampling frequency of input signal
*/
public Butterworth(double[] s, double Fs) {
this.signal = s;
this.samplingFreq = Fs;
}
/**
* This method implements a band pass filter with given parameters, filters the signal and returns it.
* @param order Order of the filter
* @param lowCutoff The lower cutoff frequency for the filter in Hz
* @param highCutoff The upper cutoff frequency for the filter in Hz
* @throws java.lang.IllegalArgumentException The lower cutoff frequency is greater than the higher cutoff frequency
* @return double[] Filtered signal
*/
public double[] bandPassFilter(int order, double lowCutoff, double highCutoff) throws IllegalArgumentException{
if (lowCutoff >= highCutoff) {
throw new IllegalArgumentException("Lower Cutoff Frequency cannot be more than the Higher Cutoff Frequency");
}
double centreFreq = (highCutoff + lowCutoff)/2.0;
double width = Math.abs(highCutoff - lowCutoff);
this.output = new double[this.signal.length];
uk.me.berndporr.iirj.Butterworth bp = new uk.me.berndporr.iirj.Butterworth();
bp.bandPass(order, this.samplingFreq, centreFreq, width);
for (int i=0; i<this.output.length; i++) {
this.output[i] = bp.filter(this.signal[i]);
}
return this.output;
}
private val AudioSource = MediaRecorder.AudioSource.MIC
private val SampleRate = 44100
private val Channel = AudioFormat.CHANNEL_IN_MONO
private val EncodingType = AudioFormat.ENCODING_PCM_16BIT
private var bufferSizeInByte: Int = 0
private fun writeDatatoFile() {
var audioData = ByteArray(bufferSizeInByte)
val file1 = UNFILTEREDPCM
val file2 = FILTEREDPCM
file1.createNewFile()
file2.createNewFile()
val out1 = BufferedOutputStream(FileOutputStream(file1))
val out2 = BufferedOutputStream(FileOutputStream(file2))
var length = 0
while (isRecord && audioRecorder != null) {
length = audioRecorder!!.read(audioData, 0, bufferSizeInByte) // get audio data
val butterworth = Butterworth(doubleArrayOf(ByteBuffer.wrap(audioData).getDouble()),44100.0)
val result = butterworth.bandPassFilter(2, 1525.0, 1625.0)
if (AudioRecord.ERROR_INVALID_OPERATION != length) {
out1.write(audioData, 0, length) // write file
out1.flush()
for (i in 0 until result.size) {
val newBuffer = ByteBuffer.allocate(bufferSizeInByte).putDouble(result[i]).array()
out2.write(newBuffer, 0, length)
}
out2.flush()
}
}
out1.close()
out2.close()
}
我认为这个项目可以帮助你: https://github.com/soygabimoreno/RT
具体来说,您可以深入研究这个 class: https://github.com/soygabimoreno/RT/blob/master/app/src/main/java/com/appacoustic/rt/domain/calculator/processing/FilterIIR.kt
fun DoubleArray.filterIIR(
butterworthCoefficients: ButterworthCoefficients
): DoubleArray {
val nOrder = butterworthCoefficients.nOrder
val bufferSize = nOrder * 2 + 1
val buffer = DoubleArray(bufferSize)
val out = DoubleArray(size)
for (n in indices) {
for (i in 0 until bufferSize - 1) {
buffer[i] = buffer[i + 1]
}
buffer[bufferSize - 1] = 0.0
for (i in 0 until bufferSize) {
buffer[i] += this[n] * butterworthCoefficients.numeratorDenominator.b[i]
}
for (i in 0 until bufferSize - 1) {
buffer[i + 1] += -buffer[0] * butterworthCoefficients.numeratorDenominator.a[i + 1]
}
out[n] = buffer[0]
}
return out
}
我真的想通了。使用https://github.com/JorenSix/TarsosDSP,库过滤后无需转换。它还包含在 FFT 库中。