diff --git a/README.md b/README.md index 72e31c2..e1dce73 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ - 空格: 播放 - 双击时间轴: 从双击的位置开始播放 - 在时间轴上拖拽: 设置重复区间 +- 鼠标中键时间轴:将时间设置到点击位置,播放状态保持上一刻 - 按住空白拖动: 在当前音轨绘制一个音符 - 按住音符左半边拖动: 改变位置 - 按住音符右半边拖动: 改变时长 @@ -51,8 +52,8 @@ ## 小细节 - 滑动条,如果旁边有数字,点击就可以恢复初始值。 -- 多次点击“笔”右侧的选择工具,可以切换选择模式。 - +- 多次点击“笔”右侧的选择工具,可以切换选择模式。(注意,只能选中当前音轨的音符) +- 点击某个音符可以选中该轨。 ## 文件结构 ``` @@ -96,6 +97,9 @@ iconfont.woff2 ``` +## bug to fix +当前绘制为了加速使用二分法,排序依据是开始时间,没有考虑音符的长度,因此导致先开始但时长超长的音符在开头在视野外时不绘制。 + ## 重要更新记录 ### 2024 2 9 在今年完成了所有基本功能!本次更新了设置相关,简单地设计了调性分析的算法,已经完全可以用了! diff --git a/app.js b/app.js index b9bac18..935984e 100644 --- a/app.js +++ b/app.js @@ -627,13 +627,17 @@ function App() { this.scroll2(((this.time / this.dt - 1) | 0) * this._width, this.scrollY); // 留一点空位 } }, + /** + * 在指定的毫秒数开始播放 + * @param {Number} at 开始的毫秒数 如果是负数,则从当下开始 + */ start: (at) => { const a = this.AudioPlayer.audio; if (a.readyState != 4) return; if (at >= 0) a.currentTime = at / 1000; this.AudioPlayer._crossFlag = false; // 置此为假可以暂时取消重复区间 this.MidiPlayer.restart(); - if(a.readyState == 4) a.play(); + if (a.readyState == 4) a.play(); else a.oncanplay = () => { a.play(); a.oncanplay = null; @@ -963,8 +967,8 @@ function App() { }); this.MidiAction.clearSelected(); this.MidiAction.selected = copy; - // 粘贴到光标位置 目前没有做播放 因此假设位置是this.idXstart - minX = this.idXstart - minX; + // 粘贴到光标位置 + minX = (this.time / this.dt - minX) | 0; copy.forEach((note) => { note.x1 += minX; note.x2 += minX; @@ -972,6 +976,7 @@ function App() { this.MidiAction.midi.push(...copy); this.MidiAction.midi.sort((a, b) => a.x1 - b.x1); this.MidiAction.updateView(); + this.snapshot.save(); }, 'Ctrl+B': () => { // 收回面板 const channelDiv = this.MidiAction.channelDiv.container.parentNode; @@ -1186,13 +1191,13 @@ function App() { const input = document.createElement("input"); input.type = "file"; input.onchange = () => { - this.Saver.parse(input.files[0]).then((data) => { + this.Saver.parse(input.files[0]).then((data) => { // 再读取音频看看是否成功 const fileReader = new FileReader(); fileReader.onload = (e) => { // 设置音频源 缓存到浏览器 this.AudioPlayer.createAudio(e.target.result).then(() => { - if(this.AudioPlayer.name != data[0].name && + if (this.AudioPlayer.name != data[0].name && !confirm(`音频文件与分析结果(${data[0].name})不同,是否继续?`)) return; this.Saver.import(data); @@ -1298,6 +1303,7 @@ function App() { this.dt = obj.dt; this.Keyboard.freqTable.A4 = obj.A4; this.Spectrogram.spectrogram = data[1]; + this.snapshot.save(); }, write: (fileName = this.AudioPlayer.name) => { const data = this.Saver.export(); @@ -1420,15 +1426,14 @@ function App() { }; document.addEventListener('mouseup', up); return; } - this.Keyboard.mousedown(); // 以下在没有频谱数据时不启用 - if (!this.Spectrogram._spectrogram) return; - if (e.button == 0) this.MidiAction.onclick_L(e); // midi音符相关 - else if (e.button == 2 && e.shiftKey) { - this.spectrum.contextMenu.show(e); - e.stopPropagation(); - return; - } else this.MidiAction.clearSelected(); // 取消音符选中 + if (this.Spectrogram._spectrogram) { + if (e.button == 0) this.MidiAction.onclick_L(e); // midi音符相关 + else if (e.button == 2 && e.shiftKey) { + this.spectrum.contextMenu.show(e); + e.stopPropagation(); + } else this.MidiAction.clearSelected(); // 取消音符选中 + } this.Keyboard.mousedown(); // 将发声放到后面,因为onclick_L会改变选中的音轨 }); this.spectrum.addEventListener('mousemove', this.trackMouseY); this.spectrum.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); }); @@ -1444,24 +1449,31 @@ function App() { e.stopPropagation(); }); this.timeBar.addEventListener('mousedown', (e) => { - if (e.button) return; // 左键拖拽 - const x = (e.offsetX + this.scrollX) / this._width * this.dt; // 毫秒数 - let setRepeat = (e) => { - let newX = (e.offsetX + this.scrollX) / this._width * this.dt; - if (newX > x) { - this.TimeBar.repeatStart = x; - this.TimeBar.repeatEnd = newX; - } else { - this.TimeBar.repeatEnd = x; - this.TimeBar.repeatStart = newX; - } - }; - let removeEvents = () => { - this.timeBar.removeEventListener('mousemove', setRepeat); - document.removeEventListener('mouseup', removeEvents); - }; - this.timeBar.addEventListener('mousemove', setRepeat); - document.addEventListener('mouseup', removeEvents); + switch (e.button) { + case 0: + const x = (e.offsetX + this.scrollX) / this._width * this.dt; // 毫秒数 + let setRepeat = (e) => { + let newX = (e.offsetX + this.scrollX) / this._width * this.dt; + if (newX > x) { + this.TimeBar.repeatStart = x; + this.TimeBar.repeatEnd = newX; + } else { + this.TimeBar.repeatEnd = x; + this.TimeBar.repeatStart = newX; + } + }; + let removeEvents = () => { + this.timeBar.removeEventListener('mousemove', setRepeat); + document.removeEventListener('mouseup', removeEvents); + }; + this.timeBar.addEventListener('mousemove', setRepeat); + document.addEventListener('mouseup', removeEvents); + break; + case 1: // 中键跳转位置但不改变播放状态 + this.time = (e.offsetX + this.scrollX) / this._width * this.dt; + this.AudioPlayer.audio.currentTime = this.time / 1000; + break; + } }); this.keyboard.addEventListener('wheel', (e) => { this.scroll2(this.scrollX, this.scrollY - e.deltaY); // 只能上下移动 diff --git a/index.html b/index.html index f1cdc07..4784665 100644 --- a/index.html +++ b/index.html @@ -321,12 +321,14 @@