diff --git a/res/controllers/Dummy-Device-Default-Screen/OnAirTrack.qml b/res/controllers/Dummy-Device-Default-Screen/OnAirTrack.qml
index c24e5905ef84..fea5b11c33bd 100644
--- a/res/controllers/Dummy-Device-Default-Screen/OnAirTrack.qml
+++ b/res/controllers/Dummy-Device-Default-Screen/OnAirTrack.qml
@@ -85,7 +85,7 @@ Item {
onTriggered: {
if (status == OnAirTrack.TimerStatus.Cooldown) {
status += backward ? -1 : 1
- interval = 15
+ interval = 10
}
frame.x -= backward ? -1 : 1;
if (-frame.x >= (text.text.length - 29) * 11) {
diff --git a/res/controllers/Traktor Kontrol S4 MK3.bulk.xml b/res/controllers/Traktor Kontrol S4 MK3.bulk.xml
index 9a8acc57c997..e1b0fac94089 100644
--- a/res/controllers/Traktor Kontrol S4 MK3.bulk.xml
+++ b/res/controllers/Traktor Kontrol S4 MK3.bulk.xml
@@ -3,15 +3,15 @@
S4 Mk3 screens
A. Colombier
- Dummy device screens
+ Test device screens
-
-
+
+
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/BPMIndicator.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/BPMIndicator.qml
index 68164539af6b..aa40454f349a 100755
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/BPMIndicator.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/BPMIndicator.qml
@@ -17,9 +17,11 @@ Rectangle {
border.color: smallBoxBorder
border.width: 2
+ signal updated
+
Text {
- id: inidcator
- text: value.toFixed(2)
+ id: indicator
+ text: "-"
font.pixelSize: 17
color: fontColor
anchors.centerIn: parent
@@ -28,7 +30,10 @@ Rectangle {
group: root.group
key: "bpm"
onValueChanged: (value) => {
- inidcator.text = value.toFixed(2);
+ const newValue = value.toFixed(2);
+ if (newValue === indicator.text) return;
+ indicator.text = newValue;
+ root.updated()
}
}
}
@@ -46,7 +51,10 @@ Rectangle {
group: root.group
key: "rateRange"
onValueChanged: (value) => {
- range.text = `-/+ \n${(value * 100).toFixed()}%`;
+ const newValue = `-/+ \n${(value * 100).toFixed()}%`;
+ if (range.text === newValue) return;
+ range.text = newValue;
+ root.updated();
}
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/CustomCheckBox.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/CustomCheckBox.qml
deleted file mode 100755
index 14444a825274..000000000000
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/CustomCheckBox.qml
+++ /dev/null
@@ -1,43 +0,0 @@
-import QtQuick 2.9
-import QtQuick.Controls 2.14
-// import QtQuick.Controls.Styles 1.4
-import QtGraphicalEffects 1.0
-
-CheckBox {
- id: checkbox
- checked: false
-
- property int box_width: 10
- property int box_height: box_width
- property int box_radius: 3
- property int border_width: 1
- property color box_color: "white"
- property color tick_color: "black"
- property color border_color: "black"
- property double tick_size_factor: 0.75
- property url tick_image: "qrc:/resources/accept.svg"
-
- width: box_width
-
- indicator: Rectangle {
- anchors.verticalCenter: parent.verticalCenter
- implicitWidth: box_width
- implicitHeight: box_height
- radius: box_radius
- color: box_color
- border.color: border_color
- border.width: border_width
- Image {
- visible: checkbox.checked
- anchors.centerIn: parent
- width: parent.width * tick_size_factor
- fillMode: Image.PreserveAspectFit
- source: tick_image
- ColorOverlay {
- anchors.fill: parent
- source: parent
- color: tick_color
- }
- }
- }
-}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/KeyIndicator.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/KeyIndicator.qml
index 679c8c8b59bd..f5680d8ffac0 100755
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/KeyIndicator.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/KeyIndicator.qml
@@ -102,12 +102,15 @@ Rectangle {
border.width: 2
color: colorsMap[key]
+ signal updated
Mixxx.ControlProxy {
group: root.group
key: "key"
onValueChanged: (value) => {
+ if (value === root.key) return;
root.key = value;
+ root.updated()
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Keyboard.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Keyboard.qml
index 9e5bae8eee5f..d8da78c4794c 100644
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Keyboard.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Keyboard.qml
@@ -14,11 +14,15 @@ Item {
property int key: -1
+ signal updated
+
Mixxx.ControlProxy {
group: root.group
key: "key"
onValueChanged: (value) => {
+ if (value === root.key) return;
root.key = value;
+ root.updated()
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/LoopSizeIndicator.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/LoopSizeIndicator.qml
index f78925af50f2..9c252df8d5b4 100755
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/LoopSizeIndicator.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/LoopSizeIndicator.qml
@@ -15,6 +15,7 @@ Rectangle {
property color loopOnFontColor: "black"
property bool on: true
+ signal updated
radius: 6
border.width: 2
@@ -31,7 +32,10 @@ Rectangle {
group: root.group
key: "beatloop_size"
onValueChanged: (value) => {
- indicator.text = (value < 1 ? `1/${1 / value}` : `${value}`);
+ const newValue = (value < 1 ? `1/${1 / value}` : `${value}`);
+ if (newValue === indicator.text) return;
+ indicator.text = newValue;
+ root.updated()
}
}
}
@@ -40,7 +44,9 @@ Rectangle {
group: root.group
key: "loop_enabled"
onValueChanged: (value) => {
+ if (value === root.on) return;
root.on = value;
+ root.updated()
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/OnAirTrack.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/OnAirTrack.qml
index fea5b11c33bd..d25f51e8d827 100644
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/OnAirTrack.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/OnAirTrack.qml
@@ -16,6 +16,8 @@ Item {
Backward
}
+ signal updated
+
property string fullText
property int index
@@ -70,6 +72,7 @@ Item {
if (text.text.trim() == "-") {
text.text = trackLoadedControl.value ? `Unknown for ${root.group}` : qsTr("No Track Loaded")
}
+ root.updated()
}
Timer {
@@ -85,9 +88,10 @@ Item {
onTriggered: {
if (status == OnAirTrack.TimerStatus.Cooldown) {
status += backward ? -1 : 1
- interval = 10
+ interval = 15
}
frame.x -= backward ? -1 : 1;
+ root.updated()
if (-frame.x >= (text.text.length - 29) * 11) {
backward = true
status = OnAirTrack.TimerStatus.Cooldown
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Progression.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Progression.qml
index be6faded44b2..fd6650fe0149 100755
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Progression.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/Progression.qml
@@ -12,12 +12,15 @@ Item {
property real windowWidth: Window.width
width: 0
+ signal updated
Mixxx.ControlProxy {
group: root.group
key: "track_loaded"
onValueChanged: (value) => {
+ if (value === root.visible) return;
root.visible = value
+ root.updated()
}
}
@@ -25,7 +28,10 @@ Item {
group: root.group
key: "playposition"
onValueChanged: (value) => {
- root.width = value * (320 - 12);
+ const newValue = value * (320 - 12);
+ if (newValue === root.width) return;
+ root.width = newValue;
+ root.updated()
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/TimeAndBeatloopIndicator.qml b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/TimeAndBeatloopIndicator.qml
index dcbe0e545722..1d4fa7f96c9f 100755
--- a/res/controllers/Traktor-Kontrol-S4-MK3-Screens/TimeAndBeatloopIndicator.qml
+++ b/res/controllers/Traktor-Kontrol-S4-MK3-Screens/TimeAndBeatloopIndicator.qml
@@ -23,6 +23,7 @@ Rectangle {
border.color: timeColor
border.width: 2
color: timeColor
+ signal updated
function update() {
if (root.mode === TimeAndBeatloopIndicator.Mode.RemainingTime) {
@@ -34,6 +35,7 @@ Rectangle {
} else {
indicator.text = (beatjump.value < 1 ? `1/${1 / beatjump.value}` : `${beatjump.value}`);
}
+ root.updated()
}
Text {
@@ -69,6 +71,7 @@ Rectangle {
onValueChanged: (value) => {
root.border.color = value ? 'red' : timeColor
root.color = value ? 'red' : timeColor
+ root.updated()
}
}
}
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3.js b/res/controllers/Traktor-Kontrol-S4-MK3.js
index b8fbd1f1960f..c7dee6816b2a 100644
--- a/res/controllers/Traktor-Kontrol-S4-MK3.js
+++ b/res/controllers/Traktor-Kontrol-S4-MK3.js
@@ -2494,7 +2494,7 @@ class S4Mk3Deck extends Deck {
const wheelOutput = new Uint8Array(40).fill(0);
wheelOutput[0] = decks[0] - 1;
- if (engine.getValue(this.group, "end_of_track") && WheelLedBlinkOnTrackEnd) {
+ if (engine.getValue(this.group, "end_of_track") && WheelLedBlinkOnTrackEnd && fractionOfTrack < 1) {
wheelOutput[1] = wheelLEDmodes.ringFlash;
} else {
wheelOutput[1] = wheelLEDmodes.spot;
diff --git a/res/controllers/TraktorKontrolS4MK3Screens.qml b/res/controllers/TraktorKontrolS4MK3Screens.qml
index 9a24a9660f9f..e731c1a92ec6 100755
--- a/res/controllers/TraktorKontrolS4MK3Screens.qml
+++ b/res/controllers/TraktorKontrolS4MK3Screens.qml
@@ -15,7 +15,7 @@ import Mixxx.Controls 1.0 as MixxxControls
// Import other project QML scripts
import "Traktor-Kontrol-S4-MK3-Screens" as S4MK3
-Rectangle {
+Item {
id: root
required property string screenId
@@ -25,539 +25,661 @@ Rectangle {
property string group: "[Channel1]"
property var deckPlayer: Mixxx.PlayerManager.getPlayer(root.group)
- color: "black"
- antialiasing: true
+ property var _items_needing_redraw: new Set()
+
+ Timer {
+ id: timer
+ }
+ function setTimeout(cb, delayTime) {
+ timer.interval = delayTime;
+ timer.repeat = false;
+ timer.triggered.connect(cb);
+ timer.start();
+ }
function init(controlerName, isDebug) {
console.log(`Screen ${root.screenId} has started`)
- // root.stat = DummyDeviceDefaultScreen.Stat.Running
loader.sourceComponent = live
- root.group = root.screenId === "rightdeck" ? "[Channel2]" : "[Channel1]"
+ group = screenId === "rightdeck" ? "[Channel2]" : "[Channel1]"
onAir.update()
}
function shutdown() {
console.log(`Screen ${root.screenId} is stopping`)
loader.sourceComponent = splashoff
- // root.stat = DummyDeviceDefaultScreen.Stat.Stopping
}
function transformFrame(input) {
- const outputData = new ArrayBuffer(320*240*2 + 20);
- const header = new Uint8Array(outputData, 0, 16);
- const payload = new Uint8Array(outputData, 16, 320*240*2);
- const footer = new Uint8Array(outputData, 320*240*2 + 16, 4);
- header.fill(0)
- footer.fill(0)
- header[0] = 0x84;
- header[2] = root.screenId === "leftdeck" ? 0 : 1;
- header[3] = 0x21;
- header[12] = 0x1;
- header[13] = 0x40;
- header[15] = 0xf0;
- payload.set(new Uint8Array(input, 0, 320*240*2))
- footer[0] = 0x40;
- // const debug = new Uint8Array(outputData, 0, 32);
- // console.log(input.slice(0, 32));
+ const areasToDraw = []
+ let totalPixelToDraw = 0;
+
+ if (!_items_needing_redraw.size) { // No redraw needed
+ return new ArrayBuffer(0);
+ } else if (_items_needing_redraw.has(root)) { // Full redraw needed
+ areasToDraw.push([0, 0, 320, 240]);
+ totalPixelToDraw += 320 * 240;
+ } else { // Partial redraw needed
+ _items_needing_redraw.forEach(function(item) {
+ const pos = item.mapToGlobal(0, 0)
+ let x = pos.x, y = pos.y, width = item.width, height = item.height;
+ areasToDraw.push([pos.x, pos.y, item.width, item.height])
+ totalPixelToDraw += item.width * item.height;
+ });
+ // Note: Some area could overlap and this could be optimised, but the cost of checking that every time vs.
+ // the optimisation for when it is happening is likely not worth it
+ }
+ _items_needing_redraw.clear()
+
+ // console.log(`Redrawing ${totalPixelToDraw} the following region: ${areasToDraw}`)
+
+ const screenIdx = screenId === "leftdeck" ? 0 : 1;
+
+ const outputData = new ArrayBuffer(totalPixelToDraw*2 + 20*areasToDraw.length);
+ let offset = 0;
+
+ for (const area of areasToDraw) {
+ const [x, y, width, height] = area;
+ const header = new Uint8Array(outputData, offset, 16);
+ const payload = new Uint8Array(outputData, offset + 16, width*height*2);
+ const footer = new Uint8Array(outputData, offset + width*height*2 + 16, 4);
+
+ header.fill(0)
+ footer.fill(0)
+ header[0] = 0x84;
+ header[2] = screenIdx;
+ header[3] = 0x21;
+
+ header[8] = x >> 8;
+ header[9] = x & 0xff;
+ header[10] = y >> 8;
+ header[11] = y & 0xff;
+
+ header[12] = width >> 8;
+ header[13] = width & 0xff;
+ header[14] = height >> 8;
+ header[15] = height & 0xff;
+
+ if (x === 0 && width === 320) {
+ payload.set(new Uint8Array(input, y * 320 * 2, width*height*2));
+ } else {
+ for (let line = 0; line < height; line++) {
+ payload.set(new Uint8Array(input, ((y + line) * 320 + x) * 2, width*2), line*width*2);
+ }
+ }
+ footer[0] = 0x40;
+ footer[2] = screenIdx;
+ offset += width*height*2 + 20
+ }
+ // console.log(`Generated ${offset} bytes to be sent`)
return outputData;
}
- Item {
- anchors.fill: parent
+ Mixxx.ControlProxy {
+ id: trackLoadedControl
- Component {
- id: splashoff
- Rectangle {
- anchors.fill: parent
- color: "black"
-
- Image {
- anchors.centerIn: parent
- width: root.width*0.8
- height: root.height
- fillMode: Image.PreserveAspectFit
- source: "../images/templates/logo_mixxx.png" // loads cat.png
- }
+ group: root.group
+ key: "track_loaded"
+
+ onValueChanged: (value) => {
+ _items_needing_redraw.add(root)
+ }
+ }
+
+ Component {
+ id: splashoff
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+
+ Image {
+ anchors.centerIn: parent
+ width: root.width*0.8
+ height: root.height
+ fillMode: Image.PreserveAspectFit
+ source: "../images/templates/logo_mixxx.png" // loads cat.png
}
}
- Component {
- id: live
+ }
+ Component {
+ id: live
- Rectangle {
- anchors.fill: parent
- color: "black"
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
- function onRuntimeDataUpdate(data) {
- console.log(`Received data on screen#${root.screenId} while currently bind to ${root.group}: ${JSON.stringify(data)}`);
- if (typeof data === "object" && typeof data.group === "object" && data.group.length === 2 && typeof data.group[root.screenId] === "string" && root.group !== data.group[root.screenId]) {
- root.group = data.group[root.screenId]
- console.log(`Changed group for screen ${root.screenId} to ${root.group}`);
- }
- var shouldBeCompacted = false;
- if (typeof data.zoomedWaveform === "object") {
- shouldBeCompacted |= data.zoomedWaveform[root.group]
- waveformZoomed.visible = data.zoomedWaveform[root.group]
- }
- if (typeof data.keyboardMode === "object") {
- shouldBeCompacted |= data.keyboardMode[root.group]
- keyboard.visible = !!data.keyboardMode[root.group]
- }
- deckInfo.state = shouldBeCompacted ? "compacted" : ""
- if (typeof data.displayBeatloopSize === "object") {
- timeIndicator.mode = data.displayBeatloopSize[root.screenId] ? S4MK3.TimeAndBeatloopIndicator.Mode.BeetjumpSize : S4MK3.TimeAndBeatloopIndicator.Mode.RemainingTime
- timeIndicator.update()
- }
+ function onRuntimeDataUpdate(data) {
+ console.log(`Received data on screen#${root.screenId} while currently bind to ${root.group}: ${JSON.stringify(data)}`);
+ if (typeof data === "object" && typeof data.group === "object" && data.group.length === 2 && typeof data.group[root.screenId] === "string" && root.group !== data.group[root.screenId]) {
+ root.group = data.group[root.screenId]
+ console.log(`Changed group for screen ${root.screenId} to ${root.group}`);
}
-
- Component.onCompleted: {
- // engine.onRuntimeDataUpdate(onRuntimeDataUpdate)
- if (root.screenId === "rightdeck") {
- root.group = "[Channel2]"
- }
-
- onRuntimeDataUpdate({
- // zoomedWaveform: {
- // "[Channel1]": true
- // }
- })
-
- // root.deckPlayer.onWaveformLengthChanged((value) => {
- // console.warn("Changed!!")
- // })
- // root.deckPlayer.onWaveformTextureChanged((value) => {
- // console.warn("Changed!!")
- // })
- // root.deckPlayer.onWaveformTextureSizeChanged((value) => {
- // console.warn("Changed!!")
- // })
+ var shouldBeCompacted = false;
+ if (typeof data.zoomedWaveform === "object") {
+ shouldBeCompacted |= data.zoomedWaveform[root.group]
+ waveformZoomed.visible = data.zoomedWaveform[root.group]
+ }
+ if (typeof data.keyboardMode === "object") {
+ shouldBeCompacted |= data.keyboardMode[root.group]
+ keyboard.visible = !!data.keyboardMode[root.group]
+ }
+ deckInfo.state = shouldBeCompacted ? "compacted" : ""
+ if (typeof data.displayBeatloopSize === "object") {
+ timeIndicator.mode = data.displayBeatloopSize[root.screenId] ? S4MK3.TimeAndBeatloopIndicator.Mode.BeetjumpSize : S4MK3.TimeAndBeatloopIndicator.Mode.RemainingTime
+ timeIndicator.update()
}
+ }
- ColumnLayout {
- anchors.fill: parent
- anchors.leftMargin: 6
- anchors.rightMargin: 6
- anchors.topMargin: 2
- anchors.bottomMargin: 6
- spacing: 6
+ Component.onCompleted: {
+ // engine.onRuntimeDataUpdate(onRuntimeDataUpdate)
+ if (root.screenId === "rightdeck") {
+ root.group = "[Channel2]"
+ }
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 36
- color: "transparent"
+ onRuntimeDataUpdate({
+ // zoomedWaveform: {
+ // "[Channel1]": true
+ // }
+ })
+
+ // root.deckPlayer.onWaveformLengthChanged((value) => {
+ // console.warn("Changed!!")
+ // })
+ // root.deckPlayer.onWaveformTextureChanged((value) => {
+ // console.warn("Changed!!")
+ // })
+ // root.deckPlayer.onWaveformTextureSizeChanged((value) => {
+ // console.warn("Changed!!")
+ // })
+ // setTimeout(() => {root._items_needing_redraw.add(this);}, 1000);
+ }
- RowLayout {
- anchors.fill: parent
- spacing: 1
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 6
+ anchors.rightMargin: 6
+ anchors.topMargin: 2
+ anchors.bottomMargin: 6
+ spacing: 6
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 36
+ color: "transparent"
+
+ RowLayout {
+ anchors.fill: parent
+ spacing: 1
+
+ S4MK3.OnAirTrack {
+ id: onAir
+ group: root.group
+ Layout.fillWidth: true
+ Layout.fillHeight: true
- S4MK3.OnAirTrack {
- id: onAir
- group: root.group
- Layout.fillWidth: true
- Layout.fillHeight: true
+ onUpdated: {
+ root._items_needing_redraw.add(this)
}
}
}
+ }
- // Indicator
- Rectangle {
- id: deckInfo
-
- Layout.fillWidth: true
- Layout.preferredHeight: 105
- color: "transparent"
+ // Indicator
+ Rectangle {
+ id: deckInfo
- GridLayout {
- id: gridLayout
- anchors.fill: parent
- columnSpacing: 6
- rowSpacing: 6
- columns: 2
+ Layout.fillWidth: true
+ Layout.preferredHeight: 105
+ color: "transparent"
- // Section: Key
- S4MK3.KeyIndicator {
- id: keyIndicator
- group: root.group
- borderColor: smallBoxBorder
+ GridLayout {
+ id: gridLayout
+ anchors.fill: parent
+ columnSpacing: 6
+ rowSpacing: 6
+ columns: 2
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
+ // Section: Key
+ S4MK3.KeyIndicator {
+ id: keyIndicator
+ group: root.group
+ borderColor: smallBoxBorder
- // Section: Bpm
- S4MK3.BPMIndicator {
- id: bpmIndicator
- group: root.group
- borderColor: smallBoxBorder
+ Layout.fillWidth: true
+ Layout.fillHeight: true
- Layout.fillWidth: true
- Layout.fillHeight: true
+ onUpdated: {
+ root._items_needing_redraw.add(this)
}
+ }
- // Section: Key
- S4MK3.TimeAndBeatloopIndicator {
- id: timeIndicator
- group: root.group
+ // Section: Bpm
+ S4MK3.BPMIndicator {
+ id: bpmIndicator
+ group: root.group
+ borderColor: smallBoxBorder
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
- Layout.fillWidth: true
- Layout.preferredHeight: 72
- timeColor: smallBoxBorder
+ onUpdated: {
+ root._items_needing_redraw.add(this)
}
+ }
- // Section: Bpm
- S4MK3.LoopSizeIndicator {
- id: loopSizeIndicator
- group: root.group
+ // Section: Key
+ S4MK3.TimeAndBeatloopIndicator {
+ id: timeIndicator
+ group: root.group
- Layout.fillWidth: true
- Layout.preferredHeight: 72
+ Layout.fillWidth: true
+ Layout.preferredHeight: 72
+ timeColor: smallBoxBorder
+
+ onUpdated: {
+ root._items_needing_redraw.add(this)
}
}
- states: State {
- name: "compacted"
- PropertyChanges {
- target:deckInfo
- Layout.preferredHeight: 28
- }
- PropertyChanges {
- target: gridLayout
- columns: 4
- }
- PropertyChanges {
- target: bpmIndicator
- state: "compacted"
- }
- PropertyChanges {
- target: timeIndicator
- Layout.preferredHeight: -1
- Layout.fillHeight: true
- state: "compacted"
- }
- PropertyChanges {
- target: loopSizeIndicator
- Layout.preferredHeight: -1
- Layout.fillHeight: true
- state: "compacted"
+ // Section: Bpm
+ S4MK3.LoopSizeIndicator {
+ id: loopSizeIndicator
+ group: root.group
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 72
+
+ onUpdated: {
+ root._items_needing_redraw.add(this)
}
}
}
+ states: State {
+ name: "compacted"
- // Track progress
- Item {
- // Layout.preferredHeight: 40
- Layout.fillWidth: true
- Layout.fillHeight: true
- layer.enabled: true
+ PropertyChanges {
+ target:deckInfo
+ Layout.preferredHeight: 28
+ }
+ PropertyChanges {
+ target: gridLayout
+ columns: 4
+ }
+ PropertyChanges {
+ target: bpmIndicator
+ state: "compacted"
+ }
+ PropertyChanges {
+ target: timeIndicator
+ Layout.preferredHeight: -1
+ Layout.fillHeight: true
+ state: "compacted"
+ }
+ PropertyChanges {
+ target: loopSizeIndicator
+ Layout.preferredHeight: -1
+ Layout.fillHeight: true
+ state: "compacted"
+ }
+ }
- S4MK3.Progression {
- id: progression
- group: root.group
+ onStateChanged: {
+ root._items_needing_redraw.add(root)
+ }
+ }
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.bottom: parent.bottom
- }
+ // Track progress
+ Item {
+ // Layout.preferredHeight: 40
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ layer.enabled: true
- MixxxControls.WaveformOverview {
- id: waveform
- group: root.group
- anchors.fill: parent
- channels: Mixxx.WaveformOverview.Channels.BothChannels
- renderer: Mixxx.WaveformOverview.Renderer.RGB
- colorHigh: 'white'
- colorMid: 'blue'
- colorLow: 'green'
- }
+ S4MK3.Progression {
+ id: progression
+ group: root.group
- // Hotcue
- Repeater {
- model: 8
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
- S4MK3.HotcuePoint {
- required property int index
+ onUpdated: {
+ root._items_needing_redraw.add(waveform)
+ }
+ }
- Mixxx.ControlProxy {
- id: samplesControl
+ MixxxControls.WaveformOverview {
+ id: waveform
+ group: root.group
+ anchors.fill: parent
+ channels: Mixxx.WaveformOverview.Channels.BothChannels
+ renderer: Mixxx.WaveformOverview.Renderer.RGB
+ colorHigh: 'white'
+ colorMid: 'blue'
+ colorLow: 'green'
+ }
- group: root.group
- key: "track_samples"
- }
+ // Hotcue
+ Repeater {
+ model: 8
- Mixxx.ControlProxy {
- id: hotcueEnabled
- group: root.group
- key: `hotcue_${index}_status`
- }
+ S4MK3.HotcuePoint {
+ required property int index
- Mixxx.ControlProxy {
- id: hotcuePosition
- group: root.group
- key: `hotcue_${index}_position`
- }
+ Mixxx.ControlProxy {
+ id: samplesControl
- anchors.top: parent.top
- // anchors.left: parent.left
- anchors.bottom: parent.bottom
- visible: hotcueEnabled.value
+ group: root.group
+ key: "track_samples"
- number: this.index
- type: S4MK3.HotcuePoint.Type.OneShot
- position: hotcuePosition.value / samplesControl.value
+ // onValueChanged: (value) => {
+ // _items_needing_redraw.add(parent)
+ // }
}
- }
-
- // Intro
- S4MK3.HotcuePoint {
Mixxx.ControlProxy {
- id: introStartEnabled
+ id: hotcueEnabled
group: root.group
- key: `intro_start_enabled`
- onValueChanged: (value) => {
- console.log(value);
- }
+ key: `hotcue_${index}_status`
+
+ // onValueChanged: (value) => {
+ // _items_needing_redraw.add(parent)
+ // }
}
Mixxx.ControlProxy {
- id: introStartPosition
+ id: hotcuePosition
group: root.group
- key: `intro_start_position`
- onValueChanged: (value) => {
- console.log(value);
- }
+ key: `hotcue_${index}_position`
+
+ // onValueChanged: (value) => {
+ // _items_needing_redraw.add(parent)
+ // }
}
anchors.top: parent.top
+ // anchors.left: parent.left
anchors.bottom: parent.bottom
- visible: introStartEnabled.value
+ visible: hotcueEnabled.value
- type: S4MK3.HotcuePoint.Type.IntroIn
- position: introStartPosition.value / samplesControl.value
+ number: this.index
+ type: S4MK3.HotcuePoint.Type.OneShot
+ position: hotcuePosition.value / samplesControl.value
}
+ }
- // Extro
- S4MK3.HotcuePoint {
+ // Intro
+ S4MK3.HotcuePoint {
- Mixxx.ControlProxy {
- id: introEndEnabled
- group: root.group
- key: `intro_end_enabled`
- }
+ Mixxx.ControlProxy {
+ id: introStartEnabled
+ group: root.group
+ key: `intro_start_enabled`
- Mixxx.ControlProxy {
- id: introEndPosition
- group: root.group
- key: `intro_end_position`
- }
+ // onValueChanged: (value) => {
+ // _items_needing_redraw.add(parent)
+ // }
+ }
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- visible: introEndEnabled.value
+ Mixxx.ControlProxy {
+ id: introStartPosition
+ group: root.group
+ key: `intro_start_position`
- type: S4MK3.HotcuePoint.Type.IntroOut
- position: introEndPosition.value / samplesControl.value
+ // onValueChanged: (value) => {
+ // _items_needing_redraw.add(parent)
+ // }
}
- // Loop in
- S4MK3.HotcuePoint {
- Mixxx.ControlProxy {
- id: loopStartPosition
- group: root.group
- key: `loop_start_position`
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: introStartEnabled.value
+
+ type: S4MK3.HotcuePoint.Type.IntroIn
+ position: introStartPosition.value / samplesControl.value
+ }
+
+ // Extro
+ S4MK3.HotcuePoint {
+
+ Mixxx.ControlProxy {
+ id: introEndEnabled
+ group: root.group
+ key: `intro_end_enabled`
+
+ onValueChanged: (value) => {
+ _items_needing_redraw.add(parent)
}
+ }
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- visible: loopStartPosition.value > 0
+ Mixxx.ControlProxy {
+ id: introEndPosition
+ group: root.group
+ key: `intro_end_position`
- type: S4MK3.HotcuePoint.Type.LoopIn
- position: loopStartPosition.value / samplesControl.value
+ onValueChanged: (value) => {
+ _items_needing_redraw.add(parent)
+ }
}
- // Loop out
- S4MK3.HotcuePoint {
- Mixxx.ControlProxy {
- id: loopEndPosition
- group: root.group
- key: `loop_end_position`
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: introEndEnabled.value
+
+ type: S4MK3.HotcuePoint.Type.IntroOut
+ position: introEndPosition.value / samplesControl.value
+ }
+
+ // Loop in
+ S4MK3.HotcuePoint {
+ Mixxx.ControlProxy {
+ id: loopStartPosition
+ group: root.group
+ key: `loop_start_position`
+
+ onValueChanged: (value) => {
+ _items_needing_redraw.add(parent)
}
+ }
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- visible: loopEndPosition.value > 0
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: loopStartPosition.value > 0
+
+ type: S4MK3.HotcuePoint.Type.LoopIn
+ position: loopStartPosition.value / samplesControl.value
+ }
+
+ // Loop out
+ S4MK3.HotcuePoint {
+ Mixxx.ControlProxy {
+ id: loopEndPosition
+ group: root.group
+ key: `loop_end_position`
- type: S4MK3.HotcuePoint.Type.LoopOut
- position: loopEndPosition.value / samplesControl.value
+ onValueChanged: (value) => {
+ _items_needing_redraw.add(parent)
+ }
}
- Item {
- id: waveformZoomed
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: loopEndPosition.value > 0
- property var group: root.group
- property real scale: 0.2
+ type: S4MK3.HotcuePoint.Type.LoopOut
+ position: loopEndPosition.value / samplesControl.value
+ }
- visible: false
- antialiasing: true
- anchors.fill: parent
+ Item {
+ id: waveformZoomed
- // MixxxControls.WaveformDisplay {
- // anchors.fill: parent
- // group: waveformZoomed.group
- // scale: waveformZoomed.scale
- // }
+ property var group: root.group
+ property real scale: 0.2
- Rectangle {
- color: "white"
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- x: 153
- width: 2
- }
- Item {
- id: waveformContainer
+ visible: false
+ antialiasing: true
+ anchors.fill: parent
- property real duration: samplesControl.value / sampleRateControl.value
+ // MixxxControls.WaveformDisplay {
+ // anchors.fill: parent
+ // group: waveformZoomed.group
+ // scale: waveformZoomed.scale
+ // }
- anchors.fill: parent
- clip: true
+ Rectangle {
+ color: "white"
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ x: 153
+ width: 2
+ }
+ Item {
+ id: waveformContainer
- Mixxx.ControlProxy {
- id: samplesControl
+ property real duration: samplesControl.value / sampleRateControl.value
- group: root.group
- key: "track_samples"
- }
+ anchors.fill: parent
+ clip: true
- Mixxx.ControlProxy {
- id: sampleRateControl
+ Mixxx.ControlProxy {
+ id: samplesControl
- group: root.group
- key: "track_samplerate"
- }
+ group: root.group
+ key: "track_samples"
+ }
- Mixxx.ControlProxy {
- id: playPositionControl
+ Mixxx.ControlProxy {
+ id: sampleRateControl
- group: root.group
- key: "playposition"
- }
+ group: root.group
+ key: "track_samplerate"
+ }
- Mixxx.ControlProxy {
- id: rateRatioControl
+ Mixxx.ControlProxy {
+ id: playPositionControl
- group: root.group
- key: "rate_ratio"
- }
+ group: root.group
+ key: "playposition"
+ }
- Mixxx.ControlProxy {
- id: zoomControl
+ Mixxx.ControlProxy {
+ id: rateRatioControl
- group: root.group
- key: "waveform_zoom"
- }
+ group: root.group
+ key: "rate_ratio"
+ }
- Item {
- id: waveformBeat
+ Mixxx.ControlProxy {
+ id: zoomControl
+
+ group: root.group
+ key: "waveform_zoom"
+ }
+
+ Item {
+ id: waveformBeat
- property real effectiveZoomFactor: (zoomControl.value * rateRatioControl.value / waveformZoomed.scale) * 6
+ property real effectiveZoomFactor: (zoomControl.value * rateRatioControl.value / waveformZoomed.scale) * 6
- width: waveformContainer.duration * effectiveZoomFactor
- height: parent.height
- x: 0.5 * waveformContainer.width - playPositionControl.value * width
- visible: true
+ width: waveformContainer.duration * effectiveZoomFactor
+ height: parent.height
+ x: 0.5 * waveformContainer.width - playPositionControl.value * width
+ visible: true
- Shape {
- id: preroll
+ Shape {
+ id: preroll
- property real triangleHeight: waveformBeat.height
- property real triangleWidth: 0.25 * waveformBeat.effectiveZoomFactor
- property int numTriangles: Math.ceil(width / triangleWidth)
+ property real triangleHeight: waveformBeat.height
+ property real triangleWidth: 0.25 * waveformBeat.effectiveZoomFactor
+ property int numTriangles: Math.ceil(width / triangleWidth)
- anchors.top: waveformBeat.top
- anchors.right: waveformBeat.left
- width: Math.max(0, waveformBeat.x)
- height: waveformBeat.height
+ anchors.top: waveformBeat.top
+ anchors.right: waveformBeat.left
+ width: Math.max(0, waveformBeat.x)
+ height: waveformBeat.height
- ShapePath {
- strokeColor: 'red'
- strokeWidth: 1
- fillColor: "transparent"
+ ShapePath {
+ strokeColor: 'red'
+ strokeWidth: 1
+ fillColor: "transparent"
- PathMultiline {
- paths: {
- let p = [];
- for (let i = 0; i < preroll.numTriangles; i++) {
- p.push([Qt.point(preroll.width - i * preroll.triangleWidth, preroll.triangleHeight / 2), Qt.point(preroll.width - (i + 1) * preroll.triangleWidth, 0), Qt.point(preroll.width - (i + 1) * preroll.triangleWidth, preroll.triangleHeight), Qt.point(preroll.width - i * preroll.triangleWidth, preroll.triangleHeight / 2)]);
- }
- return p;
+ PathMultiline {
+ paths: {
+ let p = [];
+ for (let i = 0; i < preroll.numTriangles; i++) {
+ p.push([Qt.point(preroll.width - i * preroll.triangleWidth, preroll.triangleHeight / 2), Qt.point(preroll.width - (i + 1) * preroll.triangleWidth, 0), Qt.point(preroll.width - (i + 1) * preroll.triangleWidth, preroll.triangleHeight), Qt.point(preroll.width - i * preroll.triangleWidth, preroll.triangleHeight / 2)]);
}
+ return p;
}
}
}
+ }
+
+ Shape {
+ id: postroll
+
+ property real triangleHeight: waveformBeat.height
+ property real triangleWidth: 0.25 * waveformBeat.effectiveZoomFactor
+ property int numTriangles: Math.ceil(width / triangleWidth)
- Shape {
- id: postroll
-
- property real triangleHeight: waveformBeat.height
- property real triangleWidth: 0.25 * waveformBeat.effectiveZoomFactor
- property int numTriangles: Math.ceil(width / triangleWidth)
-
- anchors.top: waveformBeat.top
- anchors.left: waveformBeat.right
- width: waveformContainer.width / 2
- height: waveformBeat.height
-
- ShapePath {
- strokeColor: 'red'
- strokeWidth: 1
- fillColor: "transparent"
-
- PathMultiline {
- paths: {
- let p = [];
- for (let i = 0; i < postroll.numTriangles; i++) {
- p.push([Qt.point(i * postroll.triangleWidth, postroll.triangleHeight / 2), Qt.point((i + 1) * postroll.triangleWidth, 0), Qt.point((i + 1) * postroll.triangleWidth, postroll.triangleHeight), Qt.point(i * postroll.triangleWidth, postroll.triangleHeight / 2)]);
- }
- return p;
+ anchors.top: waveformBeat.top
+ anchors.left: waveformBeat.right
+ width: waveformContainer.width / 2
+ height: waveformBeat.height
+
+ ShapePath {
+ strokeColor: 'red'
+ strokeWidth: 1
+ fillColor: "transparent"
+
+ PathMultiline {
+ paths: {
+ let p = [];
+ for (let i = 0; i < postroll.numTriangles; i++) {
+ p.push([Qt.point(i * postroll.triangleWidth, postroll.triangleHeight / 2), Qt.point((i + 1) * postroll.triangleWidth, 0), Qt.point((i + 1) * postroll.triangleWidth, postroll.triangleHeight), Qt.point(i * postroll.triangleWidth, postroll.triangleHeight / 2)]);
}
+ return p;
}
}
}
}
+ }
- MixxxControls.WaveformOverview {
- property real duration: samplesControl.value / sampleRateControl.value
+ MixxxControls.WaveformOverview {
+ property real duration: samplesControl.value / sampleRateControl.value
- // anchors.fill: parent
- // clip: true
+ // anchors.fill: parent
+ // clip: true
- group: root.group
- anchors.fill: parent
- channels: Mixxx.WaveformOverview.Channels.BothChannels
- renderer: Mixxx.WaveformOverview.Renderer.RGB
- colorHigh: 'white'
- colorMid: 'blue'
- colorLow: 'green'
- }
+ group: root.group
+ anchors.fill: parent
+ channels: Mixxx.WaveformOverview.Channels.BothChannels
+ renderer: Mixxx.WaveformOverview.Renderer.RGB
+ colorHigh: 'white'
+ colorMid: 'blue'
+ colorLow: 'green'
}
}
}
+ }
- // spacer item
- S4MK3.Keyboard {
- id: keyboard
- group: root.group
- visible: false
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
+ // spacer item
+ S4MK3.Keyboard {
+ id: keyboard
+ group: root.group
+ visible: false
+ Layout.fillWidth: true
+ Layout.fillHeight: true
}
}
}
- Loader {
- id: loader
- anchors.fill: parent
- sourceComponent: live
+ }
+
+ Loader {
+ id: loader
+ anchors.fill: parent
+ sourceComponent: live
+ onLoaded: {
+ setTimeout(() => _items_needing_redraw.add(root), 100);
}
}
}
diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp
index 5bb4e54183bd..9bb97c8b4a06 100644
--- a/src/controllers/controller.cpp
+++ b/src/controllers/controller.cpp
@@ -8,6 +8,7 @@
#include "controllers/controllermanager.h"
#include "controllers/defs_controllers.h"
#include "moc_controller.cpp"
+#include "util/cmdlineargs.h"
#include "util/screensaver.h"
namespace {
@@ -156,7 +157,10 @@ void Controller::receive(const QByteArray& data, mixxx::Duration timestamp) {
triggerActivity();
int length = data.size();
- if (m_logInput().isDebugEnabled()) {
+ if (CmdlineArgs::Instance()
+ .getControllerDebug()) { // TODO shall we replicate this
+ // value local since it doesn
+ // change at runtime?
// Formatted packet display
QString message = QString("t:%2, %3 bytes:\n")
.arg(timestamp.formatMillisWithUnit(),
diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp
index 6c4f1631604e..df236fda5026 100644
--- a/src/controllers/dlgprefcontroller.cpp
+++ b/src/controllers/dlgprefcontroller.cpp
@@ -836,7 +836,9 @@ ControllerScreenPreview::ControllerScreenPreview(
m_screenInfo(screen),
m_pFrame(make_parented(this)),
m_pStat(make_parented("- FPS", this)),
+ m_frameDurationHistoryIdx(0),
m_lastFrameTimespamp(mixxx::Time::elapsed()) {
+ memset(m_frameDurationHistory, 0, 5);
m_pFrame->setFixedSize(screen.size);
m_pStat->setAlignment(Qt::AlignRight);
auto aLayout = make_parented(this);
@@ -854,10 +856,19 @@ void ControllerScreenPreview::updateFrame(
return;
}
auto currentTimestamp = mixxx::Time::elapsed();
- auto durationSinceLastFrame = (currentTimestamp - m_lastFrameTimespamp).toIntegerMillis();
- if (durationSinceLastFrame) {
+ m_frameDurationHistory[m_frameDurationHistoryIdx++] =
+ (currentTimestamp - m_lastFrameTimespamp).toIntegerMillis();
+ m_frameDurationHistoryIdx %= 5;
+
+ double durationSinceLastFrame = 0.0;
+ for (uint8_t i = 0; i < 5; i++) {
+ durationSinceLastFrame += (double)m_frameDurationHistory[i];
+ }
+ durationSinceLastFrame /= 5.0;
+
+ if (durationSinceLastFrame > 0.0) {
m_pStat->setText(QString("%0 FPS (requested %1)")
- .arg(1000 / durationSinceLastFrame)
+ .arg((int)(1000.0 / durationSinceLastFrame))
.arg(m_screenInfo.target_fps));
}
m_pFrame->setPixmap(QPixmap::fromImage(frame));
diff --git a/src/controllers/dlgprefcontroller.h b/src/controllers/dlgprefcontroller.h
index d7820ee8cb6b..bf6e3cec0d0c 100644
--- a/src/controllers/dlgprefcontroller.h
+++ b/src/controllers/dlgprefcontroller.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
#include "controllers/controllerinputmappingtablemodel.h"
@@ -32,6 +33,8 @@ class ControllerScreenPreview : public QWidget {
parented_ptr m_pFrame;
parented_ptr m_pStat;
+ uint8_t m_frameDurationHistoryIdx;
+ uint m_frameDurationHistory[5];
mixxx::Duration m_lastFrameTimespamp;
};
diff --git a/src/controllers/rendering/controllerrenderingengine.cpp b/src/controllers/rendering/controllerrenderingengine.cpp
index 7961764af4f4..0da54ce23b03 100644
--- a/src/controllers/rendering/controllerrenderingengine.cpp
+++ b/src/controllers/rendering/controllerrenderingengine.cpp
@@ -227,13 +227,6 @@ void ControllerRenderingEngine::renderFrame() {
emit frameRendered(m_screenInfo, fboImage);
- // auto endOfRender = mixxx::Time::elapsed();
- // qDebug() << "Fame took "
- // << (endOfRender - m_nextFrameStart).formatMillisWithUnit()
- // << " and frame has" << fboImage.sizeInBytes() << "bytes";
-
- m_nextFrameStart += mixxx::Duration::fromMillis(1000 / m_screenInfo.target_fps);
-
m_context->doneCurrent();
}
@@ -243,7 +236,16 @@ bool ControllerRenderingEngine::stop() {
}
void ControllerRenderingEngine::send(Controller* controller, const QByteArray& frame) {
- controller->sendBytes(frame);
+ if (!frame.isEmpty()) {
+ controller->sendBytes(frame);
+ }
+
+ // auto endOfRender = mixxx::Time::elapsed();
+ // qDebug() << "Fame took "
+ // << (endOfRender - m_nextFrameStart).formatMillisWithUnit()
+ // << " and frame has" << frame.size() << "bytes";
+
+ m_nextFrameStart += mixxx::Duration::fromSeconds(1.0 / (double)m_screenInfo.target_fps);
auto durationToWaitBeforeFrame = (m_nextFrameStart - mixxx::Time::elapsed());
auto msecToWaitBeforeFrame = durationToWaitBeforeFrame.toIntegerMillis();
@@ -252,7 +254,10 @@ void ControllerRenderingEngine::send(Controller* controller, const QByteArray& f
// qDebug() << "Waiting for "
// << durationToWaitBeforeFrame.formatMillisWithUnit()
// << " before rendering next frame";
- QTimer::singleShot(msecToWaitBeforeFrame, this, &ControllerRenderingEngine::renderFrame);
+ QTimer::singleShot(msecToWaitBeforeFrame,
+ Qt::PreciseTimer,
+ this,
+ &ControllerRenderingEngine::renderFrame);
} else {
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
diff --git a/src/controllers/scripting/legacy/controllerscriptenginelegacy.cpp b/src/controllers/scripting/legacy/controllerscriptenginelegacy.cpp
index 7d0371396dd8..2bf250e5cf40 100644
--- a/src/controllers/scripting/legacy/controllerscriptenginelegacy.cpp
+++ b/src/controllers/scripting/legacy/controllerscriptenginelegacy.cpp
@@ -331,8 +331,6 @@ bool ControllerScriptEngineLegacy::initialize() {
}
}
- // if ()
-
// For testing, do not actually initialize the scripts, just check for
// syntax errors above.
if (m_bTesting) {