function KONTROL1() {}
KONTROL1.debug=false;//debug levels - 1 to 3 or false
KONTROL1.disableFlash=false;//disables the bank indicator flashers so you can see debug messages and not get flooded out with timer msgs

//set defaults
KONTROL1.flashon=0;
KONTROL1.flashcount=0;
KONTROL1.LEDtimer=false;//stores reference to led timer - fires once per second
KONTROL1.flashTimer=false;//reference to flash timer - flashes leds quickly, fired by LEDtimer
KONTROL1.curChannel=0;//set current channel (deck selector - 0 to 3 = 1 to 4 on knob)
KONTROL1.loopmove=0.0125;//default loop move distance in seconds
KONTROL1.wheelloopmove=0.003125;//default loop move distance in seconds when using wheel to move
KONTROL1.scratching=[];
KONTROL1.modTimeout=200;//mod button timeout in ms - ie: how long do you have to hold a mod (like a knob button) to disable the bank switch or whatever it would normally do

KONTROL1.bankIndicatorLED={bank1:0x37, bank2:0x36, bank3:0x35, bank4:0x34};//midi address of bank indicator LEDs (knob leds)
KONTROL1.defaultDeck={0:"[Channel1]", 1:"[Channel2]", 2:"[Channel3]", 3:"[Channel4]"};//default deck for each channel
KONTROL1.altDeck={0:"[Channel2]", 1:"[Channel1]", 2:"[Channel4]", 3:"[Channel3]"};//alt deck for each channel
KONTROL1.syncDeck={0:"[Sampler1]", 1:"[Sampler2]", 2:"[Sampler3]", 3:"[Sampler4]"};//sync deck for each channel
KONTROL1.beatloopLengths=new Array(0,0.03125,0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32);//store loop lengths with presets in Mixxx

KONTROL1.beatlooprollactive={};
KONTROL1.beatlooprolllen={};

KONTROL1.overwriteHotcues=true;//when saving loop in points as hotcues, will the script overwrite an existing hotcue?

//scratch parameters
KONTROL1.wheelRes=150;
KONTROL1.scratchRev=160;
KONTROL1.scratchAlpha=1/16;
KONTROL1.scratchBeta=KONTROL1.scratchAlpha/32;
KONTROL1.scratchInterpolation=1;






//obj to store info specific to the state of each channel
KONTROL1.channels={};
KONTROL1.channels[0]={curShift:"a",curBank:"bank1",cc:0xb0,noteon:0x90};
KONTROL1.channels[1]={curShift:"a",curBank:"bank1",cc:0xb1,noteon:0x91};
KONTROL1.channels[2]={curShift:"a",curBank:"bank1",cc:0xb2,noteon:0x92};
KONTROL1.channels[3]={curShift:"a",curBank:"bank1",cc:0xb3,noteon:0x93};
//shift state is "a" off or "b" on
//banks are "bank1", "bank2", "bank3", or "bank4"
//plug additional state variables into this object - mutes on/off, kills on/off, etc.


//obj to store mod values - mods are pitch +-, Knob buttons, and shift when held down.
KONTROL1.mod={};
KONTROL1.mod["p1"]={state:"o",timer:false};
KONTROL1.mod["p2"]={state:"o",timer:false};
KONTROL1.mod["k1"]={state:"o",timer:false,bank:"bank1",dobankswitch:true};
KONTROL1.mod["k2"]={state:"o",timer:false,bank:"bank2",dobankswitch:true};
KONTROL1.mod["k3"]={state:"o",timer:false,bank:"bank3",dobankswitch:true};
KONTROL1.mod["k4"]={state:"o",timer:false,bank:"bank4",dobankswitch:true};
KONTROL1.mod["s"]={state:"o",timer:false,doshift:true};

//if (KONTROL1.debug){print("#####WORD#####")};



//############################################################################
//INIT AND SHUTDOWN
//############################################################################

KONTROL1.init = function init(id, debug) { // called when the device is opened & set up
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    KONTROL1.updateLEDs();
    if(KONTROL1.disableFlash!==true)KONTROL1.ledTimer = engine.beginTimer(1000, KONTROL1.doLEDs);//set timer for LED indicator flashes
    engine.connectControl("[Channel1]", "volume", "KONTROL1.testconnect");
    };

KONTROL1.shutdown = function shutdown() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    };



//############################################################################
//INPUT PROCESSING
//############################################################################

KONTROL1.button = function button(channel, control, value, status, group) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //input received - gatekeeper function connected to most buttons
    //check channel - see if the deck selector has been moved
    KONTROL1.checkChannel(channel);

    //get the commands for the current control on the current bank
    var thecontrol=KONTROL1.getControl(control);
    var command = (value==127)?thecontrol[0]:thecontrol[1];
    if (command!==false && command !='ReleaseEvalStr' && command!='ButtonFunctionEvalStr')eval(command);
    };

KONTROL1.knob = function knob(channel, control, value, status, group) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //input received - gatekeeper function connected to most knobs
    //check channel - see if the deck selector has been moved
    KONTROL1.checkChannel(channel);

    //get the commands for the current control on the current bank - press function only - release functions are invalid on knobs
    var thecontrol=KONTROL1.getControl(control);
    var command = thecontrol[0];
    if (command!==false && command!='ButtonFunctionEvalStr')eval(command);
    };


//MOD functions
KONTROL1.knobPress = function knobPress(knobnum){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //{state:"o",timer:false,dobankswitch:true};
    KONTROL1.modPress(knobnum);//turn mod on
    engine.stopTimer(KONTROL1.mod[knobnum]["timer"]);//kill any previous timer
    KONTROL1.mod[knobnum]["timer"]=engine.beginTimer(KONTROL1.modTimeout, () => KONTROL1.disableBankSwitch(knobnum), true);
    }

KONTROL1.knobRelease = function knobRelease(knobnum){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //switches banks when one of the four knob buttons is released
    //knobnum is k1, k2, k3, or k4
    //unless button has been held down for more than 1/2 second, in which case the knob is used as a modifier
    if (KONTROL1.mod[knobnum]["dobankswitch"]===true){
        engine.stopTimer(KONTROL1.mod[knobnum]["timer"]);
        KONTROL1.switchBank(KONTROL1.mod[knobnum]["bank"]);
        KONTROL1.mod[knobnum]["dobankswitch"]=true;
        KONTROL1.modRelease(knobnum);//turn mod off
        }else{
        //reset
        KONTROL1.mod[knobnum]["dobankswitch"]=true;
        KONTROL1.modRelease(knobnum);//turn mod off
        }

    //exit knob loop if no loop was active when knob pressed.
    if (KONTROL1.exitKnobLoop===true && knobnum=="k4"){
        var group=KONTROL1.getGroup("default");
        engine.setValue(group, "reloop_exit", 1);
        KONTROL1.exitKnobLoop=false;
        }
    };

KONTROL1.shiftPress = function shiftPress(){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    var knobnum="s";
    KONTROL1.modPress(knobnum);//turn mod on
    engine.stopTimer(KONTROL1.mod[knobnum]["timer"]);//kill any previous timer
    KONTROL1.mod[knobnum]["timer"]=engine.beginTimer(500, () => KONTROL1.disableShiftSwitch(knobnum), true);
    }

KONTROL1.shiftRelease = function shiftRelease(){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    var knobnum="s";
    //switches shift state
    //unless button has been held down for more than 1/2 second, in which case the knob is used as a modifier
    if (KONTROL1.mod[knobnum]["doshift"]===true){
        engine.stopTimer(KONTROL1.mod[knobnum]["timer"]);
        KONTROL1.switchShift(KONTROL1.mod[knobnum]["bank"]);
        KONTROL1.mod[knobnum]["doshift"]=true;
        KONTROL1.modRelease(knobnum);//turn mod off
        }else{
        engine.stopTimer(KONTROL1.mod[knobnum]["timer"]);
        //reset
        KONTROL1.mod[knobnum]["doshift"]=true;
        KONTROL1.modRelease(knobnum);//turn mod off
        }
    };

KONTROL1.modPress = function modPress(knobnum){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    KONTROL1.mod[knobnum]["state"]="I";//turn mod on
    if (KONTROL1.mod["p1"]["state"]=="I" && KONTROL1.mod["p2"]["state"]=="I"){//both pitch buttons on, force update of all LEDS
        KONTROL1.updateLEDs(false, true);
    } else {KONTROL1.updateLEDs();}
    }

KONTROL1.modRelease = function modRelease(knobnum){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    if (KONTROL1.mod["p1"]["state"]=="I"){
        //reset cue move stuff
        engine.stopTimer(KONTROL1.cueMoveIndicator);
        KONTROL1.cueMoveToNum=-1;
        KONTROL1.cuemoveLastIndicator=-1;
    }

    KONTROL1.mod[knobnum]["state"]="o";//turn mod off

    KONTROL1.updateLEDs();
    }



//############################################################################
//LED FUNCTIONS - indicator functions
//############################################################################

KONTROL1.doLEDs = function doLEDs() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    engine.stopTimer(KONTROL1.flashTimer);
    KONTROL1.flashTimer=engine.beginTimer(30, KONTROL1.bankIndicators);
    return;
    };

KONTROL1.bankIndicators= function bankIndicators(){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //flash knob LEDs to indicate current bank
    var cc = KONTROL1.channels[KONTROL1.curChannel]['cc'];
    var address = KONTROL1.bankIndicatorLED[KONTROL1.channels[KONTROL1.curChannel]['curBank']];
    if (KONTROL1.flashon==0){
        if (KONTROL1.flashcount>=2){KONTROL1.stopFlashTimer();return;}
        midi.sendShortMsg(cc, address, 0);
        KONTROL1.flashon=1;
        KONTROL1.flashcount++;
        }else {
        if (KONTROL1.flashcount>=2){KONTROL1.stopFlashTimer();return;}
        midi.sendShortMsg(cc, address, 127);
        KONTROL1.flashon=0;
        KONTROL1.flashcount++;
        }
    }

KONTROL1.stopFlashTimer = function stopFlashTimer() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    engine.stopTimer(KONTROL1.flashTimer);
    KONTROL1.flashcount=0;
    var cc = KONTROL1.channels[KONTROL1.curChannel]['cc'];
    var address = KONTROL1.bankIndicatorLED[KONTROL1.channels[KONTROL1.curChannel]['curBank']];
    KONTROL1.updateLEDs(true);
    }

KONTROL1.updateLEDs = function updateLEDs(knobsonly, force) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //fires on mod press/release, shift switch, channel switch, bank switch
    //updates all LEDs
    var state;
    var samehook=false;
    var hookgroup;
    var midino;
    var control;
    var controllist=(knobsonly===true)?KONTROL1.knobLEDs:KONTROL1.allLEDs;
    var force=(force===true)?force:false;

    //clear hooks
    for (var key in controllist){
        midino=controllist[key];
        control = KONTROL1.getControl(midino);
        if ((control[2]==false || control[2]=='LEDstateType') && force !== true){continue;}//control has no LED, LED Type is not set, or LED doesn't change on this mod setting.  skip to next control

        //clear hooks
        if (KONTROL1.controls[midino]["hookSet"]===true){
            hookgroup=KONTROL1.getGroup(control[5]);//get mixxx control group
            //clear the hook
            engine.connectControl(KONTROL1.controls[midino]["hookGroup"], KONTROL1.controls[midino]["hookMixxxControl"], KONTROL1.controls[midino]["hookFunction"], true);
            KONTROL1.controls[midino]["hookSet"]=false;
            }//end if
        }

    for (var key in controllist){//set hooks
        midino=controllist[key];
        control = KONTROL1.getControl(midino);
        if ((control[2]==false || control[2]=='LEDstateType') && force !== true){continue;}//control has no LED, LED Type is not set, or LED doesn't change on this mod setting.  skip to next control


        if (control[2]=="on"){//LED is always on
            state=127;
            }
        else if (control[2]=="off"){//LED is always off
            state=0;
            }
        else if (control[2]=="hook"){//LED is hooked to a mixxx control
            print("#####-----SETTING HOOK##"+hookgroup+" - "+control[3]+" - "+control[4]);
            hookgroup=KONTROL1.getGroup(control[5]);//get mixxx control group
            engine.connectControl(hookgroup, control[3], control[4]);//set hook
            KONTROL1.controls[midino]["hookGroup"]=hookgroup;
            KONTROL1.controls[midino]["hookMixxxControl"]=control[3];
            KONTROL1.controls[midino]["hookFunction"]=control[4];
            KONTROL1.controls[midino]["hookSet"]=true;
            engine.trigger(hookgroup, control[3]);
            continue;
            }
        else if (control[2]=="eval"){//LED state determined by evaluating a javascript statement
            if (KONTROL1.debug>2){print("##updateLEDs Eval: "+control[3])};
            if (KONTROL1.debug>2){print("##updateLEDs midino: "+midino)};
            if (KONTROL1.debug>2){print("##evalstate: "+eval(control[3]))};
            state=eval(control[3]);
            }//end if
        //figure out whether we want a CC or noteon message - CC for knobs, noteon otherwise
        if (midino==0x37 || midino==0x36 || midino==0x35 || midino==0x34){
            ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
            if (KONTROL1.debug>2){print("ch: "+ch)};
            if (KONTROL1.debug>2){print("midino: "+midino)};
            }else{
            ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
            }
        midi.sendShortMsg(ch, midino, 0);
        midi.sendShortMsg(ch, midino, state);
        state=0;
        }//end for
    }

//connected control functions
KONTROL1.doLogKnobLEDs = function doLogKnobLEDs(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //show values of mixxx control on knob leds
    //logarithmic scale (0 min, 4 max) - used for pregain and lo mid hi filters, for example.
    //K = 4*(V)^2
    //V = (K/4)^0.5 -- K= value coming from prog (0-4, logarithmic scale), V=value adjusted to linear 0-1
    if (KONTROL1.debug>2){print("unadjusted value: "+value)};
    value=(Math.pow((value/4),.5))*127;
    if (value<70){value+=10;}//add a bit to center 50% mark
        else if (value<110){value+=5;}
        else if (value<120){value+=2;}
    value=(value>127)?127:value;
    value=(value<0)?0:value;
    if (KONTROL1.debug>2){print("value: "+value)};
    var midino;
    var ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
    if (control=="pregain"){midino=0x37;}
    if (control=="filterHigh"){midino=0x36;}
    if (control=="filterMid"){midino=0x35;}
    if (control=="filterLow"){midino=0x34;}

    midi.sendShortMsg(ch, midino, value);
    }

KONTROL1.doHotcueLEDs = function doHotcueLEDs(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    value=value*127;
    if (KONTROL1.debug>2){print("value: "+value)};
    var hotcuenum=control.replace("hotcue_", "");
    var hotcuenum=hotcuenum.replace("_enabled", "");
    var ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    var midino=KONTROL1.hotcueLEDs[hotcuenum];

    midi.sendShortMsg(ch, midino, value);
    }

KONTROL1.doFilterKillLEDs = function doFilterKillLEDs(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    value=value*127;
    ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
    if (control=="filterHighKill"){midino=0x08}
    if (control=="filterMidKill"){midino=0x07}
    if (control=="filterLowKill"){midino=0x06}
    midi.sendShortMsg(ch, midino, value);
    }

KONTROL1.syncLED = function syncLED(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
    value=(value>0)?1:0;
    ledstate=value*127;
    midi.sendShortMsg(ch, KONTROL1.getIt['sync'], ledstate);
    }

KONTROL1.cueLED = function cueLED(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    value=(value>0)?1:0;
    ledstate=value*127;
    midi.sendShortMsg(ch, KONTROL1.getIt['cue'], ledstate);
    }

KONTROL1.playLED = function playLED(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
    value=(value>0)?1:0;
    ledstate=value*127;
    midi.sendShortMsg(ch, KONTROL1.getIt['play'], ledstate);
    }

KONTROL1.loopLenLEDs = function loopLenLEDs(deck, num){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //light LEDS for loop length dial
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if(typeof num === 'undefined'){
        num=false;
        for (i=1;i<11;i++){
            if (engine.getValue(group, "beatloop_"+KONTROL1.beatloopLengths[i]+"_enabled")==1){num=i;break;}
            }
        if (num==false){num=8;}//default to 2 beat loop
        }
    num=(num*12.8)-1;
    print("num="+num);
    ch=KONTROL1.channels[KONTROL1.curChannel]['cc'];
    midi.sendShortMsg(ch, 0x34, num);
    }


KONTROL1.lightButtonLEDs = function lightButtonLEDs(value,group,control){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //light button leds for simple binary values
    //set KONTROL1.buttonLEDs object to contain new addresses as required
    ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    value=value*127;
    value=(value>127)?127:value;
    value=(value<0)?0:value;
    if (typeof(KONTROL1.buttonLEDs[control])!= 'undefined'){
        var midino=KONTROL1.buttonLEDs[control];
        midi.sendShortMsg(ch, midino, value);
    }
};

KONTROL1.LoopBankLEDs = function LoopBankLEDs(value,group,control){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //light button leds for simple binary values
    //set KONTROL1.buttonLEDs object to contain new addresses as required
    ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    value=value*127;
    value=(value>127)?127:value;
    value=(value<0)?0:value;
    if (typeof(KONTROL1.loopbuttonLEDs[control])!= 'undefined'){
        var midino=KONTROL1.loopbuttonLEDs[control];
        midi.sendShortMsg(ch, midino, value);
    }
};

KONTROL1.resumeLED = function resumeLED() {//lights sync button if a resume position is set
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    var state;
    if (KONTROL1.channels[KONTROL1.curChannel]["resumepos"]>-1){
        state=127;
    }else{
        state=0;
    }//end if
    var ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    midi.sendShortMsg(ch, 0x1d, state);
    return state;
    };

KONTROL1.scratchParameterLEDs = function scratchParameterLEDs(midino,thearray,theindex) {//lights knobs for scratch parameter adjustment
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var state=Math.round((127/thearray.length)*theindex);
    state=(state>127)?127:state;
    state=(state<0)?0:state;

    var ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];
    midi.sendShortMsg(ch, midino, state);
    return state;
    };


//############################################################################
//CHANNEL AND BANK SWITCHING
//############################################################################

KONTROL1.checkChannel = function checkChannel(channel){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //checks to see whether the channel has changed (deck selector knob sends no midi when changed,
    //so every time input is received, this function checks its midi channel to see if it's different from the last)
    if (KONTROL1.curChannel!=channel){
        //channel has changed.
        KONTROL1.curChannel=channel;
        //add in any functions to run here.  Eg: update LEDs, etc. [TODO]
        KONTROL1.updateLEDs();
        };
    };

KONTROL1.disableBankSwitch = function disableBankSwitch(knobnum) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //timed function - fires half a second after pressing knob.
    //Don't do the bank switch if you hold down the button (if knob button is used as modifier)
    KONTROL1.mod[knobnum]["dobankswitch"]=false;
    };

KONTROL1.switchBank = function switchBank(bank){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    KONTROL1.channels[KONTROL1.curChannel]["curBank"]=bank;
    //add in any functions to run here.  Eg: update LEDs, etc. [TODO]
    KONTROL1.updateLEDs();
    };

KONTROL1.disableShiftSwitch = function disableShiftSwitch(knobnum) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //timed function - fires half a second after pressing knob.
    //Don't do the bank switch if you hold down the button (if knob button is used as modifier)
    KONTROL1.mod[knobnum]["doshift"]=false;
    };

KONTROL1.switchShift = function switchShift(bank){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    KONTROL1.channels[KONTROL1.curChannel]["curShift"]=(KONTROL1.channels[KONTROL1.curChannel]["curShift"]=="a")?"b":"a";
    //add in any functions to run here.  Eg: update LEDs, etc. [TODO]
    KONTROL1.updateLEDs();
    };



//############################################################################
//MUSIC FUNCTIONALITY - stuff that actually does stuff
//############################################################################

KONTROL1.toggleBinaryControl = function toggleBinaryControl(control, newstate){//toggles a binary control.  If newstate is provided, it will toggle to that state, otherwise it toggles on/off
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    deck=(typeof deck !== 'undefined')?deck:"default";
    var group = KONTROL1.getGroup(deck);

    if (newstate!==1 && newstate!==0){var newstate = (engine.getValue(group, control)==0)?1:0;}

    engine.setValue(group, control, newstate);
    }


KONTROL1.logKnobAdjust = function logKnobAdjust(group, control, minRange, maxRange, value){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //use a knob to adjust a mixxx control
    //on a logarithmic scale (0 min, 4 max)
    //K = 4*(V)^2
    //V = (K/4)^0.5 -- K= value coming from prog (0-4, logarithmic scale), V=value adjusted to linear 0-1
    var curValue=engine.getValue(KONTROL1.getGroup(group), control);
    var adjustedValue=Math.pow((curValue/4),.5);//adjust range so it's on a scale of 0 to 1 - logarithmic

    var inc=(value==127)?.02:-.02;//increment - how far to move on each knob click

    var newValue=4*Math.pow((adjustedValue+inc),2);

    if (KONTROL1.debug>2){print("group: "+KONTROL1.getGroup(group))};
    if (KONTROL1.debug>2){print("control: "+control)};
    if (KONTROL1.debug>2){print("curValue: "+curValue)};
    if (KONTROL1.debug>2){print("adjustedValue: "+adjustedValue)};
    if (KONTROL1.debug>2){print("newValue: "+newValue)};
    if (KONTROL1.debug>2){print("inc: "+inc)};
    if (newValue>4)newValue=4;
    if (newValue<0)newValue=0;
    engine.setValue(KONTROL1.getGroup(group), control, newValue);
    }

KONTROL1.wheelTouch = function wheelTouch(channel, control, value, status, group) {//activate scratching
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    if (KONTROL1.debug>2){print("channel: "+channel)};

    if (value == 0x7F) {//wheel is being touched
        engine.scratchEnable(channel+1, KONTROL1.wheelResArray[KONTROL1.wheelResIndex]*KONTROL1.scratchInterpolation, KONTROL1.scratchRevArray[KONTROL1.scratchRevIndex], KONTROL1.scratchAlphaArray[KONTROL1.scratchAlphaIndex], KONTROL1.scratchAlphaArray[KONTROL1.scratchAlphaIndex]/KONTROL1.scratchBetaArray[KONTROL1.scratchBetaIndex]);
        // Keep track of whether we're scratching on this virtual deck - for v1.10.x or below
        KONTROL1.scratching[group] = true;
    } else {//wheel is released
        engine.scratchDisable(channel+1);
        KONTROL1.scratching[group] = false;  // Only for v1.10.x and below
        }
    };

KONTROL1.wheelTurn = function wheelTurn(channel, control, value, status, group) {//do scratching
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    if (KONTROL1.debug>2){print("channel: "+channel)};

    if (value>1){var newValue=1}else{var newValue=-1};
    if (!KONTROL1.scratching[group]){//do jog or other functions (move loops, hotcues, etc.) if not scratching

        //check if a loop needs to be moved
        if (KONTROL1.loopbuttonDown === true || KONTROL1.loopinbuttonDown === true || KONTROL1.loopoutbuttonDown === true){
            if (KONTROL1.wheelMoveLoop(value)===true){return true;}//function returns true if loop moved
        }

        engine.setValue(group, "jog", newValue);
        return true;
        };
    for (i=0; i<KONTROL1.scratchInterpolation; i++){
        engine.scratchTick(channel+1,newValue);
    }
    };

KONTROL1.hotcueButton = function hotcueButton(hotcuenum, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group = KONTROL1.getGroup(deck);
    //activate
    if (KONTROL1.mod["p1"]["state"]=="I"){
        engine.setValue(group, "hotcue_"+hotcuenum+"_clear", 1);
        if (KONTROL1.debug>2){print("cleared")};
        } else {
        engine.setValue(group, "hotcue_"+hotcuenum+"_activate", 1);
        engine.setValue(group, "hotcue_"+hotcuenum+"_activate", 0);
        if (KONTROL1.debug>2){print("activated")};
        };

    };

KONTROL1.cueClear = function cueClear(cue, control, deck){//clear hotcue - OR move hotcue to new button
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group = KONTROL1.getGroup(deck);


    if(engine.getValue(group, "hotcue_"+cue+"_enabled")!=true){//hotcue not set - prepare to move next hotcue pressed to this button
        KONTROL1.cueMoveToNum=cue;
        engine.stopTimer(KONTROL1.cueMoveIndicator);
        KONTROL1.cueMoveIndicator=engine.beginTimer(100, () => KONTROL1.cueMoveIndicatorLEDs(control));//start timer for LED indicator flasher showing the button we're moving to
        return true;
    }

    if(KONTROL1.cueMoveToNum>0){//hotcue set, but we're moving it, not clearing it.
        engine.setValue(group, "hotcue_"+KONTROL1.cueMoveToNum+"_set", 1);
        engine.setValue(group, "hotcue_"+KONTROL1.cueMoveToNum+"_position", engine.getValue(group, "hotcue_"+cue+"_position"));
        engine.setValue(group, "hotcue_"+cue+"_clear", 1);
        engine.setValue(group, "hotcue_"+cue+"_clear", 0);

        engine.stopTimer(KONTROL1.cueMoveIndicator);
        KONTROL1.cueMoveToNum=-1;//reset
        KONTROL1.cuemoveLastIndicator=-1;
        return true;
    }

    engine.setValue(group, "hotcue_"+cue+"_clear", 1);
    engine.setValue(group, "hotcue_"+cue+"_clear", 0);
}

KONTROL1.cueMoveIndicatorLEDs = function cueMoveIndicatorLEDs(control){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    ch=KONTROL1.channels[KONTROL1.curChannel]['noteon'];

    KONTROL1.cuemovecontrol=control;
    if (KONTROL1.cuemoveLastIndicator!=-1){midi.sendShortMsg(ch, KONTROL1.cuemoveLastIndicator, 0);}//turn off last indicator in case timer was interrupted (keeps last led from being left on if you switch "move to" buttons.
    KONTROL1.cuemoveLastIndicator=control;
    KONTROL1.cuemoveflashon=(KONTROL1.cuemoveflashon!=0 && KONTROL1.cuemoveflashon!=1)?0:KONTROL1.cuemoveflashon;

    if (KONTROL1.cuemoveflashon==0){
        midi.sendShortMsg(ch, control, 0);
        KONTROL1.cuemoveflashon=1;
        }else {
        midi.sendShortMsg(ch, control, 127);
        KONTROL1.cuemoveflashon=0;
        }

    return true;
};

KONTROL1.cueLoop = function cueLoop(cue, len){//jump to hotcue and start loop
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group = KONTROL1.getGroup(deck);

    if (engine.getValue(group, "hotcue_"+cue+"_enabled")!==1){//set hotcue and start loop
        engine.setValue(group, "hotcue_"+cue+"_activate", 1);
        engine.setValue(group, "hotcue_"+cue+"_activate", 0);
        engine.setValue(group, "beatloop_"+len+"_activate", 1);
        }else{//jump to existing cue and loop

        //calculate start and end points
        var startpos=engine.getValue(group, "hotcue_"+cue+"_position");
        var loopseconds = len*(1/(engine.getValue(group, "bpm")/60));
        var loopsamples = loopseconds*engine.getValue(group, "track_samplerate")*2;//*2 to compensate for stereo samples
        var endpos=startpos+loopsamples;

        //disable loop if currently enabled
        if (engine.getValue(group, "loop_enabled")==true){engine.setValue(group, "reloop_exit", 1);}

        //set start and endpoints
        engine.setValue(group, "loop_start_position", startpos);
        engine.setValue(group, "loop_end_position", endpos);
        //enable loop
        engine.setValue(group, "reloop_exit", 1);
    }
}

KONTROL1.kill = function kill(freq, value, deck){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //kills low mid high, or mutes all by recording current track volume level, switching to 0, then restoring previous volume level
    //if P1 mod is on when button pressed, it toggles on and off.  Otherwise it's momentary
    //args: freq = "all", "low", "mid", or "high"
    //value: 127=button pressed ; 0=button released
    var group=KONTROL1.getGroup(deck);

    if (value==127){//button was pressed
        if (KONTROL1.channels[KONTROL1.curChannel]['kill'+freq]===true){
            //kill is already on...  change setting so it clears on release
            KONTROL1.channels[KONTROL1.curChannel]['kill'+freq]=false;
            return true;
            } else
        if (KONTROL1.mod["p1"]["state"]=="I"){//mod is on, set toggle
            KONTROL1.channels[KONTROL1.curChannel]['kill'+freq]=true;
            }
        if (freq=="low"){engine.setValue(group, "filterLowKill", 1);}else
        if (freq=="mid"){engine.setValue(group, "filterMidKill", 1);}else
        if (freq=="high"){engine.setValue(group, "filterHighKill", 1);}else
        if (freq=="all"){
            //mute
            curvol=engine.getValue(group, "volume");
            if (curvol==0){return;}//do nothing if volume is already zeroyyz
            engine.setValue(group, "volume", 0);
            KONTROL1.channels[KONTROL1.curChannel]['mutestoredvol']=curvol;
            KONTROL1.channels[KONTROL1.curChannel]["isMuted"]=true;
            (KONTROL1.channels[KONTROL1.curChannel]["isMuted"]==true)?127:0;
            midi.sendShortMsg(KONTROL1.channels[KONTROL1.curChannel]['noteon'], 0x09, 127);//light led
            }
        }else{//button was released
        if (KONTROL1.channels[KONTROL1.curChannel]['kill'+freq]===true){
            //kill is toggled...  ignore button release
            return true;
            }
        if (freq=="low"){engine.setValue(group, "filterLowKill", 0);}else
        if (freq=="mid"){engine.setValue(group, "filterMidKill", 0);}else
        if (freq=="high"){engine.setValue(group, "filterHighKill", 0);}else
        if (freq=="all"){
            //unmute
            engine.setValue(group, "volume", KONTROL1.channels[KONTROL1.curChannel]['mutestoredvol']);
            KONTROL1.channels[KONTROL1.curChannel]["isMuted"]=false;
            midi.sendShortMsg(KONTROL1.channels[KONTROL1.curChannel]['noteon'], 0x09, 0);//light led
            }
        }
    }

KONTROL1.resetKnob = function resetKnob(control, deck){//resets gain, low mid high to 50%
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);
    engine.setValue(group, control, 1);
    }

KONTROL1.play = function play(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);
    var state=(engine.getValue(group, "play")==1)?0:1;
    if (value>0){//button was pressed
        if (KONTROL1.debug){print("play newstate: "+state)};
        if (KONTROL1.debug){print("play group: "+group)};
        engine.setValue(group, "play", state);
        } else {//button was released
        };

}

KONTROL1.cue = function play(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);
    if (value>0){//button was pressed
        engine.setValue(group, "cue_default", 1);
        } else {//button was released
        };

}

//LOOPS
KONTROL1.beatLoop = function beatLoop(looplen, value, deck) {//activate beatloop
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);
    //activate beatloop
    if (value>0){//button was pressed
        KONTROL1.loopbuttonDown=true;
        if (engine.getValue(group, "beatloop_"+looplen+"_enabled")!=1){//if it's not already enabled (this is to prevent turning a beatlooproll into a beatloop of the same length
            engine.setValue(group, "beatloop_"+looplen+"_activate", 1);
            }
        } else {//button was released
        KONTROL1.loopbuttonDown=false;
        };

    };

KONTROL1.beatLoopRoll = function beatLoopRoll(looplen, value, deck) {//activate beatloop
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);
    //activate beatlooproll
    if (value>0){//button was pressed
        KONTROL1.loopbuttonDown=true;
        engine.setValue(group, "beatlooproll_"+looplen+"_activate", 1);
        KONTROL1.beatlooprollactive[group]=true;
        KONTROL1.beatlooprolllen[group]=looplen;
        } else {//button was released
        KONTROL1.loopbuttonDown=false;
        };

    };

KONTROL1.beatjump = function (jumplen) {//jumps back jumplen beats
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    var curpos = engine.getValue(group, "playposition")*engine.getValue(group, "track_samples");
    var numbeats = jumplen;
    var backseconds = numbeats*(1/(engine.getValue(group, "bpm")/60));
    var backsamples = backseconds*engine.getValue(group, "track_samplerate")*2;//*2 to compensate for stereo samples
    var newpos = curpos-(backsamples);

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

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

KONTROL1.saveLoop = function saveLoop(cue){//save the current loop inpoint as a hotcue
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (engine.getValue(group, "hotcue_"+cue+"_enabled")==1 && KONTROL1.overwriteHotcues===false){//hotcue is already set, return false
        return false;
        }else if (engine.getValue(group, "loop_enabled")!=true){//no loop currently active, return false
        return false;
        }else{//save the loop as a hotcue
        engine.setValue(group, "hotcue_"+cue+"_set", 1);
        engine.setValue(group, "hotcue_"+cue+"_position", engine.getValue(group, "loop_start_position"));
        return true;
    }
}

KONTROL1.reloopButton = function reloopButton(value, deck) {//reloop/exit button
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (value>0){//button was pressed
        engine.stopTimer(KONTROL1.reloopTimer);
        KONTROL1.loopbuttonDown=true;
        KONTROL1.doreloop=true;
        KONTROL1.reloopTimer = engine.beginTimer(500, "KONTROL1.disablereloop()", true);
        } else {//button was released
        KONTROL1.loopbuttonDown=false;
        if (KONTROL1.doreloop===true) {
            if (KONTROL1.beatlooprollactive[group]===true && KONTROL1.beatlooprolllen[group]>0){
                engine.setValue(group, "beatlooproll_"+KONTROL1.beatlooprolllen[group]+"_activate", 0);
                KONTROL1.beatlooprollactive[group]=false;
                KONTROL1.beatlooprolllen[group]=-1;
                }else{
                engine.setValue(group, "reloop_exit", 1);
                }

            };
        KONTROL1.doreloop=true;
        engine.stopTimer(KONTROL1.reloopTimer);
        };

    };

KONTROL1.disablereloop = function disablereloop() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //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)
    KONTROL1.doreloop=false;
    };

KONTROL1.looplenDial = function looplenDial(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //activates variable length loop depending on dial position

    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    //is a loop active?  If not, exit loop when knob button is released
    if (engine.getValue(group, "loop_enabled")!=1){
        KONTROL1.exitKnobLoop=true;
        }

    //determine active loop length, default to 2 beats
    var arraypointer=false;
    var inc=(value==127)?1:-1;//increment - how far to move on each knob click
    for (i=1;i<12;i++){
        if (engine.getValue(group, "beatloop_"+KONTROL1.beatloopLengths[i]+"_enabled")==1){arraypointer=i;break;}
        }
    if (arraypointer==false){arraypointer=8;inc=0;}//default to 2 beat loop, don't increment
    arraypointer+=inc;
    arraypointer=(arraypointer<1)?1:arraypointer;
    arraypointer=(arraypointer>11)?11:arraypointer;

    var newLoopLen=KONTROL1.beatloopLengths[arraypointer];
    engine.setValue(group, "beatloop_"+newLoopLen+"_activate", 1);
    KONTROL1.loopLenLEDs("default",arraypointer);
    return true;
    };

KONTROL1.loopIn = function loopIn(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //Set or move loop in point
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (value>0){//button was pressed
        KONTROL1.loopinbuttonDown=true;
        KONTROL1.doloopin=true;
        KONTROL1.loopinbuttonTimer = engine.beginTimer(500, KONTROL1.disableloopin, true);
        } else {//button was released
        KONTROL1.loopinbuttonDown=false;
        if (KONTROL1.doloopin===true) {engine.setValue(group, "loop_in", 1);engine.setValue(group, "loop_in", 0);};
        KONTROL1.doloopin=true;
        engine.stopTimer(KONTROL1.loopinbuttonTimer);
        };
}

KONTROL1.loopOut = function loopOut(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //set or move loop out point
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (value>0){//button was pressed
        KONTROL1.loopoutbuttonDown=true;
        KONTROL1.doloopout=true;
        KONTROL1.loopoutbuttonTimer = engine.beginTimer(500, KONTROL1.disableloopout, true);
        } else {//button was released
        KONTROL1.loopoutbuttonDown=false;
        if (KONTROL1.doloopout===true) {engine.setValue(group, "loop_out", 1);};
        KONTROL1.doloopout=true;
        engine.stopTimer(KONTROL1.loopoutbuttonTimer);
        };
}

KONTROL1.disableloopin = function disableloopin() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //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)
    KONTROL1.doloopin=false;
    };

KONTROL1.disableloopout = function disableloopout() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //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)
    KONTROL1.doloopout=false;
    };

KONTROL1.loopMinus = function loopMinus(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //shrinks loop or moves loop back
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (KONTROL1.loopbuttonDown !== true && KONTROL1.loopoutbuttonDown !== true && KONTROL1.loopinbuttonDown !== true){engine.setValue(group, "loop_halve", 1); return false;}//shrink loop if no loop button down
    else if (KONTROL1.loopbuttonDown === true && engine.getValue(group, "loop_start_position")>=0 && engine.getValue(group, "loop_end_position")>=0 ){
        //move loop
        if (KONTROL1.debug>2){print("##doing move both back#####")};
        var interval =    KONTROL1.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;
        }
    else if (KONTROL1.loopinbuttonDown === true && engine.getValue(group, "loop_start_position")>=0){
        //move loop in point
        if (KONTROL1.debug>2){print("##doing move inpoint back#####")};
        var interval =    KONTROL1.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var start = engine.getValue(group, "loop_start_position");
        engine.setValue(group, "loop_start_position", start-interval);
        return true;
        }
    else if (KONTROL1.loopoutbuttonDown === true && engine.getValue(group, "loop_end_position")>=0){
        //move loop out point
        if (KONTROL1.debug>2){print("##doing move outpoint back#####")};
        var interval =    KONTROL1.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var end = engine.getValue(group, "loop_end_position");
        engine.setValue(group, "loop_end_position", end-interval);
        return true;
        }//end if
}

KONTROL1.wheelMoveLoop = function wheelMoveLoop (val){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    var val=(val>64)?-1:1;
    var interval =    val*KONTROL1.wheelloopmove*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");

    //shrinks loop or moves loop back
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (KONTROL1.loopbuttonDown !== true && KONTROL1.loopoutbuttonDown !== true && KONTROL1.loopinbuttonDown !== true){engine.setValue(group, "loop_halve", 1); return false;}//shrink loop if no loop button down
    else if (KONTROL1.loopbuttonDown === true && start>=0 && end>=0 ){
        //move loop
        if (KONTROL1.debug>2){print("##doing move both back#####")};
        engine.setValue(group, "loop_start_position", start-interval);
        engine.setValue(group, "loop_end_position", end-interval);
        return true;
        }
    else if (KONTROL1.loopinbuttonDown === true && start>=0){
        //move loop in point
        if (KONTROL1.debug>2){print("##doing move inpoint back#####")};
        engine.setValue(group, "loop_start_position", start-interval);
        return true;
        }
    else if (KONTROL1.loopoutbuttonDown === true && end>=0){
        //move loop out point
        if (KONTROL1.debug>2){print("##doing move outpoint back#####")};
        engine.setValue(group, "loop_end_position", end-interval);
        return true;
        }//end if

    return false;
    }

KONTROL1.moveLoop = function moveLoop(moveInPoint, moveOutPoint, increment) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //
    }

KONTROL1.loopPlus = function loopPlus(value, deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //grows loop or moves loop forward
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (KONTROL1.loopbuttonDown !== true && KONTROL1.loopoutbuttonDown !== true && KONTROL1.loopinbuttonDown !== true){engine.setValue(group, "loop_double", 1); return false;}//shrink loop if no loop button down
    else if (KONTROL1.loopbuttonDown === true && engine.getValue(group, "loop_start_position")>=0 && engine.getValue(group, "loop_end_position")>=0 ){
        //move loop
        if (KONTROL1.debug>2){print("##doing move both forward#####")};
        var interval =    KONTROL1.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;
        }
    else if (KONTROL1.loopinbuttonDown === true && engine.getValue(group, "loop_start_position")>=0){
        //move loop in point
        if (KONTROL1.debug>2){print("##doing move inpoint forward#####")};
        var interval =    KONTROL1.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var start = engine.getValue(group, "loop_start_position");
        engine.setValue(group, "loop_start_position", start+interval);
        return true;        }
    else if (KONTROL1.loopoutbuttonDown === true && engine.getValue(group, "loop_end_position")>=0){
        //move loop out point
        if (KONTROL1.debug>2){print("##doing move outpoint forward#####")};
        var interval =    KONTROL1.loopmove*engine.getValue(group, "track_samples")/engine.getValue(group, "duration");
        var end = engine.getValue(group, "loop_end_position");
        engine.setValue(group, "loop_end_position", end+interval);
        return true;
        }//end if
}

//sync and resume

KONTROL1.resumeSet = function resumeSet(deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //records the time the set button is pressed, and the current play position
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (engine.getValue(group, "playposition")>=0 && engine.getValue(group, "playposition")<=1){
        KONTROL1.channels[KONTROL1.curChannel]["resumepos"] = engine.getValue(group, "playposition");
        KONTROL1.channels[KONTROL1.curChannel]["resumetime"] = Date.now();//get time in milliseconds
        };
    KONTROL1.resumeLED();
    };

KONTROL1.resumeClear = function resumeClear(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    KONTROL1.channels[KONTROL1.curChannel]["resumepos"]=-1;
    KONTROL1.resumeLED();
    };

KONTROL1.resumeUnset = function resumeUnset(value,group,control) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //when a new track is loaded, turn off resume settings
    var ch;
    for (var k in KONTROL1.defaultDeck){
        if (KONTROL1.defaultDeck[k]==group){ch=k;}
        }

    if (KONTROL1.debug){print("##ch: "+ch)};
    KONTROL1.channels[ch]["resumepos"]=-1;
    KONTROL1.resumeLED();
    };


//tie in hooks
engine.connectControl("[Channel1]","LoadSelectedTrack","KONTROL1.resumeUnset");
engine.connectControl("[Channel2]","LoadSelectedTrack","KONTROL1.resumeUnset");
engine.connectControl("[Channel3]","LoadSelectedTrack","KONTROL1.resumeUnset");
engine.connectControl("[Channel4]","LoadSelectedTrack","KONTROL1.resumeUnset");

KONTROL1.resume = function resume(deck) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};
    //records the time the set button is pressed, and the current play position
    deck=(typeof deck !== 'undefined')?deck:"default";
    var group=KONTROL1.getGroup(deck);

    if (KONTROL1.channels[KONTROL1.curChannel]["resumepos"]>-1){
        var oldpos = KONTROL1.channels[KONTROL1.curChannel]["resumepos"]*engine.getValue(group, "track_samples");
        var seconds = (Date.now()- KONTROL1.channels[KONTROL1.curChannel]["resumetime"])/1000;//get elapsed time in seconds
        var samples = seconds*engine.getValue(group, "track_samplerate")*2;//how many samples to jump forward - multiplied by 2 for stereo samples
        var newpos = oldpos + samples;//new position in samples
        engine.setValue(group, "playposition", newpos/engine.getValue(group, "track_samples"));
        }else{KONTROL1.resumeSet();}
    KONTROL1.resumeLED();
    };

//scratch parameters
KONTROL1.wheelResArray=new Array(50,75,100,125,150,175,200,225,250,275,300,325,350);
KONTROL1.scratchRevArray=new Array(16,20,24,29,33,37,40,45,50,55,60,70,78);
KONTROL1.scratchAlphaArray=new Array(1/128,1/64,1/32,1/16,1/8,1/4,1/2,1);
KONTROL1.scratchBetaArray=new Array(128,64,32,16,8,4,2,1);

KONTROL1.wheelResIndex=4;
KONTROL1.scratchRevIndex=7;
KONTROL1.scratchAlphaIndex=3;
KONTROL1.scratchBetaIndex=2;

KONTROL1.wheelResDefaultIndex=4;
KONTROL1.scratchRevDefaultIndex=7;
KONTROL1.scratchAlphaDefaultIndex=3;
KONTROL1.scratchBetaDefaultIndex=2;







KONTROL1.setScratchRev = function setScratchRev(value) {//set scratch parameter - record RPM
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var inc=(value==127)?1:-1;//increment - how far to move on each knob click
    KONTROL1.scratchRevIndex+=inc;
    KONTROL1.scratchRevIndex=(KONTROL1.scratchRevIndex<0)?0:KONTROL1.scratchRevIndex;
    KONTROL1.scratchRevIndex=(KONTROL1.scratchRevIndex>KONTROL1.scratchRevArray.length-1)?KONTROL1.scratchRevArray.length-1:KONTROL1.scratchRevIndex;

    KONTROL1.scratchParameterLEDs(0x37,KONTROL1.scratchRevArray,KONTROL1.scratchRevIndex);
    return true;
    }

KONTROL1.setScratchWheelRes = function setScratchWheelRes(value) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var inc=(value==127)?1:-1;//increment - how far to move on each knob click
    KONTROL1.wheelResIndex+=inc;
    KONTROL1.wheelResIndex=(KONTROL1.wheelResIndex<0)?0:KONTROL1.wheelResIndex;
    KONTROL1.wheelResIndex=(KONTROL1.wheelResIndex>KONTROL1.wheelResArray.length-1)?KONTROL1.wheelResArray.length-1:KONTROL1.wheelResIndex;

    KONTROL1.scratchParameterLEDs(0x36,KONTROL1.wheelResArray,KONTROL1.wheelResIndex);
    return true;
    }

KONTROL1.setScratchAlpha = function setScratchAlpha(value) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var inc=(value==127)?1:-1;//increment - how far to move on each knob click
    KONTROL1.scratchAlphaIndex+=inc;
    KONTROL1.scratchAlphaIndex=(KONTROL1.scratchAlphaIndex<0)?0:KONTROL1.scratchAlphaIndex;
    KONTROL1.scratchAlphaIndex=(KONTROL1.scratchAlphaIndex>KONTROL1.scratchAlphaArray.length-1)?KONTROL1.scratchAlphaArray.length-1:KONTROL1.scratchAlphaIndex;

    KONTROL1.scratchParameterLEDs(0x35,KONTROL1.scratchAlphaArray,KONTROL1.scratchAlphaIndex);
    return true;
}

KONTROL1.setScratchBeta = function setScratchBeta(value) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var inc=(value==127)?1:-1;//increment - how far to move on each knob click
    KONTROL1.scratchBetaIndex+=inc;
    KONTROL1.scratchBetaIndex=(KONTROL1.scratchBetaIndex<0)?0:KONTROL1.scratchBetaIndex;
    KONTROL1.scratchBetaIndex=(KONTROL1.scratchBetaIndex>KONTROL1.scratchBetaArray.length-1)?KONTROL1.scratchBetaArray.length-1:KONTROL1.scratchBetaIndex;

    KONTROL1.scratchParameterLEDs(0x34,KONTROL1.scratchBetaArray,KONTROL1.scratchBetaIndex);
    return true;
}

//############################################################################
//BASIC FUNCTIONS - stuff that makes the other stuff work
//############################################################################

KONTROL1.getControl=function getControl(control){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    //returns the array of control commands and LED state functions for the control pressed for the current bank/mods
    var bank=KONTROL1.getBankCode();//check for setting on current bank with mods first
    var nomodbank=KONTROL1.getNoModBankCode();//then current bank without mods
    var noshiftbank=KONTROL1.getNoShiftBankCode();//then for setting without shift button.  If nothing found, then use default bank.
    var noshiftnomodbank=KONTROL1.getNoShiftNoModBankCode();//then for setting without shift button.  If nothing found, then use default bank.
    var defaultbankwmod="bank1a-"+KONTROL1.getModCode();
    var defaultbank="bank1a-ooooooo";
    if(KONTROL1.controls[control][bank] !== undefined ){return KONTROL1.controls[control][bank];}
    if(KONTROL1.controls[control][nomodbank] !== undefined ){return KONTROL1.controls[control][nomodbank];}
    if(KONTROL1.controls[control][noshiftbank] !== undefined ){return KONTROL1.controls[control][noshiftbank];}
    if(KONTROL1.controls[control][noshiftnomodbank] !== undefined ){return KONTROL1.controls[control][noshiftnomodbank];}
    if(KONTROL1.controls[control][defaultbankwmod] !== undefined ){return KONTROL1.controls[control][defaultbankwmod];}
    return KONTROL1.controls[control][defaultbank];
    }

KONTROL1.getModCode = function getModCode() {//returns the mod code to use as part of the key to access the controls object
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr=KONTROL1.mod["p1"]["state"]+KONTROL1.mod["p2"]["state"]+KONTROL1.mod["k1"]["state"]+KONTROL1.mod["k2"]["state"]+KONTROL1.mod["k3"]["state"]+KONTROL1.mod["k4"]["state"]+KONTROL1.mod["s"]["state"];
    return outstr;
    };

KONTROL1.getBankCode = function getBankCode() {//returns the bank code (with mods) to use as a key to access the controls object
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr=KONTROL1.channels[KONTROL1.curChannel]["curBank"]+KONTROL1.channels[KONTROL1.curChannel]["curShift"]+"-"+KONTROL1.getModCode();
    return outstr;
    };

KONTROL1.getNoModBankCode = function getNoModBankCode() {//return current bank with no mods
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr=KONTROL1.channels[KONTROL1.curChannel]["curBank"]+KONTROL1.channels[KONTROL1.curChannel]["curShift"]+"-ooooooo";
    return outstr;
    };

KONTROL1.getNoShiftBankCode = function getNoShiftBankCode() {//return current bank with mods but no shift
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr=KONTROL1.channels[KONTROL1.curChannel]["curBank"]+"a-"+KONTROL1.getModCode();
    return outstr;
    };

KONTROL1.getNoShiftNoModBankCode = function getNoShiftNoModBankCode() {//return current bank with no shifts or mods
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr=KONTROL1.channels[KONTROL1.curChannel]["curBank"]+"a-ooooooo";
    return outstr;
    };

KONTROL1.getGroup = function getGroup(groupstr) {//return Mixxx group
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var outstr;
    if (groupstr=="default"){outstr=KONTROL1.defaultDeck[KONTROL1.curChannel];}
    else if (groupstr=="alt"){outstr=KONTROL1.altDeck[KONTROL1.curChannel];}
    else if (groupstr=="sync"){outstr=KONTROL1.syncDeck[KONTROL1.curChannel];}
    else {outstr=groupstr;}
    if (KONTROL1.debug>2){print("##getGroup groupstr: "+groupstr)};
    if (KONTROL1.debug>2){print("##getGroup outstr: "+outstr)};
    return outstr;
    };

KONTROL1.getFunctionName = function getFunctionName(){
    var re = /function (.*?)\(/
    var s = KONTROL1.getFunctionName.caller.toString();
    var m = re.exec( s )
    return m[1];
    }


//############################################################################
//TEST FUNCTIONS
//############################################################################
KONTROL1.testval=60;




KONTROL1.testconnect=function testconnect(value,group,control){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    print ("CONNECT CONTROL TEST");
    print ("value: "+value);
    print ("group: "+group);
    print ("control: "+control);
    //var args = Array.prototype.slice.call(arguments)
    //for (var key in args){
    //    print (arguments[key]);
    //    }
    }

KONTROL1.testvalknob=function testvalknob(channel, control, value, status, group){
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    var inc=(value==127)?3:-3;
    KONTROL1.testval+=inc;
    if (KONTROL1.testval>127)KONTROL1.testval=127;
    if (KONTROL1.testval<0)KONTROL1.testval=0;
    midi.sendShortMsg(0xB0, 0x37, KONTROL1.testval);
    }


KONTROL1.testflash2 = function testflash2() {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    if (KONTROL1.flashon==0){
        midi.sendShortMsg(0xB0, 0x37, 0);
        KONTROL1.flashon=1;
        KONTROL1.flashcount++;
        if (KONTROL1.flashcount>3){KONTROL1.stopFlashTimer();}
        }else {
        midi.sendShortMsg(0xB0, 0x37, 127);
        KONTROL1.flashon=0;
        KONTROL1.flashcount++;
        if (KONTROL1.flashcount>3){KONTROL1.stopFlashTimer();}
        }

    };


KONTROL1.test = function test(channel, control, value, status, group) {
    if (KONTROL1.debug>2){print("##function: "+KONTROL1.getFunctionName())};

    print("channel: "+channel);
    print("control: "+control);
    print("control.toString: "+control.toString(16));
    print("value: "+value);
    print("status: "+status);
    print("group: "+group);
    print("currentbank: "+KONTROL1.channels[0]["curBank"]);
    print("currentShift: "+KONTROL1.channels[0]["curShift"]);
    print("currentBank code: "+KONTROL1.getBankCode());
    };


    //KONTROL1.ledTimer = engine.beginTimer(250, KONTROL1.testflash);
    //midi.sendShortMsg(status, ctrl, state);
    //engine.stopTimer(KONTROL1.ledTimer);





//############################################################################
//INITIALIZE CONTROLS
//############################################################################

//initialize control object for buttons, knobs, etc.
KONTROL1.controls={};//array of controls, with commands and LED hooks

//reference array - array of control items by name
KONTROL1.getIt={"play":0x1b, "cue":0x1c, "sync":0x1d, "wheeltouch":0x1f, "wheelmove":0x20,"pslider":0x21,"pminus":0x19,"pplus":0x1e,"shift":0x1a,"k1": 0x37,"k2": 0x36,"k3": 0x35,"k4": 0x34,"kb1": 0x04,"kb2": 0x03,"kb3": 0x02,"kb4": 0x01,"b1":0x09,"b2":0x08,"b3":0x07,"b4": 0x06,"b5": 0x0e,"b6": 0x0d,"b7": 0x0c,"b8": 0x0b,"b9": 0x13,"b10": 0x12,"b11": 0x11,"b12": 0x10,"b13": 0x18,"b14": 0x17,"b15": 0x16,"b16": 0x15};//reference array - everything else by name

//initialize object
//row 1
KONTROL1.controls[0x09]={};KONTROL1.controls[0x08]={};KONTROL1.controls[0x07]={};KONTROL1.controls[0x06]={};
//row 2
KONTROL1.controls[0x0e]={};KONTROL1.controls[0x0d]={};KONTROL1.controls[0x0c]={};KONTROL1.controls[0x0b]={};
//row 3
KONTROL1.controls[0x13]={};KONTROL1.controls[0x12]={};KONTROL1.controls[0x11]={};KONTROL1.controls[0x10]={};
//row 4
KONTROL1.controls[0x18]={};KONTROL1.controls[0x17]={};KONTROL1.controls[0x16]={};KONTROL1.controls[0x15]={};
//sync, cue, play
KONTROL1.controls[0x1d]={};KONTROL1.controls[0x1c]={};KONTROL1.controls[0x1b]={};
//wheel touch, wheel move
KONTROL1.controls[0x1f]={};KONTROL1.controls[0x20]={};
//pitch slider
KONTROL1.controls[0x21]={};
//mod1 (pitch -), mod2 (pitch +), shift
KONTROL1.controls[0x19]={};KONTROL1.controls[0x1e]={};KONTROL1.controls[0x1a]={};
//knob buttons
KONTROL1.controls[0x04]={};KONTROL1.controls[0x03]={};KONTROL1.controls[0x02]={};KONTROL1.controls[0x01]={};
//knobs
KONTROL1.controls[0x37]={};KONTROL1.controls[0x36]={};KONTROL1.controls[0x35]={};KONTROL1.controls[0x34]={};


//KONTROL1.controls[midino]["bank-ooooooo"]=new Array('PressEvalStr', 'ReleaseEvalStr', 'LEDstateType', 'LEDStr1', 'LEDstr2', 'LEDstr3');
//LEDstateType = false if control has no LED, "hook" if using connectControl, "eval" if using an eval statement to get LED state
//LEDstateType = "off" if LED is always off, "on" if LED is always on
//for HOOKS: LEDstr1 = mixxx control to connect to, LEDstr2 = script function
//for HOOKS: LEDstr3 = mixxx control group - literals accepted, or "default", "sync" or "alt" = default, sync or alt deck for that channel
//for EVAL: LEDstr1 = javascript to evaluate to get LED state, LEDstr2 and 3 are not used
//mod = o=off I=on - order: mod1 mod2 knob1 knob2 knob3 knob4 shift

//BANK 1a - STANDARD CONTROLS (buttons default to this if nothing is set for other banks/mods
//row 1
KONTROL1.controls[0x09]["bank1a-ooooooo"]=new Array('KONTROL1.kill("all", value, "default");', 'KONTROL1.kill("all", value, "default");', 'eval', '(KONTROL1.channels[KONTROL1.curChannel]["isMuted"]==true)?127:0;', 'LEDstr2', 'LEDstr3');
KONTROL1.controls[0x08]["bank1a-ooooooo"]=new Array('KONTROL1.kill("high", value, "default");', 'KONTROL1.kill("high", value, "default");', 'hook', 'filterHighKill', 'KONTROL1.doFilterKillLEDs', 'default');
KONTROL1.controls[0x07]["bank1a-ooooooo"]=new Array('KONTROL1.kill("mid", value, "default");', 'KONTROL1.kill("mid", value, "default");', 'hook', 'filterMidKill', 'KONTROL1.doFilterKillLEDs', 'default');
KONTROL1.controls[0x06]["bank1a-ooooooo"]=new Array('KONTROL1.kill("low", value, "default");', 'KONTROL1.kill("low", value, "default");', 'hook', 'filterLowKill', 'KONTROL1.doFilterKillLEDs', 'default');

//row 2 - hotcues
for (i=5;i<9;i++){
    var num=i;
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-ooooooo"]=new Array('KONTROL1.hotcueButton('+num+')', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-Ioooooo"]=new Array('KONTROL1.cueClear('+num+', '+KONTROL1.getIt["b"+i]+')', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');

    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-ooIoooo"]=new Array('KONTROL1.cueLoop('+num+', .25)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-oooIooo"]=new Array('KONTROL1.cueLoop('+num+', .5)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-ooooIoo"]=new Array('KONTROL1.cueLoop('+num+', 1)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-oooooIo"]=new Array('KONTROL1.cueLoop('+num+', 2)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
}

//row 3
KONTROL1.controls[0x13]["bank1a-ooooooo"]=new Array('KONTROL1.beatLoop(1, value, "default");', 'KONTROL1.beatLoop(1, value, "default");', 'hook', "beatloop_1_enabled", 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x12]["bank1a-ooooooo"]=new Array('KONTROL1.beatLoop(2, value, "default");', 'KONTROL1.beatLoop(2, value, "default");', 'hook', "beatloop_2_enabled", 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x11]["bank1a-ooooooo"]=new Array('KONTROL1.beatLoop(4, value, "default");', 'KONTROL1.beatLoop(4, value, "default");', 'hook', "beatloop_4_enabled", 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x10]["bank1a-ooooooo"]=new Array('KONTROL1.reloopButton(value, "default");', 'KONTROL1.reloopButton(value, "default");', 'hook', "loop_enabled", 'KONTROL1.lightButtonLEDs', 'default');

//row 4
KONTROL1.controls[0x18]["bank1a-ooooooo"]=new Array('KONTROL1.loopIn(value, "default");', 'KONTROL1.loopIn(value, "default");', 'hook', 'loop_start_position', 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x17]["bank1a-ooooooo"]=new Array('KONTROL1.loopOut(value, "default");', 'KONTROL1.loopOut(value, "default");', 'hook', 'loop_end_position', 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x16]["bank1a-ooooooo"]=new Array('KONTROL1.loopMinus(value, "default");', false, 'off', 'LEDStr1', 'LEDstr2', 'LEDstr3');
KONTROL1.controls[0x15]["bank1a-ooooooo"]=new Array('KONTROL1.loopPlus(value, "default");', false, 'off', 'LEDStr1', 'LEDstr2', 'LEDstr3');


//sync, cue, play - no mod - slip mode toggle, cue default, play
//SLIP MODE CAN't BE CLEARED YET - Using homebrew slip mode - KONTROL1.controls[0x1d]["bank1a-ooooooo"]=new Array('KONTROL1.toggleBinaryControl("slip_enabled");', false, 'hook', 'slip_enabled', 'KONTROL1.syncLED', 'default');
KONTROL1.controls[0x1d]["bank1a-ooooooo"]=new Array('KONTROL1.resume("default");', false, 'eval', 'KONTROL1.resumeLED()');
KONTROL1.controls[0x1c]["bank1a-ooooooo"]=new Array('KONTROL1.toggleBinaryControl("cue_default", 1);', 'KONTROL1.toggleBinaryControl("cue_default", 0);', "off");
KONTROL1.controls[0x1b]["bank1a-ooooooo"]=new Array('KONTROL1.toggleBinaryControl("play");', false, 'hook', 'play', 'KONTROL1.playLED', 'default');

//sync, cue, play - MOD1 - slip mode reset, quantize, keylock
//SLIP MODE CAN't BE CLEARED YET - Using homebrew slip mode - KONTROL1.controls[0x1d]["bank1a-Ioooooo"]=new Array('KONTROL1.toggleBinaryControl("slip_enabled", 1);', false, 'hook', 'slip_enabled', 'KONTROL1.syncLED', 'default');
KONTROL1.controls[0x1d]["bank1a-Ioooooo"]=new Array('KONTROL1.resumeClear("default");', 'ReleaseEvalStr', 'eval', 'KONTROL1.resumeLED()');//mod P1
KONTROL1.controls[0x1c]["bank1a-Ioooooo"]=new Array('KONTROL1.toggleBinaryControl("quantize");', false, 'hook', 'quantize', 'KONTROL1.cueLED', 'default');
KONTROL1.controls[0x1b]["bank1a-Ioooooo"]=new Array('KONTROL1.toggleBinaryControl("keylock");', false, 'hook', 'play', 'KONTROL1.playLED', 'default');

//sync, cue, play - MOD2 - beatsync, beatgrid align, bpm tap
KONTROL1.controls[0x1d]["bank1a-oIooooo"]=new Array('KONTROL1.toggleBinaryControl("beatsync", 1);', 'KONTROL1.toggleBinaryControl("beatsync", 0);', 'off');
KONTROL1.controls[0x1c]["bank1a-oIooooo"]=new Array('KONTROL1.toggleBinaryControl("beats_translate_curpos", 1);', 'KONTROL1.toggleBinaryControl("beats_translate_curpos", 0);', 'off');
KONTROL1.controls[0x1b]["bank1a-oIooooo"]=new Array('KONTROL1.toggleBinaryControl("bpm_tap", 1);', 'KONTROL1.toggleBinaryControl("bpm_tap", 0);', 'off');

//sync, cue, play - knob1 - flanger, PFL, go to start of track
KONTROL1.controls[0x1d]["bank1a-ooIoooo"]=new Array('KONTROL1.toggleBinaryControl("flanger");', false, 'hook', 'flanger', 'KONTROL1.syncLED', 'default');
KONTROL1.controls[0x1c]["bank1a-ooIoooo"]=new Array('KONTROL1.toggleBinaryControl("pfl");', false, 'hook', 'pfl', 'KONTROL1.cueLED', 'default');
KONTROL1.controls[0x1b]["bank1a-ooIoooo"]=new Array('KONTROL1.toggleBinaryControl("start",1);', 'KONTROL1.toggleBinaryControl("start",0);', 'off');

//sync, cue, play - knob2 - spinbacks

//sync, cue, play - knob3 - rewind, ff, reverse play
KONTROL1.controls[0x1d]["bank1a-ooooIoo"]=new Array('KONTROL1.toggleBinaryControl("back", 1);', 'KONTROL1.toggleBinaryControl("back", 0);', 'hook', 'back', 'KONTROL1.syncLED', 'default');
KONTROL1.controls[0x1c]["bank1a-ooooIoo"]=new Array('KONTROL1.toggleBinaryControl("fwd", 1);', 'KONTROL1.toggleBinaryControl("fwd", 0);', 'hook', 'fwd', 'KONTROL1.cueLED', 'default');
KONTROL1.controls[0x1b]["bank1a-ooooIoo"]=new Array('KONTROL1.toggleBinaryControl("reverse", 1);', 'KONTROL1.toggleBinaryControl("reverse", 0);', 'hook', 'reverse', 'KONTROL1.playLED', 'default');

//sync, cue, play - knob4 - loop halve, loop double, reloop_exit
KONTROL1.controls[0x1d]["bank1a-oooooIo"]=new Array('KONTROL1.toggleBinaryControl("loop_halve", 1);', 'KONTROL1.toggleBinaryControl("loop_halve", 0);', 'off');
KONTROL1.controls[0x1c]["bank1a-oooooIo"]=new Array('KONTROL1.toggleBinaryControl("loop_double", 1);', 'KONTROL1.toggleBinaryControl("loop_double", 0);', 'off');
KONTROL1.controls[0x1b]["bank1a-oooooIo"]=new Array('KONTROL1.toggleBinaryControl("reloop_exit", 1);', 'KONTROL1.toggleBinaryControl("reloop_exit", 0);', 'off');



//shift
KONTROL1.controls[0x1a]["bank1a-ooooooo"]=new Array('KONTROL1.shiftPress()', 'KONTROL1.shiftRelease()', "eval", '(KONTROL1.channels[KONTROL1.curChannel]["curShift"]=="a")?0:127;', 'LEDstr2', 'LEDstr3');

//mod1 (pitch -)
KONTROL1.controls[0x19]["bank1a-ooooooo"]=new Array('KONTROL1.modPress("p1");', 'KONTROL1.modRelease("p1");', false);
//mod2 (pitch +)
KONTROL1.controls[0x1e]["bank1a-ooooooo"]=new Array('KONTROL1.modPress("p2");', 'KONTROL1.modRelease("p2");', false);

//MOD1 and MOD2 - w. knob1 - pitch temp
KONTROL1.controls[0x19]["bank1a-ooIoooo"]=new Array('KONTROL1.toggleBinaryControl("rate_temp_down", 1);', 'KONTROL1.toggleBinaryControl("rate_temp_down", 0);', false);
KONTROL1.controls[0x1e]["bank1a-ooIoooo"]=new Array('KONTROL1.toggleBinaryControl("rate_temp_up", 1);', 'KONTROL1.toggleBinaryControl("rate_temp_up", 0);', false);

//MOD1 and MOD2 - w. knob2 - pitch perm
KONTROL1.controls[0x19]["bank1a-oooIooo"]=new Array('KONTROL1.toggleBinaryControl("rate_perm_down", 1);', 'KONTROL1.toggleBinaryControl("rate_perm_down", 0);', false);
KONTROL1.controls[0x1e]["bank1a-oooIooo"]=new Array('KONTROL1.toggleBinaryControl("rate_perm_up", 1);', 'KONTROL1.toggleBinaryControl("rate_perm_up", 0);', false);

//MOD1 and MOD2 - w. knob3 - pitch temp small
KONTROL1.controls[0x19]["bank1a-ooooIoo"]=new Array('KONTROL1.toggleBinaryControl("rate_temp_down_small", 1);', 'KONTROL1.toggleBinaryControl("rate_temp_down_small", 0);', false);
KONTROL1.controls[0x1e]["bank1a-ooooIoo"]=new Array('KONTROL1.toggleBinaryControl("rate_temp_up_small", 1);', 'KONTROL1.toggleBinaryControl("rate_temp_up_small", 0);', false);

//MOD1 and MOD2 - w. knob4 - pitch perm small
KONTROL1.controls[0x19]["bank1a-oooooIo"]=new Array('KONTROL1.toggleBinaryControl("rate_perm_down_small", 1);', 'KONTROL1.toggleBinaryControl("rate_perm_down_small", 0);', false);
KONTROL1.controls[0x1e]["bank1a-oooooIo"]=new Array('KONTROL1.toggleBinaryControl("rate_perm_up_small", 1);', 'KONTROL1.toggleBinaryControl("rate_perm_up_small", 0);', false);

//knob buttons
KONTROL1.controls[0x04]["bank1a-ooooooo"]=new Array('KONTROL1.knobPress("k1");', 'KONTROL1.knobRelease("k1");', false);
KONTROL1.controls[0x03]["bank1a-ooooooo"]=new Array('KONTROL1.knobPress("k2");', 'KONTROL1.knobRelease("k2");', false);
KONTROL1.controls[0x02]["bank1a-ooooooo"]=new Array('KONTROL1.knobPress("k3");', 'KONTROL1.knobRelease("k3");', false);
KONTROL1.controls[0x01]["bank1a-ooooooo"]=new Array('KONTROL1.knobPress("k4");', 'KONTROL1.knobRelease("k4");', false);
//knob buttons
KONTROL1.controls[0x04]["bank1a-Ioooooo"]=new Array('KONTROL1.resetKnob("pregain");', false, false);
KONTROL1.controls[0x03]["bank1a-Ioooooo"]=new Array('KONTROL1.resetKnob("filterHigh");', false, false);
KONTROL1.controls[0x02]["bank1a-Ioooooo"]=new Array('KONTROL1.resetKnob("filterMid");', false, false);
KONTROL1.controls[0x01]["bank1a-Ioooooo"]=new Array('KONTROL1.resetKnob("filterLow");', false, false);

//knobs
KONTROL1.controls[0x37]["bank1a-ooooooo"]=new Array('KONTROL1.logKnobAdjust("default", "pregain", 0, 4, value)', false, "hook", 'pregain', 'KONTROL1.doLogKnobLEDs', 'default');
KONTROL1.controls[0x36]["bank1a-ooooooo"]=new Array('KONTROL1.logKnobAdjust("default", "filterHigh", 0, 4, value)', false, "hook", 'filterHigh', 'KONTROL1.doLogKnobLEDs', 'default');
KONTROL1.controls[0x35]["bank1a-ooooooo"]=new Array('KONTROL1.logKnobAdjust("default", "filterMid", 0, 4, value)', false, "hook", 'filterMid', 'KONTROL1.doLogKnobLEDs', 'default');
KONTROL1.controls[0x34]["bank1a-ooooooo"]=new Array('KONTROL1.logKnobAdjust("default", "filterLow", 0, 4, value)', false, "hook", 'filterLow', 'KONTROL1.doLogKnobLEDs', 'default');

//knobs w p1 p2 mods - scratch parameters
KONTROL1.controls[0x37]["bank1a-IIooooo"]=new Array('KONTROL1.setScratchRev(value)', false, "eval", 'KONTROL1.scratchParameterLEDs(0x37,KONTROL1.scratchRevArray,KONTROL1.scratchRevIndex);');
KONTROL1.controls[0x36]["bank1a-IIooooo"]=new Array('KONTROL1.setScratchWheelRes(value)', false, "eval", 'KONTROL1.scratchParameterLEDs(0x36,KONTROL1.wheelResArray,KONTROL1.wheelResIndex);');
KONTROL1.controls[0x35]["bank1a-IIooooo"]=new Array('KONTROL1.setScratchAlpha(value)', false, "eval", 'KONTROL1.scratchParameterLEDs(0x35,KONTROL1.scratchAlphaArray,KONTROL1.scratchAlphaIndex);');
KONTROL1.controls[0x34]["bank1a-IIooooo"]=new Array('KONTROL1.setScratchBeta(value)', false, "eval", 'KONTROL1.scratchParameterLEDs(0x34,KONTROL1.scratchBetaArray,KONTROL1.scratchBetaIndex);');


//loop length adjust
KONTROL1.controls[0x34]["bank1a-oooooIo"]=new Array('KONTROL1.looplenDial(value, "default");', false, "eval", 'KONTROL1.loopLenLEDs("default");');

//hotcue loops, shown when knob 4 is pressed
for (i=1;i<17;i++){
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1a-oooooIo"]=new Array('KONTROL1.cueLoop('+i+', 2);', false, 'hook', 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank1b-oooooIo"]=new Array('KONTROL1.cueLoop('+(i+16)+', 2);', false, 'hook', 'hotcue_'+(i+16)+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
}


//wheel touch
KONTROL1.controls[0x1f]["bank1a-ooooooo"]=new Array('KONTROL1.wheelTouch(channel, control, value, status, group)', 'KONTROL1.wheelTouch(channel, control, value, status, group)', false);
//wheel move
KONTROL1.controls[0x20]["bank1a-ooooooo"]=new Array('KONTROL1.wheelTurn(channel, control, value, status, group)', false, false);

//pitch slider
KONTROL1.controls[0x21]["bank1a-ooooooo"]=new Array('engine.setValue(KONTROL1.getGroup("default"), "rate", (value-64)/64);', false, false);




//BANK 2a - HOTCUES - pitch minus button clears them
for (i=1;i<17;i++){
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-ooooooo"]=new Array('KONTROL1.hotcueButton('+i+')', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-Ioooooo"]=new Array('KONTROL1.cueClear('+i+', '+KONTROL1.getIt["b"+i]+')', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');

    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-ooIoooo"]=new Array('KONTROL1.cueLoop('+i+', .25)', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-oooIooo"]=new Array('KONTROL1.cueLoop('+i+', .5)', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-ooooIoo"]=new Array('KONTROL1.cueLoop('+i+', 1)', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2a-oooooIo"]=new Array('KONTROL1.cueLoop('+i+', 2)', false, "hook", 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
}

//BANK 2b - HOTCUES - pitch minus button clears them.  knob buttons activate hotcue loops
for (i=1;i<17;i++){
    var num=i+16;
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-ooooooo"]=new Array('KONTROL1.hotcueButton('+num+')', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-Ioooooo"]=new Array('KONTROL1.cueClear('+num+', '+KONTROL1.getIt["b"+i]+')', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');

    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-ooIoooo"]=new Array('KONTROL1.cueLoop('+num+', .25)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-oooIooo"]=new Array('KONTROL1.cueLoop('+num+', .5)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-ooooIoo"]=new Array('KONTROL1.cueLoop('+num+', 1)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank2b-oooooIo"]=new Array('KONTROL1.cueLoop('+num+', 2)', false, "hook", 'hotcue_'+num+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
}

//BANK 3a - Loops, beatjumps, beatlooprolls
for (i=1;i<12;i++){
    len=KONTROL1.beatloopLengths[i];
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-ooooooo"]=new Array('KONTROL1.beatLoop('+len+', value, "default");', 'KONTROL1.beatLoop('+len+', value, "default");', 'hook', "beatloop_"+len+"_enabled", 'KONTROL1.LoopBankLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-oIooooo"]=new Array('KONTROL1.beatLoopRoll('+len+', value, "default");', 'KONTROL1.beatLoopRoll('+len+', value, "default");', 'hook', "beatloop_"+len+"_enabled", 'KONTROL1.LoopBankLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-Ioooooo"]=new Array('KONTROL1.beatjump('+len+');', false, 'hook', false);
}

//reloop
KONTROL1.controls[0x10]["bank3a-ooooooo"]=new Array('KONTROL1.reloopButton(value, "default");', 'KONTROL1.reloopButton(value, "default");', 'hook', "loop_enabled", 'KONTROL1.LoopBankLEDs', 'default');

//row 4
KONTROL1.controls[0x18]["bank3a-ooooooo"]=new Array('KONTROL1.loopIn(value, "default");', 'KONTROL1.loopIn(value, "default");', 'hook', 'loop_start_position', 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x17]["bank3a-ooooooo"]=new Array('KONTROL1.loopOut(value, "default");', 'KONTROL1.loopOut(value, "default");', 'hook', 'loop_end_position', 'KONTROL1.lightButtonLEDs', 'default');
KONTROL1.controls[0x16]["bank3a-ooooooo"]=new Array('KONTROL1.loopMinus(value, "default");', false, 'LEDstateType', 'LEDStr1', 'LEDstr2', 'LEDstr3');
KONTROL1.controls[0x15]["bank3a-ooooooo"]=new Array('KONTROL1.loopPlus(value, "default");', false, 'LEDstateType', 'LEDStr1', 'LEDstr2', 'LEDstr3');

//hotcue loops, save loop in point
for (i=1;i<17;i++){
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-ooIoooo"]=new Array('KONTROL1.saveLoop('+i+');', false, 'hook', 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-oooIooo"]=new Array('KONTROL1.saveLoop('+(i+16)+');', false, 'hook', 'hotcue_'+(i+16)+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-ooooIoo"]=new Array('KONTROL1.cueLoop('+i+', 1)', false, 'hook', 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3a-oooooIo"]=new Array('KONTROL1.cueLoop('+i+', 2);', false, 'hook', 'hotcue_'+i+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3b-ooooIoo"]=new Array('KONTROL1.cueLoop('+(i+16)+', 1)', false, 'hook', 'hotcue_'+(i+16)+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
    KONTROL1.controls[KONTROL1.getIt["b"+i]]["bank3b-oooooIo"]=new Array('KONTROL1.cueLoop('+(i+16)+', 2);', false, 'hook', 'hotcue_'+(i+16)+'_enabled', 'KONTROL1.doHotcueLEDs', 'default');
}




//########################################
// LED LOOKUP ARRAYS
//########################################

//simple list of all the available leds
KONTROL1.allLEDs=new Array(0x09,0x08,0x07,0x06,0x0e,0x0d,0x0c,0x0b,0x13,0x12,0x11,0x10,0x18,0x17,0x16,0x15,0x1d,0x1c,0x1b,0x1a,0x37,0x36,0x35,0x34);
KONTROL1.knobLEDs=new Array(0x37, 0x36, 0x35, 0x34);//lists only the knob leds

//hotcue array, allows finding LED address by hotcue number 1-32
KONTROL1.hotcueLEDs=new Array(0,0x09,0x08,0x07,0x06,0x0e,0x0d,0x0c,0x0b,0x13,0x12,0x11,0x10,0x18,0x17,0x16,0x15, 0x09,0x08,0x07,0x06,0x0e,0x0d,0x0c,0x0b,0x13,0x12,0x11,0x10,0x18,0x17,0x16,0x15);

//button LED array - allows finding which button to light up from the control name (for use with hooks) (lightButtonLeds uses this)
KONTROL1.buttonLEDs={loop_enabled:0x10,beatloop_1_enabled:0x13,beatloop_2_enabled:0x12,beatloop_4_enabled:0x11,loop_start_position:0x18,loop_end_position:0x17,play:0x1b};

//array for loop bank
KONTROL1.loopbuttonLEDs={loop_enabled:0x10,loop_start_position:0x18,loop_end_position:0x17,play:0x1b};
for (i=1;i<12;i++){
    len=KONTROL1.beatloopLengths[i];
    KONTROL1.loopbuttonLEDs["beatloop_"+len+"_enabled"]=KONTROL1.getIt["b"+i];
}
