index.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>とある滑稽の超电子琴</title>
  7. <script src="./inc/shim/Base64.js"></script>
  8. <script src="./inc/shim/Base64binary.js"></script>
  9. <script src="./inc/shim/WebAudioAPI.js"></script>
  10. <script src="./inc/shim/WebMIDIAPI.js" type="text/javascript"></script>
  11. <!-- jasmid package -->
  12. <script src="./inc/jasmid/stream.js"></script>
  13. <script src="./inc/jasmid/midifile.js"></script>
  14. <script src="./inc/jasmid/replayer.js"></script>
  15. <script src="./js/midi/audioDetect.js" type="text/javascript"></script>
  16. <script src="./js/midi/gm.js"></script>
  17. <script src="./js/midi/loader.js"></script>
  18. <script src="./js/midi/plugin.audiotag.js"></script>
  19. <script src="./js/midi/plugin.webaudio.js"></script>
  20. <script src="./js/midi/plugin.webmidi.js"></script>
  21. <script src="./js/midi/player.js" type="text/javascript"></script>
  22. <script src="./js/midi/audioDetect.js"></script>
  23. <script src="./js/util/dom_request_xhr.js" type="text/javascript"></script>
  24. <script src="./js/util/dom_request_script.js" type="text/javascript"></script>
  25. <style>
  26. body {
  27. margin: 0px;
  28. padding: 0px;
  29. }
  30. select,
  31. button {
  32. border: 1px solid black;
  33. background-color: transparent;
  34. outline: none;
  35. font-weight: 400;
  36. padding: 1px 6px;
  37. }
  38. .huaji {
  39. background-color: transparent;
  40. background-image: url('./images/huaji1.png');
  41. background-repeat: no-repeat;
  42. background-size: contain;
  43. width: 32px;
  44. padding: 0px;
  45. padding-top: 59px;
  46. border: 2px solid transparent;
  47. outline: 0px;
  48. appearance: none;
  49. }
  50. .huaji:hover,
  51. .huaji.down {
  52. border-bottom-color: rgb(232, 167, 84);
  53. cursor: pointer;
  54. }
  55. .huaji.down {
  56. background-image: url('./images/huaji2.png');
  57. }
  58. #openFile {
  59. display: none;
  60. }
  61. .play-mode {
  62. overflow: auto;
  63. word-wrap: none;
  64. white-space: nowrap;
  65. position: fixed;
  66. bottom: 0px;
  67. left: 0px;
  68. right: 0px;
  69. }
  70. .play-mode .huaji {
  71. width: 56px;
  72. padding-top: 118px;
  73. }
  74. #time {
  75. width: 40%;
  76. pointer-events: none;
  77. }
  78. .options>* {
  79. vertical-align: middle;
  80. }
  81. .options {
  82. padding: 10px 0px;
  83. overflow: auto;
  84. word-wrap: none;
  85. white-space: nowrap;
  86. }
  87. .options>*:first-child {
  88. margin-left: 8px !important;
  89. }
  90. .options>*:last-child {
  91. margin-right: 8px !important;
  92. }
  93. #keyboard {
  94. margin: auto 8px;
  95. user-select: none;
  96. }
  97. </style>
  98. </head>
  99. <body>
  100. <div class="options">
  101. <select onchange="openUrl('./midis/'+this.value+'.mid')">
  102. <option value="1" disabled selected>选择预置的乐谱</option>
  103. <optgroup label="万恶之源">
  104. <option value="Astronomia">Astronomia</option>
  105. <option value="Ikenaibodarain">禁绝边境线</option>
  106. <option value="GokuRakuJoudo">极乐净土</option>
  107. </optgroup>
  108. <optgroup label="火星文">
  109. <option value="aLIEz">aLIEz</option>
  110. </optgroup>
  111. <optgroup label="某科学的超电磁炮">
  112. <option value="OnlyMyRailgun">Only my railgun</option>
  113. <option value="Level5Judgelight">Level 5 - Judgelight</option>
  114. <option value="Sister'sNoise">Sister's Noise</option>
  115. <option value="FinalPhase">Final Phase</option>
  116. </optgroup>
  117. <optgroup label="LoveLive!">
  118. <option value="SnowHalation">Snow Halation</option>
  119. <option value="Arifutarekanashiminohate">ありふれた悲しみの果て</option>
  120. </optgroup>
  121. <optgroup label="一抹多x">
  122. <option value="Eromangasensai">ヒトリゴト</option>
  123. </optgroup>
  124. </select>
  125. <input type="file" id="openFile" accept="audio/midi" onchange="openFile()">
  126. <button onclick="document.getElementById('openFile').click()">打开</button>
  127. <button id="play" onclick="play()">播放</button>
  128. <input type="range" id="time" value="0" readonly>
  129. <button onclick="stop()">停止</button>
  130. <label><input type="checkbox" onchange="modeChange()">弹奏模式</label>
  131. </div>
  132. <div style="margin: 0px 8px;"><i>如果你发现音乐好像有哪里不对,点一下停止然后重新开始播放就好!</i></div>
  133. <div id="keyboard" oncontextmenu="return false">
  134. </div>
  135. <!-- <script src="midi.min.js"></script> -->
  136. <script>
  137. console.log(MIDI)
  138. var player;
  139. window.onload = function () {
  140. var press = false;
  141. var setPressTimeout;
  142. var keyboard = document.getElementById("keyboard")
  143. var buttons = []
  144. var time = document.getElementById("time")
  145. // console.log({keyboard})
  146. for (let index = 0; index < 88; index++) {
  147. var button = document.createElement("BUTTON")
  148. button.className = 'huaji';
  149. // button.classList.add("huaji")
  150. function down_press(e) {
  151. var button = e.target
  152. if (!button.press) {
  153. button.classList.add("down")
  154. keyDown(index + 21)
  155. press = true
  156. }
  157. }
  158. function down_move(e) {
  159. var button = e.target
  160. if (press && !button.press) {
  161. button.classList.add("down")
  162. keyDown(index + 21)
  163. if (setPressTimeout) clearTimeout(setPressTimeout)
  164. }
  165. // console.log(index + 21 + "enter")
  166. }
  167. function up_press(e) {
  168. var button = e.target
  169. button.classList.remove("down")
  170. keyUp(index + 21)
  171. press = false
  172. }
  173. function up_move(e) {
  174. var button = e.target
  175. button.classList.remove("down")
  176. keyUp(index + 21)
  177. button.press = false
  178. if (press) setPressTimeout = setTimeout(function () {
  179. press = false
  180. }, 500);
  181. }
  182. button.touchMode = button.press = false
  183. button.down_press = button.onmousedown = function (e) {if (this.touchMode) this.touchMode = false; else down_press(e)}
  184. button.down_move = button.onmouseenter = function (e) {if (!this.touchMode) down_move(e)}
  185. button.up_press = button.onmouseup = up_press
  186. button.up_move = button.onmouseleave = up_move
  187. button.ontouchstart = function (event) {
  188. // console.log("touchStart", event)
  189. // event.preventDefault();
  190. this.touchMode = true
  191. down_press(event);
  192. this.lastPostionWasHere = true
  193. this.press = true;
  194. }
  195. button.ontouchend = function (event) {
  196. // console.log("touchEnd", event)
  197. // event.preventDefault();
  198. for (const key in event.changedTouches) {
  199. if (event.changedTouches.hasOwnProperty(key)) {
  200. const touch = event.changedTouches[key];
  201. for (const index in buttons) {
  202. if (buttons.hasOwnProperty(index)) {
  203. const button = buttons[index];
  204. if (touch.target === button) continue;
  205. button.up_press({target: button})
  206. }
  207. }
  208. }
  209. }
  210. this.press = false;
  211. }
  212. button.ontouchenter = function (event) {
  213. console.log("touchEnter", event)
  214. }
  215. button.innerText = MIDI.noteToKey[index + 21]
  216. buttons.push(button)
  217. keyboard.appendChild(button)
  218. }
  219. document.addEventListener("touchmove", function (event) {
  220. // event.preventDefault();
  221. // console.groupCollapsed("TouchMove")
  222. for (const key in event.changedTouches) {
  223. if (event.changedTouches.hasOwnProperty(key)) {
  224. const touch = event.changedTouches[key];
  225. for (const index in buttons) {
  226. if (buttons.hasOwnProperty(index)) {
  227. const button = buttons[index];
  228. if (touch.pageX > button.offsetLeft && touch.pageX < button.offsetLeft + button.offsetWidth && touch.pageY > button.offsetTop && touch.pageY < button.offsetTop + button.offsetHeight) {
  229. if (!button.lastPostionWasHere) {
  230. button.down_move({target: button})
  231. // console.log("MoveIn")
  232. // console.log(button)
  233. // console.log(button.offsetLeft, button.offsetWidth)
  234. // console.log(button.offsetTop, button.offsetHeight)
  235. button.lastPostionWasHere = true
  236. }
  237. } else {
  238. if (button.lastPostionWasHere) {
  239. button.up_move({target: button})
  240. // console.log("MoveOut")
  241. // console.log(button)
  242. // console.log(button.offsetLeft, button.offsetWidth)
  243. // console.log(button.offsetTop, button.offsetHeight)
  244. button.lastPostionWasHere = false
  245. }
  246. }
  247. }
  248. }
  249. // console.log(touch.pageX, touch.pageY)
  250. }
  251. }
  252. if (!(event.path.length > 4 && (event.path[event.path.length - 5].classList.contains("options") || event.path[event.path.length - 5].classList.contains("play-mode")))) event.preventDefault()
  253. // console.groupEnd()
  254. }, {passive: false})
  255. MIDI.loadPlugin({
  256. soundfontUrl: "./soundfont/",
  257. instruments: "acoustic_grand_piano",
  258. onprogress: function (state, progress) {
  259. // console.log(state, progress);
  260. },
  261. onsuccess: function () {
  262. var delay = 0; // play one note every quarter second
  263. var note = 50; // the MIDI note
  264. var velocity = 127; // how hard the note hits
  265. player = MIDI.Player
  266. player.addListener(function (data) {
  267. // console.log(data)
  268. var pianoKey = data.note - 21;
  269. // console.log('note', MIDI.noteToKey[data.note])
  270. if (data.message == 144) {
  271. buttons[pianoKey] && buttons[pianoKey].classList.add("down")
  272. } else {
  273. buttons[pianoKey] && buttons[pianoKey].classList.remove("down")
  274. }
  275. time.value = data.now * 1000
  276. time.max = data.end * 1000
  277. });
  278. player.timeWarp = 1; // speed the song is played back
  279. // play the note
  280. MIDI.setVolume(0, 127);
  281. }
  282. });
  283. function keyDown(note) {
  284. MIDI.noteOn(0, note, 127, 0);
  285. }
  286. function keyUp(note) {
  287. MIDI.noteOff(0, note, 0);
  288. }
  289. };
  290. function openFile() {
  291. // console.log("openFile",document.getElementById('openFile').files)
  292. if (document.getElementById('openFile').files.length > 0 && player) {
  293. playUrl(URL.createObjectURL(document.getElementById('openFile').files[0]))
  294. }
  295. }
  296. function openUrl(url) {
  297. player && playUrl(url)
  298. }
  299. function playUrl(url) {
  300. // console.log(player)
  301. player.loadFile(url, function () {
  302. player.start()
  303. document.getElementById("play").innerText = "暂停"
  304. });
  305. }
  306. function modeChange() {
  307. var keyboard = document.getElementById("keyboard")
  308. keyboard.classList[keyboard.classList.contains("play-mode") ? "remove" : "add"]("play-mode")
  309. }
  310. function play() {
  311. if (player && player.currentTime !== player.endTime) {
  312. if (player.playing) {
  313. document.getElementById("play").innerText = "播放"
  314. player.pause(true);
  315. } else {
  316. document.getElementById("play").innerText = "暂停"
  317. player.resume();
  318. }
  319. }
  320. }
  321. function stop() {
  322. if (player) {
  323. document.getElementById("play").innerText = "播放"
  324. player.stop();
  325. }
  326. }
  327. </script>
  328. </body>
  329. </html>