Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: metronome render props explicit any #36

Merged
merged 3 commits into from
Jul 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/components/Metronome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,10 @@ const Metronome = ({
soundEnabled
soundPattern={barCount === 0 ? patternMaker.getMetronomeCountInString() : metronomeString}
beatsPerBar={beatsPerBar}
// temporary any for props and state
render={(props: any, state: any) => (
render={(_, { qNote, subNote }) => (
<>
{setCurrentBeat(state.qNote)}
{setCurrentSubBeat(state.subNote)}
{setCurrentBeat(qNote)}
{setCurrentSubBeat(subNote)}
<div
style={{
height: '3em',
Expand All @@ -128,7 +127,7 @@ const Metronome = ({
}}
>
{isCountIn()
? Number(patternMaker.getSettings().timeSignature.beats) - state.qNote + 1
? Number(patternMaker.getSettings().timeSignature.beats) - qNote + 1
: barCount > 0 &&
oneToBeatsPerBar.map((beat) => {
return <Box sx={{ ...counter }} key={`beat${beat}`} />;
Expand Down
145 changes: 85 additions & 60 deletions src/react-pro-metronome/src/index.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,106 @@
import React, { PureComponent } from 'react'
import PropTypes, { number } from 'prop-types'
import { Howl } from 'howler'

import { numberInRange, stringWithLength } from '../utils/advanced-prop-types'

import click3SoundFileMP3 from './sounds/click3.mp3'
import click3SoundFileOGG from './sounds/click3.ogg'
import click3SoundFileAAC from './sounds/click3.aac'

import click2SoundFileMP3 from './sounds/click2.mp3'
import click2SoundFileOGG from './sounds/click2.ogg'
import click2SoundFileAAC from './sounds/click2.aac'

import click1SoundFileMP3 from './sounds/click1.mp3'
import click1SoundFileOGG from './sounds/click1.ogg'
import click1SoundFileAAC from './sounds/click1.aac'

const MAXBPM = 300
const MAXSUBDIVISION = 8

import { Howl } from 'howler';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';

import { numberInRange, stringWithLength } from '../utils/advanced-prop-types';

import click3SoundFileAAC from './sounds/click3.aac';
import click3SoundFileMP3 from './sounds/click3.mp3';
import click3SoundFileOGG from './sounds/click3.ogg';

import click2SoundFileAAC from './sounds/click2.aac';
import click2SoundFileMP3 from './sounds/click2.mp3';
import click2SoundFileOGG from './sounds/click2.ogg';

import click1SoundFileAAC from './sounds/click1.aac';
import click1SoundFileMP3 from './sounds/click1.mp3';
import click1SoundFileOGG from './sounds/click1.ogg';

const MAXBPM = 300;
const MAXSUBDIVISION = 8;

/**
* @callback renderItem
* @param {ProMetronomeProps} props
* @param {ProMetronomeState} state
* @returns {JSX.Element}
*/
/**
* @typedef ProMetronomeProps
* @type {object}
* @property {number} bpm
* @property {number} subdivision
* @property {boolean} isPlaying
* @property {boolean} soundEnabled
* @property {number} beatsPerBar
* @property {string} soundPattern
* @property {renderItem} render
*
*/

/**
* @typedef ProMetronomeState
* @type {object}
* @property {number} qNote
* @property {number} subNote
*/

/**
* @augments {PureComponent<ProMetronomeProps, ProMetronomeState>}
*/
class ProMetronome extends PureComponent {
state = {
qNote: 1,
subNote: 1
}
subNote: 1,
};

clickSounds = [
new Howl({
src: [click1SoundFileMP3, click1SoundFileOGG, click1SoundFileAAC],
preload: true
preload: true,
}),
new Howl({
src: [click2SoundFileMP3, click2SoundFileOGG, click2SoundFileAAC],
preload: true
preload: true,
}),
new Howl({
src: [click3SoundFileMP3, click3SoundFileOGG, click3SoundFileAAC],
preload: true
})
]
preload: true,
}),
];

update = () => {
const { soundEnabled, soundPattern, subdivision } = this.props
const { qNote, subNote } = this.state
const { soundEnabled, soundPattern, subdivision } = this.props;
const { qNote, subNote } = this.state;

if (soundEnabled && soundPattern.length === this.props.beatsPerBar * subdivision) {
const soundLevel = soundPattern.charAt(
(qNote - 1) * subdivision + subNote - 1
)
if (soundLevel > 0 && soundLevel <= 3)
this.clickSounds[soundLevel - 1].play()
const soundLevel = soundPattern.charAt((qNote - 1) * subdivision + subNote - 1);
if (soundLevel > 0 && soundLevel <= 3) this.clickSounds[soundLevel - 1].play();
}

if (subNote < subdivision) {
this.setState(prevState => ({
subNote: prevState.subNote + 1
}))
this.setState((prevState) => ({
subNote: prevState.subNote + 1,
}));
} else {
this.setState(prevState => ({
this.setState((prevState) => ({
// modification: prevState.qNote === X (Beats per Bar)
qNote: prevState.qNote === this.props.beatsPerBar ? 1 : prevState.qNote + 1,
subNote: 1
}))
subNote: 1,
}));
}
}
};

calculateInterval = (bpm, subdivision) => {
return Math.floor(60000 / (bpm * subdivision))
}
return Math.floor(60000 / (bpm * subdivision));
};

componentDidMount() {
if (this.props.isPlaying) {
this.timerID = setInterval(
this.update,
this.calculateInterval(this.props.bpm, this.props.subdivision)
)
);
}
}

Expand All @@ -84,29 +110,28 @@ class ProMetronome extends PureComponent {
this.timerID = setInterval(
this.update,
this.calculateInterval(nextProps.bpm, nextProps.subdivision)
)
);
} else {
clearInterval(this.timerID)
clearInterval(this.timerID);
}
} else if (
nextProps.isPlaying &&
(nextProps.bpm !== this.props.bpm ||
nextProps.subdivision !== this.props.subdivision)
(nextProps.bpm !== this.props.bpm || nextProps.subdivision !== this.props.subdivision)
) {
clearInterval(this.timerID)
clearInterval(this.timerID);
this.timerID = setInterval(
this.update,
this.calculateInterval(nextProps.bpm, nextProps.subdivision)
)
);
}
}

componentWillUnmount() {
clearInterval(this.timerID)
clearInterval(this.timerID);
}

render() {
return this.props.render(this.props, this.state)
return this.props.render(this.props, this.state);
}
}

Expand All @@ -117,13 +142,13 @@ ProMetronome.propTypes = {
soundEnabled: PropTypes.bool,
beatsPerBar: PropTypes.number,
soundPattern: (props, propName, componentName) =>
// pretty sure props['beatsPerBar'] is correct for prop access within propTypes
// it follows the props['subdivision'] pattern, look more into propTypes checks as I believe
// these are for error checking (console, or test). Probably redundant after upgrade to typescript,
// but maybe useful for logging as cannot be checked at compile time.
// pretty sure props['beatsPerBar'] is correct for prop access within propTypes
// it follows the props['subdivision'] pattern, look more into propTypes checks as I believe
// these are for error checking (console, or test). Probably redundant after upgrade to typescript,
// but maybe useful for logging as cannot be checked at compile time.
stringWithLength(props['beatsPerBar'] * props['subdivision'])(props, propName, componentName),
render: PropTypes.func.isRequired,
}
};

ProMetronome.defaultProps = {
bpm: 80,
Expand All @@ -132,6 +157,6 @@ ProMetronome.defaultProps = {
soundEnabled: false,
beatsPerBar: 4,
soundPattern: '',
}
};

export default ProMetronome
export default ProMetronome;