anon6001

Untitled

Jun 12th, 2026 (edited)
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.81 KB | Music | 0 0
  1. loadAPI(15);
  2.  
  3. host.setShouldFailOnDeprecatedUse(true);
  4.  
  5. host.defineController(
  6. "SA",
  7. "Song Group Auto Activator",
  8. "1.0",
  9. "8f8c6f0e-8f4e-4d88-9f6e-91a19e4c5517"
  10. );
  11.  
  12. host.defineMidiPorts(0, 0);
  13.  
  14. // --------------------
  15. // Configuration
  16. // --------------------
  17.  
  18. var TRACK_BANK_SIZE = 32;
  19. var MARKER_COUNT = 32;
  20. var CHECK_INTERVAL_MS = 200;
  21.  
  22. // Set true if you want n-1, n, and n+1 active.
  23. // Useful for long delay/reverb tails.
  24. var KEEP_PREVIOUS_ACTIVE = false;
  25.  
  26. // --------------------
  27. // State
  28. // --------------------
  29.  
  30. var transport;
  31. var arranger;
  32. var cueMarkerBank;
  33. var trackBank;
  34.  
  35. var latestTransportPos = 0;
  36. var currentSongNumber = -1;
  37. var isRunning = true;
  38.  
  39. var markerCache = [];
  40. var trackCache = [];
  41.  
  42. // --------------------
  43. // Init
  44. // --------------------
  45.  
  46. function init()
  47. {
  48. transport = host.createTransport();
  49. arranger = host.createArranger();
  50.  
  51. cueMarkerBank = arranger.createCueMarkerBank(MARKER_COUNT);
  52.  
  53. trackBank = host.createMainTrackBank(TRACK_BANK_SIZE, 0, 0);
  54.  
  55. setupMarkerObservers();
  56. setupTrackObservers();
  57. setupTransportObserver();
  58.  
  59. host.showPopupNotification("Song Group Auto Activator loaded");
  60. println("Song Group Auto Activator loaded");
  61. }
  62.  
  63. // --------------------
  64. // Observers
  65. // --------------------
  66.  
  67. function setupMarkerObservers()
  68. {
  69. for (var i = 0; i < MARKER_COUNT; i++)
  70. {
  71. markerCache[i] = {
  72. name: "",
  73. position: -1
  74. };
  75.  
  76. var marker = cueMarkerBank.getItemAt(i);
  77.  
  78. observeMarkerName(marker, i);
  79. observeMarkerPosition(marker, i);
  80. }
  81. }
  82.  
  83. function observeMarkerName(marker, index)
  84. {
  85. marker.name().addValueObserver(function(name)
  86. {
  87. markerCache[index].name = name;
  88. });
  89. }
  90.  
  91. function observeMarkerPosition(marker, index)
  92. {
  93. marker.position().addValueObserver(function(position)
  94. {
  95. markerCache[index].position = position;
  96. });
  97. }
  98.  
  99. function setupTrackObservers()
  100. {
  101. for (var i = 0; i < TRACK_BANK_SIZE; i++)
  102. {
  103. var track = trackBank.getItemAt(i);
  104.  
  105. trackCache[i] = {
  106. name: "",
  107. songNumber: -1,
  108. soloed: false,
  109. track: track
  110. };
  111.  
  112. observeTrackName(track, i);
  113. observeTrackSolo(track, i);
  114. }
  115. }
  116.  
  117. function observeTrackName(track, index)
  118. {
  119. track.name().addValueObserver(function(name)
  120. {
  121. trackCache[index].name = name;
  122. trackCache[index].songNumber = parseSongNumberFromTrackName(name);
  123.  
  124. println(
  125. "Track slot " +
  126. index +
  127. ": '" +
  128. name +
  129. "', songNumber=" +
  130. trackCache[index].songNumber
  131. );
  132. });
  133. }
  134.  
  135. function observeTrackSolo(track, index)
  136. {
  137. track.solo().addValueObserver(function(soloed)
  138. {
  139. trackCache[index].soloed = soloed;
  140.  
  141. println(
  142. "Track slot " +
  143. index +
  144. " soloed=" +
  145. soloed
  146. );
  147. });
  148. }
  149.  
  150. function setupTransportObserver()
  151. {
  152. // This observer only caches the current position.
  153. // The actual marker scan happens only every CHECK_INTERVAL_MS.
  154. transport.getPosition().addValueObserver(function(position)
  155. {
  156. latestTransportPos = position;
  157. });
  158.  
  159. schedulePositionCheck();
  160. }
  161.  
  162. // --------------------
  163. // Throttled marker checking
  164. // --------------------
  165.  
  166. function schedulePositionCheck()
  167. {
  168. if (!isRunning)
  169. return;
  170.  
  171. checkSongMarkerPosition();
  172.  
  173. host.scheduleTask(function()
  174. {
  175. schedulePositionCheck();
  176. }, CHECK_INTERVAL_MS);
  177. }
  178.  
  179. function checkSongMarkerPosition()
  180. {
  181. var songNumber = getCurrentSongNumberFromMarkers(latestTransportPos);
  182.  
  183. if (songNumber > 0 && songNumber !== currentSongNumber)
  184. {
  185. currentSongNumber = songNumber;
  186. activateSongWindow(songNumber);
  187. }
  188. }
  189.  
  190. // --------------------
  191. // Marker/song logic
  192. // --------------------
  193.  
  194. function getCurrentSongNumberFromMarkers(position)
  195. {
  196. var selectedSongNumber = -1;
  197. var selectedMarkerPosition = -1;
  198.  
  199. for (var i = 0; i < markerCache.length; i++)
  200. {
  201. var marker = markerCache[i];
  202.  
  203. if (marker.position < 0)
  204. continue;
  205.  
  206. var markerSongNumber = parseIntegerMarkerName(marker.name);
  207.  
  208. if (markerSongNumber <= 0)
  209. continue;
  210.  
  211. if (marker.position <= position && marker.position > selectedMarkerPosition)
  212. {
  213. selectedMarkerPosition = marker.position;
  214. selectedSongNumber = markerSongNumber;
  215. }
  216. }
  217.  
  218. return selectedSongNumber;
  219. }
  220.  
  221. function activateSongWindow(songNumber)
  222. {
  223. var activeNumbers = {};
  224.  
  225. activeNumbers[songNumber] = true;
  226.  
  227. if (songNumber + 1 <= TRACK_BANK_SIZE)
  228. activeNumbers[songNumber + 1] = true;
  229.  
  230. if (KEEP_PREVIOUS_ACTIVE && songNumber - 1 >= 1)
  231. activeNumbers[songNumber - 1] = true;
  232.  
  233. println("Marker/song " + songNumber + " reached");
  234.  
  235. for (var i = 0; i < trackCache.length; i++)
  236. {
  237. var entry = trackCache[i];
  238. var trackSongNumber = entry.songNumber;
  239.  
  240. if (trackSongNumber <= 0)
  241. continue;
  242.  
  243. var shouldBeActive = !!activeNumbers[trackSongNumber];
  244.  
  245. if (shouldBeActive)
  246. {
  247. println(
  248. "Setting '" +
  249. entry.name +
  250. "' active=true"
  251. );
  252.  
  253. entry.track.isActivated().set(true);
  254. }
  255. else
  256. {
  257. if (entry.soloed)
  258. {
  259. println(
  260. "Keeping '" +
  261. entry.name +
  262. "' active because it is soloed"
  263. );
  264.  
  265. // Ensure the soloed group remains active.
  266. entry.track.isActivated().set(true);
  267. }
  268. else
  269. {
  270. println(
  271. "Setting '" +
  272. entry.name +
  273. "' active=false"
  274. );
  275.  
  276. entry.track.isActivated().set(false);
  277. }
  278. }
  279. }
  280.  
  281. host.showPopupNotification(
  282. "Active songs: " +
  283. songNumber +
  284. (songNumber + 1 <= TRACK_BANK_SIZE ? " + " + (songNumber + 1) : "")
  285. );
  286. }
  287.  
  288. // --------------------
  289. // Parsing helpers
  290. // --------------------
  291.  
  292. function parseIntegerMarkerName(name)
  293. {
  294. if (!name)
  295. return -1;
  296.  
  297. name = String(name).trim();
  298.  
  299. // Marker must be exactly "1", "2", "3", ...
  300. if (!/^[0-9]+$/.test(name))
  301. return -1;
  302.  
  303. return parseInt(name, 10);
  304. }
  305.  
  306. function parseSongNumberFromTrackName(name)
  307. {
  308. if (!name)
  309. return -1;
  310.  
  311. name = String(name).trim();
  312.  
  313. // Matches:
  314. // "1 - Song Name", "2 - Song Name" .. "n - Song Name"
  315. var match = /^([0-9]+)\s*-\s*/.exec(name);
  316.  
  317. if (!match)
  318. return -1;
  319.  
  320. return parseInt(match[1], 10);
  321. }
  322.  
  323. // --------------------
  324. // Bitwig lifecycle
  325. // --------------------
  326.  
  327. function flush()
  328. {
  329. // Nothing to do here
  330. }
  331.  
  332. function exit()
  333. {
  334. isRunning = false;
  335. println("Song Group Auto Activator exited");
  336. }
Advertisement
Add Comment
Please, Sign In to add comment