home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Aplikacje_64-bitowe / Mixxx / mixxx-1.9.0-win64.exe / midi / Stanton-SCS1d-scripts.js < prev    next >
Text File  |  2011-02-15  |  93KB  |  1,970 lines

  1. /****************************************************************/
  2. /*      Stanton SCS.1d MIDI controller script vPre              */
  3. /*          Copyright (C) 2009-2010, Sean M. Pappalardo         */
  4. /*      but feel free to tweak this to your heart's content!    */
  5. /*      For Mixxx version 1.9.x, controller firmware v1.25      */
  6. /****************************************************************/
  7.  
  8. function StantonSCS1d() {}
  9.  
  10. // ----------   Customization variables ----------
  11. //      See http://mixxx.org/wiki/doku.php/stanton_scs.1d_mixxx_user_guide  for details
  12. StantonSCS1d.pitchRanges = [ 0.08, 0.16, 0.25, 0.5 ];   // Pitch ranges (can add more, but the .rangeButton function would need to be extended.)
  13. StantonSCS1d.fastDeckChange = false;    // Skip the flashy lights if true, for juggling
  14. StantonSCS1d.globalMode = false;        // Stay in the current modes on deck changes if true
  15. StantonSCS1d.platterSpeed = 0;          // Speed of the platter at 0% pitch: 0=33 RPM, 1=45 RPM
  16. StantonSCS1d.deckChangeWait = 1000;     // Time in milliseconds to hold the Deck Change button down to avoid changing decks
  17. StantonSCS1d.padVelocity = true;        // Use the velocity values when recalling cues on the trigger pads
  18. StantonSCS1d.crossFader = true;         // Use the pitch slider to adjust cross-fader when Range is held down
  19. StantonSCS1d.browseDamp = 2;            // Number of platter ticks to move the highlight one item when browsing the library
  20.  
  21. // These values are heavily latency-dependent. They're preset for 10ms and will need tuning for other latencies. (For 2ms, try 0.885, 0.15, and 1.5.)
  22. StantonSCS1d.scratching = {     "sensitivity":0.11,          // How much the audio moves for a given circle arc (higher=faster response, 0<n<1)
  23.                                 "stoppedMultiplier":1.0 };  // Correction for when the deck is stopped (set higher for higher latencies)
  24.  
  25. // ----------   Other global variables    ----------
  26. StantonSCS1d.debug = false;  // Enable/disable debugging messages to the console
  27. StantonSCS1d.id = "";   // The ID for the particular device being controlled for use in debugging, set at init time
  28. StantonSCS1d.channel = 0;   // MIDI channel the device is on
  29. StantonSCS1d.swVersion = "1.9";   // Mixxx version for display
  30. StantonSCS1d.buttons = { "control":27, "browse":28, "vinyl":29, "deckSelect":64 };
  31.  
  32. StantonSCS1d.platterMode = { "[Channel1]":"vinyl", "[Channel2]":"vinyl" };   // Set vinyl mode on both decks
  33. StantonSCS1d.knobMode = { "[Channel1]":1, "[Channel2]":1 }; // 1=Low,Mid,High EQ,Vol; 2=Depth,Delay,LFO,Gain; 3=Cuemix,Headvol,Balance,MasterVol
  34. StantonSCS1d.padBank = { "deck":1, "bank1":1, "bank2":1 };
  35. StantonSCS1d.triggerBank = { 1:1, 2:1 };    // Trigger button bank for each deck
  36. StantonSCS1d.deck = 1;  // Currently active virtual deck
  37. StantonSCS1d.trackDuration = [0,0]; // Duration of the song on each deck (used for jog LCD and displays)
  38. StantonSCS1d.lastLight = [-1,-1];    // Last circle LCD values
  39. StantonSCS1d.modifier = { "cue":0, "play":0 };  // Modifier buttons (allowing alternate controls) defined on-the-fly if needed
  40. // Temporary state variables
  41. StantonSCS1d.state = { "pitchAbs":0, "jog":0, "dontMove":0, "platterGrabbed":false, "browseTicks":0};
  42. StantonSCS1d.mutex = { };   // Temporary mutual exclusion variables
  43. StantonSCS1d.prevValues = { };  // Temporary previous value storage
  44. StantonSCS1d.inSetup = false;   // Flag for if the device is in setup mode
  45. StantonSCS1d.sysex = [0xF0, 0x00, 0x01, 0x02];  // Preamble for all SysEx messages for this device
  46. StantonSCS1d.rpm = [33+1/3,45];    // RPM values for StantonSCS1d.platterSpeed
  47. // Variables used in the scratching alpha-beta filter: (revtime = 1.8 to start)
  48. StantonSCS1d.scratch = { "revtime":1.8, "resolution":4000, "alpha":1.0/8, "beta":(1.0/8)/32, "prevTimeStamp":0, "prevState":0 };
  49.                         // "alpha":0.1, "beta":1.0 for obsolete method
  50. // Pitch values for key change mode
  51. StantonSCS1d.pitchPoints = {    1:{ 8:-0.1998, 9:-0.1665, 10:-0.1332, 11:-0.0999, 12:-0.0666, 13:-0.0333,
  52.                                     14:0.0333, 15:0.0666, 18:0.0999, 19:0.1332, 20:0.1665, 21:0.1998 }, // 3.33% increments
  53.                                 2:{ 8:-0.5, 9:-0.4043, 10:-0.2905, 11:-0.1567, 12:-0.1058, 13:-0.0548, 
  54.                                     14:0.06, 15:0.12, 18:0.181, 19:0.416, 20:0.688, 21:1.0 },  // Key changes
  55.                                 3:{ 8:-0.4370, 9:-0.3677, 10:-0.3320, 11:-0.2495, 12:-0.1567, 13:-0.0548, 
  56.                                     14:0.12, 15:0.263, 18:0.338, 19:0.506, 20:0.688, 21:0.895 } };  // Notes
  57. // Multiple banks of multiple cue points:
  58. StantonSCS1d.hotCues = {    1:{ 0x20: 1, 0x21: 2, 0x22: 3, 0x23: 4 },
  59.                             2:{ 0x20: 5, 0x21: 6, 0x22: 7, 0x23: 8 },
  60.                             3:{ 0x20: 9, 0x21: 10, 0x22: 11, 0x23: 12 } };
  61. /*
  62. StantonSCS1d.padPoints =  {     1:{ // Deck
  63.                                     1:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 },   // Bank
  64.                                     2:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 },
  65.                                     3:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 } },
  66.                                 2:{ // Deck
  67.                                     1:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 },   // Bank
  68.                                     2:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 },
  69.                                     3:{ 0x20:-0.1, 0x21:-0.1, 0x22:-0.1, 0x23:-0.1 } }
  70.                             };
  71. */
  72.  
  73. // Signals to (dis)connect by mode: Group, Key, Function name
  74. StantonSCS1d.platterSignals = { "vinyl":[ ["CurrentChannel", "rate", "StantonSCS1d.platterSpeed"] ],
  75.                                 "control":[],
  76.                                 "browse":[],
  77.                                 "none":[]  // To avoid an error on forced mode changes
  78.                             };
  79. StantonSCS1d.deckSignals = [    ["CurrentChannel", "rateRange", "StantonSCS1d.pitchRangeLEDs"],
  80.                                 ["CurrentChannel", "rate", "StantonSCS1d.pitchChange"],
  81.                                 ["CurrentChannel", "play", "StantonSCS1d.playLED"],
  82.                                 ["CurrentChannel", "reverse", "StantonSCS1d.reverse"],
  83.                                 ["CurrentChannel", "cue_default", "StantonSCS1d.cueLED"],
  84.                                 ["CurrentChannel", "beatsync", "StantonSCS1d.syncLED"],
  85.                                 ["CurrentChannel", "back", "StantonSCS1d.backLED"],
  86.                                 ["CurrentChannel", "fwd", "StantonSCS1d.fwdLED"],
  87.                                 ["CurrentChannel", "pfl", "StantonSCS1d.headphoneLED"]
  88.                             ];
  89. StantonSCS1d.knobText =    [  ["Low EQ","Mid EQ","High EQ","Volume"],
  90.                               ["Depth","Delay","Period","PF Gain"],
  91.                               ["Pre/Main","Head Vol","Balance","M.Volume"]
  92.                             ];
  93. StantonSCS1d.knobSignals = [  [ ["CurrentChannel", "filterLow", "StantonSCS1d.encoder1EQLEDs"],
  94.                                 ["CurrentChannel", "filterMid", "StantonSCS1d.encoder2EQLEDs"],
  95.                                 ["CurrentChannel", "filterHigh", "StantonSCS1d.encoder3EQLEDs"],
  96.                                 ["CurrentChannel", "volume", "StantonSCS1d.encoder4VolumeLEDs"] ],
  97.                               [ ["[Flanger]", "lfoDepth", "StantonSCS1d.FXDepthLEDs"],
  98.                                 ["[Flanger]", "lfoDelay", "StantonSCS1d.FXDelayLEDs"],
  99.                                 ["[Flanger]", "lfoPeriod", "StantonSCS1d.FXPeriodLEDs"],
  100.                                 ["CurrentChannel", "pregain", "StantonSCS1d.encoder4EQLEDs"] ],
  101.                               [ ["[Master]", "headMix", "StantonSCS1d.encoder1BalanceLEDs"],
  102.                                 ["[Master]", "headVolume", "StantonSCS1d.encoder2MVolumeLEDs"],
  103.                                 ["[Master]", "balance", "StantonSCS1d.encoder3BalanceLEDs"],
  104.                                 ["[Master]", "volume", "StantonSCS1d.encoder4MVolumeLEDs"] ],
  105.                             ];
  106. StantonSCS1d.padSignals = [    [],    // Bank 0 (non-existent)
  107.                             [    ["CurrentChannel", "hotcue_1_position", "StantonSCS1d.Pad1LCD"],
  108.                                 ["CurrentChannel", "hotcue_2_position", "StantonSCS1d.Pad2LCD"],
  109.                                 ["CurrentChannel", "hotcue_3_position", "StantonSCS1d.Pad3LCD"],
  110.                                 ["CurrentChannel", "hotcue_4_position", "StantonSCS1d.Pad4LCD"],
  111.                                 ["CurrentChannel", "hotcue_1_activate", "StantonSCS1d.Pad1aLED"],
  112.                                 ["CurrentChannel", "hotcue_2_activate", "StantonSCS1d.Pad2aLED"],
  113.                                 ["CurrentChannel", "hotcue_3_activate", "StantonSCS1d.Pad3aLED"],
  114.                                 ["CurrentChannel", "hotcue_4_activate", "StantonSCS1d.Pad4aLED"] ],
  115.                             [    ["CurrentChannel", "hotcue_5_position", "StantonSCS1d.Pad1LCD"],
  116.                                 ["CurrentChannel", "hotcue_6_position", "StantonSCS1d.Pad2LCD"],
  117.                                 ["CurrentChannel", "hotcue_7_position", "StantonSCS1d.Pad3LCD"],
  118.                                 ["CurrentChannel", "hotcue_8_position", "StantonSCS1d.Pad4LCD"],
  119.                                 ["CurrentChannel", "hotcue_5_activate", "StantonSCS1d.Pad1aLED"],
  120.                                 ["CurrentChannel", "hotcue_6_activate", "StantonSCS1d.Pad2aLED"],
  121.                                 ["CurrentChannel", "hotcue_7_activate", "StantonSCS1d.Pad3aLED"],
  122.                                 ["CurrentChannel", "hotcue_8_activate", "StantonSCS1d.Pad4aLED"] ],
  123.                             [    ["CurrentChannel", "hotcue_9_position", "StantonSCS1d.Pad1LCD"],
  124.                                 ["CurrentChannel", "hotcue_10_position", "StantonSCS1d.Pad2LCD"],
  125.                                 ["CurrentChannel", "hotcue_11_position", "StantonSCS1d.Pad3LCD"],
  126.                                 ["CurrentChannel", "hotcue_12_position", "StantonSCS1d.Pad4LCD"],
  127.                                 ["CurrentChannel", "hotcue_9_activate", "StantonSCS1d.Pad1aLED"],
  128.                                 ["CurrentChannel", "hotcue_10_activate", "StantonSCS1d.Pad2aLED"],
  129.                                 ["CurrentChannel", "hotcue_11_activate", "StantonSCS1d.Pad3aLED"],
  130.                                 ["CurrentChannel", "hotcue_12_activate", "StantonSCS1d.Pad4aLED"] ] ];
  131.  
  132. // ----------   Functions   ----------
  133.  
  134. StantonSCS1d.init = function (id) {    // called when the MIDI device is opened & set up
  135.  
  136.     // Welcome message
  137.     var message = "Welcome";
  138.     // Set LCD1
  139.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 1],message.toInt(), 0xF7),7+message.length);
  140.     // Set LCD6
  141.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 6],message.toInt(), 0xF7),7+message.length);
  142.     //midi.sendShortMsg(No,49,127);   // to orange
  143.     
  144.     message = "to";
  145.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 2],message.toInt(), 0xF7),7+message.length);
  146.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 7],message.toInt(), 0xF7),7+message.length);
  147.     //midi.sendShortMsg(No,49+1,127); // to orange
  148.     
  149.     message = "Mixxx  v";
  150.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 3],message.toInt(), 0xF7),7+message.length);
  151.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 8],message.toInt(), 0xF7),7+message.length);
  152.     //midi.sendShortMsg(No,49+2,127); // to orange
  153.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 4],
  154.         StantonSCS1d.swVersion.toInt(), 0xF7),7+StantonSCS1d.swVersion.length);
  155.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 9],
  156.         StantonSCS1d.swVersion.toInt(), 0xF7),7+StantonSCS1d.swVersion.length);
  157.     //midi.sendShortMsg(No,49+3,32);  // to red
  158.     
  159.     StantonSCS1d.id = id;   // Store the ID of this device for later use
  160.     
  161.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 17, 0xF7]),7); // Extinguish all LEDs
  162.     var CC = 0xB0 + StantonSCS1d.channel;
  163.     var No = 0x90 + StantonSCS1d.channel;
  164.  
  165.     midi.sendShortMsg(CC,1,'x'.toInt());   // Stop platter
  166.     if (StantonSCS1d.platterSpeed==1) midi.sendShortMsg(CC,1,'2'.toInt());   // 45 RPM
  167.     else midi.sendShortMsg(CC,1,'1'.toInt());   // 33 RPM
  168.     
  169.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 14, 0, 0xF7]),8);  // Clear Passive mode
  170.  
  171.     // TODO: Check firmware version if possible. Platter behaves very differently after v1.25!
  172.     //StantonSCS1d.fwVersion = 
  173.     
  174.     // Force change to first deck, initializing the LEDs and connecting signals in the process
  175.     StantonSCS1d.state["Oldknob"]=1;
  176.     StantonSCS1d.deck = 2;  // Set active deck to right (#2) so the below will switch to #1.
  177.     StantonSCS1d.DeckChange(StantonSCS1d.channel, StantonSCS1d.buttons["deckSelect"], "", 0x80+StantonSCS1d.channel);
  178.     
  179.     // Connect the playposition functions permanently since they disrupt playback if connected on the fly
  180.     engine.connectControl("[Channel1]","visual_playposition","StantonSCS1d.circleBars1");
  181.     engine.connectControl("[Channel2]","visual_playposition","StantonSCS1d.circleBars2");
  182.     engine.connectControl("[Channel1]","duration","StantonSCS1d.durationChange1");
  183.     engine.connectControl("[Channel2]","duration","StantonSCS1d.durationChange2");
  184.     
  185.     //  Initialize the jog LCD if the mapping is loaded after a song is
  186.     StantonSCS1d.durationChange1(engine.getValue("[Channel1]","duration"));
  187.     StantonSCS1d.durationChange2(engine.getValue("[Channel2]","duration"));
  188.     
  189.     //midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 16, 0xF7]),7); // Light all LEDs
  190.  
  191.     print ("StantonSCS1d: \""+StantonSCS1d.id+"\" on MIDI channel "+(StantonSCS1d.channel+1)+" initialized.");
  192. }
  193.  
  194. StantonSCS1d.shutdown = function () {   // called when the MIDI device is closed
  195.     var CC = 0xB0 + StantonSCS1d.channel;
  196.     midi.sendShortMsg(CC,1,'x'.toInt());   // Stop the platter
  197.  
  198.     // Graffiti :)
  199.     var message = "Mixxx";
  200.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 1],message.toInt(), 0xF7),7+message.length);
  201.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 6],message.toInt(), 0xF7),7+message.length);
  202.     
  203.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 2],
  204.         StantonSCS1d.swVersion.toInt(), 0xF7),7+StantonSCS1d.swVersion.length);
  205.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 7],
  206.         StantonSCS1d.swVersion.toInt(), 0xF7),7+StantonSCS1d.swVersion.length);
  207.     
  208.     message = "was here";
  209.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 3],message.toInt(), 0xF7),7+message.length);
  210.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 8],message.toInt(), 0xF7),7+message.length);
  211.     
  212.     message = "";
  213.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 4],message.toInt(), 0xF7),7+message.length);
  214.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 9],message.toInt(), 0xF7),7+message.length);
  215.     
  216.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 13, 0, 0xF7]),8); // Jog backlight off
  217.     // clear jog LCD character (set to space)
  218.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 36, 0x20, 0xF7]),8);
  219.     midi.sendShortMsg(CC,2,0x00);    // Clear jog circle
  220.     
  221.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 17, 0xF7]),7); // Extinguish all LEDs
  222.  
  223.     print ("StantonSCS1d: \""+StantonSCS1d.id+"\" on MIDI channel "+(StantonSCS1d.channel+1)+" shut down.");
  224. }
  225.  
  226. StantonSCS1d.checkInSetup = function () {
  227.   if (StantonSCS1d.inSetup) print ("StantonSCS1d: In setup mode, ignoring command.");
  228.   return StantonSCS1d.inSetup;
  229. }
  230.  
  231. StantonSCS1d.setupButton = function (channel, control, value, status) {
  232.     if ((status & 0XF0) == 0x90) {
  233.         StantonSCS1d.inSetup = !StantonSCS1d.inSetup;
  234.         if (StantonSCS1d.inSetup) StantonSCS1d.connectKnobSignals(channel,true);   // Disconnect knob signals & turn off their LEDs
  235.         }
  236.     else if (!StantonSCS1d.inSetup) StantonSCS1d.connectKnobSignals(channel);   // Reconnect knob signals
  237. }
  238.  
  239. StantonSCS1d.controlButton = function (channel, control, value, status) {
  240.   if (StantonSCS1d.checkInSetup()) return;
  241.     var byte1 = 0x90 + channel;
  242.     if ((status & 0XF0) == 0x90) {    // If button down
  243.         engine.scratchDisable(StantonSCS1d.deck);
  244.         midi.sendShortMsg(0xB0 + channel,1,'x'.toInt());   // Stop platter
  245.         midi.sendShortMsg(byte1,control,1);  // Light 'er up
  246.         StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] = "control";
  247.         midi.sendShortMsg(0x80+channel,0x1C,0);  // turn off the "browse" mode button
  248.         midi.sendShortMsg(0x80+channel,0x1D,0);  // turn off the "vinyl" mode button
  249.         
  250.         return;
  251.     }
  252. }
  253.  
  254. StantonSCS1d.browseButton = function (channel, control, value, status) {
  255.   if (StantonSCS1d.checkInSetup()) return;
  256.     var byte1 = 0x90 + channel;
  257.     if ((status & 0XF0) == 0x90) {    // If button down
  258.         engine.scratchDisable(StantonSCS1d.deck);
  259.         midi.sendShortMsg(0xB0 + channel,1,'x'.toInt());   // Stop platter
  260.         midi.sendShortMsg(byte1,control,1);  // Light 'er up
  261.         midi.sendShortMsg(0x80+channel,0x1B,0);  // turn off the "control" mode button
  262.         midi.sendShortMsg(0x80+channel,0x1D,0);  // turn off the "vinyl" mode button
  263.         
  264.         return;
  265.     }
  266.     // Switch modes on button up to give the motor a chance to stop
  267.     StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] = "browse";
  268. }
  269.  
  270. StantonSCS1d.vinylButton = function (channel, control, value, status) {
  271.   if (StantonSCS1d.checkInSetup()) return;
  272.     var byte1 = 0x90 + channel;
  273.     if ((status & 0XF0) == 0x90) {    // If button down
  274.         midi.sendShortMsg(byte1,control,1);  // Light 'er up
  275.         if (!StantonSCS1d.state["outsideMotor"])    // Enable direct platter control
  276.             engine.scratchEnable(StantonSCS1d.deck, StantonSCS1d.scratch["resolution"], StantonSCS1d.rpm[StantonSCS1d.platterSpeed],StantonSCS1d.scratch["alpha"], StantonSCS1d.scratch["beta"]);
  277.         StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] = "vinyl";
  278.         midi.sendShortMsg(0x80+channel,0x1B,0);  // turn off the "control" mode button
  279.         midi.sendShortMsg(0x80+channel,0x1C,0);  // turn off the "browse" mode button
  280.         
  281.         StantonSCS1d.pitchChange(engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate")); // So the platter speed is updated and it begins spinning if applicable
  282.         return;
  283.     }
  284. }
  285.  
  286. // (Dis)connects the appropriate Mixxx control signals to/from functions based on the currently controlled deck and what mode the encoder knobs are in
  287. StantonSCS1d.connectKnobSignals = function (channel, disconnect) {
  288.  
  289.     var signalList = StantonSCS1d.knobSignals[StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"]-1];
  290.     for (var i=0; i<signalList.length; i++) {
  291.         var group = signalList[i][0];
  292.         if (group=="CurrentChannel") group = "[Channel"+StantonSCS1d.deck+"]";
  293.         engine.connectControl(group,signalList[i][1],signalList[i][2],disconnect);
  294.         
  295.         // If connecting a signal, cause it to fire (by setting it to the same value) to update the LEDs
  296. //         if (!disconnect) engine.trigger(group,signalList[i][1]);  // Commented because there's no sense in wasting queue length
  297.         if (!disconnect) {
  298.             // Alternate:
  299.             var command = signalList[i][2]+"("+engine.getValue(group,signalList[i][1])+")";
  300.             //print("StantonSCS1d: command="+command);
  301.             eval(command);
  302.             
  303.             // Set text
  304.             var message = StantonSCS1d.knobText[StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"]-1][i];
  305.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, i+1],message.toInt(), 0xF7),7+message.length);   // Set LCD
  306.         }
  307.         if (StantonSCS1d.debug) {
  308.             if (disconnect) print("StantonSCS1d: "+group+","+signalList[i][1]+" disconnected from "+signalList[i][2]);
  309.             else print("StantonSCS1d: "+group+","+signalList[i][1]+" connected to "+signalList[i][2]);
  310.         }
  311.     }
  312.     // If disconnecting signals, darken the LEDs on the knobs
  313.     if (disconnect) {
  314.         var CC = 0xB0 + channel;
  315.         midi.sendShortMsg(CC,0x7F,0x00);  // Encoder 1 LEDs off
  316.         midi.sendShortMsg(CC,0x7E,0x00);  // Encoder 2 LEDs off
  317.         midi.sendShortMsg(CC,0x7D,0x00);  // Encoder 3 LEDs off
  318.         midi.sendShortMsg(CC,0x7C,0x00);  // Encoder 4 LEDs off
  319.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 1],0x20, 0xF7),8);   // Blank LCD text
  320.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 2],0x20, 0xF7),8);   // Blank LCD text
  321.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 3],0x20, 0xF7),8);   // Blank LCD text
  322.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 4],0x20, 0xF7),8);   // Blank LCD text
  323.     }
  324. }
  325.  
  326. // (Dis)connects the appropriate Mixxx control signals to/from functions based on the currently controlled deck
  327. StantonSCS1d.connectPadSignals = function (channel, disconnect) {
  328.     var deck = StantonSCS1d.padBank["deck"];
  329.     var bank = StantonSCS1d.padBank["bank"+deck];
  330.     
  331.     var signalList = StantonSCS1d.padSignals[bank];
  332.     for (var i=0; i<signalList.length; i++) {
  333.         var group = signalList[i][0];
  334.         if (group=="CurrentChannel") group = "[Channel"+deck+"]";
  335.         engine.connectControl(group,signalList[i][1],signalList[i][2],disconnect);
  336.         
  337.         // If connecting a signal, cause it to fire (by setting it to the same value) to update the LEDs
  338. //         if (!disconnect) engine.trigger(group,signalList[i][1]);  // Commented because there's no sense in wasting queue length
  339.         if (!disconnect) {
  340.             // Alternate:
  341.             var command = signalList[i][2]+"("+engine.getValue(group,signalList[i][1])+")";
  342.             //print("StantonSCS1d: command="+command);
  343.             eval(command);
  344.         }
  345.         if (StantonSCS1d.debug) {
  346.             if (disconnect) print("StantonSCS1d: "+group+","+signalList[i][1]+" disconnected from "+signalList[i][2]);
  347.             else print("StantonSCS1d: "+group+","+signalList[i][1]+" connected to "+signalList[i][2]);
  348.         }
  349.     }
  350.     // If disconnecting signals, darken the LEDs on the pads & clear the displays
  351.     if (disconnect) {
  352.         var CC = 0xB0 + channel;
  353.         midi.sendShortMsg(CC,0x20,0x00);  // Pad 1 LEDs off
  354.         midi.sendShortMsg(CC,0x21,0x00);  // Pad 2 LEDs off
  355.         midi.sendShortMsg(CC,0x22,0x00);  // Pad 3 LEDs off
  356.         midi.sendShortMsg(CC,0x23,0x00);  // Pad 4 LEDs off
  357.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 5],0x20, 0xF7),8);   // Blank LCD text
  358.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 6],0x20, 0xF7),8);   // Blank LCD text
  359.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 7],0x20, 0xF7),8);   // Blank LCD text
  360.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 8],0x20, 0xF7),8);   // Blank LCD text
  361.     }
  362. }
  363.  
  364. // (Dis)connects the mode-independent Mixxx control signals to/from functions based on the currently controlled virtual deck
  365. StantonSCS1d.connectDeckSignals = function (channel, disconnect) {
  366.     var signalList = StantonSCS1d.deckSignals;
  367.     for (var i=0; i<signalList.length; i++) {
  368.         var group = signalList[i][0];
  369.         var name = signalList[i][1];
  370.         if (group=="CurrentChannel") group = "[Channel"+StantonSCS1d.deck+"]";
  371.         engine.connectControl(group,name,signalList[i][2],disconnect);
  372. //         print("StantonSCS1d: (dis)connected "+group+","+name+" to/from "+signalList[i][2]);
  373.         
  374.         // If connecting a signal, update the LEDs
  375.         if (!disconnect) {
  376.             switch (name) {
  377.                 case "play":
  378.                         var currentValue = engine.getValue(group,name);
  379. //                         print("StantonSCS1d: current value="+currentValue);
  380.                         StantonSCS1d.playLED(currentValue);
  381.                         break;
  382.                 case "cue_default":
  383.                 case "beatsync": break;
  384.                 default:    // Cause the signal to fire to update LEDs
  385. //                         engine.trigger(group,name);  // No sense in wasting queue length if we can do this another way
  386.                     // Alternate:
  387.                         var command = signalList[i][2]+"("+engine.getValue(group,name)+")";
  388. //                         print("StantonSCS1d: command="+command);
  389.                         eval(command);
  390.                         break;
  391.             }
  392.         }
  393.         
  394.         if (StantonSCS1d.debug) {
  395.             if (disconnect) print("StantonSCS1d: "+group+","+signalList[i][1]+" disconnected from "+signalList[i][2]);
  396.             else print("StantonSCS1d: "+group+","+signalList[i][1]+" connected to "+signalList[i][2]);
  397.         }
  398.     }
  399.     // If disconnecting signals, darken the corresponding LEDs
  400.     if (disconnect) {
  401.         var CC = 0xB0 + channel;
  402.         var No = 0x90 + channel;
  403.         midi.sendShortMsg(No,55,0x00);    // Headphone button off
  404.         midi.sendShortMsg(No,0x29,0x00);  // PLAY button off
  405.         midi.sendShortMsg(No,0x2B,0x00);  // CUE button off
  406.         midi.sendShortMsg(No,0x2A,0x00);  // SYNC button off
  407.         midi.sendShortMsg(No,0x28,0x00);  // BPM button off
  408.     }
  409. }
  410.  
  411. StantonSCS1d.playButton = function (channel, control, value, status) {
  412.     if ((status & 0xF0) != 0x80) {    // If button down
  413.         StantonSCS1d.modifier["play"]=1;
  414.         if (StantonSCS1d.modifier["cue"]==1) engine.setValue("[Channel"+StantonSCS1d.deck+"]","play",1);
  415.         else {
  416.             if (StantonSCS1d.modifier["pad"]==1) {
  417.                 // Continue playing (or stop playing) if play is pressed while a pad is held down
  418.                 midi.sendShortMsg(0x90+channel,control,127);    // Make it orange
  419.                 StantonSCS1d.state["padWasPlaying"]=!StantonSCS1d.state["padWasPlaying"];
  420.                 return;
  421.             }
  422.             
  423.             var currentlyPlaying = engine.getValue("[Channel"+StantonSCS1d.deck+"]","play");
  424.             if (currentlyPlaying && engine.getValue("[Channel"+StantonSCS1d.deck+"]","cue_default")==1) engine.setValue("[Channel"+StantonSCS1d.deck+"]","cue_default",0);
  425.             engine.setValue("[Channel"+StantonSCS1d.deck+"]","play", !currentlyPlaying);
  426.         }
  427.         return;
  428.     }
  429.     engine.trigger("[Channel"+StantonSCS1d.deck+"]","play");
  430.     StantonSCS1d.modifier["play"]=0;
  431. }
  432.  
  433. StantonSCS1d.cueButton = function (channel, control, value, status) {
  434.     var byte1 = 0x90 + channel;
  435.     if ((status & 0xF0) != 0x80) {    // If button down
  436.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","cue_default",1);
  437.         StantonSCS1d.modifier["cue"]=1;   // Set button modifier flag
  438.         return;
  439.     }
  440.     if (StantonSCS1d.modifier["play"]==0) engine.setValue("[Channel"+StantonSCS1d.deck+"]","cue_default",0);
  441.     StantonSCS1d.modifier["cue"]=0;   // Clear button modifier flag
  442. }
  443.  
  444. StantonSCS1d.syncButton = function (channel, control, value, status) {
  445.     var byte1 = 0x90 + channel;
  446.     if ((status & 0xF0) != 0x80) {    // If button down
  447.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","beatsync",1);
  448.         return;
  449.     }
  450.     midi.sendShortMsg(byte1,control,0x00);  // SYNC light off
  451.     engine.setValue("[Channel"+StantonSCS1d.deck+"]","beatsync",0);
  452. }
  453.  
  454. StantonSCS1d.bpmButton = function (channel, control, value, status) {
  455.     StantonSCS1d.buttonLED(value,control,64,0);
  456.     var byte1 = 0x90 + channel;
  457.     if ((status & 0xF0) == 0x90) {    // If button down
  458. //         print("StantonSCS1d: TAP");
  459.         bpm.tapButton(StantonSCS1d.deck);
  460.         return;
  461.     }
  462. }
  463.  
  464. StantonSCS1d.pfl = function (channel, control, value, status) {
  465.     if ((status & 0xF0) != 0x80) {    // If button down
  466.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","pfl",!engine.getValue("[Channel"+StantonSCS1d.deck+"]","pfl"));
  467.     }
  468. }
  469.  
  470. StantonSCS1d.rew = function (channel, control, value, status) {
  471.     // If in vinyl mode and button down
  472.     if ((status & 0xF0) == 0x90) {
  473.         if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] == "vinyl") {
  474.             midi.sendShortMsg(0xB0+channel,1,'4'.toInt());   // 45 RPM backward
  475.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 35, 1.5, 5, 0, 0, 0xF7]),11);  // Motor full speed
  476.             midi.sendShortMsg(0xB0+channel,1,'o'.toInt());   // Start platter
  477.         }
  478.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","back",1);
  479.         return;
  480.     }
  481.     engine.setValue("[Channel"+StantonSCS1d.deck+"]","back",0);
  482.     engine.trigger("[Channel"+StantonSCS1d.deck+"]","reverse"); // Return to correct direction
  483.     StantonSCS1d.pitchChange(engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate")); // and speed
  484.     engine.trigger("[Channel"+StantonSCS1d.deck+"]","play");    // and playback status
  485. }
  486.  
  487. StantonSCS1d.ffwd = function (channel, control, value, status) {
  488.     // If in vinyl mode and button down
  489.     if ((status & 0xF0) == 0x90) {
  490.         if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] == "vinyl") {
  491.             midi.sendShortMsg(0xB0+channel,1,'2'.toInt());   // 45 RPM foreward
  492.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 35, 1.5, 5, 0, 0, 0xF7]),11);  // Motor full speed
  493.             midi.sendShortMsg(0xB0+channel,1,'o'.toInt());   // Start platter
  494.         }
  495.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","fwd",1);
  496.         return;
  497.     }
  498.     engine.setValue("[Channel"+StantonSCS1d.deck+"]","fwd",0);
  499.     engine.trigger("[Channel"+StantonSCS1d.deck+"]","reverse"); // Return to correct direction
  500.     StantonSCS1d.pitchChange(engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate")); // and speed
  501.     engine.trigger("[Channel"+StantonSCS1d.deck+"]","play");    // and playback status
  502. }
  503.  
  504. StantonSCS1d.rangeButton = function (channel, control, value, status) {
  505.     if ((status & 0xF0) == 0x90) {    // If button down
  506.         midi.sendShortMsg(0x90+StantonSCS1d.channel,control,0x7F);  // Light button LED
  507.         StantonSCS1d.modifier["pitchRange"]=1;   // Set button modifier flag
  508.         if (StantonSCS1d.crossFader) {
  509.             // Move to cross-fader position
  510.             StantonSCS1d.pitchRangeLEDs(0); // darken range LEDs
  511.             var xfader = engine.getValue("[Master]","crossfader")*63+64;
  512.             if (StantonSCS1d.debug) print ("Moving slider to "+xfader);
  513.             midi.sendShortMsg(0xB0+StantonSCS1d.channel,0x00,xfader);
  514.             StantonSCS1d.state["crossfaderAdjusted"]=false;
  515.         }
  516.     }
  517.     else {
  518.         midi.sendShortMsg(0x80+StantonSCS1d.channel,control,0); // Darken button LED
  519.         StantonSCS1d.modifier["pitchRange"]=0; // Clear button modifier flag
  520.         midi.sendShortMsg(0xB0+StantonSCS1d.channel,0x00,engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate")*63+64);    // Move to pitch position
  521.         
  522.         if (!StantonSCS1d.state["crossfaderAdjusted"]) {
  523.             // Change the range
  524.             var currentRange = engine.getValue("[Channel"+StantonSCS1d.deck+"]","rateRange");
  525.             // TODO: Extend this to work with arbitrary-length pitchRanges array
  526.             switch (true) {
  527.                 case (currentRange<StantonSCS1d.pitchRanges[0]):
  528.                         engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",StantonSCS1d.pitchRanges[0]);
  529.                     break;
  530.                 case (currentRange>=StantonSCS1d.pitchRanges[0] &&
  531.                       currentRange<StantonSCS1d.pitchRanges[1]):
  532.                         engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",StantonSCS1d.pitchRanges[1]);
  533.                     break;
  534.                 case (currentRange>=StantonSCS1d.pitchRanges[1] &&
  535.                       currentRange<StantonSCS1d.pitchRanges[2]):
  536.                         engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",StantonSCS1d.pitchRanges[2]);
  537.                     break;
  538.                 case (currentRange>=StantonSCS1d.pitchRanges[2] &&
  539.                       currentRange<StantonSCS1d.pitchRanges[3]):
  540.                         engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",StantonSCS1d.pitchRanges[3]);
  541.                     break;
  542.                 case (currentRange>=StantonSCS1d.pitchRanges[3]):
  543.                         engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",StantonSCS1d.pitchRanges[0]);
  544.                     break;
  545.             }
  546.             // Update the screen display
  547.             engine.trigger("[Channel"+StantonSCS1d.deck+"]","rate");
  548.         }
  549.         StantonSCS1d.pitchRangeLEDs(engine.getValue("[Channel"+StantonSCS1d.deck+"]","rateRange")); // Light the LEDs again
  550.     }
  551. }
  552.  
  553. StantonSCS1d.pitchReset = function (channel, control, value, status) {
  554.     if ((status & 0xF0) == 0x90) midi.sendShortMsg(0x90+StantonSCS1d.channel,control,0x7F); // Light button LED
  555.     else {
  556.         midi.sendShortMsg(0x80+StantonSCS1d.channel,control,0); // Darken button LED
  557.         if (StantonSCS1d.modifier["pitchRange"]==1) {
  558.             engine.setValue("[Master]","crossfader",0);
  559.             StantonSCS1d.state["crossfaderAdjusted"]=true;
  560.         }
  561.         else engine.setValue("[Channel"+StantonSCS1d.deck+"]","rate",0);
  562.     }
  563. }
  564.  
  565. StantonSCS1d.vinylMoved = function (data, length) {
  566.     // Re-construct the 32-bit word
  567.     var iInfo = (data.charCodeAt(0) << 24) | (data.charCodeAt(1) << 16) | 
  568.                 (data.charCodeAt(2) << 8) | data.charCodeAt(3);
  569.     
  570.     //// Unpack the data - Firmware v1.24 and lower
  571.     //var iTimeStamp = (iInfo >>> 5);
  572.     //var iQuad = 3 & ((((iInfo >>> 1) & 1) | (iInfo << 1)) ^ (iInfo & 1)); 
  573.     //var iDirection = ((iInfo >>> 2) & 1) ^ 1;
  574.     //iDirection = 1 - (iDirection << 1);
  575.     //
  576.     //if (StantonSCS1d.debug) print("Timestamp="+iTimeStamp+" Quad="+iQuad+" Dir="+iDirection+" Info="+iInfo+" Delta="+(iTimeStamp - StantonSCS1d.scratch["prevTimeStamp"]));
  577.     //
  578.     // // Timestamp range: 131071812 - 130547712 = 524,100
  579.     
  580.     // Unpack the data - Firmware 1.25 and higher
  581.     var iTimeStamp = (iInfo >>> 8);
  582.     var iState = data.charCodeAt(3);
  583.     if (isNaN(iState)) return;
  584.     var iMoved = (iState - StantonSCS1d.scratch["prevState"] + 384) % 256 - 128;
  585.     var iSpeed = iMoved/(iTimeStamp - StantonSCS1d.scratch["prevTimeStamp"]);
  586.     
  587.     if (StantonSCS1d.debug)
  588.         print("Timestamp="+iTimeStamp+" State="+iState+" Moved="+iMoved+" Speed="+(iSpeed*60000|0));
  589.     
  590.     StantonSCS1d.scratch["prevTimeStamp"] = iTimeStamp;
  591.     StantonSCS1d.scratch["prevState"] = iState;
  592.     
  593.     // Process the data
  594.     
  595.     var platterMode = StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"];
  596.  
  597.     switch(platterMode) {
  598.         case "control":
  599.             break;
  600.         case "browse":
  601.             if (StantonSCS1d.state["browseTicks"]==StantonSCS1d.browseDamp) {
  602.                 StantonSCS1d.state["browseTicks"]=0;
  603.                 if (iMoved>0) engine.setValue("[Playlist]","SelectNextTrack",1);
  604.                 else if (iMoved<0) engine.setValue("[Playlist]","SelectPrevTrack",1);
  605.             }
  606.             else StantonSCS1d.state["browseTicks"]++;
  607.             break;
  608.         case "vinyl":    // Scratching
  609.             // Ignore if the music speed is outside the motor abilities and the platter is stopped
  610.             if (StantonSCS1d.state["outsideMotor"]) return;
  611.  
  612.             // TODO: Remember to take into account default platter speed!
  613.             //        (StantonSCS1d.platterSpeed: 0=33 RPM, 1=45 RPM)
  614.             
  615.             engine.scratchTick(StantonSCS1d.deck,iMoved);
  616.             
  617.             /*
  618.             if (iDirection==1) engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch",1);
  619.             else engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch",-0.5);
  620.  
  621.             // Skip if the track start position hasn't been set yet
  622.             if (scratch.variables["initialTrackPos"] == -1.0)
  623.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch",0);
  624.             // If the slider start value hasn't been set yet, set it
  625.             if (scratch.variables["initialControlValue"] == 0) {
  626.                 scratch.variables["initialControlValue"] = sliderValue;
  627.                  print("Initial slider="+scratch.variables["initialControlValue"]);
  628.                 }
  629.             var temp=scratch.filter(StantonSCS1d.deck, iTimeStamp, StantonSCS1d.scratch["revtime"], StantonSCS1d.scratch["alpha"], StantonSCS1d.scratch["beta"], divisions);
  630.             engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch",temp);
  631.             */
  632.             break;
  633.     }
  634. }
  635.  
  636. /*    Old DaRouter passthru stuff
  637. StantonSCS1d.platterGrabbed = function (channel, control, value, status) {
  638.     return;
  639.     // Ignore if the music speed is outside the motor abilities and the platter is stopped
  640.     if (StantonSCS1d.state["outsideMotor"]) return;
  641.     
  642.     if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] != "vinyl") return;  // Skip if not in vinyl mode
  643.     if (value == 0x7f) {
  644.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","wheel",0);
  645.         StantonSCS1d.state["platterGrabbed"]=true;
  646.         if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play")==1) {
  647.             engine.setValue("[Channel"+StantonSCS1d.deck+"]","play",0);
  648.             StantonSCS1d.scratch["wasPlaying"] = true;
  649.         }
  650.     }
  651.     if (value == 0) {
  652.         //engine.setValue("[Channel"+StantonSCS1d.deck+"]","wheel",0);
  653.         //engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch",0);
  654.         StantonSCS1d.state["platterGrabbed"]=false;
  655.     }
  656. }
  657.  
  658. StantonSCS1d.platterBend = function (channel, control, value, status) {
  659.     return;
  660.     // Ignore if the music speed is outside the motor abilities and the platter is stopped
  661.     if (StantonSCS1d.state["outsideMotor"]) return;
  662.     
  663.     //if (!StantonSCS1d.state["platterGrabbed"])
  664.         engine.setValue("[Channel"+StantonSCS1d.deck+"]","wheel",(value-64)/63);
  665. }
  666.  
  667. StantonSCS1d.platterScratch = function (channel, control, value, status) {
  668.     return;
  669.     // Ignore if the music speed is outside the motor abilities and the platter is stopped
  670.     if (StantonSCS1d.state["outsideMotor"]) return;
  671.     
  672.     var currentMode = StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"];
  673.     switch (currentMode) {
  674.         case "browse":
  675.             if ((value-64)>0) {
  676.                 engine.setValue("[Playlist]","SelectNextTrack",1);
  677.             }
  678.             else {
  679.                 engine.setValue("[Playlist]","SelectPrevTrack",1);
  680.             }
  681.             break;
  682.         case "vinyl":
  683.             var group = "[Channel"+StantonSCS1d.deck+"]";
  684.             var jogValue = (value-64)/3;
  685.             if (engine.getValue(group,"play")==1 && engine.getValue(group,"reverse")==1) jogValue= -(jogValue);
  686.             
  687.             var multiplier = StantonSCS1d.scratching["sensitivity"] * (engine.getValue(group,"play") ? 1 : StantonSCS1d.scratching["stoppedMultiplier"] );
  688. //              if (StantonSCS1d.debug) print("do scratching VALUE:" + value + " jogValue: " + jogValue );
  689.             engine.setValue(group,"scratch", (engine.getValue(group,"scratch") + (jogValue * multiplier)).toFixed(2));
  690.             break;
  691.     }
  692.     return;
  693.     //print("Play="+engine.getValue("[Channel"+StantonSCS1d.deck+"]","play")+ " Wheel="+engine.getValue("[Channel"+StantonSCS1d.deck+"]","wheel")+ " Jog="+engine.getValue("[Channel"+StantonSCS1d.deck+"]","jog")+ " Scratch="+engine.getValue("[Channel"+StantonSCS1d.deck+"]","scratch"));
  694.     //engine.setValue("[Channel"+StantonSCS1d.deck+"]","wheel",engine.getValue("[Channel"+StantonSCS1d.deck+"]","wheel")+(value-64)/63);
  695.     
  696.     // From SCS.1m
  697.     //var jogValue = (value-64)/128; // -64 to +63, - = CCW, + = CW
  698.  
  699.     //var group = "[Channel"+StantonSCS1d.deck+"]";
  700.     //if (engine.getValue(group,"play")==1 && engine.getValue(group,"reverse")==1) jogValue= -(jogValue);
  701.     //var multiplier = 0.18 * (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play") ? 1 : 2 );
  702.     //if (StantonSCS1d.debug) print("do scratching VALUE:" + value + " jogValue: " + jogValue + " scratch="+ engine.getValue(group,"scratch"));
  703.     //engine.setValue(group,"scratch", (engine.getValue(group,"scratch") + (jogValue * multiplier)).toFixed(2));
  704.     
  705.     // (33+1/3)/60 /1000
  706.     
  707.     var add = ((33+1/3)/60)/100;
  708.     
  709.     if (value==0x3d) add = -add;
  710.  
  711.     var newPosition = engine.getValue("[Channel"+StantonSCS1d.deck+"]","playposition") + add / engine.getValue("[Channel"+StantonSCS1d.deck+"]",    "duration");
  712.     engine.setValue("[Channel"+StantonSCS1d.deck+"]","playposition",newPosition);
  713. }
  714.  
  715. StantonSCS1d.scratchDecay = function (value) {
  716.  
  717.     // do some scratching
  718.     //if (StantonSCS1d.debug) print("Scratch deck"+StantonSCS1d.deck+": " + engine.getValue("[Channel"+StantonSCS1d.deck+"]","scratch"));
  719.     
  720.     //scratch = engine.getValue("[Channel"+StantonSCS1d.deck+"]","scratch");
  721.     //jogDecayRate = StantonSCS1d.slippage * (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play") ? 1 : 0.2 );
  722.      
  723.     //if (scratch != 0) {
  724.     //    if (Math.abs(scratch) > jogDecayRate*0.01) {  
  725.     //          engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", (scratch * jogDecayRate).toFixed(4));
  726.     //       } else {
  727.     //          engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", 0);
  728.     //       }
  729.     //    }
  730.     
  731.     var scratch = engine.getValue("[Channel"+StantonSCS1d.deck+"]","scratch");
  732.     var jogDecayRate = (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play") ? 0.7 : 0.7 );
  733.     //print ("Latency="+engine.getValue("[Soundcard]","latency"));
  734.     
  735.     //if (StantonSCS1d.debug) print("Scratch deck"+StantonSCS1d.deck+": " + scratch + ", Jog decay rate="+jogDecayRate);
  736.     
  737.     // If it was playing, ramp back to playback speed
  738.     if (StantonSCS1d.scratch["wasPlaying"] && !StantonSCS1d.state["platterGrabbed"]) {
  739.         var rate = engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate") * engine.getValue("[Channel"+StantonSCS1d.deck+"]","rateRange");
  740.         var convergeTo = 1+rate;
  741.         //jogDecayRate = StantonSCS1d.scratching["slippage"] * 0.2;
  742.         if (scratch != convergeTo) { // Thanks to jusics on IRC for help with this part
  743.             if (Math.abs(scratch-convergeTo) > jogDecayRate*0.001) {  
  744.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", (convergeTo + (scratch-convergeTo) * jogDecayRate).toFixed(5));
  745.                 //engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", (scratch + (convergeTo - scratch) / jogDecayRate).toFixed(5));
  746.             } else {
  747.                 // Once "scratch" has gotten close enough to the play speed, just resume normal playback
  748.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", 0);
  749.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","play",1);
  750.                 StantonSCS1d.scratch["wasPlaying"] = false;
  751.             }
  752.         }
  753.     } else
  754.     if (scratch != 0) { // For regular scratching when stopped or if playing (and ramp down...grabbed functions set scratch=1 and play=0)
  755.         if (Math.abs(scratch) > jogDecayRate*0.001) {  
  756.               engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", (scratch * jogDecayRate).toFixed(4));
  757.            } else {
  758.               engine.setValue("[Channel"+StantonSCS1d.deck+"]","scratch", 0);
  759.            }
  760.     }
  761. }
  762. */
  763.  
  764. // FIXME: use a timer
  765. StantonSCS1d.lightDelay = function () {
  766.     var date = new Date();
  767.     var curDate = null;
  768.     
  769.     do { curDate = new Date(); }
  770.     while(curDate-date < 60);
  771. }
  772.  
  773. StantonSCS1d.DeckChange = function (channel, control, value, status) {
  774.     if (StantonSCS1d.checkInSetup()) return;
  775.     var byte1 = 0x90 + channel;
  776.     if ((status & 0xF0) == 0x90) {  // If button down
  777.         midi.sendShortMsg(byte1,control,127); // Make button orange
  778.         StantonSCS1d.modifier["DeckSelect"]=1;   // Set button modifier flag
  779.         StantonSCS1d.modifier["deckTime"] = new Date();  // Store the current time in milliseconds
  780.         StantonSCS1d.connectKnobSignals(channel,true);   // Disconnect old knob signals & turn off their LEDs
  781.         StantonSCS1d.state["Oldknob"]=StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];   // Store old knob mode
  782.         midi.sendShortMsg(0x80+channel,4,0);  // Darken the knob bank button
  783.         StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"]=3;   // Global controls
  784.         StantonSCS1d.connectKnobSignals(channel);   // Connect knob signals
  785.         return;
  786.     }
  787.     StantonSCS1d.modifier["DeckSelect"]=0;   // Clear button modifier flag
  788.     StantonSCS1d.connectKnobSignals(channel,true);   // Disconnect old knob signals & turn off their LEDs
  789.     StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"]=StantonSCS1d.state["Oldknob"];   // Restore previous mode
  790.  
  791.     var newPlatterMode;
  792.     // If the button's been held down for over a second, stay on the current deck
  793.     if (new Date() - StantonSCS1d.modifier["deckTime"]>StantonSCS1d.deckChangeWait) {
  794.         //StantonSCS1d.connectKnobSignals(channel);   // Re-connect (restored) knob signals
  795.         // Return to appropriate color
  796.         if (StantonSCS1d.deck==2) midi.sendShortMsg(byte1,control,64); // Deck select button red
  797.         else midi.sendShortMsg(byte1,control,32); // Deck select button green
  798.     }
  799.     else {
  800.         engine.scratchDisable(StantonSCS1d.deck);    // To avoid accidentally stopping the outgoing deck
  801.         StantonSCS1d.connectDeckSignals(channel,true);    // Disconnect static signals
  802.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 17, 0xF7]),7); // Extinguish all LEDs
  803.         
  804.         if (StantonSCS1d.globalMode) newPlatterMode = StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"];
  805.         if (StantonSCS1d.deck == 2) {
  806.             if (StantonSCS1d.debug) print("StantonSCS1d: Switching to deck 1");
  807.             StantonSCS1d.deck--;
  808.             midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  809.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  810.             // Blank jog character
  811.             //midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 36, 0x20, 0xF7]),8);
  812.             // Deck number jog character
  813.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 36, '1'.toInt(), 0xF7]),8);
  814.             if (!StantonSCS1d.fastDeckChange) { // Make flashy lights to signal a deck change
  815.                 // TODO: Replace this with a timer
  816.                 StantonSCS1d.lightDelay();
  817.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  818.                 midi.sendShortMsg(byte1,control,32); // Deck select button green
  819.                 StantonSCS1d.lightDelay();
  820.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  821.                 midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  822.                 StantonSCS1d.lightDelay();
  823.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  824.                 midi.sendShortMsg(byte1,control,32); // Deck select button green
  825.                 StantonSCS1d.lightDelay();
  826.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  827.                 midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  828.                 StantonSCS1d.lightDelay();
  829.             }
  830.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  831.             midi.sendShortMsg(byte1,control,32); // Deck select button green
  832.         }
  833.         else {
  834.             if (StantonSCS1d.debug) print("StantonSCS1d: Switching to deck 2");
  835.             StantonSCS1d.deck++;
  836.             midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  837.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  838.             // Blank jog character
  839.             //midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 36, 0x20, 0xF7]),8);
  840.             // Deck number jog character
  841.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 36, '2'.toInt(), 0xF7]),8);
  842.             if (!StantonSCS1d.fastDeckChange) { // Make flashy lights to signal a deck change
  843.                 StantonSCS1d.lightDelay();
  844.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  845.                 midi.sendShortMsg(byte1,control,64); // Deck select button red
  846.                 StantonSCS1d.lightDelay();
  847.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  848.                 midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  849.                 StantonSCS1d.lightDelay();
  850.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  851.                 midi.sendShortMsg(byte1,control,64); // Deck select button red
  852.                 StantonSCS1d.lightDelay();
  853.                 midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 0, 0xF7]),8); // Jog backlight off
  854.                 midi.sendShortMsg(0x80 + channel,control,0); // Deck select button off
  855.                 StantonSCS1d.lightDelay();
  856.             }
  857.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, 13, 1, 0xF7]),8); // Jog backlight on
  858.             midi.sendShortMsg(byte1,control,64); // Deck select button red
  859.         }
  860.         StantonSCS1d.connectDeckSignals(channel);    // Connect static signals
  861.         StantonSCS1d.padRefresh();  // Light pad section correctly
  862.     }
  863.     if (StantonSCS1d.globalMode) StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"] = StantonSCS1d.state["Oldknob"];
  864.     else newPlatterMode = StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"];
  865.     
  866.     StantonSCS1d.connectKnobSignals(channel);   // Connect new knob signals & light LEDs & displays
  867.     StantonSCS1d.encoderBank(channel, 4, 0, 0x80);  // Light the bank button the correct color for the mode
  868.     
  869.     switch(newPlatterMode) {
  870.         case "control": StantonSCS1d.controlButton(channel, StantonSCS1d.buttons["control"], value, 0x90 + channel); break;
  871.         case "browse": StantonSCS1d.browseButton(channel, StantonSCS1d.buttons["browse"], value, 0x90 + channel); break;
  872.         case "vinyl": StantonSCS1d.vinylButton(channel, StantonSCS1d.buttons["vinyl"], value, 0x90 + channel); break;
  873.     }
  874.     
  875.     switch (StantonSCS1d.triggerBank[StantonSCS1d.deck]) {
  876.         case 1: StantonSCS1d.triggerBankSelect(channel, 22, 1, 0x90); break;
  877.         case 2: StantonSCS1d.triggerBankSelect(channel, 23, 1, 0x90); break;
  878.         case 3: StantonSCS1d.triggerBankSelect(channel, 5, 1, 0x90); break;
  879.     }
  880.     
  881. }   // End Deck Change function
  882.  
  883. // ----------   Encoders  ----------
  884.  
  885. StantonSCS1d.encoderBank = function (channel, control, value, status) {
  886.     if (StantonSCS1d.checkInSetup() || StantonSCS1d.modifier["DeckSelect"]==1) return;
  887.     
  888.     var newMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  889.     
  890.     if ((status & 0xF0) == 0x80) {  // If button up
  891.         print("Newmode="+newMode);
  892.         switch (newMode) {
  893.             case 1:
  894.                 midi.sendShortMsg(0x90+channel,control,32);    // Make button green
  895.                 break;
  896.             case 2: // FX params
  897.                 midi.sendShortMsg(0x90+channel,control,64);    // Make button red
  898.                 break;
  899.             default:
  900.                 print("Stanton SCS.1d: Unhandled knob mode #"+newMode);
  901.                 midi.sendShortMsg(0x80+channel,control,0);    // Turn button off
  902.                 break;
  903.         }
  904.     }
  905.     
  906.     if ((status & 0xF0) == 0x90) {  // If button down
  907.         midi.sendShortMsg(0x90+channel,control,127);    // Make button orange
  908.         StantonSCS1d.connectKnobSignals(channel,true);  // Disconnect old knob signals & turn off their LEDs
  909.         newMode++;
  910.         if (newMode>2) newMode=1;   // Wrap
  911.         StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"] = newMode;
  912.         StantonSCS1d.connectKnobSignals(channel);   // Connect new knob signals & light LEDs & displays
  913.     }
  914.     
  915.     // Use button 1 to toggle the flanger effect if in mode 2 (which adjusts FX params)
  916.     if (newMode==2) {
  917.         engine.connectControl("[Channel"+StantonSCS1d.deck+"]","flanger","StantonSCS1d.FXLED");
  918.         engine.trigger("[Channel"+StantonSCS1d.deck+"]","flanger");
  919.     }
  920.     else {
  921.         engine.connectControl("[Channel"+StantonSCS1d.deck+"]","flanger","StantonSCS1d.FXLED",true);
  922.         midi.sendShortMsg(0x80+channel,0x19,0);    // Turn button off
  923.     }
  924. }
  925.  
  926. StantonSCS1d.encoder1 = function (channel, control, value, status) {
  927.     if (StantonSCS1d.checkInSetup()) return;
  928.     var offset = (value-64);
  929.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  930.     switch(knobMode) {
  931.         case 1: // Low EQ
  932.             switch (status & 0xF0) {
  933.                 case 0x90:  // Reset to center
  934.                     StantonSCS1d.encoderSetAbs(knobMode,1,1);
  935.                     break;
  936.                 case 0xb0:  // Adjust
  937.                     offset = offset*0.1;
  938.                     StantonSCS1d.encoderSet(knobMode,1,offset,0,4);
  939.                     break;
  940.             }
  941.             break;
  942.         case 2: // Flanger depth
  943.             switch (status & 0xF0) {
  944.                 case 0x90:  // Reset to center
  945.                     StantonSCS1d.encoderSetAbs(knobMode,1,0.5);
  946.                     break;
  947.                 case 0xb0:  // Adjust
  948.                     offset = offset*0.03;
  949.                     StantonSCS1d.encoderSet(knobMode,1,offset,0,1);
  950.                     break;
  951.             }
  952.             break;
  953.         case 3: // Pre/Main mix
  954.             switch (status & 0xF0) {
  955.                 case 0x90:  // Reset to Left
  956.                     StantonSCS1d.encoderSetAbs(knobMode,1,-1);
  957.                     break;
  958.                 case 0xb0:  // Adjust
  959.                     offset = offset*0.1;
  960.                     StantonSCS1d.encoderSet(knobMode,1,offset,-1,1);
  961.                     break;
  962.             }
  963.             break;
  964.     }
  965. }
  966.  
  967. StantonSCS1d.display1button = function (channel, control, value, status) {
  968.     if (StantonSCS1d.checkInSetup()) return;
  969.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  970.     switch(knobMode) {
  971.         case 1: // Low EQ momentary kill
  972.             switch (status & 0xF0) {
  973.                 case 0x90:
  974.                     midi.sendShortMsg(0x90+channel,control,1);
  975.                     if (StantonSCS1d.mutex[control]) break;
  976.                     StantonSCS1d.encoderSetAbs(knobMode,1,0,true);
  977.                     StantonSCS1d.mutex[control]=true;
  978.                     break;
  979.                 case 0x80:
  980.                     StantonSCS1d.encoderSetAbs(knobMode,1,"previous");
  981.                     midi.sendShortMsg(0x90+channel,control,0);
  982.                     StantonSCS1d.mutex[control]=false;
  983.                     break;
  984.             }
  985.             break;
  986.         case 2: // Toggle Flanger
  987.             switch (status & 0xF0) {
  988.                 case 0x90:
  989.                     engine.setValue("[Channel"+StantonSCS1d.deck+"]","flanger",!engine.getValue("[Channel"+StantonSCS1d.deck+"]","flanger"));
  990.                     break;
  991.             }
  992.             break;
  993.         case 3: // Momentary Main mix
  994.             switch (status & 0xF0) {
  995.                 case 0x90:
  996.                     midi.sendShortMsg(0x90+channel,control,1);
  997.                     if (StantonSCS1d.mutex[control]) break;
  998.                     StantonSCS1d.encoderSetAbs(knobMode,1,1,true);
  999.                     StantonSCS1d.mutex[control]=true;
  1000.                     break;
  1001.                 case 0x80:
  1002.                     StantonSCS1d.encoderSetAbs(knobMode,1,"previous");
  1003.                     midi.sendShortMsg(0x90+channel,control,0);
  1004.                     StantonSCS1d.mutex[control]=false;
  1005.                     break;
  1006.             }
  1007.             break;
  1008.     }
  1009. }
  1010.  
  1011. StantonSCS1d.encoder2 = function (channel, control, value, status) {
  1012.     if (StantonSCS1d.checkInSetup()) return;
  1013.     var offset = (value-64);
  1014.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1015.     switch(knobMode) {
  1016.         case 1: // Mid EQ
  1017.             switch (status & 0xF0) {
  1018.                 case 0x90:  // Reset
  1019.                     StantonSCS1d.encoderSetAbs(knobMode,2,1);
  1020.                     break;
  1021.                 case 0xb0:  // Adjust
  1022.                     offset = offset*0.1;
  1023.                     StantonSCS1d.encoderSet(knobMode,2,offset,0,4);
  1024.                     break;
  1025.             }
  1026.             break;
  1027.         case 2: // Flanger delay
  1028.             switch (status & 0xF0) {
  1029.                 case 0x90:  // Reset
  1030.                     StantonSCS1d.encoderSetAbs(knobMode,2,4950);
  1031.                     break;
  1032.                 case 0xb0:  // Adjust
  1033.                     offset = offset*100;
  1034.                     StantonSCS1d.encoderSet(knobMode,2,offset,50,10000);
  1035.                     break;
  1036.             }
  1037.             break;
  1038.         case 3: // Headphone volume
  1039.             switch (status & 0xF0) {
  1040.                 case 0x90:  // Reset
  1041.                     StantonSCS1d.encoderSetAbs(knobMode,2,1);
  1042.                     break;
  1043.                 case 0xb0:  // Adjust
  1044.                     offset = offset*0.1;
  1045.                     StantonSCS1d.encoderSet(knobMode,2,offset,0,5);
  1046.                     break;
  1047.             }
  1048.             break;
  1049.     }
  1050. }
  1051.  
  1052. StantonSCS1d.display2button = function (channel, control, value, status) {
  1053.     if (StantonSCS1d.checkInSetup()) return;
  1054.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1055.     switch(knobMode) {
  1056.         case 1: // Mid EQ momentary kill
  1057.             switch (status & 0xF0) {
  1058.                 case 0x90:
  1059.                     midi.sendShortMsg(0x90+channel,control,1);
  1060.                     if (StantonSCS1d.mutex[control]) break;
  1061.                     StantonSCS1d.encoderSetAbs(knobMode,2,0,true);
  1062.                     StantonSCS1d.mutex[control]=true;
  1063.                     break;
  1064.                 case 0x80:
  1065.                     StantonSCS1d.encoderSetAbs(knobMode,2,"previous");
  1066.                     midi.sendShortMsg(0x90+channel,control,0);
  1067.                     StantonSCS1d.mutex[control]=false;
  1068.                     break;
  1069.             }
  1070.             break;
  1071.         case 2: // Flanger delay
  1072.             break;
  1073.         case 3: // Headphone volume
  1074.             break;
  1075.     }
  1076. }
  1077.  
  1078. StantonSCS1d.encoder3 = function (channel, control, value, status) {
  1079.     if (StantonSCS1d.checkInSetup()) return;
  1080.     var offset = (value-64);
  1081.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1082.     switch(knobMode) {
  1083.         case 1: // High EQ
  1084.             switch (status & 0xF0) {
  1085.                 case 0x90:  // Reset
  1086.                     StantonSCS1d.encoderSetAbs(knobMode,3,1);
  1087.                     break;
  1088.                 case 0xb0:  // Adjust
  1089.                     offset = offset*0.1;
  1090.                     StantonSCS1d.encoderSet(knobMode,3,offset,0,4);
  1091.                     break;
  1092.             }
  1093.             break;
  1094.         case 2: // Flanger period (LFO)
  1095.             switch (status & 0xF0) {
  1096.                 case 0x90:  // Reset
  1097.                     StantonSCS1d.encoderSetAbs(knobMode,3,1025000);
  1098.                     break;
  1099.                 case 0xb0:  // Adjust
  1100.                     offset = offset*10000;
  1101.                     StantonSCS1d.encoderSet(knobMode,3,offset,50000,2000000);
  1102.                     break;
  1103.             }
  1104.             break;
  1105.         case 3: // Master Balance
  1106.             switch (status & 0xF0) {
  1107.                 case 0x90:  // Reset
  1108.                     StantonSCS1d.encoderSetAbs(knobMode,3,0);
  1109.                     break;
  1110.                 case 0xb0:  // Adjust
  1111.                     offset = offset*0.08;
  1112.                     StantonSCS1d.encoderSet(knobMode,3,offset,-1,1);
  1113.                     break;
  1114.             }
  1115.             break;
  1116.     }
  1117. }
  1118.  
  1119. StantonSCS1d.display3button = function (channel, control, value, status) {
  1120.     if (StantonSCS1d.checkInSetup()) return;
  1121.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1122.     switch(knobMode) {
  1123.         case 1: // High EQ momentary kill
  1124.             switch (status & 0xF0) {
  1125.                 case 0x90:
  1126.                     midi.sendShortMsg(0x90+channel,control,1);
  1127.                     if (StantonSCS1d.mutex[control]) break;
  1128.                     StantonSCS1d.encoderSetAbs(knobMode,3,0,true);
  1129.                     StantonSCS1d.mutex[control]=true;
  1130.                     break;
  1131.                 case 0x80:
  1132.                     StantonSCS1d.encoderSetAbs(knobMode,3,"previous");
  1133.                     midi.sendShortMsg(0x90+channel,control,0);
  1134.                     StantonSCS1d.mutex[control]=false;
  1135.                     break;
  1136.             }
  1137.             break;
  1138.         case 2: // Flanger period
  1139.             break;
  1140.         case 3: // Master Balance
  1141.             switch (status & 0xF0) {
  1142.                 case 0x90:  // Full right
  1143.                     midi.sendShortMsg(0x90+channel,control,1);
  1144.                     if (StantonSCS1d.mutex[control]) break;
  1145.                     StantonSCS1d.encoderSetAbs(knobMode,3,1);
  1146.                     StantonSCS1d.mutex[control]=true;
  1147.                     break;
  1148.                 case 0x80:  // Full left
  1149.                     StantonSCS1d.encoderSetAbs(knobMode,3,-1);
  1150.                     midi.sendShortMsg(0x90+channel,control,0);
  1151.                     StantonSCS1d.mutex[control]=false;
  1152.                     break;
  1153.             }
  1154.             break;
  1155.     }
  1156. }
  1157.  
  1158. StantonSCS1d.encoder4 = function (channel, control, value, status) {
  1159.     if (StantonSCS1d.checkInSetup()) return;
  1160.     var offset = (value-64);
  1161.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1162.     switch(knobMode) {
  1163.         case 1: // Channel volume
  1164.             switch (status & 0xF0) {
  1165.                 case 0x90:  // Reset
  1166.                     StantonSCS1d.encoderSetAbs(knobMode,4,1);
  1167.                     break;
  1168.                 case 0xb0:  // Adjust
  1169.                     offset = offset*0.025;
  1170.                     StantonSCS1d.encoderSet(knobMode,4,offset,0,1);
  1171.                     break;
  1172.             }
  1173.             break;
  1174.         case 2: // Pre-fader gain
  1175.             switch (status & 0xF0) {
  1176.                 case 0x90:  // Reset
  1177.                     StantonSCS1d.encoderSetAbs(knobMode,4,1);
  1178.                     break;
  1179.                 case 0xb0:  // Adjust
  1180.                     offset = offset*0.05;
  1181.                     StantonSCS1d.encoderSet(knobMode,4,offset,0,4);
  1182.                     break;
  1183.             }
  1184.             break;
  1185.         case 3: // Master volume
  1186.             switch (status & 0xF0) {
  1187.                 case 0x90:  // Reset
  1188.                     StantonSCS1d.encoderSetAbs(knobMode,4,1);
  1189.                     break;
  1190.                 case 0xb0:  // Adjust
  1191.                     offset = offset*0.05;
  1192.                     StantonSCS1d.encoderSet(knobMode,4,offset,0,5);
  1193.                     break;
  1194.             }
  1195.             break;
  1196.     }
  1197. }
  1198.  
  1199. StantonSCS1d.display4button = function (channel, control, value, status) {
  1200.     if (StantonSCS1d.checkInSetup()) return;
  1201.     var knobMode = StantonSCS1d.knobMode["[Channel"+StantonSCS1d.deck+"]"];
  1202.     switch(knobMode) {
  1203.         case 1: // Channel volume momentary kill
  1204.             switch (status & 0xF0) {
  1205.                 case 0x90:
  1206.                     midi.sendShortMsg(0x90+channel,control,1);
  1207.                     if (StantonSCS1d.mutex[control]) break;
  1208.                     StantonSCS1d.encoderSetAbs(knobMode,4,0,true);
  1209.                     StantonSCS1d.mutex[control]=true;
  1210.                     break;
  1211.                 case 0x80:
  1212.                     StantonSCS1d.encoderSetAbs(knobMode,4,"previous");
  1213.                     midi.sendShortMsg(0x90+channel,control,0);
  1214.                     StantonSCS1d.mutex[control]=false;
  1215.                     break;
  1216.             }
  1217.             break;
  1218.         case 2: // Pre-fader gain
  1219.             break;
  1220.         case 3: // Master volume momentary kill
  1221.             switch (status & 0xF0) {
  1222.                 case 0x90:
  1223.                     midi.sendShortMsg(0x90+channel,control,1);
  1224.                     if (StantonSCS1d.mutex[control]) break;
  1225.                     StantonSCS1d.encoderSetAbs(knobMode,4,0,true);
  1226.                     StantonSCS1d.mutex[control]=true;
  1227.                     break;
  1228.                 case 0x80:
  1229.                     StantonSCS1d.encoderSetAbs(knobMode,4,"previous");
  1230.                     midi.sendShortMsg(0x90+channel,control,0);
  1231.                     StantonSCS1d.mutex[control]=false;
  1232.                     break;
  1233.             }
  1234.             break;
  1235.     }
  1236. }
  1237.  
  1238. StantonSCS1d.encoderSet = function (mode,knob,offset,min,max) {
  1239.     var signalList = StantonSCS1d.knobSignals[mode-1];
  1240.     var group = signalList[knob-1][0];
  1241.     if (group=="CurrentChannel") group = "[Channel"+StantonSCS1d.deck+"]";
  1242.     var newValue=engine.getValue(group,signalList[knob-1][1])+offset;
  1243.     if (newValue<min) newValue=min;
  1244.     if (newValue>max) newValue=max;
  1245.     engine.setValue(group,signalList[knob-1][1],newValue);
  1246. }
  1247.  
  1248. StantonSCS1d.encoderSetAbs = function (mode,knob,value,save) {
  1249.     var signalList = StantonSCS1d.knobSignals[mode-1];
  1250.     var group = signalList[knob-1][0];
  1251.     if (group=="CurrentChannel") group = "[Channel"+StantonSCS1d.deck+"]";
  1252.     var key=signalList[knob-1][1];
  1253.     // Save value for possible use later
  1254.     if (save) StantonSCS1d.prevValues[key]=engine.getValue(group,key);
  1255.     // Restore to previous value (for kill buttons)
  1256.     if (value=="previous") engine.setValue(group,key,StantonSCS1d.prevValues[key]);
  1257.     else engine.setValue(group,key,value)
  1258. }
  1259.  
  1260. // ----------   Sliders  ----------
  1261.  
  1262. StantonSCS1d.pitchSlider = function (channel, control, value) {
  1263.     var currentValue = engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate");
  1264.     var newValue = (value-64)/63;
  1265.     if (newValue<-1) newValue=-1.0;
  1266.     if (newValue>1) newValue=1.0;
  1267.     StantonSCS1d.state["dontMove"]=new Date();
  1268.     if (StantonSCS1d.crossFader && StantonSCS1d.modifier["pitchRange"]==1) {
  1269.         engine.setValue("[Master]","crossfader",newValue);
  1270.         StantonSCS1d.state["crossfaderAdjusted"]=true;
  1271.     }
  1272.     else engine.setValue("[Channel"+StantonSCS1d.deck+"]","rate",newValue);
  1273. }
  1274.  
  1275. // ----------   Trigger buttons  ----------
  1276.  
  1277. StantonSCS1d.triggerBankSelect = function (channel, control, value, status) {
  1278.     if ((status & 0xF0) == 0x90) {    // If button down
  1279.         var byte1 = 0x80 + channel;
  1280.         midi.sendShortMsg(byte1,22,0x00); // Turn off bank button lights
  1281.         midi.sendShortMsg(byte1,23,0x00);
  1282.         midi.sendShortMsg(byte1,5,0x00);
  1283.         
  1284.         switch (control) {
  1285.             case 22: StantonSCS1d.triggerBank[StantonSCS1d.deck]=1; break;
  1286.             case 23: StantonSCS1d.triggerBank[StantonSCS1d.deck]=2; break;
  1287.             case 5: StantonSCS1d.triggerBank[StantonSCS1d.deck]=3; break;
  1288.         }
  1289.         
  1290.         byte1 = 0x90 + channel;
  1291.         midi.sendShortMsg(byte1,control,0x01); // Turn on active bank button light
  1292.         
  1293.         // Light all the buttons since they're all pre-set
  1294.         for (var i=8; i<=15; i++)
  1295.             midi.sendShortMsg(byte1,i,0x01); // Turn on button light
  1296.         for (var i=18; i<=21; i++)
  1297.             midi.sendShortMsg(byte1,i,0x01); // Turn on button light
  1298.         
  1299.         ////  Print evrytinks
  1300.         //for (var i=8; i<=15; i++) {
  1301.         //    print ("                <control>\n\
  1302.         //            <status>0x90</status>\n\
  1303.         //            <midino>"+i+"</midino>\n\
  1304.         //            <group>[Master]</group>\n\
  1305.         //            <key>StantonSCS1d.triggerButton</key>\n\
  1306.         //            <options>\n\
  1307.         //                <Script-Binding/>\n\
  1308.         //            </options>\n\
  1309.         //        </control>");
  1310.         //    print ("                <control>\n\
  1311.         //            <status>0x80</status>\n\
  1312.         //            <midino>"+i+"</midino>\n\
  1313.         //            <group>[Master]</group>\n\
  1314.         //            <key>StantonSCS1d.triggerButton</key>\n\
  1315.         //            <options>\n\
  1316.         //                <Script-Binding/>\n\
  1317.         //            </options>\n\
  1318.         //        </control>");
  1319.         //}
  1320.         //for (var i=18; i<=21; i++) {
  1321.         //    print ("                <control>\n\
  1322.         //            <status>0x90</status>\n\
  1323.         //            <midino>"+i+"</midino>\n\
  1324.         //            <group>[Master]</group>\n\
  1325.         //            <key>StantonSCS1d.triggerButton</key>\n\
  1326.         //            <options>\n\
  1327.         //                <Script-Binding/>\n\
  1328.         //            </options>\n\
  1329.         //        </control>");
  1330.         //    print ("                <control>\n\
  1331.         //            <status>0x80</status>\n\
  1332.         //            <midino>"+i+"</midino>\n\
  1333.         //            <group>[Master]</group>\n\
  1334.         //            <key>StantonSCS1d.triggerButton</key>\n\
  1335.         //            <options>\n\
  1336.         //                <Script-Binding/>\n\
  1337.         //            </options>\n\
  1338.         //        </control>");
  1339.         //}
  1340.     }
  1341. }
  1342.  
  1343. StantonSCS1d.triggerButton = function (channel, control, value, status) {
  1344.     
  1345.     var byte1 = 0x90 + channel;
  1346.     
  1347.     var index = StantonSCS1d.triggerBank[StantonSCS1d.deck];
  1348.     
  1349.     if ((status & 0xF0) == 0x90) {    // If button down
  1350.         midi.sendShortMsg(byte1,control,0x01); // Turn off activated button LED
  1351.  
  1352.             // Multiple pitch points
  1353.             
  1354.             //if (StantonSCS1d.modifier[currentMode]==1) {    // Delete a pitch point if the mode button is held
  1355.             //    StantonSCS1d.pitchPoints[index][button] = -0.1;
  1356.             //    break;
  1357.             //}
  1358.             if (StantonSCS1d.pitchPoints[index][control] == -0.1)
  1359.                 StantonSCS1d.pitchPoints[index][control] = engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate");
  1360.             else {
  1361.                 // Need 100% range for values to be correct
  1362.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","rateRange",1.0);
  1363.                 engine.setValue("[Channel"+StantonSCS1d.deck+"]","rate",StantonSCS1d.pitchPoints[index][control]); 
  1364.             }
  1365.         return;
  1366.     }
  1367.     midi.sendShortMsg(byte1,control,0x01); // Turn on activated button LED
  1368. }
  1369.  
  1370. // ----------   Other buttons  ----------
  1371.  
  1372. StantonSCS1d.EnterButton = function (channel, control, value, status) {
  1373.     StantonSCS1d.buttonLED(value,control,127,0);
  1374.     if ((status & 0xF0) == 0x90) {    // If button down
  1375.         // If the deck is playing and the cross-fader is not completely toward the other deck...
  1376.         if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play")==1 &&
  1377.         ((StantonSCS1d.deck==1 && engine.getValue("[Master]","crossfader")<1.0) || 
  1378.         (StantonSCS1d.deck==2 && engine.getValue("[Master]","crossfader")>-1.0))) {
  1379.             // ...light the button red to show acknowledgement of the press but don't load
  1380.             StantonSCS1d.buttonLED(value,control,64,0);
  1381.             print ("StantonSCS1d: Not loading into deck "+StantonSCS1d.deck+" because it's playing to the Master output.");
  1382.         }
  1383.         else {
  1384.             engine.setValue("[Channel"+StantonSCS1d.deck+"]","LoadSelectedTrack",1);
  1385.         }
  1386.     }
  1387. }
  1388.  
  1389. // ----------   Pad section  ----------
  1390.  
  1391. StantonSCS1d.padRefresh = function () {     // Refresh the LEDs and LCDs in the pad section
  1392.     StantonSCS1d.state["padCleared"]=true;
  1393.     StantonSCS1d.velocityButton(StantonSCS1d.channel,0x34,0,0x80);
  1394.     
  1395.     // For each pad
  1396.     /*
  1397.     var deck = StantonSCS1d.padBank["deck"];
  1398.     var bank = StantonSCS1d.padBank["bank"+deck];
  1399.     for (i=0; i<=3; i++) {
  1400.         StantonSCS1d.pad(StantonSCS1d.channel,0x20+i,0,0x80);
  1401.         // Displays
  1402.         var message = "Empty";
  1403.         if (StantonSCS1d.padPoints[deck][bank][0x20+i] != -0.1) {
  1404.             var trackTime = ((StantonSCS1d.padPoints[deck][bank][0x20+i] * StantonSCS1d.trackDuration[deck])*1000) | 0;  // OR with 0 replaces Math.floor and is faster
  1405.             message = msecondstominutes(trackTime);
  1406.         }
  1407.         midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 6+i],message.toInt(), 0xF7),7+message.length);   // Set LCD
  1408.     }
  1409.     */
  1410.     
  1411.     StantonSCS1d.padBankButton(StantonSCS1d.channel, 0x35, 0, 0x80);
  1412.     StantonSCS1d.padBankButton(StantonSCS1d.channel, 0x36, 0, 0x80);
  1413.     
  1414.     StantonSCS1d.connectPadSignals(StantonSCS1d.channel);
  1415. }
  1416.  
  1417. StantonSCS1d.pad = function (channel, control, value, status) {
  1418.     var deck = StantonSCS1d.padBank["deck"];
  1419.     var bank = StantonSCS1d.padBank["bank"+deck];
  1420.     var hotCue = StantonSCS1d.hotCues[bank][control];
  1421.     var byte1 = 0x90 + channel;
  1422.  
  1423.     if ((status & 0xF0) == 0x90) {    // If button down
  1424.         StantonSCS1d.modifier["pad"]=1;
  1425.  
  1426.         // Multiple cue points
  1427.         // Mixxx has 31 hot cues. Skip any above that.
  1428.         
  1429.         if (hotCue == -1) return;
  1430.  
  1431.         if (StantonSCS1d.modifier["velocityToggle"]==1) { // Delete a cue point
  1432.             engine.setValue("[Channel"+deck+"]","hotcue_"+hotCue+"_clear",1);
  1433.             engine.setValue("[Channel"+deck+"]","hotcue_"+hotCue+"_clear",0);
  1434.             StantonSCS1d.state["padCleared"]=true;
  1435.             return;
  1436.         }
  1437.         
  1438.         if (StantonSCS1d.padVelocity && engine.getValue("[Channel"+deck+"]","hotcue_"+hotCue+"_enabled"))
  1439.             engine.setValue("[Channel"+deck+"]","volume",(value/0x7A));   // 0x7A seems the highest # the unit sends
  1440.             
  1441.         // If hotcue X is set, seeks the player to hotcue X's position.
  1442.         // If hotcue X is not set, sets hotcue X to the current play position.
  1443.         engine.setValue("[Channel"+deck+"]","hotcue_"+hotCue+"_activate",1);
  1444.         
  1445.  
  1446.         // Original code
  1447.         /*
  1448.         midi.sendShortMsg(byte1,control,127); // Light it orange
  1449.         
  1450.         if (StantonSCS1d.modifier["velocityToggle"]==1) {
  1451.             StantonSCS1d.padPoints[deck][bank][control] = -0.1;   // Erase
  1452.             var message = "Empty";
  1453.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, control-26],message.toInt(), 0xF7),7+message.length);   // Clear LCD
  1454.             StantonSCS1d.state["padCleared"]=true;
  1455.             return;
  1456.         }
  1457.         
  1458.         if (StantonSCS1d.padPoints[deck][bank][control] == -0.1) { // Set if blank
  1459.             StantonSCS1d.padPoints[deck][bank][control] = engine.getValue("[Channel"+deck+"]","playposition");
  1460.             // Set display with cue point time
  1461.             // Track time in milliseconds
  1462.             var trackTime = ((StantonSCS1d.padPoints[deck][bank][control] * StantonSCS1d.trackDuration[deck])*1000) | 0;  // OR with 0 replaces Math.floor and is faster
  1463.             var message = msecondstominutes(trackTime);
  1464.             midi.sendSysexMsg(StantonSCS1d.sysex.concat([channel, control-26],message.toInt(), 0xF7),7+message.length);   // Set LCD
  1465.         }
  1466.         else {
  1467.             engine.setValue("[Channel"+deck+"]","playposition",StantonSCS1d.padPoints[deck][bank][control]);  // Recall
  1468.             if (StantonSCS1d.padVelocity) engine.setValue("[Channel"+deck+"]","volume",(value/0x7A));   // 0x7A seems the highest # the unit sends
  1469.             if (engine.getValue("[Channel"+deck+"]","play") != 1) { // Start the deck playing if it isn't already
  1470.                 engine.setValue("[Channel"+deck+"]","play",1);
  1471.                 if (StantonSCS1d.state["padWasPlaying"]==null) StantonSCS1d.state["padWasPlaying"]=false;
  1472.             }
  1473.             else if (StantonSCS1d.state["padWasPlaying"]==null) StantonSCS1d.state["padWasPlaying"]=true;
  1474.         }
  1475.         */
  1476.         return;
  1477.     }
  1478.     
  1479.     // If button up
  1480.     engine.setValue("[Channel"+deck+"]","hotcue_"+hotCue+"_activate",0);
  1481.     StantonSCS1d.modifier["pad"]=0;
  1482.     
  1483.     // Original code
  1484.     /*
  1485.     if (StantonSCS1d.state["padWasPlaying"]==false) engine.setValue("[Channel"+deck+"]","play",0);
  1486.     StantonSCS1d.state["padWasPlaying"]=null;
  1487.     StantonSCS1d.modifier["pad"]=0;
  1488.     
  1489.     // If a cue point is set, make the pad light red
  1490.     if (StantonSCS1d.padPoints[deck][bank][control] != -0.1) midi.sendShortMsg(byte1,control,32);
  1491.     else midi.sendShortMsg(byte1,control,0x00); // If not, darken the pad
  1492.     */
  1493.     
  1494. }
  1495.  
  1496. StantonSCS1d.velocityButton = function (channel, control, value, status) {
  1497.     var byte1 = 0x90 + channel;
  1498.  
  1499.     if ((status & 0xF0) == 0x90) {  // If button down
  1500.         midi.sendShortMsg(byte1,control,127); // Light it orange
  1501.         StantonSCS1d.modifier["velocityToggle"]=1;
  1502.         return;
  1503.     }
  1504.     
  1505.     // On button up
  1506.     StantonSCS1d.modifier["velocityToggle"]=0;
  1507.     if (!StantonSCS1d.state["padCleared"]) StantonSCS1d.padVelocity = !StantonSCS1d.padVelocity;
  1508.     StantonSCS1d.state["padCleared"]=false;
  1509.     if (StantonSCS1d.padVelocity) midi.sendShortMsg(byte1,control,64);  //red
  1510.     else midi.sendShortMsg(0x80 + channel,control,0);   // off
  1511. }
  1512.  
  1513. StantonSCS1d.padBankButton = function (channel, control, value, status) {
  1514.     var byte1 = 0x90 + channel;
  1515.     var deck=control-0x34;
  1516.     
  1517.     if ((status & 0xF0) == 0x90) {  // If button down
  1518.         midi.sendShortMsg(byte1,control,127); // Light it orange
  1519.         StantonSCS1d.modifier["padBank"+deck]=1;
  1520.         
  1521.         StantonSCS1d.connectPadSignals(channel,true);    // Disconnect existing signals
  1522.     
  1523.         if (StantonSCS1d.padBank["deck"]!=deck) {
  1524.             StantonSCS1d.padBank["deck"]=deck;
  1525.             if (deck==1) midi.sendShortMsg(0x80 + channel,0x36,0);   // turn off deck 2 button
  1526.             else midi.sendShortMsg(0x80 + channel,0x35,0);  // turn off deck 1 button
  1527.         }
  1528.         else {
  1529.             if (StantonSCS1d.padBank["bank"+deck]==3) StantonSCS1d.padBank["bank"+deck]=1;
  1530.             else StantonSCS1d.padBank["bank"+deck]++;
  1531.             
  1532.             if (StantonSCS1d.debug) print("PadBank="+StantonSCS1d.padBank["bank"+deck]);
  1533.         }
  1534.         StantonSCS1d.padRefresh();    // Signals connected here
  1535.         return;
  1536.     }
  1537.     else StantonSCS1d.modifier["padBank"+deck]=0;
  1538.     
  1539.     // In either case
  1540.     if (StantonSCS1d.padBank["deck"]!=deck) midi.sendShortMsg(0x80 + channel,control,0);    // off
  1541.     else switch (StantonSCS1d.padBank["bank"+deck]) {
  1542.         case 1:
  1543.             midi.sendShortMsg(byte1,control,32);    // green
  1544.             break;
  1545.         case 2:
  1546.             midi.sendShortMsg(byte1,control,64);    // red
  1547.             break;
  1548.         case 3:
  1549.             midi.sendShortMsg(byte1,control,127);   // orange
  1550.             break;
  1551.     }
  1552. }
  1553.  
  1554. // ----------   Slot functions  ----------
  1555.  
  1556. StantonSCS1d.reverse = function (value) {
  1557.     // Ignore if fast-forwarding or rewinding
  1558.     if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","fwd")>0 || engine.getValue("[Channel"+StantonSCS1d.deck+"]","back")>0) return;
  1559.     var CC = 0xB0 + StantonSCS1d.channel;
  1560.     if (value>0) {
  1561.         if (StantonSCS1d.platterSpeed==1) midi.sendShortMsg(CC,1,'4'.toInt());   // 45 RPM backward
  1562.         else midi.sendShortMsg(CC,1,'3'.toInt());   // 33 RPM backward
  1563.         return;
  1564.     }
  1565.     if (StantonSCS1d.platterSpeed==1) midi.sendShortMsg(CC,1,'2'.toInt());   // 45 RPM foreward
  1566.     else midi.sendShortMsg(CC,1,'1'.toInt());   // 33 RPM foreward
  1567. }
  1568.  
  1569. StantonSCS1d.pitchChange = function (value) {
  1570.     var CC = 0xB0 + StantonSCS1d.channel;
  1571.     
  1572.     if (StantonSCS1d.crossFader && StantonSCS1d.modifier["pitchRange"]==1) return; // Skip if adjusting the cross-fader
  1573.     if (value < -1 || value > 1) return;  // FIXME: This sometimes happens after using the BPM button to set the tempo and changing the pitch range. We should find out why.
  1574.     var now=new Date();
  1575.     // Move slider if applicable
  1576.     if (now - StantonSCS1d.state["dontMove"]>100) {
  1577.         if (StantonSCS1d.debug) print ("Moving slider to "+(value*63+64));
  1578.         midi.sendShortMsg(0xB0+StantonSCS1d.channel,0x00,value*63+64);
  1579.         }
  1580.     
  1581.     // Skip motor speed change if fast-forwarding or rewinding
  1582.     if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","fwd")>0 || engine.getValue("[Channel"+StantonSCS1d.deck+"]","back")>0) return;
  1583.     
  1584.     if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate_dir") == -1) value = -value; // Take slider direction into account
  1585.     var multiplier = value * engine.getValue("[Channel"+StantonSCS1d.deck+"]","rateRange");
  1586.     var iMotorPitch = 1000+(1000*multiplier);
  1587.     if (iMotorPitch < 500 || iMotorPitch > 2000) {
  1588.         if (engine.getValue("[Channel"+StantonSCS1d.deck+"]","play")>0 && !StantonSCS1d.state["outsideMotor"]) {
  1589.             if (StantonSCS1d.debug) print ("Stopping platter motor: music speed is outside its abilities");
  1590.             engine.scratchDisable(StantonSCS1d.deck);    // Disable direct platter control
  1591.             midi.sendShortMsg(CC,1,'x'.toInt());    // Stop platter
  1592.         }
  1593.         StantonSCS1d.state["outsideMotor"]=true;
  1594.         return;
  1595.     } else {    // Start the platter since the music speed is within the ability of the motor
  1596.         StantonSCS1d.state["outsideMotor"]=false;
  1597.         if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] == "vinyl" && engine.getValue("[Channel"+StantonSCS1d.deck+"]","play")>0) {
  1598.             midi.sendShortMsg(CC,1,'o'.toInt());
  1599.             // Re-enable direct platter control
  1600.             engine.scratchEnable(StantonSCS1d.deck, StantonSCS1d.scratch["resolution"], StantonSCS1d.rpm[StantonSCS1d.platterSpeed],StantonSCS1d.scratch["alpha"], StantonSCS1d.scratch["beta"]);
  1601.         }
  1602.     }
  1603.     if (iMotorPitch < 500) iMotorPitch = 500;    // Or stop
  1604.     if (iMotorPitch > 2000) iMotorPitch = 2000;    // Or stop
  1605.     //print("Motor pitch: "+iMotorPitch+" multiplier: "+multiplier);
  1606.     
  1607.     // Convert for AVR protocol
  1608.     iMotorPitch -= 500;
  1609.     p1 = iMotorPitch  / 1000; 
  1610.     p2 = (iMotorPitch / 100) % 10;
  1611.     p3 = (iMotorPitch /  10) % 10;
  1612.     p4 = (iMotorPitch      ) % 10;
  1613.     
  1614.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, 35, p1, p2, p3, p4, 0xF7]),11);  // Change motor speed
  1615. }
  1616.  
  1617. // --------   LEDs  --------
  1618.  
  1619. StantonSCS1d.buttonLED = function (value, note, on, off) {
  1620.     if (value>0) midi.sendShortMsg(0x90 + StantonSCS1d.channel,note,on);
  1621.     else midi.sendShortMsg(0x80 + StantonSCS1d.channel,note,off);
  1622. }
  1623.  
  1624. // Transport buttons
  1625.  
  1626. StantonSCS1d.playLED = function (value) {
  1627.     var CC = 0xB0 + StantonSCS1d.channel;
  1628.     //if (StantonSCS1d.debug) print ("PlatterGrabbed="+StantonSCS1d.state["platterGrabbed"]);
  1629.     //if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] == "vinyl" && !StantonSCS1d.state["platterGrabbed"]) {
  1630.     if (StantonSCS1d.platterMode["[Channel"+StantonSCS1d.deck+"]"] == "vinyl") {
  1631.         if (value==0) midi.sendShortMsg(CC,1,'x'.toInt());   // Stop platter
  1632.         else if (!StantonSCS1d.state["outsideMotor"]) midi.sendShortMsg(CC,1,'o'.toInt());   // Start platter
  1633.     }
  1634.     StantonSCS1d.buttonLED(value, 41, 32, 0x00);    //green
  1635. }
  1636.  
  1637. StantonSCS1d.cueLED = function (value) {
  1638.     StantonSCS1d.buttonLED(value, 43, 127, 0x00);  //orange
  1639. }
  1640.  
  1641. StantonSCS1d.syncLED = function (value) {
  1642.     StantonSCS1d.buttonLED(value, 42, 64, 0x00);    //red
  1643. }
  1644.  
  1645. StantonSCS1d.backLED = function (value) {
  1646.     StantonSCS1d.buttonLED(value, 47, 0x7f, 0x00);
  1647. }
  1648.  
  1649. StantonSCS1d.fwdLED = function (value) {
  1650.     StantonSCS1d.buttonLED(value, 46, 0x7f, 0x00);
  1651. }
  1652.  
  1653. StantonSCS1d.headphoneLED = function (value) {
  1654.     StantonSCS1d.buttonLED(value, 55, 0x7f, 32);
  1655. }
  1656.  
  1657. StantonSCS1d.FXLED = function (value) {
  1658.     StantonSCS1d.buttonLED(value, 0x19, 0x7f, 0);
  1659. }
  1660.  
  1661. // ---- Hot cues ----
  1662.  
  1663. // Pad LCDs
  1664.  
  1665. StantonSCS1d.PadDisplay = function (value, pad) {
  1666.     // value = hotcue position
  1667.     var deck = StantonSCS1d.padBank["deck"];
  1668.     var bank = StantonSCS1d.padBank["bank"+deck];
  1669.     var hotCue = StantonSCS1d.hotCues[bank][pad];
  1670.     
  1671.     var message= "Hotcue"+hotCue;    // If empty, just display hot cue #
  1672.     
  1673.     if (value!=-1) {
  1674.         // Set display with cue point time
  1675.         var samplerate = engine.getValue("[Channel"+deck+"]","track_samplerate");
  1676.         var msecs = (value/2/samplerate)*1000 | 0;    // OR with 0 replaces Math.floor and is faster
  1677.         
  1678.         // Track time in milliseconds
  1679.         //var trackTime = ((StantonSCS1d.padPoints[deck][bank][control] * StantonSCS1d.trackDuration[deck])*1000) | 0;  // OR with 0 replaces Math.floor and is faster
  1680.         //var message = msecondstominutes(trackTime);
  1681.         message = msecondstominutes(msecs);
  1682.     }
  1683.     else {
  1684.         // Light pad LED on hot cue erase
  1685.         midi.sendShortMsg(0x90 + StantonSCS1d.channel,pad,127);
  1686.     }
  1687.     
  1688.     // Display value
  1689.     midi.sendSysexMsg(StantonSCS1d.sysex.concat([StantonSCS1d.channel, pad-26],message.toInt(), 0xF7),7+message.length);
  1690. }
  1691.  
  1692. StantonSCS1d.Pad1LCD = function (value) { StantonSCS1d.PadDisplay(value,0x20); }
  1693. StantonSCS1d.Pad2LCD = function (value) { StantonSCS1d.PadDisplay(value,0x21); }
  1694. StantonSCS1d.Pad3LCD = function (value) { StantonSCS1d.PadDisplay(value,0x22); }
  1695. StantonSCS1d.Pad4LCD = function (value) { StantonSCS1d.PadDisplay(value,0x23); }
  1696.  
  1697. // Pad LEDs
  1698.  
  1699. StantonSCS1d.PadLED = function (value, pad) {
  1700.     var deck = StantonSCS1d.padBank["deck"];
  1701.     var bank = StantonSCS1d.padBank["bank"+deck];
  1702.     var hotCue = StantonSCS1d.hotCues[bank][pad];
  1703.     
  1704.     //print ("Hot cue #"+hotCue+": value="+value+", activated="+activate);
  1705.  
  1706.     var color = 0;
  1707.     
  1708.     if (value != 0) {    // Activated
  1709.         color = 127;
  1710.     }
  1711.     else {    // Deactivated
  1712.         if (engine.getValue("[Channel"+deck+"]","hotcue_"+hotCue+"_enabled")==1) color = 32;    // Hot cue set (green)
  1713.     }
  1714.     
  1715.     midi.sendShortMsg(0x90 + StantonSCS1d.channel,pad,color);
  1716. }
  1717.  
  1718. // Hot cue activated LEDs
  1719. StantonSCS1d.Pad1aLED = function (value) { StantonSCS1d.PadLED(value,0x20); }
  1720. StantonSCS1d.Pad2aLED = function (value) { StantonSCS1d.PadLED(value,0x21); }
  1721. StantonSCS1d.Pad3aLED = function (value) { StantonSCS1d.PadLED(value,0x22); }
  1722. StantonSCS1d.Pad4aLED = function (value) { StantonSCS1d.PadLED(value,0x23); }
  1723.  
  1724. // Encoders
  1725.  
  1726. // -- 0..1..4 controls --
  1727.  
  1728. StantonSCS1d.encoder1EQLEDs = function (value) {
  1729.     StantonSCS1d.encoderEQLEDs(value,127);
  1730. }
  1731.  
  1732. StantonSCS1d.encoder2EQLEDs = function (value) {
  1733.     StantonSCS1d.encoderEQLEDs(value,126);
  1734. }
  1735.  
  1736. StantonSCS1d.encoder3EQLEDs = function (value) {
  1737.     StantonSCS1d.encoderEQLEDs(value,125);
  1738. }
  1739.  
  1740. StantonSCS1d.encoder4EQLEDs = function (value) {
  1741.     StantonSCS1d.encoderEQLEDs(value,124);
  1742. }
  1743.  
  1744. // -- 0..1..5 controls --
  1745.  
  1746. StantonSCS1d.encoder1MVolumeLEDs = function (value) {
  1747.     StantonSCS1d.encoderMVolumeLEDs(value,127);
  1748. }
  1749.  
  1750. StantonSCS1d.encoder2MVolumeLEDs = function (value) {
  1751.     StantonSCS1d.encoderMVolumeLEDs(value,126);
  1752. }
  1753.  
  1754. StantonSCS1d.encoder3MVolumeLEDs = function (value) {
  1755.     StantonSCS1d.encoderMVolumeLEDs(value,125);
  1756. }
  1757.  
  1758. StantonSCS1d.encoder4MVolumeLEDs = function (value) {
  1759.     StantonSCS1d.encoderMVolumeLEDs(value,124);
  1760. }
  1761.  
  1762. // -- 0..1 controls --
  1763.  
  1764. StantonSCS1d.encoder4VolumeLEDs = function (value) {
  1765.     StantonSCS1d.encoderVolumeLEDs(value,124);
  1766. }
  1767.  
  1768. // -- -1..0..1 controls --
  1769.  
  1770. StantonSCS1d.encoder1BalanceLEDs = function (value) {
  1771.     StantonSCS1d.encoderBalanceLEDs(value,127);
  1772. }
  1773.  
  1774. StantonSCS1d.encoder3BalanceLEDs = function (value) {
  1775.     StantonSCS1d.encoderBalanceLEDs(value,125);
  1776. }
  1777.  
  1778. // ---
  1779.  
  1780. StantonSCS1d.encoderEQLEDs = function (value,control) {
  1781.     var LEDs=StantonSCS1d.EncoderBoostCut(value, 0, 1, 4, 8, 8);
  1782.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,control,21+LEDs);
  1783. }
  1784.  
  1785. StantonSCS1d.encoderBalanceLEDs = function (value,control) {
  1786.     var LEDs=StantonSCS1d.EncoderBoostCut(value, -1, 0, 1, 8, 8);
  1787.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,control,21+LEDs);
  1788. }
  1789.  
  1790. StantonSCS1d.EncoderBoostCut = function (value, low, mid, high, lowMidSteps, midHighSteps) {
  1791.     var LEDs = 0;
  1792.     var lowMidInterval = (mid-low)/(lowMidSteps*2);     // Half the actual interval so the LEDs light in the middle of the interval
  1793.     var midHighInterval = (high-mid)/(midHighSteps*2);  // Half the actual interval so the LEDs light in the middle of the interval
  1794.     value=value.toFixed(4);
  1795.     if (value>low) LEDs++;
  1796.     for (var i=1; i<(lowMidSteps*2); i+=2) {
  1797.         if (value>low+lowMidInterval*i) LEDs++;
  1798.         }
  1799.     for (var i=1; i<(midHighSteps*2); i+=2) {
  1800.         if (value>mid+midHighInterval*i) LEDs++;
  1801.         }
  1802.     if (value>=high) LEDs++;
  1803.     return LEDs;
  1804. }
  1805.  
  1806. StantonSCS1d.encoderMVolumeLEDs = function (value,control) {
  1807.     //var LEDs=StantonSCS1d.EncoderPeak(value, 0, 5, 18);
  1808.     // 0..1..5
  1809.     var low=0;
  1810.     var mid=1;
  1811.     var high=5;
  1812.     var max=18;
  1813.     
  1814.     var top=max/2;
  1815.     var add;
  1816.     if (value<mid) add=(value*(top+1))/(mid-low);
  1817.     else add=((value*top)/(high-mid))+(top-2);
  1818.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,control,40+add);
  1819. }
  1820.  
  1821. StantonSCS1d.encoderVolumeLEDs = function (value,control) {
  1822.     var LEDs=StantonSCS1d.EncoderPeak(value, 0, 1, 18);
  1823.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,control,40+LEDs);
  1824. }
  1825.  
  1826. StantonSCS1d.EncoderPeak = function (value, low, high, number) {
  1827.     var LEDs = 0;
  1828.     var range = (high-low)/number;
  1829.     for (var i=0; i<(number-1); i++)
  1830.         if (value>low+range*i) LEDs++;  // When i==0, this becomes if (value>low) LEDs++;
  1831.     if (value>=high) LEDs++;
  1832.     return LEDs;
  1833. }
  1834.  
  1835. StantonSCS1d.FXDepthLEDs = function (value) {
  1836.     var add = StantonSCS1d.EncoderPeak(value,0,1,18);
  1837.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,127,40+add);
  1838. }
  1839.  
  1840. StantonSCS1d.FXDelayLEDs = function (value) {
  1841.     var add = StantonSCS1d.EncoderPeak(value,50,10000,18);
  1842.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,126,40+add);
  1843. }
  1844.  
  1845. StantonSCS1d.FXPeriodLEDs = function (value) {
  1846.     var add = StantonSCS1d.EncoderPeak(value,50000,2000000,18);
  1847.     midi.sendShortMsg(0xB0+StantonSCS1d.channel,125,40+add);
  1848. }
  1849.  
  1850. StantonSCS1d.pitchRangeLEDs = function (value) {
  1851.     StantonSCS1d.pitchChange(engine.getValue("[Channel"+StantonSCS1d.deck+"]","rate")); // So the platter speed is updated
  1852.     var on = 0x90 + StantonSCS1d.channel;
  1853.     var off = 0x80 + StantonSCS1d.channel;
  1854.     switch (true) {
  1855.         case (value<0.08):
  1856.                 midi.sendShortMsg(off,65,0);   // 8%
  1857.                 midi.sendShortMsg(off,66,0);   // 16%
  1858.                 midi.sendShortMsg(off,67,0);   // 25%
  1859.                 midi.sendShortMsg(off,68,0);   // 50%
  1860.             break;
  1861.         case (value==0.08):
  1862.                 midi.sendShortMsg(on,65,1);   // 8%
  1863.                 midi.sendShortMsg(off,66,0);   // 16%
  1864.                 midi.sendShortMsg(off,67,0);   // 25%
  1865.                 midi.sendShortMsg(off,68,0);   // 50%
  1866.             break;
  1867.         case (value>0.08 && value<0.16):
  1868.                 midi.sendShortMsg(on,65,1);   // 8%
  1869.                 midi.sendShortMsg(on,66,1);   // 16%
  1870.                 midi.sendShortMsg(off,67,0);   // 25%
  1871.                 midi.sendShortMsg(off,68,0);   // 50%
  1872.             break;
  1873.         case (value==0.16):
  1874.                 midi.sendShortMsg(off,65,0);   // 8%
  1875.                 midi.sendShortMsg(on,66,1);   // 16%
  1876.                 midi.sendShortMsg(off,67,0);   // 25%
  1877.                 midi.sendShortMsg(off,68,0);   // 50%
  1878.             break;
  1879.         case (value>0.16 && value<0.25):
  1880.                 midi.sendShortMsg(off,65,0);   // 8%
  1881.                 midi.sendShortMsg(on,66,1);   // 16%
  1882.                 midi.sendShortMsg(on,67,1);   // 25%
  1883.                 midi.sendShortMsg(off,68,0);   // 50%
  1884.             break;
  1885.         case (value==0.25):
  1886.                 midi.sendShortMsg(off,65,0);   // 8%
  1887.                 midi.sendShortMsg(off,66,0);   // 16%
  1888.                 midi.sendShortMsg(on,67,1);   // 25%
  1889.                 midi.sendShortMsg(off,68,0);   // 50%
  1890.             break;
  1891.         case (value>0.25 && value<0.5):
  1892.                 midi.sendShortMsg(off,65,0);   // 8%
  1893.                 midi.sendShortMsg(off,66,0);   // 16%
  1894.                 midi.sendShortMsg(on,67,1);   // 25%
  1895.                 midi.sendShortMsg(on,68,1);   // 50%
  1896.             break;
  1897.         case (value==0.5):
  1898.                 midi.sendShortMsg(off,65,0);   // 8%
  1899.                 midi.sendShortMsg(off,66,0);   // 16%
  1900.                 midi.sendShortMsg(off,67,0);   // 25%
  1901.                 midi.sendShortMsg(on,68,1);   // 50%
  1902.             break;
  1903.         case (value>0.5):
  1904.                 midi.sendShortMsg(on,65,1);   // 8%
  1905.                 midi.sendShortMsg(on,66,1);   // 16%
  1906.                 midi.sendShortMsg(on,67,1);   // 25%
  1907.                 midi.sendShortMsg(on,68,1);   // 50%
  1908.             break;
  1909.     }
  1910. }
  1911.  
  1912. StantonSCS1d.circleBars1 = function (value) {
  1913.     if (StantonSCS1d.deck!=1) return;
  1914.     StantonSCS1d.circleBars(value);
  1915. }
  1916.  
  1917. StantonSCS1d.circleBars2 = function (value) {
  1918.     if (StantonSCS1d.deck!=2) return;
  1919.     StantonSCS1d.circleBars(value);
  1920. }
  1921.  
  1922. StantonSCS1d.durationChange1 = function (value) {
  1923.     StantonSCS1d.trackDuration[1]=value;
  1924.     StantonSCS1d.padRefresh();    // Update hot cues
  1925. }
  1926.  
  1927. StantonSCS1d.durationChange2 = function (value) {
  1928.     StantonSCS1d.trackDuration[2]=value;
  1929.     StantonSCS1d.padRefresh();    // Update hot cues
  1930. }
  1931.  
  1932. StantonSCS1d.circleBars = function (value) {
  1933.     // Revolution time of the imaginary record in seconds
  1934. //     var revtime = StantonSCS1d.scratch["revtime"]/2;    // Use this for two bars
  1935.     var revtime = StantonSCS1d.scratch["revtime"];
  1936.     var currentTrackPos = value * StantonSCS1d.trackDuration[StantonSCS1d.deck];
  1937.     
  1938.     var revolutions = currentTrackPos/revtime;
  1939. //     var light = ((revolutions-(revolutions|0))*18)|0;    // Use this for two bars
  1940.     var light = ((revolutions-(revolutions|0))*36)|0;   // OR with 0 replaces Math.floor and is faster
  1941.  
  1942.     if (StantonSCS1d.lastLight[StantonSCS1d.deck]==light) return;   // Don't send light commands if there's no visible change
  1943.     
  1944.     var byte1 = 0xB0 + StantonSCS1d.channel;
  1945.     //midi.sendShortMsg(byte1,2,0);     // Clear circle markers
  1946.     StantonSCS1d.lastLight[StantonSCS1d.deck]=light;
  1947.     midi.sendShortMsg(byte1,2,light+1);
  1948. //     midi.sendShortMsg(byte1,2,18+light);   // Add this for two bars
  1949. }
  1950.  
  1951. /*
  1952. TODO:
  1953. - Add looping controls
  1954. - If in Browse mode, press Browse again to change category (Playlists, Crates, etc.)
  1955.  
  1956. - Motor calibration option (would be really nice to have GUI interaction for this)
  1957. - Wait for motor to stop in-between mode/deck changes
  1958. - Wait for motor to get to speed before changing to vinyl mode
  1959. - Stop motor on FF/REW? If not, FF/REW only at motor speed?
  1960.  
  1961. - Use timers for flashy deck change lights
  1962. - End-of-track warning flash
  1963.  
  1964. - Extend .rangeButton*() to work with arbitrary-length pitchRanges array
  1965.  
  1966. BUGS:
  1967. - Window dragging screws up speed - use timestamps
  1968. - Sticker drift - timestamps?
  1969. - Changing pitch makes speed jiggly - timestamps??
  1970. */