index.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. </optgroup>
  116. <optgroup label="LoveLive!">
  117. <option value="SnowHalation">Snow Halation</option>
  118. <option value="Arifutarekanashiminohate">ありふれた悲しみの果て</option>
  119. </optgroup>
  120. <optgroup label="一抹多x">
  121. <option value="Eromangasensai">ヒトリゴト</option>
  122. </optgroup>
  123. </select>
  124. <input type="file" id="openFile" accept="audio/midi" onchange="openFile()">
  125. <button onclick="document.getElementById('openFile').click()">打开</button>
  126. <button id="play" onclick="play()">播放</button>
  127. <input type="range" id="time" value="0" readonly>
  128. <button onclick="stop()">停止</button>
  129. <label><input type="checkbox" onchange="modeChange()">弹奏模式</label>
  130. </div>
  131. <div style="margin: 0px 8px;"><i>如果你发现音乐好像有哪里不对,点一下停止然后重新开始播放就好!</i></div>
  132. <div id="keyboard" oncontextmenu="return false">
  133. </div>
  134. <!-- <script src="midi.min.js"></script> -->
  135. <script>
  136. console.log(MIDI)
  137. var player;
  138. window.onload = function () {
  139. var press = false;
  140. var setPressTimeout;
  141. var keyboard = document.getElementById("keyboard")
  142. var buttons = []
  143. var time = document.getElementById("time")
  144. // console.log({keyboard})
  145. for (let index = 0; index < 88; index++) {
  146. var button = document.createElement("BUTTON")
  147. button.className = 'huaji';
  148. // button.classList.add("huaji")
  149. function down_press(e) {
  150. var button = e.target
  151. if (!button.press) {
  152. button.classList.add("down")
  153. keyDown(index + 21)
  154. press = true
  155. }
  156. }
  157. function down_move(e) {
  158. var button = e.target
  159. if (press && !button.press) {
  160. button.classList.add("down")
  161. keyDown(index + 21)
  162. if (setPressTimeout) clearTimeout(setPressTimeout)
  163. }
  164. // console.log(index + 21 + "enter")
  165. }
  166. function up_press(e) {
  167. var button = e.target
  168. button.classList.remove("down")
  169. keyUp(index + 21)
  170. press = false
  171. }
  172. function up_move(e) {
  173. var button = e.target
  174. button.classList.remove("down")
  175. keyUp(index + 21)
  176. button.press = false
  177. if (press) setPressTimeout = setTimeout(function () {
  178. press = false
  179. }, 500);
  180. }
  181. button.down_press = button.onmousedown = down_press
  182. button.down_move = button.onmouseenter = down_move
  183. button.up_press = button.onmouseup = up_press
  184. button.up_move = button.onmouseleave = up_move
  185. button.ontouchstart = function (event) {
  186. // console.log("touchStart", event)
  187. // event.preventDefault();
  188. down_press(event);
  189. this.lastPostionWasHere = true
  190. this.press = true;
  191. }
  192. button.ontouchend = function (event) {
  193. // console.log("touchEnd", event)
  194. // event.preventDefault();
  195. for (const index in buttons) {
  196. if (buttons.hasOwnProperty(index)) {
  197. const button = buttons[index];
  198. button.up_press({target: button})
  199. }
  200. }
  201. this.press = false;
  202. }
  203. button.ontouchenter = function (event) {
  204. console.log("touchEnter", event)
  205. }
  206. button.innerText = MIDI.noteToKey[index + 21]
  207. buttons.push(button)
  208. keyboard.appendChild(button)
  209. }
  210. document.addEventListener("touchmove", function (event) {
  211. // event.preventDefault();
  212. // console.groupCollapsed("TouchMove")
  213. for (const key in event.changedTouches) {
  214. if (event.changedTouches.hasOwnProperty(key)) {
  215. const touch = event.changedTouches[key];
  216. for (const index in buttons) {
  217. if (buttons.hasOwnProperty(index)) {
  218. const button = buttons[index];
  219. if (touch.pageX > button.offsetLeft && touch.pageX < button.offsetLeft + button.offsetWidth && touch.pageY > button.offsetTop && touch.pageY < button.offsetTop + button.offsetHeight) {
  220. if (!button.lastPostionWasHere) {
  221. button.down_move({target: button})
  222. // console.log("MoveIn")
  223. // console.log(button)
  224. // console.log(button.offsetLeft, button.offsetWidth)
  225. // console.log(button.offsetTop, button.offsetHeight)
  226. button.lastPostionWasHere = true
  227. }
  228. } else {
  229. if (button.lastPostionWasHere) {
  230. button.up_move({target: button})
  231. // console.log("MoveOut")
  232. // console.log(button)
  233. // console.log(button.offsetLeft, button.offsetWidth)
  234. // console.log(button.offsetTop, button.offsetHeight)
  235. button.lastPostionWasHere = false
  236. }
  237. }
  238. }
  239. }
  240. // console.log(touch.pageX, touch.pageY)
  241. }
  242. }
  243. 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()
  244. // console.groupEnd()
  245. }, {passive: false})
  246. MIDI.loadPlugin({
  247. soundfontUrl: "./soundfont/",
  248. instruments: "acoustic_grand_piano",
  249. onprogress: function (state, progress) {
  250. // console.log(state, progress);
  251. },
  252. onsuccess: function () {
  253. var delay = 0; // play one note every quarter second
  254. var note = 50; // the MIDI note
  255. var velocity = 127; // how hard the note hits
  256. player = MIDI.Player
  257. player.addListener(function (data) {
  258. // console.log(data)
  259. var pianoKey = data.note - 21;
  260. // console.log('note', MIDI.noteToKey[data.note])
  261. if (data.message == 144) {
  262. time.value = data.now*1000
  263. time.max = data.end*1000
  264. buttons[pianoKey] && buttons[pianoKey].classList.add("down")
  265. } else {
  266. buttons[pianoKey] && buttons[pianoKey].classList.remove("down")
  267. }
  268. });
  269. player.timeWarp = 1; // speed the song is played back
  270. // play the note
  271. MIDI.setVolume(0, 127);
  272. }
  273. });
  274. function keyDown(note) {
  275. MIDI.noteOn(0, note, 127, 0);
  276. }
  277. function keyUp(note) {
  278. MIDI.noteOff(0, note, 0);
  279. }
  280. };
  281. function openFile() {
  282. // console.log("openFile",document.getElementById('openFile').files)
  283. if (document.getElementById('openFile').files.length > 0 && player) {
  284. playUrl(URL.createObjectURL(document.getElementById('openFile').files[0]))
  285. }
  286. }
  287. function openUrl(url) {
  288. player && playUrl(url)
  289. }
  290. function playUrl(url) {
  291. // console.log(player)
  292. player.loadFile(url, function () {
  293. player.start()
  294. document.getElementById("play").innerText = "暂停"
  295. });
  296. }
  297. function modeChange() {
  298. var keyboard = document.getElementById("keyboard")
  299. keyboard.classList[keyboard.classList.contains("play-mode") ? "remove" : "add"]("play-mode")
  300. }
  301. function play() {
  302. if (player && player.currentTime !== player.endTime) {
  303. if (player.playing) {
  304. document.getElementById("play").innerText = "播放"
  305. player.pause(true);
  306. } else {
  307. document.getElementById("play").innerText = "暂停"
  308. player.resume();
  309. }
  310. }
  311. }
  312. function stop() {
  313. if (player) {
  314. document.getElementById("play").innerText = "播放"
  315. player.stop();
  316. }
  317. }
  318. </script>
  319. </body>
  320. </html>