At first I should appologize if code below looks horrible and not formatted correctly. I don't know much about programming, so I took pieces of programs from different sources and just put them together.
In app I'd like to display frequency (using zero crossing method) of sound from phone mic.
When I running app it shows frequency once but doesn't updating it.
1) Can you please help me to find solution for displaying frequency values repeatedly in the same textbox (or in more appropriate widget on the screen)?
*2) Is it possible to make this app without buttons. I mean is that app shows frequency right after it starts?*SOLVED
Thank You!
package alar.alar.com.frequencyFreq;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
public AudioRecord recorder = null;
int numCrossing, p;
short audioData[];
public int frequency;
public boolean recording;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);
recorder.startRecording();
recording = true;
audioData = new short[bufferSize];
recorder.read(audioData, 0, bufferSize);
numCrossing = 0;
for (p = 0; p < bufferSize - 1; p++) {
if ((audioData[p] > 0 && audioData[p + 1] <= 0) ||
(audioData[p] < 0 && audioData[p + 1] >= 0)) {
numCrossing++;
}
}
frequency = (8000 / bufferSize) * (numCrossing / 2);
TextView textView = (TextView) this.findViewById(R.id.textView);
textView.setText(String.valueOf(frequency));
}//onCreate
}//activity
try this
private Timer timer;
private TimerTask timerTask;
public void onPause(){ super.onPause();
timer.cancel();
} public void onResume(){
super.onResume();
try {
timer = new Timer(); timerTask = new TimerTask() {
#Override
public void run() {
//your method }
};
timer.schedule(timerTask, 1000, 1000); } catch (IllegalStateException e){ android.util.Log.i("Damn", "resume error"); } }
Related
What I want is to be able to get the current noise level in decibels (dB) on the click of a Button. I have been playing around with the sensors and can get them to work easily but this.. I'm stumped. I've tried a few codes but none work, or helped me understand this.
How can this be achieved?
EDIT:
I use the following code:
private Thread recordingThread;
private int bufferSize = 800;
private short[][] buffers = new short[256][bufferSize];
private int[] averages = new int[256];
private int lastBuffer = 0;
AudioRecord recorder;
boolean recorderStarted = false;
protected void startListenToMicrophone()
{
if (!recorderStarted)
{
recordingThread = new Thread()
{
#Override
public void run()
{
int minBufferSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(AudioSource.MIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 10);
recorder.setPositionNotificationPeriod(bufferSize);
recorder.setRecordPositionUpdateListener(new OnRecordPositionUpdateListener()
{
#Override
public void onPeriodicNotification(AudioRecord recorder)
{
short[] buffer = buffers[++lastBuffer
% buffers.length];
recorder.read(buffer, 0, bufferSize);
long sum = 0;
for (int i = 0; i < bufferSize; ++i)
{
sum += Math.abs(buffer[i]);
}
averages[lastBuffer % buffers.length] = (int) (sum / bufferSize);
lastBuffer = lastBuffer % buffers.length;
Log.i("dB", ""+averages);
tv4.setText("" + averages[1]);
}
#Override
public void onMarkerReached(AudioRecord recorder)
{
}
});
recorder.startRecording();
short[] buffer = buffers[lastBuffer % buffers.length];
recorder.read(buffer, 0, bufferSize);
while (true)
{
if (isInterrupted())
{
recorder.stop();
recorder.release();
break;
}
}
}
};
recordingThread.start();
recorderStarted = true;
}
}
private void stopListenToMicrophone()
{
if (recorderStarted)
{
if (recordingThread != null && recordingThread.isAlive()
&& !recordingThread.isInterrupted())
{
recordingThread.interrupt();
}
recorderStarted = false;
}
}
}
I have two buttons in my app. First one calls startListenToMicrophone and second calls the stop. I don't understand how this works. I got the code from here.
The textview gets a weird and very big value. What I need is the sound level in decibels.
Just a passing thought and I may be ver very wrong but, amplitude in dB=20xlog(S1/S2).
I couldn't find this calculation anywhere in your code. what you need to do is get S1, which will be the current recorded level and get S2 which needs to be the maximum possible value to record. Then calculate the dB value.
I am creating a tuner for Android (similar to a guitar tuner) and I am wondering how to allow the tuner to run continuously (for a couple minutes or so). I don't want it to be a service that runs in the background, just while the user is in my app.
I have successfully used the AudioRecord class and am obtaining data that seems correct. I am in the process of filtering this data and finding the fundamental frequency of the input signal, but need help figuring out how to allow my tuner to run continuously.
This is what my code looks like so far:
import android.app.Activity;
import android.graphics.Color;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.dustin.tuner2.FFT;
import com.dustin.tuner2.Complex;
public class Tuner2 extends Activity implements OnClickListener {
Button btnTune;
TextView fft;
TextView freq;
TextView results;
MediaRecorder recorder;
AudioRecord tuner;
boolean startTuning = true;
int audioSource = MediaRecorder.AudioSource.MIC;
int sampleRateInHz = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_SYSTEM);
int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSizeInBytes;
int samples;
short[] audioBuffer;
short[] audioData;
double[] temp;
String fileName;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTune = (Button)findViewById(R.id.btnTune);
freq = (TextView)findViewById(R.id.freq);
btnTune.setOnClickListener(this);
bufferSizeInBytes = 4096;
//bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
results = (TextView)findViewById(R.id.results);
fft = (TextView)findViewById(R.id.fft);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v == btnTune)
{
onTune(startTuning);
if (startTuning) {
((Button)v).setText("Stop Tuning");
}
else {
((Button)v).setText("Start Tuninig");
}
startTuning = !startTuning;
}
}
//------------------------------------------------------------>
private void onTune(boolean start) {
if(start) {
startTuning();
} else {
Toast.makeText(getApplicationContext(), "Tuning Stopped", Toast.LENGTH_SHORT).show();
tuner.stop();
}
}
private void startTuning()
{
tuner = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
audioData = new short[bufferSizeInBytes];
trigger();
}
public void trigger(){
acquire();
computeFFT();
display();
}
public void acquire(){
try {
tuner.startRecording();
samples = tuner.read(audioData, 0, bufferSizeInBytes);
}
catch (Throwable t){
}
}
public void computeFFT(){
//Conversion from short to double
double[] micBufferData = new double[bufferSizeInBytes];//size may need to change
final int bytesPerSample = 2; // As it is 16bit PCM
final double amplification = 100.0; // choose a number as you like
for (int index = 0, floatIndex = 0; index < bufferSizeInBytes - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
double sample = 0;
for (int b = 0; b < bytesPerSample; b++) {
int v = audioData[index + b];
if (b < bytesPerSample - 1 || bytesPerSample == 1) {
v &= 0xFF;
}
sample += v << (b * 8);
}
double sample32 = amplification * (sample / 32768.0);
micBufferData[floatIndex] = sample32;
}
//Create Complex array for use in FFT
Complex[] fftTempArray = new Complex[bufferSizeInBytes];
for (int i=0; i<bufferSizeInBytes; i++)
{
fftTempArray[i] = new Complex(micBufferData[i], 0);
}
//Obtain array of FFT data
final Complex[] fftArray = FFT.fft(fftTempArray);
final Complex[] fftInverse = FFT.ifft(fftTempArray);
//Create an array of magnitude of fftArray
double[] magnitude = new double[fftArray.length];
for (int i=0; i<fftArray.length; i++){
magnitude[i]= fftArray[i].abs();
}
fft.setTextColor(Color.GREEN);
fft.setText("fftArray is "+ fftArray[500] +" and fftTempArray is "+fftTempArray[500] + " and fftInverse is "+fftInverse[500]+" and audioData is "+audioData[500]+ " and magnitude is "+ magnitude[1] + ", "+magnitude[500]+", "+magnitude[1000]+" You rock dude!");
for(int i = 2; i < samples; i++){
fft.append(" " + magnitude[i] + " Hz");
}
}
public void display(){
results.setTextColor(Color.BLUE);
results.setText(audioData[1]+"");
for(int i = 2; i < samples; i++){
results.append(" " + audioData[i]);
}
results.invalidate();
//fft.setTextColor(Color.GREEN);
//fft.setText("Buffer size is "+bufferSizeInBytes);
//fft.setText(fftArray[1]+" Hz");
//for(int i = 2; i < samples; i++){
//fft.append(" " + fftArray[i] + " Hz");
//}
//fft.invalidate();
}
Do I need to change something concerning the button and what it does when pressed? Would it just involve the buffer size? How often I compute the FFT?
Unless I am misunderstanding, you could just use a while loop that checks a boolean variable. When the user clicks the stop button set that variable to false.
while (tuning) {
trigger();
}
you should also probably introduce a delay between these calls. It would also be wise to run this code on a thread other than the UI thread. See http://developer.android.com/resources/articles/painless-threading.html
A simple example of what I mean would be to do
new Thread(new Runnable() {
#Override
public void run() {
while (tuning) {
trigger();
try {
Thread.sleep(SLEEP_TIME_MS);
} catch (InterruptedException e) {
// handle exception
}
}
}
}).start();
but then you have to worry about updating the UI as you cannot do that from this Thread. The best option is to use AsyncTask http://developer.android.com/reference/android/os/AsyncTask.html
I have a problem reading audiodata from microphone in android. My app must change lightness of a logo picture depending on volume of a sound, which I read from microphone. I use AudioRecord class to get audiodata. Depending on sample rate and buffer size, every 2nd/4th/8th read operation goes with latency of 100-200 ms. So my app can't react on volume changes properly. I tried to reduce buffer size, and to execute data processing in other thread (using AudioRecord.setPositionNotificationPeriod() and callback) but those not make much sense - latency just becomes a little shorter. Here is my code:
package com.mikesoft.Clown;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord.OnRecordPositionUpdateListener;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import java.lang.Math;
public class TestActivity extends Activity implements OnClickListener, OnRecordPositionUpdateListener {
private static final String LOG_TAG = TestActivity.class.getSimpleName();
//arrays for selecting usable audioformat
private static int[] mSampleRates = new int[] {8000, 11025, 22050, 44100};
private static short[] audioFormat = new short[] {AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT};
private static short[] channelConfig = new short[] {AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.CHANNEL_CONFIGURATION_STEREO};
private short[][] buffers;
private int ix;
private AudioRecord recorder = null;
private int bufferSize = 0;
private AudioThread recordingThread = null;
private boolean isRecording = false;
/** The OpenGL View */
private LogoView glSurface;
//markers
private short[] buffer;
private long read_time;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_test);
findViewById(R.id.btnStart).setOnClickListener(this);
findViewById(R.id.btnStop).setOnClickListener(this);
mVolumeText = (TextView)findViewById(R.id.volume);
setRecordMode();
glSurface = (LogoView)findViewById(R.id.glSurface);
}
private void setRecordMode() {
((Button) findViewById(R.id.btnStop)).setClickable(isRecording);
((Button) findViewById(R.id.btnStart)).setClickable(!isRecording);
}
public void startRecording() {
if (null == recorder) {
recorder = findAudioRecord();
recorder.setRecordPositionUpdateListener(this);
recorder.startRecording();
isRecording = true;
recordingThread = new AudioThread();
}
Log.d(LOG_TAG, "Audio recording started");
}
//thread for executing audio recorder
private class AudioThread extends Thread {
private AudioThread() {
start();
}
#Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
recordAudio();
}
}
private void recordAudio() {
ix = 0;
int read = 0;
recorder.setPositionNotificationPeriod(bufferSize);
while (isRecording) {
//long t1 = System.currentTimeMillis();
buffer = buffers[ix++ % buffers.length];
read = recorder.read(buffer, 0, buffer.length);
//time after reading
//read_time = System.currentTimeMillis();
//Log.d(LOG_TAG, "read bytes: " + read);
//Log.d(LOG_TAG, "read_time=" + (read_time - t1));
//int vol = 0;
//for (int i=0;i<buffer.length;i++)
// vol = Math.max(vol, Math.abs(buffer[i]));
//float norm = (float)vol/(float)Short.MAX_VALUE;
//glSurface.setLightness(norm);
}
}
//initialize AudioRecord with suitable format
public AudioRecord findAudioRecord() {
//for (int rate : mSampleRates) {
int rate = 22050;
for (short format : audioFormat) {
for (short channel : channelConfig) {
try {
Log.d(LOG_TAG, "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: "
+ channelConfig);
bufferSize = AudioRecord.getMinBufferSize(rate, channel, format);
if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
// check if we can instantiate and have a success
AudioRecord recorder = new AudioRecord(AudioSource.MIC, rate, channel, format, bufferSize);
//bufferSize/=8;
if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
buffers = new short[256][bufferSize];
return recorder;
}
}
} catch (Exception e) {
Log.e(LOG_TAG, rate + "Exception, keep trying.",e);
}
}
}
//}
return null;
}
public void stopRecording() {
if (null != recorder) {
isRecording = false;
recorder.stop();
recorder.release();
recorder = null;
recordingThread = null;
}
Log.d(LOG_TAG, "Audio recording stopped");
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStart:
startRecording();
setRecordMode();
break;
case R.id.btnStop:
stopRecording();
setRecordMode();
break;
default:
break;
}
}
#Override
public void onMarkerReached(AudioRecord arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPeriodicNotification(AudioRecord arg0) {
float norm;
short[] buf = buffers[ix % buffers.length];
int vol = 0;
for (int i = 0; i < buf.length; i++)
vol = Math.max(vol, Math.abs(buf[i]));
norm = (float) vol / (float) Short.MAX_VALUE;
glSurface.setLightness(norm);
}
}
I tried devices&emulators with Android 1.6-3.2 but it's the same thing everywhere. Is there a solution for my problem in Java? Or I should use NDK approaches?
Will appreciate any help.
I am trying to record the voice using AudioRecord class and writing
the read bytes to speaker using AudioTrack class. I am able to hear
the voice coming from Speaker but the voice is very low and a lot of
noise is coming along with the recorded voice.
Any solution to resolve this problem to reduce the noise and loud the
actual voice.
I am using below code for this:
package com.my.mic.record;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.util.Log;
public class Record extends Thread
{
int numCrossing,p,numSamples,af;
static final int bufferSize = 200000;
short[] buffer = new short[bufferSize];
short[] readBuffer = new short[bufferSize];
boolean isRecording;
AudioManager am;
public AudioRecord arec;
public AudioTrack atrack;
private int sampleRate = 8000;
public void run() {
isRecording = true;
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int buffersize =
AudioRecord.getMinBufferSize(sampleRate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
arec = new
AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT,
buffersize);
//buffersize = arec.getMinBufferSize(arec.getSampleRate(),
arec.getChannelConfiguration(), arec.getAudioFormat());
atrack = new
AudioTrack(AudioManager.STREAM_MUSIC,arec.getSampleRate(),arec.getChannelConfiguration(),arec.getAudioFormat(),
buffersize,AudioTrack.MODE_STREAM);
am.setRouting(AudioManager.MODE_NORMAL,AudioManager.ROUTE_EARPIECE,
AudioManager.ROUTE_ALL);
//am.setMode(AudioManager.MODE_NORMAL);
Log.d("SPEAKERPHONE", "Is speakerphone on? : " +
am.isSpeakerphoneOn());
atrack.setPlaybackRate(sampleRate);
byte[] buffer = new byte[buffersize];
arec.startRecording();
atrack.play();
//atrack.setStereoVolume(atrack.getMaxVolume(),
atrack.getMaxVolume());
final float frequency = sampleRate;
float increment = (float)((2*Math.PI) * frequency / 44100); //
angular increment for each sample
float angle = 0;
//AndroidAudioDevice device = new AndroidAudioDevice( );
while(isRecording) {
try {
arec.read(buffer, 0, buffersize);
atrack.write(buffer, 0, buffer.length);
} catch (Exception e) {
Log.d("Record", ""+e);
}
}
arec.stop();
atrack.stop();
//device.releaseTrack();
isRecording = false;
}
public boolean isRecording() {
return isRecording;
}
public void setRecording(boolean isRecording) {
this.isRecording = isRecording;
}
public AudioManager getAm() {
return am;
}
public void setAm(AudioManager am) {
this.am = am;
}
public void stopRecording(){
arec.stop();
arec.release();
atrack.stop();
atrack.release();
arec=null;
atrack=null;
setRecording(false);
this.stop();
}
}
Go to this working audio record example, if your testing in emulator then sample rate ll be 8000 default. For device increase the sample rate to 44100 or high and record it, it ll work better.
I made a little signal processing app. It processes audio signal (morse code) on certain frequency with Goerztel algorithm. Application saves temporary file to the filesystem and after recording is finished, starts to detect signals. Now I got the result with bunch of magnitudes.
I don't really know what to read from those magnitudes. How can I decode the morse code from those magnitudes? How can I read them? Tried to find references, but nowhere is explained what is the result and how to read it.
EDIT:
My morse code application is made with Delphi and uses Windows Beep function to send signals with certain frequency. I'm using 1200 Hz for signals. Also pauses between signals and words and morse beeps are like wikipedia described. All is accurate.
Goertzel.java:
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;
}
}
EDIT2:
Notices some bugs in processing samples. Changed code in while loop.
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();
}
Regards,
evilone
The output of your Goertzel filter will increase when a tone within its passband is present, and then decrease when the tone is removed. In order to detect pulses of a tone, e.g. morse code, you need some kind of threshold detector on the output of the filter which will just give a boolean value for "tone present" / "tone not present" on a sample-by-sample basis. Try plotting the output values and it should be obvious once you see it in graphical form.
Plot the signal magnitudes on a graph versus time (some CW decoding apps for the PC do this in real-time). Now figure out what the graph for each Morse code symbol should look like. Then study some pattern matching algorithms. If there is enough noise present, you may want to try some statistical pattern matching methods.
Here's the Wikipedia link for proper Morse Code timing.