Coder Social home page Coder Social logo

michaeldzjap / waveplayer Goto Github PK

View Code? Open in Web Editor NEW
79.0 7.0 12.0 123.31 MB

An HTML5 based audio player with a waveform view

License: MIT License

HTML 3.20% JavaScript 0.77% Shell 0.05% TypeScript 95.99%
audio waveform playback-audio audio-player javascript playlist html5 html5-canvas mp3 typescript

waveplayer's Introduction

NPM Version downloads GitHub Workflow Status Libraries.io dependency status for GitHub repo tested with jest codecov Quality Gate Status License

waveplayer

An HTML5 based audio player with a waveform view.

Screenshot

Author: Michael Dzjaparidze

License: MIT

Table of Contents

Requirements

In order to minimise waveform drawing times it is adviced to supply waveform amplitude data yourself using either the data or JSON strategies. There exist a number of tools for extracting waveform data in JSON format from an audio file; wav2json or py-wav2json can be used for instance. It is enough to provide a single array of floating point values. If the JSON contains a collection of name / value pairs only the first name / value pair will be used.

Installation

This package is available through npm:

npm install --save waveplayer

After that the package may be directly included using a <script> tag:

<script src="path-to-waveform-js/waveplayer.js"></script>

or may be imported in your own scripts files:

import { Player, Playlist, View } from 'waveplayer';

API

The constructor of each class takes an option object as an argument. See the Options section for more information on which options are available. Some options are required (currently only the container option).

Factory

A convenience class for building player or playlist instances without having to explicitly specify any dependencies yourself.

player = Factory.createPlayer(options);

Create a new player instance.

Argument Example Type Required Description
options { container: '#container' } Object Yes An object where each key / value pair represents a valid player or view option.
Returns

A fully initialised Player instance that can be used to load and play audio files.

playlist = Factory.createPlaylist(tracks, options);

Create a new playlist instance.

Argument Example Type Required Description
tracks [ { url: 'path-to-audio.mp3', strategy: { type: 'data', data: [0.1, -0.4, ...] } } ] Array<{ url: string; strategy: Strategy }> Yes An array of objects, where each object references an URL or path to an audio file and a strategy data object that instructs how to resolve the amplitude data associated with the audio file. See the Strategies section for more information on the available strategies and how to use them.
options { container: '#container' } Object Yes An object where each key / value pair represents a valid player or view option.
Returns

A fully initialised Playlist instance that can be used to load and play multiple audio files in succession.

Player

player = new Player(view, options);

Create a new player instance.

Argument Example Type Required Description
view new View([], { container: '#container' }) View Yes A view instance used for drawing the waveform associated with the audio file that will be loaded.
options { audioElement: '#audio' } Object No An object where each key / value pair represents a valid player option.
Returns

A fully initialised Player instance that can be used to load and play audio files.

const player = await player.load(url, strategy);

Load an audio track using a specific strategy.

Argument Example Type Required Description
url 'path-to-audio.mp3' string Yes A path or URL to an audio file
strategy { type: 'json', url: 'path-to-amplitude-data.json' } Strategy Yes A strategy data object that instructs how to resolve the amplitude data associated with the audio file.
Returns

A promise that resolves to the player instance on which the method was called.

const player = await player.play();

Start playback of the currently loaded audio file.

Returns

A promise that resolves to the player instance on which the method was called.

const player = player.pause();

Pause playback of the currently loaded audio file.

Returns

The player instance on which the method was called.

player.destroy();

Destroy the player instance and do the appropriate clean up. This will pause audio playback, remove all internally registered event handlers, remove the HTML audio element from the DOM if it was created by the player instance, and lastly call view.destroy().

player.volume = 0.5;

Get / set the volume of the currently playing audio file.

Argument Example Type
volume 0.5 number

player.currentTime = 1;

Get / set the current playback time in seconds.

Argument Example Type
currentTime 1 number

player.duration;

Get the duration of the currently playing audio file.

player.paused;

Get the flag that checks if audio playback is currently paused.

player.view;

Get the view instance associated with the player.

player.audioElement;

Get the HTML audio element associated with the player.

Options

Option Type Default Required Description
audioElement string | HTMLAudioElement undefined No The HTML audio element associated with the player instance. If not passed in as an option when creating a new player instance it will be created internally.
preload string metadata No The value of the preload attribute of the HTML audio element. Note: will only be used when the player instance creates the HTML audio element internally.

Playlist

playlist = new Playlist(player, tracks);

Create a new playlist instance.

Argument Example Type Required Description
player new Player(new View([], { container: '#container' })) Player Yes A player instance used for playing back all the tracks / audio files that make up the playlist.
tracks [ { url: 'path-to-audio.mp3', strategy: { type: 'data', data: [0.1, -0.4, ...] } } ] Array<{ url: string; strategy: Strategy }> Yes An array of objects, where each object references an URL or path to an audio file and a strategy data object that instructs how to resolve the amplitude data associated with the audio file. See the Strategies section for more information on the available strategies and how to use them.
Returns

A fully initialised Playlist instance that can be used to load and play multiple audio files in succession.

const playlist = await playlist.prepare();

Prepare a playlist for playback. This is an alias for playlist.reset().

Returns

A promise that resolves to the playlist instance on which the method was called.

const playlist = await playlist.reset();

Reset a playlist. This will pause playback and set the first track in the playlist as the current track to play.

Returns

A promise that resolves to the playlist instance on which the method was called.

const playlist = await playlist.next(forcePlay);

Skip to the next track in the playlist.

Argument Example Type Required Description
forcePlay true boolean No This will start playback of the next track regardless if the playlist is currently paused.
Returns

A promise that resolves to the playlist instance on which the method was called.

const playlist = await playlist.previous(forcePlay);

Skip to the previous track in the playlist.

Argument Example Type Required Description
forcePlay true boolean No This will start playback of the previous track regardless if the playlist is currently paused.
Returns

A promise that resolves to the playlist instance on which the method was called.

const playlist = await playlist.select(track, forcePlay);

Select a specific track in the playlist.

Argument Example Type Required Description
track 1 number Yes The index of the track in the playlist that should be selected.
forcePlay true boolean No This will start playback of the selected track regardless if the playlist is currently paused.
Returns

A promise that resolves to the playlist instance on which the method was called.

playlist.destroy();

Destroy the player instance and do the appropriate clean up. This will remove all internally registered event handlers and call player.destroy().

playlist.forcePlay = true;

Get / set the flag that indicates whether playback should start after selecting another track in the playlist, regardless if the playlist is paused or not.

Argument Example Type
forcePlay true boolean

playlist.player;

Get the player instance associated with the playlist.

playlist.current;

Get the index of the currently playing track.

playlist.ended;

Get the flag that indicates whether the playlist has finished playback.

Options

Option Type Default Required Description
forcePlay boolean true No Indicates whether playback should start after selecting another track in the playlist, regardless if the playlist is paused or not.

View

view = new View(data, options);

Create a new view instance.

Argument Example Type Required Description
data [-0.1, 0.4, ...] Array<number> Yes An array of floating point values representing the amplitude of some audio file at equally spaced intervals that will be used to draw the waveform.
options { container: '#container' } Object Yes An object where each key / value pair represents a valid view option.
Returns

A fully initialised View instance that can be used to draw the waveform associated with an audio file.

view = view.draw();

Draw the waveform on the canvas HTML element.

Returns

The view instance on which the method was called.

view = view.clear();

Clear the canvas HTML element where the waveform is drawn on.

Returns

The view instance on which the method was called.

view.destroy();

Destroy the view instance and do the appropriate clean up. This will remove all internally registered event handlers and remove the HTML canvas element from the DOM.

view.data = [-0.1, 0.4, ...];

Get / set the waveform amplitude data.

Argument Example Type
data [-0.1, 0.4, ...] Array<number>

view.progress = 0.5;

Get / set the progress of the waveform, assumed to be in the range [0-1].

Argument Example Type
progress 0.5 number

view.container = '#container';

Get / set the HTML container element for the view instance.

Argument Example Type
container '#container' `HTMLDivElement

view.width = 512;

Get / set the width of the drawn waveform. Setting the width only has an effect if the view instance is not operating in responsive mode.

Argument Example Type
width 512 number

view.height = 128;

Get / set the height of the drawn waveform.

Argument Example Type
height 128 number

view.barWidth = 4;

Get / set the width of a bar representing an element of the waveform.

Argument Example Type
barWidth 4 number

view.barGap = 1;

Get / set the width of the gap that separates consecutive bars.

Argument Example Type
barGap 1 number

view.responsive = true;

Get / set the flag that determines if the view instance is operating in responsive mode.

Argument Example Type
responsive true boolean

view.gradient = true;

Get / set the flag that determines if the waveform should be drawn with a gradient.

Argument Example Type
gradient true boolean

view.interact = true;

Get / set the interaction state of the view instance.

Argument Example Type
interact true boolean

view.redraw = true;

Get / set the redraw flag. This flag determines whether the waveform should be redrawn when setting one of the view properties that affects the look of the waveform (e.g. width, height, gradient).

Argument Example Type
redraw true boolean

view.canvas;

Get the HTML canvas element that is used for drawing the waveform.

Options

Option Type Default Required Description
container string | HTMLDivElement undefined Yes CSS selector or div HTML element that acts as the container for the HTML canvas element on which the waveform will be drawn.
width integer 512 No The width of the waveform in pixels (only relevant when the responsive option is set to false).
height integer 128 No The height of the waveform in pixels.
waveformColor string #428bca No The fill color of the waveform bars that have not been played back so far.
progressColor string #31708f No The fill color of the waveform bars that have been played back so far.
barGap integer 1 No Gap between bars in pixels.
barWidth integer 4 No Width of a bar in pixels.
responsive boolean true No If set to true, the width of the waveform view adapts to the width of the container element.
gradient boolean true No Indicates if the waveform should be drawn with a gradient or not.
interact boolean true No Enables / disables mouse interaction with the waveform view. This may be changed at any point after creation.
redraw boolean true No Indicates if the waveform will be redrawn when one of the view properties that affects the look of the waveform (e.g. width, height, gradient) is set.

Strategies

waveplayer v2 introduces the concept of strategies for providing amplitude data for an audio file that is used to draw waveforms. Each audio file you wish to load should reference a path / URL to the file and in addition specify how the amplitude data should be resolved. Currently, three strategies are available:

Data Strategy

Use this strategy if your waveform amplitude data is readily available in the form of an array of floating point values, assumed to be in the range [-1, 1].

Key Value Type Default Required Description
type 'data' string undefined Yes The strategy type identifier.
data [0.1, -0.4, ...] Array<number> undefined Yes An array of floating point values representing the amplitude of some audio file at equally spaced intervals that will be used to draw the waveform.

JSON Strategy

Use this strategy if your waveform amplitude data is stored inside a JSON file. This JSON file should either consist of a single array structure containing floating point values, assumed to be in the range [-1, 1] or key / value pairs where the value point to such an array. Currently, only the first key / value pair is used for the amplitude data. Waveform amplitude data extracted from a JSON file is cached by default

Key Value Type Default Required Description
type 'json' string undefined Yes The strategy type identifier.
url 'path-to-amplitude-data.json' string undefined Yes A path or URL to a JSON file containing amplitude data for an audio file.
cache true boolean true No Determines whether to use cached amplitude data (if it exists) or to extract the data from the JSON file.

WebAudio Strategy

Although convenient, this strategy is a bit experimental and if you can use either the JSON or data strategies it is generally adviced to use one of these. Use this strategy if you would like to extract the amplitude data of an audio file during runtime. Note that this could take a considerable amount of time depending on the duration of the audio file and the number of points you would like to extract. Also, not all audio file formats are supported, although MP3 and WAV should work just fine.

Key Value Type Default Required Description
type 'webAudio' string undefined Yes The strategy type identifier.
points 1200 number 800 No The number of equally spaced amplitude data points to extract from the audio file.
normalise false boolean true No Determines whether to normalise the extracted data points (i.e. scale them such that the absolute maximum is 1).
logarithmic true boolean true No Determines whether to compute the extracted data points on a logarithmic or linear scale.
cache false boolean true No Determines whether to use cached amplitude data (if it exists) or to extract the data from the audio file.

Note: The JSON and WebAudio strategies cache amplitude data by default in order to speed up subsequent loading of the same audio files. If this is undesired behaviour you should set the "cache" flag of the relevant strategy to false.

Examples

This section discusses a few simple examples for more worked out, fully working examples see the /examples directory.

Player

Create a player instance and pass in some (optional) options:

import { Factory } from 'waveplayer';

const player = Factory.createPlayer({
    container: '#waveform',
    barWidth: 4,
    barGap: 1,
    height: 128,
});

await player.load('url-to-some-audio-file.mp3', { type: 'data', data: [0.1, -0.4, ...] });

player.play();

If you need more control or don't like factory classes for some reason you can also create a player instance explicitly:

import { Player, View } from 'waveplayer';

const player = new Player(new View([], { 'container': '#container' }), { 'audioElement': '#audio' });

Playlist

Load some audio files from a URL and start playback when loading has finished:

import { Factory } from 'waveplayer';

playlist = await Factory.createPlaylist(
    [{ url: 'url-to-some-audio-file.mp3', strategy: { type: 'data', data: [0.1, -0.4, ...] } }],
    { container: '#waveform' },
).prepare();

playlist.play();

Similarly to the Player class you can also create a playlist instance explicitly:

import { Player, Playlist, View } from 'waveplayer';

const playlist = new Playlist(
    new Player(new View([], { container: '#waveform' })),
    [{ url: 'url-to-some-audio-file.mp3', strategy: { type: 'json', url: 'url-to-some-amplitude-data.json' } }],
);

View

It is also possible to only use the view part of waveplayer if you are interested in the drawing of waveform data only:

import { View } from 'waveplayer';

const view = new View([0.1, -0.4, ...], { 'container': '#container' });

view.progress = 0.5;

view.draw();

waveplayer's People

Contributors

axis80 avatar michaeldzjap avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

waveplayer's Issues

Method for passing in JSON peaks on render

Again similar to wavesurfer, it would be a great idea to allow the option of passing in the json peak data onRender instead, keeping the current method of looking in the audio file dir for the .json file as the default fallback method.

In my use case the peak data is stored in a MongoDB collection so being able to define it onRender or load would be very helpful , eg :

var wavePlayer = new WavePlayer({
    container: '#waveform',
    barWidth: 4,
    barGap: 1,
    height: 128
    options: {
       peaks: myPeakData
    }
});

Or even on a new load method :

wavePlayer.loadPeaks(myPeakData);

Disable scrubbing for radio streams

WavePlayer works just as great with live radio mp3 streams, simply by providing the url to the stream.

It would be great if there was someway of disabling the scrubbing for these situations as it's not really relevant. Would also be great to be able to disable the waveform display for live streams too. Even better would be the ability to specify a separate canvas element or even a jpg / png file to replace the waveform.

I know this could be done on a track by track basis by simply hiding the waveform container div, but if there was a way to specify all of this on load it would be awesome!

I know it's a big ask, but this is such a slick and compact bit of software that is just going to get better and better so I thought I'd throw some gauntlets down early :)

Keep up the great work Michael, wavePlayer is superb!

Refactor project

The code base essentially has not been changed since it was first developed. Ideally, it could use a thorough refactoring round, as in its current state it is also rather hard to write a good test suite for it.

If refactoring is going to happen anyway, the project might as well be re-written in TypeScript.

Current position / time display?

Love what you've done here!

Can't seem to find the methods for getting / displaying the current time and duration though.

In wavesurfer they are :

getCurrentTime()
getDuration()

Have you implemented either of these (with different names - tried these - they don't work) or something else that just isn't mentioned in the docs?

Kinda need time display and position tracking for any audio player.

How to render multiple waveforms in one page correctly?

Hello Michael.

I'm trying to place multiple waveforms in a single page like soundcloud.

But everytime I create a waveform it will also create a new waveplayer instance on page which means it will create multiple audio tag elements in page.I have 100+ instance in a single page, and it will increase RAM usage soon.
And use playlist cannot display multiple waveform views in a single page.
I'm wondering how to use a single audio tag(a global context) to make it display multiple waveform view. (to avoid CPU and RAM over usage).

THX!

Load waveform but defer loading of audio file until Play is clicked

Is it possible to render the waveform from JSON file or data object, but defer loading of the actual MP3 audio file until you're actually ready to play the file?

My use case is that I have an infinite-scrolling page of players, each with a play button and waveform. I obviously don't want to abuse my users' bandwidth by loading each and every audio file they scroll past. I only want to actually load the MP3s that they play. Buffer time is not a concern as my files are voice, not music.

I suppose I could render the waveforms into a div myself, then replace that with the player once play is clicked. I worry that would cause flickering, or that I wouldn't be able to exactly replicate the look of your waveform.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.