摘要:原文鏈接最近用做了一款鋼琴類應(yīng)用,名字定為自由鋼琴,人生如音樂,歡快且自由。就類似于多年前開發(fā)的鋼琴游戲,自由鋼琴只是換了的技術(shù),同時支持了鋼琴曲的自動播放功能。目前采用的技術(shù)架構(gòu)是框架。
原文鏈接
Hate 996? Come Here & Relax~
最近用Vue + Tone.js做了一款鋼琴類web應(yīng)用,名字定為自由鋼琴(AutoPiano),人生如音樂,歡快且自由。
此文權(quán)當(dāng)作該項目的總結(jié)和分享~
項目簡介自由鋼琴(AutoPiano)是利用HTML5技術(shù)開發(fā)的在線鋼琴應(yīng)用,致力于為鋼琴愛好者、音樂愛好者以及其他所有的創(chuàng)造者提供一個優(yōu)雅、簡潔的平臺,在學(xué)習(xí)工作之余可以享受鋼琴、音樂的美好。就類似于多年前Flash開發(fā)的鋼琴游戲,自由鋼琴只是換了H5的技術(shù),同時支持了鋼琴曲的自動播放功能。
AutoPiano支持鍵盤按鍵和鼠標(biāo)點擊播放,同時琴鍵上會有按鍵和音名提示。另外,AutoPiano還有教學(xué)的功能,一種方式是快速入門,通過簡易的譜子按鍵進(jìn)行演奏,另一種是演奏示例,通過鋼琴曲的自動播放來達(dá)到演示的目的。目前這兩個功能都在持續(xù)完善中,如下圖所示:
體驗地址: http://crystalworld.gitee.io/...開發(fā)這樣的應(yīng)用需要樂理知識嗎?項目地址: https://github.com/WarpPrism/...
當(dāng)然。基本的樂理知識還是要知道的,比如 CDEFGAB 音名、五線譜、調(diào)式、節(jié)奏等等還是要懂一點的。篇幅所限,這里就不展開討論了,推薦兩個網(wǎng)站:
https://www.bilibili.com/vide...
https://www.cnblogs.com/devym...
其他的就是編程知識了,以及如何將樂理知識轉(zhuǎn)化為程序邏輯。AutoPiano目前采用的技術(shù)架構(gòu)是vue框架 + tone.js。
鋼琴界面效果是怎么寫的?可以用CSS或貼圖。筆者這里直接用css實現(xiàn)了,考慮到鋼琴有黑鍵和白鍵,且黑鍵和白鍵有序地排列成 7:5的模式,所以實現(xiàn)起來并不復(fù)雜。
= 36 && note.id <= 40" @click="clickPianoKey($event, note.keyCode)">
.piano-wrap { width: 90%; margin: 20px auto; .piano-key-wrap { width: 100%; background: @dark; overflow: hidden; position: relative; .wkey { display: inline-block; width: 2.775%; height: 100%; margin: 0 auto; background: linear-gradient(white 10%, rgb(251, 251, 251) 92%, rgb(220, 220, 220) 93%, white 97%); border: solid 1px @dark; border-radius: 0 0 5px 5px; position: relative; &:active { background: linear-gradient(#eee 10%, #ffffd 60%, #bbb 93%, #ccc 97%); } } .wkey-active { background: linear-gradient(#eee 10%, #ffffd 60%, #bbb 93%, #ccc 97%); } .bkey-wrap { width: 20%; height: 0; position: absolute; top: 0; } .bkey-wrap1 {left: 0;} .bkey-wrap2 {left: 19.5%;} .bkey-wrap3 {left: 39%;} .bkey-wrap4 {left: 58.3%;} .bkey-wrap5 {left: 77.7%;} .bkey { display: inline-block; width: 10%; height: 70%; background: linear-gradient(#000 10%, rgb(86, 86, 86) 85%, #000 90%); border-radius: 0 0 3px 3px; position: absolute; top: 0; overflow: hidden; &:active { background: linear-gradient(rgb(86, 86, 86) 10%, #000 90%, #222 100%); } } .bkey-active { background: linear-gradient(rgb(86, 86, 86) 10%, #000 90%, #222 100%); } .bkey:nth-child(1) {left: 9%;} .bkey:nth-child(2) {left: 23%;} .bkey:nth-child(3) {left: 50%;} .bkey:nth-child(4) {left: 65%;} .bkey:nth-child(5) {left: 79%;} } }
codepen上也有很多這樣的例子供參考,不一定采用上述實現(xiàn):
https://codepen.io/search/pen...
相信只要合理地控制css變量和數(shù)值,大家能做出更好的 Piano 界面。
如何實現(xiàn)單個音符的播放?實現(xiàn)音頻播放,最簡單的就是利用HTML5 中的 audio 標(biāo)簽,通過觸發(fā)audio的play和pause方法,實現(xiàn)對音頻的控制,筆者一開始就是這么實現(xiàn)的。
//// //// 預(yù)先為每個音符都建立一個audio元素 initAudioDom() { var vm = this for (let i = 0; i< vm.Notes.length; i++) { var note = vm.Notes[i] $(".audios-wrap").append(`
上述是我的第一種實現(xiàn)方式,即不同音符觸發(fā)不同audio的播放。之后也許是出于好奇,嘗試了 Tone.js,通過Tone.js + 內(nèi)置采樣器實現(xiàn)對音頻播放更有效的控制,當(dāng)然,其提供的很多復(fù)雜功能都還沒用上。。。
// 初始化合成器 this.synth = SmapleLibrary.load({ instruments: "piano" }).toMaster() // 合成器觸發(fā)音頻釋放 playNote(notename = "C4", duration = "2n") { if (!this.synth) return this.synth.triggerAttackRelease(notename, duration); }
嗯,現(xiàn)在的代碼就符合音樂美學(xué)和代碼美學(xué)了,美滋滋。當(dāng)然筆者也期望Tone.js能快點完善中文文檔,不然上手還是很吃力的,感興趣的小伙伴可以先去其官網(wǎng)研究一番。
關(guān)于鋼琴曲的自動播放這一部分應(yīng)該是開發(fā)整個應(yīng)用最難的地方了,因為音樂或者說樂譜本身是相當(dāng)復(fù)雜的,根據(jù)百度百科的描述,五線譜起源于希臘,歷經(jīng)上千年不斷完善才成為現(xiàn)在的樂譜標(biāo)準(zhǔn)。而簡譜的出現(xiàn)則要晚的多,但依然五臟俱全,可以說,簡譜也不簡單。
筆者的實現(xiàn)思路是,以一種樂譜格式為載體,將樂譜轉(zhuǎn)換為一種程序可識別的格式,然后導(dǎo)入到程序中進(jìn)行播放,這種可識別格式如下所示,也是目前所采用的:
{ name: "小星星", step: "C", speed: "100", playState: "", mainTrack: ["1(1)"," 1(1)"," 5(1)"," 5(1)"," 6(1)"," 6(1)"," 5(2)"," 4(1)"," 4(1)"," 3(1)"," 3(1)"," 2(1)"," 2(1)"," 1(2)"," 5(1)"," 5(1)"," 4(1)"," 4(1)"," 3(1)"," 3(1)"," 2(2)"," 5(1)"," 5(1)"," 4(1)"," 4(1)"," 3(1)"," 3(1)"," 2(2)"," 1(1)"," 1(1)"," 5(1)"," 5(1)"," 6(1)"," 6(1)"," 5(2)"," 4(1)"," 4(1)"," 3(1)"," 3(1)"," 2(1)"," 2(1)"," 1(2)", "1<(1)", "1<(1)", "5<(1)", "5<(1)", "6<(1)", "6<(1)", "5<(2)", "4<(1)", "4<(1)", "3<(1)", "3<(1)", "2<(1)", "2<(1)", "1<(2)", "5<(1)", "5<(1)", "4<(1)", "4<(1)", "3<(1)", "3<(1)", "2<(2)", "5<(1)", "5<(1)", "4<(1)", "4<(1)", "3<(1)", "3<(1)", "2<(2)", "1<(1)", "1<(1)", "5<(1)", "5<(1)", "6<(1)", "6<(1)", "5<(2)", "4<(1)", "4<(1)", "3<(1)", "3<(1)", "2<(1)", "2<(1)", "1<(2)"], backingTrack: ["1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "6>(0.5)", "4>(0.5)", "6>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "6>(0.5)", "4>(0.5)", "6>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "7>>(0.5)", "5>(0.5)", "2>(0.5)", "5>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "3>(0.5)", "5>(0.5)"," 1(0.5)", "1>(0.5)", "4>(0.5)", "6>(0.5)"," 1(0.5)", "1>(0.5)", "3>(0.5)", "5>(0.5)"," 1(0.5)", "5>>(0.5)", "7>>(0.5)", "2>(0.5)", "5>(0.5)", "1>(0.5)", "3>(0.5)", "5>(0.5)"," 1(0.5)", "1>(0.5)", "4>(0.5)", "6>(0.5)"," 1(0.5)", "1>(0.5)", "3>(0.5)", "5>(0.5)"," 1(0.5)", "5>>(0.5)", "7>>(0.5)", "2>(0.5)", "5>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "6>(0.5)", "4>(0.5)", "6>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1>(0.5)", "6>(0.5)", "4>(0.5)", "6>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "7>>(0.5)", "5>(0.5)", "2>(0.5)", "5>(0.5)", "1>(0.5)", "5>(0.5)", "3>(0.5)", "5>(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "6(0.25)", "4(0.5)", "6(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "6(0.25)", "4(0.5)", "6(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "7>(0.75)", "5(0.25)", "2(0.5)", "5(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "3(0.25)", "5(0.5)", "1<(0.5)", "1(0.75)", "4(0.25)", "6(0.5)", "1<(0.5)", "1(0.75)", "3(0.25)", "5(0.5)", "1<(0.5)", "5>(0.75)", "7>(0.25)", "2(0.5)", "5(0.5)", "1(0.75)", "3(0.25)", "5(0.5)", "1<(0.5)", "1(0.75)", "4(0.25)", "6(0.5)", "1<(0.5)", "1(0.75)", "3(0.25)", "5(0.5)", "1<(0.5)", "5>(0.75)", "7>(0.25)", "2(0.5)", "5(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "6(0.25)", "4(0.5)", "6(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "1(0.75)", "6(0.25)", "4(0.5)", "6(0.5)", "1(0.75)", "5(0.25)", "3(0.5)", "5(0.5)", "7>(0.75)", "5(0.25)", "2(0.5)", "5(0.5)", "1>(2)"] }
額,是不是很復(fù)雜,很臃腫。。。它以簡譜為載體,通過特殊符號來標(biāo)記音高和時長,從而產(chǎn)生mainTrack和backingTrack兩個音軌,然后同步播放即可。這種實現(xiàn)雖然簡單,但有很多致命缺點:
不兼容通用的計算機(jī)樂譜格式,如musicxml
不能完全表示音樂的所有維度,比如很多鋼琴譜不止有兩個音軌
過于抽象和復(fù)雜,不實用,很難制作這種識別格式
音樂專業(yè)人士: what are you 弄啥嘞?
所以筆者轉(zhuǎn)向另一種實現(xiàn)思路,解析musicxml,但奈何這個過程耗時耗力,目前只完成了一半,部分細(xì)節(jié)還沒有完全解析正確,如果讀者有好的想法,可以在評論區(qū)留言探討。
歡迎貢獻(xiàn)協(xié)作貢獻(xiàn)代碼,直接PR
貢獻(xiàn)首頁展示的隨機(jī)歌詞: https://github.com/WarpPrism/...
貢獻(xiàn)快速入門的彈奏方法: https://github.com/WarpPrism/...
沒想到短時間內(nèi)能有這么多star(`?ω?′),嚇得晚上下班回去又繼續(xù)碼代碼。。。不過此項目仍不完善,還在不斷更新中,特別是入門彈奏譜子比較少,目前只有:
小星星
新年好
因為愛情
隱形的翅膀
蒲公英的約定
紙短情長
同桌的你
晴天
千與千尋主題曲
明天你好
青花瓷
...
都是筆者一個一個手打出來的T_T,能力有限,會的就這么多,所以是時候見證社區(qū)的力量了。
FORK時,請遵循GPL開源協(xié)議。最后
最后再貼一下體驗地址: http://crystalworld.gitee.io/...
歡迎體驗,分享。
解析musicxml的過程仍在進(jìn)行中,如果某一天成功了,那么示例演奏里面就會加入海量的歌曲,以供學(xué)習(xí),如果失敗了,額,那就是因為生活阻擋了我奮進(jìn)的腳步。。。
原創(chuàng)不易,轉(zhuǎn)載分享時請注明出處~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/103327.html
摘要:原文鏈接最近用做了一款鋼琴類應(yīng)用,名字定為自由鋼琴,人生如音樂,歡快且自由。就類似于多年前開發(fā)的鋼琴游戲,自由鋼琴只是換了的技術(shù),同時支持了鋼琴曲的自動播放功能。目前采用的技術(shù)架構(gòu)是框架。 原文鏈接 Hate 996? Come Here & Relax~ 最近用Vue + Tone.js做了一款鋼琴類web應(yīng)用,名字定為自由鋼琴(AutoPiano),人生如音樂,歡快且自由。 此文權(quán)...
摘要:突然靈機(jī)一動,能不能用自動化腳本彈奏一曲美妙的鋼琴曲呢今天就一起帶大家如何用實現(xiàn)自動化彈出一首天空之城首先一起來看看最終實現(xiàn)的演奏效果下面,我們就開始介紹如何實現(xiàn)這個自動化彈鋼琴腳本的。 ...
閱讀 1631·2021-10-27 14:13
閱讀 1868·2021-10-11 10:59
閱讀 3367·2021-09-24 10:26
閱讀 1925·2019-08-30 12:48
閱讀 3041·2019-08-30 12:46
閱讀 2033·2019-08-30 11:16
閱讀 1414·2019-08-30 10:48
閱讀 2740·2019-08-29 16:54