I have a working app where images are being downloaded from the server, stored locally and being displayed in an image view as a slideshow every few seconds. I would like to count how many times each images is being displayed. I'm unsure how to tackle this. Should I make 6 counters (one for each image) or should I create an array to store the image name and count? But how do I update it?
Any assistance or code snippets would be much appreciated.
protected void onPostExecute(String s) {
super.onPostExecute(s);
imageView = findViewById(R.id.imgholder);
Timer mTimer = new Timer();
mTimer.schedule(new TimerTask() {
#Override
public void run() {
// As timer is not a Main/UI thread need to do all UI task on runOnUiThread
DashboardActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// increase your position so new image will show
position++;
// check whether position increased to length then set it to 0
// so it will show images in circuler
if (position >= 6)
position = 0;
File file = new File(Environment.getExternalStorageDirectory(), user.fullName + "-" + position + ".jpg");
imgUri = Uri.fromFile(file);
Log.v("test", "Now Showing Image: " + imgUri.toString());
// Set Image
imageView.setImageURI(imgUri);
counter++;
Log.v("test","Image: "+ position +" has appeared: " +counter);
}
});
}
}, 0, DELAY_TIMER);
Create an integer array of length 6 int[] counters = new int[]{0,0,0,0,0}, Then after if(position >= 6) position = 0; in your code , increment the counter as counters[position]++;. So you can always track the count of image at certain position from the array counters
Create a HashMap to store the count
private HashMap<String, Integer> countMap = new HashMap<>();
now whenever you set an image call below method:
private void updateDisplayCount(File file) {
if(countMap.containsKey(file.getName())) {
countMap.put(file.getName(), countMap.get(file.getName()) + 1);
} else {
countMap.put(file.getName(), 1);
}
}
The following code will return the display count
int count = countMap.get(file.getName())
Related
I'm trying to make the second sound on this app. oscillate in volume based on a Sin curve as a function of time passing. How would you suggest I edit what I already have to make that work? Any help would be amazing!
I'm sorry If some of the code is bad, I stepped away from this for some time and I added some sections back in that I previously commented out. I wasn't sure if it was for a good reason or not, so I added it back in.
MediaPlayer beep;
MediaPlayer sound2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //call superclass onCreate
setContentView(R.layout.activity_main); //inflate the GUI
minutesChanged = (TextView) findViewById(R.id.minutesChanged);
secondsChanged = (TextView) findViewById(R.id.secondsChanged);
minutesTotal = (TextView) findViewById(R.id.totalMinutesEntered);
secondsTotal = (TextView) findViewById(R.id.totalSecondsEntered);
percentageFinishedAmount = (TextView) findViewById(R.id.percentageFinishedAmount);
errorMessage = (TextView) findViewById(R.id.errorMessage);
secondEditText =
(EditText) findViewById(R.id.secondEditText);
secondEditText.addTextChangedListener(secondEditTextWatcher);
minuteEditText =
(EditText) findViewById(R.id.minuteEditText);
minuteEditText.addTextChangedListener(minuteEditTextWatcher);
whichSound =
(EditText) findViewById(R.id.whichSound);
whichSound.addTextChangedListener(whichSoundTextWatcher);
seekBarSeconds =
(SeekBar) findViewById(R.id.seekBarSeconds);
seekBarSeconds.setOnSeekBarChangeListener(seekBarListener);
seekBarMinutes =
(SeekBar) findViewById(R.id.seekBarMinutes);
seekBarMinutes.setOnSeekBarChangeListener(seekBarListener);
percentSeekBar =
(SeekBar) findViewById(R.id.seekBarTotal);
beep = MediaPlayer.create(MainActivity.this, R.raw.beep);
sound2 = MediaPlayer.create(MainActivity.this, R.raw.sound);
}
private void calculate() {
totalmili = minutesT + secondsT;
new CountDownTimer(totalmili + 2000, interv) {
public void onTick(long millisUntilFinished) {
secondsEllapsed = (int) ((totalmili + 2000 - millisUntilFinished) / 1000);
minutesEllapsed = secondsEllapsed / 60;
secondsEllapsed = secondsEllapsed % 60;
totalTimeEllapsed = secondsEllapsed + (minutesEllapsed * 60);
Log.d("Lucas", "total time elapsed " + totalTimeEllapsed);
if (totalTimeEllapsed < 10) {
errorMessage.setText("Elapsed Time: " + minutesEllapsed + " : 0" + secondsEllapsed);
} else if (totalTimeEllapsed >= 10) {
errorMessage.setText("Elapsed Time: " + minutesEllapsed + " : " + secondsEllapsed);
}
//set seekBar minutes/seconds and changing minute/seconds textView
seekBarMinutes.setProgress(minutesEllapsed);
minutesChanged.setText(Integer.toString(minutesEllapsed));
seekBarSeconds.setProgress(secondsEllapsed);
secondsChanged.setText(Integer.toString(secondsEllapsed));
double t = sin((360/totalmili) *(totalTimeEllapsed*1000));
float left = (float) ((t+1.0)*0.9/2.0+0.1);
float right = (float) ((t+1.0)*0.9/2.0+0.1);
sound2.setVolume(left,right);
Log.d("Lucas", "value of left and red respectively " + left + ", " +right);
if(sound == 1){
musicPlaying = beep.isPlaying();
if(musicPlaying == true){
beep.pause();
beep.seekTo(0);
beep.start();
} else {
beep.start();
}
}else if (sound == 2){
if(start ==1) {
sound2.seekTo(19);
sound2.start();
start = 2;
float left = (float) ((sin(totalTimeEllapsed)+1.0)*0.9/2.0+0.1);
float right = (float) ((sin(totalTimeEllapsed)+1.0)*0.9/2.0+0.1);
sound2.setVolume(left, right);
Log.d("Lucas", "value of left and red respectively " + left + ", " +right);
musicPlaying = sound2.isPlaying();
}else if(start ==2){
if (totalTimeEllapsed == 11) {
sound2.pause();
sound2.seekTo(34);
sound2.start();
} else if (totalTimeEllapsed == 22) {
sound2.pause();
}
}
//calculate total percentage finished, set percentage text, set total percentage seekBar
int percentProgress = (int) Math.round(((double) totalTimeEllapsed) / ((double) (totalmili) / 1000) * 100);
percentageFinishedAmount.setText(percentProgress + "% ");
percentSeekBar.setProgress(percentProgress);
//change background and text color bassed on increasing progress
errorMessage.setBackgroundColor(Color.argb(255, (int) (percentProgress * 2.5), 0, 0));
errorMessage.setTextColor(Color.argb(255, (int) (255 - (percentProgress * 2.5)), 255, 255));
}
public void onFinish() {
//display on finish text
errorMessage.setText("Done!");
}
}.start();
}
//listener object for the EditText's text-changed events
private final TextWatcher minuteEditTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// set hours Total and converts minutes to milliseconds
if (s.charAt(start + count - 1) == '\n') {
minuteEditText.getText().replace(start + count - 1, start + count, " ");
s = minuteEditText.getText();
Log.d("Lucas", "in enter key min, s = " + s);
try {
minutesT = Integer.parseInt(s.toString());
minutesTotal.setText(String.valueOf(minutesT) + " Minutes");
percentageFinishedAmount.setText(" ");
minutesT *= 60000;
} catch (NumberFormatException e) {
minutesTotal.setText("");
minutesT = 0;
}
}
}
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(
CharSequence s, int start, int count, int after) {
}
};
// listener object for the EditText's text-changed events
private final TextWatcher secondEditTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// set minutes Total when text is changed, calls calculate, changes seconds to milliseconds
if (s.charAt(start + count - 1) == '\n') {
secondEditText.getText().replace(start + count - 1, start + count, " ");
s = secondEditText.getText();
Log.d("Lucas", "in enter key sec, s = " + s);
try {
secondsT = Integer.parseInt(s.toString());
secondsTotal.setText(String.valueOf(secondsT) + " Seconds");
secondsT *= 1000;
percentageFinishedAmount.setText(" ");
calculate();
} catch (NumberFormatException e) {
secondsTotal.setText("");
secondsT = 0;
}
}
}
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(
CharSequence s, int start, int count, int after) {
}
};
Yowza. I'm not really clear on what the requirements are here or what you are doing.
My approach would be radically different, and maybe missing the point of what you are attempting. It would have these elements:
1) Output via a SourceDataLine, as this gives you a handle to affect the volume of every frame.
2) Per frame, consult a pointer into a Sin LUT that has the same number of elements as needed to correspond to the desired rate. (But it could also be fine to just create an argument into a sine function that increments the correct amount each frame to get your desired rate. No need to get into a war about whether the function or the LUT performs better.)
3) Multiply the SDL frame by the Sin LUT value.
For example an LFO of 1 Hz would imply either an LUT of 44100 elements, or an argument that increments by 1/44100 per frame.
As for getting access to the individual sound frames, see the tutorial Using Files and Format Converters/Reading Sound Files and look for the point in the example with the comment "Here, do something useful with the audio data that's now in the audioBytes array". That useful thing would be to convert the bytes to PCM (there are other StackOverflow explain how), get your next sin function value and multiply, then convert back to bytes.
EDIT:
Looking into the MediaPlayer API, I'm not clear you will be able to efficiently do what you want with this class.
A player is not prepared to respond to commands quasi-immediately
until its status has transitioned to MediaPlayer.Status.READY, which
in effect generally occurs when media pre-roll completes.
Does this imply some sort of buffer-size that will limit the frequency at which you can make the volume updates? IDK. When we get into buffer-size issues that in effect enforce a maximum number of updates per second, one has to listen carefully to check that there are no clicks or zippering due to discontinuities when the volume jumps from one level to another. A lot also depends on the amount of overhead happening under the hood when you set the position for each volume change.
With a SourceDataLine output, you have clean access to each frame which pretty much eliminates the potential zippering problems. I should add disclaimer though that I haven't grappled with MediaPlayer and may be ignorant of capabilities that you can take advantage of. Regardless, SDL is low level, powerful and efficient.
I'm writing an android app to measure display lag on tvs using the mirror function on the video out. After many revisions, my code got too complex for its own good, so I scraped it and did a rewrite. My issue is that it is not behaving as expected. The square is not blinking, and the time is 0.0 and the rating is excellent. i have tested changing the ui via the thread by making the square turn different colors, that worked fine. Can someone tell me what the issue is and how to fix it? The way the app works is that you hook the device to a tv and it mirrors the display. then it changes the color of a square in the app and dose a time stamp, then it wait till the camera detects a change then dose another time stamp. using both time stamps you can figure out the delay of the tv. I have it in a loop because the camera only captures at 15ish fps, so I need to run the test multiple times to get an accurate result. The issue is that it always shows up as 0.0ms, that is an impossible number because the lag on most consumer tvs is 9ms. I get the RGB values from each camera frame.
class lagTestThread extends Thread {
#Override
public void run () {
long lagStartTime;
long lagEndTime;
long tempResult;
final double rating;
int x;
long testResult = 0;
int cnt;
for (cnt = 0; cnt >= 100; cnt++){
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(000, 000, 000));
}
});
while (redVal >= 10.0 && blueVal >= 10.0 && greenVal >= 10.0) {
x = 0;
}
redVal = 0;
blueVal = 0;
greenVal = 0;
lagStartTime = System.nanoTime(); //start lagTimer start
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(255, 255, 255));
}
});
while (redVal <= 100.0 && blueVal <= 100.0 && greenVal <= 100.0) {
x = 0;
}
lagEndTime = System.nanoTime(); //start lagTimer end
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(000, 000, 000));
}
});
tempResult = (lagEndTime - lagStartTime);
if (tempResult <= testResult && tempResult != 0) {
testResult = tempResult;
}
}
rating = ((double) testResult) / 1000000.0;
final String finalResultString = String.valueOf(rating);
runOnUiThread(new Runnable() {
#Override
public void run() {
lagTime.setText(finalResultString);
if (rating <= 17.0) {
lagRating.setText("Excellent");
} else if (rating <= 34.0) {
lagRating.setText("Great");
} else if (rating <= 51.0) {
lagRating.setText("Average");
} else {
lagRating.setText("Bad");
}
}
});
}
}
I call it like this
public void startTest(View view) {
lagTestThread lagTest = new lagTestThread();
lagTest.start();
}
redVal, blueVal, greenVal declaration
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
double[] rgb = inputFrame.rgba().get(100, 100);
redVal = rgb[0];
blueVal = rgb[2];
greenVal = rgb[1];
Log.i("", "red:" + rgb[0] + " green:" + rgb[1] + " blue:" + rgb[2]);
return rgbMat;
}
The runOnUiThread() causes the Runnable to be posted to the UI thread, at which point the function returns immediately. The Runnable executes at some later time.
Your code is posting events to the UI thread and checking the system time, which means you're calculating how long it takes to post events to the UI thread, not how long it takes them to run. Also, because all the events are queued up behind one another, it's likely they will all execute in the same frame, so you will only see the result of the last setBackgroundColor() call.
If you really want to divorce your display and timing code from the UI thread, you should consider doing this with a SurfaceView, which can be updated independently of the UI thread. (The down side of SurfaceView is that it's a lot more complicated to work with than a custom View.)
I need to save 5 best times (an integer) for a game. The game is a reaction time game.
I'm assuming that the easiest way to do this would be to store it in a text file but I really have no idea how to go about doing this.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById(R.id.btnStart);
textTimer = (TextView) findViewById(R.id.textTimer);
myHandler = new Handler();
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startTime = SystemClock.uptimeMillis();
myHandler.postDelayed(updateTimerMethod, 0);
MainActivity.this.textTimer.setVisibility(0);
}
});
pauseButton = (Button) findViewById(R.id.btnPause);
pauseButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
timeSwap += timeInMillies;
myHandler.removeCallbacks(updateTimerMethod);
MainActivity.this.textTimer.setVisibility(0);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private Runnable updateTimerMethod = new Runnable() {
#Override
public void run() {
timeInMillies = SystemClock.uptimeMillis() - startTime;
finalTime = timeSwap + timeInMillies;
int seconds = (int) (finalTime / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
int milliseconds = (int) (finalTime % 1000);
textTimer.setText("" + minutes + ":" + String.format("%02d", seconds) + ":" + String.format("%03d", milliseconds));
myHandler.postDelayed(this, 0);
}
};
that is the code i have for now. all i want to happen is that when i press the pause button, it will go to the next screen and display the time. and also there will be a button, let's say "records". if it is pressed, it will display the 5 best times.
Instead of using a file to save scores, you can use SharedPreferences putInt() and getInt() methods to save int values.
The following sample is a possible (very lazy) implementation of it;
protected final static String PREFS_NAME = "PREF_GAME_SCORE";
public static int GetBestScore(Context context, int no)
{
SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
return settings.getInt("Score" + String.valueOf(no), 0); // # 0 is the default value if no data is found
}
public static void SetBestScore(Context context, int no, int score)
{
SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("Score" + String.valueOf(no), score);
editor.commit();
}
You can use SetBestScore(conxtext, 1, 666); to set the best score #number:1 and to get the best score, use; GetBestScore(context, 1);
The values saved using SharedPreferences will remain until the application is removed from the device or Application Data is cleared manually using:
Settings -> Applications -> Manage applications -> (choose your app)
-> Clear data
Building off of what Zefnus said, here is a method to send a score in and check if it is in the top lowest scores. If it is, it sets the TEMP_SPOT and then updates accordingly.
You could set any number of top scores too. This could easily be modified to got the other way too, IE, check for high scores.
public static void CheckAndSetBestScore(Context context, int score) {
int TOTAL_TOP_SCORES = 5;
int TEMP_SPOT = -1;
SharedPreferences settings = context
.getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
for (int i = 1; i <= TOTAL_TOP_SCORES; ++i) {
if (TEMP_SPOT == -1) {
if (score <= settings.getInt("Score" + String.valueOf(i), 1000)) {
TEMP_SPOT = i;
}
}
}
if (TEMP_SPOT != -1) {
for (int i = TOTAL_TOP_SCORES; i >= TEMP_SPOT; --i) {
if (i != TEMP_SPOT) {
editor.putInt("Score" + String.valueOf(i), settings.getInt("Score" + String.valueOf(i - 1), 1000));
}
else {
editor.putInt("Score" + String.valueOf(i), score);
}
}
}
}
There are three ways to do it
1. Flat FIle
2. Shared Preferences
3. SQL
Personally i would recommend you to go for SQL, since any of the operation that you want to add in the future makes lesser work.If in case of sharedpreferences you need a whole new set of code to handle a new operation.so better go with sqlite.
Whenever a game is ended take the score and insert it to the table. When you want to display the top 5 results. Do a query with Score in Ascending order and limit to 5 rows. Bingo you can get the result,which you wanted.
Hope it helps.
Take a look at this link for how to create and use an SQL database from android.
http://chrisrisner.com/31-Days-of-Android--Day-24%E2%80%93Using-SQLite-Databases
I'm actually working on a memory game and I'm stuck at the point where I should write the gameplay-part of the game.
So:
I have an array of N card objects. Each object has an attribute called cardNum - an identifier. I think I should write an actionListener on that array, so when I flip a card, it puts the flipped card's cardNum in an array of two elements and if the two elements of the array are equal, a pair is found.
The problem is that I just don't know how to get the last flipped card's cardNum.
Any help would be appreciated.
Here's the way I tried:
private void easyGame(Card[] cards) {
int flippedCards = 0;
int card1;
while(flippedCards != 24) {
for(int i=0; i<cards.length; i++) {
if(cards[i].getIsFlipped())
flippedCards ++;
}
if(flippedCards % 2 == 0 && flippedCards > 0)
for(int i=0; i<cards.length; i++) {
card1 = getCardIndByCardNum(cards[i].getCardNum(), cards, i);
if(!cards[card1].getIsFlipped()) {
for(int j=0; j<cards.length; j++) {
if(cards[i].getIsFlipped())
cards[i].flip();
}
flippedCards = 0;
break;
}
}
}
}
The problem is that if I call this method, the game won't be drawn. May I use use threads somehow?
EDIT
Here is how I get the indexes of the clicked cards, and I call it in the UI:
private void setCardHandlers() {
for(final Card card : cards) {
card.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
clickedCardInd = getChildren().indexOf(card)-1;
}
});
}
}
Than here is how I am using it:
setOnMouseReleased(new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent t) {
int cardIndex = clickedCardInd; // get index of what user clicked
clickedCardInd = -1;
if (cardIndex != -1 && moveRequestedFlag) { // our controller waits for the move
// get the position and report
moveRequestedFlag = false; // we handled the move
//System.out.println(cardIndex);
nextMove.setMove(cardIndex); // this will unblock controller's thread
}
}
});
It has a delay on fliping cards, also in the easyGame the requestMove method sets both indexes to the same.
I would recommend splitting you responsibilities a bit into Model/View/Controller modules, which, in simplest case would look like :
Model - your game current state and data, i.e. cards array Cards mCards = new Cards[24];
View - your UI, that can reflect current state of mCards(model) on screen in Main thread
Controller - your main game logic. This is most complex part, responsible for
requesting/handling user move,
updating mCards(model) based on user move,
Requesting UI to re-draw.
Contoroller's code (easyGame method) should run on separate thread to not block the UI.
Below I sketched a skeleton code that should fit your requirements :
class Game {
/*
* controller - main logic
*/
void startEasyGame() {
// initialize cards array, shuffle if needed
// we start with zero cards flipped
int flippedCards = 0;
// main loop
while (flippedCards != mCards.length) {
// 1. show updated UI
mBoard.showUpdatedCards();
// 2. request player move
// and block current thread to wait till move is done
// the result of the move - index of the card
int index1 = requestMove();
// temporarily flip first card face-up
mCards[index1].flip();
// show it on screen
mBoard.showUpdatedCards();
// same for second card
int index2 = requestMove();
mCards[index2].flip();
mBoard.showUpdatedCards();
// 3. check the result
if (mCards[index1].getCardNum() == mCards[index2].getCardNum()) {
// hooray, correct guess, update count
// possibly show some encouraging feedback to user
flippedCards += 2;
} else {
// incorrect, flip cards back face down
mCards[index1].flip();
mCards[index2].flip();
}
} // end of while loop
// game ended -> show score and time
mBoard.showResult();
}
}
EDIT
Extra details on how to await for result from UI thread :
int requestMove() {
// 1. show user prompt to make a move
// ...
// 2. construct latch to wait for move done on UI thread
mBoard.moveRequestedFlag = true;
NextMove nextMove = new NextMove();
mBoard.nextMove = nextMove;
// 3. await for move and get the result
return nextMove.getMove();
}
then, somewhere in UI code :
// handling card onClick somewhere on UI thread
if (mBoard.moveRequestedFlag) { // our controller waits for the move
// get the position and report
int cardIndex = ... // get index of what user clicked
mBoard.moveReqestedFlag = false; // we handled the move
mBoard.nextMove.setMove(cardIndex); // this will unblock controller's thread
}
and NextMove utility class to sync threads :
public class NextMove {
private volatile int mCardIndex;
private final CountDownLatch mMoveReady = new CountDownLatch(1);
public int getMove() throws InterruptedException {
mMoveReady.await();
return mCardIndex;
}
public synchronized void setMove(int selectedCardIndex) {
if (mMoveReady.getCount() > 0) {
mCardIndex = selectedCardIndex;
mMoveReady.countDown();
}
}
}
I need to develop an app to record frequencies in real time using the phone's mic and then display them (in text). I am posting my code here. The FFT and complex classes have been used from http://introcs.cs.princeton.edu/java/97data/FFT.java.html and http://introcs.cs.princeton.edu/java/97data/Complex.java.html .The problem is when i run this on the emulator the frequency starts from some random value and keeps on increasing till 7996. It then repeats the whole process. Can someone plz help me out?
public class Main extends Activity {
TextView disp;
private static int[] sampleRate = new int[] { 44100, 22050, 11025, 8000 };
short audioData[];
double finalData[];
int bufferSize,srate;
String TAG;
public boolean recording;
AudioRecord recorder;
Complex[] fftArray;
float freq;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disp = (TextView) findViewById(R.id.display);
Thread t1 = new Thread(new Runnable(){
public void run() {
Log.i(TAG,"Setting up recording");
for (int rate : sampleRate) {
try{
Log.d(TAG, "Attempting rate " + rate);
bufferSize=AudioRecord.getMinBufferSize(rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT)*3; //get the buffer size to use with this audio record
if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
recorder = new AudioRecord (MediaRecorder.AudioSource.MIC,rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,2048); //instantiate the AudioRecorder
Log.d(TAG, "BufferSize " +bufferSize);
srate = rate;
}
} catch (Exception e) {
Log.e(TAG, rate + "Exception, keep trying.",e);
}
}
bufferSize=2048;
recording=true; //variable to use start or stop recording
audioData = new short [bufferSize]; //short array that pcm data is put into.
Log.i(TAG,"Got buffer size =" + bufferSize);
while (recording) { //loop while recording is needed
Log.i(TAG,"in while 1");
if (recorder.getState()==android.media.AudioRecord.STATE_INITIALIZED) // check to see if the recorder has initialized yet.
if (recorder.getRecordingState()==android.media.AudioRecord.RECORDSTATE_STOPPED)
recorder.startRecording(); //check to see if the Recorder has stopped or is not recording, and make it record.
else {
Log.i(TAG,"in else");
// audiorecord();
finalData=convert_to_double(audioData);
Findfft();
for(int k=0;k<fftArray.length;k++)
{
freq = ((float)srate/(float) fftArray.length) *(float)k;
runOnUiThread(new Runnable(){
public void run()
{
disp.setText("The frequency is " + freq);
if(freq>=15000)
recording = false;
}
});
}
}//else recorder started
} //while recording
if (recorder.getState()==android.media.AudioRecord.RECORDSTATE_RECORDING)
recorder.stop(); //stop the recorder before ending the thread
recorder.release(); //release the recorders resources
recorder=null; //set the recorder to be garbage collected.
}//run
});
t1.start();
}
private void Findfft() {
// TODO Auto-generated method stub
Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
fftTempArray[i] = new Complex(finalData[i], 0);
}
fftArray = FFT.fft(fftTempArray);
}
private double[] convert_to_double(short data[]) {
// TODO Auto-generated method stub
double[] transformed = new double[data.length];
for (int j=0;j<data.length;j++) {
transformed[j] = (double)data[j];
}
return transformed;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Your question has been succinctly answered, however, to further your objectives and complete the loop...
Yes, FFT is not optimal on limited CPUs for pitch / frequency identification. A more optimal approach is YIN described here. You may find an implementation at Tarsos.
Issues you will face are the lack of javax.sound.sampled in the ADK and therefore converting the shorts/bytes from AudioRecord to the floats required for the referenced implementations.
Your problem is right here:
Findfft();
for(int k=0;k<fftArray.length;k++) {
freq = ((float)srate/(float) fftArray.length) *(float)k;
runOnUiThread(new Runnable() {
public void run() {
disp.setText("The frequency is " + freq);
if(freq>=15000) recording = false;
}
});
}
All this for loop does is go through your array of FFT values, convert the array index to a frequency in Hz, and print it.
If you want to output what frequency you're recording, you should at least look at the data in your array - the crudest method would be to calculate the square real magnitude and find the frequency bin with the biggest.
In addition to that, I don't think the FFT algorithm you're using does any precalculations - there are others that do, and seeing as you're developing for a mobile device, you might want to take CPU usage and power use into account.
JTransforms is one library that does use precalculation to lower CPU load, and its documentation is very complete.
You may also find useful information on how to interpret the data returned from the FFT at Wikipedia - no offense, but it looks like you're not quite sure what you're doing, so I'm giving pointers.
Lastly, if you're looking to use this app for musical notes, I seem to remember lots of people saying that an FFT isn't the best way to do that, but I can't remember what is. Maybe someone else can add that bit?
i find this solution after few days - the Best for getting frequency in Hrz:
download Jtransforms and this Jar also - Jtransforms need it.
then i use this task:
public class MyRecorder extends AsyncTask<Void, short[], Void> {
int blockSize = 2048;// = 256;
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;
int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
int BytesPerElement = 2;
#Override
protected Void doInBackground(Void... params) {
try {
final AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, BufferElements2Rec * BytesPerElement);
if (audioRecord == null) {
return null;
}
final short[] buffer = new short[blockSize];
final double[] toTransform = new double[blockSize];
audioRecord.startRecording();
while (started) {
Thread.sleep(100);
final int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
publishProgress(buffer);
}
audioRecord.stop();
audioRecord.release();
} catch (Throwable t) {
Log.e("AudioRecord", "Recording Failed");
}
return null;
}
#Override
protected void onProgressUpdate(short[]... buffer) {
super.onProgressUpdate(buffer);
float freq = calculate(RECORDER_SAMPLERATE, buffer[0]);
}
public static float calculate(int sampleRate, short [] audioData)
{
int numSamples = audioData.length;
int numCrossing = 0;
for (int p = 0; p < numSamples-1; p++)
{
if ((audioData[p] > 0 && audioData[p + 1] <= 0) ||
(audioData[p] < 0 && audioData[p + 1] >= 0))
{
numCrossing++;
}
}
float numSecondsRecorded = (float)numSamples/(float)sampleRate;
float numCycles = numCrossing/2;
float frequency = numCycles/numSecondsRecorded;
return frequency;
}