From 9ac644859314fc80862c9489353d8c7d5cf82ad4 Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Sat, 25 Jul 2015 13:02:20 -0700 Subject: [PATCH] Allow configuration of device audio latency/buffersize on Android --- .../apple2ix/Apple2AudioSettingsMenu.java | 59 +++++++++++++++++++ .../deadc0de/apple2ix/Apple2Preferences.java | 40 ++++++++++++- .../apple2ix/DevicePropertyCalculator.java | 3 +- Android/app/src/main/res/values/strings.xml | 6 +- 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2AudioSettingsMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2AudioSettingsMenu.java index 1da8ee55..ab493479 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2AudioSettingsMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2AudioSettingsMenu.java @@ -165,6 +165,65 @@ public class Apple2AudioSettingsMenu implements Apple2MenuView { seekBarValue.setText("" + vol); return convertView; } + }, + ADVANCED_SEPARATOR { + @Override public String getTitle(Apple2Activity activity) { + return activity.getResources().getString(R.string.settings_advanced); + } + @Override public String getSummary(Apple2Activity activity) { + return activity.getResources().getString(R.string.settings_advanced_summary); + } + }, + AUDIO_LATENCY { + @Override public String getTitle(Apple2Activity activity) { + return activity.getResources().getString(R.string.audio_latency); + } + @Override public String getSummary(Apple2Activity activity) { + return activity.getResources().getString(R.string.audio_latency_summary); + } + @Override public View getView(final Apple2Activity activity, View convertView) { + LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.a2preference_slider, null, false); + + TextView tv = (TextView)convertView.findViewById(R.id.a2preference_slider_summary); + tv.setText(getSummary(activity)); + + final TextView seekBarValue = (TextView)convertView.findViewById(R.id.a2preference_slider_seekBarValue); + + final SeekBar sb = (SeekBar)convertView.findViewById(R.id.a2preference_slider_seekBar); + sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (!fromUser) { + return; + } + if (progress <= 0) { + // buffer size cannot be zero + progress = 1; + sb.setProgress(progress); + } + float latencySecs = (float)progress / Apple2Preferences.AUDIO_LATENCY_NUM_CHOICES; + seekBarValue.setText("" + latencySecs); + Apple2Preferences.AUDIO_LATENCY.saveFloat(activity, latencySecs); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + + sb.setMax(0); // http://stackoverflow.com/questions/10278467/seekbar-not-setting-actual-progress-setprogress-not-working-on-early-android + sb.setMax(Apple2Preferences.AUDIO_LATENCY_NUM_CHOICES); + float latencySecs = Apple2Preferences.AUDIO_LATENCY.floatValue(activity); + seekBarValue.setText("" + latencySecs); + int tick = (int) (latencySecs * (float) Apple2Preferences.AUDIO_LATENCY_NUM_CHOICES); + sb.setProgress(tick); + return convertView; + } }; private static View _basicView(Apple2Activity activity, SETTINGS setting, View convertView) { diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java index c5186107..970f4f1c 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java @@ -71,6 +71,27 @@ public enum Apple2Preferences { @Override public int intValue(Apple2Activity activity) { return activity.getPreferences(Context.MODE_PRIVATE).getInt(toString(), Volume.MEDIUM.ordinal()); } + }, + AUDIO_LATENCY { + @Override public void load(Apple2Activity activity) { + nativeSetAudioLatency(floatValue(activity)); + } + @Override public float floatValue(Apple2Activity activity) { + + float defaultLatency = 0.f; + if (defaultLatency == 0.f) { + int sampleRateCanary = DevicePropertyCalculator.getRecommendedSampleRate(activity); + if (sampleRateCanary == DevicePropertyCalculator.defaultSampleRate) { + // quite possibly an audio-challenged device + defaultLatency = 0.4f; + } else { + // reasonable default for high-end devices + defaultLatency = 0.25f; + } + } + + return activity.getPreferences(Context.MODE_PRIVATE).getFloat(toString(), defaultLatency); + } }; public enum HiresColor { @@ -111,16 +132,21 @@ public enum Apple2Preferences { dialog.show(); } + public final static int AUDIO_LATENCY_NUM_CHOICES = 20; public final static String TAG = "Apple2Preferences"; // set and apply + public void saveBoolean(Apple2Activity activity, boolean value) { + activity.getPreferences(Context.MODE_PRIVATE).edit().putBoolean(toString(), value).apply(); + load(activity); + } public void saveInt(Apple2Activity activity, int value) { activity.getPreferences(Context.MODE_PRIVATE).edit().putInt(toString(), value).apply(); load(activity); } - public void saveBoolean(Apple2Activity activity, boolean value) { - activity.getPreferences(Context.MODE_PRIVATE).edit().putBoolean(toString(), value).apply(); + public void saveFloat(Apple2Activity activity, float value) { + activity.getPreferences(Context.MODE_PRIVATE).edit().putFloat(toString(), value).apply(); load(activity); } public void saveHiresColor(Apple2Activity activity, HiresColor value) { @@ -142,12 +168,21 @@ public enum Apple2Preferences { return activity.getPreferences(Context.MODE_PRIVATE).getInt(toString(), 0); } + public float floatValue(Apple2Activity activity) { + return activity.getPreferences(Context.MODE_PRIVATE).getFloat(toString(), 0.0f); + } + public static void loadPreferences(Apple2Activity activity) { for (Apple2Preferences pref : Apple2Preferences.values()) { pref.load(activity); } } + public static void resetPreferences(Apple2Activity activity) { + activity.getPreferences(Context.MODE_PRIVATE).edit().clear().commit(); + loadPreferences(activity); + } + private static native void nativeEnableTouchJoystick(boolean enabled); private static native void nativeEnableTiltJoystick(boolean enabled); private static native void nativeSetColor(int color); @@ -155,4 +190,5 @@ public enum Apple2Preferences { private static native void nativeSetSpeakerVolume(int volume); private static native boolean nativeSetMockingboardEnabled(boolean enabled); private static native void nativeSetMockingboardVolume(int volume); + private static native void nativeSetAudioLatency(float latencySecs); } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/DevicePropertyCalculator.java b/Android/app/src/main/java/org/deadc0de/apple2ix/DevicePropertyCalculator.java index 8f9d6af0..de4e742e 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/DevicePropertyCalculator.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/DevicePropertyCalculator.java @@ -38,6 +38,8 @@ import android.os.Build; */ public final class DevicePropertyCalculator { + public final static int defaultSampleRate = 22050; + public static boolean detectLowLatency( Context aContext ) { // check for low latency audio @@ -84,7 +86,6 @@ public final class DevicePropertyCalculator SR_CHECK = am.getProperty( AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE ); } - final int defaultSampleRate = 22050; return ( SR_CHECK != null ) ? Integer.parseInt( SR_CHECK ) : defaultSampleRate; } diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml index aad258d6..868dd911 100644 --- a/Android/app/src/main/res/values/strings.xml +++ b/Android/app/src/main/res/values/strings.xml @@ -9,7 +9,9 @@ Settings Apple2ix Configure audio… - Volume, mockingboard, etc + Speaker volume, Mockingboard, etc + Audio latency + Audio latency in secs Cancel Black/white Color @@ -63,6 +65,8 @@ Left/right swiping decreases/increases emulation speed Apple2ix emulator settings Apple2ix audio settings + Advanced settings + Warning: these settings may potentially degrade emulation performance General Joystick SecondActivity