diff --git a/src/main/java/org/billthefarmer/scope/Main.java b/src/main/java/org/billthefarmer/scope/Main.java
index fff0455..a6b0efd 100644
--- a/src/main/java/org/billthefarmer/scope/Main.java
+++ b/src/main/java/org/billthefarmer/scope/Main.java
@@ -62,7 +62,7 @@
// Main
@SuppressWarnings("deprecation")
public class Main extends Activity
- implements PopupMenu.OnMenuItemClickListener
+ implements PopupMenu.OnMenuItemClickListener
{
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_THEME = "pref_theme";
@@ -85,26 +85,26 @@ public class Main extends Activity
private static final String LEVEL = "level";
private static final float values[] =
- {
- 0.1f, 0.2f, 0.5f, 1.0f,
- 2.0f, 5.0f, 10.0f, 20.0f,
- 50.0f, 100.0f, 200.0f, 500.0f
- };
+ {
+ 0.1f, 0.2f, 0.5f, 1.0f,
+ 2.0f, 5.0f, 10.0f, 20.0f,
+ 50.0f, 100.0f, 200.0f, 500.0f
+ };
private static final String strings[] =
- {
- "0.1 ms", "0.2 ms", "0.5 ms",
- "1.0 ms", "2.0 ms", "5.0 ms",
- "10 ms", "20 ms", "50 ms",
- "0.1 sec", "0.2 sec", "0.5 sec"
- };
+ {
+ "0.1 ms", "0.2 ms", "0.5 ms",
+ "1.0 ms", "2.0 ms", "5.0 ms",
+ "10 ms", "20 ms", "50 ms",
+ "0.1 sec", "0.2 sec", "0.5 sec"
+ };
private static final int counts[] =
- {
- 256, 512, 1024, 2048,
- 4096, 8192, 16384, 32768,
- 65536, 131072, 262144, 524288
- };
+ {
+ 256, 512, 1024, 2048,
+ 4096, 8192, 16384, 32768,
+ 65536, 131072, 262144, 524288
+ };
private static final int REQUEST_PERMISSIONS = 1;
@@ -151,26 +151,26 @@ protected void onCreate(Bundle savedInstanceState)
switch (theme)
{
- case LIGHT:
- setTheme(R.style.AppTheme);
- break;
-
- case DARK:
- setTheme(R.style.AppDarkTheme);
- break;
-
- case SYSTEM:
- switch (night)
- {
- case Configuration.UI_MODE_NIGHT_NO:
+ case LIGHT:
setTheme(R.style.AppTheme);
break;
- case Configuration.UI_MODE_NIGHT_YES:
+ case DARK:
setTheme(R.style.AppDarkTheme);
break;
- }
- break;
+
+ case SYSTEM:
+ switch (night)
+ {
+ case Configuration.UI_MODE_NIGHT_NO:
+ setTheme(R.style.AppTheme);
+ break;
+
+ case Configuration.UI_MODE_NIGHT_YES:
+ setTheme(R.style.AppDarkTheme);
+ break;
+ }
+ break;
}
setContentView(R.layout.activity_main);
@@ -186,7 +186,7 @@ protected void onCreate(Bundle savedInstanceState)
// Find toolbar
toolbar = findViewById(getResources().getIdentifier("action_bar",
- "id", "android"));
+ "id", "android"));
// Set up navigation
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
toolbar.setNavigationOnClickListener((v) ->
@@ -222,9 +222,9 @@ protected void onCreate(Bundle savedInstanceState)
// Set up gesture detectors
gestureDetector =
- new GestureDetector(this, new GestureListener());
+ new GestureDetector(this, new GestureListener());
scaleDetector =
- new ScaleGestureDetector(this, new ScaleListener());
+ new ScaleGestureDetector(this, new ScaleListener());
if (scope != null)
scope.setOnTouchListener((v, event) ->
@@ -253,13 +253,13 @@ public boolean onPrepareOptionsMenu(Menu menu)
{
// Bright
menu.findItem(R.id.bright).setIcon(audio.bright?
- R.drawable.bright_checked :
- R.drawable.action_bright);
+ R.drawable.bright_checked :
+ R.drawable.action_bright);
// Single
menu.findItem(R.id.single).setIcon(audio.single?
- R.drawable.single_checked :
- R.drawable.action_single);
+ R.drawable.single_checked :
+ R.drawable.action_single);
// Timebase
if (timebase != DEFAULT_TIMEBASE)
@@ -276,8 +276,8 @@ public boolean onPrepareOptionsMenu(Menu menu)
// Storage
menu.findItem(R.id.storage).setIcon(scope.storage ?
- R.drawable.storage_checked :
- R.drawable.action_storage);
+ R.drawable.storage_checked :
+ R.drawable.action_storage);
menu.findItem(R.id.storage).setChecked(scope.storage);
return true;
@@ -353,198 +353,224 @@ public boolean onOptionsItemSelected(MenuItem item)
int id = item.getItemId();
switch (id)
{
- // Bright line
- case R.id.bright:
- audio.bright = !audio.bright;
- item.setIcon(audio.bright ?
- R.drawable.bright_checked :
- R.drawable.action_bright);
- showToast(audio.bright ? R.string.bright_on : R.string.bright_off);
- break;
-
- // Single shot
- case R.id.single:
- audio.single = !audio.single;
- item.setIcon(audio.single ?
- R.drawable.single_checked :
- R.drawable.action_single);
- showToast(audio.single ? R.string.single_on : R.string.single_off);
- break;
-
- // Trigger
- case R.id.trigger:
- if (audio.single)
- audio.trigger = true;
- break;
-
- // 0.1 ms
- case R.id.tb0_1ms:
- timebase = 0;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 0.2 ms
- case R.id.tb0_2ms:
- timebase = 1;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 0.5 ms
- case R.id.tb0_5ms:
- timebase = 2;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 1.0 ms
- case R.id.tb1_0ms:
- timebase = 3;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 2.0 ms
- case R.id.tb2_0ms:
- timebase = 4;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 5.0 ms
- case R.id.tb5_0ms:
- timebase = 5;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 10 ms
- case R.id.tb10ms:
- timebase = 6;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 20 ms
- case R.id.tb20ms:
- timebase = 7;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 50 ms
-
- case R.id.tb50ms:
- timebase = 8;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 0.1 sec
- case R.id.tb0_1sec:
- timebase = 9;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 0.2 sec
- case R.id.tb0_2sec:
- timebase = 10;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
-
- // 0.5 sec
- case R.id.tb0_5sec:
- timebase = 11;
- item.setChecked(true);
- setTimebase(timebase, true);
- break;
+ // Bright line
+ case R.id.bright:
+ audio.bright = !audio.bright;
+ item.setIcon(audio.bright ?
+ R.drawable.bright_checked :
+ R.drawable.action_bright);
+ showToast(audio.bright ? R.string.bright_on : R.string.bright_off);
+ break;
- // Storage
- case R.id.storage:
- if (scope != null)
- {
- scope.storage = !scope.storage;
- item.setIcon(scope.storage ?
- R.drawable.storage_checked :
- R.drawable.action_storage);
- showToast(scope.storage ?
- R.string.storage_on : R.string.storage_off);
- }
- break;
+ // Single shot
+ case R.id.single:
+ audio.single = !audio.single;
+ item.setIcon(audio.single ?
+ R.drawable.single_checked :
+ R.drawable.action_single);
+ showToast(audio.single ? R.string.single_on : R.string.single_off);
+ break;
- // Clear
- case R.id.clear:
- if ((scope != null) && scope.storage)
- scope.clear = true;
- break;
+ // Trigger
+ case R.id.trigger:
+ if (audio.single)
+ audio.trigger = true;
+ break;
- // Left
- case R.id.left:
- if (scope != null && xscale != null)
- {
- scope.start -= xscale.step;
- if (scope.start < 0)
- scope.start = 0;
+ // 0.1 ms
+ case R.id.tb0_1ms:
+ timebase = 0;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
- xscale.start = scope.start;
- xscale.invalidate();
- }
- break;
+ // 0.2 ms
+ case R.id.tb0_2ms:
+ timebase = 1;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
- // Right
- case R.id.right:
- if (scope != null && xscale != null)
- {
- scope.start += xscale.step;
- if (scope.start > audio.length)
- scope.start = audio.length - (xscale.step / 4);
+ // 0.5 ms
+ case R.id.tb0_5ms:
+ timebase = 2;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
- xscale.start = scope.start;
- xscale.invalidate();
- }
- break;
+ // 1.0 ms
+ case R.id.tb1_0ms:
+ timebase = 3;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
- // Start
- case R.id.start:
- if (scope != null && xscale != null)
- {
- scope.start = 0;
- scope.index = 0;
- xscale.start = 0;
- xscale.invalidate();
- yscale.index = 0;
- yscale.invalidate();
- setTimebase(timebase, false);
- }
- break;
+ // 2.0 ms
+ case R.id.tb2_0ms:
+ timebase = 4;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
- // End
- case R.id.end:
- if (scope != null && xscale != null)
- {
- scope.start = audio.length - (xscale.step / 4);
- xscale.start = scope.start;
- xscale.invalidate();
- }
- break;
+ // 5.0 ms
+ case R.id.tb5_0ms:
+ timebase = 5;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 10 ms
+ case R.id.tb10ms:
+ timebase = 6;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 20 ms
+ case R.id.tb20ms:
+ timebase = 7;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 50 ms
+
+ case R.id.tb50ms:
+ timebase = 8;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 0.1 sec
+ case R.id.tb0_1sec:
+ timebase = 9;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 0.2 sec
+ case R.id.tb0_2sec:
+ timebase = 10;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+
+ // 0.5 sec
+ case R.id.tb0_5sec:
+ timebase = 11;
+ item.setChecked(true);
+ setTimebase(timebase, true);
+ break;
+ // Rectangular
+ case R.id.window_rectangular:
+ audio.windowType = 0;
+ item.setChecked(true);
+ showToast("Rectangular");
+ break;
+
+// Hann
+ case R.id.window_hann:
+ audio.windowType = 1;
+ item.setChecked(true);
+ showToast("Hann");
+ break;
+
+// Hamming
+ case R.id.window_hamming:
+ audio.windowType = 2;
+ item.setChecked(true);
+ showToast("Hamming");
+ break;
- // Spectrum
- case R.id.action_spectrum:
- return onSpectrumClick(item);
+// Blackman
+ case R.id.window_blackman:
+ audio.windowType = 3;
+ item.setChecked(true);
+ showToast("Blackman");
+ break;
+ // Storage
+ case R.id.storage:
+ if (scope != null)
+ {
+ scope.storage = !scope.storage;
+ item.setIcon(scope.storage ?
+ R.drawable.storage_checked :
+ R.drawable.action_storage);
+ showToast(scope.storage ?
+ R.string.storage_on : R.string.storage_off);
+ }
+ break;
+
+ // Clear
+ case R.id.clear:
+ if ((scope != null) && scope.storage)
+ scope.clear = true;
+ break;
+
+ // Left
+ case R.id.left:
+ if (scope != null && xscale != null)
+ {
+ scope.start -= xscale.step;
+ if (scope.start < 0)
+ scope.start = 0;
+
+ xscale.start = scope.start;
+ xscale.invalidate();
+ }
+ break;
- // Help
- case R.id.action_help:
- return onHelpClick(item);
+ // Right
+ case R.id.right:
+ if (scope != null && xscale != null)
+ {
+ scope.start += xscale.step;
+ if (scope.start > audio.length)
+ scope.start = audio.length - (xscale.step / 4);
- // Settings
- case R.id.action_settings:
- return onSettingsClick(item);
+ xscale.start = scope.start;
+ xscale.invalidate();
+ }
+ break;
- default:
- return false;
+ // Start
+ case R.id.start:
+ if (scope != null && xscale != null)
+ {
+ scope.start = 0;
+ scope.index = 0;
+ xscale.start = 0;
+ xscale.invalidate();
+ yscale.index = 0;
+ yscale.invalidate();
+ setTimebase(timebase, false);
+ }
+ break;
+
+ // End
+ case R.id.end:
+ if (scope != null && xscale != null)
+ {
+ scope.start = audio.length - (xscale.step / 4);
+ xscale.start = scope.start;
+ xscale.invalidate();
+ }
+ break;
+
+ // Spectrum
+ case R.id.action_spectrum:
+ return onSpectrumClick(item);
+
+ // Help
+ case R.id.action_help:
+ return onHelpClick(item);
+
+ // Settings
+ case R.id.action_settings:
+ return onSettingsClick(item);
+
+ default:
+ return false;
}
return true;
@@ -557,21 +583,43 @@ public boolean onMenuItemClick(MenuItem item)
// Get id
int id = item.getItemId();
switch (id)
- {
- // Spectrum
- case R.id.action_spectrum:
- return onSpectrumClick(item);
+ {// Window - Rectangular
+ case R.id.window_rectangular:
+ audio.windowType = 0;
+ showToast("Rectangular");
+ return true;
+
+// Window - Hann
+ case R.id.window_hann:
+ audio.windowType = 1;
+ showToast("Hann");
+ return true;
+
+// Window - Hamming
+ case R.id.window_hamming:
+ audio.windowType = 2;
+ showToast("Hamming");
+ return true;
- // Help
- case R.id.action_help:
- return onHelpClick(item);
+// Window - Blackman
+ case R.id.window_blackman:
+ audio.windowType = 3;
+ showToast("Blackman");
+ return true;
+ // Spectrum
+ case R.id.action_spectrum:
+ return onSpectrumClick(item);
- // Settings
- case R.id.action_settings:
- return onSettingsClick(item);
+ // Help
+ case R.id.action_help:
+ return onHelpClick(item);
- default:
- return false;
+ // Settings
+ case R.id.action_settings:
+ return onSettingsClick(item);
+
+ default:
+ return false;
}
}
@@ -680,10 +728,10 @@ protected void onResume()
}
if (checkSelfPermission(Manifest.permission.RECORD_AUDIO)
- != PackageManager.PERMISSION_GRANTED)
+ != PackageManager.PERMISSION_GRANTED)
{
requestPermissions(new String[]
- {Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS);
+ {Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS);
return;
}
@@ -701,7 +749,7 @@ public void onRequestPermissionsResult(int requestCode,
{
for (int i = 0; i < grantResults.length; i++)
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO) &&
- grantResults[i] == PackageManager.PERMISSION_GRANTED)
+ grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
// Granted, recreate or start audio thread
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.M)
@@ -730,13 +778,13 @@ protected void onPause()
void getPreferences()
{
SharedPreferences preferences =
- PreferenceManager.getDefaultSharedPreferences(this);
+ PreferenceManager.getDefaultSharedPreferences(this);
// Set preferences
if (audio != null)
{
audio.input =
- Integer.parseInt(preferences.getString(PREF_INPUT, "0"));
+ Integer.parseInt(preferences.getString(PREF_INPUT, "0"));
audio.bright = preferences.getBoolean(PREF_BRIGHT, false);
audio.single = preferences.getBoolean(PREF_SINGLE, false);
}
@@ -765,7 +813,7 @@ void getPreferences()
void savePreferences()
{
SharedPreferences preferences =
- PreferenceManager.getDefaultSharedPreferences(this);
+ PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(PREF_BRIGHT, audio.bright);
@@ -780,17 +828,17 @@ void showAlert(int appName, int errorBuffer)
{
// Create an alert dialog builder
AlertDialog.Builder builder =
- new AlertDialog.Builder(this);
+ new AlertDialog.Builder(this);
// Set the title, message and button
builder.setTitle(appName);
builder.setMessage(errorBuffer);
builder.setNeutralButton(android.R.string.ok,
- (dialog, which) ->
- {
- // Dismiss dialog
- dialog.dismiss();
- });
+ (dialog, which) ->
+ {
+ // Dismiss dialog
+ dialog.dismiss();
+ });
// Create the dialog
AlertDialog dialog = builder.create();
@@ -801,7 +849,7 @@ void showAlert(int appName, int errorBuffer)
// GestureListener
private class GestureListener
- extends GestureDetector.SimpleOnGestureListener
+ extends GestureDetector.SimpleOnGestureListener
{
// onDown
@Override
@@ -816,13 +864,13 @@ public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY)
{
float scale = (float) (2.0 / ((audio.sample / 100000.0) *
- scope.xscale));
+ scope.xscale));
// Calculate target value for animator
float target = scope.start - velocityX / scale / 4;
// Start the animation
ValueAnimator animator =
- ValueAnimator.ofFloat(scope.start, target);
+ ValueAnimator.ofFloat(scope.start, target);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener((animation) ->
{
@@ -854,7 +902,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY)
{
float scale = (float) (2.0 / ((audio.sample / 100000.0) *
- scope.xscale));
+ scope.xscale));
scope.start += distanceX / scale;
if (scope.start < 0)
scope.start = 0;
@@ -879,7 +927,7 @@ public boolean onSingleTapUp(MotionEvent e)
// ScaleListener
private class ScaleListener
- extends ScaleGestureDetector.SimpleOnScaleGestureListener
+ extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
// onScale
@Override
@@ -907,7 +955,7 @@ protected class Audio implements Runnable
// Data
protected short data[];
protected long length;
-
+ protected int windowType = 1; // 0=Rectangular, 1=Hann, 2=Hamming, 3=Blackman
// Private data
private static final int SAMPLES = 524288;
private static final int FRAMES = 4096;
@@ -961,7 +1009,7 @@ private void cleanUpAudioRecord()
try
{
if (audioRecord.getRecordingState() ==
- AudioRecord.RECORDSTATE_RECORDING)
+ AudioRecord.RECORDSTATE_RECORDING)
audioRecord.stop();
audioRecord.release();
@@ -978,19 +1026,19 @@ protected void processAudio()
{
// Create the AudioRecord object
audioRecord = new AudioRecord.Builder()
- .setAudioSource(input)
- .setAudioFormat
- (new AudioFormat.Builder()
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
- .build())
- .build();
+ .setAudioSource(input)
+ .setAudioFormat
+ (new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build())
+ .build();
}
catch (Exception e)
{
runOnUiThread(() -> showAlert(R.string.app_name,
- R.string.error_init));
+ R.string.error_init));
running = false;
return;
}
@@ -1005,7 +1053,7 @@ protected void processAudio()
if (state != AudioRecord.STATE_INITIALIZED)
{
runOnUiThread(() -> showAlert(R.string.app_name,
- R.string.error_init));
+ R.string.error_init));
audioRecord.release();
running = false;
@@ -1037,113 +1085,113 @@ protected void processAudio()
// State machine for sync and copying data to display buffer
switch (state)
{
- // INIT: waiting for sync
- case INIT:
+ // INIT: waiting for sync
+ case INIT:
- index = 0;
+ index = 0;
- if (bright)
- state++;
+ if (bright)
+ state++;
- else
- {
- if (single && !trigger)
- break;
+ else
+ {
+ if (single && !trigger)
+ break;
- // Calculate sync level
- float level = -yscale.index * scope.yscale;
+ // Calculate sync level
+ float level = -yscale.index * scope.yscale;
- // Initialise sync
- int dx;
+ // Initialise sync
+ int dx;
- // Sync polarity
- if (level < 0)
- {
- for (int i = 0; i < size; i++)
+ // Sync polarity
+ if (level < 0)
{
- dx = buffer[i] - last;
-
- if (dx < 0 && last > level && buffer[i] < level)
+ for (int i = 0; i < size; i++)
{
- index = i;
- state++;
- break;
- }
+ dx = buffer[i] - last;
+
+ if (dx < 0 && last > level && buffer[i] < level)
+ {
+ index = i;
+ state++;
+ break;
+ }
- last = buffer[i];
+ last = buffer[i];
+ }
}
- }
- else
- {
- for (int i = 0; i < size; i++)
+ else
{
- dx = buffer[i] - last;
-
- if (dx > 0 && last < level && buffer[i] > level)
+ for (int i = 0; i < size; i++)
{
- index = i;
- state++;
- break;
- }
+ dx = buffer[i] - last;
+
+ if (dx > 0 && last < level && buffer[i] > level)
+ {
+ index = i;
+ state++;
+ break;
+ }
- last = buffer[i];
+ last = buffer[i];
+ }
}
}
- }
- // No sync, try next time
- if (state == INIT)
- break;
+ // No sync, try next time
+ if (state == INIT)
+ break;
- // Reset trigger
- if (single && trigger)
- trigger = false;
+ // Reset trigger
+ if (single && trigger)
+ trigger = false;
- // FIRST: First chunk of data
- case FIRST:
+ // FIRST: First chunk of data
+ case FIRST:
- // Update count
- count = counts[timebase];
- length = count;
+ // Update count
+ count = counts[timebase];
+ length = count;
- // Copy data
- System.arraycopy(buffer, index, data, 0, size - index);
- index = size - index;
+ // Copy data
+ System.arraycopy(buffer, index, data, 0, size - index);
+ index = size - index;
- // If done, wait for sync again
- if (index >= count)
- state = INIT;
+ // If done, wait for sync again
+ if (index >= count)
+ state = INIT;
- // Else get some more data next time
- else
- state++;
- break;
+ // Else get some more data next time
+ else
+ state++;
+ break;
- // NEXT: Subsequent chunks of data
- case NEXT:
+ // NEXT: Subsequent chunks of data
+ case NEXT:
- // Copy data
- System.arraycopy(buffer, 0, data, index, size);
- index += size;
+ // Copy data
+ System.arraycopy(buffer, 0, data, index, size);
+ index += size;
- // Done, wait for sync again
- if (index >= count)
- state = INIT;
+ // Done, wait for sync again
+ if (index >= count)
+ state = INIT;
- // Else if last but one chunk, get last chunk next time
- else if (index + size >= count)
- state++;
- break;
+ // Else if last but one chunk, get last chunk next time
+ else if (index + size >= count)
+ state++;
+ break;
- // LAST: Last chunk of data
- case LAST:
+ // LAST: Last chunk of data
+ case LAST:
- // Copy data
- System.arraycopy(buffer, 0, data, index, count - index);
+ // Copy data
+ System.arraycopy(buffer, 0, data, index, count - index);
- // Wait for sync next time
- state = INIT;
- break;
+ // Wait for sync next time
+ state = INIT;
+ break;
}
// Update display
diff --git a/src/main/java/org/billthefarmer/scope/SpectrumActivity.java b/src/main/java/org/billthefarmer/scope/SpectrumActivity.java
index 46d8039..df22559 100644
--- a/src/main/java/org/billthefarmer/scope/SpectrumActivity.java
+++ b/src/main/java/org/billthefarmer/scope/SpectrumActivity.java
@@ -59,7 +59,7 @@
// SpectrumActivity
@SuppressWarnings("deprecation")
public class SpectrumActivity extends Activity
- implements View.OnClickListener, PopupMenu.OnMenuItemClickListener
+ implements View.OnClickListener, PopupMenu.OnMenuItemClickListener
{
private static final String PREF_INPUT = "pref_input";
@@ -96,26 +96,26 @@ protected void onCreate(Bundle savedInstanceState)
switch (theme)
{
- case Main.LIGHT:
- setTheme(R.style.AppTheme);
- break;
-
- case Main.DARK:
- setTheme(R.style.AppDarkTheme);
- break;
-
- case Main.SYSTEM:
- switch (night)
- {
- case Configuration.UI_MODE_NIGHT_NO:
+ case Main.LIGHT:
setTheme(R.style.AppTheme);
break;
- case Configuration.UI_MODE_NIGHT_YES:
+ case Main.DARK:
setTheme(R.style.AppDarkTheme);
break;
- }
- break;
+
+ case Main.SYSTEM:
+ switch (night)
+ {
+ case Configuration.UI_MODE_NIGHT_NO:
+ setTheme(R.style.AppTheme);
+ break;
+
+ case Configuration.UI_MODE_NIGHT_YES:
+ setTheme(R.style.AppDarkTheme);
+ break;
+ }
+ break;
}
setContentView(R.layout.activity_spectrum);
@@ -140,7 +140,7 @@ protected void onCreate(Bundle savedInstanceState)
// Find toolbar
toolbar = findViewById(getResources().getIdentifier("action_bar",
- "id", "android"));
+ "id", "android"));
// Set up navigation
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
toolbar.setNavigationOnClickListener((v) ->
@@ -184,33 +184,33 @@ public boolean onOptionsItemSelected(MenuItem item)
int id = item.getItemId();
switch (id)
{
- // Home
- case android.R.id.home:
- finish();
- break;
-
- // Lock
- case R.id.action_lock:
- audio.lock = !audio.lock;
- item.setIcon(audio.lock ? R.drawable.lock_checked :
- R.drawable.action_lock);
- showToast(audio.lock ? R.string.lock_on : R.string.lock_off);
- break;
-
- // Help
- case R.id.action_help:
- intent = new Intent(this, HelpActivity.class);
- startActivity(intent);
- break;
-
- // Settings
- case R.id.action_settings:
- intent = new Intent(this, SettingsActivity.class);
- startActivity(intent);
- break;
-
- default:
- return false;
+ // Home
+ case android.R.id.home:
+ finish();
+ break;
+
+ // Lock
+ case R.id.action_lock:
+ audio.lock = !audio.lock;
+ item.setIcon(audio.lock ? R.drawable.lock_checked :
+ R.drawable.action_lock);
+ showToast(audio.lock ? R.string.lock_on : R.string.lock_off);
+ break;
+
+ // Help
+ case R.id.action_help:
+ intent = new Intent(this, HelpActivity.class);
+ startActivity(intent);
+ break;
+
+ // Settings
+ case R.id.action_settings:
+ intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ break;
+
+ default:
+ return false;
}
return true;
@@ -224,15 +224,15 @@ public void onClick(View v)
int id = v.getId();
switch (id)
{
- // Spectrum
- case R.id.spectrum:
- audio.lock = !audio.lock;
- if (lockItem != null)
- lockItem.setIcon(audio.lock ?
- R.drawable.lock_checked :
- R.drawable.action_lock);
- showToast(audio.lock ? R.string.lock_on : R.string.lock_off);
- break;
+ // Spectrum
+ case R.id.spectrum:
+ audio.lock = !audio.lock;
+ if (lockItem != null)
+ lockItem.setIcon(audio.lock ?
+ R.drawable.lock_checked :
+ R.drawable.action_lock);
+ showToast(audio.lock ? R.string.lock_on : R.string.lock_off);
+ break;
}
}
@@ -246,25 +246,25 @@ public boolean onMenuItemClick(MenuItem item)
int id = item.getItemId();
switch (id)
{
- // Scope
- case R.id.action_scope:
- finish();
- return true;
+ // Scope
+ case R.id.action_scope:
+ finish();
+ return true;
- // Help
- case R.id.action_help:
- intent = new Intent(this, HelpActivity.class);
- startActivity(intent);
- return true;
+ // Help
+ case R.id.action_help:
+ intent = new Intent(this, HelpActivity.class);
+ startActivity(intent);
+ return true;
// Settings
- case R.id.action_settings:
- intent = new Intent(this, SettingsActivity.class);
- startActivity(intent);
- return true;
+ case R.id.action_settings:
+ intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ return true;
- default:
- return false;
+ default:
+ return false;
}
}
@@ -307,10 +307,10 @@ protected void onResume()
recreate();
if (checkSelfPermission(Manifest.permission.RECORD_AUDIO)
- != PackageManager.PERMISSION_GRANTED)
+ != PackageManager.PERMISSION_GRANTED)
{
requestPermissions(new String[]
- {Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS);
+ {Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS);
return;
}
@@ -327,7 +327,7 @@ public void onRequestPermissionsResult(int requestCode,
if (requestCode == REQUEST_PERMISSIONS)
for (int i = 0; i < grantResults.length; i++)
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO) &&
- grantResults[i] == PackageManager.PERMISSION_GRANTED)
+ grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
// Granted, recreate or start audio thread
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.M)
@@ -357,13 +357,13 @@ void getPreferences()
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
SharedPreferences preferences =
- PreferenceManager.getDefaultSharedPreferences(this);
+ PreferenceManager.getDefaultSharedPreferences(this);
// Set preferences
if (audio != null)
{
audio.input =
- Integer.parseInt(preferences.getString(PREF_INPUT, "0"));
+ Integer.parseInt(preferences.getString(PREF_INPUT, "0"));
audio.fill = preferences.getBoolean(PREF_FILL, true);
audio.hold = preferences.getBoolean(PREF_HOLD, true);
}
@@ -385,7 +385,7 @@ void getPreferences()
void savePreferences()
{
SharedPreferences preferences =
- PreferenceManager.getDefaultSharedPreferences(this);
+ PreferenceManager.getDefaultSharedPreferences(this);
// TODO
}
@@ -395,17 +395,17 @@ void showAlert(int appName, int errorBuffer)
{
// Create an alert dialog builder
AlertDialog.Builder builder =
- new AlertDialog.Builder(this);
+ new AlertDialog.Builder(this);
// Set the title, message and button
builder.setTitle(appName);
builder.setMessage(errorBuffer);
builder.setNeutralButton(android.R.string.ok,
- (dialog, which) ->
- {
- // Dismiss dialog
- dialog.dismiss();
- });
+ (dialog, which) ->
+ {
+ // Dismiss dialog
+ dialog.dismiss();
+ });
// Create the dialog
AlertDialog dialog = builder.create();
@@ -458,6 +458,7 @@ protected class Audio implements Runnable
private double xf[];
+ protected int windowType = 0;
// Constructor
protected Audio()
{
@@ -522,20 +523,20 @@ protected void processAudio()
{
// Create the AudioRecord object
audioRecord = new AudioRecord.Builder()
- .setAudioSource(input)
- .setAudioFormat
- (new AudioFormat.Builder()
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
- .build())
- .build();
+ .setAudioSource(input)
+ .setAudioFormat
+ (new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build())
+ .build();
}
// Exception
catch (Exception e)
{
runOnUiThread(() -> showAlert(R.string.app_name,
- R.string.error_init));
+ R.string.error_init));
running = false;
return;
}
@@ -553,7 +554,7 @@ protected void processAudio()
if (state != AudioRecord.STATE_INITIALIZED)
{
runOnUiThread(() -> showAlert(R.string.app_name,
- R.string.error_init));
+ R.string.error_init));
audioRecord.release();
running = false;
@@ -605,11 +606,32 @@ protected void processAudio()
dmax = Math.abs(buffer[i]);
// Calculate the window
- double window =
- 0.5 - 0.5 * Math.cos(2.0 * Math.PI *
- i / SAMPLES);
+ double window;
+
+ switch (windowType)
+ {
+ case 0: // Rectangular
+ window = 1.0;
+ break;
+
+ case 1: // Hann
+ window = 0.5 - 0.5 * Math.cos(2.0 * Math.PI * i / SAMPLES);
+ break;
+
+ case 2: // Hamming
+ window = 0.54 - 0.46 * Math.cos(2.0 * Math.PI * i / SAMPLES);
+ break;
+
+ case 3: // Blackman
+ window = 0.42
+ - 0.5 * Math.cos(2.0 * Math.PI * i / SAMPLES)
+ + 0.08 * Math.cos(4.0 * Math.PI * i / SAMPLES);
+ break;
+
+ default:
+ window = 1.0;
+ }
- // Normalise and window the input data
xr[i] = buffer[i] / norm * window;
}
@@ -696,9 +718,9 @@ protected void processAudio()
if (Math.abs(xf[i] - frequency) < fps)
sumf += xa[i] * xa[i];
- // Sum the harmonics
+ // Sum the harmonics
else if (Math.abs(Math.IEEEremainder(xf[i], frequency)) <
- Math.round(xf[i] / frequency) * fps)
+ Math.round(xf[i] / frequency) * fps)
sumh += xa[i] * xa[i];
// Total harmonic distortion
@@ -709,7 +731,7 @@ else if (Math.abs(Math.IEEEremainder(xf[i], frequency)) <
for (int i = 0; i < STEP; i++)
level += ((double) data[i] / 32768.0) *
- ((double) data[i] / 32768.0);
+ ((double) data[i] / 32768.0);
level = Math.sqrt(level / STEP) * 2.0;
@@ -722,8 +744,8 @@ else if (Math.abs(Math.IEEEremainder(xf[i], frequency)) <
if (max > MIN)
{
final String s = String.format(Locale.getDefault(),
- "%1.0f%% %1.1fHz %1.1fdB",
- thd, frequency, dB);
+ "%1.0f%% %1.1fHz %1.1fdB",
+ thd, frequency, dB);
text.post(() -> text.setText(s));
}
@@ -731,7 +753,7 @@ else if (Math.abs(Math.IEEEremainder(xf[i], frequency)) <
{
frequency = 0.0;
final String s = String.format(Locale.getDefault(),
- "%1.1fdB", dB);
+ "%1.1fdB", dB);
text.post(() -> text.setText(s));
}
}
@@ -769,7 +791,7 @@ private void fftr(double ar[], double ai[])
}
for (int mmax = 1, istep = 2 * mmax; mmax < n;
- mmax = istep, istep = 2 * mmax)
+ mmax = istep, istep = 2 * mmax)
{
double delta = Math.PI / mmax;
for (int m = 0; m < mmax; m++)
@@ -793,3 +815,4 @@ private void fftr(double ar[], double ai[])
}
}
}
+
diff --git a/src/main/res/menu/navigation.xml b/src/main/res/menu/navigation.xml
index 9e52814..b56cbad 100644
--- a/src/main/res/menu/navigation.xml
+++ b/src/main/res/menu/navigation.xml
@@ -9,6 +9,35 @@
android:id="@+id/action_spectrum"
android:title="@string/action_spectrum" />
+ -
+
+
+
+
+
@@ -17,4 +46,4 @@
android:id="@+id/action_help"
android:title="@string/action_help" />
-
+
\ No newline at end of file