I own a working force and a force where the internal soundcard doesn't work anymore.
On my "not well working force" i use a presonus USB soundcard.
With this external soundcard, i have sound output only by using prime4.
To use prime 4, you have to change your productcode to JC11 when binding.
(mount -o bind productcode "/sys/firmware/devicetree/base/inmusic,product-code")
When running prime you'll see which file you can create for your assignments.
Below is an example when i run prime 4 :
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_Public_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_Public_Device.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_Private_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_Private_Device.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_MIDI_Port_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_MIDI_Port_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_MIDI_Port_Device.qml"
air.assignments.deviceadapter: Could not find file: "Akai_Pro_Force_MIDI_Port_Device.qml"
air.assignments.deviceadapter: Could not find file: "AudioBox_USB_96_MIDI_1_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "AudioBox_USB_96_MIDI_1_Assignments.qml"
air.assignments.deviceadapter: Could not find file: "AudioBox_USB_96_MIDI_1_Device.qml"
air.assignments.deviceadapter: Could not find file: "AudioBox_USB_96_MIDI_1_Device.qml"
So all these qml files can be created.
Then you can use the following qml to use it on your force.
(qml syntax has been taken from Prime Firmware)
[[http://dnttalo.cluster029.hosting.ovh.net/Qml/Prime4onBadForce/Akai_Pro_Force_Public_Device.qml|Akai_Pro_Force_Public_Device]]
[[http://dnttalo.cluster029.hosting.ovh.net/Qml/Prime4onBadForce/Akai_Pro_Force_Public_Assignments.qml|Akai_Pro_Force_Public_Assignments]]
[[http://dnttalo.cluster029.hosting.ovh.net/Qml/Prime4onBadForce/Akai_Pro_Force_Private_Device.qml|Akai_Pro_Force_Private_Device]]
[[http://dnttalo.cluster029.hosting.ovh.net/Qml/Prime4onBadForce/Akai_Pro_Force_Private_Assignments.qml|Akai_Pro_Force_Private_Assignments]]
I will explain a bit the syntax
**_Private_ files are used for inputs / actions buttons .**
Akai_Pro_Force_Private_Device.qml :
import airAssignments 1.0
import InputAssignment 0.1
import OutputAssignment 0.1
import Device 0.1
import QtQuick 2.9
// QT_LOGGING_RULES=air.planck.firmware.upgrade=true LD_LIBRARY_PATH=/usr/qt/lib /usr/Engine/JC11 -d0
Device {
id: device
property real gamma: 3.5
property real padGamma: 3.5
controls: []
useGlobalShift: false
numberOfLayers: 0
property string deviceInfo: ""
property string currentMixerFirmwareVersion
///////////////////////////////////////////////////////////////////////////
// Setup
function queryAbsoluteControls() {
console.log("Query absolute position controls")
Midi.sendSysEx("F0 00 02 0B 7F 0C 04 00 00 F7")
}
property Timer initPhaseEndTimer: Timer {
interval: 1000
repeat: false
onTriggered: {
console.log("Initialization phase ended")
device.isInitializing = false
queryAbsoluteControls()
}
}
property bool isInitializing: false
Component.onCompleted: {
currentColors = {}
currentSimpleColors = {}
console.log("Sending Initilization Message Akai Pro AMX")
Midi.sendSysEx("47 7F 2C 60 00 04 04 01 00 00");
isInitializing = true
requestPowerOnButtonState()
console.log("Sending initialization message for PRIME GO ...")
Midi.sendSysEx("F0 00 02 0B 7F 0C 60 00 04 04 01 01 04 F7")
initPhaseEndTimer.start()
}
Component.onDestruction: {
Midi.sendNoteOff(0, 118)
}
property var currentColors
property var currentSimpleColors
// Dec to Hex Conversion
function d2h(d){
return (+d).toString(16).toUpperCase()
}
function midiColorChannel(c, gamma){
return d2h(Math.min(127, Math.max(0, Math.floor(Math.pow(c, gamma) * 127))))
}
function mapColor(color) {
return Qt.rgba(color.r, color.g, color.b , color.a)
}
function midiColor(color, gamma) {
var c = mapColor(color)
return midiColorChannel(c.r, gamma) + " " + midiColorChannel(c.g, gamma) + " " + midiColorChannel(c.b, gamma)
}
function sendNoteOn(channel, index, value) {
Midi.sendNoteOn(channel, index, value)
}
function sendSimpleColor(channel, index, value) {
currentSimpleColors[index] = value
if (value === 0) {
Midi.sendNoteOff(channel, index)
}
else {
Midi.sendNoteOn(channel, index, value)
}
}
//Color Send Function
function sendColor(channel, index, color)
{
currentColors[index] = color
var g = device.gamma
if(index >= 15 && index <= 23) {
g = device.padGamma
}
var sysEx = "F0 00 02 0B 7F 0C 03 00 05 " + d2h(channel) + " " + d2h(index) + " " + midiColor(color, g)+" F7"
Midi.sendSysEx(sysEx)
}
function requestPowerOnButtonState() {
Midi.sendSysEx("F0 00 02 0B 7f 0C 42 00 00 F7")
}
function sysExToIntList(sysExString)
{
var valueList = sysExString.split(" ")
var result = []
for(var i = 0; i < valueList.length; ++i) {
result.push(parseInt(valueList[i], 16))
}
return result
}
function sysEx(sysExString) {
console.info("Received SysEx:", sysExString)
var valueList = sysExToIntList(sysExString)
var result = ""
// 0xf0 0x00 0x02 0x0b 0x00 0x06 0x42 0x00 0x01 0x01 0xf7
if(valueList[1] === 0x00 && valueList[2] === 0x02 && valueList[3] === 0x0B && valueList[4] === 0x00 && valueList[6] === 0x42)
{
if(valueList[9] === 0x0) {
console.log("No special power on request")
}
else if(valueList[9] === 0x1) {
console.log("Request test-mode entry")
quitToTestApp()
}
}
else if(valueList[1] === 0x7E && valueList[2] === 0x00 && valueList[3] === 0x06 && valueList[4] === 0x02)
{
var i
for(i = 0; i < 4; ++i) {
result += valueList[i + 11]
if(i === 1) {
result += "."
}
}
deviceInfo = result
var mixerVersion = ""
if(valueList.length === 43) {
//mixerVersion = "00." + valueList[40].toString(16)
for(i = 0; i < 4; ++i) {
mixerVersion += valueList[i + 37]
if(i === 1) {
mixerVersion += "."
}
}
}
currentMixerFirmwareVersion = mixerVersion
console.log("Mixer version:", mixerVersion, "(", valueList, ")")
var currentSerialNumber = Planck.readFromFile("/sys/firmware/devicetree/base/serial-number")
var serial = ""
for(i = 20; i < 35; ++i) {
if(valueList[i] === 0) {
break
}
serial += String.fromCharCode(valueList[i])
}
if(serial !== currentSerialNumber && serial.length > 0) {
Planck.setDeviceSerialNumber(serial)
}
}
}
}
Akai_Pro_Force_Private_Assignments.qml :
import airAssignments 1.0
import ControlSurfaceModules 0.1
import Planck 1.0
import QtQuick 2.12
import InputAssignment 0.1
import OutputAssignment 0.1
MidiAssignment {
objectName: "PRIME 4 Controller Assignment"
Utility {
id: util
}
GlobalAssignmentConfig {
id: globalConfig
midiChannel: 0
}
GlobalAction {
id: globalAction
}
Back {
note: 67
ledType: LedType.Simple
}
Forward {
//note: 115
shiftAction: Action.Quantize
ledType: LedType.Simple
}
BrowseEncoder {
//pushNote: 111
turnCC: 100
ledType: LedType.Simple
}
View {
note: 0
holdAction: Action.ToggleControlCenter
shiftAction: Action.SwitchMainViewLayout
ledType: LedType.Simple
}
Shift {
note: 49
ledType: LedType.Simple
}
Media {
mediaButtonsModel: ListModel {
ListElement {
name: 'Eject'
shiftName: 'Source'
note: 20
hasLed: true
}
}
}
Mixer {
cueMixCC: 12
cueGainCC: 13
crossfaderCC: 32
}
MicSettings {}
Mics {
mic1Note: 36
mic2Note: 37
mic1ShiftAction: Action.ToggleMicTalkover
}
Repeater {
model: ListModel {
ListElement {
deckName: 'Left'
deckMidiChannel: 9
loadNote: 123
}
}
Item {
DeckAssignmentConfig {
id: deckConfig
name: model.deckName
midiChannel: model.deckMidiChannel
}
DeckAction {
id: deckAction
}
Bank {}
Load {
note: model.loadNote
ledType: LedType.Simple
}
PerformanceModes {
ledType: LedType.RGB
modesModel: ListModel {
ListElement {
note: 110
view: "CUES"
}
ListElement {
note: 111
view: "LOOPS"
altView: "AUTO"
}
ListElement {
note: 112
view: "ROLL"
}
ListElement {
note: 113
view: "SLICER"
altView: "FIXED"
}
}
}
ActionPads {
firstPadNote: 102
ledType: LedType.RGB
}
Sync {
syncNote: 115
syncHoldAction: Action.KeySync
}
PlayCue {
cueNote: 37
cueShiftAction: Action.SetCuePoint
playNote: 114
}
PitchBend {
minusNote: 29
plusNote: 30
}
JogWheel {
touchNote: 33
ccUpper: 0x37
ccLower: 0x4D
jogSensitivity: 1638.7 * 10.08
hasTrackSearch: true
}
Vinyl {
note: 35
holdAction: Action.GridCueEdit
}
AutoLoop {
pushNote: 116
turnCC: 160
loopInactiveShiftTurnAction: Action.BeatJump
ledType: LedType.Simple
}
SpeedSlider {
ccUpper: 0x1F
ccLower: 0x4B
invert: true
}
}
}
Repeater {
model: ListModel {
ListElement {
deckName: 'Right'
deckMidiChannel: 9
loadNote: 124
}
}
Item {
DeckAssignmentConfig {
id: deckConfig
name: model.deckName
midiChannel: model.deckMidiChannel
}
DeckAction {
id: deckAction
}
Bank {}
Load {
note: model.loadNote
ledType: LedType.Simple
}
PerformanceModes {
ledType: LedType.RGB
modesModel: ListModel {
ListElement {
note: 78
view: "CUES"
}
ListElement {
note: 79
view: "LOOPS"
altView: "AUTO"
}
ListElement {
note: 80
view: "ROLL"
}
ListElement {
note: 81
view: "SLICER"
altView: "FIXED"
}
}
}
ActionPads {
firstPadNote: 70
ledType: LedType.RGB
}
Sync {
syncNote: 83
syncHoldAction: Action.KeySync
}
PlayCue {
cueNote: 9
cueShiftAction: Action.SetCuePoint
playNote: 82
}
PitchBend {
minusNote: 29
plusNote: 30
}
JogWheel {
touchNote: 33
ccUpper: 0x37
ccLower: 0x4D
jogSensitivity: 1638.7 * 10.08
hasTrackSearch: true
}
Vinyl {
note: 35
holdAction: Action.GridCueEdit
}
AutoLoop {
pushNote: 56
turnCC: 230
loopInactiveShiftTurnAction: Action.BeatJump
ledType: LedType.Simple
}
SpeedSlider {
ccUpper: 0x1F
ccLower: 0x4B
invert: true
}
}
}
Repeater {
model: ListModel {
ListElement {
mixerChannelName: '1'
mixerChannelMidiChannel: 0
}
}
Item {
objectName: 'Mixer Channel %1'.arg(model.mixerChannelName)
MixerChannelAssignmentConfig {
id: mixerChannelConfig
name: model.mixerChannelName
midiChannel: model.mixerChannelMidiChannel
}
MixerChannelCore {
pflNote: 13
trimCC: 3
trebleCC: 21
midCC: 6
bassCC: 200
faderCC: 14
}
SweepFxKnob {
cc: 24
}
SweepFxSelect {
channelNames: [model.mixerChannelName]
buttonsModel: ListModel {
ListElement {
note: 60
fxIndex: SweepEffect.DualFilter
}
ListElement {
note: 61
fxIndex: SweepEffect.Wash
}
}
}
}
}
Repeater {
model: ListModel {
ListElement {
mixerChannelName: '2'
mixerChannelMidiChannel: 0
}
}
Item {
objectName: 'Mixer Channel %1'.arg(model.mixerChannelName)
MixerChannelAssignmentConfig {
id: mixerChannelConfig
name: model.mixerChannelName
midiChannel: model.mixerChannelMidiChannel
}
MixerChannelCore {
pflNote: 13
trimCC: 3
trebleCC: 4
midCC: 6
bassCC: 22
faderCC: 14
}
SweepFxKnob {
cc: 24
}
SweepFxSelect {
channelNames: [model.mixerChannelName]
buttonsModel: ListModel {
ListElement {
note: 62
fxIndex: SweepEffect.DualFilter
}
ListElement {
note: 63
fxIndex: SweepEffect.Wash
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Effect Controls
Repeater {
model: ListModel {
ListElement {
deckName: "Left"
deckMidiChannel: 0
}
ListElement {
deckName: "Right"
deckMidiChannel: 1
}
}
Item {
id: deckFx
ValueNoteAssignment {
objectName: "FX%1 Active Button".arg(1)
note: 93
channel: deckMidiChannel
enabled: !device.shift
output: ActionOutput {
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Active".arg(1)
}
}
}
ValueNoteAssignment {
objectName: "FX%1 Active Button".arg(2)
note: 05
channel: deckMidiChannel
enabled: !device.shift
output: ActionOutput {
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Active".arg(2)
}
}
}
ValueCCAssignment {
objectName: "FX%1 Selection".arg(1)
cc: 17
channel: deckMidiChannel
enabled: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Selecting".arg(1)).translator.state
output: QtObject {
readonly property QObjProperty pSelectionIndex: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/PreselectIndex".arg(1))
function setValue(channel, value, assignmentEnabled) {
if(assignmentEnabled) {
if(value > 0.5) { // turn counter clockwise, value normalized
pSelectionIndex.translator.unnormalized = pSelectionIndex.translator.unnormalized - 1
}
else {
pSelectionIndex.translator.unnormalized = pSelectionIndex.translator.unnormalized + 1
}
}
}
}
}
ValueCCAssignment {
objectName: "FX%1 Selecting".arg(1)
cc: 17
channel: deckMidiChannel
enabled: !Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Selecting".arg(1)).translator.state
output: ActionOutput {
behaviour: force.action
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Selecting".arg(1)
}
}
}
ValueCCAssignment {
objectName: "FX%1 Selection".arg(2)
cc: 20
channel: deckMidiChannel
enabled: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Selecting".arg(2)).translator.state
output: QtObject {
readonly property QObjProperty pSelectionIndex: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/PreselectIndex".arg(2))
function setValue(channel, value, assignmentEnabled) {
if(assignmentEnabled) {
if(value > 0.5) { // turn counter clockwise, value normalized
pSelectionIndex.translator.unnormalized = pSelectionIndex.translator.unnormalized - 1
}
else {
pSelectionIndex.translator.unnormalized = pSelectionIndex.translator.unnormalized + 1
}
}
}
}
}
ValueCCAssignment {
objectName: "FX%1 Selecting".arg(2)
cc: 20
channel: deckMidiChannel
enabled: !Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Selecting".arg(2)).translator.state
output: ActionOutput {
behaviour: force.action
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Selecting".arg(2)
}
}
}
// ValueCCAssignment {
// objectName: "FX%1 Rate".arg(1)
// cc: 100
// channel: deckMidiChannel
// enabled: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Knob2Target".arg(1)).translator.string === "Rate"
// output: QtObject {
// readonly property QObjProperty pTarget: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Rate".arg(1))
// function setValue(channel, value, assignmentEnabled) {
// if(assignmentEnabled) {
// var counterClock = value > 0.5
// var changeIndex = counterClock ? -1 : 1
// pTarget.translator.index = pTarget.translator.index + changeIndex
// }
// }
// }
// }
ValueCCAssignment {
objectName: "FX%1 Amount".arg(1)
cc: 18
channel: deckMidiChannel
enabled: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Knob2Target".arg(1)).translator.string === "Amount"
output: EndlessKnobOutput {
smallestIncrement: 0.01
biggestIncrement: 0.1
target: PropertyTarget { path: "/Engine/Mixer/Channel%1/DJFx/Amount".arg(1) }
}
}
ValueCCAssignment {
objectName: "FX%1 Amount".arg(2)
cc: 21
channel: deckMidiChannel
enabled: Planck.getProperty("/Engine/Mixer/Channel%1/DJFx/Knob2Target".arg(2)).translator.string === "Amount"
output: EndlessKnobOutput {
smallestIncrement: 0.01
biggestIncrement: 0.1
target: PropertyTarget { path: "/Engine/Mixer/Channel%1/DJFx/Amount".arg(2) }
}
}
ValueCCAssignment {
objectName: "FX%1 Mix".arg(1)
cc: 19
channel: deckMidiChannel
output: ValueOutput {
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Mix".arg(1)
}
}
}
ValueCCAssignment {
objectName: "FX%1 Mix".arg(2)
cc: 22
channel: deckMidiChannel
output: ValueOutput {
target: PropertyTarget {
path: "/Engine/Mixer/Channel%1/DJFx/Mix".arg(2)
}
}
}
}
}
/*
FxAssignmentConfig {
id: fxConfig
midiChannel: 0
channelNames: ['1', '2']
}
DJFxSelect {
pushNote: 53
touchNote: 91
turnCC: 100
}
DJFxTime {
pushNote: 8
turnCC: 18
}
DJFxWetDry {
cc: 19
}
DJFxActivate {
fxActivateType: FxActivateType.Button
activateControlsModel: ListModel {
ListElement {
midiChannel: 0
note: 93
}
ListElement {
midiChannel: 0
note: 92
}
}
}
DJFxAssign {
notes: [94, 95]
}
*/
**_Public_ files are used to lit leds / pads.**
Akai_Pro_Force_Public_Device.qml :
import airAssignments 1.0
import InputAssignment 0.1
import OutputAssignment 0.1
import Device 0.1
import QtQuick 2.0
import Planck 1.0
Device {
id: device
property real gamma: 3.5
property real padGamma: 3.5
controls: []
useGlobalShift: false
numberOfLayers: 0
property string deviceInfo: ""
property QObjProperty pRunningDark: Planck.getProperty("/GUI/Scripted/RunningDark");
property bool isRunningDark: pRunningDark && pRunningDark.translator ? pRunningDark.translator.state : false
onIsRunningDarkChanged: {
if(isRunningDark) {
Midi.sendNoteOn(0, 117, 0)
}
else {
for(var channel in currentColors) {
for(var key in currentColors[channel]) {
sendColor(channel, key, currentColors[channel][key]);
}
}
for(var channel in currentSimpleColors) {
for(var simpleKey in currentSimpleColors[channel]) {
sendSimpleColor(channel, simpleKey, currentSimpleColors[channel][simpleKey]);
}
}
}
}
property QObjProperty pCalibratePlatter: Planck.getProperty("/GUI/Scripted/CalibratePlatter");
property bool calibratePlatter: pCalibratePlatter && pCalibratePlatter.translator && pCalibratePlatter.translator.state
onCalibratePlatterChanged: {
if(calibratePlatter) {
console.log("calibratePlatter")
//Midi.sendSysEx("F0 00 02 0B 7F 08 7D 00 02 10 7F F7")
}
}
///////////////////////////////////////////////////////////////////////////
// Setup
property Timer initTimer: Timer {
interval: 1000
repeat: false
onTriggered: {
console.log("Initialization phase ended")
device.isInitializing = false
}
}
property bool isInitializing: false
Component.onCompleted: {
currentColors = {}
currentSimpleColors = {}
//Midi.sendSysEx("F0 7E 00 06 01 F7")
isInitializing = true
requestPowerOnButtonState();
console.log("Sending initialization message for PRIME 4 ...")
//Midi.sendSysEx("F0 00 02 0B 7f 08 60 00 04 04 01 01 03 F7")
initTimer.start()
}
Component.onDestruction: {
if(Planck.quitReason() === QuitReasons.ControllerMode) {
Midi.sendNoteOn(0, 117, 1) // Dim all LEDs
}
else {
Midi.sendNoteOff(0, 117) // Turn all LEDs off
}
}
property var currentColors
property var currentSimpleColors
function padToTwo(str) {
return str.padStart(2, '0');
}
function dec2hex(d){
return padToTwo((+d).toString(16).toUpperCase())
}
function midiColorChannel(c, gamma){
return dec2hex(Math.min(127, Math.max(0,Math.floor(Math.pow(c, gamma) * 127))))
}
function mapColor(color) {
return Qt.rgba(color.r, color.g, color.b , color.a)
}
function midiColor(color, gamma) {
var c = mapColor(color)
return midiColorChannel(c.r, gamma)+ " " + midiColorChannel(c.g, gamma) + " " + midiColorChannel(c.b, gamma)
}
function sendNoteOn(channel, index, value) {
Midi.sendNoteOn(channel, index, value);
}
function sendSimpleColor(channel, index, value) {
if(!currentSimpleColors[channel]) {
currentSimpleColors[channel] = {};
}
currentSimpleColors[channel][index] = value
if(isRunningDark)
return;
if(value === 0) {
Midi.sendNoteOff(channel, index)
} else {
Midi.sendNoteOn(channel, index, value)
}
}
//Color Send Function
function sendColor(channel, index, color)
{
if(!currentColors[channel]) {
currentColors[channel] = {};
}
currentColors[channel][index] = color;
if(isRunningDark) {
return;
}
var g = device.gamma
if(index >= 15 && index <= 23) {
g = device.padGamma
}
//var sysEx = "F0 47 7F 40 65 00 04 " + dec2hex(channel) + " " + dec2hex(index) + " " + midiColor(color, g)+" F7"
var sysEx = "F0 47 7F 40 65 00 04 " + dec2hex(index) + " " + padToTwo(midiColor(color, g)) +" F7"
Midi.sendSysEx(sysEx)
console.info("ColorSysex:",sysEx)
}
property list- oleds
property bool enablePartialOledUpdates: false
Repeater {
model: 8
Item {
id: self
property var buffer
function numberToHex( num )
{
var result = "0x";
if(num < 0x10)
{
result += "0"
}
result += num.toString(16);
return result;
}
function buildSysEx(painter, x,y,w,h) {
var result = "00 02 0B 7F 08 0B"
var imageData = painter.imageToString(x, y, w, h);
var dataLen = ((imageData.length+1) / 3) + 5;
result += " " + numberToHex((dataLen >> 7) & 0x7F)
result += " " + numberToHex( dataLen & 0x7F )
result += " " + index.toString(16);
result += " " + numberToHex(y/8) ;
result += " " + numberToHex((y/8)+(h/8));
result += " " + numberToHex(x) + " ";
result += " " + numberToHex(x+w-1);
result += " " + imageData
return result;
}
function update(painter) {
if(device.enablePartialOledUpdates) {
var i = 0
var bufToSend = []
for(var y = 0; y < 4; y++) {
for(var x = 0; x < 16; x++) {
var result = buildSysEx(painter, x*8, y*8, 8, 8)
if(buffer[i] !== result) {
//Midi.sendSysEx(result);
buffer[i] = result;
bufToSend.push(result);
}
i++
}
}
if(bufToSend.length < 20) {
for(var i = 0; i < bufToSend.length; i++) {
//Midi.sendSysEx(bufToSend[i])
//console.info("BuftoSend:",bufToSend[i])
}
} else {
var fullSysEx = buildSysEx(painter, 0, 0, 128, 32)
//console.info("fullSysEx:",fullSysEx)
//Midi.sendSysEx(fullSysEx);
}
} else {
var fullSysEx = buildSysEx(painter, 0, 0, 128, 32)
//console.info("fullSysEx2:",fullSysEx)
//Midi.sendSysEx(fullSysEx);
}
}
Component.onCompleted: {
var b = [];
for(var i = 0; i < 4 * 16; i++)
{
b.push( "" );
}
buffer = b;
device.oleds.push(self)
}
}
}
function requestPowerOnButtonState() {
//Midi.sendSysEx("F0 00 02 0B 7F 08 42 00 00 F7");
}
function sysExToIntList(sysExString)
{
var valueList = sysExString.split(" ");
var result = [];
for(var i=0;i
}
Akai_Pro_Force_Public_Assignments.qml :
import airAssignments 1.0
import InputAssignment 0.1
import OutputAssignment 0.1
import ControlSurfaceModules 0.1
import QtQuick 2.12
MidiAssignment {
objectName: 'PRIME 4 Controller Assignment'
id: assignment
Utility {
id: util
}
GlobalAssignmentConfig {
id: globalConfig
midiChannel: 0
}
GlobalAction {
id: globalAction
}
Back {
note: 3
ledType: LedType.Simple
}
Forward {
note: 4
shiftAction: Action.Quantize
ledType: LedType.Simple
}
BrowseEncoder {
pushNote: 6
turnCC: 5
ledType: LedType.Simple
}
View {
note: 7
shiftAction: Action.SwitchMainViewLayout
holdAction: Action.ToggleControlCenter
ledType: LedType.Simple
}
ZoneOut {
note: 16
}
Media {
mediaButtonsModel: ListModel {
ListElement {
name: 'Eject'
shiftName: 'Source'
note: 20
hasLed: true
}
}
removableDeviceModel: ListModel {
ListElement { // SD
slot: '1'
note: 52
}
ListElement { // USB 1
slot: '6'
note: 53
}
ListElement { // USB 2
slot: '5'
note: 54
}
}
}
Repeater {
model: ListModel {
ListElement {
deckName: 'Left'
deckMidiChannel: 4
loadNote: 1
}
}
Item {
objectName: 'Deck %1'.arg(model.deckName)
DeckAssignmentConfig {
id: deckConfig
name: model.deckName
midiChannel: model.deckMidiChannel
}
DeckAction {
id: deckAction
}
Load {
note: model.loadNote
ledType: LedType.RGB
}
Censor {
note: 1
}
TrackSkip {
prevNote: 4
nextNote: 5
}
BeatJump {
prevNote: 6
nextNote: 7
}
Sync {
syncNote: 8
syncHoldAction: Action.InstantDouble
}
PlayCue {
cueNote: 9
cueShiftAction: Action.SetCuePoint
playNote: 60
}
PerformanceModes {
ledType: LedType.RGB
modesModel: ListModel {
ListElement {
note: 56
view: 'CUES'
}
ListElement {
note: 57
view: 'LOOPS'
altView: 'AUTO'
}
ListElement {
note: 58
view: 'ROLL'
}
ListElement {
note: 59
view: 'SLICER'
altView: 'FIXED'
}
}
}
ActionPads {
firstPadNote: 48
ledType: LedType.RGB
}
Parameter {
leftNote: 23
rightNote: 24
}
BeatGrid {
leftNote: 25
rightNote: 26
editGridNote: 27
}
Shift {
note: 28
ledType: LedType.Simple
}
PitchBend {
minusNote: 29
plusNote: 30
}
JogWheel {
touchNote: 33
ccUpper: 0x37
ccLower: 0x4D
jogSensitivity: 1638.7 * 3.6
ledType: LedType.RGB
}
KeyLock {
note: 34
}
Vinyl {
note: 35
}
Slip {
note: 36
}
ManualLoop {
inNote: 37
outNote: 38
}
AutoLoop {
pushNote: 39
turnCC: 32
ledType: LedType.Simple
}
SpeedSlider {
ccUpper: 0x1F
ccLower: 0x4B
downLedNote: 42
centreLedNote: 43
upLedNote: 44
}
DeckSelect {
primaryDeckIndex: 1 + model.index
primaryDeckNote: 28 + model.index
}
}
}
Repeater {
model: ListModel {
ListElement {
deckName: 'Right'
deckMidiChannel: 5
loadNote: 2
}
}
Item {
DeckAssignmentConfig {
id: deckConfig
name: model.deckName
midiChannel: model.deckMidiChannel
}
DeckAction {
id: deckAction
}
Load {
note: model.loadNote
ledType: LedType.RGB
}
Censor {
note: 1
}
TrackSkip {
prevNote: 4
nextNote: 5
}
BeatJump {
prevNote: 6
nextNote: 7
}
Sync {
syncNote: 8
syncHoldAction: Action.InstantDouble
}
PlayCue {
cueNote: 9
cueShiftAction: Action.SetCuePoint
playNote: 28
}
PerformanceModes {
ledType: LedType.RGB
modesModel: ListModel {
ListElement {
note: 24
view: 'CUES'
}
ListElement {
note: 25
view: 'LOOPS'
altView: 'AUTO'
}
ListElement {
note: 26
view: 'ROLL'
}
ListElement {
note: 27
view: 'SLICER'
altView: 'FIXED'
}
}
}
ActionPads {
firstPadNote: 16
ledType: LedType.RGB
}
Parameter {
leftNote: 23
rightNote: 24
}
BeatGrid {
leftNote: 25
rightNote: 26
editGridNote: 27
}
Shift {
note: 28
ledType: LedType.Simple
}
PitchBend {
minusNote: 29
plusNote: 30
}
JogWheel {
touchNote: 33
ccUpper: 0x37
ccLower: 0x4D
jogSensitivity: 1638.7 * 3.6
ledType: LedType.RGB
}
KeyLock {
note: 34
}
Vinyl {
note: 35
}
Slip {
note: 36
}
ManualLoop {
inNote: 37
outNote: 38
}
AutoLoop {
pushNote: 39
turnCC: 32
ledType: LedType.Simple
}
SpeedSlider {
ccUpper: 0x1F
ccLower: 0x4B
downLedNote: 42
centreLedNote: 43
upLedNote: 44
}
DeckSelect {
primaryDeckIndex: 1 + model.index
primaryDeckNote: 28 + model.index
}
}
}
// Numbers of channels arranged from left to right:
readonly property var mixerChannelOrder: [3,1,2,4]
Repeater {
model: mixerChannelOrder
ValueNoteAssignment {
objectName: "Channel%1 Line".arg(modelData)
note: 21 + index
channel: 15
output: QtObject {
readonly property QObjProperty pLine: Planck.getProperty("/Engine/Mixer/Channel%1/Line".arg(modelData))
function setValue(channel, value, assignmentEnabled) {
if(assignmentEnabled) {
pLine.translator.value = value
}
}
}
}
}
Repeater {
model: 4
Item {
ColorOutputAssignment {
objectName: "Mixer Cue color for deck %1".arg(index + 1)
idx: 13
channel: index
readonly property color deckColor: Planck.getProperty("/Client/Preferences/Profile/Application/PlayerColor%1".arg(index + 1)).translator.color
onColor: deckColor
}
}
Component.onCompleted: {
device.sendSimpleColor(15, 11, 1) //Split
device.sendSimpleColor(15, 12, 1) //Dub Echo
device.sendSimpleColor(15, 13, 1) //Noise
device.sendSimpleColor(15, 14, 1) //Wash Out
device.sendSimpleColor(15, 15, 1) //Gate
device.sendSimpleColor(15, 35, 1) //Mic Talkover
device.sendSimpleColor(15, 36, 1) //Mic 1 On/Off
device.sendSimpleColor(15, 37, 1) //Mic 2 On/Off
device.sendSimpleColor(15, 38, 1) //Mic 1 Echo On/Off
device.sendSimpleColor(15, 39, 1) //Mic 2 Echo On/Off
}
}
/////// FX
Repeater {
model: ListModel {
ListElement {
deck: "1"
controlChannel: 8
ccIndex: 11
}
ListElement {
deck: "2"
controlChannel: 9
ccIndex: 11
}
ListElement {
deck: "3"
controlChannel: 8
ccIndex: 13
}
ListElement {
deck: "4"
controlChannel: 9
ccIndex: 13
}
}
OutputAssignment {
properties: { 'currentBpm' : '' }
readonly property QObjProperty pCurrentBPM: Planck.getProperty("/Engine/Deck%1/CurrentBPM".arg(deck))
readonly property int currentBpm: pCurrentBPM && pCurrentBPM.translator ? Math.round(pCurrentBPM.translator.unnormalized * 10) : 1200
readonly property int minMixerTempo: 60 * 10
readonly property int maxMixerTempo: 300 * 10
function send() {
if(currentBpm === 0) {
return
}
var bpm = currentBpm
Math.log2 = Math.log2 || function(x){return Math.log(x)*Math.LOG2E;};
if(currentBpm < minMixerTempo) {
var wrapPower = Math.ceil(Math.log2(minMixerTempo / currentBpm))
bpm = currentBpm * Math.pow(2, wrapPower)
}
else if(currentBpm > maxMixerTempo) {
var wrapPower = Math.ceil(Math.log2(currentBpm / maxMixerTempo))
bpm = currentBpm / Math.pow(2, wrapPower)
}
Midi.sendControlChange(controlChannel, ccIndex, (bpm - minMixerTempo) & 0x7F)
Midi.sendControlChange(controlChannel, ccIndex + 1, (bpm - minMixerTempo) >> 7)
}
}
}
property bool displaysActive : false
onDisplaysActiveChanged: {
if(!displaysActive) {
clearPainter.draw();
}
}
ValueNoteAssignment {
objectName: "MIDI Note Activity"
note: -1
channel: -1
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
activityTimer.restart()
}
}
}
ValueCCAssignment {
objectName: "MIDI CC Activity"
cc: -1
channel: -1
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
activityTimer.restart()
}
}
}
Connections {
target: globalConfig
onCurrentFocusAreaChanged: {
activityTimer.restart()
}
}
Timer {
id: activityTimer
interval: 10 * 60 * 1000
running: true
onRunningChanged: {
if(running) {
displaysActive = true
}
}
onTriggered: displaysActive = false
}
Painter {
id: clearPainter
width: 128
height: 64
format: Painter.AK01_DISPLAY
function draw()
{
setCompositionMode(Painter.SourceOver);
clear("black")
setColor("white")
for (var displayIndex = 0; displayIndex < device.oleds.length; displayIndex++) {
device.oleds[displayIndex].update(clearPainter)
}
}
Component.onDestruction: clearPainter.draw()
}
Repeater {
model: ListModel {
ListElement {
sideChannel: 6
controlChannel: 8
displayIndex: 3
slotName: "FXSlot1"
}
ListElement {
sideChannel: 7
controlChannel: 9
displayIndex: 7
slotName: "FXSlot2"
}
}
Item {
id: beatSlot
readonly property QObjProperty pEffectType: Planck.getProperty("/Client/Mixer/%1/EffectList".arg(slotName))
readonly property string effectType: pEffectType && pEffectType.translator ? pEffectType.translator.string : ""
readonly property bool hasBeatLengths: Planck.getProperty("/Client/Mixer/%1/BeatLength".arg(slotName)).translator.numEntries > 0
ValueCCAssignment {
objectName: "%1 Knob FX WET/DRY".arg(slotName)
cc: 4
channel: sideChannel
enabled: true
output: JogOutput {
jogAcceleration: 1.0
jogSensitivity: 0.5
roundRobin: false
type: 0
target: PropertyTarget {
path: "/Client/Mixer/%1/WetDry".arg(slotName)
}
}
}
ValueNoteAssignment {
objectName: "%1 Beat Decrease Button".arg(slotName)
note: 9
channel: sideChannel
enabled: beatSlot.hasBeatLengths
output: IncDecValueOutput {
target: PropertyTarget {
path: "/Client/Mixer/%1/BeatLength".arg(slotName)
}
increaseValue: false
roundRobin: false
}
}
ValueNoteAssignment {
objectName: "%1 Beat Increase Button".arg(slotName)
note: 10
channel: sideChannel
enabled: beatSlot.hasBeatLengths
output: IncDecValueOutput {
target: PropertyTarget {
path: "/Client/Mixer/%1/BeatLength".arg(slotName)
}
increaseValue: true
roundRobin: false
}
}
ValueNoteAssignment {
objectName: "%1 Beat Decrease Button LED".arg(slotName)
note: 9
channel: sideChannel
initialValue: 0.0
enabled: beatSlot.hasBeatLengths
resendValueOnEnabledChanged: true
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
device.sendSimpleColor(sideChannel, 9, assignmentEnabled ? (value ? 127 : 1) : 0)
}
}
Component.onCompleted: output.setValue(channel, initialValue, enabled)
}
ValueNoteAssignment {
objectName: "%1 Beat Increase Button LED".arg(slotName)
note: 10
channel: sideChannel
initialValue: 0.0
enabled: beatSlot.hasBeatLengths
resendValueOnEnabledChanged: true
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
device.sendSimpleColor(sideChannel, 10, assignmentEnabled ? (value ? 127 : 1) : 0)
}
}
Component.onCompleted: output.setValue(channel, initialValue, enabled)
}
OutputAssignment {
properties: { 'beatLength' : '/Client/Mixer/%1/BeatLength'.arg(slotName) }
function send() {
if(effectType === 'Bit Crush') {
Midi.sendControlChange(controlChannel, 0x13, propertyData.beatLength.string)
}
else {
Midi.sendControlChange(controlChannel, 9, propertyData.beatLength.index)
}
}
}
OutputAssignment {
properties: { 'wetDry' : '/Client/Mixer/%1/WetDry'.arg(slotName) }
function send() {
Midi.sendControlChange(controlChannel, 7, propertyData.wetDry.value * 127)
}
readonly property bool fxEnable: Planck.getProperty("/Client/Mixer/%1/Enable".arg(slotName)).translator.state
onFxEnableChanged: {
if (fxEnable) {
send()
}
}
}
Item {
Connections {
target: assignment
onDisplaysActiveChanged: {
if(assignment.displaysActive) {
beatDrawTimer.start()
}
}
}
Timer {
id: beatDrawTimer
interval: 50
onTriggered: beatDisplayPainter.draw()
}
Painter {
id: beatDisplayPainter
width: 128
height: 64
font.family: "NimbusSanD"
font.weight: Font.Bold
font.pixelSize: 14
font.capitalization: Font.AllUppercase
format: Painter.AK01_DISPLAY
property QObjProperty pBeatLength: Planck.getProperty("/Client/Mixer/%1/BeatLength".arg(slotName))
property string beatLength: pBeatLength && pBeatLength.translator ? pBeatLength.translator.string : ""
property int beatLengthIndex: pBeatLength && pBeatLength.translator ? pBeatLength.translator.index : 0
property QObjProperty pValue: Planck.getProperty("/Client/Mixer/%1/WetDry".arg(slotName))
property real value: pValue && pValue.translator ? pValue.translator.value : 0.25
property int iValue: Math.round(value * 124)
property string effectType: beatSlot.effectType
function draw()
{
setCompositionMode(Painter.SourceOver);
clear("black")
setColor("white")
if(effectType === 'Bit Crush') {
drawTextInRect(0,18, 128, 16, Painter.AlignCenter, "Bits: " + beatLength)
}
else if(beatSlot.hasBeatLengths) {
drawTextInRect(0,18, 128, 16, Painter.AlignCenter, beatLength + (beatLengthIndex > 6 ? " Beats" : " Beat"))
}
drawRect(0, 0, 127, 13)
fillRect(2, 2, value * 124, 10, "white")
if(value !== 0.5) {
if(value > 0.5)
setColor("black")
drawLine(62, 1, 62, 12)
}
device.oleds[displayIndex].update(beatDisplayPainter)
}
onEffectTypeChanged: beatDrawTimer.start()
onBeatLengthChanged: beatDrawTimer.start()
onIValueChanged: beatDrawTimer.start()
}
}
}
}
Repeater {
model: ListModel {
ListElement {
sideChannel: 6
controlChannel: 8
displayIndex: 0
slotName: "FXSlot1"
}
ListElement {
sideChannel: 7
controlChannel: 9
displayIndex: 4
slotName: "FXSlot2"
}
}
Item {
id: fxBank
ValueCCAssignment {
objectName: "%1 Knob FX SELECT".arg(slotName)
cc: 1
channel: sideChannel
enabled: true
output: JogOutput {
jogAcceleration: 1.0
jogSensitivity: 1.0
type: 0
target: PropertyTarget {
path: "/Client/Mixer/%1/EffectJogWrapper".arg(slotName)
}
}
}
ValueCCAssignment {
objectName: "%1 Knob FX PARAMETER".arg(slotName)
cc: 2
channel: sideChannel
enabled: true
output: JogOutput {
jogAcceleration: 1.0
jogSensitivity: 0.5
roundRobin: false
type: 0
target: PropertyTarget {
path: "/Client/Mixer/%1/ParamValueJogWrapper".arg(slotName)
}
}
}
ValueCCAssignment {
objectName: "%1 Knob FX FREQUENCY".arg(slotName)
cc: 3
channel: sideChannel
enabled: true
output: JogOutput {
jogAcceleration: 1.0
jogSensitivity: 0.25
roundRobin: false
type: 0
target: PropertyTarget {
path: "/Client/Mixer/%1/FrequencyJogWrapper".arg(slotName)
}
}
}
ValueNoteAssignment {
note: 6
channel: sideChannel
enabled: device.shift
output: IncDecValueOutput {
target: PropertyTarget {
path: "/Client/Mixer/%1/EffectList".arg(slotName)
}
changeAmount: 1
increaseValue: true
roundRobin: true
}
}
ValueNoteAssignment {
objectName: '%1 Button ON'.arg(slotName)
note: 6
channel: sideChannel
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
if(value<0.5) {
return
}
var pEnable = Planck.getProperty("/Client/Mixer/%1/Enable".arg(slotName))
if(!device.shift) {
pEnable.translator.state = !pEnable.translator.state
} else if(pEnable.translator.state) {
pEnable.translator.state = false
}
}
}
}
ValueNoteAssignment {
objectName: '%1 Button PARAM'.arg(slotName)
note: 7
channel: sideChannel
enabled: true
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
if(value<0.5) {
return
}
var pTwoStateParam = Planck.getProperty("/Client/Mixer/%1/TwoStateParam".arg(slotName))
pTwoStateParam.translator.state = !pTwoStateParam.translator.state
}
}
}
ValueNoteAssignment {
objectName: '%1 Button RESET'.arg(slotName)
note: 8
channel: sideChannel
enabled: true
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
Midi.sendNoteOn(sideChannel, 8, value<0.5 ? 1 : 127)
if(value<0.5) {
return
}
Planck.getProperty("/Client/Mixer/%1/FrequencyJogWrapper".arg(slotName)).translator.value = 0.5
var pTwoStateParam = Planck.getProperty("/Client/Mixer/%1/Reset".arg(slotName))
pTwoStateParam.translator.state = true
}
}
}
OutputAssignment {
properties: { 'type' : '/Client/Mixer/%1/EffectList'.arg(slotName) }
function send() {
Midi.sendControlChange(controlChannel, 1, propertyData.type.unnormalized)
if(propertyData.type.string === 'Bit Crush') {
Midi.sendControlChange(controlChannel, 0x13, Planck.getProperty("/Client/Mixer/%1/BeatLength".arg(slotName)).translator.string)
}
else {
Midi.sendControlChange(controlChannel, 9, Planck.getProperty("/Client/Mixer/%1/BeatLength".arg(slotName)).translator.index)
}
}
}
OutputAssignment {
properties: { 'fxValue' : "/Client/Mixer/%1/ParamValue".arg(slotName) }
function send() {
Midi.sendControlChange(controlChannel, 5, propertyData.fxValue.value * 127)
}
}
OutputAssignment {
properties: { 'fxValue' : "/Client/Mixer/%1/Frequency".arg(slotName) }
function send() {
Midi.sendControlChange(controlChannel, 6, propertyData.fxValue.value * 127)
}
}
OutputAssignment {
properties: { 'fxEnable' : "/Client/Mixer/%1/Enable".arg(slotName) }
function send() {
if(propertyData.fxEnable.state) {
Midi.sendNoteOn(sideChannel, 6, 127) //color
Midi.sendNoteOn(controlChannel, 1, 127) //inform dsp
} else {
Midi.sendNoteOn(sideChannel, 6, 1) //color
Midi.sendNoteOn(controlChannel, 1, 0) //inform dsp
Midi.sendNoteOff(controlChannel, 1) //inform dsp
var pTwoStateParam = Planck.getProperty("/Client/Mixer/%1/TwoStateParam".arg(slotName))
if (pTwoStateParam.translator.state === true) {
pTwoStateParam.translator.state = false
}
}
}
}
OutputAssignment {
properties: {
'fxTwoState' : "/Client/Mixer/%1/TwoStateParam".arg(slotName),
'fxTwoStateParamName' : "/Client/Mixer/%1/TwoStateParamName".arg(slotName)
}
property var flashStates: ({})
Timer {
id: freezeTimer
repeat: true
interval: 500
running: false
property bool isOn: false
onTriggered: {
isOn = !isOn
for(var x = 0; x < Object.keys(parent.flashStates).length; ++x) {
var channelNumber = parent.flashStates[Object.keys(parent.flashStates)[x]]
if(channelNumber !== 0){
if(isOn) {
Midi.sendNoteOn(channelNumber, 7, 127)
} else {
Midi.sendNoteOff(channelNumber, 7)
}
}
}
}
}
function stopTimer() {
delete flashStates[slotName]
if(Object.keys(flashStates).length === 0)
freezeTimer.stop()
}
function send() {
const dspNote = 2
const colourNote = 7
const full = 127
const dim = 1
if(propertyData.fxTwoStateParamName.string === "") {
// No param
Midi.sendNoteOff(sideChannel, colourNote)
Midi.sendNoteOff(controlChannel, dspNote)
stopTimer()
}
else if(propertyData.fxTwoState.state){
// Param present and on
var effectEnabled = Planck.getProperty("/Client/Mixer/%1/Enable".arg(slotName)).translator.state;
var effectName = Planck.getProperty("/Client/Mixer/%1/EffectList".arg(slotName)).translator.string
if(effectEnabled) {
Midi.sendNoteOn(sideChannel, colourNote, full)
Midi.sendNoteOn(controlChannel, dspNote, full)
if(effectName === "Reverb" || effectName === "Echo" || effectName === "Hall Echo") {
freezeTimer.start()
flashStates[slotName] = sideChannel
}
}
else {
propertyData.fxTwoState.state = false
if(effectName === "Reverb" || effectName === "Echo" || effectName === "Hall Echo") {
stopTimer()
}
}
}
else {
// Param present and off
Midi.sendNoteOn(sideChannel, colourNote, dim)
Midi.sendNoteOff(controlChannel, dspNote)
stopTimer()
}
}
}
OutputAssignment {
properties: { 'fxTwoState' : "/Client/Mixer/%1/Reset".arg(slotName) }
function send() {
if(propertyData.fxTwoState.state) {
Midi.sendNoteOn(sideChannel, 8, 127) //color
} else {
Midi.sendNoteOn(sideChannel, 8, 1) //color
}
}
}
Item {
Timer {
id: fxSelectDrawTimer
repeat: false
interval: 50
running: false
onTriggered: fxSelectDisplayPainter.draw()
}
Painter {
id: fxSelectDisplayPainter
width: 128
height: 64
font.family: "NimbusSanD"
font.pixelSize: 20
font.capitalization: Font.AllUppercase
format: Painter.AK01_DISPLAY
property QObjProperty pType: Planck.getProperty("/Client/Mixer/%1/EffectList".arg(slotName))
property string type: pType && pType.translator ? pType.translator.string : ""
function draw()
{
setCompositionMode(Painter.SourceOver);
clear("black");
setColor("white");
drawTextInRect(0,0, 128, 32, Painter.AlignCenter, type);
device.oleds[displayIndex].update(fxSelectDisplayPainter)
}
onTypeChanged: fxSelectDrawTimer.start()
}
}
Item {
Timer {
id: drawTimer
repeat: false
interval: 50
running: false
onTriggered: fxDisplayPainter.draw()
}
Painter {
id: fxDisplayPainter
width: 128
height: 64
font.family: "NimbusSanD"
font.weight: Font.Bold
font.pixelSize: 14
font.capitalization: Font.AllUppercase
format: Painter.AK01_DISPLAY
property QObjProperty pType: Planck.getProperty("/Client/Mixer/%1/EffectList".arg(slotName))
property string type: pType && pType.translator ? pType.translator.string : ""
property QObjProperty pParamName: Planck.getProperty("/Client/Mixer/%1/ParamName".arg(slotName))
property string paramName: pParamName && pParamName.translator ? pParamName.translator.string : ""
property QObjProperty pValue: Planck.getProperty("/Client/Mixer/%1/ParamValue".arg(slotName))
property real value: pValue && pValue.translator ? pValue.translator.value : 0.25
property int iValue: Math.round(value * 124)
property string lastPattern: ""
readonly property var patterns: [
"xxx xxx xxx xxx ",
"x x x x x x x x ",
"x xx xx xx x",
"xxxxx x x xxx x ",
"xxx xx xxxx x x ",
"xx xx xxxx xx xx",
"x x xx xx xxx x ",
"x xxx xxx xx x ",
"x xxxx xxx xx x ",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxx",
]
function draw()
{
setCompositionMode(Painter.SourceOver);
clear("black");
setColor("white");
if(paramName !== 'Pattern') {
lastPattern = ""
}
if(paramName === 'Pattern') {
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, paramName);
var patternNumber = (value * 127) >> 3
var ptn = patterns[patternNumber]
if(lastPattern === ptn) {
return
}
lastPattern = ptn
var x = 3
for (var i = 0; i < ptn.length; i++) {
if (ptn[i] == 'x') {
fillRect(x, 20, 6, 10, "white")
}
x = x + 7
if ((i + 1) % 4 === 0) {
x = x + 3
}
}
}
else if(paramName !== '') {
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, paramName);
if(type === 'Ping Pong') {
var newValue = (2 * value) - 1
drawRect(0, 18, 127, 13)
fillRect(2, 20, 124, 10, "white")
fillRect(64, 20, -newValue * 62, 10, "black")
fillRect(64, 20, newValue * 62, 10, "black")
fillRect(64, 20, 1, 10, "black")
}
else {
drawRect(0, 18, 127, 13)
fillRect(2, 20, value * 124, 10, "white")
}
}
device.oleds[displayIndex+1].update(fxDisplayPainter)
}
onParamNameChanged: drawTimer.start()
onIValueChanged: drawTimer.start()
}
}
Item {
Timer {
id: fqDrawTimer
repeat: false
interval: 50
running: false
onTriggered: fqDisplayPainter.draw()
}
Timer {
id: waitTimer
repeat: false
interval: 2000
running: false
onTriggered: fqDisplayPainter.drawFrequencyTitle()
}
Painter {
id: fqDisplayPainter
width: 128
height: 64
font.family: "NimbusSanD"
font.weight: Font.Bold
font.pixelSize: 14
font.capitalization: Font.MixedCase
format: Painter.AK01_DISPLAY
property QObjProperty pValue: Planck.getProperty("/Client/Mixer/%1/Frequency".arg(slotName))
property real value: pValue && pValue.translator ? pValue.translator.value : 0.25
property int iValue: Math.round(value * 124)
property bool drawTitle: false
property var freqencyArray: [
60,72,79,86,94,103,113,
124,136,149,163,178,195,214,234,
256,281,307,336,368,403,442,484,
554,607,664,727,796,872,955,1046,
1145,1254,1373,1503,1646,1802,1973,2161,
2366,2591,2837,3106,3401,3724,4078,4465,
4889,5354,5862,6419,7028,7696,8427,9227,
10103,11063,12114,13880,15198,16641,18222]
function drawFrequencyTitle()
{
drawTitle = true
draw()
}
function draw()
{
setCompositionMode(Painter.SourceOver);
clear("black");
setColor("white");
waitTimer.restart()
if(iValue === 62){
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, "ALL BANDS")
fillRect(2, 20, 124, 10, "white")
}
else if(value < 0.5) {
var msg = freqencyArray[iValue] + "Hz"
if(freqencyArray[iValue] >= 1000) {
msg = (freqencyArray[iValue]/1000).toFixed(1) + "kHz"
}
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, "< " + msg)
fillRect(2, 20, value * 2 * 124, 10, "white")
} else {
var val = iValue-63
var msg = freqencyArray[val] + "Hz"
if(freqencyArray[val] >= 1000) {
msg = (freqencyArray[val]/1000).toFixed(1) + "kHz"
}
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, "> " + msg)
fillRect(2 + (value - 0.5) * 2 * 125, 20, 124, 10, "white")
}
drawRect(0, 18, 127, 13)
if(drawTitle === true){
setCompositionMode(Painter.SourceOver);
fillRect(0,0, 128, 16, "black")
drawTextInRect(0,0, 128, 16, Painter.AlignCenter, "FREQUENCY")
drawTitle = false
waitTimer.stop()
}
device.oleds[displayIndex+2].update(fqDisplayPainter)
}
onIValueChanged: fqDrawTimer.start()
}
}
Connections {
target: assignment
onDisplaysActiveChanged: {
if(assignment.displaysActive) {
fxSelectDrawTimer.start()
drawTimer.start()
fqDrawTimer.start()
}
}
}
}
}
readonly property var activeDecks: Planck.getProperty("/GUI/Decks/ActiveDecks").translator.entries
property var deckThru: [0, 0, 0, 0]
property bool xFaderStartLeft: false
property bool xFaderStartRight: false
ValueNoteAssignment {
note: 25
channel: 15
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
xFaderStartLeft = value !== 0
}
}
}
ValueNoteAssignment {
note: 27
channel: 15
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
xFaderStartRight = value !== 0
}
}
}
property var crossFaderTable: [
0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00005623,0.00010000,0.00017783,0.00031623,0.00056234,0.00074989,0.00100000,0.00115478,0.00133352,0.00153993,0.00177828,
0.00205353,0.00237137,0.00273842,0.00316228,0.00354813,0.00398107,0.00446684,0.00501187,0.00546387,0.00595662,0.00649382,0.00707946,0.00771792,0.00841395,0.00917276,0.01000000,
0.01090184,0.01188502,0.01295687,0.01412538,0.01539927,0.01678804,0.01830206,0.01995262,0.02175204,0.02371374,0.02585235,0.02818383,0.03072557,0.03349654,0.03651741,0.03981072,
0.04340103,0.04731513,0.05158222,0.05623413,0.05956621,0.06309573,0.06683439,0.07079458,0.07391796,0.07717915,0.08058422,0.08413951,0.08785167,0.09172759,0.09577452,0.10000000,
0.10441190,0.10901845,0.11382823,0.11885022,0.12409378,0.12956867,0.13528511,0.14125375,0.14748573,0.15399265,0.16078665,0.16788040,0.17528712,0.18302061,0.19109530,0.19952623,
0.20832913,0.21752040,0.22711719,0.23713737,0.24759963,0.25852348,0.26992928,0.28183829,0.29006812,0.29853826,0.30725574,0.31622777,0.32546178,0.33496544,0.34474661,0.35481339,
0.35995648,0.36517413,0.37046740,0.37583740,0.38128525,0.38681205,0.39241898,0.39810717,0.40387782,0.40973211,0.41567126,0.42169650,0.42780908,0.43401026,0.44030133,0.44668359,
0.44990933,0.45315836,0.45643086,0.45972699,0.46304692,0.46639083,0.46975888,0.47315126,0.47656813,0.48000968,0.48347609,0.48696753,0.49048418,0.49402622,0.49759385,0.50118723,
0.50626161,0.51138737,0.51359998,0.51582217,0.51954719,0.52329911,0.52707813,0.53088444,0.53471824,0.53857972,0.54246909,0.54638655,0.55033230,0.55430654,0.55830948,0.56234133,
0.56640229,0.57049258,0.57461241,0.57876199,0.58294153,0.58715126,0.59139139,0.59566214,0.59996373,0.60429639,0.60866033,0.61305579,0.61748299,0.62194216,0.62643354,0.63095734,
0.63551382,0.64010320,0.64472573,0.64938163,0.65219130,0.65501312,0.66164495,0.66834392,0.67511071,0.68194602,0.68489658,0.68785991,0.69282731,0.69783058,0.70286999,0.70794578,
0.71511354,0.72235386,0.72547926,0.72861817,0.73387991,0.73917965,0.74451765,0.74989421,0.75530959,0.76076408,0.76625796,0.77179152,0.77736503,0.78297879,0.78863310,0.79432823,
0.79719121,0.80006450,0.80294815,0.80584219,0.80874666,0.81166160,0.81458705,0.81752304,0.82046961,0.82342680,0.82639466,0.82937320,0.83236249,0.83536255,0.83837342,0.84139514,
0.84442776,0.84747130,0.85052582,0.85359134,0.85666791,0.85975557,0.86285436,0.86596432,0.86908549,0.87221791,0.87536162,0.87851666,0.88168307,0.88486089,0.88805017,0.89125094,
0.89446325,0.89768713,0.90092264,0.90416981,0.90742868,0.91069929,0.91398170,0.91727594,0.92058204,0.92390007,0.92723005,0.93057204,0.93392607,0.93729219,0.94067045,0.94406088,
0.94746353,0.95087844,0.95430566,0.95774524,0.96119721,0.96466162,0.96813852,0.97162795,0.97512996,0.97864459,0.98217189,0.98571190,0.98926467,0.99283025,0.99640868,1.00000000
];
function calculateExternalMixerVolume(deckIndex)
{
var crossFader = Planck.getProperty("/Engine/Mixer/CrossfaderPosition").translator.value;
var deckVolume = Planck.getProperty("/Engine/Deck%1/DeckFaderVolume".arg(deckIndex)).translator.value;
var externalVolume
if(deckThru[deckIndex - 1] === 1) {
externalVolume = deckVolume
} else {
var deckIdx = parseInt(deckIndex, 10)
if(deckIdx === 1 || deckIdx === 3) {
if(deckThru[deckIdx - 1] === 2) {
crossFader = 1 - crossFader
}
} else if(deckIdx === 2 || deckIdx === 4) {
if(deckThru[deckIdx - 1] === 0) {
crossFader = 1 - crossFader
}
}
var faderIndex = crossFader * 511;
if (deckIdx === 1 || deckIdx === 3)
{
faderIndex = 511 - faderIndex;
}
if (faderIndex > 255)
{
faderIndex = 255;
}
var crossFaderLow = crossFaderTable[Math.floor(faderIndex)]
var crossFaderHigh = crossFaderTable[Math.ceil(faderIndex)]
var interpolatedTableValue = crossFaderLow + ((crossFaderHigh - crossFaderLow) * (crossFader - Math.floor(crossFader)));
externalVolume = deckVolume * interpolatedTableValue;
}
Planck.getProperty("/Engine/Deck%1/ExternalMixerVolume".arg(deckIndex)).translator.setValue(externalVolume);
}
ValueCCAssignment {
id: xfaderStartStop
objectName: "Cross Fader Start/Stop"
cc: 14
channel: 15
enabled: xFaderStartLeft || xFaderStartRight
property QObjProperty pCrossFaderPosition: Planck.getProperty("/Engine/Mixer/CrossfaderPosition")
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
xfaderStartStop.pCrossFaderPosition.translator.value = value
if(assignmentEnabled) {
for (var i = 0; i < deckThru.length; i++) {
calculateExternalMixerVolume(i+1);
var deckIndex = "%1".arg(i+1)
var deckIsActive = activeDecks.indexOf(deckIndex) !== -1
if(xFaderStartLeft && deckIsActive && deckThru[i] === 0) {
if (value === 1)
Planck.getProperty("/Engine/Deck%1/RemoteStop".arg(deckIndex)).translator.state = true
else
Planck.getProperty("/Engine/Deck%1/RemoteStart".arg(deckIndex)).translator.state = true
}
if (xFaderStartRight && deckIsActive && deckThru[i] === 2) {
if (value === 0)
Planck.getProperty("/Engine/Deck%1/RemoteStop".arg(deckIndex)).translator.state = true
else
Planck.getProperty("/Engine/Deck%1/RemoteStart".arg(deckIndex)).translator.state = true
}
}
} else {
for (var index = 1; index < deckThru.length + 1; index++) {
calculateExternalMixerVolume(index);
}
}
}
}
}
Repeater {
model: parseInt(Planck.getProperty("/Configuration/NumberOfDecks").translator.string)
Item {
property string deckIndex: "%1".arg(index+1)
ValueCCAssignment {
objectName: "Channel Fader %1".arg(deckIndex)
cc: 14
channel: index
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
Planck.getProperty("/Engine/Deck%1/DeckFaderVolume".arg(deckIndex)).translator.setValue(value);
calculateExternalMixerVolume(deckIndex);
}
}
}
ValueNoteAssignment {
objectName: "Thru %1".arg(deckIndex)
note: 15
channel: index
normalizeValue: false
output: QtObject {
function setValue(channel, value, assignmentEnabled) {
deckThru[channel] = value
calculateExternalMixerVolume(deckIndex)
}
}
}
}
}
}
And what it gives :
I'm in slicer mode, on the 2 decks.
{{http://dnttalo.cluster029.hosting.ovh.net/Pictures/Prime4Slicer.jpg}}