├── LICENSE
├── Part-10
└── MOXF
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── de
│ │ └── mossgrabers
│ │ ├── MOXFExtensionExtension.java
│ │ ├── MOXFExtensionExtensionDefinition.java
│ │ ├── MOXFHardware.java
│ │ └── handler
│ │ ├── Mode.java
│ │ ├── ModeHandler.java
│ │ ├── RemoteControlHandler.java
│ │ ├── TrackHandler.java
│ │ └── TransportHandler.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-11
├── OSCExample
│ └── OSCExample.control.js
└── OSCTest.maxpat
├── Part-12
└── GraphicsTest
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── de
│ │ └── mossgrabers
│ │ ├── GraphicsTestExtension.java
│ │ └── GraphicsTestExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-13
└── Arpeggiator.control.js
├── Part-14
└── HardwareAPITest1.control.js
├── Part-15
├── HardwareAPITest2.control.js
└── config.json
├── Part-16
└── HardwareAPITest3
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── com
│ │ └── moss
│ │ ├── HardwareAPITest3Extension.java
│ │ └── HardwareAPITest3ExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-17
└── HardwareAPITest4
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── com
│ │ └── moss
│ │ ├── HardwareAPITest4Extension.java
│ │ └── HardwareAPITest4ExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-19
└── API12-Test
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── de
│ │ └── mossggraber
│ │ └── apitwelve
│ │ ├── API12TestExtension.java
│ │ └── API12TestExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-2
└── MOXF.control.js
├── Part-22
└── API16-Demo.control.js
├── Part-24
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mossgraber
│ │ ├── API20TestExtension.java
│ │ └── API20TestExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-25
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── de
│ │ └── mossgrabers
│ │ └── remote
│ │ ├── RemoteCursorTestExtension.java
│ │ └── RemoteCursorTestExtensionDefinition.java
│ └── resources
│ └── META-INF
│ └── services
│ └── com.bitwig.extension.ExtensionDefinition
├── Part-3
├── MOXF.control.js
└── OutputExamples.js
├── Part-4
└── MOXF.control.js
├── Part-5
├── MOXF.control.js
└── MOXFHardware.js
├── Part-6
├── MOXF.control.js
├── MOXFHardware.js
├── TrackHandler.js
└── TransportHandler.js
├── Part-7
├── MOXF.control.js
├── MOXFHardware.js
├── ModeHandler.js
├── RemoteControlHandler.js
├── TrackHandler.js
└── TransportHandler.js
├── Part-8
├── MOXF.control.js
├── MOXFHardware.js
├── ModeHandler.js
├── RemoteControlHandler.js
├── TrackHandler.js
└── TransportHandler.js
└── README.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2025 Jürgen Moßgraber
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Part-10/MOXF/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | de.mossgrabers
6 | MOXFExtension
7 | jar
8 | MOXF
9 | 0.1
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 7
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.5.1
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${bitwig.extension.directory}/MOXFExtension.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/MOXFExtensionExtension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers;
24 |
25 | import de.mossgrabers.handler.Mode;
26 | import de.mossgrabers.handler.ModeHandler;
27 | import de.mossgrabers.handler.RemoteControlHandler;
28 | import de.mossgrabers.handler.TrackHandler;
29 | import de.mossgrabers.handler.TransportHandler;
30 |
31 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
32 | import com.bitwig.extension.controller.ControllerExtension;
33 | import com.bitwig.extension.controller.api.ControllerHost;
34 | import com.bitwig.extension.controller.api.CursorDeviceFollowMode;
35 | import com.bitwig.extension.controller.api.CursorTrack;
36 | import com.bitwig.extension.controller.api.DocumentState;
37 | import com.bitwig.extension.controller.api.PinnableCursorDevice;
38 | import com.bitwig.extension.controller.api.Preferences;
39 | import com.bitwig.extension.controller.api.SettableEnumValue;
40 | import com.bitwig.extension.controller.api.SettableRangedValue;
41 |
42 |
43 | /**
44 | * Bitwig extension for the Yamaha MOXF.
45 | *
46 | * @author Jürgen Moßgraber
47 | */
48 | public class MOXFExtensionExtension extends ControllerExtension
49 | {
50 | private TransportHandler transportHandler;
51 | private ModeHandler modeHandler;
52 |
53 |
54 | /**
55 | * Constructor.
56 | *
57 | * @param definition The definition object
58 | * @param host The controller host
59 | */
60 | protected MOXFExtensionExtension (final MOXFExtensionExtensionDefinition definition, final ControllerHost host)
61 | {
62 | super (definition, host);
63 | }
64 |
65 |
66 | /** {@inheritDoc} */
67 | @Override
68 | public void init ()
69 | {
70 | final ControllerHost host = this.getHost ();
71 |
72 | // Preferences
73 | final Preferences preferences = host.getPreferences ();
74 | final SettableEnumValue modeSetting = preferences.getEnumSetting ("Mode", "Global", ModeHandler.MODE_OPTIONS, ModeHandler.MODE_OPTIONS[0]);
75 |
76 | // Document States
77 | final DocumentState documentState = host.getDocumentState ();
78 | final SettableEnumValue fixVelocityEnableSetting = documentState.getEnumSetting ("Enable", "Fix velocity", MOXFHardware.BOOLEAN_OPTIONS, MOXFHardware.BOOLEAN_OPTIONS[0]);
79 | final SettableRangedValue fixVelocityValueSetting = documentState.getNumberSetting ("Velocity", "Fix velocity", 0, 127, 1, "", 127);
80 |
81 | final MOXFHardware hardware = new MOXFHardware (host.getMidiOutPort (0), host.getMidiInPort (0), this::handleMidi, fixVelocityEnableSetting, fixVelocityValueSetting);
82 | this.transportHandler = new TransportHandler (host.createTransport (), hardware);
83 |
84 | final CursorTrack cursorTrack = host.createCursorTrack ("MOXF_CURSOR_TRACK", "Cursor Track", 0, 0, true);
85 | final TrackHandler trackHandler = new TrackHandler (host.createMainTrackBank (4, 0, 0), cursorTrack);
86 |
87 | final PinnableCursorDevice cursorDevice = cursorTrack.createCursorDevice ("MOXF_CURSOR_DEVICE", "Cursor Device", 0, CursorDeviceFollowMode.FOLLOW_SELECTION);
88 | final RemoteControlHandler remoteControlHandler = new RemoteControlHandler (cursorDevice, cursorDevice.createCursorRemoteControlsPage (8));
89 |
90 | final Mode [] modes = new Mode []
91 | {
92 | trackHandler,
93 | remoteControlHandler
94 | };
95 | this.modeHandler = new ModeHandler (modes, modeSetting, host);
96 |
97 | host.println ("MOXF initialized!");
98 | }
99 |
100 |
101 | /**
102 | * Callback for receiving MIDI data.
103 | *
104 | * @param statusByte The status byte
105 | * @param data1 The data1 byte
106 | * @param data2 The data2 byte
107 | */
108 | public void handleMidi (final int statusByte, final int data1, final int data2)
109 | {
110 | final ShortMidiMessage msg = new ShortMidiMessage (statusByte, data1, data2);
111 |
112 | if (this.transportHandler.handleMidi (msg))
113 | return;
114 |
115 | if (this.modeHandler.handleMidi (msg))
116 | return;
117 |
118 | this.getHost ().errorln ("Midi command not processed: " + msg.getStatusByte () + " : " + msg.getData1 ());
119 | }
120 |
121 |
122 | /** {@inheritDoc} */
123 | @Override
124 | public void exit ()
125 | {
126 | this.getHost ().println ("MOXF Exited.");
127 | }
128 |
129 |
130 | /** {@inheritDoc} */
131 | @Override
132 | public void flush ()
133 | {
134 | this.transportHandler.updateLEDs ();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/MOXFExtensionExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers;
24 |
25 | import com.bitwig.extension.api.PlatformType;
26 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
27 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 |
30 | import java.util.UUID;
31 |
32 |
33 | /**
34 | * The description of the extension.
35 | *
36 | * @author Jürgen Moßgraber
37 | */
38 | public class MOXFExtensionExtensionDefinition extends ControllerExtensionDefinition
39 | {
40 | private static final UUID DRIVER_ID = UUID.fromString ("2f4727a3-9e7f-4908-8c59-6c2759e577c0");
41 |
42 |
43 | /**
44 | * Constructor.
45 | */
46 | public MOXFExtensionExtensionDefinition ()
47 | {
48 | // Intentionally empty
49 | }
50 |
51 |
52 | /** {@inheritDoc} */
53 | @Override
54 | public String getName ()
55 | {
56 | return "MOXF";
57 | }
58 |
59 |
60 | /** {@inheritDoc} */
61 | @Override
62 | public String getAuthor ()
63 | {
64 | return "Jürgen Moßgraber";
65 | }
66 |
67 |
68 | /** {@inheritDoc} */
69 | @Override
70 | public String getVersion ()
71 | {
72 | return "0.1";
73 | }
74 |
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public UUID getId ()
79 | {
80 | return DRIVER_ID;
81 | }
82 |
83 |
84 | /** {@inheritDoc} */
85 | @Override
86 | public String getHardwareVendor ()
87 | {
88 | return "Yamaha";
89 | }
90 |
91 |
92 | /** {@inheritDoc} */
93 | @Override
94 | public String getHardwareModel ()
95 | {
96 | return "MOXF (Java)";
97 | }
98 |
99 |
100 | /** {@inheritDoc} */
101 | @Override
102 | public int getRequiredAPIVersion ()
103 | {
104 | return 7;
105 | }
106 |
107 |
108 | /** {@inheritDoc} */
109 | @Override
110 | public int getNumMidiInPorts ()
111 | {
112 | return 1;
113 | }
114 |
115 |
116 | /** {@inheritDoc} */
117 | @Override
118 | public int getNumMidiOutPorts ()
119 | {
120 | return 1;
121 | }
122 |
123 |
124 | /** {@inheritDoc} */
125 | @Override
126 | public void listAutoDetectionMidiPortNames (final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
127 | {
128 | // Note: Only an example on my system. If you use the MOXF with USB replace these with tzhe
129 | // correct names
130 |
131 | list.add (new String []
132 | {
133 | "MIDIIN4 (mio10)"
134 | }, new String []
135 | {
136 | "MIDIOUT4 (mio10)"
137 | });
138 | }
139 |
140 |
141 | /** {@inheritDoc} */
142 | @Override
143 | public MOXFExtensionExtension createInstance (final ControllerHost host)
144 | {
145 | return new MOXFExtensionExtension (this, host);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/MOXFHardware.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers;
24 |
25 | import com.bitwig.extension.callback.ShortMidiDataReceivedCallback;
26 | import com.bitwig.extension.controller.api.MidiIn;
27 | import com.bitwig.extension.controller.api.MidiOut;
28 | import com.bitwig.extension.controller.api.NoteInput;
29 | import com.bitwig.extension.controller.api.SettableEnumValue;
30 | import com.bitwig.extension.controller.api.SettableRangedValue;
31 |
32 | import java.util.Arrays;
33 |
34 |
35 | /**
36 | * Encapsulates the button and knob IDs of the MOXF and the MIDI connection.
37 | *
38 | * @author Jürgen Moßgraber
39 | */
40 | public class MOXFHardware
41 | {
42 | /** The Solo button. */
43 | public static final int MOXF_BUTTON_SOLO = 0x08;
44 | /** The Mute button. */
45 | public static final int MOXF_BUTTON_MUTE = 0x10;
46 |
47 | /** The function button 1. */
48 | public static final int MOXF_BUTTON_F1 = 0x36;
49 | /** The function button 2. */
50 | public static final int MOXF_BUTTON_F2 = 0x37;
51 | /** The function button 3. */
52 | public static final int MOXF_BUTTON_F3 = 0x38;
53 | /** The function button 4. */
54 | public static final int MOXF_BUTTON_F4 = 0x39;
55 | /** The function button 5. */
56 | public static final int MOXF_BUTTON_F5 = 0x3A;
57 | /** The function button 6. */
58 | public static final int MOXF_BUTTON_F6 = 0x3B;
59 |
60 | /** The select button 1. */
61 | public static final int MOXF_BUTTON_SF1 = 0x3C;
62 | /** The select button 2. */
63 | public static final int MOXF_BUTTON_SF2 = 0x3D;
64 | /** The select button 3. */
65 | public static final int MOXF_BUTTON_SF3 = 0x3E;
66 | /** The select button 4. */
67 | public static final int MOXF_BUTTON_SF4 = 0x3F;
68 | /** The select button 5. */
69 | public static final int MOXF_BUTTON_SF5 = 0x40;
70 | /** The select button 6. */
71 | public static final int MOXF_BUTTON_SF6 = 0x41;
72 |
73 | /** The button A. */
74 | public static final int MOXF_BUTTON_A = 0x42;
75 | /** The button B. */
76 | public static final int MOXF_BUTTON_B = 0x43;
77 | /** The button C. */
78 | public static final int MOXF_BUTTON_C = 0x44;
79 | /** The button D. */
80 | public static final int MOXF_BUTTON_D = 0x45;
81 | /** The button E. */
82 | public static final int MOXF_BUTTON_E = 0x46;
83 | /** The button F. */
84 | public static final int MOXF_BUTTON_F = 0x47;
85 |
86 | /** The Play button. */
87 | public static final int MOXF_BUTTON_PLAY = 0x5E;
88 | /** The Stop button. */
89 | public static final int MOXF_BUTTON_STOP = 0x5D;
90 | /** The Record button. */
91 | public static final int MOXF_BUTTON_REC = 0x5F;
92 | /** The Forward button. */
93 | public static final int MOXF_BUTTON_FWD = 0x5C;
94 | /** The Backwards button. */
95 | public static final int MOXF_BUTTON_BACK = 0x5B;
96 | /** The Locate button. */
97 | public static final int MOXF_BUTTON_LOCATE = 0x58;
98 |
99 | /** The Category button. */
100 | public static final int MOXF_BUTTON_CATEGORY = 0x76;
101 | /** The Favorite button. */
102 | public static final int MOXF_BUTTON_FAVORITE = 0x77;
103 |
104 | /** The main knob. */
105 | public static final int MOXF_KNOB_MAIN = 0x3C;
106 | /** The knob 1. */
107 | public static final int MOXF_KNOB_1 = 0x4A;
108 | /** The knob 2. */
109 | public static final int MOXF_KNOB_2 = 0x47;
110 | /** The knob 3. */
111 | public static final int MOXF_KNOB_3 = 0x49;
112 | /** The knob 4. */
113 | public static final int MOXF_KNOB_4 = 0x48;
114 | /** The knob 5. */
115 | public static final int MOXF_KNOB_5 = 0x1C;
116 | /** The knob 6. */
117 | public static final int MOXF_KNOB_6 = 0x1D;
118 | /** The knob 7. */
119 | public static final int MOXF_KNOB_7 = 0x1E;
120 | /** The knob 8. */
121 | public static final int MOXF_KNOB_8 = 0x1F;
122 |
123 | /** The options for turning on/off the fixed velocity option. */
124 | public static final String [] BOOLEAN_OPTIONS =
125 | {
126 | "Off",
127 | "On"
128 | };
129 |
130 | private final MidiOut portOut;
131 | private final MidiIn portIn;
132 | private final int [] ledCache = new int [128];
133 | private final NoteInput noteIn;
134 | private final Integer [] velocityMapIdentity = new Integer [128];
135 | private final Integer [] velocityMapFixed = new Integer [128];
136 |
137 | private boolean isFixedVelocity;
138 |
139 |
140 | /**
141 | * Construcotr.
142 | *
143 | * @param outputPort The MIDI output port
144 | * @param inputPort The MIDI input port
145 | * @param inputCallback The MIDI input callback
146 | * @param fixVelocityEnableSetting The fixed velocity setting
147 | * @param fixVelocityValueSetting The value for the fixed velocity setting
148 | */
149 | public MOXFHardware (final MidiOut outputPort, final MidiIn inputPort, final ShortMidiDataReceivedCallback inputCallback, final SettableEnumValue fixVelocityEnableSetting, final SettableRangedValue fixVelocityValueSetting)
150 | {
151 | this.portOut = outputPort;
152 | this.portIn = inputPort;
153 |
154 | Arrays.fill (this.ledCache, -1);
155 |
156 | this.portIn.setMidiCallback (inputCallback);
157 | this.noteIn = this.portIn.createNoteInput ("Yamaha MOXF", "91????", "81????", "B101??", "B140??", "E1????");
158 |
159 | for (int i = 0; i < 128; i++)
160 | this.velocityMapIdentity[i] = Integer.valueOf (i);
161 |
162 | this.isFixedVelocity = false;
163 |
164 | fixVelocityEnableSetting.addValueObserver (this::setVelocityMap);
165 | fixVelocityValueSetting.addRawValueObserver (this::updateVelocityMap);
166 | }
167 |
168 |
169 | /**
170 | * Set the velocity map depending on the given setting value (callback).
171 | *
172 | * @param value The setting value from BOOLEAN_OPTIONS
173 | */
174 | public void setVelocityMap (final String value)
175 | {
176 | this.isFixedVelocity = value == BOOLEAN_OPTIONS[1];
177 | this.noteIn.setVelocityTranslationTable (this.isFixedVelocity ? this.velocityMapFixed : this.velocityMapIdentity);
178 | }
179 |
180 |
181 | /**
182 | * Update the fixed velocity map with the given value.
183 | *
184 | * @param value The new fixed setting value
185 | */
186 | public void updateVelocityMap (final Double value)
187 | {
188 | final Integer val = Integer.valueOf (value.intValue ());
189 | for (int i = 0; i < 128; i++)
190 | this.velocityMapFixed[i] = val;
191 | if (this.isFixedVelocity)
192 | this.setVelocityMap (BOOLEAN_OPTIONS[1]);
193 | }
194 |
195 |
196 | /**
197 | * Update an LED on the MOXF (only an example for how to do this but not working with the MOXF).
198 | *
199 | * @param note The note
200 | * @param isOn True to turn on
201 | */
202 | public void updateLED (final int note, final boolean isOn)
203 | {
204 | final int value = isOn ? 127 : 0;
205 | if (this.ledCache[note] == value)
206 | return;
207 | this.ledCache[note] = value;
208 | this.portOut.sendMidi (0x90, note, value);
209 | }
210 | }
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/handler/Mode.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.handler;
24 |
25 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
26 |
27 |
28 | /**
29 | * A mode allows to use the same controls on a device for different tasks depending on the active
30 | * mode.
31 | *
32 | * @author Jürgen Moßgraber
33 | */
34 | public interface Mode
35 | {
36 | /**
37 | * Get a descriptive name for the mode.
38 | *
39 | * @return The name
40 | */
41 | String getName ();
42 |
43 |
44 | /**
45 | * Set the indication for the controls in Bitwig.
46 | *
47 | * @param enable True to enable otherwise disable
48 | */
49 | void setIndication (boolean enable);
50 |
51 |
52 | /**
53 | * Handle/react to the midi commands of the buttons and knobs of the device.
54 | *
55 | * @param message The midi message
56 | * @return True if the midi message was processed in the method
57 | */
58 | boolean handleMidi (ShortMidiMessage message);
59 | }
60 |
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/handler/ModeHandler.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.handler;
24 |
25 | import de.mossgrabers.MOXFHardware;
26 |
27 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 | import com.bitwig.extension.controller.api.SettableEnumValue;
30 |
31 |
32 | /**
33 | * Handles all available modes.
34 | *
35 | * @author Jürgen Moßgraber
36 | */
37 | public class ModeHandler
38 | {
39 | /** Description texts for the available modes. */
40 | public static final String [] MODE_OPTIONS =
41 | {
42 | "Track",
43 | "Device"
44 | };
45 |
46 | private final Mode [] modes;
47 | private final SettableEnumValue modeSetting;
48 | private final ControllerHost host;
49 |
50 | private Mode activeMode;
51 |
52 |
53 | /**
54 | * Constructor.
55 | *
56 | * @param modes The avilable modes
57 | * @param modeSetting The setting to store the active mode
58 | * @param host The controller host
59 | */
60 | public ModeHandler (final Mode [] modes, final SettableEnumValue modeSetting, final ControllerHost host)
61 | {
62 | this.modes = modes;
63 | this.modeSetting = modeSetting;
64 | this.host = host;
65 |
66 | this.setActiveMode (modes[0]);
67 |
68 | this.modeSetting.addValueObserver (value -> {
69 | for (int i = 0; i < MODE_OPTIONS.length; i++)
70 | {
71 | if (MODE_OPTIONS[i] == value)
72 | this.setActiveMode (this.modes[i]);
73 | }
74 | });
75 | }
76 |
77 |
78 | /**
79 | * Set the active mode.
80 | *
81 | * @param newMode The new active mode to set
82 | */
83 | public void setActiveMode (final Mode newMode)
84 | {
85 | this.activeMode = newMode;
86 | this.host.showPopupNotification (this.activeMode.getName ());
87 | this.updateIndication ();
88 | }
89 |
90 |
91 | /**
92 | * Update the indications in Bitwig.
93 | */
94 | public void updateIndication ()
95 | {
96 | for (final Mode mode: this.modes)
97 | mode.setIndication (false);
98 | this.activeMode.setIndication (true);
99 | }
100 |
101 |
102 | /**
103 | * Handle/process the given MIDI message.
104 | *
105 | * @param message The message to process
106 | * @return True if the message was process by the method
107 | */
108 | public boolean handleMidi (final ShortMidiMessage message)
109 | {
110 | if (message.isNoteOn ())
111 | {
112 | switch (message.getData1 ())
113 | {
114 | case MOXFHardware.MOXF_BUTTON_A:
115 | this.setActiveMode (this.modes[0]);
116 | this.modeSetting.set (MODE_OPTIONS[0]);
117 | return true;
118 |
119 | case MOXFHardware.MOXF_BUTTON_B:
120 | this.setActiveMode (this.modes[1]);
121 | this.modeSetting.set (MODE_OPTIONS[1]);
122 | return true;
123 | }
124 | }
125 |
126 | if (this.activeMode.handleMidi (message))
127 | return true;
128 |
129 | return false;
130 | }
131 | }
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/handler/RemoteControlHandler.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.handler;
24 |
25 | import de.mossgrabers.MOXFHardware;
26 |
27 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
28 | import com.bitwig.extension.controller.api.CursorDevice;
29 | import com.bitwig.extension.controller.api.CursorRemoteControlsPage;
30 |
31 |
32 | /**
33 | * The remote control mode.
34 | *
35 | * @author Jürgen Moßgraber
36 | */
37 | public class RemoteControlHandler implements Mode
38 | {
39 | private final CursorDevice cursorDevice;
40 | private final CursorRemoteControlsPage remoteControlsBank;
41 |
42 |
43 | /**
44 | * Constructor.
45 | *
46 | * @param cursorDevice The cursor device
47 | * @param remoteControlsBank The remote controls bank
48 | */
49 | public RemoteControlHandler (final CursorDevice cursorDevice, final CursorRemoteControlsPage remoteControlsBank)
50 | {
51 | this.cursorDevice = cursorDevice;
52 | this.remoteControlsBank = remoteControlsBank;
53 |
54 | for (int i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
55 | this.remoteControlsBank.getParameter (i).markInterested ();
56 |
57 | this.cursorDevice.isEnabled ().markInterested ();
58 | this.cursorDevice.isWindowOpen ().markInterested ();
59 | }
60 |
61 |
62 | /** {@inheritDoc} */
63 | @Override
64 | public String getName ()
65 | {
66 | return "Device Mode";
67 | }
68 |
69 |
70 | /** {@inheritDoc} */
71 | @Override
72 | public void setIndication (final boolean enable)
73 | {
74 | for (int i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
75 | this.remoteControlsBank.getParameter (i).setIndication (enable);
76 | }
77 |
78 |
79 | /** {@inheritDoc} */
80 | @Override
81 | public boolean handleMidi (final ShortMidiMessage message)
82 | {
83 | if (message.isNoteOn ())
84 | {
85 | switch (message.getData1 ())
86 | {
87 | case MOXFHardware.MOXF_BUTTON_SF1:
88 | this.cursorDevice.selectPrevious ();
89 | return true;
90 |
91 | case MOXFHardware.MOXF_BUTTON_SF2:
92 | this.cursorDevice.selectNext ();
93 | return true;
94 |
95 | case MOXFHardware.MOXF_BUTTON_SF3:
96 | // Not used
97 | return true;
98 |
99 | case MOXFHardware.MOXF_BUTTON_SF4:
100 | // Not used
101 | return true;
102 |
103 | case MOXFHardware.MOXF_BUTTON_SF5:
104 | this.remoteControlsBank.selectPrevious ();
105 | return true;
106 |
107 | case MOXFHardware.MOXF_BUTTON_SF6:
108 | this.remoteControlsBank.selectNext ();
109 | return true;
110 |
111 | case MOXFHardware.MOXF_BUTTON_SOLO:
112 | this.cursorDevice.isWindowOpen ().toggle ();
113 | return true;
114 |
115 | case MOXFHardware.MOXF_BUTTON_MUTE:
116 | this.cursorDevice.isEnabled ().toggle ();
117 | return true;
118 |
119 | default:
120 | return false;
121 | }
122 | }
123 |
124 | if (message.isControlChange ())
125 | {
126 | final int data2 = message.getData2 ();
127 | final Integer value = Integer.valueOf (data2 > 64 ? 64 - data2 : data2);
128 |
129 | switch (message.getData1 ())
130 | {
131 | // Absolute values
132 | case MOXFHardware.MOXF_KNOB_1:
133 | this.remoteControlsBank.getParameter (0).set (Integer.valueOf (data2), Integer.valueOf (128));
134 | return true;
135 |
136 | case MOXFHardware.MOXF_KNOB_2:
137 | this.remoteControlsBank.getParameter (1).set (Integer.valueOf (data2), Integer.valueOf (128));
138 | return true;
139 |
140 | case MOXFHardware.MOXF_KNOB_3:
141 | this.remoteControlsBank.getParameter (2).set (Integer.valueOf (data2), Integer.valueOf (128));
142 | return true;
143 |
144 | case MOXFHardware.MOXF_KNOB_4:
145 | this.remoteControlsBank.getParameter (3).set (Integer.valueOf (data2), Integer.valueOf (128));
146 | return true;
147 |
148 | // Relative values
149 | case MOXFHardware.MOXF_KNOB_5:
150 | this.remoteControlsBank.getParameter (4).inc (value, Integer.valueOf (128));
151 | return true;
152 |
153 | case MOXFHardware.MOXF_KNOB_6:
154 | this.remoteControlsBank.getParameter (5).inc (value, Integer.valueOf (128));
155 | return true;
156 |
157 | case MOXFHardware.MOXF_KNOB_7:
158 | this.remoteControlsBank.getParameter (6).inc (value, Integer.valueOf (128));
159 | return true;
160 |
161 | case MOXFHardware.MOXF_KNOB_8:
162 | this.remoteControlsBank.getParameter (7).inc (value, Integer.valueOf (128));
163 | return true;
164 |
165 | default:
166 | return false;
167 | }
168 | }
169 |
170 | return false;
171 | }
172 | }
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/handler/TrackHandler.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.handler;
24 |
25 | import de.mossgrabers.MOXFHardware;
26 |
27 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
28 | import com.bitwig.extension.controller.api.CursorTrack;
29 | import com.bitwig.extension.controller.api.Track;
30 | import com.bitwig.extension.controller.api.TrackBank;
31 |
32 |
33 | /**
34 | * The track control mode.
35 | *
36 | * @author Jürgen Moßgraber
37 | */
38 | public class TrackHandler implements Mode
39 | {
40 | private final TrackBank trackbank;
41 | private final CursorTrack cursorTrack;
42 |
43 |
44 | /**
45 | * Constructor.
46 | *
47 | * @param trackbank The trackbank
48 | * @param cursorTrack The cursor track
49 | */
50 | public TrackHandler (final TrackBank trackbank, final CursorTrack cursorTrack)
51 | {
52 | this.trackbank = trackbank;
53 | this.cursorTrack = cursorTrack;
54 |
55 | this.trackbank.followCursorTrack (this.cursorTrack);
56 |
57 | for (int i = 0; i < this.trackbank.getSizeOfBank (); i++)
58 | {
59 | final Track track = this.trackbank.getItemAt (i);
60 | track.pan ().markInterested ();
61 | track.volume ().markInterested ();
62 | }
63 |
64 | this.cursorTrack.solo ().markInterested ();
65 | this.cursorTrack.mute ().markInterested ();
66 | }
67 |
68 |
69 | /** {@inheritDoc} */
70 | @Override
71 | public String getName ()
72 | {
73 | return "Track Mode";
74 | }
75 |
76 |
77 | /** {@inheritDoc} */
78 | @Override
79 | public void setIndication (final boolean enable)
80 | {
81 | for (int i = 0; i < this.trackbank.getSizeOfBank (); i++)
82 | {
83 | final Track track = this.trackbank.getItemAt (i);
84 | track.pan ().setIndication (enable);
85 | track.volume ().setIndication (enable);
86 | }
87 | }
88 |
89 |
90 | /** {@inheritDoc} */
91 | @Override
92 | public boolean handleMidi (final ShortMidiMessage message)
93 | {
94 | if (message.isNoteOn ())
95 | {
96 | switch (message.getData1 ())
97 | {
98 | case MOXFHardware.MOXF_BUTTON_SF1:
99 | this.trackbank.getItemAt (0).selectInMixer ();
100 | return true;
101 |
102 | case MOXFHardware.MOXF_BUTTON_SF2:
103 | this.trackbank.getItemAt (1).selectInMixer ();
104 | return true;
105 |
106 | case MOXFHardware.MOXF_BUTTON_SF3:
107 | this.trackbank.getItemAt (2).selectInMixer ();
108 | return true;
109 |
110 | case MOXFHardware.MOXF_BUTTON_SF4:
111 | this.trackbank.getItemAt (3).selectInMixer ();
112 | return true;
113 |
114 | case MOXFHardware.MOXF_BUTTON_SF5:
115 | this.trackbank.scrollPageBackwards ();
116 | return true;
117 |
118 | case MOXFHardware.MOXF_BUTTON_SF6:
119 | this.trackbank.scrollPageForwards ();
120 | return true;
121 |
122 | case MOXFHardware.MOXF_BUTTON_SOLO:
123 | this.cursorTrack.solo ().toggle ();
124 | return true;
125 |
126 | case MOXFHardware.MOXF_BUTTON_MUTE:
127 | this.cursorTrack.mute ().toggle ();
128 | return true;
129 |
130 | default:
131 | return false;
132 | }
133 | }
134 |
135 | if (message.isControlChange ())
136 | {
137 | final int data2 = message.getData2 ();
138 | final Integer value = Integer.valueOf (data2 > 64 ? 64 - data2 : data2);
139 |
140 | switch (message.getData1 ())
141 | {
142 | // Absolute values
143 | case MOXFHardware.MOXF_KNOB_1:
144 | this.trackbank.getItemAt (0).pan ().set (Integer.valueOf (data2), Integer.valueOf (128));
145 | return true;
146 |
147 | case MOXFHardware.MOXF_KNOB_2:
148 | this.trackbank.getItemAt (1).pan ().set (Integer.valueOf (data2), Integer.valueOf (128));
149 | return true;
150 |
151 | case MOXFHardware.MOXF_KNOB_3:
152 | this.trackbank.getItemAt (2).pan ().set (Integer.valueOf (data2), Integer.valueOf (128));
153 | return true;
154 |
155 | case MOXFHardware.MOXF_KNOB_4:
156 | this.trackbank.getItemAt (3).pan ().set (Integer.valueOf (data2), Integer.valueOf (128));
157 | return true;
158 |
159 | // Relative values
160 | case MOXFHardware.MOXF_KNOB_5:
161 | this.trackbank.getItemAt (0).volume ().inc (value, Integer.valueOf (128));
162 | return true;
163 |
164 | case MOXFHardware.MOXF_KNOB_6:
165 | this.trackbank.getItemAt (1).volume ().inc (value, Integer.valueOf (128));
166 | return true;
167 |
168 | case MOXFHardware.MOXF_KNOB_7:
169 | this.trackbank.getItemAt (2).volume ().inc (value, Integer.valueOf (128));
170 | return true;
171 |
172 | case MOXFHardware.MOXF_KNOB_8:
173 | this.trackbank.getItemAt (3).volume ().inc (value, Integer.valueOf (128));
174 | return true;
175 |
176 | default:
177 | return false;
178 | }
179 | }
180 |
181 | return false;
182 | }
183 | }
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/java/de/mossgrabers/handler/TransportHandler.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.handler;
24 |
25 | import de.mossgrabers.MOXFHardware;
26 |
27 | import com.bitwig.extension.api.util.midi.ShortMidiMessage;
28 | import com.bitwig.extension.controller.api.Transport;
29 |
30 |
31 | /**
32 | * Handles all MIDI commands related to the transport controls like Play, Stop, etc.
33 | *
34 | * @author Jürgen Moßgraber
35 | */
36 | public class TransportHandler
37 | {
38 | private Transport transport;
39 | private MOXFHardware hardware;
40 |
41 |
42 | /**
43 | * Constructor.
44 | *
45 | * @param transport The Bitwig transport object
46 | * @param hardware The MOXF device
47 | */
48 | public TransportHandler (final Transport transport, final MOXFHardware hardware)
49 | {
50 | this.transport = transport;
51 | this.hardware = hardware;
52 |
53 | this.transport.isPlaying ().markInterested ();
54 | this.transport.isArrangerRecordEnabled ().markInterested ();
55 | }
56 |
57 |
58 | /**
59 | * Handle/process the given MIDI message.
60 | *
61 | * @param message The message to process
62 | * @return True if the message was process by the method
63 | */
64 | public boolean handleMidi (final ShortMidiMessage message)
65 | {
66 | if (!message.isNoteOn ())
67 | return false;
68 |
69 | if (message.getData2 () == 0)
70 | return true;
71 |
72 | switch (message.getData1 ())
73 | {
74 | case MOXFHardware.MOXF_BUTTON_PLAY:
75 | this.transport.play ();
76 | return true;
77 |
78 | case MOXFHardware.MOXF_BUTTON_STOP:
79 | this.transport.stop ();
80 | return true;
81 |
82 | case MOXFHardware.MOXF_BUTTON_REC:
83 | this.transport.record ();
84 | return true;
85 |
86 | case MOXFHardware.MOXF_BUTTON_FWD:
87 | this.transport.fastForward ();
88 | return true;
89 |
90 | case MOXFHardware.MOXF_BUTTON_BACK:
91 | this.transport.rewind ();
92 | return true;
93 |
94 | case MOXFHardware.MOXF_BUTTON_LOCATE:
95 | this.transport.tapTempo ();
96 | return true;
97 |
98 | default:
99 | return false;
100 | }
101 | }
102 |
103 |
104 | /**
105 | * Update the LED states on the device (only as an example but not working with the MOXF).
106 | */
107 | public void updateLEDs ()
108 | {
109 | this.hardware.updateLED (MOXFHardware.MOXF_BUTTON_PLAY, this.transport.isPlaying ().get ());
110 | this.hardware.updateLED (MOXFHardware.MOXF_BUTTON_REC, this.transport.isArrangerRecordEnabled ().get ());
111 | }
112 | }
--------------------------------------------------------------------------------
/Part-10/MOXF/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | de.mossgrabers.MOXFExtensionExtensionDefinition
--------------------------------------------------------------------------------
/Part-11/OSCExample/OSCExample.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("MOSS", "OSCExample", "0.1", "8ca40c37-78c9-4c8c-932a-19a835cdbc06", "MOSS");
27 |
28 | var connection = null;
29 |
30 | function init()
31 | {
32 | var oscModule = host.getOscModule ();
33 |
34 | // Sending
35 | connection = oscModule.connectToUdpServer ("127.0.0.1", 9000, oscModule.createAddressSpace ());
36 |
37 | // Receiving
38 | var addressSpace = oscModule.createAddressSpace ();
39 | addressSpace.setShouldLogMessages (true);
40 | addressSpace.registerMethod ("/whatever", ",s", "A test function", function (source, message)
41 | {
42 | println ("Received: " + message.getAddressPattern () + " " + message.getTypeTag () + " " + message.getString (0));
43 | });
44 | addressSpace.registerDefaultMethod (function (source, message)
45 | {
46 | println ("Received unknown: " + message.getAddressPattern ());
47 | });
48 | oscModule.createUdpServer (8000, addressSpace);
49 |
50 | println("OSCExample initialized!");
51 | }
52 |
53 | function sendOSCMessage ()
54 | {
55 | // connection.startBundle ();
56 | connection.sendMessage ("/hello/world", "Good Morning!");
57 | // connection.endBundle ();
58 | }
59 |
60 |
61 | function flush()
62 | {
63 | }
64 |
65 | function exit()
66 | {
67 | }
--------------------------------------------------------------------------------
/Part-11/OSCTest.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 0,
7 | "revision" : 2,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "classnamespace" : "box",
13 | "rect" : [ -3392.0, 116.0, 367.0, 618.0 ],
14 | "bglocked" : 0,
15 | "openinpresentation" : 0,
16 | "default_fontsize" : 12.0,
17 | "default_fontface" : 0,
18 | "default_fontname" : "Arial",
19 | "gridonopen" : 1,
20 | "gridsize" : [ 15.0, 15.0 ],
21 | "gridsnaponopen" : 1,
22 | "objectsnaponopen" : 1,
23 | "statusbarvisible" : 2,
24 | "toolbarvisible" : 1,
25 | "lefttoolbarpinned" : 0,
26 | "toptoolbarpinned" : 0,
27 | "righttoolbarpinned" : 0,
28 | "bottomtoolbarpinned" : 0,
29 | "toolbars_unpinned_last_save" : 0,
30 | "tallnewobj" : 0,
31 | "boxanimatetime" : 200,
32 | "enablehscroll" : 1,
33 | "enablevscroll" : 1,
34 | "devicewidth" : 0.0,
35 | "description" : "",
36 | "digest" : "",
37 | "tags" : "",
38 | "style" : "",
39 | "subpatcher_template" : "",
40 | "boxes" : [ {
41 | "box" : {
42 | "bgcolor" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
43 | "bgfillcolor_angle" : 270.0,
44 | "bgfillcolor_autogradient" : 0.79,
45 | "bgfillcolor_color" : [ 0.290196, 0.309804, 0.301961, 1.0 ],
46 | "bgfillcolor_color1" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
47 | "bgfillcolor_color2" : [ 0.685, 0.685, 0.685, 1.0 ],
48 | "bgfillcolor_proportion" : 0.39,
49 | "bgfillcolor_type" : "gradient",
50 | "fontname" : "Arial",
51 | "fontsize" : 12.0,
52 | "gradient" : 0,
53 | "id" : "obj-3",
54 | "maxclass" : "message",
55 | "numinlets" : 2,
56 | "numoutlets" : 1,
57 | "outlettype" : [ "" ],
58 | "patching_rect" : [ 175.0, 34.0, 139.0, 22.0 ],
59 | "presentation_linecount" : 2,
60 | "text" : "/whatever/test PingPong",
61 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ]
62 | }
63 |
64 | }
65 | , {
66 | "box" : {
67 | "bgcolor" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
68 | "bgfillcolor_angle" : 270.0,
69 | "bgfillcolor_autogradient" : 0.79,
70 | "bgfillcolor_color" : [ 0.290196, 0.309804, 0.301961, 1.0 ],
71 | "bgfillcolor_color1" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
72 | "bgfillcolor_color2" : [ 0.685, 0.685, 0.685, 1.0 ],
73 | "bgfillcolor_proportion" : 0.39,
74 | "bgfillcolor_type" : "gradient",
75 | "fontname" : "Arial",
76 | "fontsize" : 12.0,
77 | "gradient" : 0,
78 | "id" : "obj-2",
79 | "maxclass" : "message",
80 | "numinlets" : 2,
81 | "numoutlets" : 1,
82 | "outlettype" : [ "" ],
83 | "patching_rect" : [ 281.0, 102.0, 34.0, 22.0 ],
84 | "text" : "/oink",
85 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ]
86 | }
87 |
88 | }
89 | , {
90 | "box" : {
91 | "bgcolor" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
92 | "bgfillcolor_angle" : 270.0,
93 | "bgfillcolor_autogradient" : 0.79,
94 | "bgfillcolor_color" : [ 0.290196, 0.309804, 0.301961, 1.0 ],
95 | "bgfillcolor_color1" : [ 0.678431, 0.819608, 0.819608, 1.0 ],
96 | "bgfillcolor_color2" : [ 0.685, 0.685, 0.685, 1.0 ],
97 | "bgfillcolor_proportion" : 0.39,
98 | "bgfillcolor_type" : "gradient",
99 | "fontname" : "Arial",
100 | "fontsize" : 12.0,
101 | "gradient" : 0,
102 | "id" : "obj-213",
103 | "maxclass" : "message",
104 | "numinlets" : 2,
105 | "numoutlets" : 1,
106 | "outlettype" : [ "" ],
107 | "patching_rect" : [ 198.0, 69.0, 116.0, 22.0 ],
108 | "text" : "/whatever PingPong",
109 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ]
110 | }
111 |
112 | }
113 | , {
114 | "box" : {
115 | "fontname" : "Arial",
116 | "fontsize" : 12.0,
117 | "id" : "obj-60",
118 | "maxclass" : "newobj",
119 | "numinlets" : 1,
120 | "numoutlets" : 0,
121 | "patching_rect" : [ 28.0, 80.9090576171875, 34.0, 22.0 ],
122 | "text" : "print"
123 | }
124 |
125 | }
126 | , {
127 | "box" : {
128 | "fontname" : "Arial",
129 | "fontsize" : 12.0,
130 | "id" : "obj-59",
131 | "maxclass" : "newobj",
132 | "numinlets" : 1,
133 | "numoutlets" : 1,
134 | "outlettype" : [ "" ],
135 | "patching_rect" : [ 28.0, 34.0, 99.0, 22.0 ],
136 | "text" : "udpreceive 9000"
137 | }
138 |
139 | }
140 | , {
141 | "box" : {
142 | "fontname" : "Arial",
143 | "fontsize" : 12.0,
144 | "id" : "obj-1",
145 | "maxclass" : "newobj",
146 | "numinlets" : 1,
147 | "numoutlets" : 0,
148 | "patching_rect" : [ 175.0, 139.9090576171875, 140.0, 22.0 ],
149 | "text" : "udpsend 127.0.0.1 8000"
150 | }
151 |
152 | }
153 | ],
154 | "lines" : [ {
155 | "patchline" : {
156 | "destination" : [ "obj-1", 0 ],
157 | "source" : [ "obj-2", 0 ]
158 | }
159 |
160 | }
161 | , {
162 | "patchline" : {
163 | "destination" : [ "obj-1", 0 ],
164 | "source" : [ "obj-213", 0 ]
165 | }
166 |
167 | }
168 | , {
169 | "patchline" : {
170 | "destination" : [ "obj-1", 0 ],
171 | "source" : [ "obj-3", 0 ]
172 | }
173 |
174 | }
175 | , {
176 | "patchline" : {
177 | "destination" : [ "obj-60", 0 ],
178 | "source" : [ "obj-59", 0 ]
179 | }
180 |
181 | }
182 | ],
183 | "dependency_cache" : [ ],
184 | "autosave" : 0
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/Part-12/GraphicsTest/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | de.mossgrabers
6 | graphicstest
7 | jar
8 | GraphicsTest
9 | 0.1
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 7
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.5.1
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/GraphicsTest.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-12/GraphicsTest/src/main/java/de/mossgrabers/GraphicsTestExtension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers;
24 |
25 | import com.bitwig.extension.api.Color;
26 | import com.bitwig.extension.api.graphics.Bitmap;
27 | import com.bitwig.extension.api.graphics.BitmapFormat;
28 | import com.bitwig.extension.api.graphics.GraphicsOutput;
29 | import com.bitwig.extension.controller.ControllerExtension;
30 | import com.bitwig.extension.controller.api.ControllerHost;
31 |
32 |
33 | public class GraphicsTestExtension extends ControllerExtension
34 | {
35 | protected GraphicsTestExtension (final GraphicsTestExtensionDefinition definition, final ControllerHost host)
36 | {
37 | super (definition, host);
38 | }
39 |
40 |
41 | /** {@inheritDoc} */
42 | @Override
43 | public void init ()
44 | {
45 | final ControllerHost host = this.getHost ();
46 |
47 | final Bitmap bitmap = host.createBitmap (200, 200, BitmapFormat.ARGB32);
48 | bitmap.showDisplayWindow ();
49 | bitmap.setDisplayWindowTitle ("Remote Display Debug Output");
50 |
51 | // Put this in a separate thread! Requires synchronization!
52 | bitmap.render (this::render);
53 |
54 | // That is how you get your data to send to a display, mitght go to flush() or a separate
55 | // Thread depending on the refresh requirements of targeted display
56 | // ByteBuffer buffer = bitmap.getMemoryBlock ().createByteBuffer ();
57 | }
58 |
59 |
60 | private void render (final GraphicsOutput gc)
61 | {
62 | gc.setColor (Color.fromRGB (1.0, 0, 0));
63 | gc.circle (100, 100, 50);
64 | gc.fill ();
65 | }
66 |
67 |
68 | /** {@inheritDoc} */
69 | @Override
70 | public void exit ()
71 | {
72 | // Intentionally empty
73 | }
74 |
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public void flush ()
79 | {
80 | // Intentionally empty
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Part-12/GraphicsTest/src/main/java/de/mossgrabers/GraphicsTestExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers;
24 |
25 | import com.bitwig.extension.api.PlatformType;
26 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
27 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 |
30 | import java.util.UUID;
31 |
32 |
33 | public class GraphicsTestExtensionDefinition extends ControllerExtensionDefinition
34 | {
35 | private static final UUID DRIVER_ID = UUID.fromString ("b17f9f0b-1041-45e9-a391-d28609254de4");
36 |
37 |
38 | /**
39 | * Constructor.
40 | */
41 | public GraphicsTestExtensionDefinition ()
42 | {
43 | // Intentionally empty
44 | }
45 |
46 |
47 | /** {@inheritDoc} */
48 | @Override
49 | public boolean isUsingBetaAPI ()
50 | {
51 | return true;
52 | }
53 |
54 |
55 | /** {@inheritDoc} */
56 | @Override
57 | public String getName ()
58 | {
59 | return "GraphicsTest";
60 | }
61 |
62 |
63 | /** {@inheritDoc} */
64 | @Override
65 | public String getAuthor ()
66 | {
67 | return "Jürgen Moßgraber";
68 | }
69 |
70 |
71 | /** {@inheritDoc} */
72 | @Override
73 | public String getVersion ()
74 | {
75 | return "0.1";
76 | }
77 |
78 |
79 | /** {@inheritDoc} */
80 | @Override
81 | public UUID getId ()
82 | {
83 | return DRIVER_ID;
84 | }
85 |
86 |
87 | /** {@inheritDoc} */
88 | @Override
89 | public String getHardwareVendor ()
90 | {
91 | return "MOSS";
92 | }
93 |
94 |
95 | /** {@inheritDoc} */
96 | @Override
97 | public String getHardwareModel ()
98 | {
99 | return "GraphicsTest";
100 | }
101 |
102 |
103 | /** {@inheritDoc} */
104 | @Override
105 | public int getRequiredAPIVersion ()
106 | {
107 | return 7;
108 | }
109 |
110 |
111 | /** {@inheritDoc} */
112 | @Override
113 | public int getNumMidiInPorts ()
114 | {
115 | return 0;
116 | }
117 |
118 |
119 | /** {@inheritDoc} */
120 | @Override
121 | public int getNumMidiOutPorts ()
122 | {
123 | return 0;
124 | }
125 |
126 |
127 | /** {@inheritDoc} */
128 | @Override
129 | public void listAutoDetectionMidiPortNames (final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
130 | {
131 | // Intentionall empty
132 | }
133 |
134 |
135 | /** {@inheritDoc} */
136 | @Override
137 | public GraphicsTestExtension createInstance (final ControllerHost host)
138 | {
139 | return new GraphicsTestExtension (this, host);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/Part-12/GraphicsTest/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | de.mossgrabers.GraphicsTestExtensionDefinition
--------------------------------------------------------------------------------
/Part-13/Arpeggiator.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(10);
24 |
25 | host.defineController("moss", "Arpeggiator", "0.1", "cce26ee9-30b8-4fa1-aee7-41d52193b9d9", "moss");
26 | host.defineMidiPorts(1, 0);
27 |
28 | var arpeggiator = null;
29 | var noteLatch = null;
30 |
31 |
32 | function init()
33 | {
34 | // MidiIn
35 | var midiIn = host.getMidiInPort(0);
36 |
37 | // NoteInput
38 | var noteInput = midiIn.createNoteInput ("Arpeggiator Test");
39 |
40 | // Arpeggiator
41 | arpeggiator = noteInput.arpeggiator ();
42 |
43 | // NoteLatch
44 | noteLatch = noteInput.noteLatch ();
45 |
46 | // SettableBooleanValue
47 | arpeggiator.isEnabled ().addValueObserver (function (value) {
48 | println ("Arpeggiator is: " + (value ? "On" : "Off" ));
49 | })
50 |
51 | // SettableBooleanValue
52 | noteLatch.isEnabled ().addValueObserver (function (value) {
53 | println ("Note Latch is: " + (value ? "On" : "Off" ));
54 | })
55 |
56 | println("Arpeggiator initialized!");
57 | }
58 |
59 | function toggleArpeggiator ()
60 | {
61 | arpeggiator.isEnabled ().toggle ();
62 | }
63 |
64 | function toggleNoteLatch ()
65 | {
66 | noteLatch.isEnabled ().toggle ();
67 | }
68 |
69 |
70 | function flush()
71 | {
72 | }
73 |
74 | function exit()
75 | {
76 | }
--------------------------------------------------------------------------------
/Part-14/HardwareAPITest1.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(10);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("Moss", "Hardware API Test 1", "1.0", "68e7b117-0999-496a-b5a0-16f533fc5da0", "Moss");
27 | host.defineMidiPorts(1, 0);
28 |
29 | function init()
30 | {
31 | var channel = 0;
32 | var buttonControl = 112; // TODO -> EXCHANGE WITH YOUR CONTROL
33 | var sliderControl = 14; // TODO -> EXCHANGE WITH YOUR CONTROL
34 | var absKnobControl = 15; // TODO -> EXCHANGE WITH YOUR CONTROL
35 | var relKnobControl = 16; // TODO -> EXCHANGE WITH YOUR CONTROL
36 |
37 | // 1. Create the controls
38 |
39 | var hardwareSurface = host.createHardwareSurface ();
40 | // HardwareButton
41 | var playButton = hardwareSurface.createHardwareButton ("PLAY_BUTTON");
42 | // HardwareSlider
43 | var slider = hardwareSurface.createHardwareSlider ("SLIDER_1");
44 | // AbsoluteHardwareKnob
45 | var absKnob = hardwareSurface.createAbsoluteHardwareKnob ("ABSOLUTE_KNOB");
46 | // RelativeHardwareKnob
47 | var relKnob = hardwareSurface.createRelativeHardwareKnob ("RELATIVE_KNOB");
48 |
49 |
50 | // 2. Bind the controls to MIDI commands
51 |
52 | var port = host.getMidiInPort(0);
53 | playButton.pressedAction ().setActionMatcher (port.createCCActionMatcher (channel, buttonControl, 127));
54 | slider.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (channel, sliderControl));
55 | absKnob.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (channel, absKnobControl));
56 | relKnob.setAdjustValueMatcher (port.createRelativeBinOffsetCCValueMatcher (channel, relKnobControl, 127));
57 |
58 |
59 | // 3. Bind the controls to actions / targets
60 |
61 | var transport = host.createTransport ();
62 | playButton.pressedAction ().setBinding (transport.playAction ());
63 |
64 | var trackBank = host.createMainTrackBank (3, 0, 0);
65 | slider.setBinding (trackBank.getItemAt (0).volume ());
66 | absKnob.setBinding (trackBank.getItemAt (1).volume ());
67 | relKnob.setBinding (trackBank.getItemAt (2).volume ());
68 |
69 | println("Initialized!");
70 | }
71 |
72 | function flush()
73 | {
74 | // Empty
75 | }
76 |
77 | function exit()
78 | {
79 | println("Exited!");
80 | }
--------------------------------------------------------------------------------
/Part-15/HardwareAPITest2.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(10);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("Moss", "Hardware API Test 2", "1.0", "98ff9719-450a-4a5c-86fb-48ff171127ae", "Moss");
27 | host.defineMidiPorts(1, 0);
28 |
29 | function init()
30 | {
31 | var channel = 0;
32 | var buttonControl = 112; // TODO -> EXCHANGE WITH YOUR CONTROL
33 | var sliderControl = 14; // TODO -> EXCHANGE WITH YOUR CONTROL
34 | var absKnobControl = 15; // TODO -> EXCHANGE WITH YOUR CONTROL
35 | var relKnobControl = 16; // TODO -> EXCHANGE WITH YOUR CONTROL
36 |
37 | // 1. Create the controls
38 |
39 | var hardwareSurface = host.createHardwareSurface ();
40 | // HardwareButton
41 | var playButton = hardwareSurface.createHardwareButton ("PLAY_BUTTON");
42 | // HardwareSlider
43 | var slider = hardwareSurface.createHardwareSlider ("SLIDER_1");
44 | // AbsoluteHardwareKnob
45 | var absKnob = hardwareSurface.createAbsoluteHardwareKnob ("ABSOLUTE_KNOB");
46 | // RelativeHardwareKnob
47 | var relKnob = hardwareSurface.createRelativeHardwareKnob ("RELATIVE_KNOB");
48 |
49 | // PianoKeyboard
50 | var keyboard = hardwareSurface.createPianoKeyboard ("THE_KEYBOARD",49, 0, 36);
51 |
52 |
53 | // 2. Bind the controls to MIDI commands
54 |
55 | var port = host.getMidiInPort(0);
56 | playButton.pressedAction ().setActionMatcher (port.createCCActionMatcher (channel, buttonControl, 127));
57 | slider.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (channel, sliderControl));
58 | absKnob.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (channel, absKnobControl));
59 | relKnob.setAdjustValueMatcher (port.createRelativeBinOffsetCCValueMatcher (channel, relKnobControl, 127));
60 |
61 | var noteInput = port.createNoteInput ("Hardware Test 2");
62 | keyboard.setMidiIn (port);
63 |
64 |
65 | // 3. Bind the controls to actions / targets
66 |
67 | var transport = host.createTransport ();
68 | playButton.pressedAction ().setBinding (transport.playAction ());
69 |
70 | var trackBank = host.createMainTrackBank (3, 0, 0);
71 | slider.setBinding (trackBank.getItemAt (0).volume ());
72 | absKnob.setBinding (trackBank.getItemAt (1).volume ());
73 | relKnob.setBinding (trackBank.getItemAt (2).volume ());
74 |
75 |
76 | // 4. Layout the simulator UI
77 |
78 | hardwareSurface.setPhysicalSize (200, 200);
79 |
80 | playButton.setBounds(137.0, 133.5, 8.75, 7.0);
81 | hardwareSurface.hardwareElementWithId("SLIDER_1").setBounds(106.0, 120.0, 10.0, 34.25);
82 | hardwareSurface.hardwareElementWithId("ABSOLUTE_KNOB").setBounds(127.25, 12.25, 10.0, 10.0);
83 | hardwareSurface.hardwareElementWithId("RELATIVE_KNOB").setBounds(127.25, 78.75, 10.0, 10.0);
84 | hardwareSurface.hardwareElementWithId("THE_KEYBOARD").setBounds(4.5, 163.5, 191.0, 28.25);
85 |
86 | println("Initialized!");
87 | }
88 |
89 | function flush()
90 | {
91 | // Empty
92 | }
93 |
94 | function exit()
95 | {
96 | println("Exited!");
97 | }
--------------------------------------------------------------------------------
/Part-15/config.json:
--------------------------------------------------------------------------------
1 | testing-features : true
--------------------------------------------------------------------------------
/Part-16/HardwareAPITest3/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.moss
6 | hardwareapitest3
7 | jar
8 | HardwareAPITest3
9 | 1.0
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 11
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.8.0
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/HardwareAPITest3.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-16/HardwareAPITest3/src/main/java/com/moss/HardwareAPITest3Extension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.moss;
24 |
25 | import com.bitwig.extension.controller.ControllerExtension;
26 | import com.bitwig.extension.controller.api.AbsoluteHardwareKnob;
27 | import com.bitwig.extension.controller.api.ControllerHost;
28 | import com.bitwig.extension.controller.api.HardwareButton;
29 | import com.bitwig.extension.controller.api.HardwareSlider;
30 | import com.bitwig.extension.controller.api.HardwareSurface;
31 | import com.bitwig.extension.controller.api.MidiIn;
32 | import com.bitwig.extension.controller.api.PianoKeyboard;
33 | import com.bitwig.extension.controller.api.RelativeHardwareKnob;
34 | import com.bitwig.extension.controller.api.TrackBank;
35 | import com.bitwig.extension.controller.api.Transport;
36 |
37 |
38 | /**
39 | * Demo code to explain the Bitwig Hardware API.
40 | *
41 | * @author Jürgen Moßgraber
42 | */
43 | public class HardwareAPITest3Extension extends ControllerExtension
44 | {
45 | private final int channel = 0;
46 | private final int buttonControl = 112; // TODO -> EXCHANGE WITH YOUR CONTROL
47 | private final int sliderControl = 14; // TODO -> EXCHANGE WITH YOUR CONTROL
48 | private final int absKnobControl = 15; // TODO -> EXCHANGE WITH YOUR CONTROL
49 | private final int relKnobControl = 16; // TODO -> EXCHANGE WITH YOUR CONTROL
50 |
51 |
52 | protected HardwareAPITest3Extension (final HardwareAPITest3ExtensionDefinition definition, final ControllerHost host)
53 | {
54 | super (definition, host);
55 | }
56 |
57 |
58 | /** {@inheritDoc} */
59 | @Override
60 | public void init ()
61 | {
62 | final ControllerHost host = this.getHost ();
63 |
64 | // 1. Create the controls
65 |
66 | final HardwareSurface hardwareSurface = host.createHardwareSurface ();
67 | // HardwareButton
68 | final HardwareButton playButton = hardwareSurface.createHardwareButton ("PLAY_BUTTON");
69 | // HardwareSlider
70 | final HardwareSlider slider = hardwareSurface.createHardwareSlider ("SLIDER_1");
71 | // AbsoluteHardwareKnob
72 | final AbsoluteHardwareKnob absKnob = hardwareSurface.createAbsoluteHardwareKnob ("ABSOLUTE_KNOB");
73 | // RelativeHardwareKnob
74 | final RelativeHardwareKnob relKnob = hardwareSurface.createRelativeHardwareKnob ("RELATIVE_KNOB");
75 |
76 | // PianoKeyboard
77 | final PianoKeyboard keyboard = hardwareSurface.createPianoKeyboard ("THE_KEYBOARD", 49, 0, 36);
78 |
79 | // 2. Bind the controls to MIDI commands
80 |
81 | final MidiIn port = host.getMidiInPort (0);
82 | playButton.pressedAction ().setActionMatcher (port.createCCActionMatcher (this.channel, this.buttonControl, 127));
83 | slider.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (this.channel, this.sliderControl));
84 | absKnob.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (this.channel, this.absKnobControl));
85 | relKnob.setAdjustValueMatcher (port.createRelativeBinOffsetCCValueMatcher (this.channel, this.relKnobControl, 127));
86 |
87 | // Use these actions to bind to knob touch events...
88 | // relKnob.beginTouchAction ()
89 | // relKnob.endTouchAction ()
90 |
91 | port.createNoteInput ("Hardware Test 3");
92 | keyboard.setMidiIn (port);
93 |
94 | // 3. Bind the controls to actions / targets
95 |
96 | final Transport transport = host.createTransport ();
97 | playButton.pressedAction ().setBinding (transport.playAction ());
98 |
99 | final TrackBank trackBank = host.createMainTrackBank (3, 0, 0);
100 | slider.setBinding (trackBank.getItemAt (0).volume ());
101 | absKnob.setBinding (trackBank.getItemAt (1).volume ());
102 | relKnob.setBinding (trackBank.getItemAt (2).volume ());
103 |
104 | // Use these actions to bind to knob touch events...
105 | // relKnob.beginTouchAction ()
106 | // relKnob.endTouchAction ()
107 |
108 | // If you want to create a clickable knob...
109 | // relKnob.setHardwareButton (playButton);
110 |
111 | // 4. Layout the simulator UI
112 |
113 | hardwareSurface.setPhysicalSize (200, 200);
114 |
115 | playButton.setBounds (137.0, 133.5, 8.75, 7.0);
116 | hardwareSurface.hardwareElementWithId ("SLIDER_1").setBounds (106.0, 120.0, 10.0, 34.25);
117 | hardwareSurface.hardwareElementWithId ("ABSOLUTE_KNOB").setBounds (127.25, 12.25, 10.0, 10.0);
118 | hardwareSurface.hardwareElementWithId ("RELATIVE_KNOB").setBounds (127.25, 78.75, 10.0, 10.0);
119 | hardwareSurface.hardwareElementWithId ("THE_KEYBOARD").setBounds (4.5, 163.5, 191.0, 28.25);
120 | }
121 |
122 |
123 | /** {@inheritDoc} */
124 | @Override
125 | public void exit ()
126 | {
127 | // Not used
128 | }
129 |
130 |
131 | /** {@inheritDoc} */
132 | @Override
133 | public void flush ()
134 | {
135 | // Not used
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Part-16/HardwareAPITest3/src/main/java/com/moss/HardwareAPITest3ExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.moss;
24 |
25 | import com.bitwig.extension.api.PlatformType;
26 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
27 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 |
30 | import java.util.UUID;
31 |
32 |
33 | /**
34 | * Demo code to explain the Bitwig Hardware API. The definition class.
35 | *
36 | * @author Jürgen Moßgraber
37 | */
38 | public class HardwareAPITest3ExtensionDefinition extends ControllerExtensionDefinition
39 | {
40 | private static final UUID DRIVER_ID = UUID.fromString ("a4d5c3b8-29f5-4851-b725-f93fe91e7764");
41 |
42 |
43 | /**
44 | * Constructor
45 | */
46 | public HardwareAPITest3ExtensionDefinition ()
47 | {
48 | // Intentionally empty
49 | }
50 |
51 |
52 | /** {@inheritDoc} */
53 | @Override
54 | public String getName ()
55 | {
56 | return "HardwareAPITest3";
57 | }
58 |
59 |
60 | /** {@inheritDoc} */
61 | @Override
62 | public String getAuthor ()
63 | {
64 | return "moss";
65 | }
66 |
67 |
68 | /** {@inheritDoc} */
69 | @Override
70 | public String getVersion ()
71 | {
72 | return "1.0";
73 | }
74 |
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public UUID getId ()
79 | {
80 | return DRIVER_ID;
81 | }
82 |
83 |
84 | /** {@inheritDoc} */
85 | @Override
86 | public String getHardwareVendor ()
87 | {
88 | return "moss";
89 | }
90 |
91 |
92 | /** {@inheritDoc} */
93 | @Override
94 | public String getHardwareModel ()
95 | {
96 | return "HardwareAPITest3";
97 | }
98 |
99 |
100 | /** {@inheritDoc} */
101 | @Override
102 | public int getRequiredAPIVersion ()
103 | {
104 | return 11;
105 | }
106 |
107 |
108 | /** {@inheritDoc} */
109 | @Override
110 | public int getNumMidiInPorts ()
111 | {
112 | return 1;
113 | }
114 |
115 |
116 | /** {@inheritDoc} */
117 | @Override
118 | public int getNumMidiOutPorts ()
119 | {
120 | return 1;
121 | }
122 |
123 |
124 | /** {@inheritDoc} */
125 | @Override
126 | public void listAutoDetectionMidiPortNames (final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
127 | {
128 | // Not used
129 | }
130 |
131 |
132 | /** {@inheritDoc} */
133 | @Override
134 | public HardwareAPITest3Extension createInstance (final ControllerHost host)
135 | {
136 | return new HardwareAPITest3Extension (this, host);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Part-16/HardwareAPITest3/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | com.moss.HardwareAPITest3ExtensionDefinition
--------------------------------------------------------------------------------
/Part-17/HardwareAPITest4/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.moss
6 | hardwareapitest4
7 | jar
8 | HardwareAPITest4
9 | 1.0
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 11
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.8.0
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/HardwareAPITest4.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-17/HardwareAPITest4/src/main/java/com/moss/HardwareAPITest4Extension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.moss;
24 |
25 | import com.bitwig.extension.api.Color;
26 | import com.bitwig.extension.controller.ControllerExtension;
27 | import com.bitwig.extension.controller.api.AbsoluteHardwareKnob;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 | import com.bitwig.extension.controller.api.HardwareButton;
30 | import com.bitwig.extension.controller.api.HardwareLightVisualState;
31 | import com.bitwig.extension.controller.api.HardwareSlider;
32 | import com.bitwig.extension.controller.api.HardwareSurface;
33 | import com.bitwig.extension.controller.api.HardwareTextDisplay;
34 | import com.bitwig.extension.controller.api.HardwareTextDisplayLine;
35 | import com.bitwig.extension.controller.api.MidiIn;
36 | import com.bitwig.extension.controller.api.MidiOut;
37 | import com.bitwig.extension.controller.api.OnOffHardwareLight;
38 | import com.bitwig.extension.controller.api.PianoKeyboard;
39 | import com.bitwig.extension.controller.api.RelativeHardwareKnob;
40 | import com.bitwig.extension.controller.api.TrackBank;
41 | import com.bitwig.extension.controller.api.Transport;
42 |
43 |
44 | /**
45 | * Demo code to explain the Bitwig Hardware API.
46 | *
47 | * @author Jürgen Moßgraber
48 | */
49 | public class HardwareAPITest4Extension extends ControllerExtension
50 | {
51 | private final int channel = 0;
52 | private final int buttonControl = 112; // TODO -> EXCHANGE WITH YOUR CONTROL
53 | private final int sliderControl = 14; // TODO -> EXCHANGE WITH YOUR CONTROL
54 | private final int absKnobControl = 15; // TODO -> EXCHANGE WITH YOUR CONTROL
55 | private final int relKnobControl = 16; // TODO -> EXCHANGE WITH YOUR CONTROL
56 |
57 | private HardwareSurface hardwareSurface;
58 |
59 |
60 | protected HardwareAPITest4Extension (final HardwareAPITest4ExtensionDefinition definition, final ControllerHost host)
61 | {
62 | super (definition, host);
63 | }
64 |
65 |
66 | /** {@inheritDoc} */
67 | @Override
68 | public void init ()
69 | {
70 | final ControllerHost host = this.getHost ();
71 |
72 | /////////////////////////////////////////////////////////////////////////////////////
73 | // 1. Create the controls
74 |
75 | // 1.1 Create input controls
76 |
77 | this.hardwareSurface = host.createHardwareSurface ();
78 | // HardwareButton
79 | final HardwareButton playButton = this.hardwareSurface.createHardwareButton ("PLAY_BUTTON");
80 | // HardwareSlider
81 | final HardwareSlider slider = this.hardwareSurface.createHardwareSlider ("SLIDER_1");
82 | // AbsoluteHardwareKnob
83 | final AbsoluteHardwareKnob absKnob = this.hardwareSurface.createAbsoluteHardwareKnob ("ABSOLUTE_KNOB");
84 | // RelativeHardwareKnob
85 | final RelativeHardwareKnob relKnob = this.hardwareSurface.createRelativeHardwareKnob ("RELATIVE_KNOB");
86 |
87 | // PianoKeyboard
88 | final PianoKeyboard keyboard = this.hardwareSurface.createPianoKeyboard ("THE_KEYBOARD", 49, 0, 36);
89 |
90 | // 1.2 Create output controls
91 |
92 | // OnOffHardwareLight
93 | final OnOffHardwareLight playButtonLight = this.hardwareSurface.createOnOffHardwareLight ("PLAY_BUTTON_LIGHT");
94 | playButton.setBackgroundLight (playButtonLight);
95 |
96 | // HardwareTextDisplay
97 | final HardwareTextDisplay textDisplay = this.hardwareSurface.createHardwareTextDisplay ("TEXT_DISPLAY", 1);
98 |
99 | /////////////////////////////////////////////////////////////////////////////////////
100 | // 2. Bind the controls to MIDI commands
101 |
102 | final MidiIn port = host.getMidiInPort (0);
103 | playButton.pressedAction ().setActionMatcher (port.createCCActionMatcher (this.channel, this.buttonControl, 127));
104 | playButton.releasedAction ().setActionMatcher (port.createCCActionMatcher (this.channel, this.buttonControl, 0));
105 |
106 | slider.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (this.channel, this.sliderControl));
107 | absKnob.setAdjustValueMatcher (port.createAbsoluteCCValueMatcher (this.channel, this.absKnobControl));
108 | relKnob.setAdjustValueMatcher (port.createRelativeBinOffsetCCValueMatcher (this.channel, this.relKnobControl, 127));
109 |
110 | // Use these actions to bind to knob touch events...
111 | // relKnob.beginTouchAction ()
112 | // relKnob.endTouchAction ()
113 |
114 | port.createNoteInput ("Hardware Test 3");
115 | keyboard.setMidiIn (port);
116 |
117 | /////////////////////////////////////////////////////////////////////////////////////
118 | // 3. Bind the controls to actions / targets
119 |
120 | final Transport transport = host.createTransport ();
121 | playButton.pressedAction ().setBinding (transport.playAction ());
122 |
123 | final TrackBank trackBank = host.createMainTrackBank (3, 0, 0);
124 | slider.setBinding (trackBank.getItemAt (0).volume ());
125 | absKnob.setBinding (trackBank.getItemAt (1).volume ());
126 | relKnob.setBinding (trackBank.getItemAt (2).volume ());
127 |
128 | // Use these actions to bind to knob touch events...
129 | // relKnob.beginTouchAction ()
130 | // relKnob.endTouchAction ()
131 |
132 | // If you want to create a clickable knob...
133 | // relKnob.setHardwareButton (playButton);
134 |
135 | /////////////////////////////////////////////////////////////////////////////////////
136 | // 4. Set and create output element callbacks
137 |
138 | final MidiOut midiOutPort = host.getMidiOutPort (0);
139 |
140 | // Use state() instead of isOn() for the MultiStateHardwareLight
141 |
142 | transport.isPlaying ().markInterested ();
143 | playButtonLight.isOn ().setValueSupplier ( () -> transport.isPlaying ().get ());
144 |
145 | playButtonLight.isOn ().onUpdateHardware (isOn -> {
146 |
147 | host.println ("Updating LED: " + (isOn.booleanValue () ? "On" : "Off"));
148 |
149 | // TODO Send the LED light state to the display, normally this is a CC or note command
150 | // midiOutPort.sendMidi (int status, int data1, int data2)
151 | });
152 |
153 | playButtonLight.setStateToVisualStateFuncation (onOffState -> {
154 |
155 | final Color backgroundColor = onOffState.booleanValue () ? Color.fromRGB (0, 1, 0) : Color.fromRGB (0, 0, 0);
156 | final Color labelColor = onOffState.booleanValue () ? Color.fromRGB (0, 0, 0) : Color.fromRGB (1, 1, 1);
157 | return HardwareLightVisualState.createForColor (backgroundColor, labelColor);
158 |
159 | });
160 |
161 | textDisplay.line (0).text ().setValueSupplier ( () -> {
162 |
163 | final boolean isPlaying = transport.isPlaying ().get ();
164 | return "Playback is: " + (isPlaying ? "On" : "Off");
165 |
166 | });
167 |
168 | textDisplay.onUpdateHardware ( () -> {
169 |
170 | final HardwareTextDisplayLine displayLine = textDisplay.line (0);
171 | final String content = displayLine.text ().currentValue ();
172 |
173 | // Note: line.backgroundColor () and line.textColor () can also be used if the
174 | // displays colors can be changed ...
175 |
176 | host.println ("Updating display: " + content);
177 | // TODO Send 'content' to the display, normally this is a sysex command
178 | // midiOutPort.sendSysex (byte[] data)
179 |
180 | });
181 |
182 | /////////////////////////////////////////////////////////////////////////////////////
183 | // 5. Layout the simulator UI
184 |
185 | this.hardwareSurface.setPhysicalSize (200, 200);
186 |
187 | playButton.setBounds (137.0, 133.5, 30.75, 11.5);
188 | this.hardwareSurface.hardwareElementWithId ("SLIDER_1").setBounds (106.0, 120.0, 10.0, 34.25);
189 | this.hardwareSurface.hardwareElementWithId ("ABSOLUTE_KNOB").setBounds (127.25, 12.25, 10.0, 10.0);
190 | this.hardwareSurface.hardwareElementWithId ("RELATIVE_KNOB").setBounds (127.25, 78.75, 10.0, 10.0);
191 | this.hardwareSurface.hardwareElementWithId ("THE_KEYBOARD").setBounds (4.5, 163.5, 191.0, 28.25);
192 | this.hardwareSurface.hardwareElementWithId ("TEXT_DISPLAY").setBounds (16.25, 25.75, 87.75, 10.0);
193 | }
194 |
195 |
196 | /** {@inheritDoc} */
197 | @Override
198 | public void exit ()
199 | {
200 | // Not used
201 | }
202 |
203 |
204 | /** {@inheritDoc} */
205 | @Override
206 | public void flush ()
207 | {
208 | if (this.hardwareSurface != null)
209 | this.hardwareSurface.updateHardware ();
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/Part-17/HardwareAPITest4/src/main/java/com/moss/HardwareAPITest4ExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.moss;
24 |
25 | import com.bitwig.extension.api.PlatformType;
26 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
27 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
28 | import com.bitwig.extension.controller.api.ControllerHost;
29 |
30 | import java.util.UUID;
31 |
32 |
33 | /**
34 | * Demo code to explain the Bitwig Hardware API. The definition class.
35 | *
36 | * @author Jürgen Moßgraber
37 | */
38 | public class HardwareAPITest4ExtensionDefinition extends ControllerExtensionDefinition
39 | {
40 | private static final UUID DRIVER_ID = UUID.fromString ("456e8ab6-ea9a-4cf5-92c9-b5131d68f4be");
41 |
42 |
43 | /**
44 | * Constructor
45 | */
46 | public HardwareAPITest4ExtensionDefinition ()
47 | {
48 | // Intentionally empty
49 | }
50 |
51 |
52 | /** {@inheritDoc} */
53 | @Override
54 | public String getName ()
55 | {
56 | return "HardwareAPITest4";
57 | }
58 |
59 |
60 | /** {@inheritDoc} */
61 | @Override
62 | public String getAuthor ()
63 | {
64 | return "moss";
65 | }
66 |
67 |
68 | /** {@inheritDoc} */
69 | @Override
70 | public String getVersion ()
71 | {
72 | return "1.0";
73 | }
74 |
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public UUID getId ()
79 | {
80 | return DRIVER_ID;
81 | }
82 |
83 |
84 | /** {@inheritDoc} */
85 | @Override
86 | public String getHardwareVendor ()
87 | {
88 | return "moss";
89 | }
90 |
91 |
92 | /** {@inheritDoc} */
93 | @Override
94 | public String getHardwareModel ()
95 | {
96 | return "HardwareAPITest4";
97 | }
98 |
99 |
100 | /** {@inheritDoc} */
101 | @Override
102 | public int getRequiredAPIVersion ()
103 | {
104 | return 11;
105 | }
106 |
107 |
108 | /** {@inheritDoc} */
109 | @Override
110 | public int getNumMidiInPorts ()
111 | {
112 | return 1;
113 | }
114 |
115 |
116 | /** {@inheritDoc} */
117 | @Override
118 | public int getNumMidiOutPorts ()
119 | {
120 | return 1;
121 | }
122 |
123 |
124 | /** {@inheritDoc} */
125 | @Override
126 | public void listAutoDetectionMidiPortNames (final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
127 | {
128 | // Not used
129 | }
130 |
131 |
132 | /** {@inheritDoc} */
133 | @Override
134 | public HardwareAPITest4Extension createInstance (final ControllerHost host)
135 | {
136 | return new HardwareAPITest4Extension (this, host);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Part-17/HardwareAPITest4/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | com.moss.HardwareAPITest4ExtensionDefinition
--------------------------------------------------------------------------------
/Part-19/API12-Test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | de.mossggraber.apitwelve
6 | apitwelve-test
7 | jar
8 | API12-Test
9 | 1.0
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 12
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.8.0
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/API12Test.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-19/API12-Test/src/main/java/de/mossggraber/apitwelve/API12TestExtension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossggraber.apitwelve;
24 |
25 | import com.bitwig.extension.controller.ControllerExtension;
26 | import com.bitwig.extension.controller.api.ControllerHost;
27 | import com.bitwig.extension.controller.api.CursorTrack;
28 | import com.bitwig.extension.controller.api.Device;
29 | import com.bitwig.extension.controller.api.DeviceBank;
30 | import com.bitwig.extension.controller.api.DeviceMatcher;
31 | import com.bitwig.extension.controller.api.DocumentState;
32 | import com.bitwig.extension.controller.api.InsertionPoint;
33 | import com.bitwig.extension.controller.api.Parameter;
34 | import com.bitwig.extension.controller.api.SpecificPluginDevice;
35 |
36 | /**
37 | * Demo code to explain the Bitwig API 12.
38 | *
39 | * @author Jürgen Moßgraber
40 | */
41 | public class API12TestExtension extends ControllerExtension
42 | {
43 | private static final String NEOVERB_ID = "5653545A4D52314E656F766572620000";
44 |
45 |
46 | protected API12TestExtension(final API12TestExtensionDefinition definition, final ControllerHost host)
47 | {
48 | super(definition, host);
49 | }
50 |
51 |
52 | /** {@inheritDoc} */
53 | @Override
54 | public void init()
55 | {
56 | final ControllerHost host = getHost();
57 |
58 | ///////////////////////////////////////////////////////////////////////////////////////////////
59 | // #1 Create a VST3 matcher
60 | final CursorTrack cursorTrack = host.createCursorTrack(0, 0);
61 | final DeviceBank neoverbFilterDeviceBank = cursorTrack.createDeviceBank(1);
62 |
63 | final DeviceMatcher neoverbDeviceMatcher = host.createVST3DeviceMatcher (NEOVERB_ID);
64 | neoverbFilterDeviceBank.setDeviceMatcher(neoverbDeviceMatcher);
65 |
66 | final Device device = neoverbFilterDeviceBank.getItemAt(0);
67 |
68 | device.exists().addValueObserver(newValue -> {
69 |
70 | host.println("Neoverb found: " + (newValue ? "Yes" : "No"));
71 |
72 | });
73 |
74 | ///////////////////////////////////////////////////////////////////////////////////////////////
75 | // #2 Listen for specific parameter value
76 | final SpecificPluginDevice specificDevice = device.createSpecificVst3Device(NEOVERB_ID);
77 | final Parameter dryWetParameter = specificDevice.createParameter(2);
78 | dryWetParameter.displayedValue().addValueObserver(value -> host.println("Dry/Wet: " + value));
79 |
80 | ///////////////////////////////////////////////////////////////////////////////////////////////
81 | // #3 Insert a specific device
82 | final DocumentState documentState = host.getDocumentState();
83 | documentState.getSignalSetting("Insert NeoVerb", "Add", "Insert").addSignalObserver(() ->
84 | {
85 | final InsertionPoint startOfDeviceChainInsertionPoint = cursorTrack.startOfDeviceChainInsertionPoint ();
86 |
87 | // Crashes Bitwig 3.3.1
88 | // startOfDeviceChainInsertionPoint.insertVST3Device(NEOVERB_ID);
89 |
90 | // Let's insert an EQ+ instead, which works fine...
91 | startOfDeviceChainInsertionPoint.insertBitwigDevice (UUID.fromString ("e4815188-ba6f-4d14-bcfc-2dcb8f778ccb"));
92 | });
93 |
94 | }
95 |
96 |
97 | /** {@inheritDoc} */
98 | @Override
99 | public void exit()
100 | {
101 | // Not used
102 | }
103 |
104 |
105 | /** {@inheritDoc} */
106 | @Override
107 | public void flush()
108 | {
109 | // Not used
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Part-19/API12-Test/src/main/java/de/mossggraber/apitwelve/API12TestExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossggraber.apitwelve;
24 |
25 | import java.util.UUID;
26 |
27 | import com.bitwig.extension.api.PlatformType;
28 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
29 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
30 | import com.bitwig.extension.controller.api.ControllerHost;
31 |
32 |
33 | public class API12TestExtensionDefinition extends ControllerExtensionDefinition
34 | {
35 | private static final UUID DRIVER_ID = UUID.fromString("e9a88dc4-25d3-4b3e-ac96-41cad164c3c2");
36 |
37 | public API12TestExtensionDefinition()
38 | {
39 | }
40 |
41 | @Override
42 | public String getName()
43 | {
44 | return "API12-Test";
45 | }
46 |
47 | @Override
48 | public String getAuthor()
49 | {
50 | return "moss";
51 | }
52 |
53 | @Override
54 | public String getVersion()
55 | {
56 | return "1.0";
57 | }
58 |
59 | @Override
60 | public UUID getId()
61 | {
62 | return DRIVER_ID;
63 | }
64 |
65 | @Override
66 | public String getHardwareVendor()
67 | {
68 | return "moss";
69 | }
70 |
71 | @Override
72 | public String getHardwareModel()
73 | {
74 | return "API12-Test";
75 | }
76 |
77 | @Override
78 | public int getRequiredAPIVersion()
79 | {
80 | return 12;
81 | }
82 |
83 | @Override
84 | public int getNumMidiInPorts()
85 | {
86 | return 0;
87 | }
88 |
89 | @Override
90 | public int getNumMidiOutPorts()
91 | {
92 | return 0;
93 | }
94 |
95 | @Override
96 | public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
97 | {
98 | }
99 |
100 | @Override
101 | public API12TestExtension createInstance(final ControllerHost host)
102 | {
103 | return new API12TestExtension(this, host);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Part-19/API12-Test/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | de.mossggraber.apitwelve.API12TestExtensionDefinition
--------------------------------------------------------------------------------
/Part-2/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
27 |
28 | function init()
29 | {
30 | println("MOXF initialized!");
31 | }
32 |
33 | function flush()
34 | {
35 | println ("Flush called.");
36 | }
37 |
38 | function exit()
39 | {
40 | println("Exited!");
41 | }
--------------------------------------------------------------------------------
/Part-22/API16-Demo.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(16);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("moss", "API16-Demo", "0.1", "85661779-961a-45ab-8312-130f7c81ce63", "moss");
27 |
28 | var cursorTrack;
29 | var slotBank;
30 | var cursorDevice;
31 | var controlsPage;
32 | var parameter;
33 |
34 | function init ()
35 | {
36 | cursorTrack = host.createCursorTrack ("APITEST_CURSOR_TRACK", "Cursor Track", 0, 8, true);
37 | slotBank = cursorTrack.clipLauncherSlotBank ();
38 |
39 | cursorDevice = cursorTrack.createCursorDevice ("APITEST_CURSOR_DEVICE", "Cursor Device", 0, CursorDeviceFollowMode.FOLLOW_SELECTION);
40 | controlsPage = cursorDevice.createCursorRemoteControlsPage (8);
41 | parameter = controlsPage.getParameter (0);
42 |
43 | println ("API16-Demo initialized.");
44 | }
45 |
46 | function flush ()
47 | {
48 | // Not used
49 | }
50 |
51 | function exit ()
52 | {
53 | // Not used
54 | }
55 |
56 | function launchWithOptions (slotIndex)
57 | {
58 | var slot = slotBank.getItemAt (slotIndex);
59 | slot.launchWithOptions ("1", "play_with_quantization");
60 | }
61 |
62 | function launchLastClipWithOptions ()
63 | {
64 | cursorTrack.launchLastClipWithOptions ("none", "continue_immediately");
65 | }
66 |
67 | function createPresetPage ()
68 | {
69 | controlsPage.createPresetPage ();
70 | }
71 |
72 | function activateMapping ()
73 | {
74 | parameter.isBeingMapped ().set (true);
75 | }
76 |
77 | function removeMapping ()
78 | {
79 | parameter.deleteObject ();
80 | }
--------------------------------------------------------------------------------
/Part-24/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.mossgraber
6 | api20test
7 | jar
8 | API20Test
9 | 0.1
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 20
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.8.0
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/API20Test.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-24/src/main/java/com/mossgraber/API20TestExtension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.mossgraber;
24 |
25 | import com.bitwig.extension.controller.*;
26 | import com.bitwig.extension.controller.api.*;
27 |
28 | public class API20TestExtension extends ControllerExtension
29 | {
30 | protected API20TestExtension(final API20TestExtensionDefinition definition, final ControllerHost host)
31 | {
32 | super(definition, host);
33 | }
34 |
35 | @Override
36 | public void init()
37 | {
38 | // Uncomment the methods you want to test
39 | // this.testLastClickedParameter ();
40 | this.testMasterRecorder ();
41 | // this.testNewParameterValues ();
42 | }
43 |
44 |
45 | private void testLastClickedParameter ()
46 | {
47 | final ControllerHost host = getHost();
48 |
49 | final LastClickedParameter lcp = host.createLastClickedParameter("MyLastClickedParam", "Last Clicked 1");
50 | final Parameter lastParameter = lcp.parameter();
51 |
52 | lastParameter.name().addValueObserver(name -> host.println("Last Clicked - Name: '" + name + "'"));
53 | lastParameter.discreteValueCount().addValueObserver(discrete -> host.println("Last Clicked - Discrete: " + discrete));
54 | lastParameter.getOrigin().addValueObserver(origin -> host.println("Last Clicked - Origin: " + origin));
55 |
56 | final ColorValue cv = lcp.parameterColor();
57 | cv.addValueObserver((red, green, blue) -> host.println("Last Clicked - Color: '" + red + ":" + green + ":" + blue));
58 |
59 | // Not implemented:
60 | // lcp.updateLockedToLastClicked();
61 |
62 | lcp.isLocked().addValueObserver(isLocked -> host.println("Is locked: " + isLocked));
63 | // Bitwig seems to remember the last locked parameter on restart, if it was locked and then does not release it!
64 | lcp.isLocked().set(false);
65 |
66 | host.scheduleTask (() -> {
67 |
68 | lcp.isLocked().set(true);
69 | host.println("Now locked to: " + lastParameter.name().get());
70 |
71 | host.scheduleTask (() -> lcp.isLocked().set(false), 10000);
72 |
73 | }, 10000);
74 | }
75 |
76 | private void testMasterRecorder ()
77 | {
78 | final ControllerHost host = getHost();
79 | final MasterRecorder recorder = host.createMasterRecorder();
80 |
81 | recorder.isActive().addValueObserver(active ->
82 | {
83 | host.println ("Master recorder: " + (active ? "Recording..." : "Stopped."));
84 | });
85 |
86 | recorder.duration().addValueObserver(milliseconds ->
87 | {
88 | long hours = (milliseconds / 3600000);
89 | long minutes = (milliseconds % 3600000) / 60000;
90 | long seconds = (milliseconds % 60000) / 1000;
91 | long millis = milliseconds % 1000;
92 | host.println (String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis));
93 | });
94 |
95 | host.scheduleTask (() -> {
96 |
97 | host.println ("Starting Master recording...");
98 | recorder.start();
99 |
100 | host.scheduleTask (() -> {
101 |
102 | host.println ("Stopping Master recording...");
103 | recorder.stop();
104 |
105 | }, 10000);
106 |
107 | }, 10000);
108 | }
109 |
110 | private void testNewParameterValues ()
111 | {
112 | final ControllerHost host = getHost();
113 | final Project project = host.getProject ();
114 | final Track rootTrackGroup = project.getRootTrackGroup ();
115 | final CursorRemoteControlsPage remoteControlsPage = rootTrackGroup.createCursorRemoteControlsPage (1);
116 | final RemoteControl parameter = remoteControlsPage.getParameter(0);
117 | parameter.name().addValueObserver(name -> host.println("Param 1 - Name: " + name));
118 | parameter.discreteValueCount().addValueObserver(discrete -> host.println("Param 1 - Discrete: " + discrete));
119 | parameter.getOrigin().addValueObserver(origin -> host.println("Param 1 - Origin: " + origin));
120 | }
121 |
122 | @Override
123 | public void exit()
124 | {
125 | }
126 |
127 | @Override
128 | public void flush()
129 | {
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Part-24/src/main/java/com/mossgraber/API20TestExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package com.mossgraber;
24 | import java.util.UUID;
25 |
26 | import com.bitwig.extension.api.PlatformType;
27 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
28 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
29 | import com.bitwig.extension.controller.api.ControllerHost;
30 |
31 | public class API20TestExtensionDefinition extends ControllerExtensionDefinition
32 | {
33 | private static final UUID DRIVER_ID = UUID.fromString("0213ac5d-112a-46eb-a6b7-930ca30c25e6");
34 |
35 | public API20TestExtensionDefinition()
36 | {
37 | }
38 |
39 | @Override
40 | public String getName()
41 | {
42 | return "API20Test";
43 | }
44 |
45 | @Override
46 | public String getAuthor()
47 | {
48 | return "moss";
49 | }
50 |
51 | @Override
52 | public String getVersion()
53 | {
54 | return "0.1";
55 | }
56 |
57 | @Override
58 | public UUID getId()
59 | {
60 | return DRIVER_ID;
61 | }
62 |
63 | @Override
64 | public String getHardwareVendor()
65 | {
66 | return "moss";
67 | }
68 |
69 | @Override
70 | public String getHardwareModel()
71 | {
72 | return "API20Test";
73 | }
74 |
75 | @Override
76 | public int getRequiredAPIVersion()
77 | {
78 | return 20;
79 | }
80 |
81 | @Override
82 | public int getNumMidiInPorts()
83 | {
84 | return 0;
85 | }
86 |
87 | @Override
88 | public int getNumMidiOutPorts()
89 | {
90 | return 0;
91 | }
92 |
93 | @Override
94 | public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
95 | {
96 | }
97 |
98 | @Override
99 | public API20TestExtension createInstance(final ControllerHost host)
100 | {
101 | return new API20TestExtension(this, host);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Part-24/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | com.mossgraber.API20TestExtensionDefinition
--------------------------------------------------------------------------------
/Part-25/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | de.mossgrabers.remote
6 | remotecursortest
7 | jar
8 | RemoteCursorTest
9 | 0.1
10 |
11 |
12 |
13 | bitwig
14 | Bitwig Maven Repository
15 | https://maven.bitwig.com
16 |
17 |
18 |
19 |
20 |
21 | com.bitwig
22 | extension-api
23 | 20
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-compiler-plugin
32 | 3.8.0
33 |
34 | true
35 | true
36 | 1.8
37 | 1.8
38 | UTF-8
39 | 1024m
40 |
41 |
42 |
43 |
44 | com.coderplus.maven.plugins
45 | copy-rename-maven-plugin
46 | 1.0
47 |
48 |
49 | rename-file
50 | install
51 |
52 | copy
53 |
54 |
55 | ${project.build.directory}/${project.build.finalName}.jar
56 | ${project.build.directory}/RemoteCursorTest.bwextension
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Part-25/src/main/java/de/mossgrabers/remote/RemoteCursorTestExtension.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.remote;
24 |
25 | import com.bitwig.extension.controller.*;
26 | import com.bitwig.extension.controller.api.*;
27 |
28 |
29 | public class RemoteCursorTestExtension extends ControllerExtension
30 | {
31 | ControllerHost host;
32 |
33 |
34 | protected RemoteCursorTestExtension (final RemoteCursorTestExtensionDefinition definition, final ControllerHost host)
35 | {
36 | super (definition, host);
37 | }
38 |
39 | @Override
40 | public void init ()
41 | {
42 | this.host = getHost ();
43 | final CursorTrack cursorTrack = host.createCursorTrack ("MyCursorTrackID", "The Cursor Track", 0, 0, true);
44 | final PinnableCursorDevice cursorDevice = cursorTrack.createCursorDevice ("CURSOR_DEVICE", "Cursor device", 0, CursorDeviceFollowMode.FOLLOW_SELECTION);
45 |
46 | final int numMonitoredPages = 8;
47 |
48 | for (int i = 0; i < numMonitoredPages; i++)
49 | {
50 | final int page = i;
51 | final CursorRemoteControlsPage remoteControls = cursorDevice.createCursorRemoteControlsPage ("Page " + page, 8, "");
52 | remoteControls.pageCount ().addValueObserver(newValue -> readjust (remoteControls, page), -1);
53 | remoteControls.selectedPageIndex ().addValueObserver(newValue -> readjust (remoteControls, page), -1);
54 | final RemoteControl control = remoteControls.getParameter (0);
55 | control.name ().addValueObserver (name -> notifyParamName (name, remoteControls, page));
56 | }
57 |
58 | host.showPopupNotification("RemoteCursorTest Initialized");
59 | }
60 |
61 |
62 | private void notifyParamName (final String name, final CursorRemoteControlsPage remoteControls, final int page)
63 | {
64 | if (page >= remoteControls.pageCount().get())
65 | host.println ("Page " + page + " Param 1: Invalid (no page " + page + ")");
66 | else
67 | host.println ("Page " + page + " Param 1: " + name);
68 | }
69 |
70 |
71 | private void readjust (final CursorRemoteControlsPage remoteControls, final int page)
72 | {
73 | if (page >= remoteControls.pageCount ().get ())
74 | {
75 | host.println ("Page " + page + " is out of range.");
76 | return;
77 | }
78 |
79 | if (remoteControls.selectedPageIndex ().get () == page)
80 | {
81 | host.println ("Page " + page + " OK.");
82 | return;
83 | }
84 |
85 | host.println ("Reselecting Page " + page + "...");
86 | host.scheduleTask (() ->
87 | {
88 | remoteControls.selectedPageIndex ().set (page);
89 | }, 500);
90 |
91 | }
92 |
93 | @Override
94 | public void exit ()
95 | {
96 | getHost().showPopupNotification ("RemoteCursorTest Exited");
97 | }
98 |
99 | @Override
100 | public void flush ()
101 | {
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Part-25/src/main/java/de/mossgrabers/remote/RemoteCursorTestExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | package de.mossgrabers.remote;
24 | import java.util.UUID;
25 |
26 | import com.bitwig.extension.api.PlatformType;
27 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
28 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
29 | import com.bitwig.extension.controller.api.ControllerHost;
30 |
31 |
32 | public class RemoteCursorTestExtensionDefinition extends ControllerExtensionDefinition
33 | {
34 | private static final UUID DRIVER_ID = UUID.fromString ("5a418561-6058-412f-aa7f-b9d8e318269d");
35 |
36 | public RemoteCursorTestExtensionDefinition ()
37 | {
38 | }
39 |
40 | @Override
41 | public String getName ()
42 | {
43 | return "RemoteCursorTest";
44 | }
45 |
46 | @Override
47 | public String getAuthor ()
48 | {
49 | return "moss";
50 | }
51 |
52 | @Override
53 | public String getVersion ()
54 | {
55 | return "0.1";
56 | }
57 |
58 | @Override
59 | public UUID getId ()
60 | {
61 | return DRIVER_ID;
62 | }
63 |
64 | @Override
65 | public String getHardwareVendor ()
66 | {
67 | return "moss";
68 | }
69 |
70 | @Override
71 | public String getHardwareModel ()
72 | {
73 | return "RemoteCursorTest";
74 | }
75 |
76 | @Override
77 | public int getRequiredAPIVersion ()
78 | {
79 | return 20;
80 | }
81 |
82 | @Override
83 | public int getNumMidiInPorts ()
84 | {
85 | return 0;
86 | }
87 |
88 | @Override
89 | public int getNumMidiOutPorts ()
90 | {
91 | return 0;
92 | }
93 |
94 | @Override
95 | public void listAutoDetectionMidiPortNames (final AutoDetectionMidiPortNamesList list, final PlatformType platformType)
96 | {
97 | }
98 |
99 | @Override
100 | public RemoteCursorTestExtension createInstance (final ControllerHost host)
101 | {
102 | return new RemoteCursorTestExtension (this, host);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Part-25/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | de.mossgrabers.remote.RemoteCursorTestExtensionDefinition
--------------------------------------------------------------------------------
/Part-3/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
27 | host.defineMidiPorts(1, 1);
28 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
29 |
30 |
31 | function init()
32 | {
33 | var inputPort = host.getMidiInPort(0);
34 | inputPort.setMidiCallback(onMidi0);
35 |
36 | println("MOXF initialized!");
37 | }
38 |
39 | function onMidi0(status, data1, data2)
40 | {
41 | printMidi(status, data1, data2);
42 | }
43 |
44 | function flush()
45 | {
46 | // println("Flush called.");
47 | }
48 |
49 | function exit()
50 | {
51 | println("Exited!");
52 | }
--------------------------------------------------------------------------------
/Part-3/OutputExamples.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Output examples
24 |
25 | host.println("Hello World!");
26 |
27 | host.errorln("Hello World!");
28 |
29 | errorln("Hello World!");
30 |
31 |
32 |
33 | function sayHello ()
34 | {
35 | host.showPopupNotification("Hello World!");
36 | }
37 |
--------------------------------------------------------------------------------
/Part-4/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 |
25 | host.setShouldFailOnDeprecatedUse(true);
26 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
27 | host.defineMidiPorts(1, 1);
28 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
29 |
30 | const MOXF_BUTTON_PLAY = 0x5E;
31 | const MOXF_BUTTON_STOP = 0x5D;
32 | const MOXF_BUTTON_REC = 0x5F;
33 | const MOXF_BUTTON_FWD = 0x5C;
34 | const MOXF_BUTTON_BACK = 0x5B;
35 | const MOXF_BUTTON_LOCATE = 0x58;
36 |
37 | var transport = null;
38 |
39 |
40 | function init()
41 | {
42 | var inputPort = host.getMidiInPort(0);
43 | inputPort.setMidiCallback(onMidi0);
44 |
45 | noteIn = inputPort.createNoteInput("Yamaha MOXF", "8?????", "9?????", "B?01??", "B?40??", "E?????");
46 |
47 | var noteMap = initArray (-1, 128);
48 | for (i = 0; i < 128; i++)
49 | noteMap[i] = 127 - i;
50 | noteIn.setKeyTranslationTable (noteMap);
51 |
52 | transport = host.createTransport ();
53 |
54 | println("MOXF initialized!");
55 | }
56 |
57 | function onMidi0(status, data1, data2)
58 | {
59 | if (isNoteOn(status) && data2 == 127)
60 | {
61 | switch (data1)
62 | {
63 | case MOXF_BUTTON_PLAY:
64 | transport.play ();
65 | break;
66 |
67 | case MOXF_BUTTON_STOP:
68 | transport.stop ();
69 | break;
70 |
71 | case MOXF_BUTTON_REC:
72 | transport.record ();
73 | break;
74 |
75 | case MOXF_BUTTON_FWD:
76 | transport.fastForward ();
77 | break;
78 |
79 | case MOXF_BUTTON_BACK:
80 | transport.rewind ();
81 | break;
82 |
83 | case MOXF_BUTTON_LOCATE:
84 | transport.tapTempo ();
85 | break;
86 |
87 | default:
88 | host.errorln ("Command " + data1 + " is not supported.");
89 | break;
90 | }
91 | }
92 |
93 | }
94 |
95 | function flush()
96 | {
97 | // println("Flush called.");
98 | }
99 |
100 | function exit()
101 | {
102 | println("Exited!");
103 | }
--------------------------------------------------------------------------------
/Part-5/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 | load ("MOXFHardware.js");
25 |
26 | host.setShouldFailOnDeprecatedUse(true);
27 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
28 | host.defineMidiPorts(1, 1);
29 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
30 |
31 | var hardware = null;
32 | var transport = null;
33 |
34 |
35 | function init ()
36 | {
37 | hardware = new MOXFHardware (host.getMidiOutPort (0), host.getMidiInPort (0), onMidi0);
38 |
39 | transport = host.createTransport ();
40 |
41 | transport.isPlaying ().addValueObserver (function (value)
42 | {
43 | println (value ? "Playing..." : "Stopped.");
44 | });
45 |
46 | transport.isPlaying ().markInterested ();
47 |
48 | println ("MOXF initialized!");
49 | }
50 |
51 | function onMidi0(status, data1, data2)
52 | {
53 | if (isNoteOn(status) && data2 == 127)
54 | {
55 | switch (data1)
56 | {
57 | case MOXF_BUTTON_PLAY:
58 | transport.play ();
59 | break;
60 |
61 | case MOXF_BUTTON_STOP:
62 | transport.stop ();
63 | break;
64 |
65 | case MOXF_BUTTON_REC:
66 | transport.record ();
67 | break;
68 |
69 | case MOXF_BUTTON_FWD:
70 | transport.fastForward ();
71 | break;
72 |
73 | case MOXF_BUTTON_BACK:
74 | transport.rewind ();
75 | break;
76 |
77 | case MOXF_BUTTON_LOCATE:
78 | transport.tapTempo ();
79 | break;
80 |
81 | default:
82 | host.errorln ("Command " + data1 + " is not supported.");
83 | break;
84 | }
85 | }
86 | }
87 |
88 | function flush()
89 | {
90 | var isPlaying = transport.isPlaying ().get ();
91 | hardware.updateLED (MOXF_BUTTON_PLAY, isPlaying);
92 | }
93 |
94 | function exit()
95 | {
96 | println ("Exited!");
97 | }
--------------------------------------------------------------------------------
/Part-5/MOXFHardware.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | const MOXF_BUTTON_PLAY = 0x5E;
24 | const MOXF_BUTTON_STOP = 0x5D;
25 | const MOXF_BUTTON_REC = 0x5F;
26 | const MOXF_BUTTON_FWD = 0x5C;
27 | const MOXF_BUTTON_BACK = 0x5B;
28 | const MOXF_BUTTON_LOCATE = 0x58;
29 |
30 |
31 | function MOXFHardware (outputPort, inputPort, inputCallback)
32 | {
33 | this.portOut = outputPort;
34 | this.portIn = inputPort;
35 |
36 | this.ledCache = initArray (-1, 128);
37 |
38 | this.portIn.setMidiCallback (inputCallback);
39 | this.noteIn = this.portIn.createNoteInput ("Yamaha MOXF", "91????", "81????", "B101??", "B140??", "E1????");
40 |
41 | // --- Example for inverting the keyboard
42 | //
43 | // var noteMap = initArray (-1, 128);
44 | // for (i = 0; i < 128; i++)
45 | // noteMap[i] = 127 - i;
46 | // this.noteIn.setKeyTranslationTable (noteMap);
47 | }
48 |
49 | MOXFHardware.prototype.updateLED = function (note, isOn)
50 | {
51 | var value = isOn ? 127 : 0;
52 | if (this.ledCache[note] != value)
53 | {
54 | this.ledCache[note] = value;
55 | this.portOut.sendMidi (0x90, note, value);
56 | println ("Updated to " + this.ledCache[note]);
57 | }
58 | else
59 | println ("Not updated.");
60 | }
61 |
--------------------------------------------------------------------------------
/Part-6/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 | load ("MOXFHardware.js");
25 | load ("TransportHandler.js");
26 | load ("TrackHandler.js");
27 |
28 | host.setShouldFailOnDeprecatedUse(true);
29 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
30 | host.defineMidiPorts(1, 1);
31 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
32 |
33 | var hardware = null;
34 | var transportHandler = null;
35 | var trackHandler = null;
36 |
37 |
38 | function init ()
39 | {
40 | hardware = new MOXFHardware (host.getMidiOutPort (0), host.getMidiInPort (0), handleMidi);
41 | transportHandler = new TransportHandler (host.createTransport ());
42 | trackHandler = new TrackHandler (host.createMainTrackBank (4, 0, 0), host.createCursorTrack ("MOXF_CURSOR_TRACK", "Cursor Track", 0, 0, true));
43 |
44 | println ("MOXF initialized!");
45 | }
46 |
47 | function handleMidi (status, data1, data2)
48 | {
49 | if (transportHandler.handleMidi (status, data1, data2))
50 | return;
51 |
52 | if (trackHandler.handleMidi (status, data1, data2))
53 | return;
54 |
55 | host.errorln ("Midi command not processed: " + status + " : " + data1);
56 | }
57 |
58 | function flush ()
59 | {
60 | transportHandler.updateLEDs ();
61 | }
62 |
63 | function exit ()
64 | {
65 | println ("Exited!");
66 | }
--------------------------------------------------------------------------------
/Part-6/MOXFHardware.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Buttons
24 | const MOXF_BUTTON_SOLO = 0x08;
25 | const MOXF_BUTTON_MUTE = 0x10;
26 |
27 | const MOXF_BUTTON_F1 = 0x36;
28 | const MOXF_BUTTON_F2 = 0x37;
29 | const MOXF_BUTTON_F3 = 0x38;
30 | const MOXF_BUTTON_F4 = 0x39;
31 | const MOXF_BUTTON_F5 = 0x3A;
32 | const MOXF_BUTTON_F6 = 0x3B;
33 |
34 | const MOXF_BUTTON_SF1 = 0x3C;
35 | const MOXF_BUTTON_SF2 = 0x3D;
36 | const MOXF_BUTTON_SF3 = 0x3E;
37 | const MOXF_BUTTON_SF4 = 0x3F;
38 | const MOXF_BUTTON_SF5 = 0x40;
39 | const MOXF_BUTTON_SF6 = 0x41;
40 |
41 | const MOXF_BUTTON_A = 0x42;
42 | const MOXF_BUTTON_B = 0x43;
43 | const MOXF_BUTTON_C = 0x44;
44 | const MOXF_BUTTON_D = 0x45;
45 | const MOXF_BUTTON_E = 0x46;
46 | const MOXF_BUTTON_F = 0x47;
47 |
48 | const MOXF_BUTTON_PLAY = 0x5E;
49 | const MOXF_BUTTON_STOP = 0x5D;
50 | const MOXF_BUTTON_REC = 0x5F;
51 | const MOXF_BUTTON_FWD = 0x5C;
52 | const MOXF_BUTTON_BACK = 0x5B;
53 | const MOXF_BUTTON_LOCATE = 0x58;
54 |
55 | const MOXF_BUTTON_CATEGORY = 0x76;
56 | const MOXF_BUTTON_FAVORITE = 0x77;
57 |
58 | // Knobs
59 | const MOXF_KNOB_MAIN = 0x3C;
60 | const MOXF_KNOB_1 = 0x4A;
61 | const MOXF_KNOB_2 = 0x47;
62 | const MOXF_KNOB_3 = 0x49;
63 | const MOXF_KNOB_4 = 0x48;
64 | const MOXF_KNOB_5 = 0x1C;
65 | const MOXF_KNOB_6 = 0x1D;
66 | const MOXF_KNOB_7 = 0x1E;
67 | const MOXF_KNOB_8 = 0x1F;
68 |
69 |
70 | function MOXFHardware (outputPort, inputPort, inputCallback)
71 | {
72 | this.portOut = outputPort;
73 | this.portIn = inputPort;
74 |
75 | this.ledCache = initArray (-1, 128);
76 |
77 | this.portIn.setMidiCallback (inputCallback);
78 | this.noteIn = this.portIn.createNoteInput ("Yamaha MOXF", "91????", "81????", "B101??", "B140??", "E1????");
79 |
80 | // --- Example for inverting the keyboard
81 | //
82 | // var noteMap = initArray (-1, 128);
83 | // for (i = 0; i < 128; i++)
84 | // noteMap[i] = 127 - i;
85 | // this.noteIn.setKeyTranslationTable (noteMap);
86 | }
87 |
88 | MOXFHardware.prototype.updateLED = function (note, isOn)
89 | {
90 | var value = isOn ? 127 : 0;
91 | if (this.ledCache[note] == value)
92 | return;
93 | this.ledCache[note] = value;
94 | this.portOut.sendMidi (0x90, note, value);
95 | }
96 |
--------------------------------------------------------------------------------
/Part-6/TrackHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TrackHandler (trackbank, cursorTrack)
24 | {
25 | this.trackbank = trackbank;
26 | this.cursorTrack = cursorTrack;
27 |
28 | for (i = 0; i < this.trackbank.getSizeOfBank (); i++)
29 | {
30 | var track = this.trackbank.getItemAt (i);
31 |
32 | var p = track.pan ();
33 | p.markInterested ();
34 | p.setIndication (true);
35 |
36 | p = track.volume ();
37 | p.markInterested ();
38 | p.setIndication (true);
39 | }
40 |
41 | this.trackbank.followCursorTrack (this.cursorTrack);
42 |
43 | this.cursorTrack.solo ().markInterested ();
44 | this.cursorTrack.mute ().markInterested ();
45 | }
46 |
47 | TrackHandler.prototype.handleMidi = function (status, data1, data2)
48 | {
49 | if (isNoteOn(status))
50 | {
51 | switch (data1)
52 | {
53 | case MOXF_BUTTON_SF1:
54 | this.trackbank.getItemAt (0).select ();
55 | return true;
56 |
57 | case MOXF_BUTTON_SF2:
58 | this.trackbank.getItemAt (1).select ();
59 | return true;
60 |
61 | case MOXF_BUTTON_SF3:
62 | this.trackbank.getItemAt (2).select ();
63 | return true;
64 |
65 | case MOXF_BUTTON_SF4:
66 | this.trackbank.getItemAt (3).select ();
67 | return true;
68 |
69 | case MOXF_BUTTON_SF5:
70 | this.trackbank.scrollPageBackwards ();
71 | return true;
72 |
73 | case MOXF_BUTTON_SF6:
74 | this.trackbank.scrollPageForwards ();
75 | return true;
76 |
77 | case MOXF_BUTTON_SOLO:
78 | this.cursorTrack.solo ().toggle ();
79 | return true;
80 |
81 | case MOXF_BUTTON_MUTE:
82 | this.cursorTrack.mute ().toggle ();
83 | return true;
84 |
85 | default:
86 | return false;
87 | }
88 | }
89 |
90 | if (isChannelController(status))
91 | {
92 | switch (data1)
93 | {
94 | // Absolute values
95 | case MOXF_KNOB_1:
96 | this.trackbank.getItemAt (0).pan ().set (data2, 128);
97 | return true;
98 |
99 | case MOXF_KNOB_2:
100 | this.trackbank.getItemAt (1).pan ().set (data2, 128);
101 | return true;
102 |
103 | case MOXF_KNOB_3:
104 | this.trackbank.getItemAt (2).pan ().set (data2, 128);
105 | return true;
106 |
107 | case MOXF_KNOB_4:
108 | this.trackbank.getItemAt (3).pan ().set (data2, 128);
109 | return true;
110 |
111 | // Relative values
112 | case MOXF_KNOB_5:
113 | var value = data2 > 64 ? 64 - data2 : data2;
114 | this.trackbank.getItemAt (0).volume ().inc (value, 128);
115 | return true;
116 |
117 | case MOXF_KNOB_6:
118 | var value = data2 > 64 ? 64 - data2 : data2;
119 | this.trackbank.getItemAt (1).volume ().inc (value, 128);
120 | return true;
121 |
122 | case MOXF_KNOB_7:
123 | var value = data2 > 64 ? 64 - data2 : data2;
124 | this.trackbank.getItemAt (2).volume ().inc (value, 128);
125 | return true;
126 |
127 | case MOXF_KNOB_8:
128 | var value = data2 > 64 ? 64 - data2 : data2;
129 | this.trackbank.getItemAt (3).volume ().inc (value, 128);
130 | return true;
131 |
132 | default:
133 | return false;
134 | }
135 | }
136 |
137 | return false;
138 | }
139 |
--------------------------------------------------------------------------------
/Part-6/TransportHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TransportHandler (transport)
24 | {
25 | this.transport = transport;
26 |
27 | // --- Examplefor using value observers
28 | // transport.isPlaying ().addValueObserver (function (value)
29 | // {
30 | // println (value ? "Playing..." : "Stopped.");
31 | // });
32 |
33 | this.transport.isPlaying ().markInterested ();
34 | this.transport.isArrangerRecordEnabled ().markInterested ();
35 | }
36 |
37 | TransportHandler.prototype.handleMidi = function (status, data1, data2)
38 | {
39 | if (!isNoteOn(status))
40 | return false;
41 |
42 | if (data2 == 0)
43 | return true;
44 |
45 | switch (data1)
46 | {
47 | case MOXF_BUTTON_PLAY:
48 | this.transport.play ();
49 | return true;
50 |
51 | case MOXF_BUTTON_STOP:
52 | this.transport.stop ();
53 | return true;
54 |
55 | case MOXF_BUTTON_REC:
56 | this.transport.record ();
57 | return true;
58 |
59 | case MOXF_BUTTON_FWD:
60 | this.transport.fastForward ();
61 | return true;
62 |
63 | case MOXF_BUTTON_BACK:
64 | this.transport.rewind ();
65 | return true;
66 |
67 | case MOXF_BUTTON_LOCATE:
68 | this.transport.tapTempo ();
69 | return true;
70 |
71 | default:
72 | return false;
73 | }
74 | }
75 |
76 | TransportHandler.prototype.updateLEDs = function ()
77 | {
78 | hardware.updateLED (MOXF_BUTTON_PLAY, this.transport.isPlaying ().get ());
79 | hardware.updateLED (MOXF_BUTTON_REC, this.transport.isArrangerRecordEnabled ().get ());
80 | }
--------------------------------------------------------------------------------
/Part-7/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 | load ("MOXFHardware.js");
25 | load ("TransportHandler.js");
26 | load ("TrackHandler.js");
27 | load ("RemoteControlHandler.js");
28 | load ("ModeHandler.js");
29 |
30 | host.setShouldFailOnDeprecatedUse(true);
31 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
32 | host.defineMidiPorts(1, 1);
33 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
34 |
35 | var hardware = null;
36 | var transportHandler = null;
37 | var modeHandler = null;
38 |
39 |
40 | function init ()
41 | {
42 | hardware = new MOXFHardware (host.getMidiOutPort (0), host.getMidiInPort (0), handleMidi);
43 | transportHandler = new TransportHandler (host.createTransport ());
44 |
45 | var cursorTrack = host.createCursorTrack ("MOXF_CURSOR_TRACK", "Cursor Track", 0, 0, true);
46 | var trackHandler = new TrackHandler (host.createMainTrackBank (4, 0, 0), cursorTrack);
47 |
48 | var cursorDevice = cursorTrack.createCursorDevice ("MOXF_CURSOR_DEVICE", "Cursor Device", 0, CursorDeviceFollowMode.FOLLOW_SELECTION);
49 | var remoteControlHandler = new RemoteControlHandler (cursorDevice, cursorDevice.createCursorRemoteControlsPage (8));
50 |
51 | var modes = [ trackHandler, remoteControlHandler ];
52 | modeHandler = new ModeHandler (modes);
53 |
54 | println ("MOXF initialized!");
55 | }
56 |
57 | function handleMidi (status, data1, data2)
58 | {
59 | if (transportHandler.handleMidi (status, data1, data2))
60 | return;
61 |
62 | if (modeHandler.handleMidi (status, data1, data2))
63 | return;
64 |
65 | host.errorln ("Midi command not processed: " + status + " : " + data1);
66 | }
67 |
68 | function flush ()
69 | {
70 | transportHandler.updateLEDs ();
71 | }
72 |
73 | function exit ()
74 | {
75 | println ("Exited!");
76 | }
--------------------------------------------------------------------------------
/Part-7/MOXFHardware.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Buttons
24 | const MOXF_BUTTON_SOLO = 0x08;
25 | const MOXF_BUTTON_MUTE = 0x10;
26 |
27 | const MOXF_BUTTON_F1 = 0x36;
28 | const MOXF_BUTTON_F2 = 0x37;
29 | const MOXF_BUTTON_F3 = 0x38;
30 | const MOXF_BUTTON_F4 = 0x39;
31 | const MOXF_BUTTON_F5 = 0x3A;
32 | const MOXF_BUTTON_F6 = 0x3B;
33 |
34 | const MOXF_BUTTON_SF1 = 0x3C;
35 | const MOXF_BUTTON_SF2 = 0x3D;
36 | const MOXF_BUTTON_SF3 = 0x3E;
37 | const MOXF_BUTTON_SF4 = 0x3F;
38 | const MOXF_BUTTON_SF5 = 0x40;
39 | const MOXF_BUTTON_SF6 = 0x41;
40 |
41 | const MOXF_BUTTON_A = 0x42;
42 | const MOXF_BUTTON_B = 0x43;
43 | const MOXF_BUTTON_C = 0x44;
44 | const MOXF_BUTTON_D = 0x45;
45 | const MOXF_BUTTON_E = 0x46;
46 | const MOXF_BUTTON_F = 0x47;
47 |
48 | const MOXF_BUTTON_PLAY = 0x5E;
49 | const MOXF_BUTTON_STOP = 0x5D;
50 | const MOXF_BUTTON_REC = 0x5F;
51 | const MOXF_BUTTON_FWD = 0x5C;
52 | const MOXF_BUTTON_BACK = 0x5B;
53 | const MOXF_BUTTON_LOCATE = 0x58;
54 |
55 | const MOXF_BUTTON_CATEGORY = 0x76;
56 | const MOXF_BUTTON_FAVORITE = 0x77;
57 |
58 | // Knobs
59 | const MOXF_KNOB_MAIN = 0x3C;
60 | const MOXF_KNOB_1 = 0x4A;
61 | const MOXF_KNOB_2 = 0x47;
62 | const MOXF_KNOB_3 = 0x49;
63 | const MOXF_KNOB_4 = 0x48;
64 | const MOXF_KNOB_5 = 0x1C;
65 | const MOXF_KNOB_6 = 0x1D;
66 | const MOXF_KNOB_7 = 0x1E;
67 | const MOXF_KNOB_8 = 0x1F;
68 |
69 |
70 | function MOXFHardware (outputPort, inputPort, inputCallback)
71 | {
72 | this.portOut = outputPort;
73 | this.portIn = inputPort;
74 |
75 | this.ledCache = initArray (-1, 128);
76 |
77 | this.portIn.setMidiCallback (inputCallback);
78 | this.noteIn = this.portIn.createNoteInput ("Yamaha MOXF", "91????", "81????", "B101??", "B140??", "E1????");
79 |
80 | // --- Example for inverting the keyboard
81 | //
82 | // var noteMap = initArray (-1, 128);
83 | // for (i = 0; i < 128; i++)
84 | // noteMap[i] = 127 - i;
85 | // this.noteIn.setKeyTranslationTable (noteMap);
86 | }
87 |
88 | MOXFHardware.prototype.updateLED = function (note, isOn)
89 | {
90 | var value = isOn ? 127 : 0;
91 | if (this.ledCache[note] == value)
92 | return;
93 | this.ledCache[note] = value;
94 | this.portOut.sendMidi (0x90, note, value);
95 | }
96 |
--------------------------------------------------------------------------------
/Part-7/ModeHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Mode Interface:
24 | // - String getName ()
25 | // - void setIndication (boolean enable)
26 | // - void handleMidi (byte status, byte data1, byte data2)
27 |
28 | function ModeHandler (modes)
29 | {
30 | this.modes = modes;
31 | this.setActiveMode (modes[0]);
32 | }
33 |
34 | ModeHandler.prototype.setActiveMode = function (newMode)
35 | {
36 | this.activeMode = newMode;
37 | host.showPopupNotification (this.activeMode.getName ());
38 | this.updateIndication ();
39 | }
40 |
41 | ModeHandler.prototype.updateIndication = function ()
42 | {
43 | var i;
44 | for (i = 0; i < this.modes.length; i++)
45 | this.modes[i].setIndication (false);
46 | this.activeMode.setIndication (true);
47 | }
48 |
49 | ModeHandler.prototype.handleMidi = function (status, data1, data2)
50 | {
51 | if (isNoteOn(status))
52 | {
53 | switch (data1)
54 | {
55 | case MOXF_BUTTON_A:
56 | this.setActiveMode (this.modes[0]);
57 | return true;
58 |
59 | case MOXF_BUTTON_B:
60 | this.setActiveMode (this.modes[1]);
61 | return true;
62 | }
63 | }
64 |
65 | if (this.activeMode.handleMidi (status, data1, data2))
66 | return true;
67 |
68 | return false;
69 | }
70 |
--------------------------------------------------------------------------------
/Part-7/RemoteControlHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function RemoteControlHandler (cursorDevice, remoteControlsBank)
24 | {
25 | this.cursorDevice = cursorDevice;
26 | this.remoteControlsBank = remoteControlsBank;
27 |
28 | var i;
29 | for (i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
30 | this.remoteControlsBank.getParameter (i).markInterested ();
31 |
32 | this.cursorDevice.isEnabled ().markInterested ();
33 | this.cursorDevice.isWindowOpen ().markInterested ();
34 | }
35 |
36 | RemoteControlHandler.prototype.getName = function ()
37 | {
38 | return "Device Mode";
39 | }
40 |
41 | RemoteControlHandler.prototype.setIndication = function (enable)
42 | {
43 | var i;
44 | for (i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
45 | this.remoteControlsBank.getParameter (i).setIndication (enable);
46 | }
47 |
48 | RemoteControlHandler.prototype.handleMidi = function (status, data1, data2)
49 | {
50 | if (isNoteOn(status))
51 | {
52 | switch (data1)
53 | {
54 | case MOXF_BUTTON_SF1:
55 | this.cursorDevice.selectPrevious ();
56 | return true;
57 |
58 | case MOXF_BUTTON_SF2:
59 | this.cursorDevice.selectNext ();
60 | return true;
61 |
62 | case MOXF_BUTTON_SF3:
63 | // Not used
64 | return true;
65 |
66 | case MOXF_BUTTON_SF4:
67 | // Not used
68 | return true;
69 |
70 | case MOXF_BUTTON_SF5:
71 | this.remoteControlsBank.selectPrevious ();
72 | return true;
73 |
74 | case MOXF_BUTTON_SF6:
75 | this.remoteControlsBank.selectNext ();
76 | return true;
77 |
78 | case MOXF_BUTTON_SOLO:
79 | this.cursorDevice.isWindowOpen ().toggle ();
80 | return true;
81 |
82 | case MOXF_BUTTON_MUTE:
83 | this.cursorDevice.isEnabled ().toggle ();
84 | return true;
85 |
86 | default:
87 | return false;
88 | }
89 | }
90 |
91 | if (isChannelController(status))
92 | {
93 | switch (data1)
94 | {
95 | // Absolute values
96 | case MOXF_KNOB_1:
97 | this.remoteControlsBank.getParameter (0).set (data2, 128);
98 | return true;
99 |
100 | case MOXF_KNOB_2:
101 | this.remoteControlsBank.getParameter (1).set (data2, 128);
102 | return true;
103 |
104 | case MOXF_KNOB_3:
105 | this.remoteControlsBank.getParameter (2).set (data2, 128);
106 | return true;
107 |
108 | case MOXF_KNOB_4:
109 | this.remoteControlsBank.getParameter (3).set (data2, 128);
110 | return true;
111 |
112 | // Relative values
113 | case MOXF_KNOB_5:
114 | var value = data2 > 64 ? 64 - data2 : data2;
115 | this.remoteControlsBank.getParameter (4).inc (value, 128);
116 | return true;
117 |
118 | case MOXF_KNOB_6:
119 | var value = data2 > 64 ? 64 - data2 : data2;
120 | this.remoteControlsBank.getParameter (5).inc (value, 128);
121 | return true;
122 |
123 | case MOXF_KNOB_7:
124 | var value = data2 > 64 ? 64 - data2 : data2;
125 | this.remoteControlsBank.getParameter (6).inc (value, 128);
126 | return true;
127 |
128 | case MOXF_KNOB_8:
129 | var value = data2 > 64 ? 64 - data2 : data2;
130 | this.remoteControlsBank.getParameter (7).inc (value, 128);
131 | return true;
132 |
133 | default:
134 | return false;
135 | }
136 | }
137 |
138 | return false;
139 | }
140 |
--------------------------------------------------------------------------------
/Part-7/TrackHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TrackHandler (trackbank, cursorTrack)
24 | {
25 | this.trackbank = trackbank;
26 | this.cursorTrack = cursorTrack;
27 |
28 | this.trackbank.followCursorTrack (this.cursorTrack);
29 |
30 | var i;
31 | for (i = 0; i < this.trackbank.getSizeOfBank (); i++)
32 | {
33 | var track = this.trackbank.getItemAt (i);
34 | track.pan ().markInterested ();
35 | track.volume ().markInterested ();
36 | }
37 |
38 | this.cursorTrack.solo ().markInterested ();
39 | this.cursorTrack.mute ().markInterested ();
40 | }
41 |
42 | TrackHandler.prototype.getName = function ()
43 | {
44 | return "Track Mode";
45 | }
46 |
47 | TrackHandler.prototype.setIndication = function (enable)
48 | {
49 | var i;
50 | for (i = 0; i < this.trackbank.getSizeOfBank (); i++)
51 | {
52 | var track = this.trackbank.getItemAt (i);
53 | track.pan ().setIndication (enable);
54 | track.volume ().setIndication (enable);
55 | }
56 | }
57 |
58 | TrackHandler.prototype.handleMidi = function (status, data1, data2)
59 | {
60 | if (isNoteOn(status))
61 | {
62 | switch (data1)
63 | {
64 | case MOXF_BUTTON_SF1:
65 | this.trackbank.getItemAt (0).select ();
66 | return true;
67 |
68 | case MOXF_BUTTON_SF2:
69 | this.trackbank.getItemAt (1).select ();
70 | return true;
71 |
72 | case MOXF_BUTTON_SF3:
73 | this.trackbank.getItemAt (2).select ();
74 | return true;
75 |
76 | case MOXF_BUTTON_SF4:
77 | this.trackbank.getItemAt (3).select ();
78 | return true;
79 |
80 | case MOXF_BUTTON_SF5:
81 | this.trackbank.scrollPageBackwards ();
82 | return true;
83 |
84 | case MOXF_BUTTON_SF6:
85 | this.trackbank.scrollPageForwards ();
86 | return true;
87 |
88 | case MOXF_BUTTON_SOLO:
89 | this.cursorTrack.solo ().toggle ();
90 | return true;
91 |
92 | case MOXF_BUTTON_MUTE:
93 | this.cursorTrack.mute ().toggle ();
94 | return true;
95 |
96 | default:
97 | return false;
98 | }
99 | }
100 |
101 | if (isChannelController(status))
102 | {
103 | switch (data1)
104 | {
105 | // Absolute values
106 | case MOXF_KNOB_1:
107 | this.trackbank.getItemAt (0).pan ().set (data2, 128);
108 | return true;
109 |
110 | case MOXF_KNOB_2:
111 | this.trackbank.getItemAt (1).pan ().set (data2, 128);
112 | return true;
113 |
114 | case MOXF_KNOB_3:
115 | this.trackbank.getItemAt (2).pan ().set (data2, 128);
116 | return true;
117 |
118 | case MOXF_KNOB_4:
119 | this.trackbank.getItemAt (3).pan ().set (data2, 128);
120 | return true;
121 |
122 | // Relative values
123 | case MOXF_KNOB_5:
124 | var value = data2 > 64 ? 64 - data2 : data2;
125 | this.trackbank.getItemAt (0).volume ().inc (value, 128);
126 | return true;
127 |
128 | case MOXF_KNOB_6:
129 | var value = data2 > 64 ? 64 - data2 : data2;
130 | this.trackbank.getItemAt (1).volume ().inc (value, 128);
131 | return true;
132 |
133 | case MOXF_KNOB_7:
134 | var value = data2 > 64 ? 64 - data2 : data2;
135 | this.trackbank.getItemAt (2).volume ().inc (value, 128);
136 | return true;
137 |
138 | case MOXF_KNOB_8:
139 | var value = data2 > 64 ? 64 - data2 : data2;
140 | this.trackbank.getItemAt (3).volume ().inc (value, 128);
141 | return true;
142 |
143 | default:
144 | return false;
145 | }
146 | }
147 |
148 | return false;
149 | }
150 |
--------------------------------------------------------------------------------
/Part-7/TransportHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TransportHandler (transport)
24 | {
25 | this.transport = transport;
26 |
27 | // --- Examplefor using value observers
28 | // transport.isPlaying ().addValueObserver (function (value)
29 | // {
30 | // println (value ? "Playing..." : "Stopped.");
31 | // });
32 |
33 | this.transport.isPlaying ().markInterested ();
34 | this.transport.isArrangerRecordEnabled ().markInterested ();
35 | }
36 |
37 | TransportHandler.prototype.handleMidi = function (status, data1, data2)
38 | {
39 | if (!isNoteOn(status))
40 | return false;
41 |
42 | if (data2 == 0)
43 | return true;
44 |
45 | switch (data1)
46 | {
47 | case MOXF_BUTTON_PLAY:
48 | this.transport.play ();
49 | return true;
50 |
51 | case MOXF_BUTTON_STOP:
52 | this.transport.stop ();
53 | return true;
54 |
55 | case MOXF_BUTTON_REC:
56 | this.transport.record ();
57 | return true;
58 |
59 | case MOXF_BUTTON_FWD:
60 | this.transport.fastForward ();
61 | return true;
62 |
63 | case MOXF_BUTTON_BACK:
64 | this.transport.rewind ();
65 | return true;
66 |
67 | case MOXF_BUTTON_LOCATE:
68 | this.transport.tapTempo ();
69 | return true;
70 |
71 | default:
72 | return false;
73 | }
74 | }
75 |
76 | TransportHandler.prototype.updateLEDs = function ()
77 | {
78 | hardware.updateLED (MOXF_BUTTON_PLAY, this.transport.isPlaying ().get ());
79 | hardware.updateLED (MOXF_BUTTON_REC, this.transport.isArrangerRecordEnabled ().get ());
80 | }
--------------------------------------------------------------------------------
/Part-8/MOXF.control.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | loadAPI(7);
24 | load ("MOXFHardware.js");
25 | load ("TransportHandler.js");
26 | load ("TrackHandler.js");
27 | load ("RemoteControlHandler.js");
28 | load ("ModeHandler.js");
29 |
30 | host.setShouldFailOnDeprecatedUse(true);
31 | host.defineController("Yamaha", "MOXF", "0.1", "bdf1fb54-4642-41d0-9851-460bc63fcf4e", "Jürgen Moßgraber");
32 | host.defineMidiPorts(1, 1);
33 | host.addDeviceNameBasedDiscoveryPair(["MIDIIN4 (mio10)"], ["MIDIOUT4 (mio10)"]);
34 |
35 | var hardware = null;
36 | var transportHandler = null;
37 | var modeHandler = null;
38 |
39 | var MODE_OPTIONS = [ "Track", "Device" ];
40 | var BOOLEAN_OPTIONS = [ "Off", "On" ];
41 |
42 | function doObject (object, f)
43 | {
44 | return function ()
45 | {
46 | f.apply (object, arguments);
47 | };
48 | }
49 |
50 |
51 | function init ()
52 | {
53 | // Preferences
54 | var preferences = host.getPreferences ();
55 | var modeSetting = preferences.getEnumSetting ("Mode", "Global", MODE_OPTIONS, MODE_OPTIONS[0]);
56 |
57 | // Document States
58 | var documentState = host.getDocumentState ();
59 | var fixVelocityEnableSetting = documentState.getEnumSetting ("Enable", "Fix velocity", BOOLEAN_OPTIONS, BOOLEAN_OPTIONS[0]);
60 | var fixVelocityValueSetting = documentState.getNumberSetting ("Velocity", "Fix velocity", 0, 127, 1, "", 127);
61 |
62 | hardware = new MOXFHardware (host.getMidiOutPort (0), host.getMidiInPort (0), handleMidi, fixVelocityEnableSetting, fixVelocityValueSetting);
63 | transportHandler = new TransportHandler (host.createTransport ());
64 |
65 | var cursorTrack = host.createCursorTrack ("MOXF_CURSOR_TRACK", "Cursor Track", 0, 0, true);
66 | var trackHandler = new TrackHandler (host.createMainTrackBank (4, 0, 0), cursorTrack);
67 |
68 | var cursorDevice = cursorTrack.createCursorDevice ("MOXF_CURSOR_DEVICE", "Cursor Device", 0, CursorDeviceFollowMode.FOLLOW_SELECTION);
69 | var remoteControlHandler = new RemoteControlHandler (cursorDevice, cursorDevice.createCursorRemoteControlsPage (8));
70 |
71 | var modes = [ trackHandler, remoteControlHandler ];
72 | modeHandler = new ModeHandler (modes, modeSetting);
73 |
74 | println ("MOXF initialized!");
75 | }
76 |
77 | function handleMidi (status, data1, data2)
78 | {
79 | if (transportHandler.handleMidi (status, data1, data2))
80 | return;
81 |
82 | if (modeHandler.handleMidi (status, data1, data2))
83 | return;
84 |
85 | host.errorln ("Midi command not processed: " + status + " : " + data1);
86 | }
87 |
88 | function flush ()
89 | {
90 | transportHandler.updateLEDs ();
91 | }
92 |
93 | function exit ()
94 | {
95 | println ("Exited!");
96 | }
--------------------------------------------------------------------------------
/Part-8/MOXFHardware.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Buttons
24 | const MOXF_BUTTON_SOLO = 0x08;
25 | const MOXF_BUTTON_MUTE = 0x10;
26 |
27 | const MOXF_BUTTON_F1 = 0x36;
28 | const MOXF_BUTTON_F2 = 0x37;
29 | const MOXF_BUTTON_F3 = 0x38;
30 | const MOXF_BUTTON_F4 = 0x39;
31 | const MOXF_BUTTON_F5 = 0x3A;
32 | const MOXF_BUTTON_F6 = 0x3B;
33 |
34 | const MOXF_BUTTON_SF1 = 0x3C;
35 | const MOXF_BUTTON_SF2 = 0x3D;
36 | const MOXF_BUTTON_SF3 = 0x3E;
37 | const MOXF_BUTTON_SF4 = 0x3F;
38 | const MOXF_BUTTON_SF5 = 0x40;
39 | const MOXF_BUTTON_SF6 = 0x41;
40 |
41 | const MOXF_BUTTON_A = 0x42;
42 | const MOXF_BUTTON_B = 0x43;
43 | const MOXF_BUTTON_C = 0x44;
44 | const MOXF_BUTTON_D = 0x45;
45 | const MOXF_BUTTON_E = 0x46;
46 | const MOXF_BUTTON_F = 0x47;
47 |
48 | const MOXF_BUTTON_PLAY = 0x5E;
49 | const MOXF_BUTTON_STOP = 0x5D;
50 | const MOXF_BUTTON_REC = 0x5F;
51 | const MOXF_BUTTON_FWD = 0x5C;
52 | const MOXF_BUTTON_BACK = 0x5B;
53 | const MOXF_BUTTON_LOCATE = 0x58;
54 |
55 | const MOXF_BUTTON_CATEGORY = 0x76;
56 | const MOXF_BUTTON_FAVORITE = 0x77;
57 |
58 | // Knobs
59 | const MOXF_KNOB_MAIN = 0x3C;
60 | const MOXF_KNOB_1 = 0x4A;
61 | const MOXF_KNOB_2 = 0x47;
62 | const MOXF_KNOB_3 = 0x49;
63 | const MOXF_KNOB_4 = 0x48;
64 | const MOXF_KNOB_5 = 0x1C;
65 | const MOXF_KNOB_6 = 0x1D;
66 | const MOXF_KNOB_7 = 0x1E;
67 | const MOXF_KNOB_8 = 0x1F;
68 |
69 |
70 | function MOXFHardware (outputPort, inputPort, inputCallback, fixVelocityEnableSetting, fixVelocityValueSetting)
71 | {
72 | this.portOut = outputPort;
73 | this.portIn = inputPort;
74 |
75 | this.ledCache = initArray (-1, 128);
76 |
77 | this.portIn.setMidiCallback (inputCallback);
78 | this.noteIn = this.portIn.createNoteInput ("Yamaha MOXF", "91????", "81????", "B101??", "B140??", "E1????");
79 |
80 | // --- Example for inverting the keyboard
81 | //
82 | // var noteMap = initArray (-1, 128);
83 | // for (i = 0; i < 128; i++)
84 | // noteMap[i] = 127 - i;
85 | // this.noteIn.setKeyTranslationTable (noteMap);
86 |
87 | this.velocityMapIdentity = initArray (-1, 128);
88 | this.velocityMapFixed = initArray (-1, 128);
89 | var i;
90 | for (i = 0; i < 128; i++)
91 | this.velocityMapIdentity[i] = i;
92 |
93 | this.isFixedVelocity = false;
94 |
95 | fixVelocityEnableSetting.addValueObserver (doObject (this, MOXFHardware.prototype.setVelocityMap));
96 | fixVelocityValueSetting.addRawValueObserver (doObject (this, MOXFHardware.prototype.updateVelocityMap));
97 | }
98 |
99 | MOXFHardware.prototype.setVelocityMap = function (value)
100 | {
101 | this.isFixedVelocity = value == BOOLEAN_OPTIONS[1];
102 | this.noteIn.setVelocityTranslationTable (this.isFixedVelocity ? this.velocityMapFixed : this.velocityMapIdentity);
103 | }
104 |
105 | MOXFHardware.prototype.updateVelocityMap = function (value)
106 | {
107 | var i;
108 | for (i = 0; i < 128; i++)
109 | this.velocityMapFixed[i] = value;
110 | if (this.isFixedVelocity)
111 | this.setVelocityMap (BOOLEAN_OPTIONS[1]);
112 | }
113 |
114 | MOXFHardware.prototype.updateLED = function (note, isOn)
115 | {
116 | var value = isOn ? 127 : 0;
117 | if (this.ledCache[note] == value)
118 | return;
119 | this.ledCache[note] = value;
120 | this.portOut.sendMidi (0x90, note, value);
121 | }
122 |
--------------------------------------------------------------------------------
/Part-8/ModeHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // Mode Interface:
24 | // - String getName ()
25 | // - void setIndication (boolean enable)
26 | // - void handleMidi (byte status, byte data1, byte data2)
27 |
28 | function ModeHandler (modes, modeSetting)
29 | {
30 | this.modes = modes;
31 | this.modeSetting = modeSetting;
32 |
33 | this.setActiveMode (modes[0]);
34 |
35 | this.modeSetting.addValueObserver (doObject (this, ModeHandler.prototype.modeSettingCallback));
36 | }
37 |
38 | ModeHandler.prototype.modeSettingCallback = function (value)
39 | {
40 | var i;
41 | for (i = 0; i < MODE_OPTIONS.length; i++)
42 | {
43 | if (MODE_OPTIONS[i] == value)
44 | this.setActiveMode (this.modes[i]);
45 | }
46 | }
47 |
48 | ModeHandler.prototype.setActiveMode = function (newMode)
49 | {
50 | this.activeMode = newMode;
51 | host.showPopupNotification (this.activeMode.getName ());
52 | this.updateIndication ();
53 | }
54 |
55 | ModeHandler.prototype.updateIndication = function ()
56 | {
57 | var i;
58 | for (i = 0; i < this.modes.length; i++)
59 | this.modes[i].setIndication (false);
60 | this.activeMode.setIndication (true);
61 | }
62 |
63 | ModeHandler.prototype.handleMidi = function (status, data1, data2)
64 | {
65 | if (isNoteOn(status))
66 | {
67 | switch (data1)
68 | {
69 | case MOXF_BUTTON_A:
70 | this.setActiveMode (this.modes[0]);
71 | this.modeSetting.set (MODE_OPTIONS[0]);
72 | return true;
73 |
74 | case MOXF_BUTTON_B:
75 | this.setActiveMode (this.modes[1]);
76 | this.modeSetting.set (MODE_OPTIONS[1]);
77 | return true;
78 | }
79 | }
80 |
81 | if (this.activeMode.handleMidi (status, data1, data2))
82 | return true;
83 |
84 | return false;
85 | }
86 |
--------------------------------------------------------------------------------
/Part-8/RemoteControlHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function RemoteControlHandler (cursorDevice, remoteControlsBank)
24 | {
25 | this.cursorDevice = cursorDevice;
26 | this.remoteControlsBank = remoteControlsBank;
27 |
28 | var i;
29 | for (i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
30 | this.remoteControlsBank.getParameter (i).markInterested ();
31 |
32 | this.cursorDevice.isEnabled ().markInterested ();
33 | this.cursorDevice.isWindowOpen ().markInterested ();
34 | }
35 |
36 | RemoteControlHandler.prototype.getName = function ()
37 | {
38 | return "Device Mode";
39 | }
40 |
41 | RemoteControlHandler.prototype.setIndication = function (enable)
42 | {
43 | var i;
44 | for (i = 0; i < this.remoteControlsBank.getParameterCount (); i++)
45 | this.remoteControlsBank.getParameter (i).setIndication (enable);
46 | }
47 |
48 | RemoteControlHandler.prototype.handleMidi = function (status, data1, data2)
49 | {
50 | if (isNoteOn(status))
51 | {
52 | switch (data1)
53 | {
54 | case MOXF_BUTTON_SF1:
55 | this.cursorDevice.selectPrevious ();
56 | return true;
57 |
58 | case MOXF_BUTTON_SF2:
59 | this.cursorDevice.selectNext ();
60 | return true;
61 |
62 | case MOXF_BUTTON_SF3:
63 | // Not used
64 | return true;
65 |
66 | case MOXF_BUTTON_SF4:
67 | // Not used
68 | return true;
69 |
70 | case MOXF_BUTTON_SF5:
71 | this.remoteControlsBank.selectPrevious ();
72 | return true;
73 |
74 | case MOXF_BUTTON_SF6:
75 | this.remoteControlsBank.selectNext ();
76 | return true;
77 |
78 | case MOXF_BUTTON_SOLO:
79 | this.cursorDevice.isWindowOpen ().toggle ();
80 | return true;
81 |
82 | case MOXF_BUTTON_MUTE:
83 | this.cursorDevice.isEnabled ().toggle ();
84 | return true;
85 |
86 | default:
87 | return false;
88 | }
89 | }
90 |
91 | if (isChannelController(status))
92 | {
93 | switch (data1)
94 | {
95 | // Absolute values
96 | case MOXF_KNOB_1:
97 | this.remoteControlsBank.getParameter (0).set (data2, 128);
98 | return true;
99 |
100 | case MOXF_KNOB_2:
101 | this.remoteControlsBank.getParameter (1).set (data2, 128);
102 | return true;
103 |
104 | case MOXF_KNOB_3:
105 | this.remoteControlsBank.getParameter (2).set (data2, 128);
106 | return true;
107 |
108 | case MOXF_KNOB_4:
109 | this.remoteControlsBank.getParameter (3).set (data2, 128);
110 | return true;
111 |
112 | // Relative values
113 | case MOXF_KNOB_5:
114 | var value = data2 > 64 ? 64 - data2 : data2;
115 | this.remoteControlsBank.getParameter (4).inc (value, 128);
116 | return true;
117 |
118 | case MOXF_KNOB_6:
119 | var value = data2 > 64 ? 64 - data2 : data2;
120 | this.remoteControlsBank.getParameter (5).inc (value, 128);
121 | return true;
122 |
123 | case MOXF_KNOB_7:
124 | var value = data2 > 64 ? 64 - data2 : data2;
125 | this.remoteControlsBank.getParameter (6).inc (value, 128);
126 | return true;
127 |
128 | case MOXF_KNOB_8:
129 | var value = data2 > 64 ? 64 - data2 : data2;
130 | this.remoteControlsBank.getParameter (7).inc (value, 128);
131 | return true;
132 |
133 | default:
134 | return false;
135 | }
136 | }
137 |
138 | return false;
139 | }
140 |
--------------------------------------------------------------------------------
/Part-8/TrackHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TrackHandler (trackbank, cursorTrack)
24 | {
25 | this.trackbank = trackbank;
26 | this.cursorTrack = cursorTrack;
27 |
28 | this.trackbank.followCursorTrack (this.cursorTrack);
29 |
30 | var i;
31 | for (i = 0; i < this.trackbank.getSizeOfBank (); i++)
32 | {
33 | var track = this.trackbank.getItemAt (i);
34 | track.pan ().markInterested ();
35 | track.volume ().markInterested ();
36 | }
37 |
38 | this.cursorTrack.solo ().markInterested ();
39 | this.cursorTrack.mute ().markInterested ();
40 | }
41 |
42 | TrackHandler.prototype.getName = function ()
43 | {
44 | return "Track Mode";
45 | }
46 |
47 | TrackHandler.prototype.setIndication = function (enable)
48 | {
49 | var i;
50 | for (i = 0; i < this.trackbank.getSizeOfBank (); i++)
51 | {
52 | var track = this.trackbank.getItemAt (i);
53 | track.pan ().setIndication (enable);
54 | track.volume ().setIndication (enable);
55 | }
56 | }
57 |
58 | TrackHandler.prototype.handleMidi = function (status, data1, data2)
59 | {
60 | if (isNoteOn(status))
61 | {
62 | switch (data1)
63 | {
64 | case MOXF_BUTTON_SF1:
65 | this.trackbank.getItemAt (0).select ();
66 | return true;
67 |
68 | case MOXF_BUTTON_SF2:
69 | this.trackbank.getItemAt (1).select ();
70 | return true;
71 |
72 | case MOXF_BUTTON_SF3:
73 | this.trackbank.getItemAt (2).select ();
74 | return true;
75 |
76 | case MOXF_BUTTON_SF4:
77 | this.trackbank.getItemAt (3).select ();
78 | return true;
79 |
80 | case MOXF_BUTTON_SF5:
81 | this.trackbank.scrollPageBackwards ();
82 | return true;
83 |
84 | case MOXF_BUTTON_SF6:
85 | this.trackbank.scrollPageForwards ();
86 | return true;
87 |
88 | case MOXF_BUTTON_SOLO:
89 | this.cursorTrack.solo ().toggle ();
90 | return true;
91 |
92 | case MOXF_BUTTON_MUTE:
93 | this.cursorTrack.mute ().toggle ();
94 | return true;
95 |
96 | default:
97 | return false;
98 | }
99 | }
100 |
101 | if (isChannelController(status))
102 | {
103 | switch (data1)
104 | {
105 | // Absolute values
106 | case MOXF_KNOB_1:
107 | this.trackbank.getItemAt (0).pan ().set (data2, 128);
108 | return true;
109 |
110 | case MOXF_KNOB_2:
111 | this.trackbank.getItemAt (1).pan ().set (data2, 128);
112 | return true;
113 |
114 | case MOXF_KNOB_3:
115 | this.trackbank.getItemAt (2).pan ().set (data2, 128);
116 | return true;
117 |
118 | case MOXF_KNOB_4:
119 | this.trackbank.getItemAt (3).pan ().set (data2, 128);
120 | return true;
121 |
122 | // Relative values
123 | case MOXF_KNOB_5:
124 | var value = data2 > 64 ? 64 - data2 : data2;
125 | this.trackbank.getItemAt (0).volume ().inc (value, 128);
126 | return true;
127 |
128 | case MOXF_KNOB_6:
129 | var value = data2 > 64 ? 64 - data2 : data2;
130 | this.trackbank.getItemAt (1).volume ().inc (value, 128);
131 | return true;
132 |
133 | case MOXF_KNOB_7:
134 | var value = data2 > 64 ? 64 - data2 : data2;
135 | this.trackbank.getItemAt (2).volume ().inc (value, 128);
136 | return true;
137 |
138 | case MOXF_KNOB_8:
139 | var value = data2 > 64 ? 64 - data2 : data2;
140 | this.trackbank.getItemAt (3).volume ().inc (value, 128);
141 | return true;
142 |
143 | default:
144 | return false;
145 | }
146 | }
147 |
148 | return false;
149 | }
150 |
--------------------------------------------------------------------------------
/Part-8/TransportHandler.js:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2019-2025 Jürgen Moßgraber
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | function TransportHandler (transport)
24 | {
25 | this.transport = transport;
26 |
27 | // --- Examplefor using value observers
28 | // transport.isPlaying ().addValueObserver (function (value)
29 | // {
30 | // println (value ? "Playing..." : "Stopped.");
31 | // });
32 |
33 | this.transport.isPlaying ().markInterested ();
34 | this.transport.isArrangerRecordEnabled ().markInterested ();
35 | }
36 |
37 | TransportHandler.prototype.handleMidi = function (status, data1, data2)
38 | {
39 | if (!isNoteOn(status))
40 | return false;
41 |
42 | if (data2 == 0)
43 | return true;
44 |
45 | switch (data1)
46 | {
47 | case MOXF_BUTTON_PLAY:
48 | this.transport.play ();
49 | return true;
50 |
51 | case MOXF_BUTTON_STOP:
52 | this.transport.stop ();
53 | return true;
54 |
55 | case MOXF_BUTTON_REC:
56 | this.transport.record ();
57 | return true;
58 |
59 | case MOXF_BUTTON_FWD:
60 | this.transport.fastForward ();
61 | return true;
62 |
63 | case MOXF_BUTTON_BACK:
64 | this.transport.rewind ();
65 | return true;
66 |
67 | case MOXF_BUTTON_LOCATE:
68 | this.transport.tapTempo ();
69 | return true;
70 |
71 | default:
72 | return false;
73 | }
74 | }
75 |
76 | TransportHandler.prototype.updateLEDs = function ()
77 | {
78 | hardware.updateLED (MOXF_BUTTON_PLAY, this.transport.isPlaying ().get ());
79 | hardware.updateLED (MOXF_BUTTON_REC, this.transport.isArrangerRecordEnabled ().get ());
80 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bitwig-Controller-API-Tutorials
2 | Source code for my Bitwig Controller API Tutorials on Youtube
3 |
4 | https://www.youtube.com/playlist?list=PLqRWeSPiYQ66KBGONBenPv1O3luQCFQR2
5 |
6 | IMPORTANT: If you use any of the examples as a basis of your own script do not forget to change the UUID!
7 |
--------------------------------------------------------------------------------