Stavba klavíru s React Hooks

V tomto článku se podíváme, jak postavit piano s háky na reakce. Stavba klavíru s React Hooks. pokud jste úplně noví v reakci na háčky, podívejte se na tento kurz.

Nedávné články

TypeScript pro vývojáře React v roce 2020

Vytváření rozhraní API v reálném čase pomocí předplatného graphql

Než budeme pokračovat dále, uvidíme demo

Věci, které je třeba při stavbě klavíru zvážit, je

  • Jak namapovat klávesy notebooku na noty klavíru.
  • Namapujte zvuk stisknutím klávesy.
  • Jak vykreslit klaviaturu klavíru v reakci.

Zkusme to rozebrat jednu po druhé. Nejprve se podíváme, jak přidat zvuk do aplikace kliknutím na tlačítko.

budeme používat knihovnu s názvem sound font player pro zvuk v aplikaci Reagovat.

npx create-react-app piano-hooks
npm i soundfont-player

Jakmile je hotovo, přidejte následující kód pro Audio Player a Audio Context.

Audio kontext bude mít kontext a Audio Player bude mít dvě metody, které jsou setInstrument a playNote .

import SoundFontPlayer from "soundfont-player";
import AudioContext from "./AudioContext";

const NullSoundFontPlayerNoteAudio = {
  stop() {}
};

const NullSoundFontPlayer = {
  play() {
    return NullSoundFontPlayerNoteAudio;
  }
};
const AudioPlayer = () => {
  //Audio Context
  const audioContext = AudioContext && new AudioContext();

  //soundPlayer
  let soundPlayer = NullSoundFontPlayer;
  //setInstrument
  const Player = {
    setInstrument(instrumentName) {
      SoundFontPlayer.instrument(audioContext, instrumentName)
        .then(soundfontPlayer => {
          soundPlayer = soundfontPlayer;
        })
        .catch(e => {
          soundPlayer = NullSoundFontPlayer;
        });
    },
    playNote(note) {
      soundPlayer.play(note);
    }
  };
  return Player;
};

export default AudioPlayer;

a AudioContext.js bude obsahovat

export default window.AudioContext;

Poté otestujte, zda funguje správně, přidejte následující kód do App.js

import React, { useEffect } from "react";
import "./App.css";
import AudioPlayer from "./core/AudioPlayer";
function App() {
  const audioPlayer = AudioPlayer();

  useEffect(() => {
    audioPlayer.setInstrument("acoustic_grand_piano");
  }, []);

  const handleClick = () => {
    audioPlayer.playNote("C4");
  };

  return (
    <div className="app-container">
      <button onClick={handleClick}>Play</button>
    </div>
  );
}

export default App;

V podstatě máme tlačítko, které zahraje notu, když na ni klikneme. Zde se useEffect spustí na každém připojení komponenty a nastaví nástroj s názvem.

Klávesnice – Render Render

Zkusme použít koncepty renderovacích rekvizit na nástroji. pokud nejste obeznámeni s rekvizitami pro renderování, podívejte se na tento kurz.

Nástroj má především dvě důležité části. Jsou to nástroj samotný a instrumentAudio.

Nejprve uvidíme, jak nastavit zvuk nástroje. přesuneme naši logiku app.js do instrumentAudio.

vytvořte soubor InstrumentAudio.js a přidejte následující kód,

import React, { useEffect, useState } from "react";
import AudioPlayer from "./AudioPlayer";

const InstrumentAudio = ({ instrumentName, notes }) => {
  const [instrumentPlayer, setInstrumentPlayer] = useState(null);
  useEffect(() => {
    setInstrumentPlayer(AudioPlayer());
  }, []);

  useEffect(() => {
    if (instrumentPlayer) {
      setInstrument();
      playNotes();
    }
  }, [instrumentPlayer]);

  useEffect(() => {
    if (notes && notes.length > 0) {
      playNotes();
    }
  }, [notes]);

  const setInstrument = () => {
    instrumentPlayer.setInstrument(instrumentName);
  };

  const playNotes = () => {
    if (instrumentPlayer) {
      instrumentPlayer.playNote(notes[0]);
    }
  };

  return null;
};

export default InstrumentAudio;

Zde udržujeme nástroj Player ve stavu, abychom jej mohli ovládat.

když se komponenta připojí jako první, zavolá setInstrument metoda, která nastaví nástroj s názvem.

Poté pokaždé, když se změní rekvizity not, zahraje notu definovanou v useEffect, která má závislost na notách.

Nyní je čas implementovat samotný nástroj. nástroj bude mít počáteční a koncovou notu jako rekvizity. na základě toho vykreslí všechny poznámky mezi tím.

import React, { Fragment } from "react";
import InstrumentAudio from "./Keyboard/InstrumentAudio";
import getNotesBetween from "./utils/getNotesBetween";

const Instrument = ({ instrumentName, startNote, endNote }) => {
  const notes = getNotesBetween(startNote, endNote);
  return (
    <Fragment>
      {notes.map(note => {
        return <Fragment>Note is : {note}</Fragment>;
      })}
      <InstrumentAudio />
    </Fragment>
  );
};

export default Instrument;

Zde dostaneme všechny noty mezi počáteční a koncovou notou. vytvořte soubor s názvem notes.js a přidejte následující kód.

const TONES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
const OCTAVE_NUMBERS = [1, 2, 3, 4, 5, 6, 7];

export default OCTAVE_NUMBERS.reduce((notes, octaveNumber) => {
  const notesInOctave = TONES.map(tone => `${tone}${octaveNumber}`);
  return [...notes, ...notesInOctave];
}, []);

Poté vytvořte soubor getNotesBetween.js abyste získali všechny poznámky mezi počáteční a koncovou poznámkou.

import NOTES from "../constants/note";

export default function getNotesBetween(startNote, endNote) {
  const startingIndex = NOTES.indexOf(startNote);
  const endingIndex = NOTES.indexOf(endNote);
  return NOTES.slice(startingIndex, endingIndex + 1);
}

Nyní je čas přidat nástroj a jeho stavové poznámky v Instrument.js .

import React, { Fragment, useState } from "react";
import InstrumentAudio from "./Keyboard/InstrumentAudio";
import getNotesBetween from "./utils/getNotesBetween";
import isAccidentalNote from "./utils/isAccidentalNote";

const Instrument = ({
  instrumentName,
  startNote,
  endNote,
  renderPianoKey,
  keyboardMap
}) => {
  const notes = getNotesBetween(startNote, endNote);

  const [state, setState] = useState({
    notesPlaying: []
  });

  const onPlayNoteStart = note => {
    setState({ ...state, notesPlaying: [...state.notesPlaying, note] });
  };

  const onPlayNoteEnd = note => {
    setState({
      ...state,
      notesPlaying: state.notesPlaying.filter(
        notePlaying => notePlaying !== note
      )
    });
  };

  return (
    <Fragment>
      {notes.map(note => {
        return (
          <Fragment key={note}>
            {renderPianoKey({
              note,
              isAccidentalNote: isAccidentalNote(note),
              isNotePlaying: state.notesPlaying.includes(note),
              startPlayingNote: () => onPlayNoteStart(note),
              stopPlayingNote: () => onPlayNoteEnd(note),
              keyboardShortcut: getKeyboardShortcutsForNote(keyboardMap, note)
            })}
          </Fragment>
        );
      })}
      <InstrumentAudio
        instrumentName={instrumentName}
        notes={state.notesPlaying}
      />
    </Fragment>
  );
};

export default Instrument;

Logika je, renderPianoKey je renderovací rekvizita se stavem z Instrument Component.

isAccidentalNote zkontroluje, zda se jedná o přirozený klíč nebo náhodný klíč.

isAccidentalNote.js

import NOTES from '../constants/note'
export default (note) => {
    return NOTES.includes(note) && note.includes('#')
}

isNotePlaying kontroluje stav, zda je nota ve stavu přehrávání not.

Metoda startPlayingNote se zavolá, když uživatel klikne na tlačítko, když se zavolá, přidáme konkrétní poznámku do stavu.

při stopPlayingNote odstraníme poznámku ze stavu.

nakonec přidáme akce klávesnice, jako je stisknutí klávesy a stisknutí kláves, abychom mohli ovládat akce klávesnice.

 useEffect(() => {
    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("keyup", handleKeyUp);
  }, []);

const handleKeyDown = e => {
    if (isRegularKey(e) && !e.repeat) {
      const note = getNoteFromKeyboardKey(e.key);
      if (note) {
        setState({ ...state, notesPlaying: [...state.notesPlaying, note] });
      }
    }
  };

  const handleKeyUp = e => {
    if (isRegularKey(e) && !e.repeat) {
      const note = getNoteFromKeyboardKey(e.key);
      if (note) {
        setState({
          ...state,
          notesPlaying: state.notesPlaying.filter(
            notePlaying => notePlaying !== note
          )
        });
      }
    }
  };

Piano

Protože nástroj používá renderovací rekvizity. potřebujeme předat nástrojovou komponentu z Piano.js soubor.

Zde máme funkci renderPianoKey, která přebírá všechny argumenty z této metody. pokud se jedná o náhodnou notu, vykreslí náhodnou klíčovou komponentu.

Pokud se jedná o přirozenou klíčovou notu, vykreslí přirozenou klíčovou složku. Také musíme poskytnout mapu klávesnice, kde bude každá klávesa namapována s klavírními notami.

Dokončete zdrojový kód

Demo