Android Open Source - BLE-MIDI-for-Android Ble Midi Callback






From Project

Back to project page BLE-MIDI-for-Android.

License

The source code is released under:

Apache License

If you think the Android project BLE-MIDI-for-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package jp.kshoji.blemidi.central;
/*  w  ww  .j av a 2s . c o  m*/
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.util.Log;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import jp.kshoji.blemidi.device.MidiInputDevice;
import jp.kshoji.blemidi.device.MidiOutputDevice;
import jp.kshoji.blemidi.listener.OnMidiDeviceAttachedListener;
import jp.kshoji.blemidi.listener.OnMidiDeviceDetachedListener;
import jp.kshoji.blemidi.util.Constants;

/**
 * BluetoothGattCallback implementation for BLE MIDI devices.
 *
 * @author K.Shoji
 */
public final class BleMidiCallback extends BluetoothGattCallback {
    final Map<String, Set<MidiInputDevice>> midiInputDevicesMap = new HashMap<>();
    final Map<String, Set<MidiOutputDevice>> midiOutputDevicesMap = new HashMap<>();
    final Map<String, BluetoothGatt> deviceAddressGattMap = new HashMap<>();
    final Context context;
    final Handler handler;

    OnMidiDeviceAttachedListener midiDeviceAttachedListener;
    OnMidiDeviceDetachedListener midiDeviceDetachedListener;

    /**
     * Constructor
     *
     * @param context
     */
    public BleMidiCallback(final Context context) {
        super();
        this.context = context;
        this.handler = new Handler(context.getMainLooper());
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        // In this method, the `status` parameter shall be ignored.
        // so, look `newState` parameter only.

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            if (gatt.discoverServices()) {
                // successfully started discovering
            } else {
                // already disconnected
            }
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            String gattDeviceAddress = gatt.getDevice().getAddress();

            Set<String> addressToRemove = new HashSet<>();
            // invoke event listeners
            synchronized (midiInputDevicesMap) {
                Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gattDeviceAddress);
                if (midiInputDevices != null) {
                    for (MidiInputDevice midiInputDevice : midiInputDevices) {// java.lang.NullPointerException: Attempt to invoke interface method 'java.util.Iterator java.util.Set.iterator()' on a null object reference
                        midiInputDevice.close();
                        if (midiDeviceDetachedListener != null) {
                            midiDeviceDetachedListener.onMidiInputDeviceDetached(midiInputDevice);
                        }

                        addressToRemove.add(((jp.kshoji.blemidi.central.MidiInputDevice)midiInputDevice).getDeviceAddress());
                    }
                    midiInputDevices.clear();
                    midiInputDevicesMap.remove(gattDeviceAddress);
                }
            }

            synchronized (midiOutputDevicesMap) {
                Set<MidiOutputDevice> midiOutputDevices = midiOutputDevicesMap.get(gattDeviceAddress);
                if (midiOutputDevices != null) {
                    for (MidiOutputDevice midiOutputDevice : midiOutputDevices) {
                        if (midiDeviceDetachedListener != null) {
                            midiDeviceDetachedListener.onMidiOutputDeviceDetached(midiOutputDevice);
                        }

                        addressToRemove.add(((jp.kshoji.blemidi.central.MidiOutputDevice)midiOutputDevice).getDeviceAddress());
                    }
                    midiOutputDevices.clear();
                    midiOutputDevicesMap.remove(gattDeviceAddress);
                }
            }

            synchronized (deviceAddressGattMap) {
                for (String address : addressToRemove) {
                    deviceAddressGattMap.remove(address);
                }
            }

            gatt.close();
        }
    }

    @SuppressLint("NewApi")
    @Override
    public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);

        if (status != BluetoothGatt.GATT_SUCCESS) {
            return;
        }

        final String gattDeviceAddress = gatt.getDevice().getAddress();

        // find MIDI Input device
        if (midiInputDevicesMap.containsKey(gattDeviceAddress)) {
            synchronized (midiInputDevicesMap) {
                Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gattDeviceAddress);
                for (MidiInputDevice midiInputDevice : midiInputDevices) {
                    midiInputDevice.close();
                }
                midiInputDevicesMap.remove(gattDeviceAddress);
            }
        }

        MidiInputDevice midiInputDevice = null;
        try {
            midiInputDevice = new jp.kshoji.blemidi.central.MidiInputDevice(context, gatt);
        } catch (IllegalArgumentException iae) {
            Log.i(Constants.TAG, iae.getMessage());
        }
        if (midiInputDevice != null) {
            synchronized (midiInputDevicesMap) {
                Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gattDeviceAddress);
                if (midiInputDevices == null) {
                    midiInputDevices = new HashSet<>();
                    midiInputDevicesMap.put(gattDeviceAddress, midiInputDevices);
                }

                midiInputDevices.add(midiInputDevice);
            }

            if (midiDeviceAttachedListener != null) {
                midiDeviceAttachedListener.onMidiInputDeviceAttached(midiInputDevice);
            }
        }

        // find MIDI Output device
        if (midiOutputDevicesMap.containsKey(gattDeviceAddress)) {
            synchronized (midiOutputDevicesMap) {
                midiOutputDevicesMap.remove(gattDeviceAddress);
            }
        }

        MidiOutputDevice midiOutputDevice = null;
        try {
            midiOutputDevice = new jp.kshoji.blemidi.central.MidiOutputDevice(context, gatt);
        } catch (IllegalArgumentException iae) {
            Log.i(Constants.TAG, iae.getMessage());
        }
        if (midiOutputDevice != null) {
            synchronized (midiOutputDevicesMap) {
                Set<MidiOutputDevice> midiOutputDevices = midiOutputDevicesMap.get(gattDeviceAddress);
                if (midiOutputDevices == null) {
                    midiOutputDevices = new HashSet<MidiOutputDevice>();
                    midiOutputDevicesMap.put(gattDeviceAddress, midiOutputDevices);
                }

                midiOutputDevices.add(midiOutputDevice);
            }

            if (midiDeviceAttachedListener != null) {
                midiDeviceAttachedListener.onMidiOutputDeviceAttached(midiOutputDevice);
            }
        }

        if (midiInputDevice != null || midiOutputDevice != null) {
            synchronized (deviceAddressGattMap) {
                deviceAddressGattMap.put(gattDeviceAddress, gatt);
            }

            // Create bond and configure Gatt, if this is BLE MIDI device
            BluetoothDevice bluetoothDevice = gatt.getDevice();
            if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE) {
                bluetoothDevice.createBond();
                bluetoothDevice.setPairingConfirmation(true);

                IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
                context.registerReceiver(new BondingBroadcastReceiver(midiInputDevice, midiOutputDevice), filter);
            } else {
                if (midiInputDevice != null) {
                    ((jp.kshoji.blemidi.central.MidiInputDevice)midiInputDevice).configureAsCentralDevice();
                }
                if (midiOutputDevice != null) {
                    ((jp.kshoji.blemidi.central.MidiOutputDevice)midiOutputDevice).configureAsCentralDevice();
                }
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                // default is 23: maximum data length is 20 bytes
                // max is 512: maximum data length is 509 bytes
                gatt.requestMtu(23); // default value

                // Set the connection priority to high(for low latency)
                gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
            }
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);

        Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gatt.getDevice().getAddress());
        for (MidiInputDevice midiInputDevice : midiInputDevices) {
            ((jp.kshoji.blemidi.central.MidiInputDevice)midiInputDevice).incomingData(characteristic.getValue());
        }
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);

        if (descriptor != null) {
            if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, descriptor.getValue())) {
                gatt.setCharacteristicNotification(descriptor.getCharacteristic(), true);
            }
        }
    }

    /**
     * Disconnect the specified device
     * @param midiInputDevice the device
     */
    void disconnectDevice(MidiInputDevice midiInputDevice) {
        synchronized (deviceAddressGattMap) {
            String deviceAddress = ((jp.kshoji.blemidi.central.MidiInputDevice) midiInputDevice).getDeviceAddress();
            BluetoothGatt bluetoothGatt = deviceAddressGattMap.get(deviceAddress);
            if (bluetoothGatt != null) {
                bluetoothGatt.close();
                deviceAddressGattMap.remove(deviceAddress);
            }
        }
    }

    /**
     * Disconnect the specified device
     * @param midiOutputDevice the device
     */
    void disconnectDevice(MidiOutputDevice midiOutputDevice) {
        synchronized (deviceAddressGattMap) {
            String deviceAddress = ((jp.kshoji.blemidi.central.MidiOutputDevice) midiOutputDevice).getDeviceAddress();
            BluetoothGatt bluetoothGatt = deviceAddressGattMap.get(deviceAddress);
            if (bluetoothGatt != null) {
                bluetoothGatt.close();
                deviceAddressGattMap.remove(deviceAddress);
            }
        }
    }

    /**
     * BroadcastReceiver for BLE Bonding
     *
     * @author K.Shoji
     */
    public static class BondingBroadcastReceiver extends BroadcastReceiver {
        final MidiInputDevice midiInputDevice;
        final MidiOutputDevice midiOutputDevice;

        BondingBroadcastReceiver(MidiInputDevice midiInputDevice, MidiOutputDevice midiOutputDevice) {
            this.midiInputDevice = midiInputDevice;
            this.midiOutputDevice = midiOutputDevice;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();

            if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);

                if (state == BluetoothDevice.BOND_BONDED) {
                    // successfully bonded
                    context.unregisterReceiver(this);

                    ((jp.kshoji.blemidi.central.MidiInputDevice)midiInputDevice).configureAsCentralDevice();
                    ((jp.kshoji.blemidi.central.MidiOutputDevice)midiOutputDevice).configureAsCentralDevice();
                }
            }
        }
    }

    /**
     * Obtains connected input devices
     * @return Set of {@link jp.kshoji.blemidi.device.MidiInputDevice}
     */
    public Set<MidiInputDevice> getMidiInputDevices() {
        Collection<Set<MidiInputDevice>> values = midiInputDevicesMap.values();

        Set<MidiInputDevice> result = new HashSet<MidiInputDevice>();
        for (Set<MidiInputDevice> value: values) {
            result.addAll(value);
        }

        return Collections.unmodifiableSet(result);
    }

    /**
     * Obtains connected output devices
     * @return Set of {@link jp.kshoji.blemidi.device.MidiOutputDevice}
     */
    public Set<MidiOutputDevice> getMidiOutputDevices() {
        Collection<Set<MidiOutputDevice>> values = midiOutputDevicesMap.values();

        Set<MidiOutputDevice> result = new HashSet<>();
        for (Set<MidiOutputDevice> value: values) {
            result.addAll(value);
        }

        return Collections.unmodifiableSet(result);
    }

    /**
     * Set the event listener to listen the Device attachment event
     * @param midiDeviceAttachedListener the listener
     */
    public void setOnMidiDeviceAttachedListener(OnMidiDeviceAttachedListener midiDeviceAttachedListener) {
        this.midiDeviceAttachedListener = midiDeviceAttachedListener;
    }

    /**
     * Set the event listener to listen the Device attachment event
     * @param midiDeviceDetachedListener the listener
     */
    public void setOnMidiDeviceDetachedListener(OnMidiDeviceDetachedListener midiDeviceDetachedListener) {
        this.midiDeviceDetachedListener = midiDeviceDetachedListener;
    }
}




Java Source Code List

jp.kshoji.blemidi.central.BleMidiCallback.java
jp.kshoji.blemidi.central.BleMidiCentralProvider.java
jp.kshoji.blemidi.central.MidiInputDevice.java
jp.kshoji.blemidi.central.MidiOutputDevice.java
jp.kshoji.blemidi.device.MidiInputDevice.java
jp.kshoji.blemidi.device.MidiOutputDevice.java
jp.kshoji.blemidi.listener.OnMidiDeviceAttachedListener.java
jp.kshoji.blemidi.listener.OnMidiDeviceDetachedListener.java
jp.kshoji.blemidi.listener.OnMidiInputEventListener.java
jp.kshoji.blemidi.listener.OnMidiScanStatusListener.java
jp.kshoji.blemidi.peripheral.BleMidiPeripheralProvider.java
jp.kshoji.blemidi.peripheral.MidiInputDevice.java
jp.kshoji.blemidi.peripheral.MidiOutputDevice.java
jp.kshoji.blemidi.sample.CentralActivity.java
jp.kshoji.blemidi.sample.PeripheralActivity.java
jp.kshoji.blemidi.sample.util.SoundMaker.java
jp.kshoji.blemidi.sample.util.Tone.java
jp.kshoji.blemidi.util.BleMidiDeviceUtils.java
jp.kshoji.blemidi.util.BleUtils.java
jp.kshoji.blemidi.util.BleUuidUtils.java
jp.kshoji.blemidi.util.Constants.java
jp.kshoji.blemidi.util.MidiParser.java
jp.kshoji.blemidi.util.ReusableByteArrayOutputStream.java