Play a dandelion agreement with 8 keys on the keyboard. Give it to yourself in 996 or a moon to her on behalf of my heart

Experience address: http://wscats.gitee.io/piano/... Project address: https://github.com/Wscats/piano or https://gitee.com/wscats/piano

Play a dandelion with 8 keys on the keyboard. Give it to yourself in 996 or the moon on behalf of my heart to her on Tanabata. It's very simple~

This project is only implemented with a few simple front-end technologies, dedicated to every coder who loves music 🎹

Technical points and directory structure

Instead of using mainstream frameworks (React, Vue and Angular) and popular technologies in the market, the project uses omi framework (JSX+WebComponents) and Omil's single file component SFCs loader. The component communication is based on Proxy characteristics and combined with VScode plug-in eno snippets, which is based on AST and regular real-time compilation Eno or omi suffix component reduces the local compilation pressure of some webpacks. Of course, the technology familiar to other students is not mentioned here.

  • src
    • assets
    • element
      • app-piano
        • songs piano catalogue
        • app-piano.eno single file component
        • app-piano.js file compiled by JS component
        • notes.js keyboard keys and notes mapping
    • index.js component root container to configure the communication method of Proxy
  • public
    • samples/piano single note material

app-piano.eno

Single file components you need to write in development

app-piano.js

Modify or save the file after eno snippets hello js file converted by plug-in after eno

As shown in the figure below, the code on the left is written by us Single file component with eno suffix. On the right is generated by Eno Snippets js suffix file.

Development and installation

Develop, build and run.

# Get remote warehouse code
git clone https://github.com/Wscats/piano
# Enter directory
cd piano
# Installation dependency
npm install
# Start project
npm start
# Access in browser http://localhost:3000
copy

Install using the npm package manager.

npm install omi-piano
copy

Run or publish your own version of the performance.

# Enter directory
cd omi-piano
# Installation dependency
npm install
# Start project
npm start
# Release your own version
npm run build
copy

Simple music theory knowledge

First of all, let's make up some basic music and collect the most basic music in advance Piano single tone material , one note for each note mp3 files are recorded with an object, similar to the following. For example, A here refers to A in the sound name of CDEFGAB, that is, La. This is the most basic music theory. Does it remind you of the staff on the drawing board when you were A child in music class.

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

Of course, here we use numbers to replace them equivalently to reduce the difficulty of beginners. See Table 1 below, which is equivalent to C Alto, that is, Do. Since many songs use more dense keys in the middle of the piano, we default to the number keys corresponding to Alto:

1 === C4 === Do

Numeric key

1

2

3

4

5

6

7

Phonetic name

C4

D4

E4

F4

G4

A4

B4

note

Do

Re

Mi

Fa

Sol

La

Si

Here is a special drawing for our understanding:

Of course, the actual situation also includes the distinction between full tone and semitone. For example, the semitone of a is a#, and there are midrange, treble and treble. Here, A4 represents midrange, A5 represents treble and A6 represents treble, so the table can be sorted out more clearly. When we want to play midrange A4, we only need to press the number key 6 on the keyboard. If we want to play treble A5, we only need to use the combination key Option+6, and we only need to draw inferences from one example, You can know the keyboard keys corresponding to each note.

Double bass

C2

D2

E2

F2

G2

A2

B2

Shift + (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

Number keys 1-7

1

2

3

4

5

6

7

a treble

C5

D5

E5

F5

G5

A5

B5

Option key + (1-7)

Option+1

Option+2

Option+3

Option+4

Option+5

Option+6

Option+7

Treble

C6

D6

E6

F6

G6

A6

B6

Command key + (1-7)

Command+1

Command+2

Command+3

Command+4

Command+5

Command+6

Command+7

note

Do

Re

Mi

Fa

Sol

La

Si

The above is the whole tone table, and the semitone table is attached here:

Double low semitone

C#2

D#2

F#2

G#2

A#2

Shift+

Shift+q

Shift+w

Shift+e

Shift+r

Shift+t

flat

C#3

D#3

F#3

G#3

A#3

Ctrl+

Ctrl+q

Ctrl+w

Ctrl+e

Ctrl+r

Ctrl+t

Halftone

C#4

D#4

F#4

G#4

A#4

Letter key

q

w

e

r

t

High semitone

C#5

D#5

F#5

G#5

A#5

Option+

Option+q

Option+w

Option+e

Option+r

Option+t

Double high semitone

C#6

D#6

F#6

G#6

A#6

Command+

Command+q

Command+w

Command+e

Command+r

Command+t

Now we only need to use 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) on the keyboard to play 60 single tones (35 full tones + 25 semitones) of the piano. In fact, a simple piano song doesn't need so much, just a few simple chords.

Build piano interface

With the above preliminary preparation, the following is transformed into our programming knowledge. We need to use HTML to draw our piano interface, which we can refer to codepen and codesandbox Here I use the flex layout with shadow and excessive realization of the black-and-white keys of the piano, which uses the JSX syntax of React to traverse and render the 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

You can observe the source code of CSS and write the styles of black keys and white keys respectively. You can also write another style for the effect when the keyboard or mouse clicks the piano keys. You can simply add a background color to it. The overall implementation will not be too complicated. Specifically, you can adjust the style parameters to create your own piano style.

.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.4rem 0.4rem #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.8rem;
  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.2rem;
  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

Play piano music

When we complete the piano interface, we need to match the sound for each key. Here, we use the < audio > tag of HTML5, which can load the notes of the piano. When we trigger the mouse click event or keyboard click event, we let it play. Before the piano is played, we use the attribute value preload="auto" to preload it.

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

To play, just use the ref attribute to obtain the node of the piano sound, and then control the playback logic for its trigger method, audio Currenttime = 0 resets playback progress and audio Play() performs playback. When the playback is triggered, the key animation can be realized with the delayer.

playNote(name) {
  let audio = this[name].childNodes[1]
  this[name].style.background = `linear-gradient(-20deg, #3330fb, #000, #222)`
  let timer = setTimeout(() => {
    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

After completing the audio processing of < audio >, you need to bind the keyboard event to its logic. Here, you need to understand the keycode of the keyboard. Each entity key of the keyboard will correspond to a key code. According to the key code, use JS keyboard event monitoring to judge whether the key is pressed.

We use window document. Onkeydown to monitor the global keyboard events on the page, and then judge whether the four function keys of the event object e.altKey, e.ctrl key, e.metaKey and e.shiftKey are triggered, then judge whether the number keys are triggered, and finally judge whether the letter keys are 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

Auto play

The following is the logic of how to play automatically. If we want to play complex songs, especially in the case of multi chord, we can write the score and give it to the programming for automatic performance. The following is the piano score of Jay Chou's Dandelion agreement. We use an array to record the notes of each key, and then just use a timer or recursion to take out each note for function recognition, Then trigger the corresponding < audio > tag to play. Here is an explanation of each item in the array. If the string is a number, it corresponds to a midrange, that is, if it is' 3 ', you only need to press 3 on the keyboard. If it is' + 3', it is a treble, that is, using the combination key option + 3 mentioned above. If it is + 1, That is to tell the programming that we need to pause two beats here. When we actually play, we can just pause 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',
    // Send a letter by plane
    '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..',
    // Growing up together
    '3', '5', '+1', '+3', '+3.', '+4', '+2..', '+2', '+5', '7', '+1..',
    '+3', '+4', '+5', '+1', '+1', '+2', '+3', '+3..',
    // Agreed to travel together
    '3', '5', '+1.', '+3', '+3.', '+4', '+2..',
    // It's you now
    '+2', '+5', '7', '+1..',
    // The only persistent willfulness
    '+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',
    // Growing up together
    '3', '5', '+1', '+3', '+3.', '+4', '+2..', '+2', '+5', '7', '+1..',
    '+3', '+4', '+5', '+1', '+1', '+2', '+3', '+3..',
    // Agreed to travel together
    '3', '5', '+1.', '+3', '+3.', '+4', '+2..',
    // It's you now
    '+2', '+5', '7', '+1..',
    // The only persistent willfulness
    '+3', '+4', '+5', '+1', '+1', '+2.', '+1', '+1',
    // Growing up together
    '+6', '+5', '+3', '+2', '+1', '+3.', '+4', '+2..',
    '+6', '+5', '7', '+1..',
    // I can't finish talking with you once
    '+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

With the above array, we only need to write a recursive function to traverse the array, and then turn it into note CDEFGAB according to the simplified spectrum of such numbers. At the beginning, I used a timer to realize the spectrum reading function. Later, I found that it is difficult to control the pause time between notes with a timer. On the contrary, it is easier to realize with recursion, but it is also difficult to realize the pause function with recursion, Because it is also complex to interrupt the recursive function from the outside, students should pay a little attention to this place if they want to realize the piano by themselves. Promise in the following code, together with await, async and timer, is to accept a time variable to control the pause time between notes. For the outer if (offset < Song. Length & & this. Store. Data. Song. Length > 0) judgment condition, the condition on the left is that the judgment index value should be less than the length of the simple spectrum array, and the judgment value passed in from the outer layer on the right is the termination boundary condition of the recursive function.

playSong(song) {
  this.setSong([...song])
  let offset = 0
  let time = 0
  let playSong = async () => {
    // On the right is to interrupt recursion from the outside
    if (offset < song.length && this.store.data.song.length > 0) {
      switch (typeof song[offset]) {
        // Musical notation 2 playing method according to + + 12345 -- 6 Simple melody
        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
        // The playing method of notation 1 is based on CDEFGAB and complex melodies, such as chords
        case 'object':
          console.log(song[offset]['note'])
          time = song[offset]['time'];
          this.playNote(song[offset]['note'])
          break;
        case 'number':
          // Rest
          switch (song[offset]) {
            case 0:
              time = 1000
              break
          }
          break
      }
      await new Promise((resolve) => {
        let timer = setTimeout(() => {
          clearInterval(timer)
          resolve()
        }, time)
      })
      offset++
      // Custom events, combined with the automatic beating of notes at the bottom below
      this.add()
      playSong()
    } else {
      // Pause playback
      clearTimeout(this.timer)
      this.store.data.song = []
      this.store.data.count = 0
      return
    }
  }
  playSong()
}
copy

Dandelion's Promise

After reading the above array spectrum, of course, some students will ask. There are more than eight keys in the above array. If you carefully observe it, you will find that only midrange and treble are used here, that is, the combination of pure number keys (1-7) and Option keys, not even half tone is used. In fact, only eight keys are used. Therefore, the above simplified spectrum for programming recognition is transformed into the keyboard spectrum for human recognition, Just slightly adjust it to the following key combination.

'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',
// Send a letter by plane
'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..',
// Growing up together
'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..',
// Agreed to travel together
'3', '5', 'Option+1.', 'Option+3', 'Option+3.', 'Option+4', 'Option+2..',
// It's you now
'Option+2', 'Option+5', '7', 'Option+1..',
// The only persistent willfulness
'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',
// Growing up together
'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..',
// Agreed to travel together
'3', '5', 'Option+1.', 'Option+3', 'Option+3.', 'Option+4', 'Option+2..',
// It's you now
'Option+2', 'Option+5', '7', 'Option+1..',
// The only persistent willfulness
'Option+3', 'Option+4', 'Option+5', 'Option+1', 'Option+1', 'Option+2.', 'Option+1', 'Option+1',
// Growing up together
'Option+6', 'Option+5', 'Option+3', 'Option+2', 'Option+1', 'Option+3.', 'Option+4', 'Option+2..',
'Option+6', 'Option+5', '7', 'Option+1..',
// I can't finish talking with you once
'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 moon represents my heart

We can also play another familiar piano song "the moon represents 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

thank

Thanks for music and programming! I also pay tribute to all coders who strive for 996. Welcome to share. I also look forward to your contribution of code and PR issue Discuss the problem in, or talk about your suggestions, as the Leehom Wang song sings:

If the world is too dangerous, only music is the safest. Take me into my dream and let the lyrics come true 💞 —— Our song

Posted by anthony88guy on Thu, 05 May 2022 12:18:43 +0300