Address: experience wscats. Making. IO/piano/build…

Project address: github.com/Wscats/pian…

Use the keyboard 8 keys to play a dandelion agreement to 996 of their own or the moon on behalf of my heart to her Tanabata, very simple ~

This project is implemented with just a few simple front-end technologies for every music loving coder 🎹

If you like it or if it’s helpful to you, give me a thumbs up and support 😊

Technical points and directory structure

Instead of using mainstream frameworks (React, Vue and Angular) and popular technologies, Omi framework (JSX+WebComponents) and Omil’s single-file component SFCs loader are used. Component communication is based on Proxy features. Eno-snippets are compiled in real-time with AST and re in combination with VScode, and Eno or.omi components relieve some of the local compilation pressure of Webpack, but of course other familiar techniques are not mentioned here.

  • src
    • assets
    • element
      • app-piano
        • Songs Simple Piano Catalogue
        • App-piano. Eno single file component
        • App-pian. js compiled JS file of app-pian. js component
        • Notes.js keyboard keys and notes mapping
    • Index.js component root container, configuredProxyThe communication method of
  • public
    • Samples /piano single-note materials
app-piano.eno Develop a single file component that you need to write
app-piano.js afterEno-SnippetsModify or save the fileHello.enoAfter the plug-in into the JS file

In the image below, the code on the left is a single file component with the.eno suffix that we wrote, and on the right is a.js file with the Eno Snippets suffix.

Develop & Installation

Develop, build, and run.

Get the remote repository code
git clone https://github.com/Wscats/piano
# enter directory
cd piano
# install dependencies
npm install
# Start a project
npm start
# Visit http://localhost:3000 in your browser
Copy the code

Install using NPM package manager.

npm install omi-piano
Copy the code

Run or publish their own performance version.

# enter directory
cd omi-piano
# install dependencies
npm install
# Start a project
npm start
# Release your own performance version
npm run build
Copy the code

Simple music theory

First we cram some music foundation, collect in advance of the most basic piano tone material, each note corresponds to A. Mp3 files, use an object record rise, like the following, for example A refers to CDEFGAB here have A is in La, this is the most basic music theory, have reminded you of music lessons as A child, A staff on a drawing board.

export default {
  A2: "./samples/piano/a54.mp3".A3: "./samples/piano/a69.mp3".A4: "./samples/piano/a80.mp3".A5: "./samples/piano/a74.mp3".A6: "./samples/piano/a66.mp3".'A#3': "./samples/piano/b69.mp3".'A#4': "./samples/piano/b80.mp3".'A#5': "./samples/piano/b74.mp3".'A#6': "./samples/piano/b66.mp3".// other... 
}
Copy the code

Of course, we use numbers for equivalent substitution here to reduce the difficulty for beginners. See table 1, which is equivalent to middle C, also known as Do. Since many songs use keys in the middle part of the piano that are more dense, we default the middle key corresponding to number keys:

1 === C4 === Do

The number keys 1 2 3 4 5 6 7
have C4 D4 E4 F4 G4 A4 B4
Notes. Do Re Mi Fa Sol La Si

Here is a special diagram for our understanding:

, of course, the actual situation and the distinction between the whole and chromatic, such as A semitone is A #, alto, soprano and times soprano, alto here we use A4, A5 said soprano, A6 said times high, so form can continue to organize more clear, when we are going to play the mediant A4, you just need to press the number keys on the keyboard 6, if you want to play A high-pitched A5, We only need to use the combination key Option+6, and we only need to draw inferences from one example to know the keyboard keys corresponding to each note.

Double bass C2 D2 E2 F2 G2 A2 B2
The Shift key + (1-7) Shift+1 Shift+2 Shift+3 Shift+4 Shift+5 Shift+6 Shift+7
bass C3 D3 E3 F3 G3 A3 B3
Ctrl + (1-7) Ctrl+1 Ctrl+2 Ctrl+3 Ctrl+4 Ctrl+5 Ctrl+6 Ctrl+7
alto C4 D4 E4 F4 G4 A4 B4
The number keys 1 to 7 1 2 3 4 5 6 7
The high C5 D5 E5 F5 G5 A5 B5
Option + (1-7) Option+1 Option+2 Option+3 Option+4 Option+5 Option+6 Option+7
Times as high C6 D6 E6 F6 G6 A6 B6
The Command + (1-7) Command+1 Command+2 Command+3 Command+4 Command+5 Command+6 Command+7
Notes. Do Re Mi Fa Sol La Si

Above is the chromatic table, and here is the chromatic table:

Times low chromatic C#2 D#2 F#2 G#2 A#2
Shift+ Shift+q Shift+w Shift+e Shift+r Shift+t
Low chromatic C#3 D#3 F#3 G#3 A#3
Ctrl+ Ctrl+q Ctrl+w Ctrl+e Ctrl+r Ctrl+t
The chromatic C#4 D#4 F#4 G#4 A#4
The letters q w e r t
High chromatic C#5 D#5 F#5 G#5 A#5
Option+ Option+q Option+w Option+e Option+r Option+t
Times as high chromatic C#6 D#6 F#6 G#6 A#6
Command+ Command+q Command+w Command+e Command+r Command+t

So all we need to do now is use the 5 letter keys (q,w,e,r,t) + 4 function keys (Shift,Control,Option, and Command) + 7 number keys (1,2,3,4,5,6,7) for a total of 16 keys, Play the piano with 60 single notes (35 whole notes +25 semitones). A simple piano piece can be composed with a few simple chords instead of that many.

Building a Piano Interface

We need to use HTML to draw our piano interface. We can refer to the materials of Codepen and CodesandBox. Here I used Flex layout with shadows and over-realized the black and white keys of the piano. React JSX syntax is used to iterate over render black and white keys.

<div class="piano">
  {this.data.pianoKeys.map((item)=>{return(
  <div class="piano-key">
    <div data-type="white" ref={e= >{ this[item.white.name] = e }} class="piano-key__white"
      onClick={this.playNote.bind(this,item.white.name)} data-key={item.white.keyCode}
      data-note={item.white.name}>
      <span class="piano-note">{item.white.name}</span>
      <audio preload="auto" src={this.data.notes[item.white.name]} hidden='true' data-note={item.white.name}
        class='audioEle'></audio>
    </div>
    <div data-type="black" ref={e= >{ this[item.black.name] = e }} style={{
      display: item.black.name ? 'block' : 'none'
    }} class="piano-key__black" onClick={this.playNote.bind(this,item.black.name)} data-key={item.black.keyCode}
      data-note={item.black.name}>
      <span class="piano-note" style="color:#fff">{item.black.name}</span>
      <audio preload="auto" src={this.data.notes[item.white.name]} hidden='true' data-note={item.white.name}
        class='audioEle'></audio>
    </div>
  </div>
  )})}
</div>
Copy the code

Could look at the source code of CSS, correspond to write the style of the black keys and white, can also write a style more, for the effect of the keyboard or the mouse to click the keys, you can simply add a background color to it, the overall implementation is not too complex, and the specific parameters of the style can be adjusted to make their own style of piano.

.piano {
  margin: 0 200px;
  background: linear-gradient(-65deg, #000, #222, #000, #666, #222 75%);
  border-top:.8rem solid # 282828;
  box-shadow: inset 0 -1px 1px hsla(0, 0%, 100%, .5), inset -0.4 rem 0.4 rem # 282828;
  display: flex;
  height: 80vh;
  height: 20vh;
  justify-content: center;
  overflow: hidden;
  padding-bottom: 2%;
  padding-left: 2.5%;
  padding-right: 2.5%;
}
.piano-key {
  color: blue;
  flex: 1;
  margin: 0 .1rem;
  max-width: 8.8 rem;
  position: relative;
}

.piano-key__white {
  display: flex;
  flex-direction: column-reverse;
  background: linear-gradient(-30deg, #f8f8f8, #fff);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 #fff, inset 1px 0 0 #fff, inset -1px 0 0 #fff.0 4px 3px rgba(0, 0, 0, .7), inset 0 -1px 0 #fff, inset 1px 0 0 #fff, inset -1px -1px 15px rgba(0, 0, 0,.5), -3px 4px 6px rgba(0, 0, 0, 5);height: 100%;
  position: relative;
}

.piano-key__black {
  display: flex;
  flex-direction: column-reverse;
  background: linear-gradient(-20deg, #222, #000, #222);
  box-shadow: inset 0 -1px 2px hsla(0, 0%, 100%, 4),0 2px 3px rgba(0, 0, 0, 4);border-width:.2rem .4rem 1.2 rem;
  border-style: solid;
  border-color: # 666 # 222 # 111 # 555;
  height: 60%;
  left: 100%;
  position: absolute;
  transform: translateX(50%);top: 0;
  width: 70%;
  z-index: 1;
}
Copy the code

Play piano sound

When we’re done with the piano interface, we need to match the sound for each key, and here we’re using HTML5 < Audio > tags, which can load the notes of the piano, and when we trigger the mouse click event or the keyboard click event, we make it play, We’re going to use the property value preload=”auto” to preload the piano before it plays.

<audio preload="auto" src={this.data.notes[item.white.name]} hidden='true' data-note={item.white.name} class='audioEle'></audio>
Copy the code

As long as the ref attribute is used to get the node of the piano sound, then the triggering method is used to control the playing logic, audio-currentTime = 0 to reset the playing progress and audio-.play () to perform the playing. When triggering the playing, the button animation can be realized with the delay device.

playNote(name) {
  let audio = this[name].childNodes[1]
  this[name].style.background = `linear-gradient(-20deg, #3330fb, #000, #222)`
  let timer = setTimeout((a)= > {
    this[name].getAttribute('data-type') = = ='white' ? this[name].style.background = `linear-gradient(-30deg, #f8f8f8, #fff)` : this[name].style.background = `linear-gradient(-20deg, #222, #000, #222)`
    clearTimeout(timer)
  }, 1000)
  audio.currentTime = 0;
  audio.play();
}
Copy the code

After the

We use the window. The document. The onkeydown to monitor page global keyboard events, and then determine the event object e.a ltKey, e.c. with our fabrication: trlKey, e.m etaKey and e.s hiftKey whether these four function keys is triggered, then determine whether the number keys is triggered, Finally determine whether the letter key is triggered.

document.onkeydown = (event) = > {
  var e = event || window.event || arguments.callee.caller.arguments[0];
  let playNote = (key) = > {
    if (e.shiftKey === true) {
      this.playNote(`${key}2 `)}else if (e.altKey === true) {
      this.playNote(`${key}5 `)}else if (e.ctrlKey === true) {
      this.playNote(`${key}3 `)}else if (e.metaKey === true) {
      this.playNote(`${key}6 `)
      e.returnValue = false;
    } else {
      this.playNote(`${key}4 `)}}if (e && 49 <= e.keyCode && e.keyCode <= 55) {
    switch (e.keyCode) {
      case 49:
        playNote('C')
        break;
      case 50:
        playNote('D')
        break;
      case 51:
        playNote('E')
        break;
      case 52:
        playNote('F')
        break;
      case 53:
        playNote('G')
        break;
      case 54:
        playNote('A')
        break;
      case 55:
        playNote('B')
        break; }}if (e && (81 === e.keyCode || e.keyCode === 87 || e.keyCode === 69 || e.keyCode === 82 || e.keyCode === 84)) {
    switch (e.keyCode) {
      case 81:
        playNote('C#')
        break;
      case 87:
        playNote('D#')
        break;
      case 69:
        playNote('F#')
        break;
      case 82:
        playNote('G#')
        break;
      case 84:
        playNote('A#')
        break; }}};Copy the code

Note synchronization display

Every time you automatically press a piano key, you can see the notes bouncing below and automatically highlight, which involves the communication between the piano component and the bottom text component. We use the Store function of Omi to realize the communication of components. In essence, it hijacks data based on Proxy. When we change a data, we can map the latest status to another component in real time to complete the communication of components. Here I set a count and song as the communication values between the two components. Count records the number of notes that are clicked and song is the piano score that is being played.

render(<my-app />, '#root', { data: { count: 0, song: []}, sub() {this.data.count--}, add() {this.data.count++}, setSong(song) {let melody = []; song.map((item, index) => { melody.push({ ... For (let j = 0; let j = 0; let j = 0; j< melody.length; j+ =30) {
            this.data.song.push(melody.slice(j.j + 30}}})))Copy the code

Automatically play

The following is the logic about how to play automatically. If we want to play complex songs, especially in the case of multi-chord, we can write a good song score and give it to the program to play automatically. The following is the brief piano score of Jay Chou’s “The Agreement of Dandelion”, we record the notes of each key with an array. Then just use a timer or recursive taking out each note to the function identification, and then trigger the corresponding < audio > tag can be played, it explain array inside each item, if the string is inside, there is a corresponding digital alto, also known as if ‘3’, it only need to press the keyboard 3, if it is’ + 3 ‘that is high, That is the combination key option + 3, if +1.. That’s telling the program to pause two beats here, and when we’re actually playing ourselves we’re just going to pause a little bit here and control the melody.

const song = [
    '3'.'4'.'5'.'5'.'5'.'6'.'7'.'+ 1.. '.'+ 1'.'+ 1'.'7'.'+ 2'.'6'.'5'.'5'.'5'.'+ 2'.'+ 1'.'+ 1'.'+ 3'.'+ 3.. '.'+ 1'.'+ 2'.'+ 3'.'+ 3'.'+ 4'.'+ 3'.'+ 2'.'+ 3'.'+ 1'.'+ 1'.'6'.'6'.'6'.'7'.'+ 1'.'+ 2'.'+ 2'.'+ 1'.'7'.'6'.'+ 4'.'+ 2'.// Will wish...
    '+ 2.. '.'3'.'4'.'5'.// Fold a plane to send a letter...
    '5'.'5'.'5'.'6'.'7'.'+ 1.. '.'+ 1'.'+ 1'.'7'.'+ 2'.'6'.'5'.'5'.'5'.'+ 2'.'+ 1'.'+ 1'.'+ 3'.'+ 3.. '.'+ 1'.'+ 2'.'+ 3'.'+ 3'.'+ 4'.'+ 3'.'+ 2'.'+ 3'.'+ 1'.'+ 1'.'6'.'6'.'6'.'7'.'+ 1'.'+ 2'.'+ 2'.'+ 1'.'7'.'6'.'+ 4'.'+ 2.. '.// The promise of growing up...
    '3'.'5'.'+ 1'.'+ 3'.'+ 3..'+ 4'.'+ 2.. '.'+ 2'.'+ 5'.'7'.'+ 1.. '.'+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2'.'+ 3'.'+ 3.. '.// Say we're going to travel together...
    '3'.'5'.'+ 1..'+ 3'.'+ 3..'+ 4'.'+ 2.. '.// It is you who now...
    '+ 2'.'+ 5'.'7'.'+ 1.. '.// The only wayward insist
    '+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2..'+ 1'.'+ 1'.// In the corridor...
    '3'.'4'.'5'.'5'.'5'.'6'.'7'.'+ 1.. '.'+ 1'.'+ 1'.'7'.'+ 2'.'6'.'5'.'5'.'5'.'+ 2'.'+ 1'.'+ 1'.'+ 3'.'+ 3.. '.'+ 1'.'+ 2'.'+ 3'.'+ 3'.'+ 4'.'+ 3'.'+ 2'.'+ 3'.'+ 1'.'+ 1'.'6'.'6'.'6'.'7'.'+ 1'.'+ 2'.'+ 2'.'+ 1'.'7'.'6'.'+ 4'.'+ 2'.// The promise of growing up...
    '3'.'5'.'+ 1'.'+ 3'.'+ 3..'+ 4'.'+ 2.. '.'+ 2'.'+ 5'.'7'.'+ 1.. '.'+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2'.'+ 3'.'+ 3.. '.// Say we're going to travel together...
    '3'.'5'.'+ 1..'+ 3'.'+ 3..'+ 4'.'+ 2.. '.// It is you who now...
    '+ 2'.'+ 5'.'7'.'+ 1.. '.// The only wayward...
    '+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2..'+ 1'.'+ 1'.// The promise of growing up...
    '+ 6'.'+ 5'.'+ 3'.'+ 2'.'+ 1'.'+ 3..'+ 4'.'+ 2.. '.'+ 6'.'+ 5'.'7'.'+ 1.. '.// With you endless talk of the past...
    '+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2'.'+ 3'.'+ 3.. '.// And I can't tell...
    '3'.'5'.'+ 1'.'+ 3'.'+ 3..'+ 2'.'+ 2'.'+ 2.. '.'+ 2'.'+ 5'.'7'.'+ 2'.'+ 1'.'+ 1'.// Or missed love...
    '+ 3'.'+ 4'.'+ 5'.'+ 1'.'+ 1'.'+ 2..'+ 1'.'+ 1.. '
]
export default [...song]
Copy the code

With the above array, we only need to write a recursive function to iterate through the group, and then according to this kind of digital chords, put it into notes CDEFGAB, at first I used the timer to achieve read music function, later found that difficult to control with timer, the pauses between the notes, in contrast with the recursion will be easy to implement, However, recursion is also difficult to implement the pause function, because it is also more complicated to interrupt the recursive function from the outside, so if you want to implement the piano yourself, you should pay a little attention to this area. Promises in the following code with await, async and timer accept a time variable to control the pause time between notes, And outer the if (offset < song. Length && enclosing store. The data. The song. The length > 0) judgment on the left side of the condition is judgment index value less than the length of the chords array, the right is the outer layer of the incoming value judgment as a recursive function end boundary conditions.

playSong(song) {
  this.setSong([...song])
  let offset = 0
  let time = 0
  let playSong = async() = > {// The right-hand side breaks the recursion from the outside
    if (offset < song.length && this.store.data.song.length > 0) {
      switch (typeof song[offset]) {
        ++12345--6. Simple melodies
        case 'string':
          let letters = song[offset].match(/[0-9]/g)
          switch (letters.length) {
            case 1:
              time = this.handleString(song, offset)
              break
            default:
              time = this.handleStrings(song, offset)
              break
          }
          break
        // Simple notation 1 plays according to CDEFGAB, complex melodic situations, such as chords
        case 'object':
          console.log(song[offset]['note'])
          time = song[offset]['time'];
          this.playNote(song[offset]['note'])
          break;
        case 'number':
          / / rests.
          switch (song[offset]) {
            case 0:
              time = 1000
              break
          }
          break
      }
      await new Promise((resolve) = > {
        let timer = setTimeout((a)= > {
          clearInterval(timer)
          resolve()
        }, time)
      })
      offset++
      // Custom event, with the bottom note automatically beat in combination
      this.add()
      playSong()
    } else {
      // Pause the playback
      clearTimeout(this.timer)
      this.store.data.song = []
      this.store.data.count = 0
      return
    }
  }
  playSong()
}
Copy the code

Dandelion agreement

After watching the above array chords will certainly have a classmate ask, of course, the above array inside not only applied to eight key, if you look carefully, you will find here only the tenor and soprano, namely pure number keys (1-7) and the Option key to cooperate, even useless to chromatic, so the actual use by eight keys, so the above for programming to identify the chords, Converting our human-recognized keyboard spectrum requires only a slight adjustment to the following key combinations.

'3'.'4'.'5'.'5'.'5'.'6'.'7'.'Option+1.. '.'Option+1'.'Option+1'.'7'.'Option+2'.'6'.'5'.'5'.'5'.'Option+2'.'Option+1'.'Option+1'.'Option+3'.'Option+3.. '.'Option+1'.'Option+2'.'Option+3'.'Option+3'.'Option+4'.'Option+3'.'Option+2'.'Option+3'.'Option+1'.'Option+1'.'6'.'6'.'6'.'7'.'Option+1'.'Option+2'.'Option+2'.'Option+1'.'7'.'6'.'Option+4'.'Option+2'.// Will wish...
'Option+2.. '.'3'.'4'.'5'.// Fold a plane to send a letter...
'5'.'5'.'5'.'6'.'7'.'Option+1.. '.'Option+1'.'Option+1'.'7'.'Option+2'.'6'.'5'.'5'.'5'.'Option+2'.'Option+1'.'Option+1'.'Option+3'.'Option+3.. '.'Option+1'.'Option+2'.'Option+3'.'Option+3'.'Option+4'.'Option+3'.'Option+2'.'Option+3'.'Option+1'.'Option+1'.'6'.'6'.'6'.'7'.'Option+1'.'Option+2'.'Option+2'.'Option+1'.'7'.'6'.'Option+4'.'Option+2.. '.// The promise of growing up...
'3'.'5'.'Option+1'.'Option+3'.'Option+3.'.'Option+4'.'Option+2.. '.'Option+2'.'Option+5'.'7'.'Option+1.. '.'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2'.'Option+3'.'Option+3.. '.// Say we're going to travel together...
'3'.'5'.'Option+1.'.'Option+3'.'Option+3.'.'Option+4'.'Option+2.. '.// It is you who now...
'Option+2'.'Option+5'.'7'.'Option+1.. '.// The only wayward insist
'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2.'.'Option+1'.'Option+1'.// In the corridor...
'3'.'4'.'5'.'5'.'5'.'6'.'7'.'Option+1.. '.'Option+1'.'Option+1'.'7'.'Option+2'.'6'.'5'.'5'.'5'.'Option+2'.'Option+1'.'Option+1'.'Option+3'.'Option+3.. '.'Option+1'.'Option+2'.'Option+3'.'Option+3'.'Option+4'.'Option+3'.'Option+2'.'Option+3'.'Option+1'.'Option+1'.'6'.'6'.'6'.'7'.'Option+1'.'Option+2'.'Option+2'.'Option+1'.'7'.'6'.'Option+4'.'Option+2'.// The promise of growing up...
'3'.'5'.'Option+1'.'Option+3'.'Option+3.'.'Option+4'.'Option+2.. '.'Option+2'.'Option+5'.'7'.'Option+1.. '.'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2'.'Option+3'.'Option+3.. '.// Say we're going to travel together...
'3'.'5'.'Option+1.'.'Option+3'.'Option+3.'.'Option+4'.'Option+2.. '.// It is you who now...
'Option+2'.'Option+5'.'7'.'Option+1.. '.// The only wayward...
'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2.'.'Option+1'.'Option+1'.// The promise of growing up...
'Option+6'.'Option+5'.'Option+3'.'Option+2'.'Option+1'.'Option+3.'.'Option+4'.'Option+2.. '.'Option+6'.'Option+5'.'7'.'Option+1.. '.// With you endless talk of the past...
'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2'.'Option+3'.'Option+3.. '.// And I can't tell...
'3'.'5'.'Option+1'.'Option+3'.'Option+3.'.'Option+2'.'Option+2'.'Option+2.. '.'Option+2'.'Option+5'.'7'.'Option+2'.'Option+1'.'Option+1'.// Or missed love...
'Option+3'.'Option+4'.'Option+5'.'Option+1'.'Option+1'.'Option+2.'.'Option+1'.'Option+1.. '
Copy the code

The moonlight stands for my heart

We can also play another well-known piano piece, the Moon Stands for My Heart.

'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'1'.'Ctrl+6'.'2'.'3'.'2'.'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'1'.'Ctrl+6'.'2'.'3'.'2'.'3'.'5'.'3'.'2'.'1'.'5'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'Ctrl+6'.'Ctrl+5'.'3'.'5'.'3'.'2'.'1'.'5'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'1'.'1'.'1'.'2'.'3'.'2'.'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'Ctrl+6'.'Ctrl+7'.'1'.'2'.'1'.'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'1'.'Ctrl+6'.'2'.'3'.'2'.'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'1'.'Ctrl+6'.'2'.'3'.'2'.'3'.'5'.'3'.'2'.'1'.'5'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'Ctrl+6'.'Ctrl+5'.'3'.'5'.'3'.'2'.'1'.'5'.'Ctrl+7'.'Ctrl+6'.'Ctrl+7'.'1'.'1'.'1'.'2'.'3'.'2'.'Ctrl+5'.'1'.'3'.'5'.'1'.'Ctrl+7'.'3'.'5'.'5'.'6'.'7'.'Option+1'.'6'.'5'.'3'.'2'.'1'.'1'.'1'.'3'.'2'.'1'.'1'.'1'.'2'.'3'.'2'.'Ctrl+6'.'Ctrl+7'.'1'.'2'.'1'
Copy the code

Contributing

Thanks for the music and programming company! Welcome to share, and we look forward to your contributing code, PR, discussion in issue, or suggestions, as Leehom Wang sings:

If the world is too dangerous, only music is the safest, take me into the dream, let the lyrics come true 💞 — “Our Song”