Android Open Source - BLE-MIDI-for-Android Ble Midi Peripheral Provider






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.peripheral;
//from  w  ww  . j  a  va2 s. c  o  m
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.ParcelUuid;
import android.util.Log;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

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.BleUuidUtils;
import jp.kshoji.blemidi.util.Constants;

/**
 * Represents BLE MIDI Peripheral functions<br />
 * Supported with Android Lollipop or newer.
 *
 * @author K.Shoji
 */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public final class BleMidiPeripheralProvider {
    public static final UUID SERVICE_DEVICE_INFORMATION = BleUuidUtils.fromShortValue(0x180A);
    public static final UUID CHARACTERISTIC_MANUFACTURER_NAME = BleUuidUtils.fromShortValue(0x2A29);
    public static final UUID CHARACTERISTIC_MODEL_NUMBER = BleUuidUtils.fromShortValue(0x2A24);
    public static final UUID SERVICE_BLE_MIDI = UUID.fromString("03b80e5a-ede8-4b33-a751-6ce34ec4c700");
    public static final UUID CHARACTERISTIC_BLE_MIDI = UUID.fromString("7772e5db-3868-4112-a1a9-f2669d106bf3");

    private static final int DEVICE_NAME_MAX_LENGTH = 100;

    final Context context;
    final BluetoothManager bluetoothManager;
    final BluetoothLeAdvertiser bluetoothLeAdvertiser;
    final BluetoothGattService informationGattService;
    final BluetoothGattService midiGattService;
    final BluetoothGattCharacteristic midiCharacteristic;
    BluetoothGattServer gattServer;

    final Map<String, MidiInputDevice> midiInputDevicesMap = new HashMap<>();
    final Map<String, MidiOutputDevice> midiOutputDevicesMap = new HashMap<>();

    OnMidiDeviceAttachedListener midiDeviceAttachedListener;
    OnMidiDeviceDetachedListener midiDeviceDetachedListener;

    String manufacturer = "kshoji.jp";
    String deviceName = "BLE MIDI";

    /**
     * Constructor
     *
     * @param context the context
     */
    public BleMidiPeripheralProvider(final Context context) {
        this.context = context.getApplicationContext();

        bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE);

        final BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

        Log.i(Constants.TAG, "isMultipleAdvertisementSupported:" + bluetoothAdapter.isMultipleAdvertisementSupported());
        if (bluetoothAdapter.isMultipleAdvertisementSupported() == false) {
            throw new UnsupportedOperationException("Bluetooth LE Advertising not supported on this device.");
        }

        bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
        Log.i(Constants.TAG, "bluetoothLeAdvertiser: " + bluetoothLeAdvertiser);
        if (bluetoothLeAdvertiser == null) {
            throw new UnsupportedOperationException("Bluetooth LE Advertising not supported on this device.");
        }

        // Device information service
        informationGattService = new BluetoothGattService(SERVICE_DEVICE_INFORMATION, BluetoothGattService.SERVICE_TYPE_PRIMARY);
        informationGattService.addCharacteristic(new BluetoothGattCharacteristic(CHARACTERISTIC_MANUFACTURER_NAME, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ));
        informationGattService.addCharacteristic(new BluetoothGattCharacteristic(CHARACTERISTIC_MODEL_NUMBER, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ));

        // MIDI service
        midiGattService = new BluetoothGattService(SERVICE_BLE_MIDI, BluetoothGattService.SERVICE_TYPE_PRIMARY);
        midiCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_BLE_MIDI, BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
        midiGattService.addCharacteristic(midiCharacteristic);
    }

    /**
     * Starts advertising
     */
    public void startAdvertising() {
        // register Gatt service to Gatt server
        gattServer = bluetoothManager.openGattServer(context, gattServerCallback);
        if (gattServer == null) {
            Log.i(Constants.TAG, "gattServer is null, check Bluetooth is ON.");
            return;
        }

        gattServer.addService(informationGattService);
        gattServer.addService(midiGattService);

        // set up advertising setting
        AdvertiseSettings advertiseSettings = new AdvertiseSettings.Builder()
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
                .setConnectable(true)
                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
                .build();

        AdvertiseData advertiseData = new AdvertiseData.Builder()
                .setIncludeTxPowerLevel(false)
                .addServiceUuid(ParcelUuid.fromString("03b80e5a-ede8-4b33-a751-6ce34ec4c700")) // Service for BLE MIDI
                .setIncludeDeviceName(true)
                .build();

        bluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertiseData, advertiseData, advertiseCallback);
    }

    /**
     * Stops advertising
     */
    public void stopAdvertising() {
        try {
            bluetoothLeAdvertiser.stopAdvertising(advertiseCallback);
        } catch (IllegalStateException ignored) {
            // BT Adapter is not turned ON
        }

        if (gattServer != null) {
            gattServer.clearServices();
            gattServer.close();
            gattServer = null;
        }
    }

    /**
     * Callback for BLE connection<br />
     * nothing to do.
     */
    final AdvertiseCallback advertiseCallback = new AdvertiseCallback() {};

    /**
     * Disconnect all devices attached
     */
    public void disconnectAllDevices() {
        synchronized (midiInputDevicesMap) {
            for (MidiInputDevice midiInputDevice : midiInputDevicesMap.values()) {
                midiDeviceDetachedListener.onMidiInputDeviceDetached(midiInputDevice);
            }

            midiInputDevicesMap.clear();
        }

        synchronized (midiOutputDevicesMap) {
            for (MidiOutputDevice midiOutputDevice : midiOutputDevicesMap.values()) {
                midiDeviceDetachedListener.onMidiOutputDeviceDetached(midiOutputDevice);
            }

            midiOutputDevicesMap.clear();
        }

        if (gattServer != null) {
            gattServer.clearServices();
            gattServer.close();
            gattServer = null;
        }
    }

    /**
     * BroadcastReceiver for BLE Bonding
     *
     * @author K.Shoji
     */
    public class BondingBroadcastReceiver extends BroadcastReceiver {
        final BluetoothDevice device;

        BondingBroadcastReceiver(BluetoothDevice device) {
            this.device = device;
        }

        @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);

                Log.i(Constants.TAG, "BondingBroadcastReceiver.onReceive state(12:bonded):" + state);

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

                    gattServer.connect(device, true);
                    MidiInputDevice midiInputDevice = new jp.kshoji.blemidi.peripheral.MidiInputDevice(device);
                    MidiOutputDevice midiOutputDevice = new jp.kshoji.blemidi.peripheral.MidiOutputDevice(device, gattServer, midiCharacteristic);

                    String deviceAddress = device.getAddress();

                    synchronized (midiInputDevicesMap) {
                        midiInputDevicesMap.put(deviceAddress, midiInputDevice);
                    }

                    synchronized (midiOutputDevicesMap) {
                        midiOutputDevicesMap.put(deviceAddress, midiOutputDevice);
                    }

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

    /**
     * Callback for BLE data transfer
     */
    final BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {

        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            super.onConnectionStateChange(device, status, newState);

            String deviceAddress = device.getAddress();

            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    // TODO check bond status
                    // TODO create bond
//                    if (device.getBondState() == BluetoothDevice.BOND_NONE) {
//                        Log.i(Constants.TAG, "creating Bond with: " + device.getName());
//                        device.createBond();
//                        device.setPairingConfirmation(true);
//
//                        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//                        context.registerReceiver(new BondingBroadcastReceiver(device), filter);
//                    } else
                    {
                        // connecting to the device
                        gattServer.connect(device, true);
                        MidiInputDevice midiInputDevice = new jp.kshoji.blemidi.peripheral.MidiInputDevice(device);
                        MidiOutputDevice midiOutputDevice = new jp.kshoji.blemidi.peripheral.MidiOutputDevice(device, gattServer, midiCharacteristic);

                        synchronized (midiInputDevicesMap) {
                            midiInputDevicesMap.put(deviceAddress, midiInputDevice);
                        }

                        synchronized (midiOutputDevicesMap) {
                            midiOutputDevicesMap.put(deviceAddress, midiOutputDevice);
                        }

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

                case BluetoothProfile.STATE_DISCONNECTED:
                    if (midiDeviceAttachedListener != null) {


                        synchronized (midiInputDevicesMap) {
                            MidiInputDevice midiInputDevice = midiInputDevicesMap.get(deviceAddress);
                            if (midiInputDevice != null) {
                                midiDeviceDetachedListener.onMidiInputDeviceDetached(midiInputDevice);
                                midiInputDevicesMap.remove(deviceAddress);
                            }
                        }

                        synchronized (midiOutputDevicesMap) {
                            MidiOutputDevice midiOutputDevice = midiOutputDevicesMap.get(deviceAddress);
                            if (midiOutputDevice != null) {
                                midiDeviceDetachedListener.onMidiOutputDeviceDetached(midiOutputDevice);
                                midiOutputDevicesMap.remove(deviceAddress);
                            }
                        }
                    }
                    break;
            }
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);

            if (BleUuidUtils.matches(CHARACTERISTIC_BLE_MIDI, characteristic.getUuid())) {
                // send empty
                gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, new byte[] {});
            } else {
                switch (BleUuidUtils.toShortValue(characteristic.getUuid())) {
                    case 0x2A24:
                        // Model number
                        gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, deviceName.getBytes());
                        break;
                    case 0x2A29:
                        // Manufacturer
                        gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, manufacturer.getBytes());
                        break;
                }
            }
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);

            if (BleUuidUtils.matches(characteristic.getUuid(), CHARACTERISTIC_BLE_MIDI)) {
                MidiInputDevice midiInputDevice = midiInputDevicesMap.get(device.getAddress());

                if (midiInputDevice != null) {
                    ((jp.kshoji.blemidi.peripheral.MidiInputDevice)midiInputDevice).incomingData(value);
                }

                if (responseNeeded) {
                    // return empty data
                    gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value);
                }
            }
        }

        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);

            gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, new byte[] {});
        }
    };

    /**
     * Obtains connected {@link jp.kshoji.blemidi.device.MidiInputDevice}
     * @return the set contains all connected devices
     */
    public Set<MidiInputDevice> getMidiInputDevices() {
        Set<MidiInputDevice> result = new HashSet<>();
        result.addAll(midiInputDevicesMap.values());
        return Collections.unmodifiableSet(result);
    }

    /**
     * Obtains connected {@link jp.kshoji.blemidi.device.MidiOutputDevice}
     * @return the set contains all connected devices
     */
    public Set<MidiOutputDevice> getMidiOutputDevices() {
        Set<MidiOutputDevice> result = new HashSet<>();
        result.addAll(midiOutputDevicesMap.values());
        return Collections.unmodifiableSet(result);
    }

    public void setOnMidiDeviceAttachedListener(OnMidiDeviceAttachedListener midiDeviceAttachedListener) {
        this.midiDeviceAttachedListener = midiDeviceAttachedListener;
    }

    public void setOnMidiDeviceDetachedListener(OnMidiDeviceDetachedListener midiDeviceDetachedListener) {
        this.midiDeviceDetachedListener = midiDeviceDetachedListener;
    }

    /**
     * Set the manufacturer name
     * @param manufacturer the name
     */
    public void setManufacturer(String manufacturer) {
        // length check
        if (manufacturer.getBytes().length > DEVICE_NAME_MAX_LENGTH) {
            // shorten
            byte[] bytes = new byte[DEVICE_NAME_MAX_LENGTH];
            System.arraycopy(manufacturer.getBytes(), 0, bytes, 0, DEVICE_NAME_MAX_LENGTH);
            this.manufacturer = new String(bytes);
        } else {
            this.manufacturer = manufacturer;
        }
    }

    /**
     * Set the device name
     * @param deviceName the name
     */
    public void setDeviceName(String deviceName) {
        // length check
        if (deviceName.getBytes().length > DEVICE_NAME_MAX_LENGTH) {
            // shorten
            byte[] bytes = new byte[DEVICE_NAME_MAX_LENGTH];
            System.arraycopy(deviceName.getBytes(), 0, bytes, 0, DEVICE_NAME_MAX_LENGTH);
            this.deviceName = new String(bytes);
        } else {
            this.deviceName = deviceName;
        }
    }
}




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