void LaunchpadController::deviceMessage(LaunchpadDevice *device, const MidiMessage &msg)
{
    DBG("Launchpad %d received %s", device->index(), msg);

    int deviceIndex = device->index();

    int event = msg.event();
    int channel = msg.channel();
    const auto &data = msg.data();
    int data1 = data[1];
    int data2 = data[2];

    if (event == 0x09 && channel == 0) {
        if ((data1 & 0x0f) == 8) {
            // Button A..H
            int b = data1 >> 4;

            switch (device->rotation()) {
            case LaunchpadDevice::Rotation0: {
                int mappedX = 0x40 + 0x10*(deviceIndex % 2);
                int mappedY = b + 8*(deviceIndex / 2);
                _blm->sendNoteEvent(mappedY, mappedX, data2);
            } break;
            case LaunchpadDevice::Rotation90: {
                int mappedX = 0x60 + 8*(deviceIndex % 2) + (7-b) + 8*(deviceIndex / 2);
                int mappedY = 0;
                _blm->sendNoteEvent(mappedY, mappedX, data2);
            } break;
            case LaunchpadDevice::Rotation180: {
                int mappedX = 0x40 + 0x10*(deviceIndex % 2);
                int mappedY = (7-b) + 8*(deviceIndex / 2);
                _blm->sendNoteEvent(mappedY, mappedX, data2);
            } break;
            case LaunchpadDevice::Rotation270: {
                int mappedX = 0x60 + 8*(deviceIndex % 2) + b + 8*(deviceIndex / 2);
                int mappedY = 0;
                _blm->sendNoteEvent(mappedY, mappedX, data2);
            } break;
            }
        } else if ((data1 & 0x0f) <= 7) {