package ut.ewh.audiometrytest; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.os.Build; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Date; public class TestProctoring extends ActionBarActivity { private final int duration = 1; private final int sampleRate = 44100; private final int numSamples = duration * sampleRate; private final int volume = 32767; //private final int volume = 15000; private final int[] testingFrequencies = {1000, 500, 1000, 3000, 4000, 6000, 8000}; final private double mGain = 0.0044; final private double mAlpha = 0.9; private boolean heard = false; private boolean loop = true; int a = 0; public static boolean running = true; public double[] thresholds_right = {0, 0, 0, 0, 0, 0, 0}; public double[] thresholds_left = {0, 0, 0, 0, 0, 0, 0}; public static void stopThread(){ running = false; } /** * Randomly picks time gap between test tones in ms * @return */ public int randomTime(){ int time; double num = Math.random(); if (num < 0.3){ time = 2000; } else if (num < 0.67 && num >= 0.3){ time = 2500; }else{ time = 3000; }; return time; } /** * Changes background to white when called. */ Runnable bkgrndFlash = new Runnable() { @Override public void run(){ View view = findViewById(R.id.page); view.setBackgroundColor(Color.parseColor("#adce49")); } }; /** * Changes background color to black when called */ Runnable bkgrndFlashBlack = new Runnable() { @Override public void run(){ View view = findViewById(R.id.page); view.setBackgroundColor(Color.parseColor("#424242")); } }; /** * go to TestComplete activity */ public void gotoComplete(){ Intent intent = new Intent(this, TestComplete.class); startActivity(intent); }; /** * Generates the tone based on the increment and volume, used in inner loop * @param increment - the amount to increment by * @param volume - the volume to generate */ public byte[] genTone(float increment, int volume){ float angle = 0; double sample[] = new double[numSamples]; byte generatedSnd[] = new byte[2 * numSamples]; for (int i = 0; i < numSamples; i++){ sample[i] = Math.sin(angle); angle += increment; } int idx = 0; for (final double dVal : sample) { final short val = (short) ((dVal * volume)); //volume controlled by the value multiplied by dVal; max value is 32767 generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } return generatedSnd; } /** * Writes the parameter byte array to an AudioTrack and plays the array * @param generatedSnd- input 16-bit PCM Array */ public AudioTrack playSound(byte[] generatedSnd, int ear) { AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length, AudioTrack.MODE_STATIC); audioTrack.write(generatedSnd, 0, generatedSnd.length); if (ear == 0) { audioTrack.setStereoVolume(0, AudioTrack.getMaxVolume()); } else { audioTrack.setStereoVolume(AudioTrack.getMaxVolume(), 0); } audioTrack.play(); return audioTrack; } public class testThread extends Thread { public void run() { byte calibrationByteData[] = new byte[48]; try{ FileInputStream fis = openFileInput("CalibrationPreferences"); fis.read(calibrationByteData, 0, 48); fis.close(); } catch (IOException e) {}; final double calibrationArray[] = new double[6]; int counter = 0; for (int i = 0; i < calibrationArray.length; i++){ byte tmpByteBuffer[] = new byte[8]; for (int j = 0; j < 8; j++) { tmpByteBuffer[j] = calibrationByteData[counter]; counter++; } calibrationArray[i] = ByteBuffer.wrap(tmpByteBuffer).getDouble(); } //iterated once for every frequency to be tested for (int s = 0; s < 2; s++) { for (int i = 0; i < testingFrequencies.length; i++) { int frequency = testingFrequencies[i]; float increment = (float) (Math.PI) * frequency / sampleRate; int maxVolume = volume; int minVolume = 0; // This is the loop for each individual sample using a binary search algorithm for (; ; ) { int tempResponse = 0; int actualVolume = (minVolume + maxVolume) / 2; if ((maxVolume - minVolume) < 50) { //the test is done if the range is less than 400 if (s == 0) { if (i == 0 || i == 2) { thresholds_right[i] = actualVolume * calibrationArray[1]; //records volume as threshold } else if (i == 1){ thresholds_right[i] = actualVolume * calibrationArray[0]; //records volume as threshold } else if (i == 3) { thresholds_right[i] = actualVolume * calibrationArray[2]; //records volume as threshold } else if (i == 4) { thresholds_right[i] = actualVolume * calibrationArray[3]; //records volume as threshold } else if (i == 5) { thresholds_right[i] = actualVolume * calibrationArray[4]; //records volume as threshold } else if (i == 6) { thresholds_right[i] = actualVolume * calibrationArray[5]; //records volume as threshold } else {} if (i == 0 || i == 2) { thresholds_left[i] = actualVolume * calibrationArray[1]; //records volume as threshold } else if (i == 1){ thresholds_left[i] = actualVolume * calibrationArray[0]; //records volume as threshold } else if (i == 3) { thresholds_left[i] = actualVolume * calibrationArray[2]; //records volume as threshold } else if (i == 4) { thresholds_left[i] = actualVolume * calibrationArray[3]; //records volume as threshold } else if (i == 5) { thresholds_left[i] = actualVolume * calibrationArray[4]; //records volume as threshold } else if (i == 6) { thresholds_left[i] = actualVolume * calibrationArray[5]; //records volume as threshold } else {} break; //go to next frequency } else { for (int z = 0; z < 3; z++) { //iterate three times per volume level heard = false; if (!running){ return; } AudioTrack audioTrack = playSound(genTone(increment, actualVolume), s); try { Thread.sleep(randomTime()); } catch (InterruptedException e) {} audioTrack.release(); if (heard) { tempResponse++; } // // Checks if the first two test were positive, and skips the third if true. Helps speed the test along. if (tempResponse >= 2){ break; } // Check if the first two tests were misses, and skips the third if this is the case. if (z == 1 && tempResponse == 0){ break; } } //If the response was positive two out of three times, register as heard if (tempResponse >= 2) { maxVolume = actualVolume; } else { minVolume = actualVolume; } } //continue with test } } TestProctoring.this.runOnUiThread(bkgrndFlashBlack); } loop = false; SimpleDateFormat sdf = new SimpleDateFormat("MM_dd_yyyy-HHmmss"); String currentDateTime = sdf.format(new Date()); counter = 0; byte thresholdVolumeRightbyte[] = new byte[thresholds_right.length * 8]; for (int x = 0; x < thresholds_right.length; x++){ byte tmpByteArray[] = new byte[8]; ByteBuffer.wrap(tmpByteArray).putDouble(thresholds_right[x]); for (int j = 0; j < 8; j++){ thresholdVolumeRightbyte[counter] = tmpByteArray[j]; counter++; } } try{ FileOutputStream fos = openFileOutput("TestResults-Right-" + currentDateTime, Context.MODE_PRIVATE); try{ fos.write(thresholdVolumeRightbyte); fos.close(); } catch (IOException q) {} } catch (FileNotFoundException e) {} counter = 0; byte thresholdVolumeLeftbyte[] = new byte[thresholds_left.length * 8]; for (int x = 0; x < thresholds_left.length; x++){ byte tmpByteArray[] = new byte[8]; ByteBuffer.wrap(tmpByteArray).putDouble(thresholds_left[x]); for (int j = 0; j < 8; j++){ thresholdVolumeLeftbyte[counter] = tmpByteArray[j]; counter++; } } try{ FileOutputStream fos = openFileOutput("TestResults-Left-" + currentDateTime, Context.MODE_PRIVATE); try{ fos.write(thresholdVolumeLeftbyte); fos.close(); } catch (IOException q) {} } catch (FileNotFoundException e) {} gotoComplete(); } } //-------------------------------------------------------------------------- //End of Variable and Method Definitions //-------------------------------------------------------------------------- @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_proctoring); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); getWindow().setStatusBarColor(getResources().getColor(R.color.primary_dark)); } AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE); am.setStreamVolume(AudioManager.STREAM_MUSIC, 9, 0); Thread timingThread = new Thread (new Runnable() { public void run() { while (loop) { if (!running){ return; } if (heard) { try { Thread.sleep(500); } catch (InterruptedException x) {} TestProctoring.this.runOnUiThread(bkgrndFlashBlack); } } } }); Thread screenThread = new Thread (new Runnable() { public void run(){ while (loop){ if (!running){ return; } if (heard){ TestProctoring.this.runOnUiThread(bkgrndFlash); while (heard){ } } } } }); Thread testRunningThread = new Thread(new Runnable(){ public void run() { final testThread testThread = new testThread(); testThread.run(); } }); testRunningThread.start(); screenThread.start(); timingThread.start(); } @Override public boolean dispatchTouchEvent(MotionEvent e){ Log.i("Touch Alert", "Screen was hit!" + a++ + heard); heard = true; return super.dispatchTouchEvent(e); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.test_proctoring, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void onStop(){ super.onStop(); stopThread(); } }