This repository has been archived on 2022-09-16. You can view files and clone it, but cannot push or open issues/pull-requests.
tlaternet-templates/src/music/MusicPlayer.tsx

107 lines
3.1 KiB
TypeScript

import React from "react";
import { connect } from "react-redux";
import Controls from "./components/controls";
import Visualizer from "./components/visualizer";
import { State } from "./store";
type AudioState = {
audioContext: AudioContext;
audioSource: HTMLAudioElement;
audioSourceNode: MediaElementAudioSourceNode;
audioVolume: GainNode;
};
type MusicPlayerProps = {
playing: boolean;
muted: boolean;
source?: string;
};
class MusicPlayer extends React.Component<MusicPlayerProps, State> {
private audioState: AudioState;
constructor(props: MusicPlayerProps) {
super(props);
const context = new AudioContext();
const source = new Audio();
const sourceNode = context.createMediaElementSource(source);
const volume = context.createGain();
sourceNode.connect(volume);
volume.connect(context.destination);
this.audioState = {
audioContext: context,
audioSourceNode: sourceNode,
audioSource: source,
audioVolume: volume,
};
}
render() {
return (
<div className="is-flex-grow-1 is-flex is-flex-direction-column">
<div className="is-flex-grow-1 is-overflow-hidden">
<Visualizer
audioContext={this.audioState.audioContext}
audioSource={this.audioState.audioSourceNode}
/>
</div>
<div className="is-flex-grow-0">
<Controls />
</div>
</div>
);
}
async componentDidUpdate() {
const context = this.audioState.audioContext;
const source = this.audioState.audioSource;
const volume = this.audioState.audioVolume;
// First, set the audio source (if it changed)
if (this.props.source && source.src != this.props.source) {
source.src = this.props.source;
}
if (this.props.playing) {
// Chrome is super awkward about AudioContext, and won't
// even allow creating one without complaining about
// wanting user input, so we need to resume the context
// before we can actually play.
//
// Luckily, this has no adverse effects on Firefox.
try {
await context.resume();
await source.play();
} catch (error) {
if (error instanceof DOMException) {
console.error(`Could not play audio: ${error.message}`);
} else {
throw error;
}
}
} else {
source.pause();
}
if (!this.props.muted) {
volume.gain.setValueAtTime(1, context.currentTime);
} else {
volume.gain.setValueAtTime(0, context.currentTime);
}
}
}
function mapStateToProps(state: State): MusicPlayerProps {
return {
playing: state.musicState.playing,
muted: state.musicState.muted,
source: state.musicState.source,
};
}
export default connect(mapStateToProps)(MusicPlayer);