index.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <html>
  2. <head>
  3. <meta name="viewport" content="width=device-width, initial-scale=1">
  4. <style type="text/css">
  5. html,body {
  6. margin: 0;
  7. padding: 0;
  8. }
  9. .header {
  10. text-align: center;
  11. height: 20%;
  12. min-height: 110px;
  13. }
  14. .main {
  15. width: 100%;
  16. height: 80%;
  17. display: flex;
  18. flex-direction: row;
  19. }
  20. .sidebar {
  21. width: 20%;
  22. min-width: 140px;
  23. height: 100%;
  24. overflow-y: scroll;
  25. }
  26. .timeline-container {
  27. width: 80%;
  28. height: 100%;
  29. overflow-x: scroll;
  30. }
  31. #sample-sidebar {
  32. display: flex;
  33. flex-direction: column;
  34. }
  35. button.sample {
  36. max-width: 200px;
  37. height: 40px;
  38. margin: 0 auto;
  39. }
  40. #timeline {
  41. height: 100%;
  42. background-color: #a3a3a3;
  43. }
  44. </style>
  45. </head>
  46. <body>
  47. <div class="container">
  48. <div class="header">
  49. <h1>Keygen Loop Maker</h1>
  50. <label for="loop-duration">Loop Duration (ms)</label><input id="loop-duration" name="loop-duration" value="8000" />
  51. <label for="loop-steps">Loop Step (ms)</label><input id="loop-steps" name="loop-steps" value="125" />
  52. <button id="start">Start Loop</button>
  53. <button id="stop">Stop Loop</button>
  54. <button id="demo">Demo</button>
  55. <button id="reset">Reset</button>
  56. <button id="export">Export</button>
  57. <button id="import">Import</button>
  58. </div>
  59. <div class="main">
  60. <div class="sidebar">
  61. <div id="sample-sidebar"></div>
  62. </div>
  63. <div class="timeline-container">
  64. <canvas id="timeline"></canvas>
  65. </div>
  66. </div>
  67. </div>
  68. <script type="text/javascript">
  69. var playLoopId = 0;
  70. var channelIds = [];
  71. var loopDuration = 8000;
  72. loopSteps = 125;
  73. var loopStartTimestamp = 0;
  74. //close enough script
  75. //ls 8-Bit\ Kit\ Volume1/*.wav | xargs | sed -e 's/.wav /.wav",\n"/g'
  76. var samples = [
  77. "8-Bit Kit Volume1/Arcade Echo FX 001.wav",
  78. "8-Bit Kit Volume1/Blip 001.wav",
  79. "8-Bit Kit Volume1/Blip 002.wav",
  80. "8-Bit Kit Volume1/Blip 003.wav",
  81. "8-Bit Kit Volume1/Blip 004.wav",
  82. "8-Bit Kit Volume1/Bouncer 001.wav",
  83. "8-Bit Kit Volume1/Bouncer 002.wav",
  84. "8-Bit Kit Volume1/Bouncer 003.wav",
  85. "8-Bit Kit Volume1/Creature 001.wav",
  86. "8-Bit Kit Volume1/Creature 002.wav",
  87. "8-Bit Kit Volume1/Effect 001.wav",
  88. "8-Bit Kit Volume1/Effect 002.wav",
  89. "8-Bit Kit Volume1/Effect 003.wav",
  90. "8-Bit Kit Volume1/Engine Bass 001.wav",
  91. "8-Bit Kit Volume1/Jump 001.wav",
  92. "8-Bit Kit Volume1/Jump 002.wav",
  93. "8-Bit Kit Volume1/Jump 003.wav",
  94. "8-Bit Kit Volume1/Jump 004.wav",
  95. "8-Bit Kit Volume1/Kick 001.wav",
  96. "8-Bit Kit Volume1/Kick 002.wav",
  97. "8-Bit Kit Volume1/Lift Off FX 001.wav",
  98. "8-Bit Kit Volume1/Lift Off FX 002.wav",
  99. "8-Bit Kit Volume1/Lift Off FX 003.wav",
  100. "8-Bit Kit Volume1/Lift Off FX 004.wav",
  101. "8-Bit Kit Volume1/Machine Blip 001.wav",
  102. "8-Bit Kit Volume1/Noise 001.wav",
  103. "8-Bit Kit Volume1/Noise 002.wav",
  104. "8-Bit Kit Volume1/Noise 003.wav",
  105. "8-Bit Kit Volume1/Noise 004.wav",
  106. "8-Bit Kit Volume1/Phat Retro Bass 001.wav",
  107. "8-Bit Kit Volume1/Phat Retro Bass 002.wav",
  108. "8-Bit Kit Volume1/Quick Bass 001.wav",
  109. "8-Bit Kit Volume1/Quick Bass 002.wav",
  110. "8-Bit Kit Volume1/Quick Bass 003.wav",
  111. "8-Bit Kit Volume1/Red Alert FX 001.wav",
  112. "8-Bit Kit Volume1/Robot Talk 001.wav",
  113. "8-Bit Kit Volume1/Robot Talk 002.wav",
  114. "8-Bit Kit Volume1/Robot Talk 003.wav",
  115. "8-Bit Kit Volume1/Robot Talk 004.wav",
  116. "8-Bit Kit Volume1/Snare 001.wav",
  117. "8-Bit Kit Volume1/Snare 002.wav",
  118. "8-Bit Kit Volume1/Snare 003.wav",
  119. "8-Bit Kit Volume1/Snare 004.wav",
  120. "8-Bit Kit Volume1/Stutter Blip 001.wav",
  121. "8-Bit Kit Volume1/Synth 001.wav",
  122. "8-Bit Kit Volume1/Zap FX 001.wav",
  123. "8-Bit Kit Volume1/Zap FX 002.wav",
  124. "8-Bit Kit Volume1/Zap FX 003.wav",
  125. "8-Bit Kit Volume1/Zap FX 004.wav",
  126. "8-Bit Kit Volume1/Zap FX 005.wav",
  127. "8-Bit Kit Volume1/Zap FX 006.wav",
  128. "8-Bit Kit Volume1/Zap FX 007.wav",
  129. "8-Bit Kit Volume1/Zoom Down FX 001.wav",
  130. ];
  131. var isHeld = {};
  132. var bar = {};
  133. var barNotes = [];
  134. var lastPosition = 0;
  135. var canvas = document.getElementById("timeline");
  136. var context = canvas.getContext("2d");
  137. function keepplaying(isHeld, audio) {
  138. if(isHeld) {
  139. audio.currentTime = 0;
  140. audio.play();
  141. }
  142. }
  143. function added(soundName) {
  144. if(playLoopId == 0) {
  145. return;
  146. }
  147. var timeSlot = Math.floor((new Date().getTime() - loopStartTimestamp) / loopSteps);
  148. if(bar[timeSlot] == null) {
  149. bar[timeSlot] = [];
  150. }
  151. if(barNotes.indexOf(soundName) == -1) {
  152. barNotes.push(soundName);
  153. }
  154. bar[timeSlot].push(barNotes.indexOf(soundName));
  155. }
  156. function pressed(audio) {
  157. if(audio.currentTime > 0) {
  158. audio.currentTime = 0;
  159. }
  160. audio.play();
  161. }
  162. document.addEventListener("DOMContentLoaded", function(event) {
  163. for(let index in samples) {
  164. let soundName = samples[index];
  165. isHeld[soundName] = false;
  166. let audio = document.createElement("audio");
  167. audio.src = soundName;
  168. audio.preload = "auto";
  169. audio.id = "audio-" + soundName;
  170. audio.addEventListener("ended", function(event) {
  171. keepplaying(isHeld[soundName], audio);
  172. });
  173. let button = document.createElement("button");
  174. button.id = "button-" + soundName;
  175. button.innerHTML = soundName;
  176. button.className = "sample";
  177. button.addEventListener("mousedown", function(event) {
  178. isHeld[soundName] = true;
  179. pressed(audio);
  180. added(soundName);
  181. });
  182. button.addEventListener("mouseup", function(event) {
  183. isHeld[soundName] = false;
  184. });
  185. button.addEventListener("mouseout", function(event) {
  186. isHeld[soundName] = false;
  187. });
  188. button.addEventListener("touchdown", function(event) {
  189. isHeld[soundName] = true;
  190. pressed(audio);
  191. added(soundName);
  192. });
  193. button.addEventListener("touchup", function(event) {
  194. isHeld[soundName] = false;
  195. });
  196. var body = document.getElementById("sample-sidebar");
  197. body.appendChild(audio);
  198. body.appendChild(button);
  199. }
  200. var startButton = document.getElementById("start");
  201. startButton.addEventListener("click", function(event) {
  202. if(playLoopId == 0){
  203. loopDuration = parseInt(document.getElementById("loop-duration").value);
  204. loopSteps = parseInt(document.getElementById("loop-steps").value);
  205. musicLoop();
  206. canvasResize();
  207. }
  208. });
  209. var stopButton = document.getElementById("stop");
  210. stopButton.addEventListener("click", function(event) {
  211. clearTimeout(playLoopId);
  212. playLoopId = 0;
  213. for(var index in channelIds) {
  214. clearTimeout(channelIds[index]);
  215. }
  216. canvasResize();
  217. });
  218. var demoButton = document.getElementById("demo");
  219. demoButton.addEventListener("click", function(event) {
  220. document.getElementById("stop").click();
  221. document.getElementById("loop-duration").value = 8000;
  222. document.getElementById("loop-steps").value = 125;
  223. barNotes = ["8-Bit Kit Volume1/Kick 001.wav", "8-Bit Kit Volume1/Kick 002.wav", "8-Bit Kit Volume1/Snare 001.wav", "8-Bit Kit Volume1/Synth 001.wav"];
  224. bar = {
  225. "0": [0],
  226. "4": [1],
  227. //"6": [2],
  228. "8": [0],
  229. "12": [1],
  230. //"14": [2],
  231. "16": [0],
  232. "20": [1],
  233. //"22": [2],
  234. "24": [0],
  235. "28": [1],
  236. "30": [2],
  237. "32": [0],
  238. "36": [1],
  239. "38": [2],
  240. "40": [0],
  241. "44": [1],
  242. "46": [2],
  243. "48": [0],
  244. "52": [1],
  245. "53": [3],
  246. "54": [2, 3],
  247. "55": [3],
  248. "56": [0,3],
  249. "57": [3],
  250. "58": [3],
  251. "59": [3],
  252. "60": [1, 3],
  253. };
  254. document.getElementById("start").click();
  255. });
  256. var resetButton = document.getElementById("reset");
  257. resetButton.addEventListener("click", function(event) {
  258. barNotes = [];
  259. bar = {};
  260. canvasResize();
  261. lastPosition = 0;
  262. var container = document.getElementsByClassName("timeline-container")[0];
  263. container.scrollTo(0, 0);
  264. });
  265. var importButton = document.getElementById("import");
  266. importButton.addEventListener("click", function(event) {
  267. var data = prompt("Please paste your import code");
  268. var json = atob(data);
  269. var importData = JSON.parse(json);
  270. barNotes = importData.notes;
  271. bar = importData.bar;
  272. canvasResize();
  273. lastPosition = 0;
  274. var container = document.getElementsByClassName("timeline-container")[0];
  275. container.scrollTo(0, 0);
  276. });
  277. var exportButton = document.getElementById("export");
  278. exportButton.addEventListener("click", function(event) {
  279. var exportData = {};
  280. exportData.bar = bar;
  281. exportData.notes = barNotes;
  282. var json = JSON.stringify(exportData);
  283. prompt("Export code", btoa(json));
  284. });
  285. canvasResize();
  286. });
  287. function musicLoop() {
  288. channelIds = [];
  289. loopStartTimestamp = new Date().getTime();
  290. for(let key in bar) {
  291. var timeToPlay = loopSteps * parseInt(key);
  292. if(timeToPlay <= loopDuration) {
  293. channelIds.push(setTimeout(function() { playBar(bar[key]); }, timeToPlay));
  294. }
  295. }
  296. playLoopId = setTimeout(musicLoop, loopDuration);
  297. }
  298. function playBar(notes) {
  299. for(var index in notes) {
  300. var audio = document.getElementById("audio-" + barNotes[notes[index]]);
  301. pressed(audio);
  302. }
  303. }
  304. function canvasResize() {
  305. var container = document.getElementsByClassName("timeline-container")[0];
  306. canvas.width = Math.min(Math.floor(64 * loopSteps), Math.floor(64 * (loopDuration / loopSteps)));
  307. canvas.height = container.offsetHeight;
  308. var context = canvas.getContext("2d");
  309. context.translate(0.5, 0.5);
  310. draw();
  311. }
  312. function randomColor(index) {
  313. return ["red", "orange", "yellow", "green", "blue", "indigo", "violet"][index%7];
  314. }
  315. window.addEventListener("resize", canvasResize);
  316. function draw() {
  317. context.strokeStyle = null;
  318. context.fillStyle = "#a3a3a3";
  319. context.beginPath();
  320. context.rect(0, 0, canvas.width, canvas.height);
  321. context.fill();
  322. var stepRatio = loopDuration / loopSteps;
  323. var canvasRatio = canvas.width / stepRatio;
  324. context.strokeStyle = "#8A8A8A";
  325. context.lineWidth = 1;
  326. for(var i = 0; i < stepRatio; i++) {
  327. context.beginPath();
  328. context.moveTo(canvasRatio * i, 0);
  329. context.lineTo(canvasRatio * i, canvas.height);
  330. context.stroke();
  331. }
  332. for(var timeslot in bar) {
  333. for(var noteIndex in bar[timeslot]) {
  334. context.beginPath();
  335. context.fillStyle = randomColor(bar[timeslot][noteIndex]);
  336. context.rect(canvasRatio * parseInt(timeslot), 20 * bar[timeslot][noteIndex], canvasRatio, 20);
  337. context.fill();
  338. }
  339. }
  340. if(playLoopId != 0) {
  341. var delta = new Date().getTime() - loopStartTimestamp;
  342. var percentage = delta / loopDuration;
  343. lastPosition = canvas.width * percentage;
  344. var container = document.getElementsByClassName("timeline-container")[0];
  345. container.scrollTo(Math.floor((canvas.width * percentage) - 200), 0);
  346. }
  347. context.strokeStyle = "#8ADDDD";
  348. context.lineWidth = 3;
  349. context.beginPath();
  350. context.moveTo(lastPosition, 0);
  351. context.lineTo(lastPosition, canvas.height);
  352. context.stroke();
  353. }
  354. function raf() {
  355. draw();
  356. requestAnimationFrame(raf);
  357. }
  358. requestAnimationFrame(raf);
  359. </script>
  360. </body>
  361. </html>