Stability fixes

Fixes various crashes, by (a) disabling the stats gathering thread and
(b) only trying to open a USB device if it identifies itself as a MIDI
keyboard. Disabling stats gathering should improve responsiveness and
improve battery consumption as well.

The crash in the stats gathering thread was a race on destruction, where
the audio thread set the stats_ringbuffer to NULL, but the Java thread
continued to try to read from it. This could be made more robust.
master
Raph Levien 12 years ago
parent dedde725d2
commit dffa68cee6
  1. 4
      android/AndroidManifest.xml
  2. 51
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 18
      android/src/com/levien/synthesizer/android/widgets/piano/PianoView.java

@ -2,8 +2,8 @@
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
package="com.levien.synthesizer" package="com.levien.synthesizer"
android:versionCode="3" android:versionCode="4"
android:versionName="0.91"> android:versionName="0.92">
<uses-sdk android:minSdkVersion="9" <uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="17" android:targetSdkVersion="17"
/> <!-- 9 = Gingerbread --> /> <!-- 9 = Gingerbread -->

@ -35,7 +35,6 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.WindowManager; import android.view.WindowManager;
@ -123,23 +122,27 @@ public class PianoActivity2 extends Activity {
tryConnectUsb(); tryConnectUsb();
} }
jitterStats_ = new JitterStats(); final boolean doStats = false;
jitterStats_.setNominalCb(params.bufferSize / (double)params.sampleRate);
statusHandler_ = new Handler(); if (doStats) {
statusRunnable_ = new Runnable() { jitterStats_ = new JitterStats();
public void run() { jitterStats_.setNominalCb(params.bufferSize / (double)params.sampleRate);
int n = androidGlue_.statsBytesAvailable(); statusHandler_ = new Handler();
if (n > 0) { statusRunnable_ = new Runnable() {
byte[] buf = new byte[n]; public void run() {
androidGlue_.readStatsBytes(buf, 0, n); int n = androidGlue_.statsBytesAvailable();
jitterStats_.aggregate(buf); if (n > 0) {
TextView statusTextView = (TextView)findViewById(R.id.status); byte[] buf = new byte[n];
statusTextView.setText(jitterStats_.report()); androidGlue_.readStatsBytes(buf, 0, n);
jitterStats_.aggregate(buf);
TextView statusTextView = (TextView)findViewById(R.id.status);
statusTextView.setText(jitterStats_.report());
}
statusHandler_.postDelayed(statusRunnable_, 100);
} }
statusHandler_.postDelayed(statusRunnable_, 100); };
} statusRunnable_.run();
}; }
statusRunnable_.run();
// Create burst of load -- test code to be removed. Ultimately we'll // Create burst of load -- test code to be removed. Ultimately we'll
// be able to get this kind of functionality by hooking up the sequencer // be able to get this kind of functionality by hooking up the sequencer
@ -229,21 +232,31 @@ public class PianoActivity2 extends Activity {
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
TextView label = (TextView)findViewById(R.id.status); TextView label = (TextView)findViewById(R.id.status);
if (!deviceList.isEmpty()) { Log.i("synth", "USB device count=" + deviceList.size());
UsbDevice device = deviceList.values().iterator().next(); for (UsbDevice device : deviceList.values()) {
//label.setText("ic:" + device.getInterfaceCount()); //label.setText("ic:" + device.getInterfaceCount());
Log.i("synth", "usb name=" + device.toString() + " #if=" + device.getInterfaceCount());
if (device.getInterfaceCount() < 2) {
continue;
}
UsbInterface usbIf = device.getInterface(1); UsbInterface usbIf = device.getInterface(1);
if (usbIf.getInterfaceClass() != 1 || usbIf.getInterfaceSubclass() != 3) {
continue;
}
UsbDeviceConnection connection = usbManager.openDevice(device); UsbDeviceConnection connection = usbManager.openDevice(device);
if (connection != null) { if (connection != null) {
connection.claimInterface(usbIf, true); connection.claimInterface(usbIf, true);
UsbEndpoint endpoint = getInputEndpoint(usbIf); UsbEndpoint endpoint = getInputEndpoint(usbIf);
//label.setText(endpoint.toString()); //label.setText(endpoint.toString());
Log.i("synth", "MIDI keyboard detected, starting USB thread");
startUsbThread(connection, endpoint); startUsbThread(connection, endpoint);
} else { } else {
if (label != null) { if (label != null) {
Log.i("synth", "error opening USB MIDI device");
label.setText("error opening device"); label.setText("error opening device");
} }
} }
break;
} }
} }

@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -230,6 +231,7 @@ public class PianoView extends View {
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction(); int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK; int actionCode = action & MotionEvent.ACTION_MASK;
//Log.i("synth", "actionCode = " + actionCode);
boolean redraw = false; boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) { if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0); int pointerId = event.getPointerId(0);
@ -238,6 +240,8 @@ public class PianoView extends View {
int y = (int)event.getY(); int y = (int)event.getY();
float pressure = event.getPressure(); float pressure = event.getPressure();
redraw |= onTouchDown(pointerId, x, y, pressure); redraw |= onTouchDown(pointerId, x, y, pressure);
} else {
Log.i("synth", "Discarded ACTION_DOWN pointerId=" + pointerId);
} }
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) { } else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
@ -248,6 +252,8 @@ public class PianoView extends View {
int y = (int)event.getY(pointerIndex); int y = (int)event.getY(pointerIndex);
float pressure = event.getPressure(pointerIndex); float pressure = event.getPressure(pointerIndex);
redraw |= onTouchDown(pointerId, x, y, pressure); redraw |= onTouchDown(pointerId, x, y, pressure);
} else {
Log.i("synth", "Discarded ACTION_POINTER_DOWN pointerId=" + pointerId);
} }
} else if (actionCode == MotionEvent.ACTION_MOVE) { } else if (actionCode == MotionEvent.ACTION_MOVE) {
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) { for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
@ -277,7 +283,11 @@ public class PianoView extends View {
} }
} }
if (!found) { if (!found) {
redraw |= onTouchUp(pointerId); boolean thisRedraw = onTouchUp(pointerId);
if (thisRedraw) {
Log.i("synth", "ACTION_UP cleaned up pointerId=" + pointerId);
}
redraw |= thisRedraw;
} }
} }
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) { } else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
@ -296,7 +306,11 @@ public class PianoView extends View {
} }
} }
if (!found) { if (!found) {
redraw |= onTouchUp(pointerId); boolean thisRedraw = onTouchUp(pointerId);
if (thisRedraw) {
Log.i("synth", "ACTION_POINTER_UP cleaned up pointerId=" + pointerId);
}
redraw |= thisRedraw;
} }
} }
} else { } else {

Loading…
Cancel
Save