Android 用Goertzel算法处理音频信号的结果
我做了一个小的信号处理应用程序。它使用Goerztel算法处理特定频率上的音频信号(莫尔斯电码)。应用程序将临时文件保存到文件系统,并在录制完成后开始检测信号。现在我得到了一系列量级的结果 我真的不知道从这些震级上读到什么。我怎样才能从这些量级上破译莫尔斯电码?我怎么读呢?试图找到参考资料,但没有人解释结果是什么以及如何阅读 编辑: 我的莫尔斯电码应用程序是用Delphi开发的,它使用Windows的蜂鸣功能发送特定频率的信号。我使用1200赫兹的信号。信号和文字之间的停顿以及莫尔斯的哔哔声也和维基百科描述的一样。一切都是准确的 Goertzel.java:Android 用Goertzel算法处理音频信号的结果,android,algorithm,signal-processing,goertzel-algorithm,Android,Algorithm,Signal Processing,Goertzel Algorithm,我做了一个小的信号处理应用程序。它使用Goerztel算法处理特定频率上的音频信号(莫尔斯电码)。应用程序将临时文件保存到文件系统,并在录制完成后开始检测信号。现在我得到了一系列量级的结果 我真的不知道从这些震级上读到什么。我怎样才能从这些量级上破译莫尔斯电码?我怎么读呢?试图找到参考资料,但没有人解释结果是什么以及如何阅读 编辑: 我的莫尔斯电码应用程序是用Delphi开发的,它使用Windows的蜂鸣功能发送特定频率的信号。我使用1200赫兹的信号。信号和文字之间的停顿以及莫尔斯的哔哔声也和
public class Goertzel {
private float samplingRate;
private float targetFrequency;
private int n;
private double coeff, Q1, Q2;
private double sine, cosine;
public Goertzel(float samplingRate, float targetFrequency, int inN) {
this.samplingRate = samplingRate;
this.targetFrequency = targetFrequency;
n = inN;
sine = Math.sin(2 * Math.PI * (targetFrequency / samplingRate));
cosine = Math.cos(2 * Math.PI * (targetFrequency / samplingRate));
coeff = 2 * cosine;
}
public void resetGoertzel() {
Q1 = 0;
Q2 = 0;
}
public void initGoertzel() {
int k;
float floatN;
double omega;
floatN = (float) n;
k = (int) (0.5 + ((floatN * targetFrequency) / samplingRate));
omega = (2.0 * Math.PI * k) / floatN;
sine = Math.sin(omega);
cosine = Math.cos(omega);
coeff = 2.0 * cosine;
resetGoertzel();
}
public void processSample(double sample) {
double Q0;
Q0 = coeff * Q1 - Q2 + sample;
Q2 = Q1;
Q1 = Q0;
}
public double[] getRealImag(double[] parts) {
parts[0] = (Q1 - Q2 * cosine);
parts[1] = (Q2 * sine);
return parts;
}
public double getMagnitudeSquared() {
return (Q1 * Q1 + Q2 * Q2 - Q1 * Q2 * coeff);
}
}
SoundCompareActivity.java
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class SoundCompareActivity extends Activity {
private static final int RECORDER_SAMPLE_RATE = 8000; // at least 2 times
// higher than sound
// frequency,
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord recorder = null;
private int bufferSize = 0;
private Thread recordingThread = null;
private boolean isRecording = false;
private Button startRecBtn;
private Button stopRecBtn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startRecBtn = (Button) findViewById(R.id.button1);
stopRecBtn = (Button) findViewById(R.id.button2);
startRecBtn.setEnabled(true);
stopRecBtn.setEnabled(false);
bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLE_RATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
startRecBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("SOUNDCOMPARE", "Start Recording");
startRecBtn.setEnabled(false);
stopRecBtn.setEnabled(true);
stopRecBtn.requestFocus();
startRecording();
}
});
stopRecBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("SOUNDCOMPARE", "Stop recording");
startRecBtn.setEnabled(true);
stopRecBtn.setEnabled(false);
startRecBtn.requestFocus();
stopRecording();
}
});
}
private void startRecording() {
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLE_RATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, bufferSize);
recorder.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
writeAudioDataToTempFile();
}
}, "AudioRecorder Thread");
recordingThread.start();
}
private String getTempFilename() {
File file = new File(getFilesDir(), "tempaudio");
if (!file.exists()) {
file.mkdirs();
}
File tempFile = new File(getFilesDir(), "signal.raw");
if (tempFile.exists())
tempFile.delete();
return (file.getAbsolutePath() + "/" + "signal.raw");
}
private void writeAudioDataToTempFile() {
byte data[] = new byte[bufferSize];
String filename = getTempFilename();
FileOutputStream os = null;
try {
os = new FileOutputStream(filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
int read = 0;
if (os != null) {
while (isRecording) {
read = recorder.read(data, 0, bufferSize);
if (read != AudioRecord.ERROR_INVALID_OPERATION) {
try {
os.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void deleteTempFile() {
File file = new File(getTempFilename());
file.delete();
}
private void stopRecording() {
if (recorder != null) {
isRecording = false;
recorder.stop();
recorder.release();
recorder = null;
recordingThread = null;
}
new MorseDecoder().execute(new File(getTempFilename()));
}
}
MorseDecoder.java:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.os.AsyncTask;
import android.util.Log;
public class MorseDecoder extends AsyncTask<File, Void, Void> {
private FileInputStream is = null;
@Override
protected Void doInBackground(File... files) {
int index;
//double magnitudeSquared;
double magnitude;
int bufferSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
Goertzel g = new Goertzel(8000, 1200, bufferSize);
g.initGoertzel();
for (int i = 0; i < files.length; i++) {
byte[] data = new byte[bufferSize];
try {
is = new FileInputStream(files[i]);
while(is.read(data) != -1) {
ShortBuffer sbuf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
float[] audioFloats = new float[audioShorts.length];
for (int j = 0; j < audioShorts.length; j++) {
audioFloats[j] = ((float)audioShorts[j]) / 0x8000;
}
for (index = 0; index < audioFloats.length; index++) {
g.processSample(data[index]);
}
magnitude = Math.sqrt(g.getMagnitudeSquared());
Log.d("SoundCompare", "Relative magnitude = " + magnitude);
g.resetGoertzel();
}
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.FileNotFoundException;
导入java.io.IOException;
导入java.nio.ByteBuffer;
导入java.nio.ByteOrder;
导入java.nio.ShortBuffer;
导入android.media.AudioFormat;
导入android.media.AudioRecord;
导入android.os.AsyncTask;
导入android.util.Log;
公共类MorseDecoder扩展异步任务{
私有FileInputStream为空;
@凌驾
受保护的Void doInBackground(文件…文件){
整数指数;
//双振幅平方;
双幅;
int bufferSize=AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL\u CONFIGURATION\u MONO,AudioFormat.ENCODING\u PCM\u 16位);
Goertzel g=新的Goertzel(8000、1200、缓冲区大小);
g、 initGoertzel();
对于(int i=0;i
EDIT2:
注意到处理示例时出现了一些错误。在while循环中更改了代码
while(is.read(data) != -1) {
ShortBuffer sbuf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
float[] audioFloats = new float[audioShorts.length];
for (int j = 0; j < audioShorts.length; j++) {
audioFloats[j] = ((float)audioShorts[j]) / 0x8000;
}
for (index = 0; index < audioFloats.length; index++) {
g.processSample(audioFloats[index]);
magnitude = Math.sqrt(g.getMagnitudeSquared());
Log.d("SoundCompare", "Relative magnitude = " + magnitude);
}
//magnitude = Math.sqrt(g.getMagnitudeSquared());
//Log.d("SoundCompare", "Relative magnitude = " + magnitude);
g.resetGoertzel();
}
while(正在读取(数据)!=-1){
ShortBuffer sbuf=ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[]audioShorts=新的short[sbuf.capacity()];
sbuf.get(音频短片);
float[]audioFloats=新的float[audioShorts.length];
对于(int j=0;j
问候,,
evilone当通带内有音调出现时,您的输出将增加,然后当音调消失时,输出将减少。为了检测音调脉冲,例如莫尔斯电码,您需要在滤波器的输出上使用某种阈值检测器,该检测器只需在逐个样本的基础上给出“音调存在”/“音调不存在”的布尔值。尝试绘制输出值,当您以图形形式看到它时,它应该是显而易见的。在图形上绘制信号大小与时间的关系(一些电脑的CW解码应用程序会实时这样做)。现在找出每个莫尔斯电码符号的图形应该是什么样子。然后研究了一些模式匹配算法。如果存在足够的噪声,您可能需要尝试一些统计模式匹配方法
以下是。有没有关于如何做到这一点的例子?@evione:你读过维基百科页面吗?在靠近页面底部的参考资料部分,有几个链接指向使用Goertzel进行DTMF解码的文章。DTMF解码与莫尔斯解码非常相似,因为你需要检测音调的开始/停止时间,所以你应该能够使用相同的技术。我认为,没有模式。尝试绘制一些示例音调。也许我用错了Goertzel过滤器。在Java中有一些实现吗?也许你应该开始一个新的问题,发布你现有的Goertzel代码,这样我们都可以看到你做的是否正确?我看不出你的代码有任何明显的错误-为了调试它,我倾向于转储输入信号和Goertzel输出,验证输入信号是否包含1200 Hz正弦波脉冲,然后比较滤波器输出的静音周期与音调周期。