regosen / Gapless-5

Gapless JavaScript audio player using HTML5 and WebAudio
MIT License
142 stars 22 forks source link
audio-player crossfade javascript loop seamless shuffle ui web webaudio

Gapless 5  

A gapless JavaScript audio player using HTML5 and WebAudio.

PROBLEM: There are 2 modern APIs for playing audio through the web, and both of them have problems:

SOLUTION: Use both!

1. Demos

The following sites utilize Gapless 5. If you'd like to be featured here, please contact the repo owner or start a new issue!

2. Features

2.1. Browser Support

NOTE for Boostrap users: Bootstrap's CSS will mess up the optional built-in UI. If you don't need Bootstrap in its entirety, try using Twitter customize to get just the subset of rules you need.

3. Installation

3.1. Using npm

  1. Install the npm package:

    $ npm install @regosen/gapless-5
  2. Import Gapless5 from the module:

    const { Gapless5 } = require('@regosen/gapless-5');

3.2. Using direct HTML

A. If not using built-in UI, just add and reference Gapless5.js from your HTML head.

<script src="https://github.com/regosen/Gapless-5/raw/main/gapless5.js" language="JavaScript" type="text/javascript"></script>

B. If using the built-in UI, add and reference Gapless5.js and Gapless5.css. Also, create a <div> or <span> element where you want the player to appear. Give it a particular id.

<link href="https://github.com/regosen/Gapless-5/blob/main/gapless5.css" rel="stylesheet" type="text/css" />
<script src="https://github.com/regosen/Gapless-5/raw/main/gapless5.js" language="JavaScript" type="text/javascript"></script>

<!-- then in body: -->
<div id="gapless5-player-id" />

4. Usage

  1. Create a Gapless5 object with an optional parameter object
    • If you want the built-in UI, pass in the element ID as guiId under options.
  2. Add tracks via options in constructor or addTrack()
  3. Optional stuff:
    • Add a cross-fade between tracks using crossfade under options.
      • TIP: try setting this between 25 and 50 ms if you still hear gaps between your tracks. Gap sizes depend on the audio format, browser, etc.
    • Manipulate tracklist with insertTrack(), removeTrack(), and more.
    • Register your own callbacks.
    • Connect key presses to actions using mapKeys() or options in constructor.
const player = new Gapless5({ guiId: 'gapless5-player-id' });

// You can add tracks by relative or absolute URL:
player.addTrack('audio/song1.mp3');
player.addTrack('https://my-audio-site.org/song2.m4a');

// You can also let the user upload tracks from a file loader like this:
const files = document.getElementById('my-file-input').files;
files.forEach(file => {
  player.addTrack(URL.createObjectURL(file)); // this creates a 'blob://' URL
});
player.play();

If you want the user to upload tracks from a file loader, here's an example of that:

<form>
  <input type="file" id="my-file-input" accept="audio/*">
</form>

4.1. Options

These can be passed into a Gapless5 constructor, or (with the exception of tracks and guiId) set later on the object.

Example:

const player = new Gapless5({
  tracks: ['loop1.mp3', 'loop2.mp3'],
  loop: true,
  loadLimit: 2,
  mapKeys: {prev: 'a', playpause: 's', stop: 'd', next: 'f'},
});

NOTE 1: if you need audio to keep playing on iOS with Safari in the background, set useWebAudio to false.

NOTE 2: if you use loadLimit with useWebAudio set to false, Safari on iOS may fail to play subsequent tracks. This is due to user interaction requirements, and JS console will show a warning if this happens.

4.2. Functions

You can call these functions on Gapless5 objects.

4.2.1. Parameterized Functions

4.2.2. Accessors

4.2.3. Actions

All actions can be mapped to keys via mapKeys.

These correspond to built-in UI buttons

These do not correspond to built-in UI buttons

Examples:

player.mapKeys({cue: '7', stop: '8', next: '9'});

player.play();
player.pause();

// indexes start at 0
player.replaceTrack(0, 'audio/song1_alt.flac');
player.insertTrack(1, 'audio/transition.wav');

player.gotoTrack(1);
player.gotoTrack('audio/song1_alt.flac'); // can also goto track by path

player.removeTrack(2);
player.removeTrack('audio/transition.wav'); // can also remove track by path
player.removeAllTracks();

4.3. Callbacks

You can set these on a Gapless5 object. All callbacks include the affected track's audio path except where indicated.

// audio position has changed
ontimeupdate = (current_track_time: number, current_track_index: number) => void

// play requested by user
onplayrequest = (track_path: string) => void

// play actually starts
onplay = (track_path: string) => void 

// play is paused
onpause = (track_path: string) => void

// play is stopped
onstop = (track_path: string) => void

// prev track, where:
//   from_track = track that we're switching from
//   to_track = track that we're switching to
onprev = (from_track: string, to_track: string) => void

// next track, where:
//   from_track = track that we're switching from
//   to_track = track that we're switching to
onnext = (from_track: string, to_track: string) => void

// loading started
onloadstart = (track_path: string) => void 

// loading completed
//   fully_loaded = true for WebAudio data, false for HTML5 Audio data
//   NOTE: this triggers twice per track when both WebAudio and HTML5 are enabled
onload = (track_path: string, fully_loaded: boolean) => void

// track unloaded (to save memory)
onunload = (track_path: string) => void

// track failed to load or play
onerror = (track_path: string, error?: Error | string) => void

// track finished playing
onfinishedtrack = (track_path: string) => void

// entire playlist finished playing
onfinishedall = () => void

Example:

function nextCallback(from_track, to_track) {
  console.log(`User skipped to next track (from ${from_track} to ${to_track})`);
}

const player = new Gapless5({guiId: 'gapless5-player-id', tracks: ['track1.mp3', 'track2.mp3']});
player.onnext = nextCallback;
player.onplay = function (track_path) { console.log(`Now playing ${track_path}`); };

4.4. GUI Customization

While Gapless provides its own GUI, you can also customize it in CSS, or even create your own spans of text controlled by Gapless5.

Example: in CSS, hide the built-in gapless5 text:

  #g5positionbar-MyID {
    display: none;
  }

and then create your own elements to be controlled by Gapless5:

<p>
  Now Playing: <span id="#g5trackname-MyID"><span> 
  (<span id="#g5position-MyID"><span>)
</p>

See an example of customized player text here.

5. License

Licensed under the MIT License.