顺序 + 音乐: 我的自在钢琴开辟进程

原文链接

Hate 996? Come Here & Relax~

近来用Vue + Tone.js做了一款钢琴类web运用,名字定为自在钢琴(AutoPiano),人生如音乐,愉快且自在。

此文权看成该项目标总结和分享~

项目简介

自在钢琴(AutoPiano)是应用HTML5手艺开辟的在线钢琴运用,致力于为钢琴爱好者、音乐爱好者以及其他一切的创造者供应一个文雅、简约的平台,在进修事情之余能够享用钢琴、音乐的优美。就类似于多年前Flash开辟的钢琴游戏,自在钢琴只是换了H5的手艺,同时支撑了钢琴曲的自动播放功用。

AutoPiano支撑键盘按键和鼠标点击播放,同时琴键上会有按键和音名提醒。别的,AutoPiano另有教授教养的功用,一种体式格局是疾速入门,经由历程浅易的谱子按键举行吹奏,另一种是吹奏示例,经由历程钢琴曲的自动播放来到达演示的目标。如今这两个功用都在延续完美中,以下图所示:

《顺序 + 音乐: 我的自在钢琴开辟进程》

体验地点:
http://crystalworld.gitee.io/…

项目地点: https://github.com/WarpPrism/…

开辟如许的运用须要乐理学问吗?

固然。基础的乐理学问照样要知道的,比方 CDEFGAB 音名、五线谱、调式、节拍等等照样要懂一点的。篇幅所限,这里就不展开讨论了,引荐两个网站:

其他的就是编程学问了,以及怎样将乐理学问转化为顺序逻辑。AutoPiano如今采纳的手艺架构是vue框架 + tone.js。

钢琴界面结果是怎样写的?

能够用CSS或贴图。笔者这里直接用css完成了,考虑到钢琴有黑键和白键,且黑键和白键有序地排列成 7:5的形式,所以完成起来并不庞杂。

<div class="piano-key-wrap">
  <div class="piano-key wkey" v-for="note in Notes" :key="note.keyCode" :data-keyCode = "note.keyCode" v-if="note.type=='white'" @click="clickPianoKey($event, note.keyCode)"></div>
  <div class="bkey-wrap bkey-wrap1">
    <div class="piano-key bkey" v-for="note in Notes" :key="note.keyCode" :data-keyCode = "note.keyCode" v-if="note.type=='black' && note.id >= 36 && note.id <= 40" @click="clickPianoKey($event, note.keyCode)"></div>
  </div>
</div>
.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%, #ddd 60%, #bbb 93%, #ccc 97%);
      }
    }
    .wkey-active {
      background: linear-gradient(#eee 10%, #ddd 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上也有许多如许的例子供参考,不一定采纳上述完成:

https://codepen.io/search/pen…

置信只需合理地掌握css变量和数值,大家能做出更好的 Piano 界面。

怎样完成单个音符的播放?

完成音频播放,最简朴的就是应用HTML5 中的 audio 标签,经由历程触发audio的play和pause要领,完成对音频的掌握,笔者一开始就是这么完成的。

// <div class="audios-wrap" id="audios-wrap">
//   <audio src="" id="preloadAudio" ref="preloadAudio"></audio>
// </div>

// 预先为每一个音符都竖立一个audio元素
initAudioDom() {
  var vm = this
  for (let i = 0; i< vm.Notes.length; i++) {
    var note = vm.Notes[i]
    $('.audios-wrap').append(`<audio src='${note.url}' hidden='true' data-id='audio${i}' class='audioEle'>`);
  }
},
// 触发某个audio元素的播放
playNote(url) {
  var vm = this
  if (!url || typeof url != 'string') return;
  var audios = $('.audioEle');
  for (let i = 0; i< audios.length; i++) {
    let audio = audios[i];
    if (audio.src.indexOf(url) > -1) {
      var cloneAudioNode = audio.cloneNode()
      cloneAudioNode.play()
      cloneAudioNode.remove()
      break;
    }
  }
}

上述是我的第一种完成体式格局,即差别音符触发差别audio的播放。以后也许是出于猎奇,尝试了 Tone.js,经由历程Tone.js + 内置采样器完成对音频播放更有用的掌握,固然,其供应的许多庞杂功用都还没用上。。。

// 初始化合成器
this.synth = SmapleLibrary.load({
  instruments: "piano"
}).toMaster()

// 合成器触发音频开释
playNote(notename = 'C4', duration = '2n') {
  if (!this.synth) return
  this.synth.triggerAttackRelease(notename, duration);
}

嗯,如今的代码就相符音乐美学和代码美学了,美滋滋。固然笔者也希冀Tone.js能快点完美中文文档,不然上手照样很费劲的,感兴趣的小伙伴能够先去其官网研讨一番。

关于钢琴曲的自动播放

这一部份应该是开辟全部运用最难的处所了,由于音乐或者说曲谱自身是相称庞杂的,依据百度百科的形貌,五线谱起源于希腊,历经上千年不断完美才成为如今的曲谱规范。而简谱的涌现则要晚的多,但依旧五脏俱全,能够说,简谱也不简朴。

笔者的完成思绪是,以一种曲谱花样为载体,将曲谱转换为一种顺序可辨认的花样,然后导入到顺序中举行播放,这类可辨认花样以下所示,也是如今所采纳的:

  {
    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)']
  }

额,是否是很庞杂,很痴肥。。。它以简谱为载体,经由历程特殊符号来标记音高和时长,从而发生mainTrack和backingTrack两个音轨,然后同步播放即可。这类完成虽然简朴,但有许多致命瑕玷:

  1. 不兼容通用的计算机曲谱花样,如musicxml
  2. 不能完整示意音乐的一切维度,比方许多钢琴谱不止有两个音轨
  3. 过于笼统和庞杂,不实用,很难制造这类辨认花样
  4. 音乐专业人士: what are you 弄啥嘞?

所以笔者转向另一种完成思绪,剖析musicxml,但怎样这个历程耗时耗力,如今只完成了一半,部份细节还没有完整剖析准确,假如读者有好的主意,能够在批评区留言讨论。

迎接孝敬合作

没想到短时间内能有这么多star(`・ω・´),吓得晚上放工归去又继承码代码。。。不过此项目仍不完美,还在不断更新中,特别是入门弹奏谱子比较少,如今只要:

  • 小星星
  • 新年好
  • 由于恋爱
  • 隐形的翅膀
  • 蒲公英的商定
  • 纸短情长
  • 同桌的你
  • 好天
  • 千与千寻主题曲
  • 来日诰日你好
  • 青花瓷

都是笔者一个一个手打出来的T_T,才能有限,会的就这么多,所以是时刻见证社区的气力了。

FORK时,请遵照GPL开源协定。

末了

末了再贴一下体验地点:
http://crystalworld.gitee.io/…

迎接体验,分享。

剖析musicxml的历程仍在举行中,假如某一天胜利了,那末示例吹奏内里就会到场海量的歌曲,以供进修,假如失利了,额,那就是由于生涯阻挡了我奋进的脚步。。。

《顺序 + 音乐: 我的自在钢琴开辟进程》

原创不容易,转载分享时请说明出处~

    原文作者:棱镜
    原文地址: https://segmentfault.com/a/1190000018793537
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞