Skip to content
This repository has been archived by the owner on Aug 7, 2018. It is now read-only.

Commit

Permalink
passing the position to a callback works now thanks to the newly adde…
Browse files Browse the repository at this point in the history
…d onPosChange which merges audioprocess and seek events into one (to fix audioprocess not being fired on seek), documentation and example have been updated accordingly
  • Loading branch information
mspae committed Jan 19, 2016
1 parent f31ab47 commit e2a999f
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 25 deletions.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,35 @@ import ReactDOM from 'react-dom';
import Wavesurfer from 'react-wavesurfer';

class MyComponent extends React.Component {
constructor(props) {
super(props);

this.state = {
playing: false,
pos: 0
};
this.handleTogglePlay = this.handleTogglePlay.bind(this);
this.handlePosChange = this.handlePosChange.bind(this);
}
handleTogglePlay() {
this.setState({
playing: !this.state.playing
});
}
handlePosChange(e) {
this.setState({
pos: e.originalArgs[0]
});
}
render() {
let props = this.state;
return (
<div>
<Wavesurfer
audioFile: 'path/to/audio/file.mp3'
pos: 20,
playing: false
onPosChange={this.handlePosChange}
playing={this.state.playing}
/>
</div>
);
Expand All @@ -45,6 +66,16 @@ Prop name | type | description

### Callback props

#### Passing the playback position to a callback

If you want to use a callback specifically to receive the playback position you can use the `onPosChange` prop callback. It is basically called on `audioprocess` and `seek` events and receives the same type of argument object as the `onAudioprocess` callback: `{ wavesurfer: wavesurferInstance, originalArgs: [playBackPositionInSecs] }`

This function is a hack. Otherwise to be able to work with the playback position would require you to pass both `onSeek` and `onAudioprocess` to the component. However the `onSeek` function would receive the position in a different format (as a float) than the `onAudioprocess` function (in seconds). So this is just way to keep things simple until this issue has been properly resolved. (https://github.com/katspaugh/wavesurfer.js/issues/618)

See the `example/index.jsx` for a simple example how to use this.

#### Wavesurfer event callbacks

You can hook into wavesurfer events via functions you define as props. They can be used to wire up wavesurfer plugins until proper support for them is added. They receive an object of parameters:

```javascript
Expand All @@ -67,3 +98,4 @@ The full list of available callback props, see [documentation of wavesurfer.js](
`onScroll`
`onSeek`
`onZoom`

17 changes: 11 additions & 6 deletions example/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Wavesurfer from '../src/react-wavesurfer.js';
import Wavesurfer from '../dist/react-wavesurfer.js';



Expand All @@ -12,15 +12,22 @@ class SimpleExample extends React.Component {
super(props);

this.state = {
playing: false
playing: false,
pos: 0
};
this.handleTogglePlay = this.handleTogglePlay.bind(this);
this.handlePosChange = this.handlePosChange.bind(this);
}
handleTogglePlay() {
this.setState({
playing: !this.state.playing
});
}
handlePosChange(e) {
this.setState({
pos: e.originalArgs[0]
});
}
render() {
const waveOptions = {
scrollParent: true,
Expand All @@ -29,16 +36,14 @@ class SimpleExample extends React.Component {
waveColor: '#c4c8dc',
normalize: true
};
const process = (e) => {
console.log(e);
};
return (
<div className='example'>
<p>Clean Example</p>
<button onClick={this.handleTogglePlay}>toggle play</button>
<Wavesurfer
pos={this.state.pos}
options={waveOptions}
onAudioprocess={process}
onPosChange={this.handlePosChange}
audioFile={this.props.audioFile}
playing={this.state.playing}
/>
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "react-wavesurfer",
"version": "0.2.2",
"version": "0.2.3",
"description": "React component wrapper for wavesurfer.js",
"main": "dist/react-wavesurfer.js",
"scripts": {
"lint": "eslint ./src",
"start": "npm run lint && node example/index.js",
"test": "nodemon ./node_modules/.bin/jest",
"test": "./node_modules/.bin/jest",
"dist": "./node_modules/.bin/webpack --progress --colors --config webpack/dist.config.js",
"dist.min": "./node_modules/.bin/webpack --progress --colors --optimize-minimize --optimize-occurence-order --optimize-dedupe --config webpack/dist.config.min.js",
"build": "npm run bundle-plugins && npm run dist && npm run dist.min",
Expand All @@ -26,6 +26,7 @@
"react": ">=0.13"
},
"dependencies": {
"lodash": "^4.0.0",
"merge": "^1.2.0",
"react": ">=0.13",
"wavesurfer.js": "^1.0.48"
Expand Down
60 changes: 53 additions & 7 deletions src/react-wavesurfer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* global WaveSurfer */
import React, {Component, PropTypes} from 'react';
import merge from 'merge';
import _ from 'lodash';

// import wavesurfer.js commonjs build
const WaveSurfer = require('wavesurfer.js/dist/wavesurfer.cjs.js');
Expand Down Expand Up @@ -50,17 +51,22 @@ function positiveIntegerProptype(props, propName, componentName) {
}
}



class Wavesurfer extends Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
pos: 0
};

if (typeof WaveSurfer === undefined) {
throw new Error('WaveSurfer is undefined!');
}

this._wavesurfer = Object.create(WaveSurfer);
this._fileLoaded = false;
this._playing = false;
this._loadAudio = this._loadAudio.bind(this);
this._seekTo = this._seekTo.bind(this);
}
Expand All @@ -86,6 +92,31 @@ class Wavesurfer extends Component {
}
});

this._wavesurfer.on('audioprocess', (pos) => {
this.setState({
pos
});
this.props.onPosChange({
wavesurfer: this._wavesurfer,
originalArgs: [pos]
});
});

// `audioprocess` is not fired when seeking, so we have to plug into the
// `seek` event and calculate the equivalent in seconds (seek event
// receives a position float 0-1) – See the README.md for explanation why we
// need this
this._wavesurfer.on('seek', (pos) => {
pos = this._posToSec(pos);
this.setState({
pos
});
this.props.onPosChange({
wavesurfer: this._wavesurfer,
originalArgs: [pos]
});
});

const hookUpPropCallback = (e) => {
const propCallback = this.props['on' + capitaliseFirstLetter(e)];
const wavesurfer = this._wavesurfer;
Expand Down Expand Up @@ -133,7 +164,11 @@ class Wavesurfer extends Component {
if (this.props.audioFile !== nextProps.audioFile) {
this._loadAudio(nextProps.audioFile);
}
if (typeof nextProps.pos === 'number' && this._fileLoaded) {

if (nextProps.pos &&
this._fileLoaded &&
nextProps.pos !== this.props.pos &&
nextProps.pos !== this.state.pos) {
this._seekTo(nextProps.pos);
}
/*if (nextProps.regions) {
Expand All @@ -156,19 +191,31 @@ class Wavesurfer extends Component {
if (this.props.playing !== nextProps.playing) {
if (nextProps.playing) {
this._wavesurfer.play();
this._playing = true;
} else {
this._wavesurfer.pause();
this._playing = false;
}
}
}

shouldComponentUpdate(nextProps, nextState) {
return true;
return false;
}

// receives seconds and transforms this to the position as a float 0-1
_secToPos(sec) {
return 1 / this._wavesurfer.getDuration() * sec;
}

// receives position as a float 0-1 and transforms this to seconds
_posToSec(pos) {
return pos * this._wavesurfer.getDuration();
}

// pos is in seconds, the 0-1 proportional position we calculate here …
_seekTo(sec) {
let pos = 1 / this._wavesurfer.getDuration() * sec;
const pos = this._secToPos(sec);
if (this.props.autoCenter) {
this._wavesurfer.seekAndCenter(pos);
} else {
Expand All @@ -188,8 +235,6 @@ class Wavesurfer extends Component {
}
}



render() {
return (
<div ref='wavesurfer' />
Expand Down Expand Up @@ -242,7 +287,8 @@ Wavesurfer.defaultProps = {
playing: false,
pos: 0,
audioFile: undefined,
options: WaveSurfer.defaultParams
options: WaveSurfer.defaultParams,
onPosChange: function() {}
};

export default Wavesurfer;
Expand Down
42 changes: 33 additions & 9 deletions test/react-wavesurfer.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global describe, it, jest, expect, beforeEach */
/* global describe, it, jest, expect, beforeEach, spyOn */

jest.dontMock('../src/react-wavesurfer.js');

Expand All @@ -7,19 +7,43 @@ import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';

const WavesurferComponent = require('../src/react-wavesurfer').default;
global.WaveSurfer = require('../vendor/wavesurfer-bundle.js');
const Wavesurfer = require('../src/react-wavesurfer').default;

describe('React Wavesurfer component', () => {
describe('component basics', () => {
let mountedComponent, component;

it('should run the tests', () => {
expect(true).toBe(true);
beforeEach(() => {
mountedComponent = TestUtils.renderIntoDocument(<Wavesurfer />);
component = TestUtils.findRenderedComponentWithType(mountedComponent, Wavesurfer);
});

it('should render component', () => {
let wavesurfer = TestUtils.renderIntoDocument(<WavesurferComponent />);
let component = TestUtils.findRenderedComponentWithType(wavesurfer, WavesurferComponent);
expect(TestUtils.isElementOfType(component), WavesurferComponent);
expect(TestUtils.isElementOfType(component), Wavesurfer);
});

it('should have a wavesurfer.js instance', () => {
expect(component._wavesurfer).toBeDefined();
});
});

describe('wavesurfer.js callbacks', () => {
let callbacks, wavesurfer, component;
beforeEach(() => {
});

it('should trigger callbacks', () => {
callbacks = {
handleReady: (e) => {
console.log('done', e);
}
};
spyOn(callbacks, 'handleReady');
wavesurfer = TestUtils.renderIntoDocument(<Wavesurfer onReady={callbacks.handleReady} />);
component = TestUtils.findRenderedComponentWithType(wavesurfer, Wavesurfer);
component._wavesurfer.on('ready', callbacks.handleReady);
component._wavesurfer.fireEvent('ready');
//callbacks.handleReady();
console.log(component._wavesurfer);
expect(callbacks.handleReady).toHaveBeenCalled();
});
});

0 comments on commit e2a999f

Please sign in to comment.