package com.seeth.clapir;
import android.app.Activity;
import android.os.Bundle;
import android.os.Build.VERSION;
import android.view.View;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.content.Context;
import android.util.Log;
import root.gast.audio.record.*;
import root.gast.audio.interp.*;
import android.widget.TextView;
import android.widget.ImageButton;
import android.graphics.Typeface;
import android.graphics.Color;
import android.media.AudioFormat;
import android.view.View.OnTouchListener;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.view.ViewGroup;
import java.math.*;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.AnimationUtils;
import android.view.animation.Animation;
import android.animation.LayoutTransition;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.Keyframe;
import com.jjoe64.graphview.*;
import com.jjoe64.graphview.GraphView.GraphViewData;
import com.jjoe64.graphview.GraphView.*;
import android.os.AsyncTask;
public class MainActivity extends Activity {
private boolean isrecording;
private boolean inversed;
private ClapAnalyzer audioLogger;
private AudioClipRecorder audioRecorder;
private static final String TAG = "MainActivity";
private int numberofclaps = 0;
private Typeface robotoType;
private MySurfaceView mySurfaceView;
private SurfaceHolder mySurfaceHolder;
protected ClapRecord recordTask;
protected ClapAnalyze analyzeTask;
private double baseline = 0;
private boolean listenBase = true;
private boolean surfaceCreated = false;
private boolean micState = false;
private NowLayout layout;
private Claps claps;
private LayoutTransition mTransition;
Animator customAppearingAnim, customDisappearingAnim;
Animator customChangingAppearingAnim, customChangingDisappearingAnim;
private void createCustomAnimations(LayoutTransition transition) {
// Changing while Adding
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop =
PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight =
PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom =
PropertyValuesHolder.ofInt("bottom", 0, 1);
PropertyValuesHolder pvhScaleX =
PropertyValuesHolder.ofFloat("translationX", 1f, 0f, 1f);
PropertyValuesHolder pvhScaleY =
PropertyValuesHolder.ofFloat("translationY", 1f, 0f, 1f);
customChangingAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX, pvhScaleY).
setDuration(transition.getDuration(LayoutTransition.CHANGE_APPEARING));
customChangingAppearingAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setScaleX(1f);
view.setScaleY(1f);
}
});
customChangingAppearingAnim = transition.getAnimator(LayoutTransition.CHANGE_APPEARING);
// Changing while Removing
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation =
PropertyValuesHolder.ofKeyframe("translationY", kf0, kf1, kf2);
customChangingDisappearingAnim = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
customChangingDisappearingAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotation(0f);
}
});
// Adding
PropertyValuesHolder pvhTranslateY =
PropertyValuesHolder.ofFloat("translationY", 300, 0);
PropertyValuesHolder pvhAlpha =
PropertyValuesHolder.ofFloat("alpha", 0, 1);
customAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhTranslateY, pvhAlpha).
setDuration(transition.getDuration(LayoutTransition.APPEARING));
customAppearingAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotationY(0f);
}
});
// Removing
customDisappearingAnim = ObjectAnimator.ofFloat(null, "alpha", 1, 0).
setDuration(transition.getDuration(LayoutTransition.DISAPPEARING)/2);
customDisappearingAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotationX(0f);
}
});
}
private void setupCustomAnimations(LayoutTransition mTransition) {
mTransition.setAnimator(LayoutTransition.APPEARING, customAppearingAnim);
mTransition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
mTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, customChangingAppearingAnim);
mTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, customChangingDisappearingAnim);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
inversed = false;
isrecording = false;
claps = new Claps();
setContentView(R.layout.main);
layout = (NowLayout) findViewById(R.id.mainLayout);
robotoType = Typeface.createFromAsset(getAssets(), "Roboto-Light.ttf");
TextView txt = (TextView) findViewById(R.id.infoHeading);
txt.setTypeface(robotoType);
mySurfaceView = (MySurfaceView) findViewById(R.id.waveform);
mySurfaceHolder = mySurfaceView.getHolder();
if (android.os.Build.VERSION.SDK_INT >= 14) {
Animator customAppearingAnim, customDisappearingAnim;
Animator customChangingAppearingAnim, customChangingDisappearingAnim;
mTransition = new LayoutTransition();
layout.setLayoutTransition(mTransition);
//
Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_up_right);
customAppearingAnim = mTransition.getAnimator(LayoutTransition.APPEARING);
customDisappearingAnim = mTransition.getAnimator(LayoutTransition.DISAPPEARING);
customChangingAppearingAnim = mTransition.getAnimator(LayoutTransition.CHANGE_APPEARING);
customChangingDisappearingAnim = mTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING);
createCustomAnimations(mTransition);
setupCustomAnimations(mTransition);
}
final ImageButton but = (ImageButton) findViewById(R.id.micButton);
but.setBackgroundColor(Color.TRANSPARENT);
but.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
if (listenBase) {
but.setColorFilter(Color.BLUE);
}
else {
but.setColorFilter(Color.RED);
}
return false;
}
else if (me.getAction() == MotionEvent.ACTION_UP) {
if (listenBase) {
but.setColorFilter(Color.BLUE);
}
else {
but.setColorFilter(Color.RED);
}
return false;
}
return false;
}
});
final ImageButton canc = (ImageButton) findViewById(R.id.cancelButton);
canc.setBackgroundColor(Color.TRANSPARENT);
canc.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
canc.setColorFilter(Color.RED);
return false;
}
else if (me.getAction() == MotionEvent.ACTION_UP) {
canc.setColorFilter(Color.DKGRAY);
return false;
}
return false;
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isrecording) {
audioLogger.setCancel(true);
}
numberofclaps = 0;
listenBase = false;
baseline = 0;
}
@Override
protected void onPause() {
super.onPause();
if (isrecording) {
audioLogger.setCancel(true);
}
mySurfaceView.terminateThread();
}
@Override
protected void onResume() {
super.onResume();
if (mySurfaceHolder.getSurface().isValid()) {
mySurfaceView.startThread();
}
}
public void cancelRecord(View view) {
/*
int num = 150;
GraphViewData[] data = new GraphViewData[num];
float st = 0;
for (int i = 0; i < num; i++) {
st+=0.2;
data[i] = new GraphViewData(st, Math.sin(st));
}
displayGraph(R.layout.frcard, R.id.frText, R.id.frGraph, true, data, st, R.id.frGraphView);*/
if (isrecording) {
audioLogger.setCancel(true);
}
}
public void startRecord(View view) {
if (!isrecording) {
//Log.d(TAG, "in startRecord");
final ImageButton but = (ImageButton) findViewById(R.id.micButton);
if (listenBase) {
but.setColorFilter(Color.BLUE);
TextView txt = (TextView) findViewById(R.id.infoDescription);
TextView txt2 = (TextView) findViewById(R.id.infoHeading);
txt2.setText("Taking baseline.");
txt.setText("Wait for a red icon and sound meter before clapping.");
}
else {
but.setColorFilter(Color.RED);
}
mySurfaceView.setBaseline(listenBase);
audioLogger = new ClapImpulseResponse(50*baseline, listenBase, baseline, mySurfaceView);
audioRecorder = new AudioClipRecorder(audioLogger);
recordTask = new ClapRecord();
analyzeTask = new ClapAnalyze();
mySurfaceView.setRecording();
isrecording = true;
analyzeTask.execute();
recordTask.execute();
}
}
public void addCard(int resource, ViewGroup root) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout child = (LinearLayout) inflater.inflate(resource, root);
// Animation anim;
// if (inversed) {
// anim = AnimationUtils.loadAnimation(this, R.anim.slide_up_right);
// }
// else {
// anim = AnimationUtils.loadAnimation(this, R.anim.slide_up_left);
// }
// inversed = !inversed;
layout.addView(child);
// child.startAnimation(anim);
}
public void removeCard(int resource, ViewGroup root) {
LinearLayout deadChild = (LinearLayout) findViewById(resource);
layout.removeView(deadChild);
}
public void displayNumClaps() {
View current = findViewById(R.id.countCard);
if (current == null) {
addCard(R.layout.clapcount, null);
TextView txt = (TextView) findViewById(R.id.numclaps);
txt.setTypeface(robotoType);
}
else {
TextView txt = (TextView) findViewById(R.id.numclaps);
txt.setText(numberofclaps + " claps.");
}
//Log.d(TAG, "displaying clap");
}
public void displayGraph(int card, int resource, int graph, boolean line, int which, float end, int graphview) {
View c = findViewById(resource);
GraphView graphView = (GraphView) findViewById(graphview);
if (c == null) {
if (line) {
graphView = new LineGraphView(this, "");
}
else {
graphView = new BarGraphView(this, "");
}
switch (which) {
case 0:
graphView.addSeries(claps.getRoot().clapCurve);
break;
case 1:
graphView.addSeries(claps.getRoot().freqDecay);
break;
case 2:
graphView.addSeries(claps.getRoot().clapSpectra);
break;
case 3:
graphView.addSeries(claps.getRoot().freqResponse);
break;
}
graphView.setScalable(true);
graphView.setScrollable(true);
graphView.setViewPort(0, end);
addCard(card, null);
TextView txt = (TextView) findViewById(resource);
txt.setTypeface(robotoType);
LinearLayout g = (LinearLayout) findViewById(graph);
g.addView(graphView);
graphView.setId(graphview);
}
else {
Clap current = claps.getRoot();
Clap next = current.next;
GraphViewData[] avg;
GraphViewSeries avgseries;
float[] avgs;
switch (which) {
case 0:
current.clapCurve.setStyle(Color.GRAY, 3);
graphView.addSeries(current.clapCurve);
next.clapCurve.setStyle(Color.LTGRAY, 3);
next.clapCurve.setDescription("Previous samples");
avg = new GraphViewData[current.clapData.length];
avgs = claps.getAverage(0);
for (int i = 0; i < avg.length; i++) {
avg[i] = new GraphViewData(current.clapData[i].valueX, avgs[i]);
}
if (numberofclaps > 1)
graphView.removeSeries(claps.clapCurve);
avgseries = new GraphViewSeries("Average", null, avg);
claps.clapCurve = avgseries;
graphView.addSeries(avgseries);
break;
case 1:
current.freqDecay.setStyle(Color.GRAY, 3);
graphView.addSeries(current.freqDecay);
next.freqDecay.setStyle(Color.LTGRAY, 3);
next.freqDecay.setDescription("Previous samples");
avg = new GraphViewData[current.freqDecayData.length];
avgs = claps.getAverage(1);
for (int i = 0; i < avg.length; i++) {
avg[i] = new GraphViewData(current.freqDecayData[i].valueX, avgs[i]);
}
if (numberofclaps > 1)
graphView.removeSeries(claps.freqDecay);
avgseries = new GraphViewSeries("Average", null, avg);
claps.freqDecay = avgseries;
graphView.addSeries(avgseries);
break;
case 2:
current.clapSpectra.setStyle(Color.GRAY, 3);
graphView.addSeries(current.clapSpectra);
next.clapSpectra.setStyle(Color.LTGRAY, 3);
next.clapSpectra.setDescription("Previous samples");
avg = new GraphViewData[current.clapSpecData.length];
avgs = claps.getAverage(2);
for (int i = 0; i < avg.length; i++) {
avg[i] = new GraphViewData(current.clapSpecData[i].valueX, avgs[i]);
}
if (numberofclaps > 1)
graphView.removeSeries(claps.clapSpectra);
avgseries = new GraphViewSeries("Average", null, avg);
claps.clapSpectra = avgseries;
graphView.addSeries(avgseries);
break;
case 3:
current.freqResponse.setStyle(Color.GRAY, 3);
graphView.addSeries(current.freqResponse);
next.freqResponse.setStyle(Color.LTGRAY, 3);
next.freqResponse.setDescription("Previous samples");
avg = new GraphViewData[current.freqRespData.length];
avgs = claps.getAverage(3);
for (int i = 0; i < avg.length; i++) {
avg[i] = new GraphViewData(current.freqRespData[i].valueX, avgs[i]);
}
if (numberofclaps > 1)
graphView.removeSeries(claps.freqResponse);
avgseries = new GraphViewSeries("Average", null, avg);
claps.freqResponse = avgseries;
graphView.addSeries(avgseries);
break;
}
}
graphView.setShowLegend(true);
graphView.setLegendAlign(LegendAlign.TOP);
graphView.setLegendWidth(130);
graphView.setViewPort(0, end);
graphView.redrawAll();
}
private class ClapAnalyze extends AsyncTask<Void, Void, Integer> {
private StatusUpdate record;
private Clap clap;
@Override
protected void onPreExecute() {
super.onPreExecute();
record = new StatusUpdate();
clap = new Clap();
record.base = !listenBase;
record.done = true;
record.clap_heard = true;
record.RT60 = true;
record.clapgraph = true;
record.RT60s = true;
record.dsSpectra = true;
record.freqResp = true;
}
@Override
protected void onPostExecute(Integer i) {
}
@Override
protected void onProgressUpdate(Void... v) {
super.onProgressUpdate(v);
StatusUpdate status = audioLogger.getStatus();
Results results = audioLogger.getResults();
if (!status.base && !record.base) {
//Log.d(TAG, "false");
ImageButton but = (ImageButton) findViewById(R.id.micButton);
but.setColorFilter(Color.RED);
addCard(R.layout.noiselevelcard, null);
TextView txt = (TextView) findViewById(R.id.noiseLevel);
txt.setTypeface(robotoType);
record.base = !record.base;
listenBase = false;
baseline = audioLogger.getBaseline();
double bl =audioLogger.getBaselineV();
int roundOff = 2;
BigDecimal bd = new BigDecimal(bl);
bd = bd.setScale(roundOff, BigDecimal.ROUND_HALF_UP);
bl = bd.doubleValue();
txt.setText(bl + " dB.");
mySurfaceView.setBaseline(listenBase);
}
if (status.RT60 && record.RT60) {
View current = findViewById(R.id.reverbCard);
if (current == null) {
addCard(R.layout.reverbcard, null);
}
TextView txtR = (TextView) findViewById(R.id.reverbTimeRecent);
txtR.setTypeface(robotoType);
BigDecimal rt = new BigDecimal(results.RT60);
rt = rt.setScale(2, BigDecimal.ROUND_HALF_UP);
double rtd = rt.doubleValue();
TextView txt = (TextView) findViewById(R.id.reverbTimeDescription);
TextView txtA = (TextView) findViewById(R.id.reverbTimeAverage);
if (results.RT60 == -1) {
txtR.setText("Too quiet.");
txt.setText("The impulse must have enough energy to excite the room.");
}
else if (results.RT60 < 0) {
txtR.setText(rtd + " seconds.");
txt.setText("Overall reverberation time. Something has gone horribly wrong.");
}
else {
float rtavg;
numberofclaps++;
clap.RT60 = results.RT60;
clap.clapNumber = numberofclaps;
claps.insert(clap);
clap.inserted = true;
float currentAvg = claps.getAverageRT60();
float std = claps.std();
if (currentAvg < 0) {
rtavg = (float) rtd;
}
else {
rtavg = currentAvg;
BigDecimal rtav = new BigDecimal(rtavg);
rtav = rtav.setScale(2, BigDecimal.ROUND_HALF_UP);
rtavg = (float) rtav.doubleValue();
BigDecimal st = new BigDecimal(std);
st = st.setScale(2, BigDecimal.ROUND_HALF_UP);
std = (float) st.doubleValue();
}
txtR.setText(rtd + " seconds.");
txt.setText("Overall reverberation time.");
txtA.setText("Mean: " + rtavg + ", Variance: " + std);
}
displayNumClaps();
record.clap_heard = false;
removeCard(R.id.infoCard, null);
record.RT60 = false;
}
if (status.clapgraph && record.clapgraph) {
if (clap.inserted) {
int window = 6;
GraphViewData[] data = new GraphViewData[results.clapData.length/window];
float x = 0;
for (int i = 0; i < data.length; i++) {
x = ((float) (window*i*results.smoothwindow))/results.sampleRate;
data[i] = new GraphViewData(x, results.clapData[window*i]);
}
clap.clapCurve = new GraphViewSeries("Most recent sample", null, data);
clap.clapData = data;
displayGraph(R.layout.clapcard, R.id.clapText, R.id.clapGraph, true, 0, x, R.id.clapGraphView);
record.clapgraph = false;
}
}
if (status.RT60s && record.RT60s) {
GraphViewData[] data = new GraphViewData[results.RT60s.length];
for (int i = 0; i < data.length; i++) {
data[i] = new GraphViewData(results.freqs[i], results.RT60s[i]);
}
clap.freqDecay = new GraphViewSeries("Most recent sample", null, data);
clap.freqDecayData = data;
displayGraph(R.layout.fdecay, R.id.fdText, R.id.fdGraph, true, 1, results.freqs[results.freqs.length-1], R.id.fdGraphView);
record.RT60s = false;
}
if (status.dsSpectra && record.dsSpectra) {
GraphViewData[] data = new GraphViewData[results.dsSpectra.length];
for (int i = 0; i < data.length; i++) {
data[i] = new GraphViewData(results.freqs[i], results.dsSpectra[i]);
}
clap.clapSpectra = new GraphViewSeries("Most recent sample", null, data);
clap.clapSpecData = data;
displayGraph(R.layout.clapspec, R.id.csText, R.id.csGraph, true, 2, results.freqs[results.freqs.length-1], R.id.csGraphView);
record.dsSpectra = false;
}
if (status.freqResp && record.freqResp) {
GraphViewData[] data = new GraphViewData[results.freqResp.length];
for (int i = 0; i < data.length; i++) {
data[i] = new GraphViewData(results.freqs[i], results.freqResp[i]);
}
clap.freqResponse = new GraphViewSeries("Most recent sample", null, data);
clap.freqRespData = data;
displayGraph(R.layout.frcard, R.id.frText, R.id.frGraph, true, 3, results.freqs[results.freqs.length-1], R.id.frGraphView);
record.freqResp = false;
}
if (status.done && record.done) {
ImageButton but = (ImageButton) findViewById(R.id.micButton);
but.setColorFilter(Color.DKGRAY);
mySurfaceView.stopRecording();
record.done = false;
}
}
@Override
protected Integer doInBackground(Void... v) {
while (isrecording) {
publishProgress();
}
publishProgress();
return 0;
}
}
private class Claps {
public Clap root;
public GraphViewSeries clapCurve;
public GraphViewSeries clapSpectra;
public GraphViewSeries freqResponse;
public GraphViewSeries freqDecay;
public Claps() {
this.root = null;
}
public Clap getRoot() {
return this.root;
}
public float getAverageRT60() {
if (root == null) {
//Log.d(TAG, "no claps yet");
return -1;
}
Clap current = root;
float sum = 0;
int num = 0;
while (current != null) {
sum += current.RT60;
num++;
current = current.next;
}
return sum/num;
}
public float[] getAverage(int which) {
Clap current = root;
int num = 0;
float[] summed = new float[2];
switch (which) {
case 0:
Clap longest = root;
int max = 0;
while (longest != null) {
if (longest.clapData.length > max) {
max = longest.clapData.length;
}
longest = longest.next;
}
summed = new float[max];
break;
case 1:
summed = new float[current.freqDecayData.length];
break;
case 2:
summed = new float[current.clapSpecData.length];
break;
case 3:
summed = new float[current.freqRespData.length];
break;
}
for (int i = 0; i < summed.length; i++) {
summed[i] = 0;
}
float c;
while (current != null) {
switch (which) {
case 0:
for (int i = 0; i < summed.length; i++) {
if (i >= current.clapData.length) {
c = 0;
}
else {
c = (float) current.clapData[i].valueY;
}
summed[i] += c;
}
break;
case 1:
for (int i = 0; i < summed.length; i++) {
c = (float) current.freqDecayData[i].valueY;
summed[i] += c;
}
break;
case 2:
for (int i = 0; i < summed.length; i++) {
c = (float) current.clapSpecData[i].valueY;
summed[i] += c;
}
break;
case 3:
for (int i = 0; i < summed.length; i++) {
c = (float) current.freqRespData[i].valueY;
summed[i] += c;
}
break;
}
current = current.next;
num++;
}
for (int i = 0; i < summed.length; i++) {
summed[i] = summed[i] / num;
}
return summed;
}
public float std() {
if (root == null) {
//Log.d(TAG, "no claps yet");
return -1;
}
Clap current = root;
float sum = 0;
float sqrs = 0;
int num = 0;
while (current != null) {
sum += current.RT60;
sqrs += current.RT60*current.RT60;
num++;
current = current.next;
}
float lower = (sum*sum)/num;
float std = (float) Math.sqrt((sqrs - lower)/num);
return std;
}
public void display() {
Clap current = root;
while (current != null) {
//Log.d(TAG, current.clapNumber + " -> ");
current = current.next;
}
}
public void insert(Clap ins) {
ins.next = root;
root = ins;
}
}
private class Clap {
public GraphViewSeries freqDecay;
public GraphViewData[] freqDecayData;
public GraphViewSeries freqResponse;
public GraphViewData[] freqRespData;
public GraphViewSeries clapCurve;
public GraphViewData[] clapData;
public GraphViewSeries clapSpectra;
public GraphViewData[] clapSpecData;
public float RT60;
public int clapNumber;
public Clap next;
public boolean inserted;
public Clap() {
this.inserted = false;
}
}
private class ClapRecord extends AsyncTask<Void, Void, Integer> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer i) {
}
@Override
protected void onProgressUpdate(Void... v) {
}
@Override
protected Integer doInBackground(Void... v) {
//Log.d(TAG, "starting record");
boolean heard = audioRecorder.startRecordingForTime(1,
AudioClipRecorder.RECORDER_SAMPLERATE_CD,
AudioFormat.ENCODING_PCM_16BIT);
if (heard) {
if (!audioLogger.getCancel()) {
audioLogger.process();
}
}
isrecording = false;
return 0;
}
}
}