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 / midi-mappings-scripts.js < prev    next >
Text File  |  2011-02-04  |  16KB  |  384 lines

  1. // Functions common to all controllers go in this file
  2.  
  3. nop = function () {}    // Only here so you don't get a syntax error on load if the file was otherwise empty
  4.  
  5. // ----------------- Prototype enhancements ---------------------
  6.  
  7. // Returns an ASCII byte array for the string
  8. String.prototype.toInt = function() {
  9.     var a = new Array();
  10.     for (var i = 0; i < this.length; i++) {
  11.         a[i] = this.charCodeAt(i);
  12.     }
  13.     return a;
  14. }
  15.  
  16. // ----------------- Function overloads ---------------------
  17.  
  18. // Causes script print() calls to appear in the log file as well
  19. print = function(string) {
  20.     engine.log(string);
  21. }
  22.  
  23. // ----------------- Generic functions ---------------------
  24.  
  25. function secondstominutes(secs)
  26. {
  27.    var m = (secs / 60) | 0;
  28.  
  29.    return (m < 10 ? "0" + m : m) 
  30.           + ":"
  31.           + ( ( secs %= 60 ) < 10 ? "0" + secs : secs);
  32. }
  33.  
  34. function msecondstominutes(msecs)
  35. {
  36.     var m = (msecs / 60000) | 0;
  37.     msecs %= 60000;
  38.     var secs = (msecs / 1000) | 0;
  39.     msecs %= 1000;
  40.     msecs = Math.round(msecs * 100 / 1000);
  41.     if (msecs==100) msecs=99;
  42.     
  43. //     print("secs="+secs+", msecs="+msecs);
  44.  
  45.     return (m < 10 ? "0" + m : m) 
  46.         + ":"
  47.         + ( secs < 10 ? "0" + secs : secs )
  48.         + "."
  49.         + ( msecs < 10 ? "0" + msecs : msecs);
  50. }
  51.  
  52. function script() {}
  53. script.debug = function (channel, control, value, status, group) {
  54.     print("Script.Debug --- channel: " + channel.toString(16) + 
  55.           " control: " + control.toString(16) + " value: " + value.toString(16) + 
  56.           " status: " + status.toString(16) + " group: " + group);
  57. }
  58.  
  59. // Used to control a generic Mixxx control setting (low..high) from an absolute control (0..127)
  60. script.absoluteSlider = function (group, key, value, low, high) {
  61.     if (value==127) engine.setValue(group, key, high);
  62.     else engine.setValue(group, key, (((high - low) / 127) * value) + low);
  63. }
  64.  
  65. // Returns a value for a non-linear Mixxx control (like EQs: 0..1..4) from an absolute control (0..127)
  66. script.absoluteNonLin = function (value, low, mid, high) {
  67.     if (value<=64) return value/(64/(mid-low));
  68.     else return 1+(value-63)/(64/(high-mid));
  69. }
  70.  
  71. // DEPRECATED
  72. // Used to control an EQ setting (0..1..4) from an absolute control (0..127)
  73. script.absoluteEQ = function (group, key, value) {
  74.     if (value<=64) engine.setValue(group, key, value/64);
  75.     else engine.setValue(group, key, 1+(value-63)/(21+1/3));
  76.     print ("MIDI Script: script.absoluteEQ is deprecated. " + 
  77.            "Use script.absoluteNonLin(value,0,1,4) instead and set the " + 
  78.            "MixxxControl to its return value.");
  79. }
  80.  
  81. /* -------- ------------------------------------------------------
  82.      script.Pitch
  83.    Purpose: Takes the value from a little-endian 14-bit MIDI pitch
  84.             wheel message and returns the value for a "rate" (pitch
  85.             slider) Mixxx control
  86.    Input:   Least significant byte, most sig. byte, MIDI status byte
  87.    Output:  Value for a "rate" control, or false if the input MIDI
  88.             message was not a Pitch message (0xE#)
  89.    -------- ------------------------------------------------------ */
  90. script.pitch = function (LSB, MSB, status) {
  91.     if ((status & 0xF0) != 0xE0) {  // Mask the upper nybble so we can check the opcode regardless of the channel
  92.         print("Script.Pitch: Error, not a MIDI pitch message: "+status);
  93.         return false;
  94.     }
  95.     var value = (MSB << 7) | LSB;  // Construct the 14-bit number
  96.     // Range is 0x0000..0x3FFF center @ 0x2000, i.e. 0..16383 center @ 8192
  97.     var rate = (value-8192)/8191;
  98. //     print("Script.Pitch: MSB="+MSB+", LSB="+LSB+", value="+value+", rate="+rate);
  99.     return rate;
  100. }
  101.  
  102. // bpm - Used for tapping the desired BPM for a deck
  103. function bpm() {}
  104.  
  105. bpm.tapTime = 0.0;
  106. bpm.tap = [];   // Tap sample values
  107.  
  108. /* -------- ------------------------------------------------------
  109.         bpm.tapButton
  110.    Purpose: Sets the bpm of the track on a deck by tapping the beats.
  111.             This only works if the track's original BPM value is correct.
  112.             Call this each time the tap button is pressed.
  113.    Input:   Mixxx deck to adjust
  114.    Output:  -
  115.    -------- ------------------------------------------------------ */
  116. bpm.tapButton = function(deck) {
  117.     var now = new Date()/1000;   // Current time in seconds
  118.     var tapDelta = now - bpm.tapTime;
  119.     bpm.tapTime=now;
  120.     if (tapDelta>2.0) { // reset if longer than two seconds between taps
  121.         bpm.tap=[];
  122.         return;
  123.     }
  124.     bpm.tap.push(60/tapDelta);
  125.     if (bpm.tap.length>8) bpm.tap.shift();  // Keep the last 8 samples for averaging
  126.     var sum = 0;
  127.     for (i=0; i<bpm.tap.length; i++) {
  128.         sum += bpm.tap[i];
  129.     }
  130.     var average = sum/bpm.tap.length;
  131.     
  132.     var fRateScale = average/engine.getValue("[Channel"+deck+"]","bpm");
  133.     
  134.     // Adjust the rate:
  135.     fRateScale = (fRateScale-1.)/engine.getValue("[Channel"+deck+"]","rateRange");
  136.     
  137.     engine.setValue("[Channel"+deck+"]","rate",fRateScale * engine.getValue("[Channel"+deck+"]","rate_dir"));
  138. //     print("Script: BPM="+average);
  139. }
  140.  
  141.  
  142. // ----------------- DEPRECATED Scratching functions ---------------------
  143. function scratch() {}
  144. // Allows for smooth scratching with MIDI controllers
  145. // See full details here: http://mixxx.org/wiki/doku.php/midi_scripting#available_common_functions
  146.  
  147. // ----------   Variables    ----------
  148. scratch.variables = { "time":0.0, "trackPos":0.0, "initialTrackPos":-1.0,
  149.                       "initialControlValue":0, "scratch":0.0,
  150.                       "prevControlValue":0, "wrapCount":0 };
  151.  
  152. // ----------   Functions   ----------
  153.  
  154. /* -------- ------------------------------------------------------
  155.     scratch.Enable
  156.    Purpose: Sets up initial variables for the scratch.<control>
  157.             functions below. Called when a modifier button is pressed
  158.             & held.
  159.    Input:   Currently-controlled Mixxx deck
  160.    Output:  -
  161.    -------- ------------------------------------------------------ */
  162. scratch.enable = function (currentDeck,newBehavior) {
  163.     // Store scratch info at the point it was touched
  164.     // Current position in seconds:
  165.     scratch.variables["initialTrackPos"] = scratch.variables["trackPos"] = engine.getValue("[Channel"+currentDeck+"]","visual_playposition") * engine.getValue("[Channel"+currentDeck+"]","duration");
  166.     
  167.     scratch.variables["time"] = new Date()/1000;   // Current time in seconds
  168.     if (newBehavior) engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
  169.     
  170.     // If the deck is playing, slow it to a stop
  171.     if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
  172.         // TODO: ramp down
  173.     }
  174.     
  175. //     print("MIDI Script: Scratch initial: time=" + scratch.variables["time"] + "s, track=" + scratch.variables["trackPos"] + "s");
  176.     return;
  177. }
  178.  
  179. /* -------- ------------------------------------------------------
  180.     scratch.Disable
  181.    Purpose: Clears varaibles used by the scratch.<control> functions
  182.             below. Called when the modifier button is released.
  183.    Input:   Currently-controlled Mixxx deck
  184.    Output:  -
  185.    -------- ------------------------------------------------------ */
  186. scratch.disable = function (currentDeck) {
  187.     // If the deck is playing, ramp it up to the play speed
  188.     if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
  189.         //TODO: ramp up
  190.     }
  191.     // Reset the triggers
  192.     scratch.variables["trackPos"] = 0.0;
  193.     scratch.variables["initialTrackPos"] = -1.0;
  194.     scratch.variables["initialControlValue"] = 0;
  195.     scratch.variables["prevControlValue"] = 0;  // for wheel
  196.     scratch.variables["wrapCount"] = 0; // for wheel
  197.     scratch.variables["time"] = 0.0;
  198.     scratch.variables["scratch"] = 0.0;
  199. //     print("MIDI Script: Scratch values CLEARED");
  200.     engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 0);  // disable scratching
  201.     engine.setValue("[Channel"+currentDeck+"]","scratch2",0);
  202.     engine.setValue("[Channel"+currentDeck+"]","scratch",0);    // Deprecated
  203. }
  204.  
  205. /* -------- ------------------------------------------------------
  206.     scratch.Slider
  207.    Purpose: Uses an alpha-beta filter to make scratching with a
  208.             slider (0..127) sound good, called each time there's a
  209.             new slider value
  210.    Input:   Currently-controlled Mixxx deck, value of the slider,
  211.             revolution time of the imaginary record (typically 1.8s,
  212.             for a 12" disc @ 33+1/3 RPM,) alpha & beta coefficients
  213.    Output:  New value for the "scratch" control
  214.    -------- ------------------------------------------------------ */
  215. scratch.slider = function (currentDeck, sliderValue, revtime, alpha, beta) {
  216.     // Skip if the track start position hasn't been set yet
  217.     if (scratch.variables["initialTrackPos"] == -1.0) return 0;
  218.     // If the slider start value hasn't been set yet, set it
  219.     if (scratch.variables["initialControlValue"] == 0) {
  220.         scratch.variables["initialControlValue"] = sliderValue;
  221.         if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
  222.             scratch.variables["scratch"] = 1;
  223.             engine.setValue("[Channel"+currentDeck+"]","scratch2", 1);
  224.         }
  225. //         engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
  226. //         print("Initial slider="+scratch.variables["initialControlValue"]);
  227.     }
  228.     return scratch.filter(currentDeck, sliderValue, revtime, alpha, beta);
  229. }
  230.  
  231. /* -------- ------------------------------------------------------
  232.     scratch.wheel
  233.    Purpose: Uses an alpha-beta filter to make scratching with a
  234.             wheel (0..127 with wrap) sound good, called each time
  235.             there's a new wheel value
  236.    Input:   Currently-controlled Mixxx deck, value of the wheel,
  237.             revolution time of the imaginary record (typically 1.8s,
  238.             for a 12" disc @ 33+1/3 RPM,) alpha & beta coefficients
  239.    Output:  New value for the "scratch" control
  240.    -------- ------------------------------------------------------ */
  241. scratch.wheel = function (currentDeck, wheelValue, revtime, alpha, beta) {
  242.     // Skip if the track start position hasn't been set yet
  243.     if (scratch.variables["initialTrackPos"] == -1.0) return 0;
  244.     // If the wheel start value hasn't been set yet, set it
  245.     if (scratch.variables["initialControlValue"] == 0) {
  246.         scratch.variables["initialControlValue"] = scratch.variables["prevControlValue"] = wheelValue;
  247.         if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
  248.             scratch.variables["scratch"] = 1;
  249.             engine.setValue("[Channel"+currentDeck+"]","scratch2", 1);
  250.         }
  251. //         engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
  252. //         print("Initial wheel="+scratch.variables["initialControlValue"]);
  253.     }
  254.         
  255.     // Take wrap around into account
  256.     if (wheelValue>=0 && wheelValue<10 && scratch.variables["prevControlValue"]>117 && scratch.variables["prevControlValue"]<=127) scratch.variables["wrapCount"]+=1;
  257.     if (wheelValue>117 && wheelValue<=127 && scratch.variables["prevControlValue"]>=0 && scratch.variables["prevControlValue"]<10) scratch.variables["wrapCount"]-=1;
  258.     
  259. //     From radiomark: change = (new - old + 192) % 128 - 64
  260.     
  261.     scratch.variables["prevControlValue"]=wheelValue;
  262.     wheelValue += scratch.variables["wrapCount"]*128;
  263.     
  264.     return scratch.filter(currentDeck, wheelValue, revtime, alpha, beta);
  265. }
  266.  
  267. // The actual alpha-beta filter
  268. scratch.filter = function (currentDeck, controlValue, revtime, alpha, beta) {
  269.     // ------------- Thanks to Mark Hills of Xwax (http://www.xwax.co.uk) for the info for below ------------------------
  270.     
  271.     // ideal position = (initial_p + (y - x) / 128 * 1.8)
  272.     var ideal_p = scratch.variables["initialTrackPos"] + (controlValue - scratch.variables["initialControlValue"]) / 128 * revtime;
  273.     
  274.     var currentTrackPos = engine.getValue("[Channel"+currentDeck+"]","visual_playposition") * engine.getValue("[Channel"+currentDeck+"]","duration");
  275.     var newTime = new Date()/1000;
  276.     var dt = newTime - scratch.variables["time"];
  277.     scratch.variables["time"] = newTime;
  278. //     print("dt="+dt);
  279.     
  280.     // predicted_p = p + dt * pitch; // Where "pitch" = 1.0 for regular speed, i.e. the "scratch" control.
  281.     var predicted_p = currentTrackPos + dt * scratch.variables["scratch"];
  282.     // rx = where_finger_corresponds_to - predicted_p;
  283.     var rx = ideal_p - predicted_p;
  284.     
  285.     // p += rx * ALPHA;
  286.     // scratch.variables["trackPos"] += rx * alpha;   // Don't need this result so why waste the CPU time?
  287.     
  288.     // v += rx * BETA / dt;
  289. //     scratch.variables["scratch"] += rx * beta / dt;   // This doesn't work
  290.     scratch.variables["scratch"] = rx * beta;
  291.     
  292. //     print("MIDI Script: Ideal position="+ideal_p+", Predicted position="+predicted_p + ", New scratch val=" + scratch.variables["scratch"]);
  293.     
  294. //     var newPos = scratch.variables["trackPos"]/engine.getValue("[Channel"+currentDeck+"]","duration");
  295. //     engine.setValue("[Channel"+currentDeck+"]","visual_playposition",newPos);
  296. //     engine.setValue("[Channel"+currentDeck+"]","scratch",scratch.variables["scratch"]);
  297.  
  298.     return scratch.variables["scratch"];
  299. }
  300. // ----------------- END Scratching functions --------------------
  301.  
  302. // ----------------- Object definitions --------------------------
  303.  
  304.  
  305. ButtonState = {"released":0x00, "pressed":0x7F};
  306. LedState =  {"off": 0x00, "on": 0x7F};
  307.  
  308. //Controller
  309. function Controller () {
  310.    this.group = "[Master]";
  311.    this.Controls = [];
  312.    this.Buttons = [];
  313. };
  314.  
  315. Controller.prototype.addButton = function(buttonName, button, eventHandler) {
  316.    if(eventHandler) {
  317.       var executionEnvironment = this;
  318.       function handler(value) {
  319.          button.state = value;
  320.          executionEnvironment[eventHandler](value);
  321.       }
  322.       button.handler = handler;
  323.    }
  324.    this.Buttons[buttonName] = button; 
  325. }
  326.  
  327. Controller.prototype.setControlValue = function(control, value) {
  328.    this.Controls[control].setValue(this.group, value);
  329. }
  330.  
  331. //Button
  332. function Button(controlId) {
  333.    this.controlId = controlId;
  334.    this.state = ButtonState.released;
  335. }
  336. Button.prototype.handleEvent = function(value) {
  337.    this.handler(value);
  338. };
  339.  
  340. //Control
  341. function Control(mappedFunction, softMode) {
  342.    this.minInput = 0;
  343.    this.midInput = 0x3F;
  344.    this.maxInput = 0x7F;
  345.    this.minOutput = -1.0;
  346.    this.midOutput = 0.0;
  347.    this.maxOutput = 1.0;
  348.    this.mappedFunction = mappedFunction;
  349.    this.softMode = softMode;
  350.    this.maxJump = 10;
  351. }
  352. Control.prototype.setValue = function(group, inputValue){
  353.    var outputValue = 0;
  354.    if(inputValue <= this.midInput){
  355.       outputValue = this.minOutput + ((inputValue - this.minInput) / (this.midInput - this.minInput)) * (this.midOutput - this.minOutput);
  356.    } else {
  357.       outputValue = this.midOutput + ((inputValue - this.midInput) / (this.maxInput - this.midInput)) * (this.maxOutput - this.midOutput);
  358.    }
  359.    if(this.softMode){ 
  360.       var currentValue = engine.getValue(group, this.mappedFunction);
  361.       var currentRelative = 0.0;
  362.       if(currentValue <= this.midOutput){
  363.          currentRelative = this.minInput + ((currentValue - this.minOutput) / (this.midOutput - this.minOutput)) * (this.midInput - this.minInput);
  364.       } else {
  365.          currentRelative = this.midInput + ((currentValue - this.midOutput) / (this.maxOutput - this.midOutput)) * (this.maxInput - this.midInput);
  366.       }
  367.       if(inputValue > currentRelative - this.maxJump && inputValue < currentRelative + this.maxJump) {
  368.          engine.setValue(group, this.mappedFunction, outputValue);
  369.       }
  370.    } else {
  371.       engine.setValue(group, this.mappedFunction, outputValue);
  372.    }
  373. }
  374.  
  375. //Deck
  376. Deck = function (deckNumber, group) {
  377.    this.deckNumber = deckNumber;
  378.    this.group = group;
  379.    this.Buttons = [];
  380. }
  381. Deck.prototype.setControlValue = Controller.prototype.setControlValue;
  382. Deck.prototype.addButton = Controller.prototype.addButton;
  383.  
  384. // ----------------- END Object definitions ----------------------