User Tools

Site Tools


prime4

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
prime4 [2023/09/10 21:08] – created ounsatnprime4 [2023/09/11 23:01] (current) ounsatn
Line 5: Line 5:
 To use prime 4, you have to change your  productcode to JC11 when binding.  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") (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 : 
 +
 +<code>
 +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"
 +</code>
 +
 +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 .** 
 +
 +<code>
 +
 +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)
 + }
 + }
 + }
 +}
 +
 +</code>
 +
 +
 +
 +<code>
 +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]
 + }
 +*/
 +
 +</code>
 +
 +**_Public_ files are used to lit leds / pads.** 
 +
 +<code>
 +
 +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<Item> 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<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[6] === 0x42)
 + {
 + if(valueList[9] === 0x0) {
 + console.log("Power on self request: No special power on request")
 + }
 + else if(valueList[9] === 0x1) {
 + console.log("Power on self request: Request test-mode entry")
 + quitToTestApp()
 + }
 + }
 + else if(valueList[1] === 0x7E && valueList[2] === 0x00 && valueList[3] === 0x06 && valueList[4] === 0x02)
 + {
 + for(var i=0;i<4;i++) {
 + result += valueList[i + 11];
 + if(i == 1)
 + result += ".";
 + }
 + deviceInfo = result;
 + }
 + }
 +}
 +
 +</code>
 +
 +
 +}
 +
 +</code>
 +
 +
 +
 +<code>
 +
 +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 ",
 + " 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)
 + }
 + }
 + }
 + }
 + }
 +}
 +
 +</code>
 +
 +
 +And what it gives : 
 +I'm in slicer mode, on the 2 decks. 
 +
 +
 +{{http://dnttalo.cluster029.hosting.ovh.net/Pictures/Prime4Slicer.jpg}}
 +
 +
 +
  
  
prime4.1694372923.txt.gz · Last modified: 2023/09/10 21:08 by ounsatn

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki