/******************************************************************* * Soundless studio. * Author: Ozzy Chiu (ozzy.chc@gmail.com) * Version: 2019-09-04 * * Variables on joyMIDI.userSetting can be modified by users to suit their preferences *******************************************************************/ var joyMIDI = { // User Setting "userSetting": { // Enable the knob(button+jog) function "knobEnable": true, // Enable the beatloop with roll(1) function otherwise toggle(0) "beatLoopRollEnable": true, // Enable the samplers played from cue(1) otherwise from track start(0) "samplerCueModeEnable": true, }, // Channel1 variable "[Channel1]": { btnShift: 0, btnSync: 0, syncTimerID: null, syncLongPress: 0, btnScratch: 0, isScratchPermanent: 0, scratchTimerID: 0, btnKey: 0, btnBeatgrid: 0, btnVolume: 0, btnFilter: { "low": 0, "middle": 0, "high": 0 }, btnFx: { "1": 0, "2": 0 }, connBeatIndicator: {}, }, // Channel2 variable "[Channel2]": { btnShift: 0, btnSync: 0, syncTimerID: null, syncLongPress: 0, btnScratch: 0, isScratchPermanent: 0, scratchTimerID: 0, btnKey: 0, btnBeatgrid: 0, btnVolume: 0, btnFilter: { "low": 0, "middle": 0, "high": 0 }, btnFx: { "1": 0, "2": 0 }, connBeatIndicator: {}, }, }; // Constant var RELEASED = 0, PRESSED = 1, ADJUSTED = 2; //---------------------------- // Constants for scratching //---------------------------- /* Internal jog-wheel */ var forwardFinetune = 0; var backwardFinetune = 0; var intervalPerRev = 64; var rpm = 33 + 1 / 3; var alpha = 1 / 16; var beta = (1 / 12) / 32; var scratchDisableTime = 100; var enableAccVal = true; /* external jog-wheel */ // var forwardFinetune = 0; // var backwardFinetune = 1; // var intervalPerRev = 1000; // var rpm = 33 + 1 / 3; // var alpha = 1.0 / 8; // var beta = alpha / 32; // var scratchDisableTime = 150; // var enableAccVal = false; //---------------------------- // Common variable //---------------------------- var joystickDelay = false; var fsrDelay = false; //==== Init, Shutdown ======================================================== joyMIDI.init = function(id, debug) { joyMIDI['[Channel1]'].connBeatIndicator = engine.makeConnection('[Channel1]', 'beat_active', joyMIDI.onBeatIndicator); joyMIDI['[Channel2]'].connBeatIndicator = engine.makeConnection('[Channel2]', 'beat_active', joyMIDI.onBeatIndicator); } joyMIDI.shutdown = function() { joyMIDI['[Channel1]'].connBeatIndicator.disconnect(); joyMIDI['[Channel2]'].connBeatIndicator.disconnect(); } //==== Callback ============================================================== joyMIDI.onBeatIndicator = function(value, group, control) { var fader = engine.getParameter('[Master]', "crossfader"); if (fader < 0.5) { // Left if (group === '[Channel1]') { midi.sendShortMsg(0x91, 0x00, (value) ? 0x7F : 0x00); } } else { // Right if (group === '[Channel2]') { midi.sendShortMsg(0x92, 0x00, (value) ? 0x7F : 0x00); } } } //==== Button ================================================================ joyMIDI.shiftButton = function(channel, control, value, status, group) { joyMIDI[group].btnShift = (value > 0) ? PRESSED : RELEASED; } joyMIDI.syncButton = function(channel, control, value, status, group) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { if (engine.getValue(group, "sync_enabled") > 0) { engine.setValue(group, "sync_enabled", 0); } else { engine.setValue(group, "sync_enabled", 1); if (joyMIDI[group].syncTimerID !== null) { engine.stopTimer(joyMIDI[group].syncTimerID); } joyMIDI[group].syncLongPress = 0; joyMIDI[group].syncTimerID = engine.beginTimer(1000, "joyMIDI.syncTimerHandler('" + group + "')", true); } } else { engine.setValue(group, "rate", 0); } } else { // release if (!joyMIDI[group].btnShift) { if (joyMIDI[group].syncTimerID !== null) { if (joyMIDI[group].syncLongPress === 0 && engine.getValue(group, "sync_enabled") === 1) { engine.stopTimer(joyMIDI[group].syncTimerID); engine.setValue(group, "sync_enabled", 0); } joyMIDI[group].syncTimerID = null; } } } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnSync = PRESSED; } else { // release if (joyMIDI[group].btnSync != ADJUSTED) { if (!joyMIDI[group].btnShift) { engine.setValue(group, "beatsync", 1); } else { engine.setValue(group, "rate", 0); } } joyMIDI[group].btnSync = RELEASED; } } } joyMIDI.syncTimerHandler = function(group) { joyMIDI[group].syncLongPress = 1; } joyMIDI.cueButton = function(channel, control, value, status, group) { var sft = joyMIDI[group].btnShift; var cmd = sft ? "start" : "cue_default"; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.playButton = function(channel, control, value, status, group) { var sft = joyMIDI[group].btnShift; var cmd = sft ? "reverse" : "play"; if (value > 0) { script.toggleControl(group, cmd); } } joyMIDI.scratchButton = function(channel, control, value, status, group) { if (value > 0) { joyMIDI[group].isScratchPermanent = !joyMIDI[group].isScratchPermanent; } } joyMIDI.keyButton = function(channel, control, value, status, group) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { engine.setValue(group, "sync_key", 1); } else { engine.setValue(group, "reset_key", 1); } } else { // release } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnKey = PRESSED; } else { // release if (joyMIDI[group].btnKey != ADJUSTED) { if (joyMIDI[group].btnShift === RELEASED) { engine.setValue(group, "sync_key", 1); } else { engine.setValue(group, "reset_key", 1); } } joyMIDI[group].btnKey = RELEASED; } } } joyMIDI.beatgridButton = function(channel, control, value, status, group) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { engine.setValue(group, "beats_translate_curpos", 1); engine.setValue(group, "beats_translate_curpos", 0); } else { engine.setValue(group, "beats_translate_match_alignment", 1); engine.setValue(group, "beats_translate_match_alignment", 0); } } else { // release } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnBeatgrid = PRESSED; } else { // release if (joyMIDI[group].btnBeatgrid != ADJUSTED) { if (joyMIDI[group].btnShift === RELEASED) { engine.setValue(group, "beats_translate_curpos", 1); engine.setValue(group, "beats_translate_curpos", 0); } else { engine.setValue(group, "beats_translate_match_alignment", 1); engine.setValue(group, "beats_translate_match_alignment", 0); } } joyMIDI[group].btnBeatgrid = RELEASED; } } } joyMIDI.volumeButton = function(channel, control, value, status, group) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { engine.setValue(group, "volume", 1.0); } } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnVolume = PRESSED; } else { // release if (joyMIDI[group].btnVolume != ADJUSTED) { if (joyMIDI[group].btnShift === RELEASED) { engine.setValue(group, "volume", 1.0); } } joyMIDI[group].btnVolume = RELEASED; } } } joyMIDI.filterLowButton = function(channel, control, value, status, group) { joyMIDI.filterButton(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "button_parameter1", "low"); } joyMIDI.filterMiddleButton = function(channel, control, value, status, group) { joyMIDI.filterButton(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "button_parameter2", "middle"); } joyMIDI.filterHighButton = function(channel, control, value, status, group) { joyMIDI.filterButton(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "button_parameter3", "high"); } joyMIDI.filterButton = function(channel, control, value, status, group, group2, param, select) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { script.toggleControl(group2, param); } } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnFilter[select] = PRESSED; } else { // release if (joyMIDI[group].btnFilter[select] != ADJUSTED) { if (joyMIDI[group].btnShift === RELEASED) { script.toggleControl(group2, param); } } joyMIDI[group].btnFilter[select] = RELEASED; } } } joyMIDI.hotcue1Button = function(channel, control, value, status, group) { joyMIDI.hotcueButton(channel, control, value, status, group, [ "hotcue_1_activate", "hotcue_1_clear" ]); } joyMIDI.hotcue2Button = function(channel, control, value, status, group) { joyMIDI.hotcueButton(channel, control, value, status, group, [ "hotcue_2_activate", "hotcue_2_clear" ]); } joyMIDI.hotcue3Button = function(channel, control, value, status, group) { joyMIDI.hotcueButton(channel, control, value, status, group, [ "hotcue_3_activate", "hotcue_3_clear" ]); } joyMIDI.hotcue4Button = function(channel, control, value, status, group) { joyMIDI.hotcueButton(channel, control, value, status, group, [ "hotcue_4_activate", "hotcue_4_clear" ]); } joyMIDI.hotcueButton = function(channel, control, value, status, group, commands) { var sft = joyMIDI[group].btnShift; var cmd = commands[sft]; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.reloopToggleButton = function(channel, control, value, status, group) { var usr = joyMIDI.userSetting.beatLoopRollEnable; var sft = joyMIDI[group].btnShift; var cmd = sft ? (usr?"beatlooproll_activate":"beatloop_activate") : "reloop_toggle"; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.loopInButton = function(channel, control, value, status, group) { var sft = joyMIDI[group].btnShift; var cmd = sft ? "loop_halve" : "loop_in"; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.loopOutButton = function(channel, control, value, status, group) { var sft = joyMIDI[group].btnShift; var cmd = sft ? "loop_double" : "loop_out"; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.loop_0p125_Button = function(channel, control, value, status, group) { joyMIDI.loop_x_Button(channel, control, value, status, group, [ "beatloop_0.125_toggle", "beatloop_2_toggle", "beatlooproll_0.125_activate", "beatlooproll_2_activate" ]); } joyMIDI.loop_0p25_Button = function(channel, control, value, status, group) { joyMIDI.loop_x_Button(channel, control, value, status, group, [ "beatloop_0.25_toggle", "beatloop_4_toggle", "beatlooproll_0.25_activate", "beatlooproll_4_activate" ]); } joyMIDI.loop_0p5_Button = function(channel, control, value, status, group) { joyMIDI.loop_x_Button(channel, control, value, status, group, [ "beatloop_0.5_toggle", "beatloop_8_toggle", "beatlooproll_0.5_activate", "beatlooproll_8_activate" ]); } joyMIDI.loop_1_Button = function(channel, control, value, status, group) { joyMIDI.loop_x_Button(channel, control, value, status, group, [ "beatloop_1_toggle", "beatloop_16_toggle", "beatlooproll_1_activate", "beatlooproll_16_activate" ]); } joyMIDI.loop_x_Button = function(channel, control, value, status, group, commands) { var usr = joyMIDI.userSetting.beatLoopRollEnable; var sft = joyMIDI[group].btnShift; var cmd = commands[ sft + (usr?2:0) ]; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.loadButton = function(channel, control, value, status, group) { var sft = joyMIDI[group].btnShift; var cmd = sft ? "eject" : "LoadSelectedTrack"; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.sampler1Button = function(channel, control, value, status, group) { joyMIDI.samplerButton(channel, control, value, status, group, 1); } joyMIDI.sampler2Button = function(channel, control, value, status, group) { joyMIDI.samplerButton(channel, control, value, status, group, 2); } joyMIDI.sampler3Button = function(channel, control, value, status, group) { joyMIDI.samplerButton(channel, control, value, status, group, 3); } joyMIDI.sampler4Button = function(channel, control, value, status, group) { joyMIDI.samplerButton(channel, control, value, status, group, 4); } joyMIDI.samplerButton = function(channel, control, value, status, group, number) { var commands = ["start_play", "start_stop", "cue_gotoandplay", "cue_default"]; var usr = joyMIDI.userSetting.samplerCueModeEnable; var sft = joyMIDI["[Channel"+ ((number > 2) ? 2 : 1) +"]"].btnShift; var cmd = commands[sft + (usr?2:0)]; var val = (value > 0) ? 1 : 0; engine.setValue(group, cmd, val); } joyMIDI.fx1Button = function(channel, control, value, status, group) { joyMIDI.fxButton(channel, control, value, status, group, "[EffectRack1_EffectUnit1]", "group_"+group+"_enable", 1); } joyMIDI.fx2Button = function(channel, control, value, status, group) { joyMIDI.fxButton(channel, control, value, status, group, "[EffectRack1_EffectUnit2]", "group_"+group+"_enable", 2); } joyMIDI.fxButton = function(channel, control, value, status, group, group2, param, select) { if (!joyMIDI.userSetting.knobEnable) { // knob disabled if (value > 0) { // press if (!joyMIDI[group].btnShift) { script.toggleControl(group2, param); } } } else { // knob enabled if (value > 0) { // press joyMIDI[group].btnFx[select] = PRESSED; } else { // release if (joyMIDI[group].btnFx[select] != ADJUSTED) { if (joyMIDI[group].btnShift === RELEASED) { script.toggleControl(group2, param); } } joyMIDI[group].btnFx[select] = RELEASED; } } } //==== Wheel ============================================================ joyMIDI.wheel = function(channel, control, value, status, group) { if (joyMIDI[group].btnSync >= PRESSED) { joyMIDI.wheelSync(channel, control, value, status, group); joyMIDI[group].btnSync = ADJUSTED; } else if (joyMIDI[group].btnKey >= PRESSED) { joyMIDI.wheelKey(channel, control, value, status, group); joyMIDI[group].btnKey = ADJUSTED; } else if (joyMIDI[group].btnBeatgrid >= PRESSED) { joyMIDI.wheelBeatgrid(channel, control, value, status, group); joyMIDI[group].btnBeatgrid = ADJUSTED; } else if (joyMIDI[group].btnVolume >= PRESSED) { joyMIDI.wheelVolume(channel, control, value, status, group); joyMIDI[group].btnVolume = ADJUSTED; } else if (joyMIDI[group].btnFilter["low"] >= PRESSED) { joyMIDI.wheelFilterLow(channel, control, value, status, group); joyMIDI[group].btnFilter["low"] = ADJUSTED; } else if (joyMIDI[group].btnFilter["middle"] >= PRESSED) { joyMIDI.wheelFilterMiddle(channel, control, value, status, group); joyMIDI[group].btnFilter["middle"] = ADJUSTED; } else if (joyMIDI[group].btnFilter["high"] >= PRESSED) { joyMIDI.wheelFilterHigh(channel, control, value, status, group); joyMIDI[group].btnFilter["high"] = ADJUSTED; } else if (joyMIDI[group].btnFx["1"] >= PRESSED) { joyMIDI.wheelFx1(channel, control, value, status, group); joyMIDI[group].btnFx["1"] = ADJUSTED; } else if (joyMIDI[group].btnFx["2"] >= PRESSED) { joyMIDI.wheelFx2(channel, control, value, status, group); joyMIDI[group].btnFx["2"] = ADJUSTED; } else if (joyMIDI[group].btnFx["3"] >= PRESSED) { joyMIDI.wheelFx3(channel, control, value, status, group); joyMIDI[group].btnFx["3"] = ADJUSTED; } else if (joyMIDI[group].isScratchPermanent) { joyMIDI.wheelScratch(channel, control, value, status, group); } else { joyMIDI.wheelPitchBend(channel, control, value, status, group); } } joyMIDI.wheelPitchBend = function(channel, control, value, status, group) { var newValue = joyMIDI.helperAccel(value - 64); engine.setValue(group, 'jog', newValue); print(group + "jog=" + newValue); } joyMIDI.wheelScratch = function(channel, control, value, status, group) { var deck = script.deckFromGroup(group); var newValue = joyMIDI.helperAccel(value - 64); if (newValue > 0) newValue = newValue + forwardFinetune; else newValue = newValue - backwardFinetune; if (joyMIDI[group].scratchTimerID) { engine.stopTimer(joyMIDI[group].scratchTimerID); } // if (!engine.isScratching(deck)) { engine.scratchEnable(deck, intervalPerRev, rpm, alpha, beta); // } engine.scratchTick(deck, newValue); joyMIDI[group].scratchTimerID = engine.beginTimer(scratchDisableTime, "joyMIDI.ScratchTimerHandler('" +deck+ "','" +group+ "')", true); } joyMIDI.ScratchTimerHandler = function(deck, group){ engine.scratchDisable(deck); joyMIDI[group].scratchTimerID = 0; } joyMIDI.wheelSync = function(channel, control, value, status, group) { var delta = joyMIDI.helperAccel(value - 64) * 0.01; var oldValue = engine.getValue(group, "rate"); var newValue = oldValue + delta; newValue = joyMIDI.helperLimit(newValue, 1.0, -1.0); engine.setValue(group, "rate", newValue); print(group + "rate=" + newValue); } joyMIDI.wheelKey = function(channel, control, value, status, group) { var delta = /*joyMIDI.helperAccel*/(value - 64) * 0.2; var oldValue = engine.getValue(group, "pitch"); var newValue = oldValue + delta; newValue = joyMIDI.helperLimit(newValue, 6.0, -6.0); engine.setValue(group, "pitch", newValue); print(group + "pitch=" + newValue); } joyMIDI.wheelBeatgrid = function(channel, control, value, status, group) { var delta = joyMIDI.helperAccel(value - 64); var count = Math.abs(delta); for (var i=0; i 0) { engine.setValue(group, "beats_translate_earlier", 1); print(group + "beats_translate_earlier"); } else { engine.setValue(group, "beats_translate_later", 1); print(group + "beats_translate_later"); } } } joyMIDI.wheelVolume = function(channel, control, value, status, group) { var delta = joyMIDI.helperAccel(value - 64) * 0.01; var oldValue = engine.getValue(group, "volume"); var newValue = oldValue + delta; newValue = joyMIDI.helperLimit(newValue, 1.0, 0); engine.setValue(group, "volume", newValue); print(group + "volume=" + newValue); } joyMIDI.wheelFilterLow = function(channel, control, value, status, group) { joyMIDI.wheelFilter(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "parameter1", "low"); } joyMIDI.wheelFilterMiddle = function(channel, control, value, status, group) { joyMIDI.wheelFilter(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "parameter2", "middle"); } joyMIDI.wheelFilterHigh = function(channel, control, value, status, group) { joyMIDI.wheelFilter(channel, control, value, status, group, "[EqualizerRack1_" + group + "_Effect1]", "parameter3", "high"); } joyMIDI.wheelFilter = function(channel, control, value, status, group, group2, param, select) { var delta = joyMIDI.helperAccel(value - 64) * 0.01; var oldValue = engine.getParameter(group2, param); var newValue = oldValue + delta; newValue = joyMIDI.helperLimit(newValue, 1.0, 0); engine.setParameter(group2, param, newValue); print(group + "EQ_" + select + "=" + newValue); } joyMIDI.wheelFx1 = function(channel, control, value, status, group) { if (group === "[Channel1]") { joyMIDI.wheelFx(channel, control, value, status, group, "[EffectRack1_EffectUnit1]", "mix", 1); } else { joyMIDI.wheelFx(channel, control, value, status, group, "[EffectRack1_EffectUnit2]", "mix", 1); } } joyMIDI.wheelFx2 = function(channel, control, value, status, group) { if (group === "[Channel1]") { joyMIDI.wheelFx(channel, control, value, status, group, "[EffectRack1_EffectUnit1]", "super1", 2); } else { joyMIDI.wheelFx(channel, control, value, status, group, "[EffectRack1_EffectUnit2]", "super1", 2); } } joyMIDI.wheelFx = function(channel, control, value, status, group, group2, param, select) { var delta = joyMIDI.helperAccel(value - 64) * 0.01; var oldValue = engine.getParameter(group2, param); var newValue = oldValue + delta; newValue = joyMIDI.helperLimit(newValue, 1.0, 0); engine.setParameter(group2, param, newValue); } //==== Jojstick ============================================================ joyMIDI.joystick = function(channel, control, value, status, group) { if (joystickDelay == false) { /* Delay a while */ joystickDelay = true; engine.beginTimer(300, function() { joystickDelay = false; }, true); var isShift = joyMIDI['[Channel1]'].btnShift == true || joyMIDI['[Channel2]'].btnShift == true; if (!joyMIDI['[Channel1]'].btnShift) { /* Joystick only */ if (control == 0x10) { engine.setValue("[Library]", "MoveLeft", true); } else if (control == 0x11) { engine.setValue("[Library]", "MoveRight", true); } else if (control == 0x12) { engine.setValue("[Library]", "MoveDown", true); } else if (control == 0x13) { engine.setValue("[Library]", "MoveUp", true); } } else { /* Joystick + shift */ if (control == 0x10) { // Do nothing } else if (control == 0x11) { // Do nothing } else if (control == 0x12) { engine.setValue("[Library]", "ScrollDown", true); } else if (control == 0x13) { engine.setValue("[Library]", "ScrollUp", true); } } } } //==== FSR ============================================================ joyMIDI.fsr = function(channel, control, value, status, group) { if (fsrDelay == false) { /* Delay a while */ fsrDelay = true; engine.beginTimer(300, function() { fsrDelay = false; }, true); var isShift = joyMIDI['[Channel1]'].btnShift == true || joyMIDI['[Channel2]'].btnShift == true; if (! isShift) { /* FSR only */ engine.setValue("[Library]", "MoveFocusForward", true); } else { /* FSR + shift */ engine.setValue("[Library]", "MoveFocusBackward", true); } } } //==== Helper ============================================================= joyMIDI.helperLimit = function(input, max, min) { input = input > max ? max : input; input = input < min ? min : input; return input; } joyMIDI.helperAccel = function(input) { var acc = 0; if (enableAccVal == true) { acc = Math.pow(Math.abs(input), 2); acc = (input > 0) ? acc : -acc; } else { acc = input; } return acc; }