Skip to content

Instantly share code, notes, and snippets.

@gavinwahl
Created August 11, 2018 04:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gavinwahl/8b02eadd05e1159525fae75b914d4808 to your computer and use it in GitHub Desktop.
Save gavinwahl/8b02eadd05e1159525fae75b914d4808 to your computer and use it in GitHub Desktop.
Audio in React
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const {Provider: AudioContextProvider, Consumer: AudioConsumer} = React.createContext();
class AudioProvider extends Component {
static propTypes = {
children: PropTypes.arrayOf(PropTypes.node).isRequired,
}
state = {
currentlyPlaying: null,
playingFailed: false,
}
play = (file) => {
return new Promise((resolve, reject) =>
this.setState(
{
currentlyPlaying: file,
onComplete: resolve,
},
() => {
this.audioTag.load();
let playPromise = this.audioTag.play();
if (playPromise !== undefined) {
playPromise.catch((e) => {
if (e.name === 'NotAllowedError') {
// autoplay not allowed
this.setState({playingFailed: true});
} else {
// some other error, give it to the caller of play().
reject(e);
}
});
}
}
)
);
}
playArray = async (files) => {
for (let i = 0; i < files.length; i++) {
await this.play(files[i]);
}
}
audioEnded = () => {
this.setState({currentlyPlaying: null, onComplete: null}, this.state.onComplete);
}
playButtonClick = () => {
this.audioTag.play();
this.setState({playingFailed: false});
}
render() {
let ctx = {
play: this.play,
playArray: this.playArray,
currentlyPlaying: this.state.currentlyPlaying,
};
return (
<AudioContextProvider
value={ ctx }
>
<audio
ref={ (audioTag) => this.audioTag = audioTag }
onEnded={ this.audioEnded }
>
<source src={ this.state.currentlyPlaying ? `/static/audio/${this.state.currentlyPlaying}` : null } />
</audio>
{ this.state.playingFailed ? this.renderPlayButton() : null }
{ this.props.children }
</AudioContextProvider>
);
}
renderPlayButton() {
return (
<div
id="playButtonOverlay"
onClick={ this.playButtonClick }
/>
);
}
}
export { AudioConsumer, AudioProvider };
componentDidMount() {
this.props.audio.sequence(async () => {
await this.props.audio.playArray('1.mp3');
await delay(1000);
this.setState({isGlowing: 0});
await this.props.audio.play('2.mp3');
this.setState({isGlowing: null});
await delay(1000);
this.setState({isGlowing: 1});
await this.props.audio.play('3.mp3');
this.setState({isGlowing: null});
await delay(500);
await this.props.audio.play('4.mp3');
this.setState({ready: true});
}, this.props.autoPlay);
}
import React, { Component } from 'react';
import { AudioConsumer } from '../../audio';
export default function withAudio(WrappedComponent) {
return class NewComponent extends Component {
state = {
inSequence: false,
beginSequence: null,
}
sequence = async (beginSequence, autoplay=true) => {
this.setState({
beginSequence: beginSequence,
inSequence: true,
});
if (autoplay) {
await beginSequence();
}
this.setState({
inSequence: false,
});
}
replay = () => {
return this.sequence(this.state.beginSequence);
}
render() {
return (
<AudioConsumer>
{
audio => (
<WrappedComponent
audio={ {
replay: this.replay,
sequence: this.sequence,
inSequence: this.state.inSequence,
...audio
} }
{ ...this.props }
/>
)
}
</AudioConsumer>
);
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment