Skip to content

Commit

Permalink
Merge pull request #50 from souporserious/add-audio-context-support
Browse files Browse the repository at this point in the history
add connectSource prop to Player
  • Loading branch information
souporserious authored Oct 4, 2018
2 parents 7c665dd + 0679db9 commit 54a165f
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 31 deletions.
99 changes: 79 additions & 20 deletions example/AudioPlayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,105 @@ import { Media, Player, controls, utils } from '../src/react-media-player'
import PlayPause from './PlayPause'
import MuteUnmute from './MuteUnmute'

const { CurrentTime, Progress, SeekBar, Duration, Volume, Fullscreen } = controls
const {
CurrentTime,
Progress,
SeekBar,
Duration,
Volume,
Fullscreen,
} = controls
const { formatTime } = utils

const audioContext = new (window.AudioContext || window.webkitAudioContext)()
const panner = audioContext.createPanner()
class Panner {
constructor({ source, audioContext, panningAmount = 0 }) {
this._source = source
this._audioContext = audioContext
this._initialPanningAmount = panningAmount
}

panner.setPosition(0, 0, 1)
panner.panningModel = 'equalpower'
panner.connect(audioContext.destination)
connect() {
this._splitter = this._audioContext.createChannelSplitter(2)
this._gainLeft = this._audioContext.createGain()
this._gainRight = this._audioContext.createGain()
this._merger = this._audioContext.createChannelMerger(2)
this._source.connect(
this._splitter,
0,
0
)
this._splitter.connect(
this._gainLeft,
0
)
this._splitter.connect(
this._gainRight,
1
)
this._gainLeft.connect(
this._merger,
0,
0
)
this._gainRight.connect(
this._merger,
0,
1
)
return this._merger
}

setPosition(amount) {
this._gainLeft.gain.value = amount <= 0 ? 1 : 1 - amount
this._gainRight.gain.value = amount >= 0 ? 1 : 1 + amount
}
}

const tracks = ['podcast', 'armstrong']

class AudioPlayer extends Component {
componentDidMount() {
const source = audioContext.createMediaElementSource(this._player.instance)
source.connect(panner)
panner.connect(audioContext.destination)
state = {
currentTrack: tracks[0],
}

_handlePannerChange = ({ target }) => {
const x = +target.value
const y = 0
const z = 1 - Math.abs(x)
panner.setPosition(x, y, z)
this.panner.setPosition(x, y, z)
}

_connectSource = (source, audioContext) => {
this.panner = new Panner({ source, audioContext })
return this.panner.connect()
}

render() {
return (
<Media>
<Media ref={c => (this.media = c)}>
<div>
{tracks.map(track => (
<button
key={track}
onClick={() => this.setState({ currentTrack: track })}
>
{track}
</button>
))}
<Player
ref={c => this._player = c}
src={this.props.src}
ref={c => (this._player = c)}
src={`/audio/${this.state.currentTrack}.mp3`}
connectSource={this._connectSource}
useAudioObject
// autoPlay
/>
<div className="media-controls">
<PlayPause className="media-control media-control--play-pause"/>
<CurrentTime className="media-control media-control--current-time"/>
<SeekBar className="media-control media-control--volume-range"/>
<Duration className="media-control media-control--duration"/>
<MuteUnmute className="media-control media-control--mute-unmute"/>
<Volume className="media-control media-control--volume"/>
<PlayPause className="media-control media-control--play-pause" />
<CurrentTime className="media-control media-control--current-time" />
<SeekBar className="media-control media-control--volume-range" />
<Duration className="media-control media-control--duration" />
<MuteUnmute className="media-control media-control--mute-unmute" />
<Volume className="media-control media-control--volume" />
</div>
<input
type="range"
Expand Down
Binary file added example/audio/podcast.mp3
Binary file not shown.
4 changes: 2 additions & 2 deletions src/Media.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ class Media extends Component {

playPause = () => {
if (!this.state.isPlaying) {
this._player.play()
return this.play()
} else {
this._player.pause()
this.pause()
}
}

Expand Down
68 changes: 59 additions & 9 deletions src/vendors/HTML5.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
import { findDOMNode } from 'react-dom'
import vendorPropTypes from './vendor-prop-types'

const audioContext = new (window.AudioContext || window.webkitAudioContext)()

class HTML5 extends Component {
static propTypes = vendorPropTypes

Expand All @@ -14,7 +16,48 @@ class HTML5 extends Component {
return findDOMNode(this._player)
}

componentDidMount() {
if (this.props.vendor === 'audio' && this.props.extraProps.connectSource) {
this.connectAudioContext()
}
}

componentDidUpdate(lastProps) {
if (this.props.vendor === 'audio' && this.props.extraProps.connectSource) {
if (this.props.vendor !== lastProps.vendor) {
this.connectAudioContext()
} else if (this.props.src !== lastProps.src) {
this.disconnectAudioContext()
this.connectAudioContext()
}
}
}

componentWillUnmount() {
if (this._source) {
this.disconnectAudioContext()
}
}

connectAudioContext() {
if (this.props.extraProps.useAudioObject || !this._source) {
this._source = audioContext.createMediaElementSource(this.node)
}
this._gain = audioContext.createGain()
this.props.extraProps
.connectSource(this._source, audioContext)
.connect(this._gain)
this._gain.connect(audioContext.destination)
}

disconnectAudioContext() {
this._source.disconnect(0)
}

play() {
if (audioContext.state === 'suspended') {
audioContext.resume()
}
return this._player.play()
}

Expand All @@ -34,11 +77,20 @@ class HTML5 extends Component {
}

mute(muted) {
const nextVolume = muted ? 0 : 1
this._player.muted = muted
this.setVolume(nextVolume)
this.props.onMute(muted)
this.props.onVolumeChange(nextVolume)
}

setVolume(volume) {
this._player.volume = volume
if (this._gain) {
this._gain.gain.value = volume
} else {
this._player.volume = volume
}
this.props.onVolumeChange(volume)
}

get _playerEvents() {
Expand All @@ -53,7 +105,6 @@ class HTML5 extends Component {
onProgress: this._handleProgress,
onLoadedMetadata: this._handleDuration,
onTimeUpdate: this._handleTimeUpdate,
onVolumeChange: this._handleVolumeChange,
}
}

Expand Down Expand Up @@ -105,18 +156,17 @@ class HTML5 extends Component {
this.props.onTimeUpdate(currentTime)
}

_handleVolumeChange = ({ target: { volume, muted } }) => {
this.props.onMute(muted)
this.props.onVolumeChange(volume)
}

render() {
const { vendor, src, extraProps } = this.props
const {
vendor,
src,
extraProps: { connectSource, useAudioObject, ...playerProps },
} = this.props

return createElement(vendor, {
ref: c => (this._player = c),
src,
...extraProps,
...playerProps,
...this._playerEvents,
})
}
Expand Down

0 comments on commit 54a165f

Please sign in to comment.