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 >
Wrap
Text File
|
2011-02-04
|
16KB
|
384 lines
// Functions common to all controllers go in this file
nop = function () {} // Only here so you don't get a syntax error on load if the file was otherwise empty
// ----------------- Prototype enhancements ---------------------
// Returns an ASCII byte array for the string
String.prototype.toInt = function() {
var a = new Array();
for (var i = 0; i < this.length; i++) {
a[i] = this.charCodeAt(i);
}
return a;
}
// ----------------- Function overloads ---------------------
// Causes script print() calls to appear in the log file as well
print = function(string) {
engine.log(string);
}
// ----------------- Generic functions ---------------------
function secondstominutes(secs)
{
var m = (secs / 60) | 0;
return (m < 10 ? "0" + m : m)
+ ":"
+ ( ( secs %= 60 ) < 10 ? "0" + secs : secs);
}
function msecondstominutes(msecs)
{
var m = (msecs / 60000) | 0;
msecs %= 60000;
var secs = (msecs / 1000) | 0;
msecs %= 1000;
msecs = Math.round(msecs * 100 / 1000);
if (msecs==100) msecs=99;
// print("secs="+secs+", msecs="+msecs);
return (m < 10 ? "0" + m : m)
+ ":"
+ ( secs < 10 ? "0" + secs : secs )
+ "."
+ ( msecs < 10 ? "0" + msecs : msecs);
}
function script() {}
script.debug = function (channel, control, value, status, group) {
print("Script.Debug --- channel: " + channel.toString(16) +
" control: " + control.toString(16) + " value: " + value.toString(16) +
" status: " + status.toString(16) + " group: " + group);
}
// Used to control a generic Mixxx control setting (low..high) from an absolute control (0..127)
script.absoluteSlider = function (group, key, value, low, high) {
if (value==127) engine.setValue(group, key, high);
else engine.setValue(group, key, (((high - low) / 127) * value) + low);
}
// Returns a value for a non-linear Mixxx control (like EQs: 0..1..4) from an absolute control (0..127)
script.absoluteNonLin = function (value, low, mid, high) {
if (value<=64) return value/(64/(mid-low));
else return 1+(value-63)/(64/(high-mid));
}
// DEPRECATED
// Used to control an EQ setting (0..1..4) from an absolute control (0..127)
script.absoluteEQ = function (group, key, value) {
if (value<=64) engine.setValue(group, key, value/64);
else engine.setValue(group, key, 1+(value-63)/(21+1/3));
print ("MIDI Script: script.absoluteEQ is deprecated. " +
"Use script.absoluteNonLin(value,0,1,4) instead and set the " +
"MixxxControl to its return value.");
}
/* -------- ------------------------------------------------------
script.Pitch
Purpose: Takes the value from a little-endian 14-bit MIDI pitch
wheel message and returns the value for a "rate" (pitch
slider) Mixxx control
Input: Least significant byte, most sig. byte, MIDI status byte
Output: Value for a "rate" control, or false if the input MIDI
message was not a Pitch message (0xE#)
-------- ------------------------------------------------------ */
script.pitch = function (LSB, MSB, status) {
if ((status & 0xF0) != 0xE0) { // Mask the upper nybble so we can check the opcode regardless of the channel
print("Script.Pitch: Error, not a MIDI pitch message: "+status);
return false;
}
var value = (MSB << 7) | LSB; // Construct the 14-bit number
// Range is 0x0000..0x3FFF center @ 0x2000, i.e. 0..16383 center @ 8192
var rate = (value-8192)/8191;
// print("Script.Pitch: MSB="+MSB+", LSB="+LSB+", value="+value+", rate="+rate);
return rate;
}
// bpm - Used for tapping the desired BPM for a deck
function bpm() {}
bpm.tapTime = 0.0;
bpm.tap = []; // Tap sample values
/* -------- ------------------------------------------------------
bpm.tapButton
Purpose: Sets the bpm of the track on a deck by tapping the beats.
This only works if the track's original BPM value is correct.
Call this each time the tap button is pressed.
Input: Mixxx deck to adjust
Output: -
-------- ------------------------------------------------------ */
bpm.tapButton = function(deck) {
var now = new Date()/1000; // Current time in seconds
var tapDelta = now - bpm.tapTime;
bpm.tapTime=now;
if (tapDelta>2.0) { // reset if longer than two seconds between taps
bpm.tap=[];
return;
}
bpm.tap.push(60/tapDelta);
if (bpm.tap.length>8) bpm.tap.shift(); // Keep the last 8 samples for averaging
var sum = 0;
for (i=0; i<bpm.tap.length; i++) {
sum += bpm.tap[i];
}
var average = sum/bpm.tap.length;
var fRateScale = average/engine.getValue("[Channel"+deck+"]","bpm");
// Adjust the rate:
fRateScale = (fRateScale-1.)/engine.getValue("[Channel"+deck+"]","rateRange");
engine.setValue("[Channel"+deck+"]","rate",fRateScale * engine.getValue("[Channel"+deck+"]","rate_dir"));
// print("Script: BPM="+average);
}
// ----------------- DEPRECATED Scratching functions ---------------------
function scratch() {}
// Allows for smooth scratching with MIDI controllers
// See full details here: http://mixxx.org/wiki/doku.php/midi_scripting#available_common_functions
// ---------- Variables ----------
scratch.variables = { "time":0.0, "trackPos":0.0, "initialTrackPos":-1.0,
"initialControlValue":0, "scratch":0.0,
"prevControlValue":0, "wrapCount":0 };
// ---------- Functions ----------
/* -------- ------------------------------------------------------
scratch.Enable
Purpose: Sets up initial variables for the scratch.<control>
functions below. Called when a modifier button is pressed
& held.
Input: Currently-controlled Mixxx deck
Output: -
-------- ------------------------------------------------------ */
scratch.enable = function (currentDeck,newBehavior) {
// Store scratch info at the point it was touched
// Current position in seconds:
scratch.variables["initialTrackPos"] = scratch.variables["trackPos"] = engine.getValue("[Channel"+currentDeck+"]","visual_playposition") * engine.getValue("[Channel"+currentDeck+"]","duration");
scratch.variables["time"] = new Date()/1000; // Current time in seconds
if (newBehavior) engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
// If the deck is playing, slow it to a stop
if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
// TODO: ramp down
}
// print("MIDI Script: Scratch initial: time=" + scratch.variables["time"] + "s, track=" + scratch.variables["trackPos"] + "s");
return;
}
/* -------- ------------------------------------------------------
scratch.Disable
Purpose: Clears varaibles used by the scratch.<control> functions
below. Called when the modifier button is released.
Input: Currently-controlled Mixxx deck
Output: -
-------- ------------------------------------------------------ */
scratch.disable = function (currentDeck) {
// If the deck is playing, ramp it up to the play speed
if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
//TODO: ramp up
}
// Reset the triggers
scratch.variables["trackPos"] = 0.0;
scratch.variables["initialTrackPos"] = -1.0;
scratch.variables["initialControlValue"] = 0;
scratch.variables["prevControlValue"] = 0; // for wheel
scratch.variables["wrapCount"] = 0; // for wheel
scratch.variables["time"] = 0.0;
scratch.variables["scratch"] = 0.0;
// print("MIDI Script: Scratch values CLEARED");
engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 0); // disable scratching
engine.setValue("[Channel"+currentDeck+"]","scratch2",0);
engine.setValue("[Channel"+currentDeck+"]","scratch",0); // Deprecated
}
/* -------- ------------------------------------------------------
scratch.Slider
Purpose: Uses an alpha-beta filter to make scratching with a
slider (0..127) sound good, called each time there's a
new slider value
Input: Currently-controlled Mixxx deck, value of the slider,
revolution time of the imaginary record (typically 1.8s,
for a 12" disc @ 33+1/3 RPM,) alpha & beta coefficients
Output: New value for the "scratch" control
-------- ------------------------------------------------------ */
scratch.slider = function (currentDeck, sliderValue, revtime, alpha, beta) {
// Skip if the track start position hasn't been set yet
if (scratch.variables["initialTrackPos"] == -1.0) return 0;
// If the slider start value hasn't been set yet, set it
if (scratch.variables["initialControlValue"] == 0) {
scratch.variables["initialControlValue"] = sliderValue;
if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
scratch.variables["scratch"] = 1;
engine.setValue("[Channel"+currentDeck+"]","scratch2", 1);
}
// engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
// print("Initial slider="+scratch.variables["initialControlValue"]);
}
return scratch.filter(currentDeck, sliderValue, revtime, alpha, beta);
}
/* -------- ------------------------------------------------------
scratch.wheel
Purpose: Uses an alpha-beta filter to make scratching with a
wheel (0..127 with wrap) sound good, called each time
there's a new wheel value
Input: Currently-controlled Mixxx deck, value of the wheel,
revolution time of the imaginary record (typically 1.8s,
for a 12" disc @ 33+1/3 RPM,) alpha & beta coefficients
Output: New value for the "scratch" control
-------- ------------------------------------------------------ */
scratch.wheel = function (currentDeck, wheelValue, revtime, alpha, beta) {
// Skip if the track start position hasn't been set yet
if (scratch.variables["initialTrackPos"] == -1.0) return 0;
// If the wheel start value hasn't been set yet, set it
if (scratch.variables["initialControlValue"] == 0) {
scratch.variables["initialControlValue"] = scratch.variables["prevControlValue"] = wheelValue;
if (engine.getValue("[Channel"+currentDeck+"]","play") > 0) {
scratch.variables["scratch"] = 1;
engine.setValue("[Channel"+currentDeck+"]","scratch2", 1);
}
// engine.setValue("[Channel"+currentDeck+"]","scratch2_enable", 1);
// print("Initial wheel="+scratch.variables["initialControlValue"]);
}
// Take wrap around into account
if (wheelValue>=0 && wheelValue<10 && scratch.variables["prevControlValue"]>117 && scratch.variables["prevControlValue"]<=127) scratch.variables["wrapCount"]+=1;
if (wheelValue>117 && wheelValue<=127 && scratch.variables["prevControlValue"]>=0 && scratch.variables["prevControlValue"]<10) scratch.variables["wrapCount"]-=1;
// From radiomark: change = (new - old + 192) % 128 - 64
scratch.variables["prevControlValue"]=wheelValue;
wheelValue += scratch.variables["wrapCount"]*128;
return scratch.filter(currentDeck, wheelValue, revtime, alpha, beta);
}
// The actual alpha-beta filter
scratch.filter = function (currentDeck, controlValue, revtime, alpha, beta) {
// ------------- Thanks to Mark Hills of Xwax (http://www.xwax.co.uk) for the info for below ------------------------
// ideal position = (initial_p + (y - x) / 128 * 1.8)
var ideal_p = scratch.variables["initialTrackPos"] + (controlValue - scratch.variables["initialControlValue"]) / 128 * revtime;
var currentTrackPos = engine.getValue("[Channel"+currentDeck+"]","visual_playposition") * engine.getValue("[Channel"+currentDeck+"]","duration");
var newTime = new Date()/1000;
var dt = newTime - scratch.variables["time"];
scratch.variables["time"] = newTime;
// print("dt="+dt);
// predicted_p = p + dt * pitch; // Where "pitch" = 1.0 for regular speed, i.e. the "scratch" control.
var predicted_p = currentTrackPos + dt * scratch.variables["scratch"];
// rx = where_finger_corresponds_to - predicted_p;
var rx = ideal_p - predicted_p;
// p += rx * ALPHA;
// scratch.variables["trackPos"] += rx * alpha; // Don't need this result so why waste the CPU time?
// v += rx * BETA / dt;
// scratch.variables["scratch"] += rx * beta / dt; // This doesn't work
scratch.variables["scratch"] = rx * beta;
// print("MIDI Script: Ideal position="+ideal_p+", Predicted position="+predicted_p + ", New scratch val=" + scratch.variables["scratch"]);
// var newPos = scratch.variables["trackPos"]/engine.getValue("[Channel"+currentDeck+"]","duration");
// engine.setValue("[Channel"+currentDeck+"]","visual_playposition",newPos);
// engine.setValue("[Channel"+currentDeck+"]","scratch",scratch.variables["scratch"]);
return scratch.variables["scratch"];
}
// ----------------- END Scratching functions --------------------
// ----------------- Object definitions --------------------------
ButtonState = {"released":0x00, "pressed":0x7F};
LedState = {"off": 0x00, "on": 0x7F};
//Controller
function Controller () {
this.group = "[Master]";
this.Controls = [];
this.Buttons = [];
};
Controller.prototype.addButton = function(buttonName, button, eventHandler) {
if(eventHandler) {
var executionEnvironment = this;
function handler(value) {
button.state = value;
executionEnvironment[eventHandler](value);
}
button.handler = handler;
}
this.Buttons[buttonName] = button;
}
Controller.prototype.setControlValue = function(control, value) {
this.Controls[control].setValue(this.group, value);
}
//Button
function Button(controlId) {
this.controlId = controlId;
this.state = ButtonState.released;
}
Button.prototype.handleEvent = function(value) {
this.handler(value);
};
//Control
function Control(mappedFunction, softMode) {
this.minInput = 0;
this.midInput = 0x3F;
this.maxInput = 0x7F;
this.minOutput = -1.0;
this.midOutput = 0.0;
this.maxOutput = 1.0;
this.mappedFunction = mappedFunction;
this.softMode = softMode;
this.maxJump = 10;
}
Control.prototype.setValue = function(group, inputValue){
var outputValue = 0;
if(inputValue <= this.midInput){
outputValue = this.minOutput + ((inputValue - this.minInput) / (this.midInput - this.minInput)) * (this.midOutput - this.minOutput);
} else {
outputValue = this.midOutput + ((inputValue - this.midInput) / (this.maxInput - this.midInput)) * (this.maxOutput - this.midOutput);
}
if(this.softMode){
var currentValue = engine.getValue(group, this.mappedFunction);
var currentRelative = 0.0;
if(currentValue <= this.midOutput){
currentRelative = this.minInput + ((currentValue - this.minOutput) / (this.midOutput - this.minOutput)) * (this.midInput - this.minInput);
} else {
currentRelative = this.midInput + ((currentValue - this.midOutput) / (this.maxOutput - this.midOutput)) * (this.maxInput - this.midInput);
}
if(inputValue > currentRelative - this.maxJump && inputValue < currentRelative + this.maxJump) {
engine.setValue(group, this.mappedFunction, outputValue);
}
} else {
engine.setValue(group, this.mappedFunction, outputValue);
}
}
//Deck
Deck = function (deckNumber, group) {
this.deckNumber = deckNumber;
this.group = group;
this.Buttons = [];
}
Deck.prototype.setControlValue = Controller.prototype.setControlValue;
Deck.prototype.addButton = Controller.prototype.addButton;
// ----------------- END Object definitions ----------------------