Skip to content

Commit

Permalink
Adjust README to account for most recent additions to the API, allow …
Browse files Browse the repository at this point in the history
…for passing waveform data for each URL when creating a new playlist
  • Loading branch information
michaeldzjap committed Jul 11, 2018
1 parent 5b2580f commit 1fb8081
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
"max-params": ["warn", 3],
"max-statements": [
"warn",
10,
20,
{"ignoreTopLevelFunctions": true}
],
"multiline-ternary": ["error", "always-multiline"],
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ License: MIT
## Browser Support & Other Requirements
*waveplayer.js* is tested to work on *Chrome*, *Firefox* and *Opera* (no support for *Internet Explorer* yet).

In order to minimise waveform drawing times it is necessary to supply a URL to a JSON file representing the waveform of the audio file numerically. This JSON file should have the same name as the corresponding audio file and should exist at the same location. An error will be thrown if this JSON file does not exist.
In order to minimise waveform drawing times it is necessary to supply an URL to a JSON file or a data array / object representing the waveform of the audio file numerically. When supplying a JSON file, it should have the same name as the corresponding audio file and should exist at the same location. An error will be thrown if this JSON file does not exist and no waveform data was passed in explicitly as a second argument to the `load()` function.

There exist a number of tools for extracting waveform data in JSON format from an audio file; [wav2json](https://github.com/beschulz/wav2json) or [py-wav2json](https://github.com/michaeldzjap/py-wav2json) can be used for instance. It is enough to provide a single array of floating point values. If the JSON contains an associative array, only the first entree will be used.

Expand Down Expand Up @@ -84,34 +84,43 @@ View the code for a full playlist example [here](/example/src/playlist.js)
| `interact` | boolean | `true` | Enables/disables mouse interaction with the waveform view. This may be changed at any point after creation. |
| `progressColor` | string | `#31708f` | The fill color of the waveform bars that have been played back so far. |
| `responsive` | boolean | `true` | If set to true, the width of the waveform view adapts to the width of the container element. |
| `useGradient` | boolean | `true` | Indicates if the waveform should be drawn with a gradient or not. |
| `waveColor` | string | `#428bca` | The fill color of the waveform bars that have not been played back so far. |
| `width` | integer | 512 | The width of the waveform in pixels (only relevant when the `responsive` option is set to `false`). |

## waveplayer.js Methods
* `cancelPlaylist()`

Cancels the currently active playlist (will also stop the audio instantly).
* `createPlaylist(urls)`
* `createPlaylist(urls, options)`

Creates a new playlist (it will cancel the currently active playlist if there is one).

**Arguments**:

`urls` is an array of valid audio file URL's.
`urls` is an array of valid audio file URL's or an array of objects each having an `url` and `data` property, where `url` points to the audio file URL and `data` is an array or object holding valid waveform data (also see documentation for `load()`).

`options` (**optional**) is an object with the following keys:

- `autoPlay`: A boolean that indicates if the playlist should start playing automatically after it was created.
* `currentTime`

Getter for the playback time (in seconds) of the currently loaded / playing track.
* `destroy()`

Stops playback, cancels any running playlists and unsubscribes from any events that are listened for.
* `load(url)`
* `duration`

Getter for the total duration (in seconds) of the currently loaded / playing track.
* `load(url, data)`

Load an audio file from a URL and return a Promise which may be used to perform an action when the audio file has finished loading.

**Arguments**:

`url` is a valid URL to an audio file.

`data` (**optional**) is an array or object containing waveform data. If it is not supplied, the waveform data is extracted from the JSON file. If `data` is an object it is expected that the first key points to an array of waveform values. Note that only the first key found in the object is queried for waveform data.
* `interact` or `interact = bool`

Setter or getter for enabling/disabling mouse interaction with the waveform view.
Expand Down
45 changes: 25 additions & 20 deletions src/Playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class Playlist {
// Merge any supplied options with default options
this._options = {...this._defaultOptions, ...options};
this._wavePlayer = wavePlayer;
this._urls = [...urls];
this._urls = urls;
this._audioElm = this._wavePlayer._audioElm;
this._scheduler = this._createScheduler(urls, this._options.autoplay);
}
Expand Down Expand Up @@ -163,47 +163,52 @@ class Playlist {
/**
* Create a new scheduler for the playlist instance.
*
* @param {array} urls
* @param {Array} urls
* @param {boolean} autoPlay
* @returns {Promise}
*/
_createScheduler(urls, autoPlay) {
this._currentTrackIndex = 0;
const scheduler = stateResolver((function *(urls) {
while (this._currentTrackIndex < urls.length) {
yield this._wavePlayer.load(urls[this._currentTrackIndex]);
if (this._currentTrackIndex > 0) {
const scheduler = stateResolver((function *(urls, me) { // eslint-disable-line
while (me._currentTrackIndex < urls.length) {
const {url, data} = isObject(urls[me._currentTrackIndex])
? urls[me._currentTrackIndex]
: {url: urls[me._currentTrackIndex], data: null};

yield me._wavePlayer.load(url, data);

if (me._currentTrackIndex > 0) {
WavePlayer._mediator.fire(
'waveplayer:playlist:next',
this._wavePlayer,
me._wavePlayer,
{
url: urls[this._currentTrackIndex],
trackNumber: this._currentTrackIndex + 1
url: urls[me._currentTrackIndex],
trackNumber: me._currentTrackIndex + 1
}
);
this._wavePlayer.play();
me._wavePlayer.play();
} else {
WavePlayer._mediator
.fire('waveplayer:playlist:ready', this._wavePlayer);
if (autoPlay || this._skipped) {
this._wavePlayer.play();
}
.fire('waveplayer:playlist:ready', me._wavePlayer);
if (autoPlay || me._skipped) me._wavePlayer.play();
}

// Wait until the current track finishes playing
yield this._onEnd();
this._currentTrackIndex++;
yield me._onEnd();
me._currentTrackIndex++;
}

return this._currentTrackIndex;
})).bind(this);
return me._currentTrackIndex;
}));

scheduler(urls).then(
scheduler(urls, this).then(
response => {
this._skipped = false;
WavePlayer._mediator
.fire(
'waveplayer:playlist:finished',
this._wavePlayer, response
this._wavePlayer,
response
);
}
);
Expand Down
32 changes: 21 additions & 11 deletions src/WavePlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class WavePlayer {
*/
_currentTime;

/**
* The waveform amplitude data.
*/

/**
* Initialize a new waveplayer instance.
*
Expand All @@ -61,9 +65,7 @@ class WavePlayer {
*/
constructor(options) {
// Create a new mediator if there does not exist one yet
if (!WavePlayer._mediator) {
WavePlayer._mediator = new Mediator;
}
if (!WavePlayer._mediator) WavePlayer._mediator = new Mediator;

this._waveView = new WaveView(null, {...options});

Expand Down Expand Up @@ -180,17 +182,25 @@ class WavePlayer {
* when the track has finished loading.
*
* @param {string} url
* @param {Object|Array|null} data
* @returns {Promise}
*/
load(url) {
load(url, data = null) {
return Promise.all([
new Promise(resolve => {
this._audioElm.src = url;
this._audioElm.load();
this._currentTime = 0;
WavePlayer._mediator.on('waveplayer:canplay', () => resolve());
}),
this._getWaveformData(url)
data
? Promise.resolve(this._waveView.drawWave(
typeof data === 'object'
? [...data[Object.keys(data)[0]]]
: [...data],
0
))
: this._getWaveformData(url)
]);
}

Expand Down Expand Up @@ -439,12 +449,12 @@ class WavePlayer {
return new Promise((resolve, reject) => {
getJSON(`${url.substr(0, url.lastIndexOf('.'))}.json`)
.then(response => {
if (typeof response === 'object') {
this._waveView
.drawWave(response[Object.keys(response)[0]], 0);
} else {
this._waveView.drawWave(response, 0);
}
this._waveView.drawWave(
typeof response === 'object'
? response[Object.keys(response)[0]]
: response,
0
);
resolve('waveplayer:json:fetched');
})
.catch(err => reject(err));
Expand Down
10 changes: 5 additions & 5 deletions src/WaveView.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ class WaveView {
interact: true,
responsive: true,
progress: 0,
useGradient: true
useGradient: true,
};

/**
* The amplitude data that will be used to draw the waveform.
*
* @var {Array}
* @var {Array|null}
*/
_data;
_data = null;

/**
* The options for this waveplayer instance.
Expand Down Expand Up @@ -96,7 +96,7 @@ class WaveView {
WavePlayer._mediator = new Mediator;
}

this._data = data;
if (data) this.drawWave(data, 0);
this._options = {...this._defaultOptions, ...options};
this.container = 'string' === typeof this._options.container
? document.querySelector(this._options.container)
Expand Down Expand Up @@ -278,7 +278,7 @@ class WaveView {
* @returns {void}
*/
drawWave(values, progress) {
this.data = values;
this._data = values;
this._progress = progress;
this._barData = this._calcAvgAmps();
this.clearWave();
Expand Down

0 comments on commit 1fb8081

Please sign in to comment.