| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- const LINE_SPACE = 20;
- const START_SPACE = 50;
- const END_SPACE = 50;
- const RGB_WHITE = "rgb(255, 255, 255)";
- const RGB_BLACK = "rgb(0, 0, 0)";
- const RGB_GREEN = "rgb(0, 255, 0)";
- const RGB_BLUE = "rgba(0, 0, 255)";
- const RGB_BLUE_LIGHT = "rgba(0, 155, 255)";
- const RGB_RED = "rgba(255, 0, 0)";
- const RGB_YELLOW = "rgba(255, 255, 0)";
- const RGB_GRAY = "rgba(55, 55, 55)";
- var menu_right = document.getElementById("menu-right");
- var btn_add = menu_right.children[0];
- var btn_delete = menu_right.children[1];
- var btn_import = menu_right.children[2];
- var btn_export = menu_right.children[3];
- var audio = new Audio("000.mp3");
- var canvas = document.querySelector("canvas")
- var ctx = canvas.getContext("2d");
- var music_progress_bar = document.getElementById("music-progress-bar");
- var file_name = "tempo";
- var beats = [0.267,1.013,1.765,2.245,2.784,3.515,3.776,4.256,4.784,5.765,6.267,6.768,7.515,7.765,8.267,8.768,9.765,10.267,10.795,11.509,11.765,12.251,12.779,13.765,14.256,14.768,15.259,16.261,16.768,17.312,17.787,18.267,18.768,19.515,19.765,20.267,20.768,21.765,22.267,22.768,23.509,23.781,24.261,24.789,25.765,26.245,26.779,27.509,27.765,28.261,28.768,29.771,30.267,30.752,31.259,31.771,32.261,32.768,33.259,33.765,34.24,34.768,35.28,35.787,36.256,36.768,37.259,37.765,38.267,38.779,39.275,39.765,40.256,40.517,40.768,41.269,41.765,42.261,42.768,43.28,43.787,44.267,44.517,44.768,45.019,45.28,45.781,46.267,46.763,47.269,47.728,48.245,48.768,49.024,49.771,50.027,50.763,51.264,51.76,52.027,52.773,53.253,53.765,54.027,54.768,55.243,55.76,56.027,56.768,57.253,57.765,58.251,58.763,59.259,59.765,60.021,60.768,61.259,61.765,62.245,62.768,63.264,63.765,64.245,64.768,65.259,65.765,66.027,66.763,67.264,67.76,68.24,68.768,69.264,69.765,70.021,70.763,71.264,71.749,72.021,72.768,73.264,73.771,74.256,74.763,75.259,75.765,76.021,76.517,76.768,77.264,77.771,78.256,78.768,79.264,79.765,80.245,80.768,81.243,81.765,82.245,82.768,83.264,83.765,84.251,84.763,85.259,85.765,86.261,86.763,87.248,87.765,88.251,88.763,89.259,89.765,90.261,90.763,91.269,91.765,92.251,92.763,93.259,93.792,94.256,94.763,95.269,95.771,96.261,96.763,97.248,97.771,98.251,98.768,99.259,99.765,100.256,100.773,101.248,101.765,102.272,102.768,103.269,103.765,104.261,104.768,105.259,105.771,106.245,106.768,107.259,107.765,108.256,108.763,109.248,109.765,110.267,110.768,111.253,111.781,112.245,112.768,113.269,113.771,114.021,114.763,115.264,115.76,116.021,116.763,117.253,117.765,118.027,118.768,119.243,119.76,120.027,120.768,121.269,121.765,122.251,122.768,123.259,123.765,124.021,124.768,125.259,125.771,126.245,126.768,127.264,127.765,128.245,128.768,129.259,129.765,130.027,130.763,131.264,131.76,132.24,132.768,133.264,133.771,134.021,134.768,135.259,135.749,136.021,136.763,137.259,137.765,138.256,138.763,139.264,139.765,140.021,140.517,140.768,141.264,141.771,142.261,142.768,143.264,143.765,144.261,145.269,145.669,146.24,146.992,147.333,148.267,149.52,150.213,150.997,152.267,153.285,153.749,154.704,155.579,156.277,157.445,157.733,158.256,158.528,159.344,159.813,160.251,160.768,161.253,161.765,162.245,162.768,163.509,163.781,164.256,164.768,165.264,165.765,166.272,166.784,167.515,167.771,168.256,168.768,169.019,169.771,170.245,170.768,171.509,171.765,172.256,172.992,173.765,174.267,174.768,175.264,175.781,176.256,176.768,177.248,177.765,178.261,178.768,179.253,179.749,180.261,180.763,181.253,181.765,182.261,182.763,183.248,183.749,184.256,184.768,185.259,185.765,186.245,186.763,187.253,187.76,188.261,188.768,189.259,189.765,190.245,190.768,191.264,191.765,192.245,192.763,193.253,193.765,194.261,194.768,195.259,195.76,196.245,196.757,197.248,197.765,198.261,198.763,199.264,199.744,200.261,200.768,201.253,201.765,202.261,202.757,203.259,203.765,204.261,204.517,204.768,205.253,205.765,206.251,206.768,207.264,207.765,211.968];
- var duration = 213.1;
- var time_table_width = duration * LINE_SPACE * 10;
- var offsetX = 0;
- var btn_control = {x: START_SPACE - 40, y: 300, width: 80, height: 40, on: false};
- var last_right_mouse_btn_x = null;//记录最后一次右击canvas的x坐标
- function drawBG(){
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- canvas.width = innerWidth;
- canvas.height = innerHeight;
- ctx.fillStyle = RGB_BLACK;
- ctx.fillRect(0,0,canvas.width,canvas.height);
- ctx.lineWidth = 1;
- ctx.strokeStyle = RGB_WHITE;
- ctx.beginPath();
- for (let x = 0; x <= time_table_width; x += LINE_SPACE){
- let actualX = x + START_SPACE + offsetX;
- if (actualX < 0) {
- continue;
- }
- else if (actualX > innerWidth) {
- break;
- }
- ctx.moveTo(actualX, 0);
- if (x % (LINE_SPACE * 10) == 0) {
- ctx.lineTo(actualX, 100);
- } else {
- ctx.lineTo(actualX, 60);
- }
- }
- ctx.closePath();
- ctx.stroke();
- ctx.fillStyle = RGB_WHITE;
- ctx.textBaseline = "top";
- ctx.textAlign = "center";
- ctx.font = "24px Arial"
- for (let t = 0; t <= duration; t++){
- ctx.fillText(t.toString(), t * LINE_SPACE * 10 + START_SPACE + offsetX, 110);
- if (t + START_SPACE + offsetX > innerWidth) {
- break;
- }
- }
- let progressX = audio.currentTime * LINE_SPACE * 10 + START_SPACE + offsetX;
- btn_control.x = progressX - btn_control.width / 2;
- if (audio.currentTime * LINE_SPACE * 10 + START_SPACE > innerWidth / 2) {
- offsetX = innerWidth / 2 - (audio.currentTime * LINE_SPACE * 10 + START_SPACE);
- } else {
- offsetX = 0;
- }
- if (audio.paused) {
- ctx.strokeStyle = RGB_YELLOW;
- } else {
- ctx.strokeStyle = RGB_GREEN;
- }
- ctx.beginPath();
- ctx.moveTo(progressX, 0);
- ctx.lineTo(progressX, btn_control.y);
- ctx.closePath();
- ctx.stroke();
- ctx.font = "24px Arial"
- if (audio.paused) {
- ctx.fillStyle = RGB_YELLOW;
- ctx.fillRect(progressX - btn_control.width / 2, btn_control.y, btn_control.width, btn_control.height);
- ctx.fillStyle = RGB_GRAY;
- ctx.fillText("暂停", progressX, btn_control.y + (btn_control.height - 22) / 2);
- } else {
- ctx.fillStyle = RGB_GREEN;
- ctx.fillRect(progressX - btn_control.width / 2, btn_control.y, btn_control.width, btn_control.height);
- ctx.fillStyle = RGB_GRAY;
- ctx.fillText("播放", progressX, btn_control.y + (btn_control.height - 22) / 2);
- }
-
- ctx.strokeStyle = RGB_BLUE_LIGHT;
- ctx.beginPath();
- for (let beat of beats) {
- let x = beat * LINE_SPACE * 10 + START_SPACE + offsetX;
- if (x < 0) {
- continue;
- } else if (x > innerWidth) {
- break;
- }
- ctx.moveTo(x, 0);
- ctx.lineTo(x, 200);
- if (Math.abs(beat - audio.currentTime) < 0.1) {
- ctx.fillStyle = RGB_GREEN;
- } else {
- ctx.fillStyle = RGB_BLUE_LIGHT;
- }
- ctx.fillRect(x - 28, 200, 56, 20);
- ctx.font = "14px Arial"
- ctx.fillStyle = RGB_WHITE;
- ctx.fillText(beat.toString(), x, 204);
- }
- ctx.closePath();
- ctx.stroke();
- }
- function download(filename, text) {
- var aTag = document.createElement("a");
- aTag.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
- aTag.setAttribute("download", filename);
- aTag.style.display = "none";
- document.body.appendChild(aTag);
- aTag.click();
- document.body.removeChild(aTag);
- }
- canvas.oncontextmenu = function(e){
- e.preventDefault();
- menu_right.style.display = "block";
- menu_right.style.left = e.offsetX+"px";
- menu_right.style.top = e.offsetY+"px";
- }
- canvas.onclick = function(){
- menu_right.style.display = "none";
- }
- menu_right.onclick = function() {
- menu_right.style.display = "none";
- }
- canvas.onmousemove = function(e) {
- if (btn_control.on) {
- audio.currentTime += e.movementX / (LINE_SPACE * 10);
- }
- }
- var select_beat_index = undefined;
- canvas.onmousedown = function(e) {
- if (
- e.clientX > btn_control.x &&
- e.clientX < btn_control.x + btn_control.width &&
- e.clientY > btn_control.y &&
- e.clientY < btn_control.y + btn_control.height
- ) {
- btn_control.on = true;
- }
- if (e.offsetY >= 200 && e.offsetY <= 220) {
- for (let i in beats) {
- let beat = beats[i];
- let x = beat * LINE_SPACE * 10 + START_SPACE + offsetX;
- if (Math.abs(e.offsetX - x) <= 20) {
- select_beat_index = parseInt(i);
- break;
- }
- }
- }
- if (e.button == 2) {
- last_right_mouse_btn_x = e.offsetX;
- }
- }
- canvas.onmouseup = function(e) {
- if (btn_control.on) {
- if (
- e.clientX > btn_control.x &&
- e.clientX < btn_control.x + btn_control.width &&
- e.clientY > btn_control.y &&
- e.clientY < btn_control.y + btn_control.height
- ) {
- if (audio.paused) {
- audio.play();
- } else {
- audio.pause();
- }
- }
- btn_control.on = false;
- }
- }
- btn_import.onclick = function() {
- //创建导入按钮的临时标签
- let inputTag = document.createElement("input");
- inputTag.setAttribute("type","file");
- inputTag.setAttribute("multiple", "");
- inputTag.style.display = "none";
- //监听传入文件
- inputTag.onchange = function() {
- //确保导入的文件有音乐文件和节奏文件
- if (inputTag.files.length != 2) {
- alert("请同时导入音乐的MP3和JSON");
- return;
- }
- let musicFile = null;
- let rhythmFile = null;
- for (let i = 0; i < inputTag.files.length; i++) {
- let file = inputTag.files[i];
- if (file.name.endsWith(".mp3")) {
- musicFile = file;
- } else if (file.name.endsWith(".json")) {
- rhythmFile = file;
- }
- }
- if (!musicFile || !rhythmFile) {
- alert("请同时导入音乐的MP3和JSON");
- return;
- }
- //检测导入进度并提示
- let imortProgress = 0;
- let checkImortProgress = () => {
- imortProgress++;
- if (imortProgress == 2) {
- alert("导入完成");
- }
- }
- //记录音乐名称和设置音乐播放的本地地址
- file_name = musicFile.name.split('.')[0];
- audio.src = URL.createObjectURL(musicFile);
- //读取节拍文件内容
- let rhythmReader = new FileReader();
- rhythmReader.onloadend = (e) => {
- beats = JSON.parse(e.target.result);
- checkImortProgress();
- }
- rhythmReader.readAsText(rhythmFile, "utf-8");
- //读取音乐文件的信息
- let musicReader = new FileReader();
- musicReader.onload = function(e) {
- let audioContext = new AudioContext({
- sampleRate: 48000,
- });
- audioContext.decodeAudioData(e.target.result, function(audioBuffer) {
- duration = parseFloat(audioBuffer.duration.toFixed(1));
- time_table_width = duration * LINE_SPACE * 10;
- checkImortProgress();
- });
- }
- musicReader.readAsArrayBuffer(musicFile);
- }
- //触发并销毁导入按钮的临时标签
- document.body.appendChild(inputTag);
- inputTag.click();
- document.body.removeChild(inputTag);
- }
- btn_add.onclick = function() {
- let beat = (last_right_mouse_btn_x - START_SPACE - offsetX) / (LINE_SPACE * 10);
- beat = parseFloat(beat.toFixed(3));
- if (beat < 0 || beat > duration) {
- return;
- }
- let beatsLength = beats.length;
- for (let i = beatsLength - 1; i >= 0; i--) {
- let currentBeat = beats[i];
- if (currentBeat > beat) {
- beats[i + 1] = currentBeat;
- beats[i] = null;
- } else {
- beats[i + 1] = beat;
- break;
- }
- if (i == 0 && beats[i] == null) {
- beats[i] = beat;
- }
- }
- }
- btn_delete.onclick = function() {
- if (typeof select_beat_index == "number") {
- beats.splice(select_beat_index, 1);
- select_beat_index = undefined;
- }
- }
- btn_export.onclick = function() {
- download(file_name + ".json", JSON.stringify(beats));
- }
- music_progress_bar.onclick = function(e) {
- audio.currentTime = e.offsetX / music_progress_bar.clientWidth * audio.duration;
- }
- function draw() {
- requestAnimationFrame(draw);
- drawBG();
- try {
- music_progress_bar.max = audio.duration;
- music_progress_bar.value = audio.currentTime;
- } catch(e) {}
- }
- draw();
|