Git Product home page Git Product logo

react-piano's Introduction

react-piano

npm version build status bundle size

An interactive piano keyboard for React. Supports custom sounds, touch/click/keyboard events, and fully configurable styling. Try it out on CodeSandbox.

react-piano screenshot

Installing

yarn add react-piano

Alternatively, you can download the UMD build from unpkg.

Usage

You can view or fork the CodeSandbox demo to get a live version of the component in action.

Import the component and styles:

import { Piano, KeyboardShortcuts, MidiNumbers } from 'react-piano';
import 'react-piano/dist/styles.css';

Importing CSS requires a CSS loader (if you're using create-react-app, this is already set up for you). If you don't have a CSS loader, you can alternatively copy the CSS file into your project from src/styles.css.

Then to use the component:

function App() {
  const firstNote = MidiNumbers.fromNote('c3');
  const lastNote = MidiNumbers.fromNote('f5');
  const keyboardShortcuts = KeyboardShortcuts.create({
    firstNote: firstNote,
    lastNote: lastNote,
    keyboardConfig: KeyboardShortcuts.HOME_ROW,
  });

  return (
    <Piano
      noteRange={{ first: firstNote, last: lastNote }}
      playNote={(midiNumber) => {
        // Play a given note - see notes below
      }}
      stopNote={(midiNumber) => {
        // Stop playing a given note - see notes below
      }}
      width={1000}
      keyboardShortcuts={keyboardShortcuts}
    />
  );
}

Implementing audio playback

react-piano does not implement audio playback of each note, so you have to implement it with playNote and stopNote props. This gives you the ability to use any sounds you'd like with the rendered piano. The react-piano demo page uses @danigb's excellent soundfont-player to play realistic-sounding soundfont samples. Take a look at the CodeSandbox demo to see how you can implement that yourself.

Props

Name Type Description
noteRange Required object An object with format { first: 48, last: 77 } where first and last are MIDI numbers that correspond to natural notes. You can use MidiNumbers.NATURAL_MIDI_NUMBERS to identify whether a number is a natural note or not.
playNote Required function (midiNumber) => void function to play a note specified by MIDI number.
stopNote Required function (midiNumber) => void function to stop playing a note.
width Conditionally required number Width in pixels of the component. While this is not strictly required, if you omit it, the container around the <Piano> will need to have an explicit width and height in order to render correctly.
activeNotes Array of numbers An array of MIDI numbers, e.g. [44, 47, 54], which allows you to programmatically play notes on the piano.
keyWidthToHeight Number Ratio of key width to height. Used to specify the dimensions of the piano key.
renderNoteLabel Function ({ keyboardShortcut, midiNumber, isActive, isAccidental }) => node function to render a label on piano keys that have keyboard shortcuts
className String A className to add to the component.
disabled Boolean Whether to show disabled state. Useful when audio sounds need to be asynchronously loaded.
keyboardShortcuts Array of object An array of form [{ key: 'a', midiNumber: 48 }, ...], where key is a keyEvent.key value. You can generate this using KeyboardShortcuts.create, or use your own method to generate it. You can omit it if you don't want to use keyboard shortcuts. Note: this shouldn't be generated inline in JSX because it can cause problems when diffing for shortcut changes.
onPlayNoteInput Function (midiNumber, { prevActiveNotes }) => void function that fires whenever a play-note event is fired. Can use prevActiveNotes to record notes.
onStopNoteInput Function (midiNumber, { prevActiveNotes }) => void function that fires whenever a stop-note event is fired. Can use prevActiveNotes to record notes.

Recording/saving notes

You can "record" notes that are played on a <Piano> by using onPlayNoteInput or onStopNoteInput, and you can then play back the recording by using activeNotes. See this CodeSandbox which demonstrates how to set that up.

demo of recording

Customizing styles

You can customize many aspects of the piano using CSS. In javascript, you can override the base styles by creating your own set of overrides:

import 'react-piano/dist/styles.css';
import './customPianoStyles.css';  // import a set of overrides

In the CSS file you can do things like:

.ReactPiano__Key--active {
  background: #f00;  /* Change the default active key color to bright red */
}

.ReactPiano__Key--accidental {
  background: #000;  /* Change accidental keys to be completely black */
}

See styles.css for more detail on what styles can be customized.

Upgrading versions

See the CHANGELOG which contains migration guides for instructions on upgrading to each major version.

Browser compatibility

To support IE, you'll need to provide an Array.find polyfill.

License

MIT

react-piano's People

Contributors

dependabot[bot] avatar kevinsqi avatar ritz078 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-piano's Issues

A readOnly mode

In this case no listeners like press etc. should work but it can still be controlled from outside by passing playbackNotes

Internationalization issue (Y and Z)

Hey,

I have a German keyboard and so when I use your module (which is amazing btw) with keyboard shortcuts enabled, I have to press Y even though for me the respective black key should be Z.
Is there some parameter that can handle that today already, or would we have to make a change for this? Happy to help if necessary.

on play / stop functions without pressing a button

onPlayNoteInput/onStopNoteInput functions triggered logging to console even if I didn't press the key

Issue

Code:

import React, { useState } from 'react';
import { Piano, KeyboardShortcuts, MidiNumbers } from 'react-piano';
import SoundfontProvider from './soundfontProvider'
import 'react-piano/dist/styles.css';
import { getOctaves } from './services/octavesServices'

const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const soundfontHostname = 'https://d1pzp51pvbm36p.cloudfront.net';

const noteRange = getOctaves(4)

const keyboardShortcuts = KeyboardShortcuts.create({
    firstNote: noteRange.first,
    lastNote: noteRange.last,
    keyboardConfig: KeyboardShortcuts.HOME_ROW,
});



function PianoInteractive(props) {
    const [isPlaying, setIsPlaying] = useState(0);
    const [pressedMidiNumbers, setPressedMidiNumbers] = useState([''])

    const recording = {
        mode: isPlaying ? 'PLAYING' : 'RECORDING',
        events: [{ "midiNumber": 53, "time": 0, "duration": 0.2 }],
        currentTime: 0,
        currentEvents: [{ "midiNumber": 50, "time": 0, "duration": 0.2 }, { "midiNumber": 53, "time": 0.2, "duration": 0.2 }, { "midiNumber": 55, "time": 0.4, "duration": 0.2 }, { "midiNumber": 57, "time": 0.6000000000000001, "duration": 0.2 }],
    }

    const onStopNoteInput = (midiNumber, { prevActiveNotes }) => {
        console.log({ onStopNoteInput_prevActiveNotes: prevActiveNotes });
        console.log({ onStopNoteInput: midiNumber });
    };

    const onPlayNoteInput = (midiNumber, { prevActiveNotes }) => {
        console.log({ onStopNoteInput_onPlayNoteInput: prevActiveNotes });
        console.log({ onPlayNoteInput: midiNumber });
    }

    const activeNotes =
        recording.mode === 'PLAYING' ? [MidiNumbers.fromNote(props.note)] : null;

    const onClickPlay = () => setIsPlaying(1)
    const onClickStop = () => setIsPlaying(0)

    return (
        <>
            <SoundfontProvider
                instrumentName="acoustic_grand_piano"
                audioContext={audioContext}
                hostname={soundfontHostname}
                render={({ isLoading, playNote, stopNote }) => (
                    <Piano
                        noteRange={noteRange}
                        width={700}
                        playNote={playNote}
                        stopNote={stopNote}
                        disabled={isLoading}
                        keyboardShortcuts={keyboardShortcuts}
                        activeNotes={activeNotes}
                        onPlayNoteInput={onPlayNoteInput}
                        onStopNoteInput={onStopNoteInput}
                    />
                )}
            />

            <div>
                <button onClick={onClickPlay}>Play</button>
                <button onClick={onClickStop}>Stop</button>
            </div>
        </>
    );
}

export default PianoInteractive;

Octave keyboard shortcut?

Hi Kevin,

I'm impressed with the piano. The sounds are great!

I wonder if it might benefit from keyboard shortcuts to change the octave up/down?

Best wishes,
Dan

Trim down the file size

Currently the package composition looks like below

screen shot 2018-09-18 at 12 25 13 pm

We can totally get rid of lodash.find and use Array.prototype.find

Question: Can I use this component for visual highlighting of what notes are being played

I have a song and a set of pitches and duration. My audio is already taken care of. But I need a visual representation of what keys are being played on the keyboard. Can I use this component for the visualization purpose. I am imagining I will have timer events that will invoke the component with a new prop of the note being played and duration and it will render the highlighted key (without playing the audio).

Will that be possible?

Apologies for creating an issue for this. I didn't see a discussion tab.

Thank you so much.

activeNotes prop is not used to initialize state on first render

The initial state of the Piano component in src/Piano.js is set like this:

state = {
    activeNotes: [],
};

This makes it so the initial value is an empty array no matter what. I am proposing this as the fixx:

state = {
    activeNotes: this.props.activeNotes || [],
};

I've confirmed this fixes it, so I'm creating a PR for the issue.

Logo for project proposal

Hello @iqnivek , i've seen this project and notice it needs a logo!
i can help with it, i'm a graphic designer and i contribute to open source projects, like this one!
i'd like to know your toughts about this proposal before proceeding.

TJ.

What's the difference between playNote and onPlayNoteInput?

Hi there,
Thanks for this handy little library! It looks to me like playNote and onPlayNoteInput are both callbacks which are called when a key is pressed, and they both get passed the midi number of the note. onPlayNoteInput gets an additional argument, prevActiveNotes. Besides that, is there a difference between these two? Thanks!

register keyboard events on custom dom nodes?

First thanks for this great component. It's a delight to use.

WOuld it be possible to register keyboard events on something else than "window"? In our application, we may end up having multiple piano on screen. WOuld it be possible to scope the keyboard shortcut processing to the focused piano?

The component re-renders on each update.

There should be a check for equality. PureComponent might not be a good fix as it uses shallowCompare and that will always return true even if playNotes has the same values but different reference. So we need to manually use shouldComponentUpdate.

npm install fails

Hi all,
I have attached build log.

I forked the repo and did a npm install and got the following errors:

Quick summary:
npm ERR! In file included from ../src/common/allocator.cc:1:
npm ERR! In file included from ../../nan/nan.h:174:
npm ERR! ../../nan/nan_callbacks.h:55:23: error: no member named 'AccessorSignature' in namespace 'v8'
npm ERR! typedef v8::Localv8::AccessorSignature Sig;
npm ERR! ~~~~^
npm ERR! In file included from ../src/common/allocator.cc:1:
npm ERR! ../../nan/nan.h:2536:8: error: no matching member function for call to 'SetAccessor'
npm ERR! tpl->SetAccessor(
npm ERR! ~~~~~^~~~~~~~~~~
npm ERR! /Users/paulwilkinson/Library/Caches/node-gyp/19.3.0/include/node/v8-template.h:814:8: note: candidate function not viable: no known conversion from 'imp::Sig' (aka 'int') to 'v8::SideEffectType' for 7th argument
npm ERR! void SetAccessor(
npm ERR! ^
npm ERR! /Users/paulwilkinson/Library/Caches/node-gyp/19.3.0/include/node/v8-template.h:807:8: note: candidate function not viable: no known conversion from 'imp::NativeGetter' (aka 'void ()(v8::Localv8::Name, const v8::PropertyCallbackInfov8::Value &)') to 'v8::AccessorGetterCallback' (aka 'void ()(Localv8::String, const PropertyCallbackInfov8::Value &)') for 2nd argument
npm ERR! void SetAccessor(
npm ERR! ^
npm ERR! In file included from ../src/common/allocator.cc:1:
npm ERR! In file included from ../../nan/nan.h:2884:
npm ERR! ../../nan/nan_typedarray_contents.h:34:43: error: no member named 'GetContents' in 'v8::ArrayBuffer'
npm ERR! data = static_cast<char*>(buffer->GetContents().Data()) + byte_offset;
npm ERR! ~~~~~~~~^
npm ERR! 3 errors generated.
2023-01-03T18_26_32_656Z-debug-0.log

Separating the utilities from the UI

I only need the utilities like MidiNumbers, keyboardShortcuts etc. Do you think it makes sense to separate them so that they can be used in the generic projects who want to use a different UI?

When using two piano keyboards in two lines, on renderNoteLabel only labels on bottom keyboard will be turned of on

  <PianoController firstNote="f4" secondNote="f7" soundFont={soundFont} />
  <PianoController firstNote="e1" secondNote="e4" soundFont={soundFont} />

Hi there, Im using your keyboard on mobile screen, so I split it in two keyboards due to screen size.

When I'm turning note labels on or off, only note labels on bottom keyboard will be turned off or on, only after app is restarted, will both keyboards labels be turned of or on, how could I change that so that I can turn labels on both keyboards?

mazzeka.com

hello .. u can make web studio audio record and midi player and editor ?

Disable Shortcut Labels

How do I disable the shortcut labels displayed on the piano whenever keyboard shortcuts are enabled?

How to sustain Active Notes on Mouse Over?

Hi Kevin,
Thank you so much for your contribution of react-piano. I am utilizing your package to display different piano chord shapes - when a user selects a chord shape (e.g. Major), I have set those notes in Active Notes (& in turn, Active Notes highlights the 3 keys on the piano that make C Major). This all functions correctly. However, if I move my mouse over any of the highlighted keys (active), they become unhighlighted (inactive). How can I change this property such that unless a new array of Active notes is provided, the keys remain highlighted & are unaffected by mouseover? Thank you!

how to display notes on keys

Hi there

How can I display Note on keys?

There is noteLabel to display assigned keyboard keys?

But how to display notes on the keys? Like in the photo
Screenshot_20220116-171037495

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.