diff --git a/android/.classpath b/android/.classpath
index a607c7f..bea3b48 100644
--- a/android/.classpath
+++ b/android/.classpath
@@ -8,5 +8,6 @@
+
diff --git a/android/src/com/levien/synthesizer/android/service/SynthesizerService.java b/android/src/com/levien/synthesizer/android/service/SynthesizerService.java
index 9e508b5..b9c85dc 100755
--- a/android/src/com/levien/synthesizer/android/service/SynthesizerService.java
+++ b/android/src/com/levien/synthesizer/android/service/SynthesizerService.java
@@ -19,6 +19,7 @@ package com.levien.synthesizer.android.service;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import android.annotation.TargetApi;
@@ -69,6 +70,7 @@ public class SynthesizerService extends Service {
* Run when the Service is first created.
*/
@Override
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public void onCreate() {
Log.d("synth", "service onCreate");
if (androidGlue_ == null) {
@@ -98,12 +100,10 @@ public class SynthesizerService extends Service {
}
}
androidGlue_.setPlayState(true);
- if (usbDevice_ != null && usbMidiConnection_ == null) {
- connectUsbMidi(usbDevice_);
- }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(usbReceiver_, filter);
+ scanUsbMidi();
}
}
@@ -112,6 +112,7 @@ public class SynthesizerService extends Service {
*/
@Override
public void onDestroy() {
+ Log.d("synth", "service onDestroy");
androidGlue_.setPlayState(false);
setMidiInterface(null, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
@@ -144,6 +145,27 @@ public class SynthesizerService extends Service {
return patchNames_;
}
+ public boolean connectUsbMidi(UsbDevice device) {
+ usbDeviceNeedsPermission_ = null;
+ if (usbDevice_ == device) {
+ return device != null;
+ }
+ UsbInterface intf = device != null ? UsbMidiDevice.findMidiInterface(device) : null;
+ boolean success = setMidiInterface(device, intf);
+ usbDevice_ = success ? device : null;
+ return success;
+ }
+
+ /**
+ * Call to find out whether there is a device that has been scanned
+ * but not connected to because of missing permission.
+ *
+ * @return Device that needs permission, or null if none.
+ */
+ public UsbDevice usbDeviceNeedsPermission() {
+ return usbDeviceNeedsPermission_;
+ }
+
class AudioParams {
AudioParams(int sr, int bs) {
confident = false;
@@ -159,7 +181,7 @@ public class SynthesizerService extends Service {
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- void getJbMr1Params(AudioParams params) {
+ private void getJbMr1Params(AudioParams params) {
AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
String sr = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
String bs = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
@@ -180,6 +202,7 @@ public class SynthesizerService extends Service {
// based on experimentation just closing seems more robust
//usbMidiConnection_.releaseInterface(usbMidiInterface_);
}
+ Log.d("synth", "closing connection " + usbMidiConnection_);
usbMidiConnection_.close();
usbMidiConnection_ = null;
}
@@ -205,14 +228,24 @@ public class SynthesizerService extends Service {
return false;
}
- // Handles both connect and disconnect actions
- public boolean connectUsbMidi(UsbDevice device) {
- UsbInterface intf = device != null ? UsbMidiDevice.findMidiInterface(device) : null;
- Log.d("synth", "connecting USB");
- boolean success = setMidiInterface(device, intf);
- Log.d("synth", "connecting USB done");
- usbDevice_ = success ? device : null;
- return success;
+ // scan for MIDI devices on the USB bus
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
+ private void scanUsbMidi() {
+ UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ HashMap deviceList = usbManager.getDeviceList();
+ Log.i("synth", "USB device count=" + deviceList.size());
+ for (UsbDevice device : deviceList.values()) {
+ UsbInterface intf = UsbMidiDevice.findMidiInterface(device);
+ if (intf != null) {
+ if (usbManager.hasPermission(device)) {
+ if (connectUsbMidi(device)) {
+ break;
+ }
+ } else {
+ usbDeviceNeedsPermission_ = device;
+ }
+ }
+ }
}
private final BroadcastReceiver usbReceiver_ = new BroadcastReceiver() {
@@ -237,8 +270,9 @@ public class SynthesizerService extends Service {
private static List patchNames_;
// State for USB MIDI keyboard connection
- private static UsbDevice usbDevice_;
+ private UsbDevice usbDevice_;
private UsbDeviceConnection usbMidiConnection_;
private UsbMidiDevice usbMidiDevice_;
private UsbInterface usbMidiInterface_;
+ private UsbDevice usbDeviceNeedsPermission_;
}
diff --git a/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java b/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
index d98098a..5434aaa 100644
--- a/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
+++ b/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
@@ -145,32 +145,6 @@ public class PianoActivity2 extends Activity {
connectUsbFromIntent(intent);
}
- // scan for MIDI devices on the USB bus
- @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
- private void scanUsbMidi() {
- UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
- HashMap deviceList = usbManager.getDeviceList();
- Log.i("synth", "USB device count=" + deviceList.size());
- for (UsbDevice device : deviceList.values()) {
- UsbInterface intf = UsbMidiDevice.findMidiInterface(device);
- if (intf != null) {
- if (usbManager.hasPermission(device)) {
- if (connectUsbMidi(device)) {
- break;
- }
- } else {
- synchronized (usbReceiver_) {
- if (!permissionRequestPending_) {
- permissionRequestPending_ = true;
- usbManager.requestPermission(device, permissionIntent_);
- }
- }
- break; // Don't try to connect anything else after perms dialog up
- }
- }
- }
- }
-
boolean connectUsbMidi(UsbDevice device) {
if (synthesizerService_ != null) {
return synthesizerService_.connectUsbMidi(device);
@@ -196,9 +170,7 @@ public class PianoActivity2 extends Activity {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
registerReceiver(usbReceiver_, filter);
- if (!connectUsbFromIntent(intent)) {
- scanUsbMidi();
- }
+ connectUsbFromIntent(intent);
}
private static final String ACTION_USB_PERMISSION = "com.levien.synthesizer.USB_PERSMISSION";
@@ -222,10 +194,13 @@ public class PianoActivity2 extends Activity {
public void sendMidiBytes(byte[] buf) {
// TODO: in future we'll want to reflect MIDI to UI (knobs turn, keys press)
- synthesizerService_.sendRawMidi(buf);
+ if (synthesizerService_ != null) {
+ synthesizerService_.sendRawMidi(buf);
+ }
}
private ServiceConnection synthesizerConnection_ = new ServiceConnection() {
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public void onServiceConnected(ComponentName className, IBinder service) {
SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service;
synthesizerService_ = binder.getService();
@@ -238,6 +213,17 @@ public class PianoActivity2 extends Activity {
if (usbDevicePending_ != null) {
synthesizerService_.connectUsbMidi(usbDevicePending_);
usbDevicePending_ = null;
+ } else {
+ UsbDevice device = synthesizerService_.usbDeviceNeedsPermission();
+ if (device != null) {
+ synchronized (usbReceiver_) {
+ if (!permissionRequestPending_) {
+ permissionRequestPending_ = true;
+ UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ usbManager.requestPermission(device, permissionIntent_);
+ }
+ }
+ }
}
}
public void onServiceDisconnected(ComponentName className) {
diff --git a/android/src/com/levien/synthesizer/android/usb/UsbMidiDevice.java b/android/src/com/levien/synthesizer/android/usb/UsbMidiDevice.java
index 25e6d34..a52e35c 100644
--- a/android/src/com/levien/synthesizer/android/usb/UsbMidiDevice.java
+++ b/android/src/com/levien/synthesizer/android/usb/UsbMidiDevice.java
@@ -95,10 +95,14 @@ public class UsbMidiDevice {
return;
}
}
- final int TIMEOUT = 0;
+ // Using a timeout here is a hacky workaround to shut down the
+ // thread. If we could call releaseInterface, that would cause
+ // the bulkTransfer to return immediately, but that causes other
+ // problems.
+ final int TIMEOUT = 1000;
int nBytes = mDeviceConnection.bulkTransfer(mEndpoint, buf, buf.length, TIMEOUT);
if (nBytes < 0) {
- Log.e("synth", "bulkTransfer error " + nBytes);
+ //Log.e("synth", "bulkTransfer error " + nBytes);
// break;
}
for (int i = 0; i < nBytes; i += 4) {
@@ -113,7 +117,7 @@ public class UsbMidiDevice {
if (payloadBytes > 0) {
byte[] newBuf = new byte[payloadBytes];
System.arraycopy(buf, i + 1, newBuf, 0, payloadBytes);
- Log.d("synth", "sending midi");
+ //Log.d("synth", "sending midi");
mReceiver.sendMidi(newBuf);
}
}