Skip to content

Commit

Permalink
Add keyboard layout setting
Browse files Browse the repository at this point in the history
  • Loading branch information
t-hamano committed Jul 19, 2023
1 parent 1dd6005 commit c9d501b
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 14 deletions.
3 changes: 3 additions & 0 deletions src/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
},
"synthesizerSetting": {
"type": "object"
},
"keyLayout": {
"type": "string"
}
},
"supports": {
Expand Down
19 changes: 17 additions & 2 deletions src/components/controls/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
MIN_VOLUME,
MAX_VOLUME,
} from '../../constants';
import { KEYBOARD_LAYOUTS } from '../../keyboard-layout';
import SynthesizerSetting from '../synthesizer-setting';
import HelpModal from '../help-modal';
import { getNormalizedVolume } from '../../utils';
Expand All @@ -41,7 +42,8 @@ type Props = {
};

const Controls = ( { settings, piano, onChange }: Props ) => {
const { volume, useSustainPedal, octaveOffset, instrument, synthesizerSetting } = settings;
const { volume, useSustainPedal, octaveOffset, instrument, synthesizerSetting, keyLayout } =
settings;
const [ isHelpOpen, setIsHelpOpen ] = useState< boolean >( false );
const [ isSynthesizerSettingOpen, setIsSynthesizerSettingOpen ] = useState< boolean >( false );

Expand All @@ -67,6 +69,10 @@ const Controls = ( { settings, piano, onChange }: Props ) => {
onChange( { useSustainPedal: ! useSustainPedal } );
};

const onKeyLayoutChange = ( newKeyLayout: string ) => {
onChange( { keyLayout: newKeyLayout } );
};

const onSynthesizerSettingChange = (
newSynthesizerSetting: BlockAttributes[ 'synthesizerSetting' ]
) => {
Expand Down Expand Up @@ -145,12 +151,21 @@ const Controls = ( { settings, piano, onChange }: Props ) => {
) }
<ToggleControl
className="piano-block-controls__control"
label={ __( 'Use Sustain Pedal', 'piano-block' ) }
label={ __( 'Sustain Pedal', 'piano-block' ) }
checked={ useSustainPedal }
onChange={ onUseSustainPedalChange }
// @ts-ignore: `disabled` prop is not exist at @types
disabled={ instrument === 'synthesizer' }
/>
<SelectControl
className="piano-block-controls__control"
label={ __( 'Key Layout', 'piano-block' ) }
value={ keyLayout }
options={ KEYBOARD_LAYOUTS.map( ( { label, value } ) => {
return { label, value };
} ) }
onChange={ onKeyLayoutChange }
/>
<Button
className="piano-block-controls__help"
label={ __( 'Help', 'piano-block' ) }
Expand Down
2 changes: 1 addition & 1 deletion src/components/controls/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
border-radius: 2px;

.components-range-control {
width: 170px;
width: 140px;
}

.components-base-control__label {
Expand Down
12 changes: 9 additions & 3 deletions src/components/keyboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,24 @@ import {
/**
* Internal dependencies
*/
import { KEYBOARD_WIDTH, KEYBOARD_PADDING, KEYS } from '../../constants';
import { KEYBOARD_WIDTH, KEYBOARD_PADDING } from '../../constants';
import { KEYBOARD_LAYOUTS } from '../../keyboard-layout';
import type { Key } from '../../constants';

type Props = {
activeKeys: Key[];
keyLayout: string;
onKeyClick: ( note: string, octave: number ) => void;
};

const Keyboard = ( { activeKeys, onKeyClick }: Props ) => {
const Keyboard = ( { activeKeys, keyLayout, onKeyClick }: Props ) => {
// Hooks to control the display of horizontal scroll bars.
const [ resizeListener, keysInnerSizes ] = useResizeObserver();

const keys =
KEYBOARD_LAYOUTS.find( ( { value } ) => value === keyLayout )?.keys ||
KEYBOARD_LAYOUTS[ 0 ].keys;

// Trigger the note when the key is clicked by the mouse cursor or when the enter key is pressed.
const onClick = ( note: string, octave: number ) => {
onKeyClick( note, octave );
Expand All @@ -44,7 +50,7 @@ const Keyboard = ( { activeKeys, onKeyClick }: Props ) => {
className="piano-block-keyboard__inner"
style={ { width: `${ KEYBOARD_WIDTH }px`, padding: `0 ${ KEYBOARD_PADDING }px` } }
>
{ KEYS.map( ( key, index ) => (
{ keys.map( ( key, index ) => (
<button
key={ index }
className={ classnames( 'piano-block-keyboard__key', {
Expand Down
2 changes: 1 addition & 1 deletion src/components/keyboard/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
justify-content: center;
padding: 0;
padding-bottom: 8px;
font-size: 14px;
font-size: 13px;
font-weight: normal;
text-transform: uppercase;

Expand Down
17 changes: 11 additions & 6 deletions src/components/piano/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { useEffect, useState, useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import { DEFAULT_ENVELOPE, INSTRUMENTS, KEYS } from '../../constants';
import { DEFAULT_ENVELOPE, INSTRUMENTS } from '../../constants';
import { KEYBOARD_LAYOUTS } from '../../keyboard-layout';
import Loading from '../loading';
import Keyboard from '../keyboard';
import Controls from '../controls';
Expand All @@ -26,14 +27,19 @@ type Props = {

const Piano = ( { settings, onChange }: Props ) => {
const { assetsUrl } = window.pianoBlockVars;
const { volume, useSustainPedal, octaveOffset, instrument, synthesizerSetting } = settings;
const { volume, useSustainPedal, octaveOffset, instrument, synthesizerSetting, keyLayout } =
settings;
const [ piano, setPiano ] = useState< Tone.Sampler | Tone.PolySynth >();
const [ isReady, setIsReady ] = useState< boolean >( false );
const [ activeKeys, setActiveKeys ] = useState< Key[] >( [] );
const [ instrumentOctaveOffset, setInstrumentOctaveOffset ] = useState< number >( 0 );

const ref = useRef< HTMLDivElement >( null );

const keys: Key[] =
KEYBOARD_LAYOUTS.find( ( { value } ) => value === keyLayout )?.keys ||
KEYBOARD_LAYOUTS[ 0 ].keys;

// Create Tone.js Player.
useEffect( () => {
const instrumentSetting = INSTRUMENTS.find( ( { value } ) => value === instrument );
Expand Down Expand Up @@ -118,9 +124,7 @@ const Piano = ( { settings, onChange }: Props ) => {
}

// Search the pressed key.
const targetKey: Key | undefined = KEYS.find( ( key ) =>
key.name.some( ( name ) => event.key === name )
);
const targetKey = keys.find( ( key ) => key.name.some( ( name ) => event.key === name ) );
if ( ! targetKey ) {
return;
}
Expand Down Expand Up @@ -150,7 +154,7 @@ const Piano = ( { settings, onChange }: Props ) => {
return;
}

const targetKey = KEYS.find( ( key ) => key.name.some( ( name ) => event.key === name ) );
const targetKey = keys.find( ( key ) => key.name.some( ( name ) => event.key === name ) );

if ( ! targetKey ) {
return;
Expand Down Expand Up @@ -199,6 +203,7 @@ const Piano = ( { settings, onChange }: Props ) => {

const keyboardProps = {
activeKeys,
keyLayout,
onKeyClick,
};

Expand Down
9 changes: 8 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface BlockAttributes {
release: number;
};
};
keyLayout: string;
}

export const MIN_VOLUME = -10 as const;
Expand All @@ -36,6 +37,7 @@ export const DEFAULT_SETTINGS = {
instrument: DEFAULT_INSTRUMENT,
showOnFront: false,
synthesizerSetting: {},
keyLayout: 'qwerty-1',
};

export const DEFAULT_ENVELOPE = {
Expand Down Expand Up @@ -331,6 +333,11 @@ export interface Instrument {
volumeOffset: number;
}

export type Key = ( typeof KEYS )[ number ];
export type OscillatorType = ( typeof OSCILLATOR_TYPES )[ number ];
export type EmvelopeControl = ( typeof EMVELOPE_CONTROLS )[ number ];
export type Key = {
note: string;
octave: number;
isBlackKey: boolean;
name: string[];
};
112 changes: 112 additions & 0 deletions src/keyboard-layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

export const KEYBOARD_LAYOUTS = [
{
label: __( 'QWERTY 1', 'piano-block' ),
value: 'qwerty-1',
keys: [
{ note: 'C', octave: 3, isBlackKey: false, name: [ 'z' ] },
{ note: 'C#', octave: 3, isBlackKey: true, name: [ 's' ] },
{ note: 'D', octave: 3, isBlackKey: false, name: [ 'x' ] },
{ note: 'D#', octave: 3, isBlackKey: true, name: [ 'd' ] },
{ note: 'E', octave: 3, isBlackKey: false, name: [ 'c' ] },
{ note: 'F', octave: 3, isBlackKey: false, name: [ 'v' ] },
{ note: 'F#', octave: 3, isBlackKey: true, name: [ 'g' ] },
{ note: 'G', octave: 3, isBlackKey: false, name: [ 'b' ] },
{ note: 'G#', octave: 3, isBlackKey: true, name: [ 'h' ] },
{ note: 'A', octave: 3, isBlackKey: false, name: [ 'n' ] },
{ note: 'A#', octave: 3, isBlackKey: true, name: [ 'j' ] },
{ note: 'B', octave: 3, isBlackKey: false, name: [ 'm' ] },
{ note: 'C', octave: 4, isBlackKey: false, name: [ ',', 'q' ] },
{ note: 'C#', octave: 4, isBlackKey: true, name: [ 'l', '2' ] },
{ note: 'D', octave: 4, isBlackKey: false, name: [ '.', 'w' ] },
{ note: 'D#', octave: 4, isBlackKey: true, name: [ '3' ] },
{ note: 'E', octave: 4, isBlackKey: false, name: [ 'e' ] },
{ note: 'F', octave: 4, isBlackKey: false, name: [ 'r' ] },
{ note: 'F#', octave: 4, isBlackKey: true, name: [ '5' ] },
{ note: 'G', octave: 4, isBlackKey: false, name: [ 't' ] },
{ note: 'G#', octave: 4, isBlackKey: true, name: [ '6' ] },
{ note: 'A', octave: 4, isBlackKey: false, name: [ 'y' ] },
{ note: 'A#', octave: 4, isBlackKey: true, name: [ '7' ] },
{ note: 'B', octave: 4, isBlackKey: false, name: [ 'u' ] },
{ note: 'C', octave: 5, isBlackKey: false, name: [ 'i' ] },
{ note: 'C#', octave: 5, isBlackKey: true, name: [ '9' ] },
{ note: 'D', octave: 5, isBlackKey: false, name: [ 'o' ] },
{ note: 'D#', octave: 5, isBlackKey: true, name: [ '0' ] },
{ note: 'E', octave: 5, isBlackKey: false, name: [ 'p' ] },
],
},
{
label: __( 'QWERTY 2', 'piano-block' ),
value: 'qwerty-2',
keys: [
{ note: 'C', octave: 3, isBlackKey: false, name: [ 'z' ] },
{ note: 'C#', octave: 3, isBlackKey: true, name: [ 's' ] },
{ note: 'D', octave: 3, isBlackKey: false, name: [ 'x' ] },
{ note: 'D#', octave: 3, isBlackKey: true, name: [ 'd' ] },
{ note: 'E', octave: 3, isBlackKey: false, name: [ 'c' ] },
{ note: 'F', octave: 3, isBlackKey: false, name: [ 'v' ] },
{ note: 'F#', octave: 3, isBlackKey: true, name: [ 'g' ] },
{ note: 'G', octave: 3, isBlackKey: false, name: [ 'b' ] },
{ note: 'G#', octave: 3, isBlackKey: true, name: [ 'h' ] },
{ note: 'A', octave: 3, isBlackKey: false, name: [ 'n' ] },
{ note: 'A#', octave: 3, isBlackKey: true, name: [ 'j' ] },
{ note: 'B', octave: 3, isBlackKey: false, name: [ 'm' ] },
{ note: 'C', octave: 4, isBlackKey: false, name: [ ',', 'q' ] },
{ note: 'C#', octave: 4, isBlackKey: true, name: [ 'l', '1' ] },
{ note: 'D', octave: 4, isBlackKey: false, name: [ '.', 'w' ] },
{ note: 'D#', octave: 4, isBlackKey: true, name: [ '2' ] },
{ note: 'E', octave: 4, isBlackKey: false, name: [ 'e' ] },
{ note: 'F', octave: 4, isBlackKey: false, name: [ 'r' ] },
{ note: 'F#', octave: 4, isBlackKey: true, name: [ '4' ] },
{ note: 'G', octave: 4, isBlackKey: false, name: [ 't' ] },
{ note: 'G#', octave: 4, isBlackKey: true, name: [ '5' ] },
{ note: 'A', octave: 4, isBlackKey: false, name: [ 'y' ] },
{ note: 'A#', octave: 4, isBlackKey: true, name: [ '6' ] },
{ note: 'B', octave: 4, isBlackKey: false, name: [ 'u' ] },
{ note: 'C', octave: 5, isBlackKey: false, name: [ 'i' ] },
{ note: 'C#', octave: 5, isBlackKey: true, name: [ '8' ] },
{ note: 'D', octave: 5, isBlackKey: false, name: [ 'o' ] },
{ note: 'D#', octave: 5, isBlackKey: true, name: [ '9' ] },
{ note: 'E', octave: 5, isBlackKey: false, name: [ 'p' ] },
],
},
{
label: __( 'AZERTY 1', 'piano-block' ),
value: 'azerty-1',
keys: [
{ note: 'C', octave: 3, isBlackKey: false, name: [ '<' ] },
{ note: 'C#', octave: 3, isBlackKey: true, name: [ 'q' ] },
{ note: 'D', octave: 3, isBlackKey: false, name: [ 'w' ] },
{ note: 'D#', octave: 3, isBlackKey: true, name: [ 's' ] },
{ note: 'E', octave: 3, isBlackKey: false, name: [ 'x' ] },
{ note: 'F', octave: 3, isBlackKey: false, name: [ 'c' ] },
{ note: 'F#', octave: 3, isBlackKey: true, name: [ 'f' ] },
{ note: 'G', octave: 3, isBlackKey: false, name: [ 'v' ] },
{ note: 'G#', octave: 3, isBlackKey: true, name: [ 'g' ] },
{ note: 'A', octave: 3, isBlackKey: false, name: [ 'b' ] },
{ note: 'A#', octave: 3, isBlackKey: true, name: [ 'h' ] },
{ note: 'B', octave: 3, isBlackKey: false, name: [ 'n' ] },
{ note: 'C', octave: 4, isBlackKey: false, name: [ ',', 'a' ] },
{ note: 'C#', octave: 4, isBlackKey: true, name: [ 'k', '&' ] },
{ note: 'D', octave: 4, isBlackKey: false, name: [ ';', 'z' ] },
{ note: 'D#', octave: 4, isBlackKey: true, name: [ 'é' ] },
{ note: 'E', octave: 4, isBlackKey: false, name: [ 'e' ] },
{ note: 'F', octave: 4, isBlackKey: false, name: [ 'r' ] },
{ note: 'F#', octave: 4, isBlackKey: true, name: [ "'" ] },
{ note: 'G', octave: 4, isBlackKey: false, name: [ 't' ] },
{ note: 'G#', octave: 4, isBlackKey: true, name: [ '(' ] },
{ note: 'A', octave: 4, isBlackKey: false, name: [ 'y' ] },
{ note: 'A#', octave: 4, isBlackKey: true, name: [ '-' ] },
{ note: 'B', octave: 4, isBlackKey: false, name: [ 'u' ] },
{ note: 'C', octave: 5, isBlackKey: false, name: [ 'i' ] },
{ note: 'C#', octave: 5, isBlackKey: true, name: [ '_' ] },
{ note: 'D', octave: 5, isBlackKey: false, name: [ 'o' ] },
{ note: 'D#', octave: 5, isBlackKey: true, name: [ 'ç' ] },
{ note: 'E', octave: 5, isBlackKey: false, name: [ 'p' ] },
],
},
];

0 comments on commit c9d501b

Please sign in to comment.