├── .gitignore
├── README.md
├── dependency-reduced-pom.xml
├── pom.xml
└── src
└── main
├── java
└── com
│ └── kernicpanel
│ ├── RandomizerExtension.java
│ └── RandomizerExtensionDefinition.java
└── resources
└── META-INF
└── services
└── com.bitwig.extension.ExtensionDefinition
/.gitignore:
--------------------------------------------------------------------------------
1 | /${bitwig.extension.directory}
2 | /target
3 | .DS_Store
4 | last-build.bin
5 | fileHashes.lock
6 | /.settings/
7 | /.classpath
8 | /.project
9 | /.idea
10 | *.iml
11 | /bin/
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Randomizer
5 |
6 | Bitwig randomizer
7 |
8 | @polarity did a review for the version 1.0
9 | https://www.youtube.com/watch?v=KXkJyn4Qsho
10 |
11 | ## WARNING
12 |
13 | This WILL lead to unexpected results, be sure to lower your volume before using.
14 |
15 | ## Motivation
16 |
17 | I enjoy surprises, and I think that creativity comes from constraints.
18 |
19 | So I wanted to make a script that would randomly select a Bitwig browser item.
20 | This allows me two main things:
21 | - Select unexpected items, which open up possibilities for me to explore.
22 | - Force me to use a forgotten device, plugin… etc. to try to master it.
23 |
24 | I added a name genarator, wich tries to be inspiring.
25 | It can be used to name devices, tracks, presets, etc., but also to name projects.
26 |
27 | ## Activation
28 |
29 | Copy the released extension to your Bitwig extensions folder.
30 |
31 | Activate it in the settings.
32 |
33 |
34 | https://user-images.githubusercontent.com/720491/178076494-40867f31-c326-4319-8b38-6d4cedb6aeaa.mp4
35 |
36 |
37 | ## Usage
38 |
39 | ### Parameters randomization
40 |
41 | Devices parameters can be randomized.
42 | A ratio can be applied to limit the range of the new value.
43 | We can randomize the current parameters page or all of them.
44 |
45 |
46 | https://user-images.githubusercontent.com/720491/180964015-50166694-b680-47f7-b954-8091a33ea6e3.mp4
47 |
48 |
49 | ### Browser random selection
50 |
51 | This extension allows users to select a random item from the browser.
52 | It could be any browser related item type, like device, plugin, wave file, preset, modulator…
53 |
54 | - `Select random item` opens a browser if none is already opened and selects a random item from the current opened browser.
55 |
56 | - `Add current item` adds the current selected item.
57 | This is the same as `OK` in the browser.
58 | For me, it is convenient as it is close to the random button.
59 |
60 |
61 | https://user-images.githubusercontent.com/720491/178076578-f902a6bb-7716-4c7d-8d67-cb6ed1db0af2.mp4
62 |
63 |
64 | ### Random name generator
65 |
66 | An option is available to prepend the generated filename with the current date.
67 |
68 | https://user-images.githubusercontent.com/720491/178076594-a29e99ac-e81d-426d-8bca-ff4e8bfc7013.mp4
69 |
70 |
71 | Another option lets you customize the date format. The default template is
72 | `yyyy-MM-DD_` which would expand to e.g. `2022-07-31_`.
73 | All the possible patterns are docuemnted
74 | [here](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns).
75 |
--------------------------------------------------------------------------------
/dependency-reduced-pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.kernicpanel
5 | randomizer
6 | Randomizer
7 | 2.1
8 |
9 |
10 |
11 | maven-compiler-plugin
12 | 3.8.0
13 |
14 | true
15 | true
16 | 1.8
17 | 1.8
18 | UTF-8
19 | 1024m
20 |
21 |
22 |
23 | maven-jar-plugin
24 | 3.2.0
25 |
26 |
27 |
28 | true
29 |
30 |
31 |
32 |
33 |
34 | maven-shade-plugin
35 | 3.2.4
36 |
37 |
38 | package
39 |
40 | shade
41 |
42 |
43 |
44 |
45 | com.bitwig:extension-api
46 |
47 |
48 |
49 |
50 | net.datafaker:*
51 |
52 | META-INF/*
53 |
54 |
55 |
56 | dk.brics.automaton:*
57 |
58 | META-INF/*
59 |
60 |
61 |
62 | com.github.mifmif:generex:*
63 |
64 | META-INF/*
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | ${project.artifactId}-${project.version}
73 |
74 |
75 |
76 | com.coderplus.maven.plugins
77 | copy-rename-maven-plugin
78 | 1.0
79 |
80 |
81 | rename-file
82 | install
83 |
84 | copy
85 |
86 |
87 | ${project.build.directory}/${project.build.finalName}.jar
88 | ${user.home}/Bitwig Studio/Extensions/Randomizer.bwextension
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | bitwig
98 | Bitwig Maven Repository
99 | https://maven.bitwig.com
100 |
101 |
102 |
103 |
104 | com.bitwig
105 | extension-api
106 | 17
107 | compile
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.kernicpanel
6 | randomizer
7 | jar
8 | Randomizer
9 | 2.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 | 17
24 |
25 |
26 | net.datafaker
27 | datafaker
28 | 1.7.0
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.apache.maven.plugins
36 | maven-compiler-plugin
37 | 3.8.0
38 |
39 | true
40 | true
41 | 1.8
42 | 1.8
43 | UTF-8
44 | 1024m
45 |
46 |
47 |
48 |
49 | org.apache.maven.plugins
50 | maven-jar-plugin
51 | 3.2.0
52 |
53 |
54 |
55 | true
56 |
57 |
58 |
59 |
60 |
61 |
62 | org.apache.maven.plugins
63 | maven-shade-plugin
64 | 3.2.4
65 |
66 |
67 | package
68 |
69 | shade
70 |
71 |
72 |
73 |
74 | com.bitwig:extension-api
75 |
76 |
77 |
78 |
79 | net.datafaker:*
80 |
81 | META-INF/*
82 |
83 |
84 |
85 | dk.brics.automaton:*
86 |
87 | META-INF/*
88 |
89 |
90 |
91 | com.github.mifmif:generex:*
92 |
93 | META-INF/*
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | ${project.artifactId}-${project.version}
102 |
103 |
104 |
105 |
106 | com.coderplus.maven.plugins
107 | copy-rename-maven-plugin
108 | 1.0
109 |
110 |
111 | rename-file
112 | install
113 |
114 | copy
115 |
116 |
117 | ${project.build.directory}/${project.build.finalName}.jar
118 | ${user.home}/Bitwig Studio/Extensions/Randomizer.bwextension
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/src/main/java/com/kernicpanel/RandomizerExtension.java:
--------------------------------------------------------------------------------
1 | package com.kernicpanel;
2 |
3 | import com.bitwig.extension.callback.NoArgsCallback;
4 | import com.bitwig.extension.controller.api.*;
5 | import com.bitwig.extension.controller.ControllerExtension;
6 | import net.datafaker.Faker;
7 |
8 | import java.time.LocalDate;
9 | import java.time.format.DateTimeFormatter;
10 | import java.time.temporal.UnsupportedTemporalTypeException;
11 | import java.util.Arrays;
12 | import java.util.Locale;
13 | import java.util.Random;
14 | import java.util.stream.IntStream;
15 |
16 | public class RandomizerExtension extends ControllerExtension {
17 | Random rand = new Random();
18 | Faker faker = new Faker();
19 |
20 | private SettableBooleanValue useDate;
21 | private SettableStringValue dateFormatTemplate;
22 | private static final String DEFAULT_DATE_FORMAT_TEMPLATE = "yyyy-MM-dd_";
23 |
24 | private ControllerHost host;
25 | private Application application;
26 | private DocumentState documentState;
27 | private PopupBrowser popupBrowser;
28 | private CursorTrack cursorTrack;
29 | private CursorDevice cursorDevice;
30 | private CursorRemoteControlsPage remoteControlsPage;
31 | private BrowserResultsItemBank resultsItemBank;
32 | private Preferences preferences;
33 |
34 | private SettableStringValue filenameOutput;
35 | private SettableStringValue nameOutput;
36 | private RemoteControl parameter;
37 | private SettableRangedValue randomRatio;
38 |
39 | protected RandomizerExtension(
40 | final RandomizerExtensionDefinition definition, final ControllerHost host) {
41 | super(definition, host);
42 | }
43 |
44 | private void printer(String s) {
45 | host.println(s);
46 | java.lang.System.out.println(s);
47 | }
48 |
49 | @Override
50 | public void init() {
51 | host = getHost();
52 | printer("RandomizerExtension initialized");
53 | application = host.createApplication();
54 |
55 | documentState = host.getDocumentState();
56 | popupBrowser = host.createPopupBrowser();
57 | preferences = host.getPreferences();
58 | popupBrowser.exists().markInterested();
59 | popupBrowser.resultsColumn().entryCount().markInterested();
60 | cursorTrack = host.createCursorTrack(0, 0);
61 | preferences = host.getPreferences();
62 | cursorDevice = cursorTrack.createCursorDevice();
63 | remoteControlsPage = cursorDevice.createCursorRemoteControlsPage(80);
64 |
65 | remoteControlsPage.pageCount().markInterested();
66 | remoteControlsPage.pageNames().markInterested();
67 | remoteControlsPage.selectedPageIndex().markInterested();
68 |
69 | for (int parameterIndex = 0; parameterIndex < 8; parameterIndex++) {
70 | remoteControlsPage.getParameter(parameterIndex).name().markInterested();
71 | }
72 |
73 | resultsItemBank = popupBrowser.resultsColumn().createItemBank(100000);
74 |
75 | // Naming settings
76 | useDate = preferences.getBooleanSetting("Prefix filename with date", "Random name", true);
77 | dateFormatTemplate =
78 | preferences.getStringSetting(
79 | "Format string for date prefix", "Random name", 15, DEFAULT_DATE_FORMAT_TEMPLATE);
80 | dateFormatTemplate.addValueObserver(
81 | value -> {
82 | try {
83 | LocalDate.now().format(DateTimeFormatter.ofPattern(value));
84 | } catch (IllegalArgumentException | UnsupportedTemporalTypeException e) {
85 | dateFormatTemplate.set(DEFAULT_DATE_FORMAT_TEMPLATE);
86 | host.showPopupNotification("Invalid date format template.");
87 | }
88 | });
89 |
90 | // Naming controls
91 | filenameOutput = documentState.getStringSetting("Filename", "Random name", 50, "");
92 | nameOutput = documentState.getStringSetting("Name", "Random name", 50, "");
93 | documentState.getSignalSetting(" ", "Random name", "Generate").addSignalObserver(randomName());
94 |
95 | // Browser selection controls
96 | documentState
97 | .getSignalSetting("Select", "Randomize browser selection", "Select random item")
98 | .addSignalObserver(selectRandomItem());
99 | documentState
100 | .getSignalSetting("Add", "Randomize browser selection", "Add current item")
101 | .addSignalObserver(popupBrowser::commit);
102 |
103 | // Parameters controls
104 | randomRatio =
105 | documentState.getNumberSetting("Ratio", "Randomize Device parameters", 0, 100, 1, "%", 15);
106 | documentState
107 | .getSignalSetting(" ", "Randomize Device parameters", "Randomize current page")
108 | .addSignalObserver(randomizeCurrentPageDeviceParameters());
109 | documentState
110 | .getSignalSetting(" ", "Randomize Device parameters", "Randomize all parameters")
111 | .addSignalObserver(randomizeDeviceParameters());
112 | }
113 |
114 | private NoArgsCallback selectRandomItem() {
115 | return () -> {
116 | if (!popupBrowser.exists().getAsBoolean()) {
117 | cursorTrack.endOfDeviceChainInsertionPoint().browse();
118 | }
119 |
120 | host.scheduleTask(
121 | () -> {
122 | Integer random = rand.nextInt(popupBrowser.resultsColumn().entryCount().get());
123 | resultsItemBank.getItemAt(random).isSelected().set(true);
124 | },
125 | 300);
126 | };
127 | }
128 |
129 | private NoArgsCallback randomName() {
130 | return () -> {
131 | String[] prefixes = {
132 | faker.mood().emotion(),
133 | faker.mood().tone(),
134 | faker.mood().feeling(),
135 | faker.color().name(),
136 | faker.coffee().intensifier(),
137 | faker.company().buzzword(),
138 | faker.dungeonsAndDragons().languages(),
139 | faker.size().adjective(),
140 | faker.subscription().subscriptionTerms(),
141 | };
142 | // printer(Arrays.toString(prefixes));
143 | String prefix = prefixes[rand.nextInt(prefixes.length)];
144 |
145 | String[] names = {
146 | // faker.superhero().power(),
147 | // faker.superhero().name(),
148 | faker.hacker().ingverb(),
149 | faker.hacker().noun(),
150 | faker.hacker().verb(),
151 | faker.food().fruit(),
152 | faker.food().ingredient(),
153 | faker.food().vegetable(),
154 | faker.verb().ingForm(),
155 | faker.verb().past(),
156 | faker.verb().pastParticiple(),
157 | faker.appliance().equipment(),
158 | faker.coffee().body(),
159 | faker.darkSoul().classes(),
160 | faker.darkSoul().stats(),
161 | faker.pokemon().type(),
162 | faker.dessert().flavor(),
163 | };
164 | // printer(Arrays.toString(names));
165 | String name = names[rand.nextInt(names.length)];
166 |
167 | String[] suffixes = {
168 | faker.animal().name(),
169 | faker.weather().description(),
170 | faker.address().streetSuffix(),
171 | faker.address().citySuffix(),
172 | faker.battlefield1().classes(),
173 | faker.coffee().descriptor(),
174 | faker.company().profession(),
175 | faker.construction().heavyEquipment(),
176 | faker.construction().materials(),
177 | faker.cosmere().shards(),
178 | faker.cosmere().surges(),
179 | faker.cosmere().knightsRadiant(),
180 | faker.cosmere().metals(),
181 | faker.cosmere().allomancers(),
182 | faker.cosmere().feruchemists(),
183 | faker.dessert().variety(),
184 | faker.dessert().topping(),
185 | faker.dungeonsAndDragons().klasses(),
186 | faker.dungeonsAndDragons().meleeWeapons(),
187 | faker.dungeonsAndDragons().monsters(),
188 | faker.dungeonsAndDragons().races(),
189 | faker.dungeonsAndDragons().rangedWeapons(),
190 | faker.electricalComponents().active(),
191 | faker.electricalComponents().passive(),
192 | faker.electricalComponents().electromechanical(),
193 | faker.house().furniture(),
194 | faker.house().room(),
195 | faker.pokemon().move(),
196 | faker.restaurant().nameSuffix(),
197 | faker.science().element(),
198 | faker.science().quark(),
199 | faker.science().leptons(),
200 | faker.science().bosons(),
201 | faker.science().tool(),
202 | faker.team().creature(),
203 | };
204 | // printer(Arrays.toString(suffixes));
205 | String suffix = suffixes[rand.nextInt(suffixes.length)];
206 |
207 | String[] generators = {prefix, name, suffix};
208 | String generatedString = String.join(" ", Arrays.asList(generators));
209 | nameOutput.set(generatedString.toLowerCase(Locale.ROOT));
210 |
211 | if (useDate.get()) {
212 | LocalDate dateObj = LocalDate.now();
213 | String formattedDate;
214 | try {
215 | formattedDate = dateObj.format(DateTimeFormatter.ofPattern(dateFormatTemplate.get()));
216 | } catch (IllegalArgumentException | UnsupportedTemporalTypeException e) {
217 | // Should not happen, unless e.g. the config data got corrupted.
218 | dateFormatTemplate.set(DEFAULT_DATE_FORMAT_TEMPLATE);
219 | formattedDate = dateObj.format(DateTimeFormatter.ofPattern(dateFormatTemplate.get()));
220 | }
221 | generatedString = formattedDate + generatedString;
222 | }
223 |
224 | filenameOutput.set(generatedString.replace(" ", "_").toLowerCase(Locale.ROOT));
225 | };
226 | }
227 |
228 | private NoArgsCallback randomizeCurrentPageDeviceParameters() {
229 | return () -> {
230 | IntStream.range(0, 8)
231 | .forEach(
232 | parameterIndex -> {
233 | double randomValue = rand.nextDouble();
234 | parameter = remoteControlsPage.getParameter(parameterIndex);
235 | String parameterName = parameter.name().get();
236 |
237 | if (!parameterName.trim().isEmpty()
238 | && !parameterName.toLowerCase(Locale.ROOT).equals("output")) {
239 | double newValue = ((2 * randomValue) - 1) * randomRatio.get();
240 | parameter.inc(newValue);
241 | }
242 | });
243 | };
244 | }
245 |
246 | private NoArgsCallback randomizeDeviceParameters() {
247 | return () -> {
248 | IntStream.range(0, remoteControlsPage.pageCount().get())
249 | .forEachOrdered(
250 | (pageIndex) -> {
251 | randomizeCurrentPageDeviceParameters().call();
252 | try {
253 | remoteControlsPage.selectNextPage(true);
254 | Thread.sleep(10);
255 | } catch (InterruptedException e) {
256 | e.printStackTrace();
257 | }
258 | });
259 | };
260 | }
261 |
262 | @Override
263 | public void exit() {
264 | // TODO: Perform any cleanup once the driver exits
265 | }
266 |
267 | @Override
268 | public void flush() {
269 | // TODO Send any updates you need here.
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/src/main/java/com/kernicpanel/RandomizerExtensionDefinition.java:
--------------------------------------------------------------------------------
1 | package com.kernicpanel;
2 |
3 | import java.util.UUID;
4 |
5 | import com.bitwig.extension.api.PlatformType;
6 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList;
7 | import com.bitwig.extension.controller.ControllerExtensionDefinition;
8 | import com.bitwig.extension.controller.api.ControllerHost;
9 |
10 | public class RandomizerExtensionDefinition extends ControllerExtensionDefinition {
11 | private static final UUID DRIVER_ID = UUID.fromString("5c4b743f-cc91-41cf-be42-092ecac4a64b");
12 |
13 | public RandomizerExtensionDefinition() {}
14 |
15 | @Override
16 | public String getName() {
17 | return "Randomizer";
18 | }
19 |
20 | @Override
21 | public String getAuthor() {
22 | return "kernicPanel";
23 | }
24 |
25 | @Override
26 | public String getVersion() {
27 | return "2.0";
28 | }
29 |
30 | @Override
31 | public UUID getId() {
32 | return DRIVER_ID;
33 | }
34 |
35 | @Override
36 | public String getHardwareVendor() {
37 | return "kernicPanel";
38 | }
39 |
40 | @Override
41 | public String getHardwareModel() {
42 | return "Randomizer";
43 | }
44 |
45 | @Override
46 | public int getRequiredAPIVersion() {
47 | return 17;
48 | }
49 |
50 | @Override
51 | public int getNumMidiInPorts() {
52 | return 0;
53 | }
54 |
55 | @Override
56 | public int getNumMidiOutPorts() {
57 | return 0;
58 | }
59 |
60 | @Override
61 | public void listAutoDetectionMidiPortNames(
62 | final AutoDetectionMidiPortNamesList list, final PlatformType platformType) {}
63 |
64 | @Override
65 | public RandomizerExtension createInstance(final ControllerHost host) {
66 | return new RandomizerExtension(this, host);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition:
--------------------------------------------------------------------------------
1 | com.kernicpanel.RandomizerExtensionDefinition
--------------------------------------------------------------------------------