function LPD8RK() {}
LPD8RK.debug=false;

//set defaults - don't change these
LPD8RK.oldHotcueBank=0;//storage for the last active hotcue bank - used for bank indicator lights
LPD8RK.LEDinterval=300;//interval in milliseconds for LED update
LPD8RK.hotcueClear=false;//if true, hitting hotcue button clears it.
LPD8RK.loopbuttonDown=false;//if true, special loop functions with knob modifiers are enabled.
LPD8RK.looplen=0x40;//default position of looplength dial - active when loop buttons are held down
LPD8RK.looptype="beatloop";//default looptype - beatloop if <64, beatlooproll otherwise
LPD8RK.loopmove=0.0125;//default loop move distance in seconds
LPD8RK.doreloop=true;//don't change this
LPD8RK.beatjumpstep=1;//default beat jump step in beats
LPD8RK.vol=new Array();//array of volumes to return to after mute - don't change
LPD8RK.hotcueBank=1;//default hotcue bank to show on PAD setting, adjustable by knob


//array of valid LED addresses
LPD8RK.validLEDS = new Array();
LPD8RK.validLEDS[0x90]={0x2b : true,0x27 : true,0x2a : true,0x26 : true,0x29 : true,0x25 : true,0x28 : true,0x24 : true};
LPD8RK.validLEDS[0x91]={0x2c : true,0x27 : true,0x2e : true,0x2a : true,0x26 : true,0x24 : true,0x25 : true,0x23 : true};
LPD8RK.validLEDS[0x92]={0x48 : true,0x41 : true,0x47 : true,0x40 : true,0x45 : true,0x3e : true,0x43 : true,0x3c : true};
LPD8RK.validLEDS[0x93]={0x30 : true,0x29 : true,0x2f : true,0x28 : true,0x2d : true,0x26 : true,0x2b : true,0x24 : true};
LPD8RK.validLEDS[0xb0]={0x01 : true,0x09 : true,0x04 : true,0x08 : true,0x03 : true,0x06 : true,0x03 : true,0x05 : true};
LPD8RK.validLEDS[0xb1]={0x01 : true,0x09 : true,0x04 : true,0x08 : true,0x03 : true,0x06 : true,0x03 : true,0x05 : true};
LPD8RK.validLEDS[0xb2]={0x01 : true,0x09 : true,0x04 : true,0x08 : true,0x03 : true,0x06 : true,0x03 : true,0x05 : true};
LPD8RK.validLEDS[0xb3]={0x01 : true,0x09 : true,0x04 : true,0x08 : true,0x03 : true,0x06 : true,0x03 : true,0x05 : true};


//map hotcue pads and leds
LPD8RK.hotcues = new Array();
//preset 1 (group, cuenum, status, ctrl)
//initialize
LPD8RK.hotcues["902b"] = new Array();
LPD8RK.hotcues["9027"] = new Array();
LPD8RK.hotcues["902a"] = new Array();
LPD8RK.hotcues["9026"] = new Array();
LPD8RK.hotcues["9029"] = new Array();
LPD8RK.hotcues["9025"] = new Array();
//bank 1
LPD8RK.hotcues["902b"][1] = new Array("[Channel1]", "1", 0x90, 0x2b);
LPD8RK.hotcues["9027"][1] = new Array("[Channel1]", "2", 0x90, 0x27);
LPD8RK.hotcues["902a"][1] = new Array("[Channel1]", "3", 0x90, 0x2a);
LPD8RK.hotcues["9026"][1] = new Array("[Channel1]", "4", 0x90, 0x26);
LPD8RK.hotcues["9029"][1] = new Array("[Channel1]", "5", 0x90, 0x29);
LPD8RK.hotcues["9025"][1] = new Array("[Channel1]", "6", 0x90, 0x25);
//bank 2
LPD8RK.hotcues["902b"][2] = new Array("[Channel1]", "7", 0x90, 0x2b);
LPD8RK.hotcues["9027"][2] = new Array("[Channel1]", "8", 0x90, 0x27);
LPD8RK.hotcues["902a"][2] = new Array("[Channel1]", "9", 0x90, 0x2a);
LPD8RK.hotcues["9026"][2] = new Array("[Channel1]", "10", 0x90, 0x26);
LPD8RK.hotcues["9029"][2] = new Array("[Channel1]", "11", 0x90, 0x29);
LPD8RK.hotcues["9025"][2] = new Array("[Channel1]", "12", 0x90, 0x25);
//bank 3
LPD8RK.hotcues["902b"][3] = new Array("[Channel1]", "13", 0x90, 0x2b);
LPD8RK.hotcues["9027"][3] = new Array("[Channel1]", "14", 0x90, 0x27);
LPD8RK.hotcues["902a"][3] = new Array("[Channel1]", "15", 0x90, 0x2a);
LPD8RK.hotcues["9026"][3] = new Array("[Channel1]", "16", 0x90, 0x26);
LPD8RK.hotcues["9029"][3] = new Array("[Channel1]", "17", 0x90, 0x29);
LPD8RK.hotcues["9025"][3] = new Array("[Channel1]", "18", 0x90, 0x25);
//bank 4
LPD8RK.hotcues["902b"][4] = new Array("[Channel1]", "19", 0x90, 0x2b);
LPD8RK.hotcues["9027"][4] = new Array("[Channel1]", "20", 0x90, 0x27);
LPD8RK.hotcues["902a"][4] = new Array("[Channel1]", "21", 0x90, 0x2a);
LPD8RK.hotcues["9026"][4] = new Array("[Channel1]", "22", 0x90, 0x26);
LPD8RK.hotcues["9029"][4] = new Array("[Channel1]", "23", 0x90, 0x29);
LPD8RK.hotcues["9025"][4] = new Array("[Channel1]", "24", 0x90, 0x25);
//bank 5
LPD8RK.hotcues["902b"][5] = new Array("[Channel1]", "25", 0x90, 0x2b);
LPD8RK.hotcues["9027"][5] = new Array("[Channel1]", "26", 0x90, 0x27);
LPD8RK.hotcues["902a"][5] = new Array("[Channel1]", "27", 0x90, 0x2a);
LPD8RK.hotcues["9026"][5] = new Array("[Channel1]", "28", 0x90, 0x26);
LPD8RK.hotcues["9029"][5] = new Array("[Channel1]", "29", 0x90, 0x29);
LPD8RK.hotcues["9025"][5] = new Array("[Channel1]", "30", 0x90, 0x25);

//##################
//preset 2 (group, cuenum, status, ctrl)
//initialize
LPD8RK.hotcues["912c"] = new Array();
LPD8RK.hotcues["9127"] = new Array();
LPD8RK.hotcues["912e"] = new Array();
LPD8RK.hotcues["912a"] = new Array();
LPD8RK.hotcues["9126"] = new Array();
LPD8RK.hotcues["9124"] = new Array();
//bank 1
LPD8RK.hotcues["912c"][1] = new Array("[Channel2]", "1", 0x91, 0x2c);
LPD8RK.hotcues["9127"][1] = new Array("[Channel2]", "2", 0x91, 0x27);
LPD8RK.hotcues["912e"][1] = new Array("[Channel2]", "3", 0x91, 0x2e);
LPD8RK.hotcues["912a"][1] = new Array("[Channel2]", "4", 0x91, 0x2a);
LPD8RK.hotcues["9126"][1] = new Array("[Channel2]", "5", 0x91, 0x26);
LPD8RK.hotcues["9124"][1] = new Array("[Channel2]", "6", 0x91, 0x24);
//bank 2
LPD8RK.hotcues["912c"][2] = new Array("[Channel2]", "7", 0x91, 0x2c);
LPD8RK.hotcues["9127"][2] = new Array("[Channel2]", "8", 0x91, 0x27);
LPD8RK.hotcues["912e"][2] = new Array("[Channel2]", "9", 0x91, 0x2e);
LPD8RK.hotcues["912a"][2] = new Array("[Channel2]", "10", 0x91, 0x2a);
LPD8RK.hotcues["9126"][2] = new Array("[Channel2]", "11", 0x91, 0x26);
LPD8RK.hotcues["9124"][2] = new Array("[Channel2]", "12", 0x91, 0x24);
//bank 3
LPD8RK.hotcues["912c"][3] = new Array("[Channel2]", "13", 0x91, 0x2c);
LPD8RK.hotcues["9127"][3] = new Array("[Channel2]", "14", 0x91, 0x27);
LPD8RK.hotcues["912e"][3] = new Array("[Channel2]", "15", 0x91, 0x2e);
LPD8RK.hotcues["912a"][3] = new Array("[Channel2]", "16", 0x91, 0x2a);
LPD8RK.hotcues["9126"][3] = new Array("[Channel2]", "17", 0x91, 0x26);
LPD8RK.hotcues["9124"][3] = new Array("[Channel2]", "18", 0x91, 0x24);
//bank 4
LPD8RK.hotcues["912c"][4] = new Array("[Channel2]", "19", 0x91, 0x2c);
LPD8RK.hotcues["9127"][4] = new Array("[Channel2]", "20", 0x91, 0x27);
LPD8RK.hotcues["912e"][4] = new Array("[Channel2]", "21", 0x91, 0x2e);
LPD8RK.hotcues["912a"][4] = new Array("[Channel2]", "22", 0x91, 0x2a);
LPD8RK.hotcues["9126"][4] = new Array("[Channel2]", "23", 0x91, 0x26);
LPD8RK.hotcues["9124"][4] = new Array("[Channel2]", "24", 0x91, 0x24);
//bank 5
LPD8RK.hotcues["912c"][5] = new Array("[Channel2]", "25", 0x91, 0x2c);
LPD8RK.hotcues["9127"][5] = new Array("[Channel2]", "26", 0x91, 0x27);
LPD8RK.hotcues["912e"][5] = new Array("[Channel2]", "27", 0x91, 0x2e);
LPD8RK.hotcues["912a"][5] = new Array("[Channel2]", "28", 0x91, 0x2a);
LPD8RK.hotcues["9126"][5] = new Array("[Channel2]", "29", 0x91, 0x26);
LPD8RK.hotcues["9124"][5] = new Array("[Channel2]", "30", 0x91, 0x24);

//##################
//preset 3 (group, cuenum, status, ctrl)
//initialize
LPD8RK.hotcues["9248"] = new Array();
LPD8RK.hotcues["9241"] = new Array();
LPD8RK.hotcues["9247"] = new Array();
LPD8RK.hotcues["9240"] = new Array();
LPD8RK.hotcues["9245"] = new Array();
LPD8RK.hotcues["923e"] = new Array();
//bank 1
LPD8RK.hotcues["9248"][1] = new Array("[Sampler1]", "1", 0x92, 0x48);
LPD8RK.hotcues["9241"][1] = new Array("[Sampler1]", "2", 0x92, 0x41);
LPD8RK.hotcues["9247"][1] = new Array("[Sampler1]", "3", 0x92, 0x47);
LPD8RK.hotcues["9240"][1] = new Array("[Sampler1]", "4", 0x92, 0x40);
LPD8RK.hotcues["9245"][1] = new Array("[Sampler1]", "5", 0x92, 0x45);
LPD8RK.hotcues["923e"][1] = new Array("[Sampler1]", "6", 0x92, 0x3e);
//bank 2
LPD8RK.hotcues["9248"][2] = new Array("[Sampler1]", "7", 0x92, 0x48);
LPD8RK.hotcues["9241"][2] = new Array("[Sampler1]", "8", 0x92, 0x41);
LPD8RK.hotcues["9247"][2] = new Array("[Sampler1]", "9", 0x92, 0x47);
LPD8RK.hotcues["9240"][2] = new Array("[Sampler1]", "10", 0x92, 0x40);
LPD8RK.hotcues["9245"][2] = new Array("[Sampler1]", "11", 0x92, 0x45);
LPD8RK.hotcues["923e"][2] = new Array("[Sampler1]", "12", 0x92, 0x3e);
//bank 3
LPD8RK.hotcues["9248"][3] = new Array("[Sampler1]", "13", 0x92, 0x48);
LPD8RK.hotcues["9241"][3] = new Array("[Sampler1]", "14", 0x92, 0x41);
LPD8RK.hotcues["9247"][3] = new Array("[Sampler1]", "15", 0x92, 0x47);
LPD8RK.hotcues["9240"][3] = new Array("[Sampler1]", "16", 0x92, 0x40);
LPD8RK.hotcues["9245"][3] = new Array("[Sampler1]", "17", 0x92, 0x45);
LPD8RK.hotcues["923e"][3] = new Array("[Sampler1]", "18", 0x92, 0x3e);
//bank 4
LPD8RK.hotcues["9248"][4] = new Array("[Sampler1]", "19", 0x92, 0x48);
LPD8RK.hotcues["9241"][4] = new Array("[Sampler1]", "20", 0x92, 0x41);
LPD8RK.hotcues["9247"][4] = new Array("[Sampler1]", "21", 0x92, 0x47);
LPD8RK.hotcues["9240"][4] = new Array("[Sampler1]", "22", 0x92, 0x40);
LPD8RK.hotcues["9245"][4] = new Array("[Sampler1]", "23", 0x92, 0x45);
LPD8RK.hotcues["923e"][4] = new Array("[Sampler1]", "24", 0x92, 0x3e);
//bank 5
LPD8RK.hotcues["9248"][5] = new Array("[Sampler1]", "25", 0x92, 0x48);
LPD8RK.hotcues["9241"][5] = new Array("[Sampler1]", "26", 0x92, 0x41);
LPD8RK.hotcues["9247"][5] = new Array("[Sampler1]", "27", 0x92, 0x47);
LPD8RK.hotcues["9240"][5] = new Array("[Sampler1]", "28", 0x92, 0x40);
LPD8RK.hotcues["9245"][5] = new Array("[Sampler1]", "29", 0x92, 0x45);
LPD8RK.hotcues["923e"][5] = new Array("[Sampler1]", "30", 0x92, 0x3e);

//##################
//preset 4 (group, cuenum, status, ctrl)
//initialize
LPD8RK.hotcues["9330"] = new Array();
LPD8RK.hotcues["9329"] = new Array();
LPD8RK.hotcues["932f"] = new Array();
LPD8RK.hotcues["9328"] = new Array();
LPD8RK.hotcues["932d"] = new Array();
LPD8RK.hotcues["9326"] = new Array();
//bank 1
LPD8RK.hotcues["9330"][1] = new Array("[Sampler2]", "1", 0x93, 0x30);
LPD8RK.hotcues["9329"][1] = new Array("[Sampler2]", "2", 0x93, 0x29);
LPD8RK.hotcues["932f"][1] = new Array("[Sampler2]", "3", 0x93, 0x2f);
LPD8RK.hotcues["9328"][1] = new Array("[Sampler2]", "4", 0x93, 0x28);
LPD8RK.hotcues["932d"][1] = new Array("[Sampler2]", "5", 0x93, 0x2d);
LPD8RK.hotcues["9326"][1] = new Array("[Sampler2]", "6", 0x93, 0x26);
//bank 2
LPD8RK.hotcues["9330"][2] = new Array("[Sampler2]", "7", 0x93, 0x30);
LPD8RK.hotcues["9329"][2] = new Array("[Sampler2]", "8", 0x93, 0x29);
LPD8RK.hotcues["932f"][2] = new Array("[Sampler2]", "9", 0x93, 0x2f);
LPD8RK.hotcues["9328"][2] = new Array("[Sampler2]", "10", 0x93, 0x28);
LPD8RK.hotcues["932d"][2] = new Array("[Sampler2]", "11", 0x93, 0x2d);
LPD8RK.hotcues["9326"][2] = new Array("[Sampler2]", "12", 0x93, 0x26);
//bank 3
LPD8RK.hotcues["9330"][3] = new Array("[Sampler2]", "13", 0x93, 0x30);
LPD8RK.hotcues["9329"][3] = new Array("[Sampler2]", "14", 0x93, 0x29);
LPD8RK.hotcues["932f"][3] = new Array("[Sampler2]", "15", 0x93, 0x2f);
LPD8RK.hotcues["9328"][3] = new Array("[Sampler2]", "16", 0x93, 0x28);
LPD8RK.hotcues["932d"][3] = new Array("[Sampler2]", "17", 0x93, 0x2d);
LPD8RK.hotcues["9326"][3] = new Array("[Sampler2]", "18", 0x93, 0x26);
//bank 4
LPD8RK.hotcues["9330"][4] = new Array("[Sampler2]", "19", 0x93, 0x30);
LPD8RK.hotcues["9329"][4] = new Array("[Sampler2]", "20", 0x93, 0x29);
LPD8RK.hotcues["932f"][4] = new Array("[Sampler2]", "21", 0x93, 0x2f);
LPD8RK.hotcues["9328"][4] = new Array("[Sampler2]", "22", 0x93, 0x28);
LPD8RK.hotcues["932d"][4] = new Array("[Sampler2]", "23", 0x93, 0x2d);
LPD8RK.hotcues["9326"][4] = new Array("[Sampler2]", "24", 0x93, 0x26);
//bank 5
LPD8RK.hotcues["9330"][5] = new Array("[Sampler2]", "25", 0x93, 0x30);
LPD8RK.hotcues["9329"][5] = new Array("[Sampler2]", "26", 0x93, 0x29);
LPD8RK.hotcues["932f"][5] = new Array("[Sampler2]", "27", 0x93, 0x2f);
LPD8RK.hotcues["9328"][5] = new Array("[Sampler2]", "28", 0x93, 0x28);
LPD8RK.hotcues["932d"][5] = new Array("[Sampler2]", "29", 0x93, 0x2d);
LPD8RK.hotcues["9326"][5] = new Array("[Sampler2]", "30", 0x93, 0x26);




//map hotcue loop and leds
LPD8RK.loops = new Array();
//preset 1 (group, beatloop_len, status, ctrl)
LPD8RK.loops["b09"] = new Array("[Channel1]", "1", 0xB0, 0x09);
LPD8RK.loops["b04"] = new Array("[Channel1]", "2", 0xB0, 0x04);
LPD8RK.loops["b08"] = new Array("[Channel1]", "4", 0xB0, 0x08);
LPD8RK.loops["b03"] = new Array("[Channel1]", "8", 0xB0, 0x03);

//preset 2 (group, beatloop_len, status, ctrl)
LPD8RK.loops["b19"] = new Array("[Channel2]", "1", 0xB1, 0x09);
LPD8RK.loops["b14"] = new Array("[Channel2]", "2", 0xB1, 0x04);
LPD8RK.loops["b18"] = new Array("[Channel2]", "4", 0xB1, 0x08);
LPD8RK.loops["b13"] = new Array("[Channel2]", "8", 0xB1, 0x03);

//preset 3 (group, beatloop_len, status, ctrl)
LPD8RK.loops["b29"] = new Array("[Sampler1]", "1", 0xB2, 0x09);
LPD8RK.loops["b24"] = new Array("[Sampler1]", "2", 0xB2, 0x04);
LPD8RK.loops["b28"] = new Array("[Sampler1]", "4", 0xB2, 0x08);
LPD8RK.loops["b23"] = new Array("[Sampler1]", "8", 0xB2, 0x03);

//preset 4 (group, beatloop_len, status, ctrl)
LPD8RK.loops["b39"] = new Array("[Sampler2]", "1", 0xB3, 0x09);
LPD8RK.loops["b34"] = new Array("[Sampler2]", "2", 0xB3, 0x04);
LPD8RK.loops["b38"] = new Array("[Sampler2]", "4", 0xB3, 0x08);
LPD8RK.loops["b33"] = new Array("[Sampler2]", "8", 0xB3, 0x03);



LPD8RK.init = function (id, debug) { // called when the device is opened & set up
    if (LPD8RK.debug){print("###init##############")};
    //soft takeovers
    engine.softTakeover("[Master]","crossfader",true);
    engine.softTakeover("[Channel1]","volume",true);
    engine.softTakeover("[Channel1]","rate",true);
    engine.softTakeover("[Channel2]","volume",true);
    engine.softTakeover("[Channel2]","rate",true);
    engine.softTakeover("[Sampler1]","volume",true);
    engine.softTakeover("[Sampler1]","rate",true);
    engine.softTakeover("[Sampler2]","volume",true);
    engine.softTakeover("[Sampler2]","rate",true);

    //set LED timer
    LPD8RK.ledTimer = engine.beginTimer(LPD8RK.LEDinterval, LPD8RK.setLeds);
};

LPD8RK.shutdown = function () {
    engine.stopTimer(LPD8RK.ledTimer);
};

LPD8RK.resetLEDTimer = function () {
    engine.stopTimer(LPD8RK.ledTimer);
    LPD8RK.setLeds()
    LPD8RK.ledTimer = engine.beginTimer(LPD8RK.LEDinterval, LPD8RK.setLeds);
};

LPD8RK.setLeds = function () {
    //runs repeatedly on a timer set in init()
    //has to run on a timer because you apparently can only set LEDs on the current prog selection on the LPD8
    //ie: leds on a PAD or CC other than the one you're currently using can't be set.
    //and no midi messages are sent when you change a PROG or pad.  No way of telling MIXXX which LEDs are currently active
    //so, this runs 4x a second to make sure LEDs are updated.  Kludge, but so it goes.

    //hotcues
    for (var id in LPD8RK.hotcues){
        for (var bank in LPD8RK.hotcues[id]){
            //iterate through hotcues for current bank, set hotcue leds
            var status = LPD8RK.hotcues[id][LPD8RK.hotcueBank][2];
            var ctrl = LPD8RK.hotcues[id][LPD8RK.hotcueBank][3];
            var state = engine.getValue(LPD8RK.hotcues[id][LPD8RK.hotcueBank][0], "hotcue_"+LPD8RK.hotcues[id][LPD8RK.hotcueBank][1]+"_enabled");

            //if (LPD8RK.debug){print("midi.sendShortMsg("+status+", "+ctrl+", "+state+")")};

            LPD8RK.lightLED(status, ctrl, state);
            };
        };

    //loops
    for (var id in LPD8RK.loops){
        //iterate through hotcues, set hotcue leds
        var status = LPD8RK.loops[id][2];
        var ctrl = LPD8RK.loops[id][3];
        var state = engine.getValue(LPD8RK.loops[id][0], "beatloop_"+LPD8RK.loops[id][1]+"_enabled");

        //if (LPD8RK.debug){print("midi.sendShortMsg("+status+", "+ctrl+", "+state+")")};

        LPD8RK.lightLED(status, ctrl, state);
        };

    //reloop buttons
    LPD8RK.lightLED(0xb0, 0x01, engine.getValue("[Channel1]", "loop_enabled"));
    LPD8RK.lightLED(0xb1, 0x01, engine.getValue("[Channel2]", "loop_enabled"));
    LPD8RK.lightLED(0xb2, 0x01, engine.getValue("[Sampler1]", "loop_enabled"));
    LPD8RK.lightLED(0xb3, 0x01, engine.getValue("[Sampler2]", "loop_enabled"));
    };

LPD8RK.lightLED = function (status, ctrl, state){
    //function to check for valid LED lighting messages - LPD8 seems to go into a weird nonresponsive mode when some unknown code is sent to it
    //don't know what the messed code is, but this function checks for valid LED addresses.  Hopefully this will prevent problems.
    if (LPD8RK.validLEDS[status][ctrl] !== true){
        print("######## INVALID STATUS ########");
        return false;
        } else {
        if (state > 0 || state === true){state=1;}//make sure state is valid
        midi.sendShortMsg(status, ctrl, state);
        }
    };

LPD8RK.clear = function (){//enables hotcue clearing
    if (LPD8RK.debug){print("###hotcueclear##############")};
    LPD8RK.hotcueClear=true;
    };

LPD8RK.noclear = function (){//disables hotcue clearing
    LPD8RK.hotcueClear=false;
    };

LPD8RK.hotcueButton = function (channel, control, value, status, group) {
    //weird status bug workaround
    if (channel==0 && status != 0x90){status = 0x90};
    if (channel==1 && status != 0x91){status = 0x91};
    if (channel==2 && status != 0x92){status = 0x92};
    if (channel==3 && status != 0x93){status = 0x93};

    if (LPD8RK.debug){print("###hotcueButton##############")};
    if (LPD8RK.debug){print("status:"+status)};
    if (LPD8RK.debug){print("channel:"+channel)};
    if (LPD8RK.debug){print("control:"+control)};
    if (LPD8RK.debug){print("hotcuebank:"+LPD8RK.hotcueBank)};
    if (LPD8RK.debug){print("phrase:#"+status.toString(16).toLowerCase()+control.toString(16).toLowerCase()+"#")};
    //activate or clear depending on whether clear button is pressed
    var thecue = LPD8RK.hotcues[status.toString(16).toLowerCase()+control.toString(16).toLowerCase()][LPD8RK.hotcueBank];
    if (LPD8RK.hotcueClear){
        engine.setValue(thecue[0], "hotcue_"+thecue[1]+"_clear", 1);
        engine.setValue(thecue[0], "hotcue_"+thecue[1]+"_clear", 0);
        if (LPD8RK.debug){print("cleared")};
        } else {
        engine.setValue(thecue[0], "hotcue_"+thecue[1]+"_activate", 1);
        engine.setValue(thecue[0], "hotcue_"+thecue[1]+"_activate", 0);
        if (LPD8RK.debug){print("###"+status.toString(16).toLowerCase()+control.toString(16).toLowerCase()+"--activated")};
        };

    };

LPD8RK.loopButton = function (channel, control, value, status, group) {
    if (LPD8RK.debug){print(LPD8RK.looplen+"len");}
    if (LPD8RK.debug){print(LPD8RK.looptype+"type");}
    //activate beatloop
    var theloop = LPD8RK.loops[status.toString(16).toLowerCase()+control.toString(16).toLowerCase()];
    if (value>0){//button was pressed
        LPD8RK.loopbuttonDown=true;
        engine.setValue(group, LPD8RK.looptype+"_"+theloop[1]+"_activate", 1);
        } else {//button was released
        LPD8RK.loopbuttonDown=false;
        };

    };

LPD8RK.reloopButton = function (channel, control, value, status, group) {
    if (LPD8RK.debug){print(LPD8RK.looplen+"len");}
    if (LPD8RK.debug){print(LPD8RK.looptype+"type");}

    if (value>0){//button was pressed
        engine.stopTimer(LPD8RK.reloopTimer);
        LPD8RK.loopbuttonDown=true;
        LPD8RK.doreloop=true;
        LPD8RK.reloopTimer = engine.beginTimer(500, LPD8RK.disablereloop, true);
        } else {//button was released
        LPD8RK.loopbuttonDown=false;
        if (LPD8RK.doreloop===true) {engine.setValue(group, "reloop_exit", 1);};
        LPD8RK.doreloop=true;
        engine.stopTimer(LPD8RK.reloopTimer);
        };

    };

LPD8RK.disablereloop = function () {
    //timed function - fires half a second after pressing reloop.  Don't do the reloop if you hold down the button (so you can move the loop without exiting)
    LPD8RK.doreloop=false;
    };

LPD8RK.looptypeDial = function (channel, control, value, status, group) {
    //activates variable length type depending on dial position
    //beatlooproll only works in 1.11 or above - script is a placeholder, knob used to select hotcue bank for now
    //if(value>63){LPD8RK.looptype="beatlooproll";}else{LPD8RK.looptype="beatloop";}
    //if (LPD8RK.debug){print(LPD8RK.looptype);}
    };

LPD8RK.resetOldBank = function () {
    //clears stored old hotcue bank
    LPD8RK.oldHotcueBank=0;
    };

LPD8RK.hotcueBankDial = function (channel, control, value, status, group) {
    //sets which hotcue bank to display (separate this out on dedicated controller)

    //pause LED resets, so bank indicator lights will be visible
    engine.stopTimer(LPD8RK.ledTimer);

    //select hotcue bank
    if (value>=0 && value <=12){LPD8RK.hotcueBank=1;};
    if (value>12 && value <=46){LPD8RK.hotcueBank=2;};
    if (value>46 && value <=80){LPD8RK.hotcueBank=3;};
    if (value>80 && value <=114){LPD8RK.hotcueBank=4;};
    if (value>114 && value <=128){LPD8RK.hotcueBank=5;};

    //light up indicator light
    if (LPD8RK.oldHotcueBank != LPD8RK.hotcueBank){//check if the bank's changed.  If it has, change the LEDs - LPD8RK.oldHotcueBank != LPD8RK.hotcueBank
        for (var id in LPD8RK.hotcues){
            for (var bank in LPD8RK.hotcues[id][1]){
                var status = LPD8RK.hotcues[id][1][2];
                var ctrl = LPD8RK.hotcues[id][1][3];
                //find bank number indicator light
                if (LPD8RK.hotcues[id][1][1]==LPD8RK.hotcueBank){
                    var state = 1;
                    }else{
                    var state = 0;
                    };

                LPD8RK.lightLED(status, ctrl, state);
                };
            };
        };
    //record last hotcue bank
    LPD8RK.oldHotcueBank=LPD8RK.hotcueBank;
    //set timer to clear old bank number after 500 msec, so bank indicator light will light up
    engine.stopTimer(LPD8RK.oldbanktimer);
    LPD8RK.oldbanktimer = engine.beginTimer(500, LPD8RK.resetOldBank, true);

    //set timer to restart LED updates in 500 msec
    engine.stopTimer(LPD8RK.LEDPauseTimer);
    LPD8RK.LEDPauseTimer = engine.beginTimer(LPD8RK.LEDinterval, LPD8RK.resetLEDTimer, true);
    };

LPD8RK.looplenDial = function (channel, control, value, status, group) {
    //activates variable length loop depending on dial position
    LPD8RK.looplen=value;

    if (LPD8RK.loopbuttonDown !== true){return false;}//exit if no loop button down
    else if (LPD8RK.looplen<=0x12){engine.setValue(group, LPD8RK.looptype+"_0.0625_activate", .0625);return true;} else
    if (LPD8RK.looplen<=0x25){engine.setValue(group, LPD8RK.looptype+"_0.125_activate", .125);return true;} else
    if (LPD8RK.looplen<=0x37){engine.setValue(group, LPD8RK.looptype+"_0.25_activate", .25);return true;} else
    if (LPD8RK.looplen<=0x49){engine.setValue(group, LPD8RK.looptype+"_0.5_activate", .5);return true;} else
    if (LPD8RK.looplen<=0x5b){engine.setValue(group, LPD8RK.looptype+"_1_activate", 1);return true;} else
    if (LPD8RK.looplen<=0x6d){engine.setValue(group, LPD8RK.looptype+"_2_activate", 2);return true;} else
    if (LPD8RK.looplen<=0x70){engine.setValue(group, LPD8RK.looptype+"_4_activate", 4);return true;} else
    if (LPD8RK.looplen<=0x7f){engine.setValue(group, LPD8RK.looptype+"_8_activate", 8);return true;};
    };

LPD8RK.loopminus = function (channel, control, value, status, group) {
    //shrinks loop or moves loop back
    if (LPD8RK.loopbuttonDown !== true){engine.setValue(group, "loop_halve", 1);engine.setValue(group, "loop_halve", 0); return false;}//shrink loop if no loop button down
    else if (engine.getValue(group, "loop_start_position")>=0 && engine.getValue(group, "loop_end_position")>=0 ){
        //move loop
        var interval =    LPD8RK.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var start = engine.getValue(group, "loop_start_position");
        var end = engine.getValue(group, "loop_end_position");
        engine.setValue(group, "loop_start_position", start-interval);
        engine.setValue(group, "loop_end_position", end-interval);
        return true;
        };
    };

LPD8RK.loopplus = function (channel, control, value, status, group) {
    //grows loop or moves loop forward
    if (LPD8RK.loopbuttonDown !== true){engine.setValue(group, "loop_double", 1); engine.setValue(group, "loop_double", 0); return false;}//shrink loop if no loop button down
    else if (engine.getValue(group, "loop_start_position")>=0 && engine.getValue(group, "loop_end_position")>=0 ){
        //move loop
        var interval =    LPD8RK.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var start = engine.getValue(group, "loop_start_position");
        var end = engine.getValue(group, "loop_end_position");
        engine.setValue(group, "loop_start_position", start+interval);
        engine.setValue(group, "loop_end_position", end+interval);
        return true;
        };
    };

LPD8RK.beatjump = function (channel, control, value, status, group) {
    //jumps back certain number of beats depending on knob modifier
    var curpos = engine.getValue(group, "playposition")*engine.getValue(group, "track_samples");
    var numbeats = LPD8RK.beatjumpstep;
    var backseconds = numbeats*(1/(engine.getValue(group, "bpm")/60));
    var backsamples = backseconds*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
    var newpos = curpos-(backsamples+engine.getValue("[App]", "output_latency_ms"));

    if (LPD8RK.debug){print("backseconds: "+backseconds);}
    if (LPD8RK.debug){print("backsamples: "+backsamples);}
    if (LPD8RK.debug){print("curpos: "+curpos);}
    if (LPD8RK.debug){print("newpos: "+newpos);}
    if (LPD8RK.debug){print("numbeats: "+numbeats);}

    engine.setValue(group, "playposition", newpos/engine.getValue(group, "track_samples"));
    };

LPD8RK.beatjumpDial = function (channel, control, value, status, group) {
    //activates variable length loop depending on dial position
    if(value>=0 && value <=127){
        if (value<=1){LPD8RK.beatjumpstep=.25; return true;} else
        if (value<=31){LPD8RK.beatjumpstep=.5; return true;} else
        if (value<=63){LPD8RK.beatjumpstep=1; return true;} else
        if (value<=94){LPD8RK.beatjumpstep=2; return true;} else
        if (value<=125){LPD8RK.beatjumpstep=4; return true;} else
        if (value<=127){LPD8RK.beatjumpstep=8; return true;};
        };
    };

LPD8RK.mute = function (group) {
    //toggles mute, then returns to previous volume
    storedvol=LPD8RK.vol[group];
    curvol=engine.getValue(group, "volume");
    engine.softTakeover(group,"volume",false);
    if (curvol==0){//is muted.  unmute.
        engine.setValue(group, "volume", storedvol);
        }else{//is not muted.  mute.
        engine.setValue(group, "volume", 0);
        LPD8RK.vol[group]=curvol;
        };
    engine.softTakeover(group,"volume",true);

    if (LPD8RK.debug){print("MUTE");}
    };

LPD8RK.progChng = function (channel, control, value, status, group) {
    if (LPD8RK.debug){print("###PROG CHANGE###");}
    //workaround because prog chng buttons don't seem to like to work unless they're linked to scripts
    if (control==0x07){LPD8RK.toggleplay(group, engine.getValue(group, "play")); return true;} else
    if (control==0x03){engine.setValue(group, "cue_default", true); engine.setValue(group, "cue_default", false); return true;} else
    if (control==0x06){engine.setValue(group, "beatsync", true); engine.setValue(group, "beatsync", false); return true;} else
    if (control==0x02){LPD8RK.togglepfl(group, engine.getValue(group, "pfl")); return true;} else
    if (control==0x05){LPD8RK.togglereverse(group, engine.getValue(group, "reverse")); return true;} else
    if (control==0x04){LPD8RK.toggleback(group, engine.getValue(group, "back")); return true;} else
    if (control==0x00){LPD8RK.togglefwd(group, engine.getValue(group, "fwd")); return true;}
    if (control==0x01){LPD8RK.mute(group); return true;}
    };

LPD8RK.toggleplay = function (group, state) {
    if (state==true){engine.setValue(group, "reverse",0);engine.setValue(group, "play", false);} else {engine.setValue(group, "reverse",0);engine.setValue(group, "play", true);};
    };

LPD8RK.togglepfl = function (group, state) {
    if (state==true){engine.setValue(group, "pfl", false);} else {engine.setValue(group, "pfl", true);};
    };

LPD8RK.togglereverse = function (group, state) {
    if (state==true){engine.setValue(group, "reverse", false);} else {engine.setValue(group, "reverse", true);};
    };

LPD8RK.toggleback = function (group, state) {
    if (state==true){engine.setValue(group, "back", false);} else {engine.setValue(group, "back", true);};
    };

LPD8RK.togglefwd = function (group, state) {
    if (state==true){engine.setValue(group, "fwd", false);} else {engine.setValue(group, "fwd", true);};
    };

LPD8RK.softXfade = function (channel, control, value, status, group) {
    engine.setValue(group, "crossfader", (value/64)-1);
    };

LPD8RK.softVolume = function (channel, control, value, status, group) {
    engine.setValue(group, "volume", (value/127));
    };

LPD8RK.softRate = function (channel, control, value, status, group) {
    engine.setValue(group, "rate", (value/64)-1);
    };


LPD8RK.test = function (channel, control, value, status, group) {
    print("channel: "+channel);
    print("control: "+control.toString(16));
    print("value: "+value);
    print("status: "+status);
    print("group: "+group);
    print("test: ##"+status.toString(16)+control.toString(16)+"##");
    print("loopbuttondown: ##"+LPD8RK.loopbuttonDown+"##");
    print("LPD8RK.looplen: ##"+LPD8RK.looplen+"##");
    print("LPD8RK.looplen.tostring: ##"+LPD8RK.looplen.toString(16)+"##");
    print("LPD8RK.looptype: ##"+LPD8RK.looptype+"##");
    print("test: ##"+2+"##");
    };
