├── src
├── images
│ ├── Key - Enter.png
│ ├── Key - Space.png
│ ├── Key - Backspace.png
│ ├── Key - Left Arrow.png
│ └── Key - Right Arrow.png
├── images.qrc
├── qml.qrc
├── xfm2.pro
├── CustomDial.qml
├── main.cpp
├── ArpeggiatorPage.qml
├── LFOPage.qml
├── PopupMessage.qml
├── ModulationPage.qml
├── xfmoperator.h
├── CommonPage.qml
├── main.qml
├── EffectsPage2.qml
├── xfmoperator.cpp
├── xfm2.h
├── EffectsPage.qml
├── AlgorithmPage.qml
├── SynthModel.h
├── CustomKeyboard.qml
└── EnvelopePage.qml
└── README.md
/src/images/Key - Enter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ataristdude/xfm2controller/HEAD/src/images/Key - Enter.png
--------------------------------------------------------------------------------
/src/images/Key - Space.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ataristdude/xfm2controller/HEAD/src/images/Key - Space.png
--------------------------------------------------------------------------------
/src/images/Key - Backspace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ataristdude/xfm2controller/HEAD/src/images/Key - Backspace.png
--------------------------------------------------------------------------------
/src/images/Key - Left Arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ataristdude/xfm2controller/HEAD/src/images/Key - Left Arrow.png
--------------------------------------------------------------------------------
/src/images/Key - Right Arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ataristdude/xfm2controller/HEAD/src/images/Key - Right Arrow.png
--------------------------------------------------------------------------------
/src/images.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | images/Key - Space.png
4 | images/Key - Right Arrow.png
5 | images/Key - Left Arrow.png
6 | images/Key - Enter.png
7 | images/Key - Backspace.png
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 | OperatorPage.qml
5 | CommonPage.qml
6 | EnvelopePage.qml
7 | AlgorithmPage.qml
8 | PopupMessage.qml
9 | LFOPage.qml
10 | ModulationPage.qml
11 | ArpeggiatorPage.qml
12 | EffectsPage.qml
13 | EffectsPage2.qml
14 | CustomDial.qml
15 | CustomKeyboard.qml
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xfm2controller
2 | A controller for the XFM2 Synthesizer using Qt 5.12.
3 | XFM2 is a fantastic FPGA based synthesizer created by Futur3soundz see https://www.futur3soundz.com/xfm2
4 | I do not own XFM2. This project is just a controller for that synth.
5 |
6 | I designed this controller for a Raspberry Pi running an 800x480 touch screen but it will also work for the Desktop.
7 | Here's what you need to know:
8 | 1. The application uses the USB serial port to control the synth. This is set up with a SERIALPORT define in the SynthModel.cpp file.
9 | 2. Patch names are stored by the application. The filename is specified in the PATCHFILE define in the SynthModel.cpp file.
10 | 3. On the desktop you probably won't want to run it as a full sceeen app. Comment out the visibility property in Main.qml
11 |
12 | Enjoy!
13 |
--------------------------------------------------------------------------------
/src/xfm2.pro:
--------------------------------------------------------------------------------
1 | QT += quick serialport
2 |
3 | CONFIG += c++11
4 |
5 | # The following define makes your compiler emit warnings if you use
6 | # any Qt feature that has been marked deprecated (the exact warnings
7 | # depend on your compiler). Refer to the documentation for the
8 | # deprecated API to know how to port your code away from it.
9 | DEFINES += QT_DEPRECATED_WARNINGS
10 |
11 | # You can also make your code fail to compile if it uses deprecated APIs.
12 | # In order to do so, uncomment the following line.
13 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
14 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
15 |
16 | SOURCES += \
17 | SynthModel.cpp \
18 | main.cpp \
19 | xfmoperator.cpp
20 |
21 | RESOURCES += qml.qrc \
22 | images.qrc
23 |
24 | # Additional import path used to resolve QML modules in Qt Creator's code model
25 | QML_IMPORT_PATH =
26 |
27 | # Additional import path used to resolve QML modules just for Qt Quick Designer
28 | QML_DESIGNER_IMPORT_PATH =
29 |
30 | # Default rules for deployment.
31 | qnx: target.path = /tmp/$${TARGET}/bin
32 | else: unix:!android: target.path = /opt/$${TARGET}/bin
33 | !isEmpty(target.path): INSTALLS += target
34 |
35 | HEADERS += \
36 | SynthModel.h \
37 | xfm2.h \
38 | xfmoperator.h
39 |
--------------------------------------------------------------------------------
/src/CustomDial.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 | import QtQuick.Controls.Styles 1.4
29 |
30 | Dial {
31 | id: control
32 | style: DialStyle {
33 | handle: Rectangle {
34 | width: control.width/10
35 | height: control.width/10
36 | color: "black"
37 | radius: control.width/20
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include "SynthModel.h"
29 |
30 |
31 | int main(int argc, char *argv[])
32 | {
33 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
34 |
35 | QGuiApplication app(argc, argv);
36 |
37 | // Register the FM Operator class
38 | qmlRegisterType("Xfm.Synth", 1, 0, "XFMOperator");
39 |
40 | // Set the app's default font. This is important for
41 | // correct scaling as some of the Qt forms are reliant
42 | // on point size. We assume Ubuntu as the default font
43 | QFont font("Ubuntu");
44 | app.setFont(font);
45 |
46 | QQmlApplicationEngine engine;
47 |
48 | // Create and register the synth model object so it's visible to QML
49 | SynthModel *synthModel = new SynthModel();
50 |
51 | engine.rootContext()->setContextProperty("synthModel", synthModel);
52 |
53 | const QUrl url(QStringLiteral("qrc:/main.qml"));
54 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) {
55 | if (!obj && url == objUrl) {
56 | QCoreApplication::exit(-1);
57 | }
58 | }, Qt::QueuedConnection);
59 |
60 | engine.load(url);
61 |
62 | return app.exec();
63 | }
64 |
--------------------------------------------------------------------------------
/src/ArpeggiatorPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 |
30 | Item {
31 | id: arpeggiatorPage
32 | width: 800
33 | height: 357
34 |
35 | Connections {
36 | target: synthModel
37 | onPatchNumberChanged: {
38 | updatePage();
39 | }
40 | }
41 |
42 | function updatePage()
43 | {
44 | spinArpMode.value=synthModel.arpeggiatorMode;
45 | spinTempo.value=synthModel.arpeggiatorTempo === 0 ? 0 : synthModel.arpeggiatorTempo-49;
46 | spinMultiplier.value=synthModel.arpeggiatorTempoMultiplier;
47 | spinOctaveRange.value=synthModel.arpeggiatorOctaveRange;
48 | }
49 |
50 |
51 | Rectangle {
52 | anchors.fill: parent
53 | color: "#505050"
54 | }
55 |
56 | SpinBox {
57 | id: spinArpMode
58 | x: 110
59 | y: 24
60 | width: 160
61 | height: 40
62 | to: 5
63 | onValueChanged: {
64 | synthModel.arpeggiatorMode=spinArpMode.value;
65 | }
66 | textFromValue: function(value, locale) {
67 | var vals=["OFF", "UP", "DOWN", "UP/DOWN", "PLAYED", "RANDOM"];
68 |
69 | return vals[value];
70 | }
71 | }
72 |
73 | Label {
74 | id: label
75 | x: 49
76 | y: 35
77 | color: "#ffffff"
78 | text: qsTr("Mode")
79 | font.pixelSize: 16
80 | }
81 |
82 | SpinBox {
83 | id: spinTempo
84 | x: 110
85 | y: 98
86 | width: 160
87 | height: 40
88 | to: 206
89 | onValueChanged: {
90 | if (spinTempo.value === 0) {
91 | synthModel.arpeggiatorTempo=0;
92 | } else {
93 | synthModel.arpeggiatorTempo=spinTempo.value+49;
94 | }
95 | }
96 | textFromValue: function(value, locale) {
97 | if (value === 0) {
98 | return "MIDI";
99 | }
100 |
101 | return (value+49).toString();
102 | }
103 | }
104 |
105 | Label {
106 | id: label1
107 | x: 41
108 | y: 110
109 | color: "#ffffff"
110 | text: qsTr("Tempo")
111 | font.pixelSize: 16
112 | }
113 |
114 | SpinBox {
115 | id: spinMultiplier
116 | x: 444
117 | y: 98
118 | to: 255
119 | onValueChanged: {
120 | synthModel.arpeggiatorTempoMultiplier=spinMultiplier.value;
121 | }
122 | }
123 |
124 | Label {
125 | id: label2
126 | x: 310
127 | y: 110
128 | color: "#ffffff"
129 | text: qsTr("Tempo Multiplier")
130 | font.pixelSize: 16
131 | }
132 |
133 | SpinBox {
134 | id: spinOctaveRange
135 | x: 110
136 | y: 170
137 | to: 9
138 | onValueChanged: {
139 | synthModel.arpeggiatorOctaveRange=spinOctaveRange.value;
140 | }
141 | }
142 |
143 | Label {
144 | id: label3
145 | x: 17
146 | y: 182
147 | color: "#ffffff"
148 | text: qsTr("Oct Range")
149 | font.pixelSize: 16
150 | }
151 |
152 |
153 | Component.onCompleted: {
154 | updatePage();
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/LFOPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 |
30 | Item {
31 | id: lfoPage
32 | width: 800
33 | height: 357
34 |
35 |
36 | Rectangle {
37 | anchors.fill: parent
38 | color: "#505050"
39 | }
40 |
41 | Connections {
42 | target: synthModel
43 | onPatchNumberChanged: {
44 | updatePage();
45 | }
46 | }
47 |
48 | function updatePage()
49 | {
50 | spinWave.value=synthModel.lfoWave;
51 | spinSync.value=synthModel.lfoSync;
52 | dialSpeed.value=synthModel.lfoSpeed;
53 | dialFade.value=synthModel.lfoFade;
54 | dialPitch.value=synthModel.lfoDepthPitch;
55 | dialAmp.value=synthModel.lfoDepthAmplitude;
56 | }
57 |
58 | Label {
59 | id: label
60 | x: 27
61 | y: 37
62 | color: "#ffffff"
63 | text: qsTr("Wave")
64 | font.pixelSize: 16
65 | }
66 |
67 | CustomDial {
68 | id: dialSpeed
69 | x: 51
70 | y: 130
71 | width: 136
72 | height: 136
73 | maximumValue: 255
74 | onValueChanged: {
75 | synthModel.lfoSpeed=dialSpeed.value;
76 | }
77 | }
78 |
79 | SpinBox {
80 | id: spinWave
81 | x: 84
82 | y: 26
83 | width: 167
84 | height: 40
85 | to: 5
86 | onValueChanged: {
87 | synthModel.lfoWave=spinWave.value
88 | }
89 | textFromValue: function(value, locale) {
90 | var vals=["TRI", "SQR", "SAW UP", "SAW DN", "SIN", "RND"];
91 |
92 | return vals[value];
93 | }
94 | }
95 |
96 | Label {
97 | id: label1
98 | x: 98
99 | y: 280
100 | width: 42
101 | height: 17
102 | color: "#ffffff"
103 | text: qsTr("Rate")
104 | font.pixelSize: 16
105 | }
106 |
107 | Label {
108 | id: label2
109 | x: 304
110 | y: 37
111 | color: "#ffffff"
112 | text: qsTr("Sync")
113 | font.pixelSize: 16
114 | }
115 |
116 | SpinBox {
117 | id: spinSync
118 | x: 353
119 | y: 26
120 | width: 202
121 | height: 40
122 | to: 3
123 | onValueChanged: {
124 | synthModel.lfoSync=spinSync.value;
125 | }
126 |
127 | textFromValue: function(value, locale) {
128 | var vals=["SINGLE FREE", "SINGLE KEY", "MULTI FREE", "MULTI KEY"];
129 |
130 | return vals[value];
131 | }
132 | }
133 |
134 | CustomDial {
135 | id: dialFade
136 | x: 222
137 | y: 130
138 | width: 136
139 | height: 136
140 | maximumValue: 255
141 | onValueChanged: {
142 | synthModel.lfoFade=synthModel.lfoFade;
143 | }
144 | }
145 |
146 | Label {
147 | id: label3
148 | x: 269
149 | y: 280
150 | width: 42
151 | height: 17
152 | color: "#ffffff"
153 | text: qsTr("Fade")
154 | font.pixelSize: 16
155 | }
156 |
157 | CustomDial {
158 | id: dialPitch
159 | x: 431
160 | y: 130
161 | width: 136
162 | height: 136
163 | maximumValue: 255
164 | onValueChanged: {
165 | synthModel.lfoDepthPitch=dialPitch.value;
166 | }
167 | }
168 |
169 | Label {
170 | id: label4
171 | x: 455
172 | y: 280
173 | width: 89
174 | height: 17
175 | color: "#ffffff"
176 | text: qsTr("Depth Pitch")
177 | font.pixelSize: 16
178 | }
179 |
180 | CustomDial {
181 | id: dialAmp
182 | x: 603
183 | y: 130
184 | width: 136
185 | height: 136
186 | maximumValue: 255
187 | onValueChanged: {
188 | synthModel.lfoDepthAmplitude=dialAmp.value;
189 | }
190 | }
191 |
192 | Label {
193 | id: label5
194 | x: 604
195 | y: 280
196 | width: 134
197 | height: 17
198 | color: "#ffffff"
199 | text: qsTr("Depth Amplitude")
200 | font.pixelSize: 16
201 | horizontalAlignment: Text.AlignHCenter
202 | }
203 |
204 | Component.onCompleted: {
205 | updatePage();
206 | }
207 |
208 | }
209 |
210 | /*##^##
211 | Designer {
212 | D{i:1;anchors_height:400;anchors_x:0;anchors_y:0}D{i:2;anchors_height:400;anchors_x:0;anchors_y:0}
213 | }
214 | ##^##*/
215 |
--------------------------------------------------------------------------------
/src/PopupMessage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.12
26 |
27 | Popup {
28 | id: rootPopup
29 | width: 600
30 | height: 240
31 | x: (800 - width) / 2
32 | y: (480 - height) / 2
33 | dim: true
34 | background: Rectangle { color: "#00ffffff" }
35 | closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
36 | clip: true
37 |
38 | signal okClicked()
39 | signal cancelClicked()
40 |
41 | property alias titleText: popupLabel.text;
42 | property alias detailText: confirmLabel.text;
43 | property alias okButtonText: okBtnLabel.text;
44 | property alias cancelButtonText: cancelBtnLabel.text;
45 | property alias okButton: okBtn;
46 | property alias cancelButton: cancelBtn;
47 |
48 | // popup content
49 | Rectangle {
50 | id: popupRect
51 | anchors.fill: parent
52 | radius: 0
53 | clip: true
54 | border.color: "white"
55 |
56 | Label {
57 | id: popupLabel
58 | x: 1
59 | y: 1
60 | width: 598
61 | height: 40
62 | color: "#ffffff"
63 | text: "Title"
64 | clip: true
65 | anchors.right: parent.right
66 | anchors.rightMargin: 0
67 | anchors.left: parent.left
68 | anchors.leftMargin: 0
69 | verticalAlignment: Text.AlignVCenter
70 | font.pixelSize: 30
71 | anchors.top: parent.top
72 | anchors.topMargin: 0
73 | horizontalAlignment: Text.AlignHCenter
74 | background: Rectangle {
75 | color: "#808080"
76 | }
77 | }
78 |
79 | Label {
80 | id: confirmLabel
81 | width: 571
82 | height: 91
83 | text: "Detailed text"
84 | verticalAlignment: Text.AlignVCenter
85 | wrapMode: Text.WordWrap
86 | font.pixelSize: 22
87 | anchors.top: popupLabel.bottom
88 | anchors.topMargin: 10
89 | horizontalAlignment: Text.AlignHCenter
90 | anchors.horizontalCenter: parent.horizontalCenter
91 | }
92 |
93 | Rectangle {
94 | id: horizontalLine
95 | x: 1
96 | y: 145
97 | width: parent.width
98 | height: 1
99 | anchors.top : popupLabel.bottom
100 | anchors.topMargin: 110
101 | color: "lightgray"
102 | }
103 |
104 | Rectangle {
105 | id: cancelBtn
106 | y: 154
107 | width: 298
108 | height: 64
109 | radius: 10.0
110 | border.color: "#00000000"
111 | anchors.top: horizontalLine.bottom
112 | anchors.left: parent.left
113 | anchors.leftMargin: 1
114 | Label {
115 | id: cancelBtnLabel
116 | text: "Cancel"
117 | font.pixelSize: 25
118 | anchors.horizontalCenter: parent.horizontalCenter
119 | anchors.verticalCenter: parent.verticalCenter
120 | horizontalAlignment: Text.AlignHCenter
121 | }
122 |
123 | TapHandler {
124 | gesturePolicy: TapHandler.ReleaseWithinBounds
125 | onTapped: {
126 | rootPopup.cancelClicked();
127 | rootPopup.close()
128 | }
129 | onCanceled: {
130 | }
131 | }
132 |
133 | }
134 |
135 | Rectangle {
136 | id: verticalLine
137 | width: 1
138 | height: 64
139 | anchors.top : horizontalLine.bottom
140 | anchors.horizontalCenter: parent.horizontalCenter
141 | color: "lightgray"
142 | }
143 |
144 | Rectangle {
145 | id: okBtn
146 | y: 154
147 | width: parent.width / 2 - 2
148 | height: 64
149 | radius: 10.0
150 | border.color: "#00000000"
151 | anchors.top: horizontalLine.bottom
152 | anchors.right: parent.right
153 | anchors.rightMargin: 1
154 |
155 | Label {
156 | id: okBtnLabel
157 | text: "Ok"
158 | font.pixelSize: 25
159 | anchors.horizontalCenter: parent.horizontalCenter
160 | anchors.verticalCenter: parent.verticalCenter
161 | horizontalAlignment: Text.AlignHCenter
162 | }
163 | TapHandler {
164 | gesturePolicy: TapHandler.ReleaseWithinBounds
165 | onTapped: {
166 | rootPopup.okClicked();
167 | rootPopup.close()
168 | }
169 | onCanceled: {
170 | }
171 | }
172 |
173 | }
174 | }
175 | }
176 |
177 |
--------------------------------------------------------------------------------
/src/ModulationPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 |
30 | Item {
31 | id: modulationPage
32 | width: 800
33 | height: 357
34 |
35 | Connections {
36 | target: synthModel
37 | onPatchNumberChanged: {
38 | updatePage();
39 | }
40 | }
41 |
42 | function updatePage()
43 | {
44 | dialPitchAftertouch.value=synthModel.modPitchAftertouch;
45 | dialPitchLFOAftertouch.value=synthModel.modPitchLFOAftertouch;
46 | dialAmplitudeLFOAftertouch.value=synthModel.modAmpLFOAftertouch;
47 | dialEGBiasAftertouch.value=synthModel.modEnvelopeBiasAftertouch;
48 |
49 | dialPitchRandom.value=synthModel.modPitchRandom;
50 | dialPitchLFOWheel.value=synthModel.modPitchLFOWheel;
51 | dialAmplitudeLFOWheel.value=synthModel.modAmpLFOWheel;
52 | dialEGBiasWheel.value=synthModel.modEnvelopeBiasWheel;
53 |
54 | dialPitchBreath.value=synthModel.modPitchBreath;
55 | dialPitchLFOBreath.value=synthModel.modPitchLFOBreath;
56 | dialAmplitudeLFOBreath.value=synthModel.modAmpLFOBreath;
57 | dialEGBiasBreath.value=synthModel.modEnvelopeBiasBreath;
58 |
59 | dialPitchFoot.value=synthModel.modPitchFoot;
60 | dialPitchLFOFoot.value=synthModel.modPitchLFOFoot;
61 | dialAmplitudeLFOFoot.value=synthModel.modAmpLFOFoot;
62 | dialEGBiasFoot.value=synthModel.modEnvelopeBiasFoot;
63 | }
64 |
65 | Rectangle {
66 | anchors.fill: parent
67 | color: "#505050"
68 | }
69 |
70 |
71 | Label {
72 | id: label
73 | x: 144
74 | y: 10
75 | color: "#ffffff"
76 | text: qsTr("Aftertouch")
77 | font.pixelSize: 16
78 | }
79 |
80 | Label {
81 | id: label1
82 | x: 81
83 | y: 60
84 | color: "#ffffff"
85 | text: qsTr("Pitch")
86 | font.pixelSize: 16
87 | }
88 |
89 | CustomDial {
90 | id: dialPitchAftertouch
91 | x: 141
92 | y: 33
93 | width: 80
94 | height: 70
95 | maximumValue: 255
96 | onValueChanged: {
97 | synthModel.modPitchAftertouch=dialPitchAftertouch.value;
98 | }
99 | }
100 |
101 | CustomDial {
102 | id: dialPitchLFOAftertouch
103 | x: 141
104 | y: 109
105 | width: 80
106 | height: 70
107 | maximumValue: 255
108 | onValueChanged: {
109 | synthModel.modPitchLFOAftertouch=dialPitchLFOAftertouch.value;
110 | }
111 | }
112 |
113 | CustomDial {
114 | id: dialAmplitudeLFOAftertouch
115 | x: 141
116 | y: 185
117 | width: 80
118 | height: 70
119 | maximumValue: 255
120 | onValueChanged: {
121 | synthModel.modAmpLFOAftertouch=dialAmplitudeLFOAftertouch.value;
122 | }
123 | }
124 |
125 | CustomDial {
126 | id: dialEGBiasAftertouch
127 | x: 141
128 | y: 261
129 | width: 80
130 | height: 70
131 | maximumValue: 255
132 | onValueChanged: {
133 | synthModel.modEnvelopeBiasAftertouch=dialEGBiasAftertouch.value;
134 | }
135 | }
136 |
137 | Label {
138 | id: label2
139 | x: 50
140 | y: 136
141 | color: "#ffffff"
142 | text: qsTr("Pitch LFO")
143 | font.pixelSize: 16
144 | }
145 |
146 | Label {
147 | id: label3
148 | x: 13
149 | y: 212
150 | color: "#ffffff"
151 | text: qsTr("Amplitude LFO")
152 | font.pixelSize: 16
153 | }
154 |
155 | Label {
156 | id: label4
157 | x: 65
158 | y: 288
159 | color: "#ffffff"
160 | text: qsTr("EG Bias")
161 | font.pixelSize: 16
162 | }
163 |
164 | Label {
165 | id: label5
166 | x: 297
167 | y: 10
168 | color: "#ffffff"
169 | text: qsTr("Wheel/RND")
170 | font.pixelSize: 16
171 | }
172 |
173 | CustomDial {
174 | id: dialPitchRandom
175 | x: 297
176 | y: 33
177 | width: 80
178 | height: 70
179 | maximumValue: 255
180 | onValueChanged: {
181 | synthModel.modPitchRandom=dialPitchRandom.value;
182 | }
183 | }
184 |
185 | CustomDial {
186 | id: dialPitchLFOWheel
187 | x: 297
188 | y: 109
189 | width: 80
190 | height: 70
191 | maximumValue: 255
192 | onValueChanged: {
193 | synthModel.modPitchLFOWheel=dialPitchLFOWheel.value;
194 | }
195 | }
196 |
197 | CustomDial {
198 | id: dialAmplitudeLFOWheel
199 | x: 297
200 | y: 185
201 | width: 80
202 | height: 70
203 | maximumValue: 255
204 | onValueChanged: {
205 | synthModel.modAmpLFOWheel=dialAmplitudeLFOWheel.value;
206 | }
207 | }
208 |
209 | CustomDial {
210 | id: dialEGBiasWheel
211 | x: 297
212 | y: 261
213 | width: 80
214 | height: 70
215 | maximumValue: 255
216 | onValueChanged: {
217 | synthModel.modEnvelopeBiasWheel=dialEGBiasWheel.value;
218 | }
219 | }
220 |
221 | Label {
222 | id: label6
223 | x: 470
224 | y: 10
225 | color: "#ffffff"
226 | text: qsTr("Breath")
227 | font.pixelSize: 16
228 | }
229 |
230 | CustomDial {
231 | id: dialPitchBreath
232 | x: 453
233 | y: 33
234 | width: 80
235 | height: 70
236 | maximumValue: 255
237 | onValueChanged: {
238 | synthModel.modPitchBreath=dialPitchBreath.value;
239 | }
240 | }
241 |
242 | CustomDial {
243 | id: dialPitchLFOBreath
244 | x: 453
245 | y: 110
246 | width: 80
247 | height: 70
248 | maximumValue: 255
249 | onValueChanged: {
250 | synthModel.modPitchLFOBreath=dialPitchLFOBreath.value;
251 | }
252 | }
253 |
254 | CustomDial {
255 | id: dialAmplitudeLFOBreath
256 | x: 453
257 | y: 185
258 | width: 80
259 | height: 70
260 | maximumValue: 255
261 | onValueChanged: {
262 | synthModel.modAmpLFOBreath=dialAmplitudeLFOBreath.value;
263 | }
264 | }
265 |
266 | CustomDial {
267 | id: dialEGBiasBreath
268 | x: 453
269 | y: 262
270 | width: 80
271 | height: 70
272 | maximumValue: 255
273 | onValueChanged: {
274 | synthModel.modEnvelopeBiasBreath=dialEGBiasBreath.value;
275 | }
276 | }
277 |
278 | Label {
279 | id: label7
280 | x: 633
281 | y: 10
282 | color: "#ffffff"
283 | text: qsTr("Foot")
284 | font.pixelSize: 16
285 | }
286 |
287 | CustomDial {
288 | id: dialPitchFoot
289 | x: 609
290 | y: 33
291 | width: 80
292 | height: 70
293 | maximumValue: 255
294 | onValueChanged: {
295 | synthModel.modPitchFoot=dialPitchFoot.value;
296 | }
297 | }
298 |
299 | CustomDial {
300 | id: dialPitchLFOFoot
301 | x: 609
302 | y: 109
303 | width: 80
304 | height: 70
305 | maximumValue: 255
306 | onValueChanged: {
307 | synthModel.modPitchLFOFoot=dialPitchLFOFoot.value;
308 | }
309 | }
310 |
311 | CustomDial {
312 | id: dialAmplitudeLFOFoot
313 | x: 609
314 | y: 185
315 | width: 80
316 | height: 70
317 | maximumValue: 255
318 | onValueChanged: {
319 | synthModel.modAmpLFOFoot=dialAmplitudeLFOFoot.value;
320 | }
321 | }
322 |
323 | CustomDial {
324 | id: dialEGBiasFoot
325 | x: 609
326 | y: 262
327 | width: 80
328 | height: 70
329 | maximumValue: 255
330 | onValueChanged: {
331 | synthModel.modEnvelopeBiasFoot=dialEGBiasFoot.value;
332 | }
333 | }
334 |
335 | Component.onCompleted: {
336 | updatePage();
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/src/xfmoperator.h:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | #ifndef XFMOPERATOR_H
25 | #define XFMOPERATOR_H
26 |
27 | #include
28 | #include
29 | #include "xfm2.h"
30 |
31 | /*
32 | * This is a class for a single XFM2 Operator
33 | * It makes it easy to manipulate the operator's parameters
34 | */
35 |
36 | class XFMOperator : public QObject {
37 | Q_OBJECT
38 |
39 | // The operator number keeps track of which operator this is
40 | Q_PROPERTY(int operatorNumber READ operatorNumber WRITE setOperatorNumber NOTIFY operatorNumberChanged)
41 |
42 | // Algorithm, feedback and frequency ratio
43 | Q_PROPERTY(int algorithm READ algorithm WRITE setAlgorithm NOTIFY algorithmChanged)
44 | Q_PROPERTY(int feedback READ feedback WRITE setFeedback NOTIFY feedbackChanged)
45 | Q_PROPERTY(int ratio READ ratio WRITE setRatio NOTIFY ratioChanged)
46 | Q_PROPERTY(int ratioFine READ ratioFine WRITE setRatioFine NOTIFY ratioFineChanged)
47 |
48 | // Waveform and phase
49 | Q_PROPERTY(int wave1 READ wave1 WRITE setWave1 NOTIFY wave1Changed)
50 | Q_PROPERTY(int wave2 READ wave2 WRITE setWave2 NOTIFY wave2Changed)
51 | Q_PROPERTY(int oscillatorMode READ oscillatorMode WRITE setOscillatorMode NOTIFY oscillatorModeChanged)
52 | Q_PROPERTY(int oscillatorRatio READ oscillatorRatio WRITE setOscillatorRatio NOTIFY oscillatorRatioChanged)
53 | Q_PROPERTY(int phase READ phase WRITE setPhase NOTIFY phaseChanged)
54 |
55 | // Fine tuning
56 | Q_PROPERTY(int fine READ fine WRITE setFine NOTIFY fineChanged)
57 |
58 | // Level
59 | Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged)
60 | Q_PROPERTY(int levelLeft READ levelLeft WRITE setLevelLeft NOTIFY levelLeftChanged)
61 | Q_PROPERTY(int levelRight READ levelRight WRITE setLevelRight NOTIFY levelRightChanged)
62 |
63 | // Keyboard control. The breakpoint sets the split between "left" (lower) and "right" (upper)
64 | Q_PROPERTY(int velocitySensitivity READ velocitySensitivity WRITE setVelocitySensitivity NOTIFY velocitySensitivityChanged)
65 | Q_PROPERTY(int keyboardBreakpoint READ keyboardBreakpoint WRITE setKeyboardBreakpoint NOTIFY keyboardBreakpointChanged)
66 | Q_PROPERTY(int keyboardScaleLeft READ keyboardScaleLeft WRITE setKeyboardScaleLeft NOTIFY keyboardScaleLeftChanged)
67 | Q_PROPERTY(int keyboardScaleRight READ keyboardScaleRight WRITE setKeyboardScaleRight NOTIFY keyboardScaleRightChanged)
68 | Q_PROPERTY(int keyboardCurveLeft READ keyboardCurveLeft WRITE setKeyboardCurveLeft NOTIFY keyboardCurveLeftChanged)
69 | Q_PROPERTY(int keyboardCurveRight READ keyboardCurveRight WRITE setKeyboardCurveRight NOTIFY keyboardCurveRightChanged)
70 | Q_PROPERTY(int rateKey READ rateKey WRITE setRateKey NOTIFY rateKeyChanged)
71 |
72 | // Levels 0-5
73 | Q_PROPERTY(int L0 READ L0 WRITE setL0 NOTIFY L0Changed)
74 | Q_PROPERTY(int L1 READ L1 WRITE setL1 NOTIFY L1Changed)
75 | Q_PROPERTY(int L2 READ L2 WRITE setL2 NOTIFY L2Changed)
76 | Q_PROPERTY(int L3 READ L3 WRITE setL3 NOTIFY L3Changed)
77 | Q_PROPERTY(int L4 READ L4 WRITE setL4 NOTIFY L4Changed)
78 | Q_PROPERTY(int L5 READ L5 WRITE setL5 NOTIFY L5Changed)
79 |
80 | // Rates 0-5
81 | Q_PROPERTY(int R0 READ R0 WRITE setR0 NOTIFY R0Changed)
82 | Q_PROPERTY(int R1 READ R1 WRITE setR1 NOTIFY R1Changed)
83 | Q_PROPERTY(int R2 READ R2 WRITE setR2 NOTIFY R2Changed)
84 | Q_PROPERTY(int R3 READ R3 WRITE setR3 NOTIFY R3Changed)
85 | Q_PROPERTY(int R4 READ R4 WRITE setR4 NOTIFY R4Changed)
86 | Q_PROPERTY(int R5 READ R5 WRITE setR5 NOTIFY R5Changed)
87 |
88 | // Modulation
89 | Q_PROPERTY(int amplitudeModulationSensitivity READ amplitudeModulationSensitivity WRITE setAmplitudeModulationSensitivity NOTIFY amplitudeModulationSensitivityChanged)
90 | Q_PROPERTY(int pitchModulationSensitivity READ pitchModulationSensitivity WRITE setPitchModulationSensitivity NOTIFY pitchModulationSensitivityChanged)
91 |
92 | public:
93 | explicit XFMOperator(QObject *parent = nullptr);
94 |
95 |
96 | signals:
97 | void operatorNumberChanged();
98 | void algorithmChanged();
99 | void feedbackChanged();
100 | void ratioChanged();
101 | void ratioFineChanged();
102 | void fineChanged();
103 | void levelChanged();
104 | void levelLeftChanged();
105 | void levelRightChanged();
106 | void velocitySensitivityChanged();
107 | void keyboardBreakpointChanged();
108 | void keyboardScaleLeftChanged();
109 | void keyboardScaleRightChanged();
110 | void keyboardCurveLeftChanged();
111 | void keyboardCurveRightChanged();
112 | void L0Changed();
113 | void L1Changed();
114 | void L2Changed();
115 | void L3Changed();
116 | void L4Changed();
117 | void L5Changed();
118 | void R0Changed();
119 | void R1Changed();
120 | void R2Changed();
121 | void R3Changed();
122 | void R4Changed();
123 | void R5Changed();
124 | void rateKeyChanged();
125 | void amplitudeModulationSensitivityChanged();
126 | void pitchModulationSensitivityChanged();
127 | void wave1Changed();
128 | void wave2Changed();
129 | void oscillatorModeChanged();
130 | void oscillatorRatioChanged();
131 | void phaseChanged();
132 |
133 | public:
134 | int operatorNumber() const;
135 | void setOperatorNumber(int n);
136 |
137 | int algorithm() const;
138 | void setAlgorithm(int a);
139 |
140 | int feedback() const;
141 | void setFeedback(int f);
142 |
143 | int ratio() const;
144 | void setRatio(int v);
145 |
146 | int ratioFine() const;
147 | void setRatioFine(int v);
148 |
149 | int fine() const;
150 | void setFine(int v);
151 |
152 | int level() const;
153 | void setLevel(int v);
154 |
155 | int levelLeft() const;
156 | void setLevelLeft(int v);
157 |
158 | int levelRight() const;
159 | void setLevelRight(int v);
160 |
161 | int velocitySensitivity() const;
162 | void setVelocitySensitivity(int v);
163 |
164 | int keyboardBreakpoint() const;
165 | void setKeyboardBreakpoint(int v);
166 |
167 | int keyboardScaleLeft() const;
168 | void setKeyboardScaleLeft(int v);
169 |
170 | int keyboardScaleRight() const;
171 | void setKeyboardScaleRight(int f);
172 |
173 | int keyboardCurveLeft() const;
174 | void setKeyboardCurveLeft(int f);
175 |
176 | int keyboardCurveRight() const;
177 | void setKeyboardCurveRight(int f);
178 |
179 | // Envelope
180 | int L0() const;
181 | void setL0(int f);
182 |
183 | int L1() const;
184 | void setL1(int f);
185 |
186 | int L2() const;
187 | void setL2(int f);
188 |
189 | int L3() const;
190 | void setL3(int f);
191 |
192 | int L4() const;
193 | void setL4(int f);
194 |
195 | int L5() const;
196 | void setL5(int f);
197 |
198 | int R0() const;
199 | void setR0(int f);
200 |
201 | int R1() const;
202 | void setR1(int f);
203 |
204 | int R2() const;
205 | void setR2(int f);
206 |
207 | int R3() const;
208 | void setR3(int f);
209 |
210 | int R4() const;
211 | void setR4(int f);
212 |
213 | int R5() const;
214 | void setR5(int f);
215 |
216 | int rateKey() const;
217 | void setRateKey(int f);
218 |
219 | int amplitudeModulationSensitivity() const;
220 | void setAmplitudeModulationSensitivity(int v);
221 |
222 | int pitchModulationSensitivity() const;
223 | void setPitchModulationSensitivity(int v);
224 |
225 | int wave1() const;
226 | void setWave1(int v);
227 |
228 | int wave2() const;
229 | void setWave2(int v);
230 |
231 | int oscillatorMode() const;
232 | void setOscillatorMode(int v);
233 |
234 | int oscillatorRatio() const;
235 | void setOscillatorRatio(int v);
236 |
237 | int phase() const;
238 | void setPhase(int v);
239 |
240 | private:
241 | int m_operator;
242 | unsigned char m_algorithm;
243 | unsigned char m_feedback;
244 | unsigned char m_ratio;
245 | unsigned char m_ratiofine;
246 | unsigned char m_fine;
247 | unsigned char m_level;
248 | unsigned char m_levelLeft;
249 | unsigned char m_levelRight;
250 | unsigned char m_velocitysensitivity;
251 | unsigned char m_keyboardbreakpoint;
252 | unsigned char m_keyboardscaleleft;
253 | unsigned char m_keyboardscaleright;
254 | unsigned char m_keyboardcurveleft;
255 | unsigned char m_keyboardcurveright;
256 | unsigned char m_l0;
257 | unsigned char m_l1;
258 | unsigned char m_l2;
259 | unsigned char m_l3;
260 | unsigned char m_l4;
261 | unsigned char m_l5;
262 | unsigned char m_r0;
263 | unsigned char m_r1;
264 | unsigned char m_r2;
265 | unsigned char m_r3;
266 | unsigned char m_r4;
267 | unsigned char m_r5;
268 | unsigned char m_ratekey;
269 | unsigned char m_ams;
270 | unsigned char m_pms;
271 | unsigned char m_wave1;
272 | unsigned char m_wave2;
273 | unsigned char m_wmode;
274 | unsigned char m_wratio;
275 | unsigned char m_phase;
276 | };
277 |
278 |
279 | #endif // XFMOPERATOR_H
280 |
--------------------------------------------------------------------------------
/src/CommonPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 | import QtQuick.Controls.Styles 1.4
29 |
30 | Item {
31 | id: commonPage
32 | width: 800
33 | height: 357
34 |
35 | Rectangle {
36 | anchors.fill: parent
37 | color: "#505050"
38 | }
39 |
40 | Connections {
41 | target: synthModel
42 | onPatchNumberChanged: {
43 | updatePage();
44 | }
45 | }
46 |
47 | function updatePage()
48 | {
49 | sliderOutput.value=synthModel.outputLevel
50 | dialVolume.value=synthModel.masterVolume
51 | dialPan.value=synthModel.masterPan
52 | spinBendUp.value=synthModel.masterPitchBendUp
53 | spinBendDown.value=synthModel.masterPitchBendDown
54 | spinTranspose.value=synthModel.masterTranspose-24
55 | spinVelocityOffset.value=synthModel.masterVelocityOffset
56 | sliderPortamentoTime.value=synthModel.portamentoTime
57 | switchLegato.checked=synthModel.masterLegato != 0
58 | spinPortamentoMode.value=synthModel.portamentoMode
59 | textName.text=synthModel.patchName
60 | }
61 |
62 | TextField {
63 | id: textName
64 | x: 101
65 | y: 20
66 | width: 410
67 | height: 38
68 | verticalAlignment: Text.AlignVCenter
69 | font.pixelSize: 18
70 | cursorVisible: false
71 |
72 | background: Rectangle {
73 | border.width: 1
74 | border.color: "#000000"
75 | color: "#ffffff"
76 | radius: 4
77 | }
78 |
79 | onTextChanged: {
80 | synthModel.patchName=textName.text;
81 | }
82 |
83 | onPressed: {
84 | keyboard.visible=true;
85 | textName.cursorVisible=true;
86 | }
87 | }
88 |
89 | Slider {
90 | id: sliderOutput
91 | x: 471
92 | y: 146
93 | width: 40
94 | height: 161
95 | to: 255
96 | orientation: Qt.Vertical
97 | onValueChanged: {
98 | synthModel.outputLevel=sliderOutput.value;
99 | }
100 | }
101 |
102 | CustomDial {
103 | id: dialVolume
104 | x: 627
105 | y: 15
106 | width: 136
107 | height: 136
108 | maximumValue: 255
109 | onValueChanged: {
110 | synthModel.masterVolume=dialVolume.value;
111 | }
112 | }
113 |
114 | Label {
115 | id: label1
116 | x: 669
117 | y: 162
118 | color: "#ffffff"
119 | text: qsTr("Volume")
120 | font.pixelSize: 16
121 | }
122 |
123 | Label {
124 | id: label
125 | x: 455
126 | y: 313
127 | width: 73
128 | height: 38
129 | color: "#ffffff"
130 | text: qsTr("Output Level")
131 | font.pixelSize: 16
132 | wrapMode: Text.WordWrap
133 | horizontalAlignment: Text.AlignHCenter
134 | }
135 |
136 | CustomDial {
137 | id: dialPan
138 | x: 627
139 | y: 192
140 | width: 136
141 | height: 136
142 | maximumValue: 255
143 | onValueChanged: {
144 | synthModel.masterPan=dialPan.value;
145 | }
146 | }
147 |
148 | Label {
149 | id: label3
150 | x: 27
151 | y: 95
152 | color: "#ffffff"
153 | text: qsTr("Bend Up")
154 | font.pixelSize: 16
155 | }
156 |
157 | SpinBox {
158 | id: spinBendUp
159 | x: 101
160 | y: 84
161 | to: 127
162 | onValueChanged: {
163 | synthModel.masterPitchBendUp=spinBendUp.value
164 | }
165 | }
166 |
167 | Label {
168 | id: label2
169 | x: 682
170 | y: 332
171 | color: "#ffffff"
172 | text: qsTr("Pan")
173 | font.pixelSize: 16
174 | horizontalAlignment: Text.AlignHCenter
175 | }
176 |
177 | Label {
178 | id: label4
179 | x: 5
180 | y: 154
181 | color: "#ffffff"
182 | text: qsTr("Bend Down")
183 | font.pixelSize: 16
184 | }
185 |
186 | SpinBox {
187 | id: spinBendDown
188 | x: 101
189 | y: 143
190 | to: 127
191 | onValueChanged: {
192 | synthModel.masterPitchBendDown=spinBendDown.value
193 | }
194 | }
195 |
196 | Label {
197 | id: label5
198 | x: 282
199 | y: 95
200 | color: "#ffffff"
201 | text: qsTr("Transpose")
202 | font.pixelSize: 16
203 | }
204 |
205 | SpinBox {
206 | id: spinTranspose
207 | x: 371
208 | y: 84
209 | from: -24
210 | to: 24
211 | onValueChanged: {
212 | synthModel.masterTranspose=spinTranspose.value+24
213 | }
214 | }
215 |
216 | Label {
217 | id: label6
218 | x: 8
219 | y: 324
220 | width: 81
221 | height: 17
222 | color: "#ffffff"
223 | text: qsTr("Vel. Offset")
224 | font.pixelSize: 16
225 | }
226 |
227 | SpinBox {
228 | id: spinVelocityOffset
229 | x: 101
230 | y: 312
231 | to: 127
232 | from: 0
233 | onValueChanged: {
234 | synthModel.masterVelocityOffset=spinVelocityOffset.value
235 | }
236 | }
237 |
238 | Slider {
239 | id: sliderPortamentoTime
240 | x: 371
241 | y: 146
242 | width: 40
243 | height: 161
244 | orientation: Qt.Vertical
245 | to: 255
246 | onValueChanged: {
247 | synthModel.portamentoTime=sliderPortamentoTime.value;
248 | }
249 | }
250 |
251 | Switch {
252 | id: switchLegato
253 | x: 94
254 | y: 203
255 | text: qsTr("Mono")
256 | font.pixelSize: 16
257 | onCheckedChanged: {
258 | synthModel.masterLegato=switchLegato.checked ? 1 : 0
259 | }
260 | }
261 |
262 | Label {
263 | id: label7
264 | x: 347
265 | y: 313
266 | width: 88
267 | height: 38
268 | color: "#ffffff"
269 | text: qsTr("Portamento Time")
270 | font.pixelSize: 16
271 | horizontalAlignment: Text.AlignHCenter
272 | wrapMode: Text.WordWrap
273 | }
274 |
275 | Label {
276 | id: label8
277 | x: 58
278 | y: 213
279 | height: 17
280 | color: "#ffffff"
281 | text: qsTr("Poly")
282 | font.pixelSize: 16
283 | }
284 |
285 | Label {
286 | id: label9
287 | x: -11
288 | y: 267
289 | width: 100
290 | height: 36
291 | color: "#ffffff"
292 | text: qsTr("Portamento Mode")
293 | font.pixelSize: 16
294 | wrapMode: Text.WordWrap
295 | horizontalAlignment: Text.AlignRight
296 | }
297 |
298 | SpinBox {
299 | id: spinPortamentoMode
300 | x: 101
301 | y: 265
302 | width: 178
303 | height: 40
304 | to: 2
305 | onValueChanged: {
306 | synthModel.portamentoMode=spinPortamentoMode.value
307 | }
308 | textFromValue: function(value, locale) {
309 | var clist=["OFF", "ALWAYS", "FINGERED"];
310 |
311 | return clist[value];
312 | }
313 | }
314 |
315 | Label {
316 | id: label10
317 | x: 2
318 | y: 29
319 | color: "#ffffff"
320 | text: qsTr("Patch Name")
321 | font.pixelSize: 16
322 | }
323 |
324 | /* This custom keyboard is required on the Raspberry Pi/Touch screen implementation
325 | * but you won't need it for a Desktop version.
326 | */
327 | CustomKeyboard {
328 | id: keyboard
329 | width: 800
330 | height: 260
331 | x: 0
332 | y: 97
333 | visible: false;
334 | z: 1000
335 |
336 | onKeyPressed: {
337 | if (keyval === "LEFT") {
338 | if (textName.cursorPosition > 0) {
339 | textName.cursorPosition=textName.cursorPosition-1;
340 | textName.cursorVisible=true;
341 | textName.focus=true;
342 | }
343 | } else if (keyval === "RIGHT") {
344 | if (textName.cursorPosition < textName.text.length) {
345 | textName.cursorPosition=textName.cursorPosition+1;
346 | textName.cursorVisible=true;
347 | textName.focus=true;
348 | }
349 | } else if (keyval === "DEL") {
350 | if (textName.cursorPosition > 0) {
351 | var oldpos=textName.cursorPosition;
352 |
353 | var leftText=textName.text.substring(0, oldpos-1);
354 | var rightText=textName.text.substring(oldpos, textName.text.length);
355 |
356 | textName.text=leftText+""+rightText;
357 | textName.cursorPosition=oldpos-1;
358 | textName.cursorVisible=true;
359 | textName.focus=true;
360 | }
361 | } else if (keyval === "ENTER") { // Dismiss and move to next field
362 | keyboard.visible=false;
363 | textName.cursorVisible=false;
364 | } else {
365 | var charToInsert=keyval;
366 | if (textName.cursorPosition > 0) {
367 | var prevchar=textName.text.charAt(textName.cursorPosition-1);
368 |
369 | if (prevchar !== ' ') {
370 | charToInsert=charToInsert.toLowerCase();
371 | }
372 | }
373 | textName.insert(textName.cursorPosition, charToInsert);
374 | textName.focus=true;
375 | }
376 | }
377 |
378 | }
379 |
380 | TapHandler {
381 | onTapped: {
382 | keyboard.visible=false;
383 | textName.cursorVisible=false;
384 | }
385 | onCanceled: {
386 | }
387 | }
388 |
389 | Component.onCompleted: {
390 | updatePage();
391 | }
392 |
393 | }
394 |
395 | /*##^##
396 | Designer {
397 | D{i:1;anchors_height:400;anchors_x:0;anchors_y:0}
398 | }
399 | ##^##*/
400 |
--------------------------------------------------------------------------------
/src/main.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Window 2.12
26 | import QtQuick.Controls 2.3
27 |
28 | Window {
29 | id: mainWindow
30 | visible: true
31 | width: 800
32 | height: 480
33 | title: qsTr("XFm2 Synth")
34 |
35 | // Note: This app was designed to run on a Raspberry Pi with an 800x480 touch screen
36 | // Remove the visibility line below if you want to run it as a Desktop application!
37 | visibility: "FullScreen"
38 |
39 | Rectangle {
40 | anchors.fill: parent
41 | color: "#505050"
42 | }
43 |
44 | CommonPage {
45 | id: commonPage
46 | anchors.fill: tabArea
47 | visible: true
48 | }
49 |
50 | AlgorithmPage {
51 | id: algorithmPage
52 | anchors.fill: tabArea
53 | visible: false
54 | }
55 |
56 | OperatorPage {
57 | id: operatorPage
58 | anchors.fill: tabArea
59 | visible: false
60 | }
61 |
62 | EnvelopePage {
63 | id: envelopePage
64 | anchors.fill: tabArea
65 | visible: false
66 | }
67 |
68 | LFOPage {
69 | id: lfoPage
70 | anchors.fill: tabArea
71 | visible: false
72 | }
73 |
74 | ModulationPage {
75 | id: modulationPage
76 | anchors.fill: tabArea
77 | visible: false
78 | }
79 |
80 | ArpeggiatorPage {
81 | id: arpeggiatorPage
82 | anchors.fill: tabArea
83 | visible: false
84 | }
85 |
86 | EffectsPage {
87 | id: effectsPage
88 | anchors.fill: tabArea
89 | visible: false
90 | }
91 |
92 | EffectsPage2 {
93 | id: effectsPage2
94 | anchors.fill: tabArea
95 | visible: false
96 | }
97 |
98 | Label {
99 | id: element
100 | x: 34
101 | y: 34
102 | color: "#ffb060"
103 | text: qsTr("Patch")
104 | font.pixelSize: 22
105 | }
106 |
107 | SpinBox {
108 | id: spinPatchNumber
109 | x: 107
110 | y: 27
111 | font.pixelSize: 22
112 | to: 127
113 |
114 | onValueChanged: {
115 | synthModel.patchNumber=spinPatchNumber.value;
116 | }
117 | }
118 |
119 | GroupBox {
120 | id: groupBox
121 | x: 0
122 | y: 79
123 | width: 800
124 | height: 1
125 | title: qsTr("")
126 | }
127 |
128 | TabBar {
129 | id: tabBar
130 | y: 440
131 | position: TabBar.Footer
132 | anchors.right: parent.right
133 | anchors.rightMargin: 0
134 | anchors.left: parent.left
135 | anchors.leftMargin: 0
136 | anchors.bottom: parent.bottom
137 | anchors.bottomMargin: 0
138 |
139 | TabButton {
140 | id: tabButtonCommon
141 | x: 0
142 | y: 0
143 | text: qsTr("Common")
144 | }
145 |
146 | TabButton {
147 | id: tabButtonAlgorithm
148 | text: qsTr("ALGO")
149 | }
150 |
151 | TabButton {
152 | id: tabButtonOperator
153 | text: qsTr("OPER")
154 | }
155 |
156 | TabButton {
157 | id: tabButtonEnvelope
158 | text: qsTr("ENV")
159 | }
160 |
161 | TabButton {
162 | id: tabButtonLFO
163 | text: qsTr("LFO")
164 | }
165 |
166 | TabButton {
167 | id: tabButtonModulation
168 | text: qsTr("MOD")
169 | }
170 |
171 | TabButton {
172 | id: tabButtonArpeggiator
173 | text: qsTr("ARP")
174 | }
175 |
176 | TabButton {
177 | id: tabButtonEffects
178 | text: qsTr("FX1")
179 | }
180 |
181 | TabButton {
182 | id: tabButtonEffects2
183 | text: qsTr("FX2")
184 | }
185 |
186 | onCurrentIndexChanged: {
187 | switch (tabBar.currentIndex) {
188 | case 0: // Common
189 | commonPage.visible=true;
190 | algorithmPage.visible=false;
191 | operatorPage.visible=false;
192 | envelopePage.visible=false;
193 | lfoPage.visible=false;
194 | modulationPage.visible=false;
195 | arpeggiatorPage.visible=false;
196 | effectsPage.visible=false;
197 | effectsPage2.visible=false;
198 | break;
199 |
200 | case 1: // Algorithm
201 | commonPage.visible=true;
202 | algorithmPage.visible=true;
203 | operatorPage.visible=false;
204 | envelopePage.visible=false;
205 | lfoPage.visible=false;
206 | modulationPage.visible=false;
207 | arpeggiatorPage.visible=false;
208 | effectsPage.visible=false;
209 | effectsPage2.visible=false;
210 | break;
211 |
212 | case 2: // Operator
213 | commonPage.visible=false;
214 | algorithmPage.visible=false;
215 | operatorPage.visible=true;
216 | envelopePage.visible=false;
217 | lfoPage.visible=false;
218 | modulationPage.visible=false;
219 | arpeggiatorPage.visible=false;
220 | effectsPage.visible=false;
221 | effectsPage2.visible=false;
222 | break;
223 |
224 | case 3: // Envelope
225 | commonPage.visible=false;
226 | algorithmPage.visible=false;
227 | operatorPage.visible=false;
228 | envelopePage.visible=true;
229 | lfoPage.visible=false;
230 | modulationPage.visible=false;
231 | arpeggiatorPage.visible=false;
232 | effectsPage.visible=false;
233 | effectsPage2.visible=false;
234 | break;
235 |
236 | case 4: // LFO
237 | commonPage.visible=false;
238 | algorithmPage.visible=false;
239 | operatorPage.visible=false;
240 | envelopePage.visible=false;
241 | lfoPage.visible=true;
242 | modulationPage.visible=false;
243 | arpeggiatorPage.visible=false;
244 | effectsPage.visible=false;
245 | effectsPage2.visible=false;
246 | break;
247 |
248 | case 5: // Modulation
249 | commonPage.visible=false;
250 | algorithmPage.visible=false;
251 | operatorPage.visible=false;
252 | envelopePage.visible=false;
253 | lfoPage.visible=false;
254 | modulationPage.visible=true;
255 | arpeggiatorPage.visible=false;
256 | effectsPage.visible=false;
257 | effectsPage2.visible=false;
258 | break;
259 |
260 | case 6: // Arpeggiator
261 | commonPage.visible=false;
262 | algorithmPage.visible=false;
263 | operatorPage.visible=false;
264 | envelopePage.visible=false;
265 | lfoPage.visible=false;
266 | modulationPage.visible=false;
267 | arpeggiatorPage.visible=true;
268 | effectsPage.visible=false;
269 | effectsPage2.visible=false;
270 | break;
271 |
272 | case 7: // Effects
273 | commonPage.visible=false;
274 | algorithmPage.visible=false;
275 | operatorPage.visible=false;
276 | envelopePage.visible=false;
277 | lfoPage.visible=false;
278 | modulationPage.visible=false;
279 | arpeggiatorPage.visible=false;
280 | effectsPage.visible=true;
281 | effectsPage2.visible=false;
282 | break;
283 |
284 | case 8: // Effects 2
285 | commonPage.visible=false;
286 | algorithmPage.visible=false;
287 | operatorPage.visible=false;
288 | envelopePage.visible=false;
289 | lfoPage.visible=false;
290 | modulationPage.visible=false;
291 | arpeggiatorPage.visible=false;
292 | effectsPage.visible=false;
293 | effectsPage2.visible=true;
294 | break;
295 |
296 | default:
297 | break;
298 | }
299 | }
300 | }
301 |
302 | Item {
303 | id: tabArea
304 | x: 0
305 | y: 83
306 | width: 800
307 | height: 357
308 | }
309 |
310 | Button {
311 | id: buttonWrite
312 | x: 674
313 | y: 27
314 | text: qsTr("WRITE")
315 | font.pointSize: 16
316 | onClicked: {
317 | popupWriteMessage.open();
318 | }
319 | }
320 |
321 | Button {
322 | id: buttonReload
323 | x: 280
324 | y: 27
325 | text: qsTr("READ")
326 | font.pointSize: 16
327 | onClicked: {
328 | popupReloadMessage.open();
329 | }
330 | }
331 |
332 | Button {
333 | id: buttonInit
334 | x: 417
335 | y: 27
336 | text: qsTr("INIT")
337 | font.pointSize: 16
338 | onClicked: {
339 | popupInitMessage.open();
340 | }
341 | }
342 |
343 | PopupMessage {
344 | id: popupInitMessage
345 | titleText: "Initialise Patch"
346 | detailText: "Do you want to init this patch?"
347 | okButtonText: "Yes"
348 | cancelButtonText: "No"
349 | onOkClicked: {
350 | synthModel.initPatchBuffer();
351 | }
352 | }
353 |
354 | PopupMessage {
355 | id: popupReloadMessage
356 | titleText: "Load Patch"
357 | detailText: "Do you want to reload this patch?"
358 | okButtonText: "Yes"
359 | cancelButtonText: "No"
360 | onOkClicked: {
361 | synthModel.reloadPatch();
362 | }
363 | }
364 |
365 | PopupMessage {
366 | id: popupWriteMessage
367 | titleText: "Write Patch"
368 | detailText: "Do you want to save this patch?"
369 | okButtonText: "Yes"
370 | cancelButtonText: "No"
371 | onOkClicked: {
372 | synthModel.writePatchBuffer(-1);
373 | }
374 | }
375 |
376 | Component.onCompleted: {
377 | spinPatchNumber.value=synthModel.patchNumber;
378 | synthModel.readPatchBuffer();
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/src/EffectsPage2.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 | Item {
30 | id: effectsPage2
31 | width: 800
32 | height: 357
33 |
34 |
35 | Rectangle {
36 | anchors.fill: parent
37 | color: "#505050"
38 | }
39 |
40 | Connections {
41 | target: synthModel
42 | onPatchNumberChanged: {
43 | updatePage();
44 | }
45 | }
46 |
47 | function updatePage()
48 | {
49 | dialBitCrusher.value=synthModel.fxBitCrushDepth;
50 | dialDecimator.value=synthModel.fxDecimator;
51 | dialLowPassFilter.value=synthModel.filterLoCutoff;
52 | dialHighPassFilter.value=synthModel.filterHiCutoff;
53 |
54 | dialAMDepth.value=synthModel.fxAMDepth;
55 | dialAMSpeed.value=synthModel.fxAMSpeed;
56 | dialAMRange.value=synthModel.fxAMRange;
57 | dialAMPhase.value=synthModel.fxAMPhase;
58 |
59 | dialReverbDry.value=synthModel.fxReverbDry;
60 | dialReverbWet.value=synthModel.fxReverbWet;
61 | dialReverbDecay.value=synthModel.fxReverbDecay;
62 | dialReverbDamp.value=synthModel.fxReverbDamp;
63 | spinReverbMode.value=synthModel.fxReverbMode;
64 |
65 | switchFXRoute.checked=synthModel.fxRoute !== 0;
66 | }
67 |
68 | Rectangle {
69 | id: rectangle1
70 | x: 109
71 | y: 175
72 | width: 508
73 | height: 2
74 | color: "#ffffff"
75 | border.color: "#ffb060"
76 | }
77 |
78 | Switch {
79 | id: switchFXRoute
80 | x: 623
81 | y: 218
82 | text: qsTr("Delay-Chorus")
83 | font.pixelSize: 16
84 | onCheckedChanged: {
85 | if (switchFXRoute.checked) {
86 | synthModel.fxRoute=1;
87 | } else {
88 | synthModel.fxRoute=0;
89 | }
90 | }
91 | }
92 |
93 | Rectangle {
94 | id: rectangle
95 | x: 61
96 | y: 17
97 | width: 731
98 | height: 2
99 | color: "#ffffff"
100 | border.color: "#ffb060"
101 | }
102 |
103 | Label {
104 | id: label
105 | x: 8
106 | y: 110
107 | color: "#ffffff"
108 | text: qsTr("Bit Crusher")
109 | font.pixelSize: 16
110 | }
111 |
112 | CustomDial {
113 | id: dialBitCrusher
114 | x: 14
115 | y: 40
116 | width: 64
117 | height: 64
118 | maximumValue: 24
119 | onValueChanged: {
120 | synthModel.fxBitCrushDepth=dialBitCrusher.value;
121 | }
122 | }
123 |
124 | CustomDial {
125 | id: dialDecimator
126 | x: 104
127 | y: 40
128 | width: 64
129 | height: 64
130 | maximumValue: 255
131 | onValueChanged: {
132 | synthModel.fxDecimator=dialDecimator.value;
133 | }
134 | }
135 |
136 | Label {
137 | id: label1
138 | x: 101
139 | y: 110
140 | width: 71
141 | height: 17
142 | color: "#ffffff"
143 | text: qsTr("Decimator")
144 | font.pixelSize: 16
145 | }
146 |
147 | CustomDial {
148 | id: dialLowPassFilter
149 | x: 194
150 | y: 40
151 | width: 64
152 | height: 64
153 | maximumValue: 255
154 | onValueChanged: {
155 | synthModel.filterLoCutoff=dialLowPassFilter.value;
156 | }
157 | }
158 |
159 | Label {
160 | id: label2
161 | x: 190
162 | y: 110
163 | width: 72
164 | height: 17
165 | color: "#ffffff"
166 | text: qsTr("Low Pass")
167 | font.pixelSize: 16
168 | horizontalAlignment: Text.AlignHCenter
169 | }
170 |
171 | CustomDial {
172 | id: dialHighPassFilter
173 | x: 284
174 | y: 40
175 | width: 64
176 | height: 64
177 | maximumValue: 255
178 | onValueChanged: {
179 | synthModel.filterHiCutoff=dialHighPassFilter.value;
180 | }
181 | }
182 |
183 | Label {
184 | id: label3
185 | x: 285
186 | y: 110
187 | width: 62
188 | height: 17
189 | color: "#ffffff"
190 | text: qsTr("Hi Pass")
191 | font.pixelSize: 16
192 | horizontalAlignment: Text.AlignHCenter
193 | }
194 |
195 | Label {
196 | id: label4
197 | x: 0
198 | y: 10
199 | color: "#ffb060"
200 | text: qsTr("Effects")
201 | font.pixelSize: 16
202 | }
203 |
204 | Label {
205 | id: label5
206 | x: 0
207 | y: 166
208 | color: "#ffb060"
209 | text: qsTr("Global Reverb")
210 | font.pixelSize: 16
211 | }
212 |
213 | CustomDial {
214 | id: dialAMDepth
215 | x: 419
216 | y: 40
217 | width: 64
218 | height: 64
219 | maximumValue: 255
220 | onValueChanged: {
221 | synthModel.fxAMDepth=dialAMDepth.value;
222 | }
223 | }
224 |
225 | Label {
226 | id: label6
227 | x: 420
228 | y: 110
229 | width: 62
230 | height: 35
231 | color: "#ffffff"
232 | text: qsTr("AM Depth")
233 | font.pixelSize: 16
234 | wrapMode: Text.WordWrap
235 | verticalAlignment: Text.AlignVCenter
236 | horizontalAlignment: Text.AlignHCenter
237 | }
238 |
239 | CustomDial {
240 | id: dialAMSpeed
241 | x: 509
242 | y: 40
243 | width: 64
244 | height: 64
245 | maximumValue: 255
246 | onValueChanged: {
247 | synthModel.fxAMSpeed=dialAMSpeed.value;
248 | }
249 | }
250 |
251 | Label {
252 | id: label7
253 | x: 510
254 | y: 110
255 | width: 62
256 | height: 35
257 | color: "#ffffff"
258 | text: qsTr("AM Speed")
259 | font.pixelSize: 16
260 | wrapMode: Text.WordWrap
261 | verticalAlignment: Text.AlignVCenter
262 | horizontalAlignment: Text.AlignHCenter
263 | }
264 |
265 | CustomDial {
266 | id: dialAMRange
267 | x: 599
268 | y: 40
269 | width: 64
270 | height: 64
271 | maximumValue: 255
272 | onValueChanged: {
273 | synthModel.fxAMRange=dialAMRange.value;
274 | }
275 |
276 | }
277 |
278 | Label {
279 | id: label8
280 | x: 600
281 | y: 110
282 | width: 62
283 | height: 35
284 | color: "#ffffff"
285 | text: qsTr("AM Range")
286 | font.pixelSize: 16
287 | wrapMode: Text.WordWrap
288 | verticalAlignment: Text.AlignVCenter
289 | horizontalAlignment: Text.AlignHCenter
290 | }
291 |
292 | CustomDial {
293 | id: dialAMPhase
294 | x: 689
295 | y: 40
296 | width: 64
297 | height: 64
298 | maximumValue: 255
299 | onValueChanged: {
300 | synthModel.fxAMPhase=dialAMPhase.value;
301 | }
302 | }
303 |
304 | Label {
305 | id: label9
306 | x: 690
307 | y: 110
308 | width: 62
309 | height: 35
310 | color: "#ffffff"
311 | text: qsTr("AM Phase")
312 | font.pixelSize: 16
313 | wrapMode: Text.WordWrap
314 | verticalAlignment: Text.AlignVCenter
315 | horizontalAlignment: Text.AlignHCenter
316 | }
317 |
318 | CustomDial {
319 | id: dialReverbDry
320 | x: 14
321 | y: 205
322 | width: 64
323 | height: 64
324 | maximumValue: 255
325 | onValueChanged: {
326 | synthModel.fxReverbDry=dialReverbDry.value
327 | }
328 | }
329 |
330 | Label {
331 | id: label10
332 | x: 35
333 | y: 275
334 | color: "#ffffff"
335 | text: qsTr("Dry")
336 | font.pixelSize: 16
337 | horizontalAlignment: Text.AlignHCenter
338 | }
339 |
340 | CustomDial {
341 | id: dialReverbWet
342 | x: 104
343 | y: 205
344 | width: 64
345 | height: 64
346 | maximumValue: 255
347 | onValueChanged: {
348 | synthModel.fxReverbWet=dialReverbWet.value;
349 | }
350 | }
351 |
352 | Label {
353 | id: label11
354 | x: 122
355 | y: 275
356 | width: 28
357 | height: 17
358 | color: "#ffffff"
359 | text: qsTr("Wet")
360 | font.pixelSize: 16
361 | horizontalAlignment: Text.AlignHCenter
362 | }
363 |
364 | CustomDial {
365 | id: dialReverbDecay
366 | x: 194
367 | y: 205
368 | width: 64
369 | height: 64
370 | maximumValue: 255
371 | onValueChanged: {
372 | synthModel.fxReverbDecay=dialReverbDecay.value;
373 | }
374 | }
375 |
376 | Label {
377 | id: label12
378 | x: 203
379 | y: 275
380 | width: 46
381 | height: 17
382 | color: "#ffffff"
383 | text: qsTr("Decay")
384 | font.pixelSize: 16
385 | horizontalAlignment: Text.AlignHCenter
386 | }
387 |
388 | CustomDial {
389 | id: dialReverbDamp
390 | x: 284
391 | y: 205
392 | width: 64
393 | height: 64
394 | maximumValue: 255
395 | onValueChanged: {
396 | synthModel.fxReverbDamp=dialReverbDamp.value;
397 | }
398 | }
399 |
400 | Label {
401 | id: label13
402 | x: 293
403 | y: 275
404 | width: 46
405 | height: 17
406 | color: "#ffffff"
407 | text: qsTr("Damp")
408 | font.pixelSize: 16
409 | horizontalAlignment: Text.AlignHCenter
410 | }
411 |
412 | SpinBox {
413 | id: spinReverbMode
414 | x: 440
415 | y: 217
416 | width: 160
417 | height: 40
418 | to: 1
419 | font.pointSize: 14
420 | onValueChanged: {
421 | synthModel.fxReverbMode=spinReverbMode.value;
422 | }
423 | textFromValue: function(value, locale) {
424 | var vals=["Plate", "Hall"];
425 |
426 | return vals[value];
427 | }
428 | }
429 |
430 | Label {
431 | id: label14
432 | x: 374
433 | y: 229
434 | width: 46
435 | height: 17
436 | color: "#ffffff"
437 | text: qsTr("Mode")
438 | font.pixelSize: 16
439 | horizontalAlignment: Text.AlignRight
440 | }
441 |
442 | Rectangle {
443 | id: rectangle2
444 | x: 698
445 | y: 175
446 | width: 90
447 | height: 2
448 | color: "#ffffff"
449 | border.color: "#ffb060"
450 | }
451 |
452 | Label {
453 | id: label15
454 | x: 628
455 | y: 166
456 | color: "#ffb060"
457 | text: qsTr("FX Route")
458 | font.pixelSize: 16
459 | }
460 |
461 | Component.onCompleted: {
462 | updatePage();
463 | }
464 | }
465 |
--------------------------------------------------------------------------------
/src/xfmoperator.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | #include
25 | #include
26 | #include "xfmoperator.h"
27 | #include "SynthModel.h"
28 |
29 | /* This is the implementation of a single FM operator for XFM2
30 | * Really the class just holds a set of properties and gives
31 | * them nice names that make it easy to use. The main SynthModel
32 | * actually talks to the hardware
33 | */
34 |
35 | XFMOperator::XFMOperator(QObject *parent) : QObject(parent)
36 | {
37 | m_operator=0;
38 | m_algorithm=0;
39 | m_ratio=0;
40 | m_ratiofine=0;
41 | m_fine=0;
42 | m_level=0;
43 | m_levelLeft=0;
44 | m_levelRight=0;
45 | m_velocitysensitivity=0;
46 | m_keyboardbreakpoint=0;
47 | m_keyboardscaleleft=0;
48 | m_keyboardscaleright=0;
49 | m_keyboardcurveleft=0;
50 | m_keyboardcurveright=0;
51 | m_l0=0;
52 | m_l1=0;
53 | m_l2=0;
54 | m_l3=0;
55 | m_l4=0;
56 | m_l5=0;
57 | m_r0=0;
58 | m_r1=0;
59 | m_r2=0;
60 | m_r3=0;
61 | m_r4=0;
62 | m_r5=0;
63 | m_ratekey=0;
64 | m_ams=0;
65 | m_pms=0;
66 | m_wave1=0;
67 | m_wave2=0;
68 | m_wmode=0;
69 | m_wratio=0;
70 | m_phase=0;
71 | }
72 |
73 | int XFMOperator::operatorNumber() const
74 | {
75 | return m_operator;
76 | }
77 |
78 | void XFMOperator::setOperatorNumber(int n)
79 | {
80 | if (n != m_operator) {
81 | m_operator=n;
82 | emit operatorNumberChanged();
83 | }
84 | }
85 |
86 | int XFMOperator::algorithm() const
87 | {
88 | return static_cast(m_algorithm);
89 | }
90 |
91 | void XFMOperator::setAlgorithm(int a)
92 | {
93 | unsigned char aa=static_cast(a);
94 | if (aa != m_algorithm) {
95 | m_algorithm=aa;
96 | emit algorithmChanged();
97 | }
98 | }
99 |
100 | int XFMOperator::feedback() const
101 | {
102 | return static_cast(m_feedback);
103 | }
104 |
105 | void XFMOperator::setFeedback(int f)
106 | {
107 | unsigned char ff=static_cast(f);
108 | if (ff != m_feedback) {
109 | m_feedback=ff;
110 | emit feedbackChanged();
111 | }
112 | }
113 |
114 | int XFMOperator::ratio() const
115 | {
116 | return static_cast(m_ratio);
117 | }
118 |
119 | void XFMOperator::setRatio(int f)
120 | {
121 | unsigned char ff=static_cast(f);
122 | if (ff != m_ratio) {
123 | m_ratio=ff;
124 | emit ratioChanged();
125 | }
126 | }
127 |
128 | int XFMOperator::ratioFine() const
129 | {
130 | return static_cast(m_ratiofine);
131 | }
132 |
133 | void XFMOperator::setRatioFine(int f)
134 | {
135 | unsigned char ff=static_cast(f);
136 | if (ff != m_ratiofine) {
137 | m_ratiofine=ff;
138 | emit ratioFineChanged();
139 | }
140 | }
141 |
142 | int XFMOperator::fine() const
143 | {
144 | return static_cast(m_fine);
145 | }
146 |
147 | void XFMOperator::setFine(int f)
148 | {
149 | unsigned char ff=static_cast(f);
150 | if (ff != m_fine) {
151 | m_fine=ff;
152 | emit fineChanged();
153 | }
154 | }
155 |
156 | int XFMOperator::level() const
157 | {
158 | return static_cast(m_level);
159 | }
160 |
161 | void XFMOperator::setLevel(int f)
162 | {
163 | unsigned char ff=static_cast(f);
164 | if (ff != m_level) {
165 | m_level=ff;
166 | emit levelChanged();
167 | }
168 | }
169 |
170 | int XFMOperator::levelLeft() const
171 | {
172 | return static_cast(m_levelLeft);
173 | }
174 |
175 | void XFMOperator::setLevelLeft(int f)
176 | {
177 | unsigned char ff=static_cast(f);
178 | if (ff != m_levelLeft) {
179 | m_levelLeft=ff;
180 | emit levelLeftChanged();
181 | }
182 | }
183 |
184 | int XFMOperator::levelRight() const
185 | {
186 | return static_cast(m_levelRight);
187 | }
188 |
189 | void XFMOperator::setLevelRight(int f)
190 | {
191 | unsigned char ff=static_cast(f);
192 | if (ff != m_levelRight) {
193 | m_levelRight=ff;
194 | emit levelRightChanged();
195 | }
196 | }
197 |
198 |
199 | int XFMOperator::velocitySensitivity() const
200 | {
201 | return static_cast(m_velocitysensitivity);
202 | }
203 |
204 | void XFMOperator::setVelocitySensitivity(int f)
205 | {
206 | unsigned char ff=static_cast(f);
207 | if (ff != m_velocitysensitivity) {
208 | m_velocitysensitivity=ff;
209 | emit velocitySensitivityChanged();
210 | }
211 | }
212 |
213 | int XFMOperator::keyboardBreakpoint() const
214 | {
215 | return static_cast(m_keyboardbreakpoint);
216 | }
217 |
218 | void XFMOperator::setKeyboardBreakpoint(int f)
219 | {
220 | unsigned char ff=static_cast(f);
221 | if (ff != m_keyboardbreakpoint) {
222 | m_keyboardbreakpoint=ff;
223 | emit keyboardBreakpointChanged();
224 | }
225 | }
226 |
227 | int XFMOperator::keyboardScaleLeft() const
228 | {
229 | return static_cast(m_keyboardscaleleft);
230 | }
231 |
232 | void XFMOperator::setKeyboardScaleLeft(int f)
233 | {
234 | unsigned char ff=static_cast(f);
235 | if (ff != m_keyboardscaleleft) {
236 | m_keyboardscaleleft=ff;
237 | emit keyboardScaleLeftChanged();
238 | }
239 | }
240 |
241 | int XFMOperator::keyboardScaleRight() const
242 | {
243 | return static_cast(m_keyboardscaleright);
244 | }
245 |
246 | void XFMOperator::setKeyboardScaleRight(int f)
247 | {
248 | unsigned char ff=static_cast(f);
249 | if (ff != m_keyboardscaleright) {
250 | m_keyboardscaleright=ff;
251 | emit keyboardScaleRightChanged();
252 | }
253 | }
254 |
255 | int XFMOperator::keyboardCurveLeft() const
256 | {
257 | return static_cast(m_keyboardcurveleft);
258 | }
259 |
260 | void XFMOperator::setKeyboardCurveLeft(int f)
261 | {
262 | unsigned char ff=static_cast(f);
263 | if (ff != m_keyboardcurveleft) {
264 | m_keyboardcurveleft=ff;
265 | emit keyboardCurveLeftChanged();
266 | }
267 | }
268 |
269 | int XFMOperator::keyboardCurveRight() const
270 | {
271 | return static_cast(m_keyboardcurveright);
272 | }
273 |
274 | void XFMOperator::setKeyboardCurveRight(int f)
275 | {
276 | unsigned char ff=static_cast(f);
277 | if (ff != m_keyboardcurveright) {
278 | m_keyboardcurveright=ff;
279 | emit keyboardCurveRightChanged();
280 | }
281 | }
282 |
283 | int XFMOperator::L0() const
284 | {
285 | return static_cast(m_l0);
286 | }
287 |
288 | void XFMOperator::setL0(int f)
289 | {
290 | unsigned char ff=static_cast(f);
291 | if (ff != m_l0) {
292 | m_l0=ff;
293 | emit L0Changed();
294 | }
295 | }
296 |
297 | int XFMOperator::L1() const
298 | {
299 | return static_cast(m_l1);
300 | }
301 |
302 | void XFMOperator::setL1(int f)
303 | {
304 | unsigned char ff=static_cast(f);
305 | if (ff != m_l1) {
306 | m_l1=ff;
307 | emit L1Changed();
308 | }
309 | }
310 |
311 | int XFMOperator::L2() const
312 | {
313 | return static_cast(m_l2);
314 | }
315 |
316 | void XFMOperator::setL2(int f)
317 | {
318 | unsigned char ff=static_cast(f);
319 | if (ff != m_l2) {
320 | m_l2=ff;
321 | emit L2Changed();
322 | }
323 | }
324 |
325 | int XFMOperator::L3() const
326 | {
327 | return static_cast(m_l3);
328 | }
329 |
330 | void XFMOperator::setL3(int f)
331 | {
332 | unsigned char ff=static_cast(f);
333 | if (ff != m_l3) {
334 | m_l3=ff;
335 | emit L3Changed();
336 | }
337 | }
338 |
339 | int XFMOperator::L4() const
340 | {
341 | return static_cast(m_l4);
342 | }
343 |
344 | void XFMOperator::setL4(int f)
345 | {
346 | unsigned char ff=static_cast(f);
347 | if (ff != m_l4) {
348 | m_l4=ff;
349 | emit L4Changed();
350 | }
351 | }
352 |
353 | int XFMOperator::L5() const
354 | {
355 | return static_cast(m_l5);
356 | }
357 |
358 | void XFMOperator::setL5(int f)
359 | {
360 | unsigned char ff=static_cast(f);
361 | if (ff != m_l5) {
362 | m_l5=ff;
363 | emit L5Changed();
364 | }
365 | }
366 |
367 | int XFMOperator::R0() const
368 | {
369 | return static_cast(m_r0);
370 | }
371 |
372 | void XFMOperator::setR0(int f)
373 | {
374 | unsigned char ff=static_cast(f);
375 | if (ff != m_r0) {
376 | m_r0=ff;
377 | emit R0Changed();
378 | }
379 | }
380 |
381 | int XFMOperator::R1() const
382 | {
383 | return static_cast(m_r1);
384 | }
385 |
386 | void XFMOperator::setR1(int f)
387 | {
388 | unsigned char ff=static_cast(f);
389 | if (ff != m_r1) {
390 | m_r1=ff;
391 | emit R1Changed();
392 | }
393 | }
394 |
395 | int XFMOperator::R2() const
396 | {
397 | return static_cast(m_r2);
398 | }
399 |
400 | void XFMOperator::setR2(int f)
401 | {
402 | unsigned char ff=static_cast(f);
403 | if (ff != m_r2) {
404 | m_r2=ff;
405 | emit R2Changed();
406 | }
407 | }
408 |
409 | int XFMOperator::R3() const
410 | {
411 | return static_cast(m_r3);
412 | }
413 |
414 | void XFMOperator::setR3(int f)
415 | {
416 | unsigned char ff=static_cast(f);
417 | if (ff != m_r3) {
418 | m_r3=ff;
419 | emit R3Changed();
420 | }
421 | }
422 |
423 | int XFMOperator::R4() const
424 | {
425 | return static_cast(m_r4);
426 | }
427 |
428 | void XFMOperator::setR4(int f)
429 | {
430 | unsigned char ff=static_cast(f);
431 | if (ff != m_r4) {
432 | m_r4=ff;
433 | emit R4Changed();
434 | }
435 | }
436 |
437 | int XFMOperator::R5() const
438 | {
439 | return static_cast(m_r5);
440 | }
441 |
442 | void XFMOperator::setR5(int f)
443 | {
444 | unsigned char ff=static_cast(f);
445 | if (ff != m_r5) {
446 | m_r5=ff;
447 | emit R5Changed();
448 | }
449 | }
450 |
451 | int XFMOperator::rateKey() const
452 | {
453 | return static_cast(m_ratekey);
454 | }
455 |
456 | void XFMOperator::setRateKey(int f)
457 | {
458 | unsigned char ff=static_cast(f);
459 | if (ff != m_ratekey) {
460 | m_ratekey=ff;
461 | emit rateKeyChanged();
462 | }
463 | }
464 |
465 | int XFMOperator::amplitudeModulationSensitivity() const
466 | {
467 | return static_cast(m_ams);
468 | }
469 |
470 | void XFMOperator::setAmplitudeModulationSensitivity(int v)
471 | {
472 | unsigned char vv=static_cast(v);
473 | if (vv != m_ams) {
474 | m_ams=vv;
475 | emit amplitudeModulationSensitivityChanged();
476 | }
477 | }
478 |
479 | int XFMOperator::pitchModulationSensitivity() const
480 | {
481 | return static_cast(m_pms);
482 | }
483 |
484 | void XFMOperator::setPitchModulationSensitivity(int v)
485 | {
486 | unsigned char vv=static_cast(v);
487 | if (vv != m_pms) {
488 | m_pms=vv;
489 | emit pitchModulationSensitivityChanged();
490 | }
491 | }
492 |
493 | int XFMOperator::wave1() const
494 | {
495 | return static_cast(m_wave1);
496 | }
497 |
498 | void XFMOperator::setWave1(int v)
499 | {
500 | unsigned char vv=static_cast(v);
501 | if (vv != m_wave1) {
502 | m_wave1=vv;
503 | emit wave1Changed();
504 | }
505 | }
506 |
507 |
508 | int XFMOperator::wave2() const
509 | {
510 | return static_cast(m_wave2);
511 | }
512 |
513 | void XFMOperator::setWave2(int v)
514 | {
515 | unsigned char vv=static_cast(v);
516 | if (vv != m_wave2) {
517 | m_wave2=vv;
518 | emit wave2Changed();
519 | }
520 | }
521 |
522 | int XFMOperator::oscillatorMode() const
523 | {
524 | return static_cast(m_wmode);
525 | }
526 |
527 | void XFMOperator::setOscillatorMode(int v)
528 | {
529 | unsigned char vv=static_cast(v);
530 | if (vv != m_wmode) {
531 | m_wmode=vv;
532 | emit oscillatorModeChanged();
533 | }
534 | }
535 |
536 | int XFMOperator::oscillatorRatio() const
537 | {
538 | return static_cast(m_wratio);
539 | }
540 |
541 | void XFMOperator::setOscillatorRatio(int v)
542 | {
543 | unsigned char vv=static_cast(v);
544 | if (vv != m_wratio) {
545 | m_wratio=vv;
546 | emit oscillatorRatioChanged();
547 | }
548 | }
549 |
550 | int XFMOperator::phase() const
551 | {
552 | return static_cast(m_phase);
553 | }
554 |
555 | void XFMOperator::setPhase(int v)
556 | {
557 | unsigned char vv=static_cast(v);
558 | if (vv != m_phase) {
559 | m_phase=vv;
560 | emit phaseChanged();
561 | }
562 | }
563 |
--------------------------------------------------------------------------------
/src/xfm2.h:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | #ifndef XFM2_H
25 | #define XFM2_H
26 |
27 | // This is a list of XFM2 parameter numbers so that
28 | // the rest of the app can reference them by name instead
29 | // of hard-coding the numbers
30 |
31 | enum XFM2Parameter {
32 | // Operators/Program
33 | ALGO1=1, // 1 = ALGO1
34 | ALGO2=2, // 2 = ALGO2
35 | ALGO3=3, // 3 = ALGO3
36 | ALGO4=4, // 4 = ALGO4
37 | ALGO5=5, // 5 = ALGO5
38 | ALGO6=6, // 6 = ALGO6
39 | OP_FEEDBACK1=7, // 7 = FEEDBACK1
40 | OP_FEEDBACK2=8, // 8 = FEEDBACK2
41 | OP_FEEDBACK3=9, // 9 = FEEDBACK3
42 | OP_FEEDBACK4=10, // 10 = FEEDBACK4
43 | OP_FEEDBACK5=11, // 11 = FEEDBACK5
44 | OP_FEEDBACK6=12, // 12 = FEEDBACK6
45 | OP_SYNC=13, // 13 = OPSYNC
46 | OP_MODE=14, // 14 = MODE
47 | OP_RATIO1=15, // 15 = OPRATIO1
48 | OP_RATIO2=16, // 16
49 | OP_RATIO3=17, // 17
50 | OP_RATIO4=18, // 18
51 | OP_RATIO5=19, // 19
52 | OP_RATIO6=20, // 20
53 | OP_RATIOFINE1=21, // 21
54 | OP_RATIOFINE2=22, // 22
55 | OP_RATIOFINE3=23, // 23
56 | OP_RATIOFINE4=24, // 24
57 | OP_RATIOFINE5=25, // 25
58 | OP_RATIOFINE6=26, // 26
59 | OP_FINE1=27, // 27
60 | OP_FINE2=28, // 28
61 | OP_FINE3=29, // 29
62 | OP_FINE4=30, // 30
63 | OP_FINE5=31, // 31
64 | OP_FINE6=32, // 32
65 | OP_LEVEL1=33, // 33
66 | OP_LEVEL2=34, // 34
67 | OP_LEVEL3=35, // 35
68 | OP_LEVEL4=36, // 36
69 | OP_LEVEL5=37, // 37
70 | OP_LEVEL6=38, // 38
71 | OP_VELO_SENS1=39, // 39
72 | OP_VELO_SENS2=40, // 40
73 | OP_VELO_SENS3=41, // 41
74 | OP_VELO_SENS4=42, // 42
75 | OP_VELO_SENS5=43, // 43
76 | OP_VELO_SENS6=44, // 44
77 | OP_KEY_BP1=45, // 45
78 | OP_KEY_BP2=46, // 46
79 | OP_KEY_BP3=47, // 47
80 | OP_KEY_BP4=48, // 48
81 | OP_KEY_BP5=49, // 49
82 | OP_KEY_BP6=50, // 50
83 | OP_KEY_LDEPTH1=51, // 51
84 | OP_KEY_LDEPTH2=52, // 52
85 | OP_KEY_LDEPTH3=53, // 53
86 | OP_KEY_LDEPTH4=54, // 54
87 | OP_KEY_LDEPTH5=55, // 55
88 | OP_KEY_LDEPTH6=56, // 56
89 | OP_KEY_RDEPTH1=57, // 57
90 | OP_KEY_RDEPTH2=58, // 58
91 | OP_KEY_RDEPTH3=59, // 59
92 | OP_KEY_RDEPTH4=60, // 60
93 | OP_KEY_RDEPTH5=61, // 61
94 | OP_KEY_RDEPTH6=62, // 62
95 | OP_KEY_LCURVE1=63, // 63
96 | OP_KEY_LCURVE2=64, // 64
97 | OP_KEY_LCURVE3=65, // 65
98 | OP_KEY_LCURVE4=66, // 66
99 | OP_KEY_LCURVE5=67, // 67
100 | OP_KEY_LCURVE6=68, // 68
101 | OP_KEY_RCURVE1=69, // 69
102 | OP_KEY_RCURVE2=70, // 70
103 | OP_KEY_RCURVE3=71, // 71
104 | OP_KEY_RCURVE4=72, // 72
105 | OP_KEY_RCURVE5=73, // 73
106 | OP_KEY_RCURVE6=74, // 74
107 | OP_LEVEL1_1=75, // 75
108 | OP_LEVEL1_2=76, // 76
109 | OP_LEVEL1_3=77, // 77
110 | OP_LEVEL1_4=78, // 78
111 | OP_LEVEL1_5=79, // 79
112 | OP_LEVEL1_6=80, // 80
113 | OP_LEVEL2_1=82, // 82
114 | OP_LEVEL2_2=83, // 83
115 | OP_LEVEL2_3=84, // 84
116 | OP_LEVEL2_4=85, // 85
117 | OP_LEVEL2_5=86, // 86
118 | OP_LEVEL2_6=87, // 87
119 | OP_LEVEL3_1=89, // 89
120 | OP_LEVEL3_2=90, // 90
121 | OP_LEVEL3_3=91, // 91
122 | OP_LEVEL3_4=92, // 92
123 | OP_LEVEL3_5=93, // 93
124 | OP_LEVEL3_6=94, // 94
125 | OP_LEVEL4_1=96, // 96
126 | OP_LEVEL4_2=97, // 97
127 | OP_LEVEL4_3=98, // 98
128 | OP_LEVEL4_4=99, // 99
129 | OP_LEVEL4_5=100, // 100
130 | OP_LEVEL4_6=101, // 101
131 | OP_RATE1_1=103, // 103
132 | OP_RATE1_2=104, // 104
133 | OP_RATE1_3=105, // 105
134 | OP_RATE1_4=106, // 106
135 | OP_RATE1_5=107, // 107
136 | OP_RATE1_6=108, // 108
137 | OP_RATE2_1=110, // 110
138 | OP_RATE2_2=111, // 111
139 | OP_RATE2_3=112, // 112
140 | OP_RATE2_4=113, // 113
141 | OP_RATE2_5=114, // 114
142 | OP_RATE2_6=115, // 115
143 | OP_RATE3_1=117, // 117
144 | OP_RATE3_2=118, // 118
145 | OP_RATE3_3=119, // 119
146 | OP_RATE3_4=120, // 120
147 | OP_RATE3_5=121, // 121
148 | OP_RATE3_6=122, // 122
149 | OP_RATE4_1=124, // 124
150 | OP_RATE4_2=125, // 125
151 | OP_RATE4_3=126, // 126
152 | OP_RATE4_4=127, // 127
153 | OP_RATE4_5=128, // 128
154 | OP_RATE4_6=129, // 129
155 |
156 | PITCH_EG_L1=130, // 130
157 | PITCH_EG_L2=131, // 131
158 | PITCH_EG_L3=132, // 132
159 | PITCH_EG_L4=133, // 133
160 | PITCH_EG_R1=134, // 134
161 | PITCH_EG_R2=135, // 135
162 | PITCH_EG_R3=136, // 136
163 | PITCH_EG_R4=137, // 137
164 | PITCH_EG_RANGE=138, // 138
165 | PITCH_EG_VELO=139, // 139
166 |
167 | OP_RATE_KEY1=140, // 140
168 | OP_RATE_KEY2=141, // 141
169 | OP_RATE_KEY3=142, // 142
170 | OP_RATE_KEY4=143, // 143
171 | OP_RATE_KEY5=144, // 144
172 | OP_RATE_KEY6=145, // 145
173 |
174 | PITCH_EG_RATE_KEY=146, // 146
175 |
176 | LFO_DEPTH_PITCH=149, // 149
177 | LFO_DEPTH_AMP=150, // 150
178 | LFO_SPEED=151, // 151
179 | LFO_SYNC=152, // 152
180 | LFO_WAVE=153, // 153
181 | LFO_FADE=154, // 154
182 |
183 | MOD_PITCH_LFO_WHEEL=155, // 155
184 | MOD_AMP_LFO_WHEEL=156, // 156
185 | MOD_PITCH_LFO_AFTER=157, // 157
186 | MOD_AMP_LFO_AFTER=158, // 158
187 |
188 | OP_AMS1=159, // 159
189 | OP_AMS2=160, // 160
190 | OP_AMS3=161, // 161
191 | OP_AMS4=162, // 162
192 | OP_AMS5=163, // 163
193 | OP_AMS6=164, // 164
194 |
195 | MASTER_PITCHBEND_UP=172, // 172
196 | MASTER_PITCHBEND_DOWN=173, // 173
197 | MASTER_TRANSPOSE=174, // 174
198 | MASTER_VOLUME=180, // 180
199 |
200 | OP_LEVEL0_1=181, // 181
201 | OP_LEVEL0_2=182, // 182
202 | OP_LEVEL0_3=183, // 183
203 | OP_LEVEL0_4=184, // 184
204 | OP_LEVEL0_5=185, // 185
205 | OP_LEVEL0_6=186, // 186
206 | OP_DELAY_1=187, // 187
207 | OP_DELAY_2=188, // 188
208 | OP_DELAY_3=189, // 189
209 | OP_DELAY_4=190, // 190
210 | OP_DELAY_5=191, // 191
211 | OP_DELAY_6=192, // 192
212 | OP_LEVEL5_1=193, // 193
213 | OP_LEVEL5_2=194, // 194
214 | OP_LEVEL5_3=195, // 195
215 | OP_LEVEL5_4=196, // 196
216 | OP_LEVEL5_5=197, // 197
217 | OP_LEVEL5_6=198, // 198
218 | OP_RATE5_1=199, // 199
219 | OP_RATE5_2=200, // 200
220 | OP_RATE5_3=201, // 201
221 | OP_RATE5_4=202, // 202
222 | OP_RATE5_5=203, // 203
223 | OP_RATE5_6=204, // 204
224 |
225 | PITCH_EG_L0=205, // 205
226 | PITCH_EG_DELAY=206, // 206
227 | PITCH_EG_L5=207, // 207
228 | PITCH_EG_R5=208, // 208
229 |
230 | MOD_PITCH_LFO_BREATH=209, // 209
231 | MOD_AMP_LFO_BREATH=210, // 210
232 | MOD_PITCH_LFO_FOOT=211, // 211
233 | MOD_AMP_LFO_FOOT=212, // 212
234 |
235 | MOD_EG_BIAS_AFTER=213, // 213
236 | MOD_EG_BIAS_WHEEL=214, // 214
237 | MOD_EG_BIAS_BREATH=215, // 215
238 | MOD_EG_BIAS_FOOT=216, // 216
239 |
240 | MOD_PITCH_AFTER=217, // 217
241 | MOD_PITCH_BREATH=218, // 218
242 | MOD_PITCH_FOOT=219, // 219
243 | MOD_PITCH_RANDOM=220, // 220
244 |
245 | MASTER_PAN=221, // 221
246 |
247 | OP_PMS_1=222, // 222
248 | OP_PMS_2=223, // 223
249 | OP_PMS_3=224, // 224
250 | OP_PMS_4=225, // 225
251 | OP_PMS_5=226, // 226
252 | OP_PMS_6=227, // 227
253 |
254 | MASTER_LEGATO=228, // 228
255 | MASTER_PORTAMENTO_MODE=229, // 229
256 | MASTER_PORTAMENTO_TIME=230, // 230
257 |
258 | OP_WAVE1_1=236, // 236
259 | OP_WAVE1_2=237, // 237
260 | OP_WAVE1_3=238, // 238
261 | OP_WAVE1_4=239, // 239
262 | OP_WAVE1_5=240, // 240
263 | OP_WAVE1_6=241, // 241
264 |
265 | MASTER_VELOCITY_OFFSET=242, // 242
266 |
267 | OP_EG_LOOP=244, // 244
268 | OP_EG_LOOP_SEG=245, // 245
269 |
270 | MASTER_EG_RESTART=246, // 246
271 |
272 | MASTER_TUNING=251, // 251
273 |
274 | OP_LEVEL_LEFT1=256, // 256
275 | OP_LEVEL_RIGHT1=257, // 257
276 | OP_LEVEL_LEFT2=258, // 258
277 | OP_LEVEL_RIGHT2=259, // 259
278 | OP_LEVEL_LEFT3=260, // 260
279 | OP_LEVEL_RIGHT3=261, // 261
280 | OP_LEVEL_LEFT4=262, // 262
281 | OP_LEVEL_RIGHT4=263, // 263
282 | OP_LEVEL_LEFT5=264, // 264
283 | OP_LEVEL_RIGHT5=265, // 265
284 | OP_LEVEL_LEFT6=266, // 266
285 | OP_LEVEL_RIGHT6=267, // 267
286 |
287 | OP_WAVE2_1=268, // 268
288 | OP_WAVE2_2=269, // 269
289 | OP_WAVE2_3=270, // 270
290 | OP_WAVE2_4=271, // 271
291 | OP_WAVE2_5=272, // 272
292 | OP_WAVE2_6=273, // 273
293 |
294 | OP_WMODE_1=274, // 274
295 | OP_WMODE_2=275, // 275
296 | OP_WMODE_3=276, // 276
297 | OP_WMODE_4=277, // 277
298 | OP_WMODE_5=278, // 278
299 | OP_WMODE_6=279, // 279
300 | OP_WRATIO_1=280, // 280
301 | OP_WRATIO_2=281, // 281
302 | OP_WRATIO_3=282, // 282
303 | OP_WRATIO_4=283, // 283
304 | OP_WRATIO_5=284, // 284
305 | OP_WRATIO_6=285, // 285
306 | OP_PHASE1=286, // 286
307 | OP_PHASE2=287, // 287
308 | OP_PHASE3=288, // 288
309 | OP_PHASE4=289, // 289
310 | OP_PHASE5=290, // 290
311 | OP_PHASE6=291, // 291
312 |
313 | FX_DELAY_DRY=300, // 300
314 | FX_DELAY_WET=301, // 301
315 | FX_DELAY_MODE=302, // 302
316 | FX_DELAY_TIME=303, // 303
317 | FX_DELAY_FEEDBACK=304, // 304
318 | FX_DELAY_LO=305, // 305
319 | FX_DELAY_HI=306, // 306
320 | FX_DELAY_TEMPO=307, // 307
321 | FX_DELAY_MUL=308, // 308
322 | FX_DELAY_DIV=309, // 309
323 |
324 | FX_PHASER_DRY=310, // 310
325 | FX_PHASER_WET=311, // 311
326 | FX_PHASER_MODE=312, // 312
327 | FX_PHASER_DEPTH=313, // 313
328 | FX_PHASER_SPEED=314, // 314
329 | FX_PHASER_FEEDBACK=315, // 315
330 | FX_PHASER_OFFSET=316, // 316
331 | FX_PHASER_STAGES=317, // 317
332 | FX_PHASER_LRPHASE=318, // 318
333 |
334 | FX_FILTER_LO=320, // 320
335 | FX_FILTER_HI=321, // 321
336 |
337 | FX_AM_SPEED=330, // 330
338 | FX_AM_RANGE=331, // 331
339 | FX_AM_DEPTH=332, // 332
340 | FX_AM_LRPHASE=333, // 333
341 |
342 | FX_CHORUS_DRY=360, // 360
343 | FX_CHORUS_WET=361, // 361
344 | FX_CHORUS_MODE=362, // 362
345 | FX_CHORUS_SPEED=363, // 363
346 | FX_CHORUS_DEPTH=364, // 364
347 | FX_CHORUS_FEEDBACK=365, // 365
348 | FX_CHORUS_LRPHASE=366, // 366
349 |
350 | FX_DECIMATOR_DEPTH=370, // 370
351 | FX_BITCRUSHER_DEPTH=380, // 380
352 |
353 | FX_REVERB_DRY=390, // 390
354 | FX_REVERB_WET=391, // 391
355 | FX_REVERB_MODE=392, // 392
356 | FX_REVERB_DECAY=393, // 393
357 | FX_REVERB_DAMP=394, // 394
358 |
359 | FX_ROUTING=410, // 410
360 | MASTER_OUTPUT=411, // 411
361 |
362 | PERFORMANCE_CTRL1_HI=420, // 420
363 | PERFORMANCE_CTRL1_LO=421, // 421
364 | PERFORMANCE_CTRL2_HI=422, // 422
365 | PERFORMANCE_CTRL2_LO=423, // 423
366 | PERFORMANCE_CTRL3_HI=424, // 424
367 | PERFORMANCE_CTRL3_LO=425, // 425
368 | PERFORMANCE_CTRL4_HI=426, // 426
369 | PERFORMANCE_CTRL4_LO=427, // 427
370 |
371 | ARPEGGIATOR_MODE=450, // 450
372 | ARPEGGIATOR_TEMPO=451, // 451
373 | ARPEGGIATOR_RESERVED=452, // 452
374 | ARPEGGIATOR_MUL=453, // 453
375 | ARPEGGIATOR_OCTAVES=454 // 454
376 | };
377 |
378 | #endif // XFM2_H
379 |
--------------------------------------------------------------------------------
/src/EffectsPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 | import QtQuick.Controls.Styles 1.4
29 |
30 | Item {
31 | id: effectsPage
32 | width: 800
33 | height: 357
34 |
35 |
36 | Rectangle {
37 | anchors.fill: parent
38 | color: "#505050"
39 | }
40 |
41 | Connections {
42 | target: synthModel
43 | onPatchNumberChanged: {
44 | updatePage();
45 | }
46 | }
47 |
48 | function updatePage()
49 | {
50 | dialChorusDry.value=synthModel.fxChorusDry;
51 | dialChorusWet.value=synthModel.fxChorusWet;
52 | dialChorusRate.value=synthModel.fxChorusSpeed;
53 | dialChorusDepth.value=synthModel.fxChorusDepth;
54 | dialChorusFeedback.value=synthModel.fxChorusFeedback;
55 | dialChorusPhase.value=synthModel.fxChorusPhase;
56 | spinChorusMode.value=synthModel.fxChorusMode;
57 |
58 | dialPhaserDry.value=synthModel.fxPhaserDry;
59 | dialPhaserWet.value=synthModel.fxPhaserWet;
60 | dialPhaserRate.value=synthModel.fxPhaserSpeed;
61 | dialPhaserDepth.value=synthModel.fxPhaserDepth;
62 | dialPhaserOffset.value=synthModel.fxPhaserOffset;
63 | dialPhaserFeedback.value=synthModel.fxPhaserFeedback;
64 | dialPhaserPhase.value=synthModel.fxPhaserPhase;
65 | spinPhaserStages.value=synthModel.fxPhaserStages;
66 | spinPhaserMode.value=synthModel.fxPhaserMode;
67 |
68 | dialDelayDry.value=synthModel.fxDelayDry;
69 | dialDelayWet.value=synthModel.fxDelayWet;
70 | dialDelayTime.value=synthModel.fxDelayTime;
71 | dialDelayFeedback.value=synthModel.fxDelayFeedback;
72 | dialDelayTempo.value=synthModel.fxDelayTempo;
73 | dialDelayMultiplier.value=synthModel.fxDelayMultiplier;
74 | dialDelayDivider.value=synthModel.fxDelayDivider;
75 | dialDelayLoPass.value=synthModel.fxDelayLowPass;
76 | dialDelayHiPass.value=synthModel.fxDelayHighPass;
77 | spinDelayMode.value=synthModel.fxDelayMode;
78 | }
79 |
80 |
81 | SpinBox {
82 | id: spinPhaserMode
83 | x: 676
84 | y: 182
85 | width: 115
86 | height: 40
87 | to: 2
88 | font.pointSize: 14
89 | onValueChanged: {
90 | synthModel.fxPhaserMode=spinPhaserMode.value;
91 | }
92 | textFromValue: function(value, locale) {
93 | var vals=["M", "S", "C"];
94 |
95 | return vals[value];
96 | }
97 | }
98 |
99 | SpinBox {
100 | id: spinPhaserStages
101 | x: 676
102 | y: 130
103 | width: 115
104 | height: 40
105 | to: 12
106 | from: 0
107 | font.pointSize: 14
108 | onValueChanged: {
109 | synthModel.fxPhaserStages=spinPhaserStages.value;
110 | }
111 | }
112 |
113 | Rectangle {
114 | id: rectangle1
115 | x: 60
116 | y: 119
117 | width: 731
118 | height: 2
119 | color: "#ffffff"
120 | border.color: "#ffb060"
121 | }
122 |
123 | Rectangle {
124 | id: rectangle
125 | x: 61
126 | y: 9
127 | width: 731
128 | height: 2
129 | color: "#ffffff"
130 | border.color: "#ffb060"
131 | }
132 |
133 | CustomDial {
134 | id: dialChorusDry
135 | x: 14
136 | y: 20
137 | width: 64
138 | height: 64
139 | maximumValue: 255
140 | onValueChanged: {
141 | synthModel.fxChorusDry=dialChorusDry.value;
142 | }
143 | }
144 |
145 | Label {
146 | id: label4
147 | x: 33
148 | y: 90
149 | color: "#ffffff"
150 | text: qsTr("Dry")
151 | font.pixelSize: 16
152 | }
153 |
154 | CustomDial {
155 | id: dialChorusWet
156 | x: 104
157 | y: 20
158 | width: 64
159 | height: 64
160 | maximumValue: 255
161 | onValueChanged: {
162 | synthModel.fxChorusWet=dialChorusWet.value;
163 | }
164 | }
165 |
166 | Label {
167 | id: label5
168 | x: 122
169 | y: 90
170 | color: "#ffffff"
171 | text: qsTr("Wet")
172 | font.pixelSize: 16
173 | }
174 |
175 | CustomDial {
176 | id: dialChorusRate
177 | x: 194
178 | y: 20
179 | width: 64
180 | height: 64
181 | maximumValue: 255
182 | onValueChanged: {
183 | synthModel.fxChorusSpeed=dialChorusRate.value;
184 | }
185 | }
186 |
187 | Label {
188 | id: label6
189 | x: 210
190 | y: 90
191 | color: "#ffffff"
192 | text: qsTr("Rate")
193 | font.pixelSize: 16
194 | }
195 |
196 | CustomDial {
197 | id: dialChorusDepth
198 | x: 284
199 | y: 20
200 | width: 64
201 | height: 64
202 | maximumValue: 255
203 | onValueChanged: {
204 | synthModel.fxChorusDepth=dialChorusDepth.value;
205 | }
206 | }
207 |
208 | Label {
209 | id: label7
210 | x: 295
211 | y: 90
212 | color: "#ffffff"
213 | text: qsTr("Depth")
214 | font.pixelSize: 16
215 | }
216 |
217 | CustomDial {
218 | id: dialChorusFeedback
219 | x: 374
220 | y: 20
221 | width: 64
222 | height: 64
223 | maximumValue: 255
224 | onValueChanged: {
225 | synthModel.fxChorusFeedback=dialChorusFeedback.value;
226 | }
227 | }
228 |
229 | Label {
230 | id: label8
231 | x: 374
232 | y: 90
233 | color: "#ffffff"
234 | text: qsTr("Feedback")
235 | font.pixelSize: 16
236 | }
237 |
238 | CustomDial {
239 | id: dialChorusPhase
240 | x: 464
241 | y: 20
242 | width: 64
243 | height: 64
244 | maximumValue: 255
245 | onValueChanged: {
246 | synthModel.fxChorusPhase=dialChorusPhase.value;
247 | }
248 | }
249 |
250 | Label {
251 | id: label9
252 | x: 462
253 | y: 90
254 | color: "#ffffff"
255 | text: qsTr("L/R Phase")
256 | font.pixelSize: 16
257 | }
258 |
259 | Label {
260 | id: label10
261 | x: 579
262 | y: 44
263 | color: "#ffffff"
264 | text: qsTr("Mode")
265 | font.pixelSize: 16
266 | }
267 |
268 | CustomDial {
269 | id: dialPhaserDry
270 | x: 14
271 | y: 137
272 | width: 64
273 | height: 64
274 | maximumValue: 255
275 | onValueChanged: {
276 | synthModel.fxPhaserDry=dialPhaserDry.value;
277 | }
278 | }
279 |
280 | Label {
281 | id: label15
282 | x: 33
283 | y: 207
284 | color: "#ffffff"
285 | text: qsTr("Dry")
286 | font.pixelSize: 16
287 | }
288 |
289 | CustomDial {
290 | id: dialPhaserWet
291 | x: 104
292 | y: 137
293 | width: 64
294 | height: 64
295 | maximumValue: 255
296 | onValueChanged: {
297 | synthModel.fxPhaserWet=dialPhaserWet.value;
298 | }
299 | }
300 |
301 | Label {
302 | id: label16
303 | x: 123
304 | y: 207
305 | color: "#ffffff"
306 | text: qsTr("Wet")
307 | font.pixelSize: 16
308 | }
309 |
310 | CustomDial {
311 | id: dialPhaserRate
312 | x: 194
313 | y: 137
314 | width: 64
315 | height: 64
316 | maximumValue: 255
317 | onValueChanged: {
318 | synthModel.fxPhaserSpeed=dialPhaserRate.value;
319 | }
320 | }
321 |
322 | Label {
323 | id: label17
324 | x: 210
325 | y: 207
326 | color: "#ffffff"
327 | text: qsTr("Rate")
328 | font.pixelSize: 16
329 | }
330 |
331 | CustomDial {
332 | id: dialPhaserDepth
333 | x: 284
334 | y: 137
335 | width: 64
336 | height: 64
337 | maximumValue: 255
338 | onValueChanged: {
339 | synthModel.fxPhaserDepth=dialPhaserDepth.value;
340 | }
341 | }
342 |
343 | Label {
344 | id: label18
345 | x: 295
346 | y: 207
347 | color: "#ffffff"
348 | text: qsTr("Depth")
349 | font.pixelSize: 16
350 | }
351 |
352 | CustomDial {
353 | id: dialPhaserFeedback
354 | x: 374
355 | y: 137
356 | width: 64
357 | height: 64
358 | maximumValue: 255
359 | onValueChanged: {
360 | synthModel.fxPhaserFeedback=dialPhaserFeedback.value;
361 | }
362 | }
363 |
364 | Label {
365 | id: label19
366 | x: 374
367 | y: 207
368 | color: "#ffffff"
369 | text: qsTr("Feedback")
370 | font.pixelSize: 16
371 | }
372 |
373 | CustomDial {
374 | id: dialPhaserPhase
375 | x: 464
376 | y: 137
377 | width: 64
378 | height: 64
379 | maximumValue: 255
380 | onValueChanged: {
381 | synthModel.fxPhaserPhase=dialPhaserPhase.value;
382 | }
383 | }
384 |
385 | Label {
386 | id: label20
387 | x: 464
388 | y: 207
389 | color: "#ffffff"
390 | text: qsTr("L/R Phase")
391 | font.pixelSize: 16
392 | }
393 |
394 | SpinBox {
395 | id: spinChorusMode
396 | x: 633
397 | y: 32
398 | width: 158
399 | height: 40
400 | to: 3
401 | onValueChanged: {
402 | synthModel.fxChorusMode=spinChorusMode.value;
403 | }
404 | textFromValue: function(value, locale) {
405 | var vals=["CHO (L)", "CHO (S)", "FLA (L)", "FLA (S)"];
406 |
407 | return vals[value];
408 | }
409 | }
410 |
411 | Label {
412 | id: label21
413 | x: 5
414 | y: 0
415 | color: "#ffb060"
416 | text: qsTr("Chorus")
417 | font.pixelSize: 16
418 | }
419 |
420 | Label {
421 | id: label22
422 | x: 5
423 | y: 111
424 | color: "#ffb060"
425 | text: qsTr("Phaser")
426 | font.pixelSize: 16
427 | }
428 |
429 | CustomDial {
430 | id: dialPhaserOffset
431 | x: 554
432 | y: 137
433 | width: 64
434 | height: 64
435 | maximumValue: 255
436 | onValueChanged: {
437 | synthModel.fxPhaserOffset=dialPhaserOffset.value;
438 | }
439 | }
440 |
441 | Label {
442 | id: label23
443 | x: 564
444 | y: 207
445 | color: "#ffffff"
446 | text: qsTr("Offset")
447 | font.pixelSize: 16
448 | horizontalAlignment: Text.AlignHCenter
449 | }
450 |
451 | Label {
452 | id: label11
453 | x: 624
454 | y: 142
455 | color: "#ffffff"
456 | text: qsTr("Stages")
457 | font.pixelSize: 16
458 | horizontalAlignment: Text.AlignRight
459 | }
460 |
461 | Label {
462 | id: label12
463 | x: 628
464 | y: 194
465 | color: "#ffffff"
466 | text: qsTr("Mode")
467 | font.pixelSize: 16
468 | horizontalAlignment: Text.AlignRight
469 | }
470 |
471 | Label {
472 | id: label24
473 | x: 5
474 | y: 230
475 | color: "#ffb060"
476 | text: qsTr("Delay")
477 | font.pixelSize: 16
478 | }
479 |
480 | Rectangle {
481 | id: rectangle2
482 | x: 61
483 | y: 238
484 | width: 731
485 | height: 2
486 | color: "#ffffff"
487 | border.color: "#ffb060"
488 | }
489 |
490 | CustomDial {
491 | id: dialDelayDry
492 | x: 14
493 | y: 260
494 | width: 64
495 | height: 64
496 | maximumValue: 255
497 | onValueChanged: {
498 | synthModel.fxDelayDry=dialDelayDry.value;
499 | }
500 | }
501 |
502 | Label {
503 | id: label25
504 | x: 33
505 | y: 325
506 | color: "#ffffff"
507 | text: qsTr("Dry")
508 | font.pixelSize: 16
509 | }
510 |
511 | CustomDial {
512 | id: dialDelayWet
513 | x: 104
514 | y: 260
515 | width: 64
516 | height: 64
517 | maximumValue: 255
518 | onValueChanged: {
519 | synthModel.fxDelayWet=dialDelayWet.value;
520 | }
521 | }
522 |
523 | Label {
524 | id: label26
525 | x: 122
526 | y: 325
527 | color: "#ffffff"
528 | text: qsTr("Wet")
529 | font.pixelSize: 16
530 | }
531 |
532 | CustomDial {
533 | id: dialDelayTime
534 | x: 194
535 | y: 260
536 | width: 64
537 | height: 64
538 | maximumValue: 255
539 | onValueChanged: {
540 | synthModel.fxDelayTime=dialDelayTime.value;
541 | }
542 | }
543 |
544 | Label {
545 | id: label27
546 | x: 209
547 | y: 325
548 | color: "#ffffff"
549 | text: qsTr("Time")
550 | font.pixelSize: 16
551 | }
552 |
553 | CustomDial {
554 | id: dialDelayFeedback
555 | x: 284
556 | y: 260
557 | width: 64
558 | height: 64
559 | maximumValue: 255
560 | onValueChanged: {
561 | synthModel.fxDelayFeedback=dialDelayFeedback.value;
562 | }
563 | }
564 |
565 | Label {
566 | id: label28
567 | x: 284
568 | y: 325
569 | color: "#ffffff"
570 | text: qsTr("Feedback")
571 | font.pixelSize: 16
572 | }
573 |
574 | CustomDial {
575 | id: dialDelayTempo
576 | x: 374
577 | y: 260
578 | width: 64
579 | height: 64
580 | maximumValue: 255
581 | onValueChanged: {
582 | synthModel.fxDelayTempo=dialDelayTempo.value;
583 | }
584 | }
585 |
586 | Label {
587 | id: label29
588 | x: 383
589 | y: 325
590 | color: "#ffffff"
591 | text: qsTr("Tempo")
592 | font.pixelSize: 16
593 | }
594 |
595 | CustomDial {
596 | id: dialDelayMultiplier
597 | x: 464
598 | y: 260
599 | width: 64
600 | height: 64
601 | maximumValue: 255
602 | onValueChanged: {
603 | synthModel.fxDelayMultiplier=dialDelayMultiplier.value;
604 | }
605 | }
606 |
607 | Label {
608 | id: label30
609 | x: 467
610 | y: 325
611 | color: "#ffffff"
612 | text: qsTr("Multiply")
613 | font.pixelSize: 16
614 | }
615 |
616 | CustomDial {
617 | id: dialDelayDivider
618 | x: 554
619 | y: 260
620 | width: 64
621 | height: 64
622 | maximumValue: 255
623 | onValueChanged: {
624 | synthModel.fxDelayDivider=dialDelayDivider.value;
625 | }
626 | }
627 |
628 | Label {
629 | id: label31
630 | x: 565
631 | y: 325
632 | color: "#ffffff"
633 | text: qsTr("Divide")
634 | font.pixelSize: 16
635 | }
636 |
637 | Label {
638 | id: label13
639 | x: 628
640 | y: 263
641 | color: "#ffffff"
642 | text: qsTr("Mode")
643 | font.pixelSize: 16
644 | horizontalAlignment: Text.AlignRight
645 | }
646 |
647 | SpinBox {
648 | id: spinDelayMode
649 | x: 676
650 | y: 251
651 | width: 115
652 | height: 40
653 | to: 2
654 | font.pointSize: 14
655 | onValueChanged: {
656 | synthModel.fxDelayMode=spinDelayMode.value;
657 | }
658 | textFromValue: function(value, locale) {
659 | var vals=["S", "C", "B"];
660 |
661 | return vals[value];
662 | }
663 | }
664 |
665 | CustomDial {
666 | id: dialDelayLoPass
667 | x: 674
668 | y: 301
669 | width: 48
670 | height: 48
671 | maximumValue: 255
672 | onValueChanged: {
673 | synthModel.fxDelayLowPass=dialDelayLoPass.value;
674 | }
675 | }
676 |
677 | CustomDial {
678 | id: dialDelayHiPass
679 | x: 747
680 | y: 301
681 | width: 48
682 | height: 48
683 | maximumValue: 255
684 | onValueChanged: {
685 | synthModel.fxDelayHighPass=dialDelayHiPass.value;
686 | }
687 | }
688 |
689 | Label {
690 | id: label14
691 | x: 651
692 | y: 317
693 | color: "#ffffff"
694 | text: qsTr("Lo")
695 | font.pixelSize: 16
696 | horizontalAlignment: Text.AlignRight
697 | }
698 |
699 | Label {
700 | id: label32
701 | x: 726
702 | y: 317
703 | width: 16
704 | height: 17
705 | color: "#ffffff"
706 | text: qsTr("Hi")
707 | font.pixelSize: 16
708 | horizontalAlignment: Text.AlignRight
709 | }
710 |
711 | Component.onCompleted: {
712 | updatePage();
713 | }
714 | }
715 |
--------------------------------------------------------------------------------
/src/AlgorithmPage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 |
30 | Item {
31 | id: algorithmPage
32 | width: 800
33 | height: 357
34 |
35 | Connections {
36 | target: synthModel
37 | onPatchNumberChanged: {
38 | updatePage();
39 | }
40 | onOperatorHasChanged: {
41 | updatePage();
42 | }
43 | }
44 |
45 | function updatePage()
46 | {
47 | checkCarrier1.checked=synthModel.isOperatorACarrier(0);
48 | checkCarrier2.checked=synthModel.isOperatorACarrier(1);
49 | checkCarrier3.checked=synthModel.isOperatorACarrier(2);
50 | checkCarrier4.checked=synthModel.isOperatorACarrier(3);
51 | checkCarrier5.checked=synthModel.isOperatorACarrier(4);
52 | checkCarrier6.checked=synthModel.isOperatorACarrier(5);
53 |
54 | checkModulate11.checked=synthModel.isOperatorModulating(0, 0);
55 | checkModulate21.checked=synthModel.isOperatorModulating(1, 0);
56 | checkModulate31.checked=synthModel.isOperatorModulating(2, 0);
57 | checkModulate41.checked=synthModel.isOperatorModulating(3, 0);
58 | checkModulate51.checked=synthModel.isOperatorModulating(4, 0);
59 | checkModulate61.checked=synthModel.isOperatorModulating(5, 0);
60 |
61 | checkModulate12.checked=synthModel.isOperatorModulating(0, 1);
62 | checkModulate22.checked=synthModel.isOperatorModulating(1, 1);
63 | checkModulate32.checked=synthModel.isOperatorModulating(2, 1);
64 | checkModulate42.checked=synthModel.isOperatorModulating(3, 1);
65 | checkModulate52.checked=synthModel.isOperatorModulating(4, 1);
66 | checkModulate62.checked=synthModel.isOperatorModulating(5, 1);
67 |
68 | checkModulate13.checked=synthModel.isOperatorModulating(0, 2);
69 | checkModulate23.checked=synthModel.isOperatorModulating(1, 2);
70 | checkModulate33.checked=synthModel.isOperatorModulating(2, 2);
71 | checkModulate43.checked=synthModel.isOperatorModulating(3, 2);
72 | checkModulate53.checked=synthModel.isOperatorModulating(4, 2);
73 | checkModulate63.checked=synthModel.isOperatorModulating(5, 2);
74 |
75 | checkModulate14.checked=synthModel.isOperatorModulating(0, 3);
76 | checkModulate24.checked=synthModel.isOperatorModulating(1, 3);
77 | checkModulate34.checked=synthModel.isOperatorModulating(2, 3);
78 | checkModulate44.checked=synthModel.isOperatorModulating(3, 3);
79 | checkModulate54.checked=synthModel.isOperatorModulating(4, 3);
80 | checkModulate64.checked=synthModel.isOperatorModulating(5, 3);
81 |
82 | checkModulate15.checked=synthModel.isOperatorModulating(0, 4);
83 | checkModulate25.checked=synthModel.isOperatorModulating(1, 4);
84 | checkModulate35.checked=synthModel.isOperatorModulating(2, 4);
85 | checkModulate45.checked=synthModel.isOperatorModulating(3, 4);
86 | checkModulate55.checked=synthModel.isOperatorModulating(4, 4);
87 | checkModulate65.checked=synthModel.isOperatorModulating(5, 4);
88 |
89 | checkModulate16.checked=synthModel.isOperatorModulating(0, 5);
90 | checkModulate26.checked=synthModel.isOperatorModulating(1, 5);
91 | checkModulate36.checked=synthModel.isOperatorModulating(2, 5);
92 | checkModulate46.checked=synthModel.isOperatorModulating(3, 5);
93 | checkModulate56.checked=synthModel.isOperatorModulating(4, 5);
94 | checkModulate66.checked=synthModel.isOperatorModulating(5, 5);
95 | }
96 |
97 | Rectangle {
98 | anchors.fill: parent
99 | color: "#505050"
100 |
101 | Grid {
102 | id: gridMatrix
103 | x: 14
104 | y: 14
105 | width: 383
106 | height: 335
107 | rows: 8
108 | columns: 7
109 | spacing: 0
110 |
111 | Label {
112 | id: heading1
113 | width: 100
114 | height: 40
115 | color: "#ffffff"
116 | text: qsTr("Operator")
117 | font.pixelSize: 16
118 | verticalAlignment: Text.AlignVCenter
119 | }
120 | Label {
121 | id: heading2
122 | width: 46
123 | height: 40
124 | color: "#ffffff"
125 | text: qsTr("1")
126 | font.pixelSize: 16
127 | verticalAlignment: Text.AlignVCenter
128 | horizontalAlignment: Text.AlignHCenter
129 | }
130 | Label {
131 | id: heading3
132 | width: 46
133 | height: 40
134 | color: "#ffffff"
135 | text: qsTr("2")
136 | font.pixelSize: 16
137 | verticalAlignment: Text.AlignVCenter
138 | horizontalAlignment: Text.AlignHCenter
139 | }
140 | Label {
141 | id: heading4
142 | width: 46
143 | height: 40
144 | color: "#ffffff"
145 | text: qsTr("3")
146 | font.pixelSize: 16
147 | verticalAlignment: Text.AlignVCenter
148 | horizontalAlignment: Text.AlignHCenter
149 | }
150 | Label {
151 | id: heading5
152 | width: 46
153 | height: 40
154 | color: "#ffffff"
155 | text: qsTr("4")
156 | font.pixelSize: 16
157 | verticalAlignment: Text.AlignVCenter
158 | horizontalAlignment: Text.AlignHCenter
159 | }
160 | Label {
161 | id: heading6
162 | width: 46
163 | height: 40
164 | color: "#ffffff"
165 | text: qsTr("5")
166 | font.pixelSize: 16
167 | verticalAlignment: Text.AlignVCenter
168 | horizontalAlignment: Text.AlignHCenter
169 | }
170 | Label {
171 | id: heading7
172 | width: 46
173 | height: 40
174 | color: "#ffffff"
175 | text: qsTr("6")
176 | font.pixelSize: 16
177 | verticalAlignment: Text.AlignVCenter
178 | horizontalAlignment: Text.AlignHCenter
179 | }
180 | Label {
181 | id: row1
182 | width: 100
183 | height: 40
184 | color: "#ffffff"
185 | text: qsTr("Carrier")
186 | font.pixelSize: 16
187 | verticalAlignment: Text.AlignVCenter
188 | }
189 |
190 | CheckBox {
191 | id: checkCarrier1
192 | text: qsTr("")
193 | display: AbstractButton.IconOnly
194 | onCheckedChanged: {
195 | synthModel.makeOperatorACarrier(0, checkCarrier1.checked);
196 | }
197 | }
198 |
199 | CheckBox {
200 | id: checkCarrier2
201 | text: qsTr("")
202 | display: AbstractButton.IconOnly
203 | onCheckedChanged: {
204 | synthModel.makeOperatorACarrier(1, checkCarrier2.checked);
205 | }
206 | }
207 |
208 | CheckBox {
209 | id: checkCarrier3
210 | text: qsTr("")
211 | display: AbstractButton.IconOnly
212 | onCheckedChanged: {
213 | synthModel.makeOperatorACarrier(2, checkCarrier3.checked);
214 | }
215 | }
216 |
217 | CheckBox {
218 | id: checkCarrier4
219 | text: qsTr("")
220 | display: AbstractButton.IconOnly
221 | onCheckedChanged: {
222 | synthModel.makeOperatorACarrier(3, checkCarrier4.checked);
223 | }
224 | }
225 |
226 | CheckBox {
227 | id: checkCarrier5
228 | text: qsTr("")
229 | display: AbstractButton.IconOnly
230 | onCheckedChanged: {
231 | synthModel.makeOperatorACarrier(4, checkCarrier5.checked);
232 | }
233 | }
234 |
235 | CheckBox {
236 | id: checkCarrier6
237 | text: qsTr("")
238 | display: AbstractButton.IconOnly
239 | onCheckedChanged: {
240 | synthModel.makeOperatorACarrier(5, checkCarrier6.checked);
241 | }
242 | }
243 |
244 | Label {
245 | id: row2
246 | width: 100
247 | height: 40
248 | color: "#ffffff"
249 | text: qsTr("Modulate 1")
250 | font.pixelSize: 16
251 | verticalAlignment: Text.AlignVCenter
252 | }
253 |
254 | CheckBox {
255 | id: checkModulate11
256 | text: qsTr("")
257 | display: AbstractButton.IconOnly
258 | onCheckedChanged: {
259 | synthModel.makeOperatorModulate(0, 0, checkModulate11.checked);
260 | }
261 | }
262 |
263 | CheckBox {
264 | id: checkModulate21
265 | text: qsTr("")
266 | display: AbstractButton.IconOnly
267 | onCheckedChanged: {
268 | synthModel.makeOperatorModulate(1, 0, checkModulate21.checked);
269 | }
270 | }
271 |
272 | CheckBox {
273 | id: checkModulate31
274 | text: qsTr("")
275 | display: AbstractButton.IconOnly
276 | onCheckedChanged: {
277 | synthModel.makeOperatorModulate(2, 0, checkModulate31.checked);
278 | }
279 | }
280 |
281 | CheckBox {
282 | id: checkModulate41
283 | text: qsTr("")
284 | display: AbstractButton.IconOnly
285 | onCheckedChanged: {
286 | synthModel.makeOperatorModulate(3, 0, checkModulate41.checked);
287 | }
288 | }
289 |
290 | CheckBox {
291 | id: checkModulate51
292 | text: qsTr("")
293 | display: AbstractButton.IconOnly
294 | onCheckedChanged: {
295 | synthModel.makeOperatorModulate(4, 0, checkModulate51.checked);
296 | }
297 | }
298 |
299 | CheckBox {
300 | id: checkModulate61
301 | text: qsTr("")
302 | display: AbstractButton.IconOnly
303 | onCheckedChanged: {
304 | synthModel.makeOperatorModulate(5, 0, checkModulate61.checked);
305 | }
306 | }
307 |
308 | Label {
309 | id: row3
310 | width: 100
311 | height: 40
312 | color: "#ffffff"
313 | text: qsTr("Modulate 2")
314 | font.pixelSize: 16
315 | verticalAlignment: Text.AlignVCenter
316 | }
317 |
318 | CheckBox {
319 | id: checkModulate12
320 | text: qsTr("")
321 | display: AbstractButton.IconOnly
322 | onCheckedChanged: {
323 | synthModel.makeOperatorModulate(0, 1, checkModulate12.checked);
324 | }
325 | }
326 |
327 | CheckBox {
328 | id: checkModulate22
329 | text: qsTr("")
330 | display: AbstractButton.IconOnly
331 | onCheckedChanged: {
332 | synthModel.makeOperatorModulate(1, 1, checkModulate22.checked);
333 | }
334 | }
335 |
336 | CheckBox {
337 | id: checkModulate32
338 | text: qsTr("")
339 | display: AbstractButton.IconOnly
340 | onCheckedChanged: {
341 | synthModel.makeOperatorModulate(2, 1, checkModulate32.checked);
342 | }
343 | }
344 |
345 | CheckBox {
346 | id: checkModulate42
347 | text: qsTr("")
348 | display: AbstractButton.IconOnly
349 | onCheckedChanged: {
350 | synthModel.makeOperatorModulate(3, 1, checkModulate42.checked);
351 | }
352 | }
353 |
354 | CheckBox {
355 | id: checkModulate52
356 | text: qsTr("")
357 | display: AbstractButton.IconOnly
358 | onCheckedChanged: {
359 | synthModel.makeOperatorModulate(4, 1, checkModulate52.checked);
360 | }
361 | }
362 |
363 | CheckBox {
364 | id: checkModulate62
365 | text: qsTr("")
366 | display: AbstractButton.IconOnly
367 | onCheckedChanged: {
368 | synthModel.makeOperatorModulate(5, 1, checkModulate62.checked);
369 | }
370 | }
371 |
372 | Label {
373 | id: row4
374 | width: 100
375 | height: 40
376 | color: "#ffffff"
377 | text: qsTr("Modulate 3")
378 | font.pixelSize: 16
379 | verticalAlignment: Text.AlignVCenter
380 | }
381 |
382 | CheckBox {
383 | id: checkModulate13
384 | text: qsTr("")
385 | display: AbstractButton.IconOnly
386 | onCheckedChanged: {
387 | synthModel.makeOperatorModulate(0, 2, checkModulate13.checked);
388 | }
389 | }
390 |
391 | CheckBox {
392 | id: checkModulate23
393 | text: qsTr("")
394 | display: AbstractButton.IconOnly
395 | onCheckedChanged: {
396 | synthModel.makeOperatorModulate(1, 2, checkModulate23.checked);
397 | }
398 | }
399 |
400 | CheckBox {
401 | id: checkModulate33
402 | text: qsTr("")
403 | display: AbstractButton.IconOnly
404 | onCheckedChanged: {
405 | synthModel.makeOperatorModulate(2, 2, checkModulate33.checked);
406 | }
407 | }
408 |
409 | CheckBox {
410 | id: checkModulate43
411 | text: qsTr("")
412 | display: AbstractButton.IconOnly
413 | onCheckedChanged: {
414 | synthModel.makeOperatorModulate(3, 2, checkModulate43.checked);
415 | }
416 | }
417 |
418 | CheckBox {
419 | id: checkModulate53
420 | text: qsTr("")
421 | display: AbstractButton.IconOnly
422 | onCheckedChanged: {
423 | synthModel.makeOperatorModulate(4, 2, checkModulate53.checked);
424 | }
425 | }
426 |
427 | CheckBox {
428 | id: checkModulate63
429 | text: qsTr("")
430 | display: AbstractButton.IconOnly
431 | onCheckedChanged: {
432 | synthModel.makeOperatorModulate(5, 2, checkModulate63.checked);
433 | }
434 | }
435 |
436 | Label {
437 | id: row5
438 | width: 100
439 | height: 40
440 | color: "#ffffff"
441 | text: qsTr("Modulate 4")
442 | font.pixelSize: 16
443 | verticalAlignment: Text.AlignVCenter
444 | }
445 |
446 | CheckBox {
447 | id: checkModulate14
448 | text: qsTr("")
449 | display: AbstractButton.IconOnly
450 | onCheckedChanged: {
451 | synthModel.makeOperatorModulate(0, 3, checkModulate14.checked);
452 | }
453 | }
454 |
455 | CheckBox {
456 | id: checkModulate24
457 | text: qsTr("")
458 | display: AbstractButton.IconOnly
459 | onCheckedChanged: {
460 | synthModel.makeOperatorModulate(1, 3, checkModulate24.checked);
461 | }
462 | }
463 |
464 | CheckBox {
465 | id: checkModulate34
466 | text: qsTr("")
467 | display: AbstractButton.IconOnly
468 | onCheckedChanged: {
469 | synthModel.makeOperatorModulate(2, 3, checkModulate34.checked);
470 | }
471 | }
472 |
473 | CheckBox {
474 | id: checkModulate44
475 | text: qsTr("")
476 | display: AbstractButton.IconOnly
477 | onCheckedChanged: {
478 | synthModel.makeOperatorModulate(3, 3, checkModulate44.checked);
479 | }
480 | }
481 |
482 | CheckBox {
483 | id: checkModulate54
484 | text: qsTr("")
485 | display: AbstractButton.IconOnly
486 | onCheckedChanged: {
487 | synthModel.makeOperatorModulate(4, 3, checkModulate54.checked);
488 | }
489 | }
490 |
491 | CheckBox {
492 | id: checkModulate64
493 | text: qsTr("")
494 | display: AbstractButton.IconOnly
495 | onCheckedChanged: {
496 | synthModel.makeOperatorModulate(5, 3, checkModulate64.checked);
497 | }
498 | }
499 |
500 | Label {
501 | id: row6
502 | width: 100
503 | height: 40
504 | color: "#ffffff"
505 | text: qsTr("Modulate 5")
506 | font.pixelSize: 16
507 | verticalAlignment: Text.AlignVCenter
508 | }
509 |
510 | CheckBox {
511 | id: checkModulate15
512 | text: qsTr("")
513 | display: AbstractButton.IconOnly
514 | onCheckedChanged: {
515 | synthModel.makeOperatorModulate(0, 4, checkModulate15.checked);
516 | }
517 | }
518 |
519 | CheckBox {
520 | id: checkModulate25
521 | text: qsTr("")
522 | display: AbstractButton.IconOnly
523 | onCheckedChanged: {
524 | synthModel.makeOperatorModulate(1, 4, checkModulate25.checked);
525 | }
526 | }
527 |
528 | CheckBox {
529 | id: checkModulate35
530 | text: qsTr("")
531 | display: AbstractButton.IconOnly
532 | onCheckedChanged: {
533 | synthModel.makeOperatorModulate(2, 4, checkModulate35.checked);
534 | }
535 | }
536 |
537 | CheckBox {
538 | id: checkModulate45
539 | text: qsTr("")
540 | display: AbstractButton.IconOnly
541 | onCheckedChanged: {
542 | synthModel.makeOperatorModulate(3, 4, checkModulate45.checked);
543 | }
544 | }
545 |
546 | CheckBox {
547 | id: checkModulate55
548 | text: qsTr("")
549 | display: AbstractButton.IconOnly
550 | onCheckedChanged: {
551 | synthModel.makeOperatorModulate(4, 4, checkModulate55.checked);
552 | }
553 | }
554 |
555 | CheckBox {
556 | id: checkModulate65
557 | text: qsTr("")
558 | display: AbstractButton.IconOnly
559 | onCheckedChanged: {
560 | synthModel.makeOperatorModulate(5, 4, checkModulate65.checked);
561 | }
562 | }
563 |
564 | Label {
565 | id: row7
566 | width: 100
567 | height: 40
568 | color: "#ffffff"
569 | text: qsTr("Modulate 6")
570 | font.pixelSize: 16
571 | verticalAlignment: Text.AlignVCenter
572 | }
573 |
574 | CheckBox {
575 | id: checkModulate16
576 | text: qsTr("")
577 | display: AbstractButton.IconOnly
578 | onCheckedChanged: {
579 | synthModel.makeOperatorModulate(0, 5, checkModulate16.checked);
580 | }
581 | }
582 |
583 | CheckBox {
584 | id: checkModulate26
585 | text: qsTr("")
586 | display: AbstractButton.IconOnly
587 | onCheckedChanged: {
588 | synthModel.makeOperatorModulate(1, 5, checkModulate26.checked);
589 | }
590 | }
591 |
592 | CheckBox {
593 | id: checkModulate36
594 | text: qsTr("")
595 | display: AbstractButton.IconOnly
596 | onCheckedChanged: {
597 | synthModel.makeOperatorModulate(2, 5, checkModulate36.checked);
598 | }
599 | }
600 |
601 | CheckBox {
602 | id: checkModulate46
603 | text: qsTr("")
604 | display: AbstractButton.IconOnly
605 | onCheckedChanged: {
606 | synthModel.makeOperatorModulate(3, 5, checkModulate46.checked);
607 | }
608 | }
609 |
610 | CheckBox {
611 | id: checkModulate56
612 | text: qsTr("")
613 | display: AbstractButton.IconOnly
614 | onCheckedChanged: {
615 | synthModel.makeOperatorModulate(4, 5, checkModulate56.checked);
616 | }
617 | }
618 |
619 | CheckBox {
620 | id: checkModulate66
621 | text: qsTr("")
622 | display: AbstractButton.IconOnly
623 | onCheckedChanged: {
624 | synthModel.makeOperatorModulate(5, 5, checkModulate66.checked);
625 | }
626 | }
627 | }
628 | }
629 |
630 | Button {
631 | id: buttonDX7Algorithm
632 | x: 728
633 | y: 19
634 | width: 59
635 | height: 40
636 | text: qsTr("SET")
637 |
638 | onClicked: {
639 | synthModel.makeDX7Algorithm(spinDX7Algorithm.value-1);
640 | }
641 | }
642 |
643 | SpinBox {
644 | id: spinDX7Algorithm
645 | x: 573
646 | y: 19
647 | to: 32
648 | from: 1
649 | value: 1
650 | }
651 |
652 | Label {
653 | id: label
654 | x: 416
655 | y: 26
656 | color: "#ffb060"
657 | text: qsTr("DX7 Algorithm")
658 | font.pixelSize: 22
659 | }
660 |
661 | Component.onCompleted: {
662 | updatePage();
663 | }
664 |
665 | }
666 |
667 |
668 | /*##^##
669 | Designer {
670 | D{i:1;anchors_height:400;anchors_x:0;anchors_y:0}D{i:11;anchors_width:100}D{i:18;anchors_width:100}
671 | D{i:25;anchors_width:100}D{i:32;anchors_width:100}D{i:39;anchors_width:100}D{i:46;anchors_width:100}
672 | D{i:53;anchors_width:100}
673 | }
674 | ##^##*/
675 |
--------------------------------------------------------------------------------
/src/SynthModel.h:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | #ifndef SYNTHMODEL_H
25 | #define SYNTHMODEL_H
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include "xfm2.h"
32 | #include "xfmoperator.h"
33 | #include
34 | #include
35 |
36 | /*
37 | * This is a model for controlling the synth. It works by using a memory buffer to hold
38 | * a copy of the synth parameters so they may be queried without having to access the serial
39 | * port. This reduces latency.
40 | *
41 | * It's not the most efficient interface. Ideally it would have made sense to create
42 | * smaller classes for some of the function blocks, e.g. I could have made a PitchEnvelope class
43 | * instead of a bunch of pitchEG properties. But in the end I went for simplicity over
44 | * a complicated class design, just to keep things easy to maintain should additional
45 | * parameters be added to the synth itself. I did use a separate class for the Operators
46 | * in order to reduce the number of duplicate properties and methods.
47 | *
48 | */
49 |
50 | class SynthModel : public QObject {
51 | Q_OBJECT
52 |
53 | // Global info
54 | Q_PROPERTY(bool isConnected READ isConnected)
55 | Q_PROPERTY(int patchNumber READ patchNumber WRITE setPatchNumber NOTIFY patchNumberChanged)
56 | Q_PROPERTY(QString patchName READ patchName WRITE setPatchName NOTIFY patchNameChanged)
57 |
58 | // Common
59 | Q_PROPERTY(int masterPitchBendUp READ masterPitchBendUp WRITE setMasterPitchBendUp NOTIFY masterPitchBendUpChanged)
60 | Q_PROPERTY(int masterPitchBendDown READ masterPitchBendDown WRITE setMasterPitchBendDown NOTIFY masterPitchBendDownChanged)
61 | Q_PROPERTY(int masterTranspose READ masterTranspose WRITE setMasterTranspose NOTIFY masterTransposeChanged)
62 | Q_PROPERTY(int masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
63 | Q_PROPERTY(int outputLevel READ outputLevel WRITE setOutputLevel NOTIFY outputLevelChanged)
64 | Q_PROPERTY(int masterPan READ masterPan WRITE setMasterPan NOTIFY masterPanChanged)
65 | Q_PROPERTY(int masterLegato READ masterLegato WRITE setMasterLegato NOTIFY masterLegatoChanged)
66 | Q_PROPERTY(int masterTuning READ masterTuning WRITE setMasterTuning NOTIFY masterTuningChanged)
67 | Q_PROPERTY(int masterVelocityOffset READ masterVelocityOffset WRITE setMasterVelocityOffset NOTIFY masterVelocityOffsetChanged)
68 |
69 | // Operators
70 | Q_PROPERTY(QList fmOperators READ fmOperators)
71 | Q_PROPERTY(int operatorSync READ operatorSync WRITE setOperatorSync NOTIFY operatorSyncChanged)
72 | Q_PROPERTY(int operatorMode READ operatorMode WRITE setOperatorMode NOTIFY operatorModeChanged)
73 | Q_PROPERTY(int envelopeLoop READ envelopeLoop WRITE setEnvelopeLoop NOTIFY envelopeLoopChanged)
74 | Q_PROPERTY(int envelopeLoopSegment READ envelopeLoopSegment WRITE setEnvelopeLoopSegment NOTIFY envelopeLoopSegmentChanged)
75 | Q_PROPERTY(int envelopeRestart READ envelopeRestart WRITE setEnvelopeRestart NOTIFY envelopeRestartChanged)
76 |
77 | // Pitch EG
78 | Q_PROPERTY(int pitchEG_L0 READ pitchEG_L0 WRITE setPitchEG_L0 NOTIFY pitchEG_L0Changed)
79 | Q_PROPERTY(int pitchEG_L1 READ pitchEG_L1 WRITE setPitchEG_L1 NOTIFY pitchEG_L1Changed)
80 | Q_PROPERTY(int pitchEG_L2 READ pitchEG_L2 WRITE setPitchEG_L2 NOTIFY pitchEG_L2Changed)
81 | Q_PROPERTY(int pitchEG_L3 READ pitchEG_L3 WRITE setPitchEG_L3 NOTIFY pitchEG_L3Changed)
82 | Q_PROPERTY(int pitchEG_L4 READ pitchEG_L4 WRITE setPitchEG_L4 NOTIFY pitchEG_L4Changed)
83 | Q_PROPERTY(int pitchEG_L5 READ pitchEG_L5 WRITE setPitchEG_L5 NOTIFY pitchEG_L5Changed)
84 | Q_PROPERTY(int pitchEG_R0 READ pitchEG_R0 WRITE setPitchEG_R0 NOTIFY pitchEG_R0Changed)
85 | Q_PROPERTY(int pitchEG_R1 READ pitchEG_R1 WRITE setPitchEG_R1 NOTIFY pitchEG_R1Changed)
86 | Q_PROPERTY(int pitchEG_R2 READ pitchEG_R2 WRITE setPitchEG_R2 NOTIFY pitchEG_R2Changed)
87 | Q_PROPERTY(int pitchEG_R3 READ pitchEG_R3 WRITE setPitchEG_R3 NOTIFY pitchEG_R3Changed)
88 | Q_PROPERTY(int pitchEG_R4 READ pitchEG_R4 WRITE setPitchEG_R4 NOTIFY pitchEG_R4Changed)
89 | Q_PROPERTY(int pitchEG_R5 READ pitchEG_R5 WRITE setPitchEG_R5 NOTIFY pitchEG_R5Changed)
90 | Q_PROPERTY(int pitchEG_Range READ pitchEG_Range WRITE setPitchEG_Range NOTIFY pitchEG_RangeChanged)
91 | Q_PROPERTY(int pitchEG_Velocity READ pitchEG_Velocity WRITE setPitchEG_Velocity NOTIFY pitchEG_VelocityChanged)
92 | Q_PROPERTY(int pitchEG_RateKey READ pitchEG_RateKey WRITE setPitchEG_RateKey NOTIFY pitchEG_RateKeyChanged)
93 |
94 | // LFO
95 | Q_PROPERTY(int lfoDepthPitch READ lfoDepthPitch WRITE setLFODepthPitch NOTIFY lfoDepthPitchChanged)
96 | Q_PROPERTY(int lfoDepthAmplitude READ lfoDepthAmplitude WRITE setLFODepthAmplitude NOTIFY lfoDepthAmplitudeChanged)
97 | Q_PROPERTY(int lfoSpeed READ lfoSpeed WRITE setLFOSpeed NOTIFY lfoSpeedChanged)
98 | Q_PROPERTY(int lfoSync READ lfoSync WRITE setLFOSync NOTIFY lfoSyncChanged)
99 | Q_PROPERTY(int lfoWave READ lfoWave WRITE setLFOWave NOTIFY lfoWaveChanged)
100 | Q_PROPERTY(int lfoFade READ lfoFade WRITE setLFOFade NOTIFY lfoFadeChanged)
101 |
102 | // Modulation
103 | Q_PROPERTY(int modPitchLFOWheel READ modPitchLFOWheel WRITE setModPitchLFOWheel NOTIFY modPitchLFOWheelChanged)
104 | Q_PROPERTY(int modPitchLFOFoot READ modPitchLFOFoot WRITE setModPitchLFOFoot NOTIFY modPitchLFOFootChanged)
105 | Q_PROPERTY(int modAmpLFOAftertouch READ modAmpLFOAftertouch WRITE setModAmpLFOAftertouch NOTIFY modAmpLFOAftertouchChanged)
106 | Q_PROPERTY(int modAmpLFOWheel READ modAmpLFOWheel WRITE setModAmpLFOWheel NOTIFY modAmpLFOWheelChanged)
107 | Q_PROPERTY(int modAmpLFOBreath READ modAmpLFOBreath WRITE setModAmpLFOBreath NOTIFY modAmpLFOBreathChanged)
108 | Q_PROPERTY(int modAmpLFOFoot READ modAmpLFOFoot WRITE setModAmpLFOFoot NOTIFY modAmpLFOFootChanged)
109 | Q_PROPERTY(int modPitchLFOAftertouch READ modPitchLFOAftertouch WRITE setModPitchLFOAftertouch NOTIFY modPitchLFOAftertouchChanged)
110 | Q_PROPERTY(int modPitchLFOBreath READ modPitchLFOBreath WRITE setModPitchLFOBreath NOTIFY modPitchLFOBreathChanged)
111 | Q_PROPERTY(int modEnvelopeBiasAftertouch READ modEnvelopeBiasAftertouch WRITE setModEnvelopeBiasAftertouch NOTIFY modEnvelopeBiasAftertouchChanged)
112 | Q_PROPERTY(int modEnvelopeBiasWheel READ modEnvelopeBiasWheel WRITE setModEnvelopeBiasWheel NOTIFY modEnvelopeBiasWheelChanged)
113 | Q_PROPERTY(int modEnvelopeBiasBreath READ modEnvelopeBiasBreath WRITE setModEnvelopeBiasBreath NOTIFY modEnvelopeBiasBreathChanged)
114 | Q_PROPERTY(int modEnvelopeBiasFoot READ modEnvelopeBiasFoot WRITE setModEnvelopeBiasFoot NOTIFY modEnvelopeBiasFootChanged)
115 | Q_PROPERTY(int modPitchAftertouch READ modPitchAftertouch WRITE setModPitchAftertouch NOTIFY modPitchAftertouchChanged)
116 | Q_PROPERTY(int modPitchBreath READ modPitchBreath WRITE setModPitchBreath NOTIFY modPitchBreathChanged)
117 | Q_PROPERTY(int modPitchFoot READ modPitchFoot WRITE setModPitchFoot NOTIFY modPitchFootChanged)
118 | Q_PROPERTY(int modPitchRandom READ modPitchRandom WRITE setModPitchRandom NOTIFY modPitchRandomChanged)
119 |
120 | // Portamento
121 | Q_PROPERTY(int portamentoMode READ portamentoMode WRITE setPortamentoMode NOTIFY portamentoModeChanged)
122 | Q_PROPERTY(int portamentoTime READ portamentoTime WRITE setPortamentoTime NOTIFY portamentoTimeChanged)
123 |
124 | // Arpeggiator
125 | Q_PROPERTY(int arpeggiatorMode READ arpeggiatorMode WRITE setArpeggiatorMode NOTIFY arpeggiatorModeChanged)
126 | Q_PROPERTY(int arpeggiatorTempo READ arpeggiatorTempo WRITE setArpeggiatorTempo NOTIFY arpeggiatorTempoChanged)
127 | Q_PROPERTY(int arpeggiatorTempoMultiplier READ arpeggiatorTempoMultiplier WRITE setArpeggiatorTempoMultiplier NOTIFY arpeggiatorTempoMultiplierChanged)
128 | Q_PROPERTY(int arpeggiatorOctaveRange READ arpeggiatorOctaveRange WRITE setArpeggiatorOctaveRange NOTIFY arpeggiatorOctaveRangeChanged)
129 |
130 | // Filter
131 | Q_PROPERTY(int filterLoCutoff READ filterLoCutoff WRITE setFilterLoCutoff NOTIFY filterLoCutoffChanged)
132 | Q_PROPERTY(int filterHiCutoff READ filterHiCutoff WRITE setFilterHiCutoff NOTIFY filterHiCutoffChanged)
133 |
134 | // FX: Bit crush and decimator
135 | Q_PROPERTY(int fxBitCrushDepth READ fxBitCrushDepth WRITE setFxBitCrushDepth NOTIFY fxBitCrushDepthChanged)
136 | Q_PROPERTY(int fxDecimator READ fxDecimator WRITE setFxDecimator NOTIFY fxDecimatorChanged)
137 |
138 | // FX: Chorus
139 | Q_PROPERTY(int fxChorusDry READ fxChorusDry WRITE setFxChorusDry NOTIFY fxChorusDryChanged)
140 | Q_PROPERTY(int fxChorusWet READ fxChorusWet WRITE setFxChorusWet NOTIFY fxChorusWetChanged)
141 | Q_PROPERTY(int fxChorusMode READ fxChorusMode WRITE setFxChorusMode NOTIFY fxChorusModeChanged)
142 | Q_PROPERTY(int fxChorusSpeed READ fxChorusSpeed WRITE setFxChorusSpeed NOTIFY fxChorusSpeedChanged)
143 | Q_PROPERTY(int fxChorusDepth READ fxChorusDepth WRITE setFxChorusDepth NOTIFY fxChorusDepthChanged)
144 | Q_PROPERTY(int fxChorusFeedback READ fxChorusFeedback WRITE setFxChorusFeedback NOTIFY fxChorusFeedbackChanged)
145 | Q_PROPERTY(int fxChorusPhase READ fxChorusPhase WRITE setFxChorusPhase NOTIFY fxChorusPhaseChanged)
146 |
147 | // FX: Phaser
148 | Q_PROPERTY(int fxPhaserDry READ fxPhaserDry WRITE setFxPhaserDry NOTIFY fxPhaserDryChanged)
149 | Q_PROPERTY(int fxPhaserWet READ fxPhaserWet WRITE setFxPhaserWet NOTIFY fxPhaserWetChanged)
150 | Q_PROPERTY(int fxPhaserMode READ fxPhaserMode WRITE setFxPhaserMode NOTIFY fxPhaserModeChanged)
151 | Q_PROPERTY(int fxPhaserSpeed READ fxPhaserSpeed WRITE setFxPhaserSpeed NOTIFY fxPhaserSpeedChanged)
152 | Q_PROPERTY(int fxPhaserDepth READ fxPhaserDepth WRITE setFxPhaserDepth NOTIFY fxPhaserDepthChanged)
153 | Q_PROPERTY(int fxPhaserFeedback READ fxPhaserFeedback WRITE setFxPhaserFeedback NOTIFY fxPhaserFeedbackChanged)
154 | Q_PROPERTY(int fxPhaserPhase READ fxPhaserPhase WRITE setFxPhaserPhase NOTIFY fxPhaserPhaseChanged)
155 | Q_PROPERTY(int fxPhaserOffset READ fxPhaserOffset WRITE setFxPhaserOffset NOTIFY fxPhaserOffsetChanged)
156 | Q_PROPERTY(int fxPhaserStages READ fxPhaserStages WRITE setFxPhaserStages NOTIFY fxPhaserStagesChanged)
157 |
158 | // FX: Delay
159 | Q_PROPERTY(int fxDelayDry READ fxDelayDry WRITE setFxDelayDry NOTIFY fxDelayDryChanged)
160 | Q_PROPERTY(int fxDelayWet READ fxDelayWet WRITE setFxDelayWet NOTIFY fxDelayWetChanged)
161 | Q_PROPERTY(int fxDelayMode READ fxDelayMode WRITE setFxDelayMode NOTIFY fxDelayModeChanged)
162 | Q_PROPERTY(int fxDelayTime READ fxDelayTime WRITE setFxDelayTime NOTIFY fxDelayTimeChanged)
163 | Q_PROPERTY(int fxDelayFeedback READ fxDelayFeedback WRITE setFxDelayFeedback NOTIFY fxDelayFeedbackChanged)
164 | Q_PROPERTY(int fxDelayLowPass READ fxDelayLowPass WRITE setFxDelayLowPass NOTIFY fxDelayLowPassChanged)
165 | Q_PROPERTY(int fxDelayHighPass READ fxDelayHighPass WRITE setFxDelayHighPass NOTIFY fxDelayHighPassChanged)
166 | Q_PROPERTY(int fxDelayTempo READ fxDelayTempo WRITE setFxDelayTempo NOTIFY fxDelayTempoChanged)
167 | Q_PROPERTY(int fxDelayMultiplier READ fxDelayMultiplier WRITE setFxDelayMultiplier NOTIFY fxDelayMultiplierChanged)
168 | Q_PROPERTY(int fxDelayDivider READ fxDelayDivider WRITE setFxDelayDivider NOTIFY fxDelayDividerChanged)
169 |
170 | // FX: Amplitude modulator
171 | Q_PROPERTY(int fxAMDepth READ fxAMDepth WRITE setFxAMDepth NOTIFY fxAMDepthChanged)
172 | Q_PROPERTY(int fxAMSpeed READ fxAMSpeed WRITE setFxAMSpeed NOTIFY fxAMSpeedChanged)
173 | Q_PROPERTY(int fxAMRange READ fxAMRange WRITE setFxAMRange NOTIFY fxAMRangeChanged)
174 | Q_PROPERTY(int fxAMPhase READ fxAMPhase WRITE setFxAMPhase NOTIFY fxAMPhaseChanged)
175 |
176 | // FX: Reverb
177 | Q_PROPERTY(int fxReverbDry READ fxReverbDry WRITE setFxReverbDry NOTIFY fxReverbDryChanged)
178 | Q_PROPERTY(int fxReverbWet READ fxReverbWet WRITE setFxReverbWet NOTIFY fxReverbWetChanged)
179 | Q_PROPERTY(int fxReverbDecay READ fxReverbDecay WRITE setFxReverbDecay NOTIFY fxReverbDecayChanged)
180 | Q_PROPERTY(int fxReverbDamp READ fxReverbDamp WRITE setFxReverbDamp NOTIFY fxReverbDampChanged)
181 | Q_PROPERTY(int fxReverbMode READ fxReverbMode WRITE setFxReverbMode NOTIFY fxReverbModeChanged)
182 |
183 | // FX Routing
184 | Q_PROPERTY(int fxRoute READ fxRoute WRITE setFxRoute NOTIFY fxRouteChanged)
185 |
186 |
187 | public:
188 | explicit SynthModel(QObject *parent = nullptr);
189 |
190 | // Helper functions
191 |
192 | // Read and write the patch buffer. When writing, an optional parameter allows the current
193 | // patch to be saved as a new patch
194 | Q_INVOKABLE bool readPatchBuffer();
195 | Q_INVOKABLE bool writePatchBuffer(int toPatch=-1);
196 |
197 | // Initialise the patch buffer
198 | Q_INVOKABLE bool initPatchBuffer();
199 |
200 | // Restore the current patch (revert to saved version)
201 | Q_INVOKABLE bool reloadPatch();
202 |
203 | // Send operator changes to the synth and update the memory buffer
204 | Q_INVOKABLE bool updateOperator(XFMOperator *op, bool notify=false);
205 |
206 | // Operator algorithm design:
207 | // XFM2 allows any operator to be a carrier, and any operator can
208 | // also modulate any other operator
209 | Q_INVOKABLE bool isOperatorACarrier(int op);
210 | Q_INVOKABLE bool makeOperatorACarrier(int op, bool enabled);
211 | Q_INVOKABLE bool isOperatorModulating(int op, int modulatingop);
212 | Q_INVOKABLE bool makeOperatorModulate(int op, int modulatingop, bool enabled);
213 |
214 | // Similate a DX7 algorithm by configuring the operators
215 | // in the same way as the DX7 would, based on info at https://www.futur3soundz.com/da-blog/dx7-algorithms-in-xfm2
216 | Q_INVOKABLE bool makeDX7Algorithm(int a);
217 |
218 |
219 | signals:
220 | void patchNumberChanged();
221 | void operatorSyncChanged();
222 | void operatorModeChanged();
223 | void pitchEG_L0Changed();
224 | void pitchEG_L1Changed();
225 | void pitchEG_L2Changed();
226 | void pitchEG_L3Changed();
227 | void pitchEG_L4Changed();
228 | void pitchEG_L5Changed();
229 | void pitchEG_R0Changed();
230 | void pitchEG_R1Changed();
231 | void pitchEG_R2Changed();
232 | void pitchEG_R3Changed();
233 | void pitchEG_R4Changed();
234 | void pitchEG_R5Changed();
235 | void pitchEG_RangeChanged();
236 | void pitchEG_VelocityChanged();
237 | void pitchEG_RateKeyChanged();
238 | void lfoDepthPitchChanged();
239 | void lfoDepthAmplitudeChanged();
240 | void lfoSpeedChanged();
241 | void lfoSyncChanged();
242 | void lfoWaveChanged();
243 | void lfoFadeChanged();
244 | void modPitchLFOWheelChanged();
245 | void modPitchLFOBreathChanged();
246 | void modPitchLFOFootChanged();
247 | void modAmpLFOAftertouchChanged();
248 | void modAmpLFOWheelChanged();
249 | void modAmpLFOBreathChanged();
250 | void modAmpLFOFootChanged();
251 | void modPitchLFOAftertouchChanged();
252 | void masterPitchBendUpChanged();
253 | void masterPitchBendDownChanged();
254 | void masterTransposeChanged();
255 | void masterVolumeChanged();
256 | void masterPanChanged();
257 | void masterLegatoChanged();
258 | void modEnvelopeBiasAftertouchChanged();
259 | void modEnvelopeBiasWheelChanged();
260 | void modEnvelopeBiasBreathChanged();
261 | void modEnvelopeBiasFootChanged();
262 | void modPitchAftertouchChanged();
263 | void modPitchBreathChanged();
264 | void modPitchFootChanged();
265 | void modPitchRandomChanged();
266 | void portamentoModeChanged();
267 | void portamentoTimeChanged();
268 | void masterVelocityOffsetChanged();
269 | void envelopeLoopChanged();
270 | void envelopeLoopSegmentChanged();
271 | void envelopeRestartChanged();
272 | void masterTuningChanged();
273 | void operatorHasChanged();
274 | void outputLevelChanged();
275 | void arpeggiatorModeChanged();
276 | void arpeggiatorTempoChanged();
277 | void arpeggiatorTempoMultiplierChanged();
278 | void arpeggiatorOctaveRangeChanged();
279 | void fxBitCrushDepthChanged();
280 | void fxDecimatorChanged();
281 | void filterLoCutoffChanged();
282 | void filterHiCutoffChanged();
283 | void fxChorusDryChanged();
284 | void fxChorusWetChanged();
285 | void fxChorusModeChanged();
286 | void fxChorusSpeedChanged();
287 | void fxChorusDepthChanged();
288 | void fxChorusFeedbackChanged();
289 | void fxChorusPhaseChanged();
290 | void fxPhaserDryChanged();
291 | void fxPhaserWetChanged();
292 | void fxPhaserModeChanged();
293 | void fxPhaserSpeedChanged();
294 | void fxPhaserDepthChanged();
295 | void fxPhaserOffsetChanged();
296 | void fxPhaserStagesChanged();
297 | void fxPhaserFeedbackChanged();
298 | void fxPhaserPhaseChanged();
299 | void fxDelayDryChanged();
300 | void fxDelayWetChanged();
301 | void fxDelayModeChanged();
302 | void fxDelayTimeChanged();
303 | void fxDelayFeedbackChanged();
304 | void fxDelayLowPassChanged();
305 | void fxDelayHighPassChanged();
306 | void fxDelayTempoChanged();
307 | void fxDelayMultiplierChanged();
308 | void fxDelayDividerChanged();
309 | void fxAMDepthChanged();
310 | void fxAMSpeedChanged();
311 | void fxAMRangeChanged();
312 | void fxAMPhaseChanged();
313 | void fxReverbDryChanged();
314 | void fxReverbWetChanged();
315 | void fxReverbDecayChanged();
316 | void fxReverbDampChanged();
317 | void fxReverbModeChanged();
318 | void fxRouteChanged();
319 | void patchNameChanged();
320 |
321 | protected:
322 | bool isConnected() const;
323 |
324 | int patchNumber() const;
325 | void setPatchNumber(int p);
326 |
327 | int operatorSync();
328 | void setOperatorSync(int v);
329 |
330 | int operatorMode();
331 | void setOperatorMode(int v);
332 |
333 | int pitchEG_L0();
334 | void setPitchEG_L0(int f);
335 |
336 | int pitchEG_L1();
337 | void setPitchEG_L1(int f);
338 |
339 | int pitchEG_L2();
340 | void setPitchEG_L2(int f);
341 |
342 | int pitchEG_L3();
343 | void setPitchEG_L3(int f);
344 |
345 | int pitchEG_L4();
346 | void setPitchEG_L4(int f);
347 |
348 | int pitchEG_L5();
349 | void setPitchEG_L5(int f);
350 |
351 | int pitchEG_R0();
352 | void setPitchEG_R0(int f);
353 |
354 | int pitchEG_R1();
355 | void setPitchEG_R1(int f);
356 |
357 | int pitchEG_R2();
358 | void setPitchEG_R2(int f);
359 |
360 | int pitchEG_R3();
361 | void setPitchEG_R3(int f);
362 |
363 | int pitchEG_R4();
364 | void setPitchEG_R4(int f);
365 |
366 | int pitchEG_R5();
367 | void setPitchEG_R5(int f);
368 |
369 | int pitchEG_Range();
370 | void setPitchEG_Range(int f);
371 |
372 | int pitchEG_Velocity();
373 | void setPitchEG_Velocity(int f);
374 |
375 | int pitchEG_RateKey();
376 | void setPitchEG_RateKey(int f);
377 |
378 | int lfoDepthPitch();
379 | void setLFODepthPitch(int f);
380 |
381 | int lfoDepthAmplitude();
382 | void setLFODepthAmplitude(int v);
383 |
384 | int lfoSpeed();
385 | void setLFOSpeed(int v);
386 |
387 | int lfoSync();
388 | void setLFOSync(int v);
389 |
390 | int lfoWave();
391 | void setLFOWave(int v);
392 |
393 | int lfoFade();
394 | void setLFOFade(int v);
395 |
396 | int modPitchLFOWheel();
397 | void setModPitchLFOWheel(int v);
398 |
399 | int modAmpLFOAftertouch();
400 | void setModAmpLFOAftertouch(int v);
401 |
402 | int modPitchLFOBreath();
403 | void setModPitchLFOBreath(int v);
404 |
405 | int modPitchLFOFoot();
406 | void setModPitchLFOFoot(int v);
407 |
408 | int modAmpLFOWheel();
409 | void setModAmpLFOWheel(int v);
410 |
411 | int modAmpLFOBreath();
412 | void setModAmpLFOBreath(int v);
413 |
414 | int modAmpLFOFoot();
415 | void setModAmpLFOFoot(int v);
416 |
417 | int modPitchLFOAftertouch();
418 | void setModPitchLFOAftertouch(int v);
419 |
420 | int masterPitchBendUp();
421 | void setMasterPitchBendUp(int v);
422 |
423 | int masterPitchBendDown();
424 | void setMasterPitchBendDown(int v);
425 |
426 | int masterTranspose();
427 | void setMasterTranspose(int v);
428 |
429 | int masterVolume();
430 | void setMasterVolume(int v);
431 |
432 | int masterPan();
433 | void setMasterPan(int v);
434 |
435 | int masterLegato();
436 | void setMasterLegato(int v);
437 |
438 | int masterVelocityOffset();
439 | void setMasterVelocityOffset(int v);
440 |
441 | int masterTuning();
442 | void setMasterTuning(int v);
443 |
444 | int modEnvelopeBiasAftertouch();
445 | void setModEnvelopeBiasAftertouch(int v);
446 |
447 | int modEnvelopeBiasWheel();
448 | void setModEnvelopeBiasWheel(int v);
449 |
450 | int modEnvelopeBiasBreath();
451 | void setModEnvelopeBiasBreath(int v);
452 |
453 | int modEnvelopeBiasFoot();
454 | void setModEnvelopeBiasFoot(int v);
455 |
456 | int modPitchAftertouch();
457 | void setModPitchAftertouch(int v);
458 |
459 | int modPitchBreath();
460 | void setModPitchBreath(int v);
461 |
462 | int modPitchFoot();
463 | void setModPitchFoot(int v);
464 |
465 | int modPitchRandom();
466 | void setModPitchRandom(int v);
467 |
468 | int portamentoMode();
469 | void setPortamentoMode(int v);
470 |
471 | int portamentoTime();
472 | void setPortamentoTime(int v);
473 |
474 | int envelopeLoop();
475 | void setEnvelopeLoop(int v);
476 |
477 | int envelopeLoopSegment();
478 | void setEnvelopeLoopSegment(int v);
479 |
480 | int envelopeRestart();
481 | void setEnvelopeRestart(int v);
482 |
483 | int outputLevel();
484 | void setOutputLevel(int v);
485 |
486 | int arpeggiatorMode();
487 | void setArpeggiatorMode(int v);
488 |
489 | int arpeggiatorTempo();
490 | void setArpeggiatorTempo(int v);
491 |
492 | int arpeggiatorTempoMultiplier();
493 | void setArpeggiatorTempoMultiplier(int v);
494 |
495 | int arpeggiatorOctaveRange();
496 | void setArpeggiatorOctaveRange(int v);
497 |
498 | int fxBitCrushDepth();
499 | void setFxBitCrushDepth(int v);
500 |
501 | int fxDecimator();
502 | void setFxDecimator(int v);
503 |
504 | int filterLoCutoff();
505 | void setFilterLoCutoff(int v);
506 |
507 | int filterHiCutoff();
508 | void setFilterHiCutoff(int v);
509 |
510 | int fxChorusDry();
511 | void setFxChorusDry(int v);
512 |
513 | int fxChorusWet();
514 | void setFxChorusWet(int v);
515 |
516 | int fxChorusMode();
517 | void setFxChorusMode(int v);
518 |
519 | int fxChorusSpeed();
520 | void setFxChorusSpeed(int v);
521 |
522 | int fxChorusDepth();
523 | void setFxChorusDepth(int v);
524 |
525 | int fxChorusFeedback();
526 | void setFxChorusFeedback(int v);
527 |
528 | int fxChorusPhase();
529 | void setFxChorusPhase(int v);
530 |
531 | int fxPhaserDry();
532 | void setFxPhaserDry(int v);
533 |
534 | int fxPhaserWet();
535 | void setFxPhaserWet(int v);
536 |
537 | int fxPhaserMode();
538 | void setFxPhaserMode(int v);
539 |
540 | int fxPhaserSpeed();
541 | void setFxPhaserSpeed(int v);
542 |
543 | int fxPhaserDepth();
544 | void setFxPhaserDepth(int v);
545 |
546 | int fxPhaserOffset();
547 | void setFxPhaserOffset(int v);
548 |
549 | int fxPhaserStages();
550 | void setFxPhaserStages(int v);
551 |
552 | int fxPhaserFeedback();
553 | void setFxPhaserFeedback(int v);
554 |
555 | int fxPhaserPhase();
556 | void setFxPhaserPhase(int v);
557 |
558 | int fxDelayDry();
559 | void setFxDelayDry(int v);
560 |
561 | int fxDelayWet();
562 | void setFxDelayWet(int v);
563 |
564 | int fxDelayMode();
565 | void setFxDelayMode(int v);
566 |
567 | int fxDelayTime();
568 | void setFxDelayTime(int v);
569 |
570 | int fxDelayFeedback();
571 | void setFxDelayFeedback(int v);
572 |
573 | int fxDelayLowPass();
574 | void setFxDelayLowPass(int v);
575 |
576 | int fxDelayHighPass();
577 | void setFxDelayHighPass(int v);
578 |
579 | int fxDelayTempo();
580 | void setFxDelayTempo(int v);
581 |
582 | int fxDelayMultiplier();
583 | void setFxDelayMultiplier(int v);
584 |
585 | int fxDelayDivider();
586 | void setFxDelayDivider(int v);
587 |
588 | int fxAMDepth();
589 | void setFxAMDepth(int v);
590 |
591 | int fxAMSpeed();
592 | void setFxAMSpeed(int v);
593 |
594 | int fxAMRange();
595 | void setFxAMRange(int v);
596 |
597 | int fxAMPhase();
598 | void setFxAMPhase(int v);
599 |
600 | int fxReverbDry();
601 | void setFxReverbDry(int v);
602 |
603 | int fxReverbWet();
604 | void setFxReverbWet(int v);
605 |
606 | int fxReverbDecay();
607 | void setFxReverbDecay(int v);
608 |
609 | int fxReverbDamp();
610 | void setFxReverbDamp(int v);
611 |
612 | int fxReverbMode();
613 | void setFxReverbMode(int v);
614 |
615 | int fxRoute();
616 | void setFxRoute(int v);
617 |
618 | void loadPatchNames();
619 | void savePatchNames();
620 |
621 | QString patchName();
622 | void setPatchName(const QString &str);
623 |
624 | unsigned char readMemoryLocation(XFM2Parameter offset, bool useCache=true);
625 | bool writeMemoryLocation(XFM2Parameter offset, unsigned char data);
626 |
627 | QList fmOperators();
628 |
629 | private:
630 | unsigned char m_xfm2[512]; // Memory buffer
631 | std::vector m_patchNames; // XFM2 hardware doesn't hold patch names, so we use the app to store them
632 | QSerialPort * m_port; // USB serial port connection
633 | int m_patchnumber; // Current patch number
634 | bool m_isconnected; // True if the hardware is connected
635 | bool m_initialised; // True if the model is initialised and the memory buffer has been read
636 | std::string m_patchNameBuffer; // The current patch name
637 | };
638 |
639 | #endif // SYNTHMODEL_H
640 |
--------------------------------------------------------------------------------
/src/CustomKeyboard.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.9
25 | import QtQuick.Window 2.3
26 | import QtQuick.Controls 2.2
27 |
28 | /*
29 | * This is a custom virtual keyboard because it is actually
30 | * rather difficult to get the standard Qt keyboard working on
31 | * a Raspberry Pi. I ran out of patience and built my own.
32 | * It suits my requirements - your mileage may vary ;-)
33 | */
34 |
35 | Item {
36 | id: root
37 | width:800
38 | height:260
39 |
40 | signal keyPressed(var keyval)
41 |
42 | property var keyColor: "#D3D3D3"
43 | property var backgroundColor: "#202020"
44 | property bool punctuationMode: false
45 |
46 | function setPunctuationMode(enabled)
47 | {
48 | punctuationMode=enabled;
49 |
50 | if (enabled) {
51 | keyQ.text="!";
52 | keyW.text="=";
53 | keyE.text="&";
54 | keyR.text="\\";
55 | keyT.text="?";
56 | keyY.text="(";
57 | keyU.text="{";
58 | keyI.text="\"";
59 | keyO.text="~";
60 | keyP.text="_";
61 |
62 | keyA.text="#";
63 | keyS.text="$";
64 | keyD.text="*";
65 | keyF.text="/";
66 | keyG.text=":";
67 | keyH.text=")";
68 | keyJ.text="}";
69 | keyK.text="'";
70 | keyL.text="`";
71 |
72 | keyZ.text="%";
73 | keyX.text="^";
74 | keyC.text="-";
75 | keyV.text="+";
76 | keyB.text=";";
77 | keyN.text=",";
78 | keyM.text="|";
79 | } else {
80 | keyQ.text="Q";
81 | keyW.text="W";
82 | keyE.text="E";
83 | keyR.text="R";
84 | keyT.text="T";
85 | keyY.text="Y";
86 | keyU.text="U";
87 | keyI.text="I";
88 | keyO.text="O";
89 | keyP.text="P";
90 |
91 | keyA.text="A";
92 | keyS.text="S";
93 | keyD.text="D";
94 | keyF.text="F";
95 | keyG.text="G";
96 | keyH.text="H";
97 | keyJ.text="J";
98 | keyK.text="K";
99 | keyL.text="L";
100 |
101 | keyZ.text="Z";
102 | keyX.text="X";
103 | keyC.text="C";
104 | keyV.text="V";
105 | keyB.text="B";
106 | keyN.text="N";
107 | keyM.text="M";
108 | }
109 | }
110 |
111 | Rectangle {
112 | id: rectangleBackground
113 | x: 0
114 | y: 0
115 | width: parent.width
116 | height: 260
117 | color: backgroundColor
118 |
119 | Button {
120 | id: key1
121 | x: 13
122 | y: 8
123 | width: 56
124 | height: 56
125 | text: "1"
126 | font.bold: true
127 | flat: false
128 | background: Rectangle {
129 | color: keyColor
130 | radius: 8
131 | }
132 | font.pixelSize: 28
133 | onClicked: {
134 | root.keyPressed("1");
135 | }
136 | }
137 |
138 | Button {
139 | id: key2
140 | x: 85
141 | y: 8
142 | width: 56
143 | height: 56
144 | text: "2"
145 | font.bold: true
146 | flat: false
147 | background: Rectangle {
148 | color: keyColor
149 | radius: 8
150 | }
151 | font.pixelSize: 28
152 | onClicked: {
153 | root.keyPressed("2");
154 | }
155 | }
156 |
157 | Button {
158 | id: key3
159 | x: 157
160 | y: 8
161 | width: 56
162 | height: 56
163 | text: "3"
164 | font.bold: true
165 | flat: false
166 | background: Rectangle {
167 | color: keyColor
168 | radius: 8
169 | }
170 | font.pixelSize: 28
171 | onClicked: {
172 | root.keyPressed("3");
173 | }
174 | }
175 |
176 | Button {
177 | id: key4
178 | x: 229
179 | y: 8
180 | width: 56
181 | height: 56
182 | text: "4"
183 | font.bold: true
184 | flat: false
185 | background: Rectangle {
186 | color: keyColor
187 | radius: 8
188 | }
189 | font.pixelSize: 28
190 | onClicked: {
191 | root.keyPressed("4");
192 | }
193 | }
194 |
195 | Button {
196 | id: key5
197 | x: 301
198 | y: 8
199 | width: 56
200 | height: 56
201 | text: "5"
202 | font.bold: true
203 | flat: false
204 | background: Rectangle {
205 | color: keyColor
206 | radius: 8
207 | }
208 | font.pixelSize: 28
209 | onClicked: {
210 | root.keyPressed("5");
211 | }
212 | }
213 |
214 | Button {
215 | id: key6
216 | x: 373
217 | y: 8
218 | width: 56
219 | height: 56
220 | text: "6"
221 | font.bold: true
222 | flat: false
223 | background: Rectangle {
224 | color: keyColor
225 | radius: 8
226 | }
227 | font.pixelSize: 28
228 | onClicked: {
229 | root.keyPressed("6");
230 | }
231 | }
232 |
233 | Button {
234 | id: key7
235 | x: 445
236 | y: 8
237 | width: 56
238 | height: 56
239 | text: "7"
240 | font.bold: true
241 | flat: false
242 | background: Rectangle {
243 | color: keyColor
244 | radius: 8
245 | }
246 | font.pixelSize: 28
247 | onClicked: {
248 | root.keyPressed("7");
249 | }
250 | }
251 |
252 | Button {
253 | id: key8
254 | x: 517
255 | y: 8
256 | width: 56
257 | height: 56
258 | text: "8"
259 | font.bold: true
260 | flat: false
261 | background: Rectangle {
262 | color: keyColor
263 | radius: 8
264 | }
265 | font.pixelSize: 28
266 | onClicked: {
267 | root.keyPressed("8");
268 | }
269 | }
270 |
271 | Button {
272 | id: key9
273 | x: 589
274 | y: 8
275 | width: 56
276 | height: 56
277 | text: "9"
278 | font.bold: true
279 | flat: false
280 | background: Rectangle {
281 | color: keyColor
282 | radius: 8
283 | }
284 | font.pixelSize: 28
285 | onClicked: {
286 | root.keyPressed("9");
287 | }
288 | }
289 |
290 | Button {
291 | id: key10
292 | x: 661
293 | y: 8
294 | width: 56
295 | height: 56
296 | text: "0"
297 | font.bold: true
298 | flat: false
299 | background: Rectangle {
300 | color: keyColor
301 | radius: 8
302 | }
303 | font.pixelSize: 28
304 | onClicked: {
305 | root.keyPressed("0");
306 | }
307 | }
308 |
309 | Button {
310 | id: keyQ
311 | x: 14
312 | y: 69
313 | width: 56
314 | height: 56
315 | text: qsTr("Q")
316 | font.bold: true
317 | flat: false
318 | background: Rectangle {
319 | color: keyColor
320 | radius: 8
321 | }
322 | font.pixelSize: 28
323 | onClicked: {
324 | root.keyPressed(punctuationMode ? "!" : "Q");
325 | }
326 | }
327 |
328 | Button {
329 | id: keyA
330 | x: 14
331 | y: 130
332 | width: 56
333 | height: 56
334 | text: qsTr("A")
335 | font.bold: true
336 | flat: false
337 | background: Rectangle {
338 | color: keyColor
339 | radius: 8
340 | }
341 | font.pixelSize: 28
342 | onClicked: {
343 | root.keyPressed(punctuationMode ? "#" : "A");
344 | }
345 | }
346 |
347 | Button {
348 | id: keyZ
349 | x: 14
350 | y: 191
351 | width: 56
352 | height: 56
353 | text: qsTr("Z")
354 | font.bold: true
355 | flat: false
356 | background: Rectangle {
357 | color: keyColor
358 | radius: 8
359 | }
360 | font.pixelSize: 28
361 | onClicked: {
362 | root.keyPressed(punctuationMode ? "%" : "Z");
363 | }
364 | }
365 |
366 | Button {
367 | id: keyW
368 | x: 86
369 | y: 69
370 | width: 56
371 | height: 56
372 | text: "W"
373 | font.bold: true
374 | flat: false
375 | background: Rectangle {
376 | color: keyColor
377 | radius: 8
378 | }
379 | font.pixelSize: 28
380 | onClicked: {
381 | root.keyPressed(punctuationMode ? "=" : "W");
382 | }
383 | }
384 |
385 | Button {
386 | id: keyE
387 | x: 158
388 | y: 69
389 | width: 56
390 | height: 56
391 | text: "E"
392 | font.bold: true
393 | flat: false
394 | background: Rectangle {
395 | color: keyColor
396 | radius: 8
397 | }
398 | font.pixelSize: 28
399 | onClicked: {
400 | root.keyPressed(punctuationMode ? "&" : "E");
401 | }
402 | }
403 |
404 | Button {
405 | id: keyR
406 | x: 230
407 | y: 69
408 | width: 56
409 | height: 56
410 | text: "R"
411 | font.bold: true
412 | flat: false
413 | background: Rectangle {
414 | color: keyColor
415 | radius: 8
416 | }
417 | font.pixelSize: 28
418 | onClicked: {
419 | root.keyPressed(punctuationMode ? "\\" : "R");
420 | }
421 | }
422 |
423 | Button {
424 | id: keyT
425 | x: 302
426 | y: 69
427 | width: 56
428 | height: 56
429 | text: "T"
430 | font.bold: true
431 | flat: false
432 | background: Rectangle {
433 | color: keyColor
434 | radius: 8
435 | }
436 | font.pixelSize: 28
437 | onClicked: {
438 | root.keyPressed(punctuationMode ? "?" : "T");
439 | }
440 | }
441 |
442 | Button {
443 | id: keyY
444 | x: 374
445 | y: 69
446 | width: 56
447 | height: 56
448 | text: "Y"
449 | font.bold: true
450 | flat: false
451 | background: Rectangle {
452 | color: keyColor
453 | radius: 8
454 | }
455 | font.pixelSize: 28
456 | onClicked: {
457 | root.keyPressed(punctuationMode ? "(" : "Y");
458 | }
459 | }
460 |
461 | Button {
462 | id: keyU
463 | x: 446
464 | y: 69
465 | width: 56
466 | height: 56
467 | text: "U"
468 | font.bold: true
469 | flat: false
470 | background: Rectangle {
471 | color: keyColor
472 | radius: 8
473 | }
474 | font.pixelSize: 28
475 | onClicked: {
476 | root.keyPressed(punctuationMode ? "{" : "U");
477 | }
478 | }
479 |
480 | Button {
481 | id: keyI
482 | x: 518
483 | y: 69
484 | width: 56
485 | height: 56
486 | text: "I"
487 | font.bold: true
488 | flat: false
489 | background: Rectangle {
490 | color: keyColor
491 | radius: 8
492 | }
493 | font.pixelSize: 28
494 | onClicked: {
495 | root.keyPressed(punctuationMode ? "\"" : "I");
496 | }
497 | }
498 |
499 | Button {
500 | id: keyO
501 | x: 590
502 | y: 69
503 | width: 56
504 | height: 56
505 | text: "O"
506 | font.bold: true
507 | flat: false
508 | background: Rectangle {
509 | color: keyColor
510 | radius: 8
511 | }
512 | font.pixelSize: 28
513 | onClicked: {
514 | root.keyPressed(punctuationMode ? "~" : "O");
515 | }
516 | }
517 |
518 | Button {
519 | id: keyP
520 | x: 662
521 | y: 69
522 | width: 56
523 | height: 56
524 | text: "P"
525 | font.bold: true
526 | flat: false
527 | background: Rectangle {
528 | color: keyColor
529 | radius: 8
530 | }
531 | font.pixelSize: 28
532 | onClicked: {
533 | root.keyPressed(punctuationMode ? "_" : "P");
534 | }
535 | }
536 |
537 | Button {
538 | id: keyS
539 | x: 86
540 | y: 130
541 | width: 56
542 | height: 56
543 | text: "S"
544 | font.bold: true
545 | flat: false
546 | background: Rectangle {
547 | color: keyColor
548 | radius: 8
549 | }
550 | font.pixelSize: 28
551 | onClicked: {
552 | root.keyPressed(punctuationMode ? "$" : "S");
553 | }
554 | }
555 |
556 | Button {
557 | id: keyD
558 | x: 158
559 | y: 130
560 | width: 56
561 | height: 56
562 | text: "D"
563 | font.bold: true
564 | flat: false
565 | background: Rectangle {
566 | color: keyColor
567 | radius: 8
568 | }
569 | font.pixelSize: 28
570 | onClicked: {
571 | root.keyPressed(punctuationMode ? "*" : "D");
572 | }
573 | }
574 |
575 | Button {
576 | id: keyF
577 | x: 230
578 | y: 130
579 | width: 56
580 | height: 56
581 | text: "F"
582 | font.bold: true
583 | flat: false
584 | background: Rectangle {
585 | color: keyColor
586 | radius: 8
587 | }
588 | font.pixelSize: 28
589 | onClicked: {
590 | root.keyPressed(punctuationMode ? "/" : "F");
591 | }
592 | }
593 |
594 | Button {
595 | id: keyG
596 | x: 302
597 | y: 130
598 | width: 56
599 | height: 56
600 | text: "G"
601 | font.bold: true
602 | flat: false
603 | background: Rectangle {
604 | color: keyColor
605 | radius: 8
606 | }
607 | font.pixelSize: 28
608 | onClicked: {
609 | root.keyPressed(punctuationMode ? ":" : "G");
610 | }
611 | }
612 |
613 | Button {
614 | id: keyH
615 | x: 374
616 | y: 130
617 | width: 56
618 | height: 56
619 | text: "H"
620 | font.bold: true
621 | flat: false
622 | background: Rectangle {
623 | color: keyColor
624 | radius: 8
625 | }
626 | font.pixelSize: 28
627 | onClicked: {
628 | root.keyPressed(punctuationMode ? ")" : "H");
629 | }
630 | }
631 |
632 | Button {
633 | id: keyJ
634 | x: 446
635 | y: 130
636 | width: 56
637 | height: 56
638 | text: "J"
639 | font.bold: true
640 | flat: false
641 | background: Rectangle {
642 | color: keyColor
643 | radius: 8
644 | }
645 | font.pixelSize: 28
646 | onClicked: {
647 | root.keyPressed(punctuationMode ? "}" : "J");
648 | }
649 | }
650 |
651 | Button {
652 | id: keyK
653 | x: 518
654 | y: 130
655 | width: 56
656 | height: 56
657 | text: "K"
658 | font.bold: true
659 | flat: false
660 | background: Rectangle {
661 | color: keyColor
662 | radius: 8
663 | }
664 | font.pixelSize: 28
665 | onClicked: {
666 | root.keyPressed(punctuationMode ? "'" : "K");
667 | }
668 | }
669 |
670 | Button {
671 | id: keyL
672 | x: 590
673 | y: 130
674 | width: 56
675 | height: 56
676 | text: "L"
677 | font.bold: true
678 | flat: false
679 | background: Rectangle {
680 | color: keyColor
681 | radius: 8
682 | }
683 | font.pixelSize: 28
684 | onClicked: {
685 | root.keyPressed(punctuationMode ? "`" : "L");
686 | }
687 | }
688 |
689 | Button {
690 | id: keyX
691 | x: 86
692 | y: 191
693 | width: 56
694 | height: 56
695 | text: "X"
696 | font.bold: true
697 | flat: false
698 | background: Rectangle {
699 | color: keyColor
700 | radius: 8
701 | }
702 | font.pixelSize: 28
703 | onClicked: {
704 | root.keyPressed(punctuationMode ? "%" : "X");
705 | }
706 | }
707 |
708 | Button {
709 | id: keyC
710 | x: 158
711 | y: 191
712 | width: 56
713 | height: 56
714 | text: "C"
715 | font.bold: true
716 | flat: false
717 | background: Rectangle {
718 | color: keyColor
719 | radius: 8
720 | }
721 | font.pixelSize: 28
722 | onClicked: {
723 | root.keyPressed(punctuationMode ? "-" : "C");
724 | }
725 | }
726 |
727 | Button {
728 | id: keyV
729 | x: 230
730 | y: 191
731 | width: 56
732 | height: 56
733 | text: "V"
734 | font.bold: true
735 | flat: false
736 | background: Rectangle {
737 | color: keyColor
738 | radius: 8
739 | }
740 | font.pixelSize: 28
741 | onClicked: {
742 | root.keyPressed(punctuationMode ? "+" : "V");
743 | }
744 | }
745 |
746 | Button {
747 | id: keyB
748 | x: 302
749 | y: 191
750 | width: 56
751 | height: 56
752 | text: "B"
753 | font.bold: true
754 | flat: false
755 | background: Rectangle {
756 | color: keyColor
757 | radius: 8
758 | }
759 | font.pixelSize: 28
760 | onClicked: {
761 | root.keyPressed(punctuationMode ? ";" : "B");
762 | }
763 | }
764 |
765 | Button {
766 | id: keyN
767 | x: 374
768 | y: 191
769 | width: 56
770 | height: 56
771 | text: "N"
772 | font.bold: true
773 | flat: false
774 | background: Rectangle {
775 | color: keyColor
776 | radius: 8
777 | }
778 | font.pixelSize: 28
779 | onClicked: {
780 | root.keyPressed(punctuationMode ? "," : "N");
781 | }
782 | }
783 |
784 | Button {
785 | id: keyM
786 | x: 446
787 | y: 191
788 | width: 56
789 | height: 56
790 | text: "M"
791 | font.bold: true
792 | flat: false
793 | background: Rectangle {
794 | color: keyColor
795 | radius: 8
796 | }
797 | font.pixelSize: 28
798 | onClicked: {
799 | root.keyPressed(punctuationMode ? "|" : "M");
800 | }
801 | }
802 |
803 | Button {
804 | id: keySpace
805 | x: 590
806 | y: 191
807 | width: 56
808 | height: 56
809 | text: ""
810 | font.bold: true
811 | flat: false
812 | background: Rectangle {
813 | color: keyColor
814 | radius: 8
815 | }
816 | font.pixelSize: 28
817 | Image {
818 | id: keySpaceImage
819 | width: 38
820 | height: 38
821 | anchors.fill: parent
822 | source: "images/Key - Space.png"
823 | fillMode: Image.PreserveAspectFit
824 | }
825 | onClicked: {
826 | root.keyPressed(" ");
827 | }
828 | }
829 |
830 | Button {
831 | id: keyLeft
832 | x: 662
833 | y: 191
834 | width: 56
835 | height: 56
836 | text: ""
837 | font.bold: true
838 | flat: false
839 | background: Rectangle {
840 | color: keyColor
841 | radius: 8
842 | }
843 | font.pixelSize: 28
844 | Image {
845 | id: keyLeftImage
846 | width: 38
847 | height: 38
848 | anchors.centerIn: parent
849 | source: "images/Key - Left Arrow.png"
850 | fillMode: Image.PreserveAspectFit
851 | }
852 | onClicked: {
853 | root.keyPressed("LEFT");
854 | }
855 | }
856 |
857 | Button {
858 | id: keyRight
859 | x: 733
860 | y: 191
861 | width: 56
862 | height: 56
863 | text: ""
864 | font.bold: true
865 | flat: false
866 | background: Rectangle {
867 | color: keyColor
868 | radius: 8
869 | }
870 | font.pixelSize: 28
871 | Image {
872 | id: keyRightImage
873 | width: 38
874 | height: 38
875 | anchors.centerIn: parent
876 | source: "images/Key - Right Arrow.png"
877 | fillMode: Image.PreserveAspectFit
878 | }
879 | onClicked: {
880 | root.keyPressed("RIGHT");
881 | }
882 | }
883 |
884 | Button {
885 | id: keyBackspace
886 | x: 733
887 | y: 8
888 | width: 56
889 | height: 56
890 | text: ""
891 | font.bold: true
892 | flat: false
893 | background: Rectangle {
894 | color: keyColor
895 | radius: 8
896 | }
897 | font.pixelSize: 28
898 | Image {
899 | id: keyBSImage
900 | width: 42
901 | height: 42
902 | anchors.centerIn: parent
903 | source: "images/Key - Backspace.png"
904 | fillMode: Image.PreserveAspectFit
905 | }
906 | onClicked: {
907 | root.keyPressed("DEL");
908 | }
909 | }
910 |
911 | Button {
912 | id: keyEnter
913 | x: 733
914 | y: 69
915 | width: 56
916 | height: 56
917 | text: ""
918 | font.family: "Futura PT"
919 | flat: false
920 | background: Rectangle {
921 | color: keyColor
922 | radius: 8
923 | }
924 | font.pixelSize: 28
925 | Image {
926 | id: keyEnterImage
927 | width: 42
928 | height: 42
929 | anchors.centerIn: parent
930 | source: "images/Key - Enter.png"
931 | fillMode: Image.PreserveAspectFit
932 | }
933 | onClicked: {
934 | root.keyPressed("ENTER");
935 | }
936 | }
937 |
938 | Button {
939 | id: keyAt
940 | x: 662
941 | y: 130
942 | width: 56
943 | height: 56
944 | text: "@"
945 | flat: false
946 | background: Rectangle {
947 | color: keyColor
948 | radius: 8
949 | }
950 | font.pixelSize: 28
951 | font.bold: true
952 | onClicked: {
953 | root.keyPressed("@");
954 | }
955 | }
956 |
957 | Button {
958 | id: keyPeriod
959 | x: 518
960 | y: 191
961 | width: 56
962 | height: 56
963 | text: "."
964 | font.bold: true
965 | flat: false
966 | background: Rectangle {
967 | color: keyColor
968 | radius: 8
969 | }
970 | font.pixelSize: 28
971 | onClicked: {
972 | root.keyPressed(".");
973 | }
974 | }
975 |
976 | Button {
977 | id: keyPunct
978 | x: 733
979 | y: 129
980 | width: 56
981 | height: 56
982 | text: "..."
983 | flat: false
984 | font.bold: true
985 | background: Rectangle {
986 | color: keyColor
987 | radius: 8
988 | }
989 | font.pixelSize: 28
990 | onClicked: {
991 | // Toggle punctuation mode
992 | // This relabels a bunch of key caps
993 | setPunctuationMode(!punctuationMode);
994 | }
995 | }
996 | }
997 | }
998 |
--------------------------------------------------------------------------------
/src/EnvelopePage.qml:
--------------------------------------------------------------------------------
1 | /*
2 | * XFM2 Synth Controller
3 | *
4 | * This is a user-friendly controller for the excellent XFM2 synth hardware designed by Futur3soundz
5 | * https://www.futur3soundz.com/xfm2
6 | *
7 | *
8 | * This file is part of the XFM2Controller distribution (https://github.com/ataristdude/xfm2controller).
9 | * Copyright (c) 2020 Don Fletcher
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, version 3.
14 | *
15 | * This program is distributed in the hope that it will be useful, but
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 | * General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | */
23 |
24 | import QtQuick 2.12
25 | import QtQuick.Controls 2.3
26 | import QtQuick.Extras 1.4
27 | import Xfm.Synth 1.0
28 |
29 | Item {
30 | id: envelopePage
31 | width: 800
32 | height: 357
33 |
34 | Connections {
35 | target: synthModel
36 | onPatchNumberChanged: {
37 | updatePage();
38 | }
39 | }
40 |
41 | function updatePage()
42 | {
43 | dialRange.value=synthModel.pitchEG_Range;
44 | dialVelocity.value=synthModel.pitchEG_Velocity;
45 | dialRateKey.value=synthModel.pitchEG_RateKey;
46 | canvas.requestPaint();
47 | }
48 |
49 | Rectangle {
50 | anchors.fill: parent
51 | color: "#505050"
52 | }
53 |
54 | Row {
55 | id: envLoopRow
56 | x: 444
57 | y: 151
58 | width: 334
59 | height: 56
60 | spacing: 2
61 |
62 | Label {
63 | id: labelLoop1
64 | width: 50
65 | text: qsTr("1")
66 | font.pixelSize: 24
67 | verticalAlignment: Text.AlignVCenter
68 | horizontalAlignment: Text.AlignHCenter
69 | anchors.bottom: parent.bottom
70 | anchors.bottomMargin: 0
71 | anchors.top: parent.top
72 | anchors.topMargin: 0
73 | color: synthModel.envelopeLoop & 0x01 ? "#ffffff" : "#000000"
74 | background: Rectangle {
75 | color: synthModel.envelopeLoop & 0x01 ? "#0000FF" : "#808080"
76 | radius: 5
77 | }
78 |
79 | MouseArea {
80 | anchors.fill: parent
81 | onClicked: {
82 | var i=synthModel.envelopeLoop;
83 |
84 | i=i ^ 0x01;
85 | synthModel.envelopeLoop=i;
86 | }
87 | }
88 | }
89 | Label {
90 | id: labelLoop2
91 | width: 50
92 | text: qsTr("2")
93 | font.pixelSize: 24
94 | verticalAlignment: Text.AlignVCenter
95 | horizontalAlignment: Text.AlignHCenter
96 | anchors.bottom: parent.bottom
97 | anchors.bottomMargin: 0
98 | anchors.top: parent.top
99 | anchors.topMargin: 0
100 | color: synthModel.envelopeLoop & 0x02 ? "#ffffff" : "#000000"
101 | background: Rectangle {
102 | color: synthModel.envelopeLoop & 0x02 ? "#0000FF" : "#808080"
103 | radius: 5
104 | }
105 | MouseArea {
106 | anchors.fill: parent
107 | onClicked: {
108 | var i=synthModel.envelopeLoop;
109 |
110 | i=i ^ 0x02;
111 | synthModel.envelopeLoop=i;
112 | }
113 | }
114 | }
115 | Label {
116 | id: labelLoop3
117 | width: 50
118 | text: qsTr("3")
119 | font.pixelSize: 24
120 | verticalAlignment: Text.AlignVCenter
121 | horizontalAlignment: Text.AlignHCenter
122 | anchors.bottom: parent.bottom
123 | anchors.bottomMargin: 0
124 | anchors.top: parent.top
125 | anchors.topMargin: 0
126 | color: synthModel.envelopeLoop & 0x04 ? "#ffffff" : "#000000"
127 | background: Rectangle {
128 | color: synthModel.envelopeLoop & 0x04 ? "#0000FF" : "#808080"
129 | radius: 5
130 | }
131 | MouseArea {
132 | anchors.fill: parent
133 | onClicked: {
134 | var i=synthModel.envelopeLoop;
135 |
136 | i=i ^ 0x04;
137 | synthModel.envelopeLoop=i;
138 | }
139 | }
140 | }
141 | Label {
142 | id: labelLoop4
143 | width: 50
144 | text: qsTr("4")
145 | font.pixelSize: 24
146 | verticalAlignment: Text.AlignVCenter
147 | horizontalAlignment: Text.AlignHCenter
148 | anchors.bottom: parent.bottom
149 | anchors.bottomMargin: 0
150 | anchors.top: parent.top
151 | anchors.topMargin: 0
152 | color: synthModel.envelopeLoop & 0x08 ? "#ffffff" : "#000000"
153 | background: Rectangle {
154 | color: synthModel.envelopeLoop & 0x08 ? "#0000FF" : "#808080"
155 | radius: 5
156 | }
157 | MouseArea {
158 | anchors.fill: parent
159 | onClicked: {
160 | var i=synthModel.envelopeLoop;
161 |
162 | i=i ^ 0x08;
163 | synthModel.envelopeLoop=i;
164 | }
165 | }
166 | }
167 | Label {
168 | id: labelLoop5
169 | width: 50
170 | text: qsTr("5")
171 | font.pixelSize: 24
172 | verticalAlignment: Text.AlignVCenter
173 | horizontalAlignment: Text.AlignHCenter
174 | anchors.bottom: parent.bottom
175 | anchors.bottomMargin: 0
176 | anchors.top: parent.top
177 | anchors.topMargin: 0
178 | color: synthModel.envelopeLoop & 0x10 ? "#ffffff" : "#000000"
179 | background: Rectangle {
180 | color: synthModel.envelopeLoop & 0x10 ? "#0000FF" : "#808080"
181 | radius: 5
182 | }
183 | MouseArea {
184 | anchors.fill: parent
185 | onClicked: {
186 | var i=synthModel.envelopeLoop;
187 |
188 | i=i ^ 0x10;
189 | synthModel.envelopeLoop=i;
190 | }
191 | }
192 | }
193 | Label {
194 | id: labelLoop6
195 | width: 50
196 | text: qsTr("6")
197 | font.pixelSize: 24
198 | verticalAlignment: Text.AlignVCenter
199 | horizontalAlignment: Text.AlignHCenter
200 | anchors.bottom: parent.bottom
201 | anchors.bottomMargin: 0
202 | anchors.top: parent.top
203 | anchors.topMargin: 0
204 | color: synthModel.envelopeLoop & 0x20 ? "#ffffff" : "#000000"
205 | background: Rectangle {
206 | color: synthModel.envelopeLoop & 0x20 ? "#0000FF" : "#808080"
207 | radius: 5
208 | }
209 | MouseArea {
210 | anchors.fill: parent
211 | onClicked: {
212 | var i=synthModel.envelopeLoop;
213 |
214 | i=i ^ 0x20;
215 | synthModel.envelopeLoop=i;
216 | }
217 | }
218 | }
219 | }
220 |
221 |
222 | Row {
223 | id: evSegRow
224 | x: 444
225 | y: 220
226 | width: 334
227 | height: 56
228 | spacing: 2
229 |
230 | Label {
231 | id: labelSeg1
232 | width: 50
233 | text: qsTr("1")
234 | font.pixelSize: 24
235 | verticalAlignment: Text.AlignVCenter
236 | horizontalAlignment: Text.AlignHCenter
237 | anchors.bottom: parent.bottom
238 | anchors.bottomMargin: 0
239 | anchors.top: parent.top
240 | anchors.topMargin: 0
241 | color: synthModel.envelopeLoopSegment & 0x01 ? "#ffffff" : "#000000"
242 | background: Rectangle {
243 | color: synthModel.envelopeLoopSegment & 0x01 ? "#0000FF" : "#808080"
244 | radius: 5
245 | }
246 |
247 | MouseArea {
248 | anchors.fill: parent
249 | onClicked: {
250 | var i=synthModel.envelopeLoopSegment;
251 |
252 | i=i ^ 0x01;
253 | synthModel.envelopeLoopSegment=i;
254 | }
255 | }
256 | }
257 | Label {
258 | id: labelSeg2
259 | width: 50
260 | text: qsTr("2")
261 | font.pixelSize: 24
262 | verticalAlignment: Text.AlignVCenter
263 | horizontalAlignment: Text.AlignHCenter
264 | anchors.bottom: parent.bottom
265 | anchors.bottomMargin: 0
266 | anchors.top: parent.top
267 | anchors.topMargin: 0
268 | color: synthModel.envelopeLoopSegment & 0x02 ? "#ffffff" : "#000000"
269 | background: Rectangle {
270 | color: synthModel.envelopeLoopSegment & 0x02 ? "#0000FF" : "#808080"
271 | radius: 5
272 | }
273 | MouseArea {
274 | anchors.fill: parent
275 | onClicked: {
276 | var i=synthModel.envelopeLoopSegment;
277 |
278 | i=i ^ 0x02;
279 | synthModel.envelopeLoopSegment=i;
280 | }
281 | }
282 | }
283 | Label {
284 | id: labelSeg3
285 | width: 50
286 | text: qsTr("3")
287 | font.pixelSize: 24
288 | verticalAlignment: Text.AlignVCenter
289 | horizontalAlignment: Text.AlignHCenter
290 | anchors.bottom: parent.bottom
291 | anchors.bottomMargin: 0
292 | anchors.top: parent.top
293 | anchors.topMargin: 0
294 | color: synthModel.envelopeLoopSegment & 0x04 ? "#ffffff" : "#000000"
295 | background: Rectangle {
296 | color: synthModel.envelopeLoopSegment & 0x04 ? "#0000FF" : "#808080"
297 | radius: 5
298 | }
299 | MouseArea {
300 | anchors.fill: parent
301 | onClicked: {
302 | var i=synthModel.envelopeLoopSegment;
303 |
304 | i=i ^ 0x04;
305 | synthModel.envelopeLoopSegment=i;
306 | }
307 | }
308 | }
309 | Label {
310 | id: labelSeg4
311 | width: 50
312 | text: qsTr("4")
313 | font.pixelSize: 24
314 | verticalAlignment: Text.AlignVCenter
315 | horizontalAlignment: Text.AlignHCenter
316 | anchors.bottom: parent.bottom
317 | anchors.bottomMargin: 0
318 | anchors.top: parent.top
319 | anchors.topMargin: 0
320 | color: synthModel.envelopeLoopSegment & 0x08 ? "#ffffff" : "#000000"
321 | background: Rectangle {
322 | color: synthModel.envelopeLoopSegment & 0x08 ? "#0000FF" : "#808080"
323 | radius: 5
324 | }
325 | MouseArea {
326 | anchors.fill: parent
327 | onClicked: {
328 | var i=synthModel.envelopeLoopSegment;
329 |
330 | i=i ^ 0x08;
331 | synthModel.envelopeLoopSegment=i;
332 | }
333 | }
334 | }
335 | Label {
336 | id: labelSeg5
337 | width: 50
338 | text: qsTr("5")
339 | font.pixelSize: 24
340 | verticalAlignment: Text.AlignVCenter
341 | horizontalAlignment: Text.AlignHCenter
342 | anchors.bottom: parent.bottom
343 | anchors.bottomMargin: 0
344 | anchors.top: parent.top
345 | anchors.topMargin: 0
346 | color: synthModel.envelopeLoopSegment & 0x10 ? "#ffffff" : "#000000"
347 | background: Rectangle {
348 | color: synthModel.envelopeLoopSegment & 0x10 ? "#0000FF" : "#808080"
349 | radius: 5
350 | }
351 | MouseArea {
352 | anchors.fill: parent
353 | onClicked: {
354 | var i=synthModel.envelopeLoopSegment;
355 |
356 | i=i ^ 0x10;
357 | synthModel.envelopeLoopSegment=i;
358 | }
359 | }
360 | }
361 | Label {
362 | id: labelSeg6
363 | width: 50
364 | text: qsTr("6")
365 | font.pixelSize: 24
366 | verticalAlignment: Text.AlignVCenter
367 | horizontalAlignment: Text.AlignHCenter
368 | anchors.bottom: parent.bottom
369 | anchors.bottomMargin: 0
370 | anchors.top: parent.top
371 | anchors.topMargin: 0
372 | color: synthModel.envelopeLoopSegment & 0x20 ? "#ffffff" : "#000000"
373 | background: Rectangle {
374 | color: synthModel.envelopeLoopSegment & 0x20 ? "#0000FF" : "#808080"
375 | radius: 5
376 | }
377 | MouseArea {
378 | anchors.fill: parent
379 | onClicked: {
380 | var i=synthModel.envelopeLoopSegment;
381 |
382 | i=i ^ 0x20;
383 | synthModel.envelopeLoopSegment=i;
384 | }
385 | }
386 | }
387 | }
388 |
389 |
390 | Rectangle {
391 | color: "#000000"
392 | x: 16
393 | width: 768
394 | height: 128
395 | anchors.top: parent.top
396 | anchors.topMargin: 0
397 | }
398 |
399 | Canvas {
400 | id: canvas;
401 | x: 16
402 | width: 768
403 | height: 128
404 | contextType: qsTr("2d")
405 | anchors.top: parent.top
406 | anchors.topMargin: 0
407 | property int grabRadius: 20
408 |
409 | onPaint: {
410 | var ctx = getContext("2d")
411 |
412 | ctx.fillStyle = "black"
413 | ctx.fillRect(0, 0, canvas.width, canvas.height)
414 |
415 | var egL0=synthModel.pitchEG_L0/2;
416 | var egL1=synthModel.pitchEG_L1/2;
417 | var egL2=synthModel.pitchEG_L2/2;
418 | var egL3=synthModel.pitchEG_L3/2;
419 | var egL4=synthModel.pitchEG_L4/2;
420 | var egL5=synthModel.pitchEG_L5/2;
421 | var egR0=synthModel.pitchEG_R0/2;
422 | var egR1=synthModel.pitchEG_R1/2;
423 | var egR2=synthModel.pitchEG_R2/2;
424 | var egR3=synthModel.pitchEG_R3/2;
425 | var egR4=synthModel.pitchEG_R4/2;
426 | var egR5=synthModel.pitchEG_R5/2;
427 |
428 | if (egL0 > 127) egL0=127;
429 | if (egL1 > 127) egL1=127;
430 | if (egL2 > 127) egL2=127;
431 | if (egL3 > 127) egL3=127;
432 | if (egL4 > 127) egL4=127;
433 | if (egL5 > 127) egL5=127;
434 | if (egR0 > 127) egR0=127;
435 | if (egR1 > 127) egR1=127;
436 | if (egR2 > 127) egR2=127;
437 | if (egR3 > 127) egR3=127;
438 | if (egR4 > 127) egR4=127;
439 | if (egR5 > 127) egR5=127;
440 |
441 | ctx.lineWidth=1;
442 | ctx.strokeStyle="lightgray";
443 | ctx.beginPath();
444 | ctx.moveTo(egR0, 0);
445 | ctx.lineTo(egR0, canvas.height-1);
446 | ctx.moveTo(egR0+egR1, 0);
447 | ctx.lineTo(egR0+egR1, canvas.height-1);
448 | ctx.moveTo(egR0+egR1+egR2, 0);
449 | ctx.lineTo(egR0+egR1+egR2, canvas.height-1);
450 | ctx.moveTo(egR0+egR1+egR2+egR3, 0);
451 | ctx.lineTo(egR0+egR1+egR2+egR3, canvas.height-1);
452 | ctx.moveTo(egR0+egR1+egR2+egR3+egR4, 0);
453 | ctx.lineTo(egR0+egR1+egR2+egR3+egR4, canvas.height-1);
454 | ctx.moveTo(egR0+egR1+egR2+egR3+egR4+egR5, 0);
455 | ctx.lineTo(egR0+egR1+egR2+egR3+egR4+egR5, canvas.height-1);
456 | ctx.stroke();
457 |
458 | ctx.lineWidth = 5;
459 | ctx.strokeStyle = "yellow"
460 | ctx.beginPath()
461 | ctx.moveTo(0, canvas.height-1);
462 | ctx.lineTo(egR0, canvas.height-egL0-1);
463 | ctx.lineTo(egR0+egR1, canvas.height-egL1-1);
464 | ctx.lineTo(egR0+egR1+egR2, canvas.height-egL2-1);
465 | ctx.lineTo(egR0+egR1+egR2+egR3, canvas.height-egL3-1);
466 | ctx.lineTo(egR0+egR1+egR2+egR3+egR4, canvas.height-egL4-1);
467 | ctx.lineTo(egR0+egR1+egR2+egR3+egR4+egR5, canvas.height-egL5-1);
468 | ctx.lineTo(canvas.width-1, canvas.height-egL5-1);
469 | //ctx.closePath()
470 | ctx.stroke()
471 |
472 | ctx.fillStyle="yellow"
473 | ctx.fillRect(egR0-5, canvas.height-egL0-5-1, 10, 10);
474 | ctx.fillRect(egR0+egR1-5, canvas.height-egL1-5-1, 10, 10);
475 | ctx.fillRect(egR0+egR1+egR2-5, canvas.height-egL2-5-1, 10, 10);
476 | ctx.fillRect(egR0+egR1+egR2+egR3-5, canvas.height-egL3-5-1, 10, 10);
477 | ctx.fillRect(egR0+egR1+egR2+egR3+egR4-5, canvas.height-egL4-5-1, 10, 10);
478 | ctx.fillRect(egR0+egR1+egR2+egR3+egR4+egR5-5, canvas.height-egL5-5-1, 10, 10);
479 |
480 | ctx.fillStyle = "white";
481 | ctx.font = "16px monospace";
482 | ctx.fillText("L0: "+egL0.toFixed(0), canvas.width-160, 16);
483 | ctx.fillText("L1: "+egL1.toFixed(0), canvas.width-160, 32);
484 | ctx.fillText("L2: "+egL2.toFixed(0), canvas.width-160, 48);
485 | ctx.fillText("L3: "+egL3.toFixed(0), canvas.width-160, 64);
486 | ctx.fillText("L4: "+egL4.toFixed(0), canvas.width-160, 80);
487 | ctx.fillText("L5: "+egL5.toFixed(0), canvas.width-160, 96);
488 |
489 | ctx.fillText("R0: "+egR0.toFixed(0), canvas.width-80, 16);
490 | ctx.fillText("R1: "+egR1.toFixed(0), canvas.width-80, 32);
491 | ctx.fillText("R2: "+egR2.toFixed(0), canvas.width-80, 48);
492 | ctx.fillText("R3: "+egR3.toFixed(0), canvas.width-80, 64);
493 | ctx.fillText("R4: "+egR4.toFixed(0), canvas.width-80, 80);
494 | ctx.fillText("R5: "+egR5.toFixed(0), canvas.width-80, 96);
495 | }
496 |
497 | MouseArea {
498 | id: mouseArea;
499 | anchors.fill: parent
500 | property int updatingPoint: -1
501 | property int deltaX: 0
502 | property int deltaY: 0
503 |
504 | onPressed: {
505 | var x=mouseArea.mouseX
506 | var y=mouseArea.mouseY
507 |
508 | var egL0=synthModel.pitchEG_L0/2;
509 | var egL1=synthModel.pitchEG_L1/2;
510 | var egL2=synthModel.pitchEG_L2/2;
511 | var egL3=synthModel.pitchEG_L3/2;
512 | var egL4=synthModel.pitchEG_L4/2;
513 | var egL5=synthModel.pitchEG_L5/2;
514 | var egR0=synthModel.pitchEG_R0/2;
515 | var egR1=synthModel.pitchEG_R1/2;
516 | var egR2=synthModel.pitchEG_R2/2;
517 | var egR3=synthModel.pitchEG_R3/2;
518 | var egR4=synthModel.pitchEG_R4/2;
519 | var egR5=synthModel.pitchEG_R5/2;
520 |
521 | if (x >= egR0-canvas.grabRadius && x <= egR0+canvas.grabRadius && y >= canvas.height-egL0-canvas.grabRadius-1 && y <= canvas.height-egL0+canvas.grabRadius-1) {
522 | console.debug("R0,L0 clicked")
523 | updatingPoint=0;
524 | deltaX=x-(egR0-canvas.grabRadius);
525 | deltaY=y-(canvas.height-egL0-canvas.grabRadius-1);
526 | } else if (x >= egR0+egR1-canvas.grabRadius && x <= egR0+egR1+canvas.grabRadius && y >= canvas.height-egL1-canvas.grabRadius-1 && y <= canvas.height-egL1+canvas.grabRadius-1) {
527 | console.debug("R1,L1 clicked")
528 | updatingPoint=1;
529 | deltaX=x-(egR0+egR1-canvas.grabRadius);
530 | deltaY=y-(canvas.height-egL1-canvas.grabRadius-1);
531 | } else if (x >= egR0+egR1+egR2-canvas.grabRadius && x <= egR0+egR1+egR2+canvas.grabRadius && y >= canvas.height-egL2-canvas.grabRadius-1 && y <= canvas.height-egL2+canvas.grabRadius-1) {
532 | console.debug("R2,L2 clicked")
533 | updatingPoint=2;
534 | deltaX=x-(egR0+egR1+egR2-canvas.grabRadius);
535 | deltaY=y-(canvas.height-egL2-canvas.grabRadius-1);
536 | } else if (x >= egR0+egR1+egR2+egR3-canvas.grabRadius && x <= egR0+egR1+egR2+egR3+canvas.grabRadius && y >= canvas.height-egL3-canvas.grabRadius-1 && y <= canvas.height-egL3+canvas.grabRadius-1) {
537 | console.debug("R3,L3 clicked")
538 | updatingPoint=3;
539 | deltaX=x-(egR0+egR1+egR2+egR3-canvas.grabRadius);
540 | deltaY=y-(canvas.height-egL3-canvas.grabRadius-1);
541 | } else if (x >= egR0+egR1+egR2+egR3+egR4-canvas.grabRadius && x <= egR0+egR1+egR2+egR3+egR4+canvas.grabRadius && y >= canvas.height-egL4-canvas.grabRadius-1 && y <= canvas.height-egL4+canvas.grabRadius-1) {
542 | console.debug("R4,L4 clicked")
543 | updatingPoint=4;
544 | deltaX=x-(egR0+egR1+egR2+egR3+egR4-canvas.grabRadius);
545 | deltaY=y-(canvas.height-egL4-canvas.grabRadius-1);
546 | } else if (x >= egR0+egR1+egR2+egR3+egR4+egR5-canvas.grabRadius && x <= egR0+egR1+egR2+egR3+egR4+egR5+canvas.grabRadius && y >= canvas.height-egL5-canvas.grabRadius-1 && y <= canvas.height-egL5+canvas.grabRadius-1) {
547 | console.debug("R5,L5 clicked")
548 | updatingPoint=5;
549 | deltaX=x-(egR0+egR1+egR2+egR3+egR4+egR5-canvas.grabRadius);
550 | deltaY=y-(canvas.height-egL5-canvas.grabRadius-1);
551 | }
552 | }
553 |
554 | onPositionChanged: {
555 | var x=mouseArea.mouseX-deltaX
556 | var y=mouseArea.mouseY-deltaY
557 | var rate;
558 | var level;
559 |
560 | if (updatingPoint == 0) {
561 | rate=x;
562 |
563 | if (rate < 0) {
564 | rate=0;
565 | } else if (rate > 127) {
566 | rate=127;
567 | }
568 |
569 | level=canvas.height-y-1;
570 |
571 | if (level < 0) {
572 | level=0;
573 | } else if (level > 127) {
574 | level=127;
575 | }
576 |
577 | synthModel.pitchEG_L0=level*2;
578 | synthModel.pitchEG_R0=rate*2;
579 | canvas.requestPaint();
580 | } else if (updatingPoint == 1) {
581 | rate=x-synthModel.pitchEG_R0/2;
582 |
583 | if (rate < 0) {
584 | rate=0;
585 | } else if (rate > 127) {
586 | rate=127;
587 | }
588 |
589 | level=canvas.height-y-1;
590 |
591 | if (level < 0) {
592 | level=0;
593 | } else if (level > 127) {
594 | level=127;
595 | }
596 |
597 | synthModel.pitchEG_L1=level*2;
598 | synthModel.pitchEG_R1=rate*2;
599 | canvas.requestPaint();
600 | } else if (updatingPoint == 2) {
601 | rate=x-(synthModel.pitchEG_R0+synthModel.pitchEG_R1)/2;
602 |
603 | if (rate < 0) {
604 | rate=0;
605 | } else if (rate > 127) {
606 | rate=127;
607 | }
608 |
609 | level=canvas.height-y-1;
610 |
611 | if (level < 0) {
612 | level=0;
613 | } else if (level > 127) {
614 | level=127;
615 | }
616 |
617 | synthModel.pitchEG_L2=level*2;
618 | synthModel.pitchEG_R2=rate*2;
619 | canvas.requestPaint();
620 | } else if (updatingPoint == 3) {
621 | rate=x-(synthModel.pitchEG_R0+synthModel.pitchEG_R1+synthModel.pitchEG_R2)/2;
622 |
623 | if (rate < 0) {
624 | rate=0;
625 | } else if (rate > 127) {
626 | rate=127;
627 | }
628 |
629 | level=canvas.height-y-1;
630 |
631 | if (level < 0) {
632 | level=0;
633 | } else if (level > 127) {
634 | level=127;
635 | }
636 |
637 | synthModel.pitchEG_L3=level*2;
638 | synthModel.pitchEG_R3=rate*2;
639 | canvas.requestPaint();
640 | } else if (updatingPoint == 4) {
641 | rate=x-(synthModel.pitchEG_R0+synthModel.pitchEG_R1+synthModel.pitchEG_R2+synthModel.pitchEG_R3)/2;
642 |
643 | if (rate < 0) {
644 | rate=0;
645 | } else if (rate > 127) {
646 | rate=127;
647 | }
648 |
649 | level=canvas.height-y-1;
650 |
651 | if (level < 0) {
652 | level=0;
653 | } else if (level > 127) {
654 | level=127;
655 | }
656 |
657 | synthModel.pitchEG_L4=level*2;
658 | synthModel.pitchEG_R4=rate*2;
659 | canvas.requestPaint();
660 | } else if (updatingPoint == 5) {
661 | rate=x-(synthModel.pitchEG_R0+synthModel.pitchEG_R1+synthModel.pitchEG_R2+synthModel.pitchEG_R3+synthModel.pitchEG_R4)/2;
662 |
663 | if (rate < 0) {
664 | rate=0;
665 | } else if (rate > 127) {
666 | rate=127;
667 | }
668 |
669 | level=canvas.height-y-1;
670 |
671 | if (level < 0) {
672 | level=0;
673 | } else if (level > 127) {
674 | level=127;
675 | }
676 |
677 | synthModel.pitchEG_L5=level*2;
678 | synthModel.pitchEG_R5=rate*2;
679 | canvas.requestPaint();
680 | }
681 | }
682 |
683 | onReleased: {
684 | updatingPoint=-1;
685 | }
686 |
687 | }
688 | }
689 |
690 | CustomDial {
691 | id: dialRange
692 | x: 16
693 | y: 139
694 | width: 121
695 | height: 80
696 | maximumValue: 127
697 | stepSize: 1
698 | onValueChanged: {
699 | synthModel.pitchEG_Range=dialRange.value;
700 | }
701 | }
702 |
703 | Label {
704 | id: label
705 | x: 55
706 | y: 225
707 | color: "#ffffff"
708 | text: qsTr("Range")
709 | font.pixelSize: 16
710 | }
711 |
712 | CustomDial {
713 | id: dialVelocity
714 | x: 143
715 | y: 139
716 | width: 121
717 | height: 80
718 | maximumValue: 255
719 | stepSize: 1
720 | onValueChanged: {
721 | synthModel.pitchEG_Velocity=dialVelocity.value;
722 | }
723 | }
724 |
725 | Label {
726 | id: label1
727 | x: 176
728 | y: 225
729 | color: "#ffffff"
730 | text: qsTr("Velocity")
731 | font.pixelSize: 16
732 | }
733 |
734 | CustomDial {
735 | id: dialRateKey
736 | x: 16
737 | y: 248
738 | width: 121
739 | height: 80
740 | maximumValue: 255
741 | stepSize: 1
742 | onValueChanged: {
743 | synthModel.pitchEG_RateKey=dialRateKey.value;
744 | }
745 | }
746 |
747 | Label {
748 | id: label2
749 | x: 49
750 | y: 334
751 | color: "#ffffff"
752 | text: qsTr("Rate Key")
753 | font.pixelSize: 16
754 | }
755 |
756 | Label {
757 | id: label3
758 | x: 330
759 | y: 171
760 | color: "#ffffff"
761 | text: qsTr("Envelope Loop")
762 | font.pixelSize: 16
763 | }
764 |
765 | Label {
766 | id: label4
767 | x: 304
768 | y: 240
769 | color: "#ffffff"
770 | text: qsTr("Env Loop Segment")
771 | font.pixelSize: 16
772 | }
773 |
774 | Component.onCompleted: {
775 | updatePage();
776 | }
777 |
778 | }
779 |
780 | /*##^##
781 | Designer {
782 | D{i:1;anchors_height:400;anchors_x:0;anchors_y:0}
783 | }
784 | ##^##*/
785 |
--------------------------------------------------------------------------------