-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Mirco Lukas
committed
Jun 22, 2018
1 parent
5c965bc
commit 374c73c
Showing
9 changed files
with
604 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Installation: | ||
|
||
Move all JavaScript files into the controller folder of Bitwig like i.e. "Bitwig Studio/Controller Scripts/Steinberg/CC121". | ||
The files are: | ||
|
||
- SteinbergCC121.control.js (starting point of the script) | ||
- SteinbergCC121.ai.js (AI knob, zoom, jog) | ||
- SteinbergCC121.channel.js (channel functions) | ||
- SteinbergCC121.device.js (device functions) | ||
- SteinbergCC121.function.js (function buttons, shortcuts) | ||
- SteinbergCC121.transport.js (transport functions) | ||
|
||
When using Linux, the script should automaticly find and subscribe the controller. It can be necessary for other operating systems to manually choose the controller script and select the right MIDI-IN and MIDI-OUT devices in the Bitwig settings. | ||
|
||
Information: | ||
|
||
Script was tested in Linux Mint 18 64-bit with Bitwig 2 and uses API version 5. There is an explanation what each knob and button does (assignment.pdf). | ||
|
||
Known issues: | ||
|
||
When changing a device parameter on parameter page 1, the LEDs of the parameter buttons (parameters 5-8 on page 2) light up like the parameters 5-8 on page 1. This seems inevitable with the current API due to the face that one can only set a callback for a parameter of the parameter view. When the view changes its page, the callback is activated. Moveover, increasing the amount of parameters per page seems not to work for values greater than 8, so that page scrolling is necessary. | ||
However, the button function itself works fine - it is only the LEDs. | ||
|
||
About: | ||
|
||
Author: Philipp Winniewski | ||
Date: 22/06/2018 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Keep selected AI knob function | ||
var isLockActive = false; | ||
|
||
// Factor to speed up scrolling by with job button | ||
var JOGFACTOR_POSITION = 4; | ||
|
||
function initAi() { | ||
// Creating views | ||
application = host.createApplication(); | ||
transport = host.createTransport(); | ||
|
||
return onMidiAi; | ||
} | ||
|
||
function onMidiAi(status, data1, data2) { | ||
// Check if message is MIDI CC from AI knob | ||
if (isChannelController(status) && data1 == CC.AIKNOB) { | ||
var knob_value = midiCcValueToInteger(data2); | ||
if (isLockActive) { | ||
if (knob_value < 0) { | ||
application.zoomOut(); | ||
} else { | ||
application.zoomIn(); | ||
} | ||
} else { | ||
if (isJogActive) { | ||
knob_value = JOGFACTOR_POSITION * knob_value; | ||
} | ||
transport.incPosition(knob_value, true); | ||
} | ||
} else if (isNoteOn(status) && data2 > 0) { | ||
switch (data1) { | ||
case NOTE.LOCK: | ||
isLockActive = !isLockActive; | ||
if (isLockActive) { | ||
sendMidi(144, NOTE.LOCK, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.LOCK, LIGHT_OFF); | ||
} | ||
break; | ||
case NOTE.JOG: | ||
isJogActive = !isJogActive; | ||
if (isJogActive) { | ||
sendMidi(144, NOTE.JOG, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.JOG, LIGHT_OFF); | ||
} | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Factor to speed up panning by with job button | ||
var JOGFACTOR_PANNING = 4; | ||
|
||
function initChannel() { | ||
// Creating views | ||
trackBank = host.createMainTrackBank(1, 1, 1); | ||
arrangerCursorTrack = host.createArrangerCursorTrack(1, 1); | ||
trackBank.followCursorTrack(arrangerCursorTrack); | ||
transport = host.createTransport(); | ||
application = host.createApplication(); | ||
|
||
// Publishing observers | ||
trackBank.getChannel(0).mute().addValueObserver(function(isMuted) { | ||
if (isMuted) { | ||
sendMidi(144, NOTE.MUTE, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.MUTE, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
trackBank.getChannel(0).solo().addValueObserver(function(isSolo) { | ||
if (isSolo) { | ||
sendMidi(144, NOTE.SOLO, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.SOLO, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
transport.isArrangerAutomationWriteEnabled().addValueObserver(function(isAutomationWrite) { | ||
if (isAutomationWrite) { | ||
sendMidi(144, NOTE.AUTOMATIONWRITE, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.AUTOMATIONWRITE, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
transport.isAutomationOverrideActive().addValueObserver(function(isAutomationOverride) { | ||
if (isAutomationOverride) { | ||
sendMidi(144, NOTE.AUTOMATIONREAD, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.AUTOMATIONREAD, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
trackBank.getTrack(0).arm().addValueObserver(function(isArmed) { | ||
if (isArmed) { | ||
sendMidi(144, NOTE.RECORDARM, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.RECORDARM, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
trackBank.getTrack(0).monitor().addValueObserver(function(isMonitored) { | ||
if (isMonitored) { | ||
sendMidi(144, NOTE.INPUTMONITOR, LIGHT_ON); | ||
} else { | ||
sendMidi(144, NOTE.INPUTMONITOR, LIGHT_OFF); | ||
} | ||
}); | ||
|
||
trackBank.getChannel(0).volume().value().addValueObserver(function(volumeValue) { | ||
var LSB = (volumeValue * 16383) % 128; | ||
var MSB = (volumeValue * 16383) / 128; | ||
sendMidi(224, LSB, MSB); | ||
}); | ||
|
||
return onMidiChannel; | ||
} | ||
|
||
function onMidiChannel(status, data1, data2) { | ||
// Check if message is MIDI CC | ||
if (isChannelController(status)) { | ||
var changeValue = midiCcValueToInteger(data2); | ||
if (isJogActive) | ||
changeValue = JOGFACTOR_PANNING * changeValue; | ||
switch (data1) { | ||
case CC.PAN: | ||
trackBank.getChannel(0).pan().value().inc(changeValue, ENCODER_RESOLUTION); | ||
break; | ||
} | ||
} else if (isNoteOn(status) && data2 > 0) { | ||
switch (data1) { | ||
case NOTE.CHANNELSELECTRIGHT: | ||
arrangerCursorTrack.selectNext(); | ||
break; | ||
case NOTE.CHANNELSELECTLEFT: | ||
arrangerCursorTrack.selectPrevious(); | ||
break; | ||
case NOTE.MUTE: | ||
trackBank.getChannel(0).mute().toggle(); | ||
break; | ||
case NOTE.SOLO: | ||
trackBank.getChannel(0).solo().toggle(); | ||
break; | ||
case NOTE.AUTOMATIONWRITE: | ||
transport.isArrangerAutomationWriteEnabled().toggle(); | ||
break; | ||
case NOTE.AUTOMATIONREAD: | ||
transport.resetAutomationOverrides(); | ||
break; | ||
case NOTE.RECORDARM: | ||
trackBank.getTrack(0).arm().toggle(); | ||
break; | ||
case NOTE.INPUTMONITOR: | ||
trackBank.getTrack(0).monitor().toggle(); | ||
break; | ||
case NOTE.EBUTTON: | ||
application.toggleInspector(); | ||
break; | ||
case NOTE.OPENVSTI: | ||
application.toggleDevices(); | ||
break; | ||
} | ||
} else if (status == 224) { | ||
trackBank.getChannel(0).volume().value().set(data2 * 128 + data1, 16383); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Note: blinking "READY LED" can not be avoided, CC121 needs F043103E150001F7 every second... | ||
loadAPI(5); | ||
load("SteinbergCC121.transport.js"); | ||
load("SteinbergCC121.function.js"); | ||
load("SteinbergCC121.ai.js"); | ||
load("SteinbergCC121.channel.js"); | ||
load("SteinbergCC121.device.js"); | ||
|
||
// Constants and magic numbers | ||
|
||
// Encoder resolution in steps | ||
var ENCODER_RESOLUTION = 256; | ||
|
||
// Controller does not send absolut values but relative changes | ||
var ENCODER_POS_LO = 1; // + 1 | ||
var ENCODER_POS_HI = 15; // + 15 | ||
var ENCODER_NEG_LO = 65; // - 1 | ||
var ENCODER_NEG_HI = 79; // - 15 | ||
|
||
// Data values for button LEDs | ||
var LIGHT_ON = 127; | ||
var LIGHT_OFF = 0; | ||
|
||
// Midi note number for buttons | ||
var NOTE = { | ||
RECORDARM: 0x00, | ||
SOLO: 0x08, | ||
MUTE: 0x10, | ||
CHANNELSELECTLEFT: 0x30, | ||
CHANNELSELECTRIGHT: 0x31, | ||
EBUTTON: 0x33, | ||
FUNCTION1: 0x36, | ||
FUNCTION2: 0x37, | ||
FUNCTION3: 0x38, | ||
FUNCTION4: 0x39, | ||
VALUEENCODERBUTTON: 0x3A, | ||
FOOTSWITCH: 0x3B, | ||
AUTOMATIONREAD: 0x4A, | ||
AUTOMATIONWRITE: 0x4B, | ||
LOOP: 0x56, | ||
TOSTART: 0x58, | ||
TOEND: 0x5A, | ||
REWIND: 0x5B, | ||
FORWARD: 0x5C, | ||
STOP: 0x5D, | ||
PLAY: 0x5E, | ||
RECORD: 0x5F, | ||
EQENABLE1: 0x70, | ||
EQENABLE2: 0x71, | ||
EQENABLE3: 0x72, | ||
EQENABLE4: 0x73, | ||
EQTYPE: 0x74, | ||
ALLBYPASS: 0x75, | ||
JOG: 0x76, | ||
LOCK: 0x77, | ||
INPUTMONITOR: 0x78, | ||
OPENVSTI: 0x79 | ||
}; | ||
|
||
// Midi CC number for knobs | ||
var CC = { | ||
PAN: 0x10, | ||
EQQ1: 0x20, | ||
EQQ2: 0x21, | ||
EQQ3: 0x22, | ||
EQQ4: 0x23, | ||
EQFREQUENCY1: 0x30, | ||
EQFREQUENCY2: 0x31, | ||
EQFREQUENCY3: 0x32, | ||
EQFREQUENCY4: 0x33, | ||
EQGAIN1: 0x40, | ||
EQGAIN2: 0x41, | ||
EQGAIN3: 0x42, | ||
EQGAIN4: 0x43, | ||
VALUE: 0x50, | ||
AIKNOB: 0x3C | ||
} | ||
|
||
// Array of midi listeners | ||
var midiListeners; | ||
|
||
// Job button is for parameter acceleration; | ||
var isJogActive = false; | ||
|
||
// Company, product, script version, UUID, author | ||
host.defineController("Steinberg", "CC121", "1.0", "A1068940-6B61-11E8-B566-0800200C9A66", "Philipp Winniewski"); | ||
host.defineMidiPorts(1, 1); | ||
// For Windows | ||
host.addDeviceNameBasedDiscoveryPair(["Steinberg CC121-1"], ["Steinberg CC121-1"]); | ||
// For Linux | ||
host.addDeviceNameBasedDiscoveryPair(["Steinberg CC121 MIDI 1"], ["Steinberg CC121 MIDI 1"]); | ||
|
||
function init() { | ||
// Configuring MIDI device | ||
host.getMidiInPort(0).setMidiCallback(onMidi); | ||
|
||
// Initializing controller sections | ||
midiListeners = [ | ||
initTransport(), | ||
initFunction(), | ||
initAi(), | ||
initChannel(), | ||
initDevice() | ||
]; | ||
} | ||
|
||
function exit() { | ||
for (i = 0; i < 128; i++) { | ||
sendMidi(144, i, LIGHT_OFF); | ||
} | ||
} | ||
|
||
function onMidi(status, data1, data2) { | ||
for (i = 0; i < midiListeners.length; i++) { | ||
midiListeners[i](status, data1, data2); | ||
} | ||
} | ||
|
||
function midiCcValueToInteger(ccValue) { | ||
if (ccValue >= ENCODER_POS_LO && ccValue <= ENCODER_POS_HI) { | ||
return ccValue - ENCODER_POS_LO + 1; | ||
} else if (ccValue >= ENCODER_NEG_LO && ccValue <= ENCODER_NEG_HI) { | ||
return -(ccValue - ENCODER_NEG_LO + 1); | ||
} else { | ||
return 0; | ||
} | ||
} |
Oops, something went wrong.