bbc/peaks.js

Name: peaks.js

Owner: BBC

Description: JavaScript UI component for interacting with audio waveforms

Created: 2013-10-14 12:53:33.0

Updated: 2018-01-16 19:41:51.0

Pushed: 2018-01-16 17:25:20.0

Homepage: http://waveform.prototyping.bbc.co.uk

Size: 94145

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Peaks.js Build Status

A browser based audio waveform visualisation frontend component from BBC R&D.

Peaks.js is a modular client-side JavaScript component designed for the display of and interaction with audio waveforms in the browser.

Peaks.js was developed by BBC R&D to allow users to make accurate clippings of audio content in the browser, using a backend API that serves the waveform data.

Peaks.js uses HTML5 canvas technology to display waveform at different zoom levels and provides some basic convenience methods for interacting with waveforms and creating time-based visual sections for denoting content to be clipped or for reference, eg: distinguishing music from speech or identifying different music tracks.

You can read more about the project here.

Installation

Using Peaks.js in your own project

Peaks.js can be included in any web page by following these steps:

  1. include it your web page
  2. include a media element and its waveform data file
  3. initialise Peaks.js
 id="peaks-container"></div>
io>
ource src="test_data/sample.mp3" type="audio/mpeg">
ource src="test_data/sample.ogg" type="audio/ogg">
dio>
ipt src="bower_components/requirejs/require.js" data-main="app.js"></script>
Start using AMD and require.js

AMD modules work out of the box without any optimiser.

n app.js
onfigure peaks path
irejs.config({
ths: {
peaks: 'bower_components/peaks.js/src/main',
EventEmitter: 'bower_components/eventemitter2/lib/eventemitter2',
Konva: 'bower_components/konvajs/konva',
'waveform-data': 'bower_components/waveform-data/dist/waveform-data.min'



equire it
ire(['peaks'], function(Peaks) {
r p = Peaks.init({
container: document.querySelector('#peaks-container'),
mediaElement: document.querySelector('audio'),
dataUri: 'test_data/sample.json'
;

on('peaks.ready', function() {
// do something when the waveform is displayed and ready
;

A working example is provided in index.html.

Start using ES2015 module loader

This works well with systems such as Meteor, webpack and browserify (with babelify transform).

rt Peaks from 'peaks.js';

t p = Peaks.init({ ? });
Start using CommonJS module loader

This works well with systems such as Meteor, webpack and browserify.

Peaks = require('peaks.js');

p = Peaks.init({ ? });
Start using vanilla JavaScript
ipt src="node_modules/peaks.js/peaks.js"></script>
ipt>
ction(Peaks) {
r p = Peaks.init({ ? });
eaks);
ript>
Generate waveform data

Peaks.js uses waveform data files produced by audiowaveform. These can be generated in either binary (.dat) or JSON format. Binary format is preferred because of the smaller file size, but this is only compatible with browsers that support Typed Arrays.

You should also use the -b 8 option when generating waveform data files, as Peaks.js does not currently support 16-bit waveform data files, and also to minimise file size.

To generate a binary waveform data file:

owaveform -i sample.mp3 -o sample.dat -b 8

To generate a JSON format waveform data file:

owaveform -i sample.mp3 -o sample.json -b 8

Refer to the man page audiowaveform(1) for full details of the available command line options.

Web Audio based waveforms

Since 0.3.0, Peaks.js can use the Web Audio API to generate waveforms, which means you would not have to pre-generate a dat or json file beforehand.

To do so, omit the dataUri option and make sure you pass in a valid AudioContext instance as the audioContext option. You may also want to make sure your browser is compatible with Web Audio.

myAudioContext = new AudioContext();

p = Peaks.init({
ntainer: document.querySelector('#peaks-container'),
diaElement: document.querySelector('audio'),
dioContext: myAudioContext


('peaks.ready', function() {
 do something when the waveform is displayed and ready

Notice: be aware it can be CPU intensive if your audio file has a long duration.

Configuration

The available options for configuration of the viewer are as follows:

options = {
* REQUIRED OPTIONS **/
 Containing element
ntainer: document.getElementById('peaks-container'),

 HTML5 Media element containing an audio track
diaElement: document.querySelector('audio'),

* Optional config with defaults **/
 URI to waveform data file in binary or JSON
taUri: {
arraybuffer: '../test_data/sample.dat',
json: '../test_data/sample.json',


 If true, peaks will send credentials with all network requests
 i.e. when fetching waveform data.
thCredentials: false,

 A Web Audio AudioContext instance which can be used
 to render the waveform if dataUri is not provided
dioContext: new AudioContext(),

 async logging function
gger: console.error.bind(console),

 default height of the waveform canvases in pixels
ight: 200,

 Array of zoom levels in samples per pixel (big >> small)
omLevels: [512, 1024, 2048, 4096],

 Bind keyboard controls
yboard: false,

 Keyboard nudge increment in seconds (left arrow/right arrow)
dgeIncrement: 0.01,

 Colour for the in marker of segments
MarkerColor: '#a0a0a0',

 Colour for the out marker of segments
tMarkerColor: '#a0a0a0',

 Colour for the zoomed in waveform
omWaveformColor: 'rgba(0, 225, 128, 1)',

 Colour for the overview waveform
erviewWaveformColor: 'rgba(0,0,0,0.2)',

 Colour for the overview waveform rectangle
 that shows what the zoom view shows
erviewHighlightRectangleColor: 'grey',

 Colour for segments on the waveform
gmentColor: 'rgba(255, 161, 39, 1)',

 Colour of the play head
ayheadColor: 'rgba(0, 0, 0, 1)',

 Colour of the play head text
ayheadTextColor: '#aaa',

 Show current time next to the play head
 (zoom view only)
owPlayheadTime: false,

 the color of a point marker
intMarkerColor: '#FF0000',

 Colour of the axis gridlines
isGridlineColor: '#ccc',

 Colour of the axis labels
isLabelColor: '#aaa',

 Random colour per segment (overrides segmentColor)
ndomizeSegmentColor: true,

 Zoom view adapter to use. Valid adapters are:
 'animated' (default) and 'static'
omAdapter: 'animated',

 Array of initial segment objects with startTime and
 endTime in seconds and a boolean for editable.
 See below.
gments: [{
startTime: 120,
endTime: 140,
editable: true,
color: "#ff0000",
labelText: "My label"


startTime: 220,
endTime: 240,
editable: false,
color: "#00ff00",
labelText: "My Second label"
,

 Array of initial point objects
ints: [{
time: 150,
editable: true,
color: "#00ff00",
labelText: "A point"


time: 160,
editable: true,
color: "#00ff00",
labelText: "Another point"


Advanced configuration

The marker and label Konva.js objects may be overridden to give the segment markers or label your own custom appearance (see main.js / waveform.mixins.js, Konva Polygon Example and Konva Text Example):


gmentInMarker: mixins.defaultInMarker(p.options),
gmentOutMarker: mixins.defaultOutMarker(p.options),
gmentLabelDraw: mixins.defaultSegmentLabelDraw(p.options)

API

Initialisation

The top level Peaks object exposes a factory function to create new Peaks instances.

Peaks.init(options)

Returns a new Peaks instance with the assigned options. You can create and manage several Peaks instances within a single page with one or several configurations.

peaksInstance = Peaks.init({ ? });
secondPeaksInstance = Peaks.init({ ? });
Player API
instance.player.play()

Starts media playback, from the current time position.

instance = Peaks.init({ ? });

ole.log(instance.player.play());
instance.player.pause()

Pauses media playback.

instance = Peaks.init({ ? });

ole.log(instance.player.pause());
instance.player.getCurrentTime()

Returns the current time from the associated media element, in seconds.

instance = Peaks.init({ ? });

ole.log(instance.player.getCurrentTime()); // -> 0
instance.player.seek(time)

Seeks the media element to the given time, in seconds.

instance = Peaks.init({ ? });

ance.player.seek(5.85);
ole.log(instance.player.getCurrentTime()); // -> 5.85
instance.player.playSegment(segment)

Plays a given segment of the media.

instance = Peaks.init({ ? });

segment = instance.segments.add({
artTime: 5.0,
dTime: 15.0,
itable: true


lays from 5.0 to 15.0, then stops.
ance.player.playSegment(segment);
Zoom API
instance.zoom.zoomOut()

Zooms in the waveform zoom view by one level.

instance = Peaks.init({ ?, zoomLevels: [512, 1024, 2048, 4096] });

ance.zoom.zoomOut(); // zoom level is now 1024
instance.zoom.zoomIn()

Zooms in the waveform zoom view by one level.

instance = Peaks.init({ ?, zoomLevels: [512, 1024, 2048, 4096] });

ance.zoom.zoomIn(); // zoom level is still 512

ance.zoom.zoomOut(); // zoom level is now 1024
ance.zoom.zoomIn(); // zoom level is now 512 again
instance.zoom.setZoom(index)

Sets the zoom level to the element in the options.zoomLevels array at index index.

instance = Peaks.init({ ?, zoomLevels: [512, 1024, 2048, 4096] });

ance.zoom.setZoom(3); // zoom level is now 4096
instance.zoom.getZoom()

Returns the current zoom level, as an index into the options.zoomLevels array.

instance = Peaks.init({ ?, zoomLevels: [512, 1024, 2048, 4096] });

ance.zoom.zoomOut();
ole.log(instance.zoom.getZoom()); // -> 1
Segments API

Segments give the ability to visually tag timed portions of the audio media. This is a great way to provide visual cues to your users.

instance.segments.add({startTime, endTime, editable, color, labelText, id})
instance.segments.add(segment[])

Adds a segment to the waveform timeline. Accepts the following parameters:

instance = Peaks.init({ ? });

dd non-editable segment, from 0 to 10.5 seconds, with a random color
ance.segments.add({startTime: 0, endTime: 10.5});

Alternatively, provide an array of segment objects to add all those segments at once.

instance = Peaks.init({ ? });

ance.segments.add([

startTime: 0,
endTime: 10.5,
labelText: '0 to 10.5 seconds non-editable demo segment'


startTime: 3.14,
endTime: 4.2,
color: '#666'


instance.segments.getSegments()

Returns an array of all segments present on the timeline.

instance.segments.getSegment(id)

Returns the segment with the given id, or null if not found.

instance.segments.removeByTime(startTime[, endTime])

Removes any segment which starts at startTime (seconds), and which optionally ends at endTime (seconds).

The return value indicates the number of deleted segments.

instance = Peaks.init({ ? });

ance.segments.add([{ startTime: 10, endTime: 12 }, { startTime: 10, endTime: 20 }]);

emove both segments as they start at `10`
ance.segments.removeByTime(10);

emove only the first segment
ance.segments.removeByTime(10, 12);
instance.segments.removeById(segmentId)

Removes segments with the given identifier.

instance = Peaks.init({ ? });

ance.segments.removeById('peaks.segment.3');
instance.segments.removeAll()

Removes all segments.

instance = Peaks.init({ ? });

ance.segments.removeAll();
Points API

Points give the ability to visually tag points in time of the audio media.

instance.points.add({time, editable, color, labelText, id})
instance.points.add(point[])

Adds one or more points to the waveform timeline. Accepts the following parameters:

instance = Peaks.init({ ? });

dd non-editable point, with a random color
ance.points.add({ time: 3.5 });

Alternatively, provide an array of point objects to add several at once.

instance = Peaks.init({ ? });

ance.points.add([

time: 3.5,
labelText: 'Test point',
color: '#666'


time: 5.6,
labelTect: 'Another test point',
color: '#666'


instance.points.getPoints()

Returns an array of all points present on the timeline.

instance.points.getPoint(id)

Returns the point with the given id, or null if not found.

instance.points.removeByTime(time)

Removes any point at the given time (seconds).

instance = Peaks.init({ ? });

ance.points.removeByTime(10);
instance.points.removeById(pointId)

Removes points with the given identifier.

instance = Peaks.init({ ? });

ance.points.removeById('peaks.point.3');
instance.points.removeAll()

Removes all points.

instance = Peaks.init({ ? });

ance.points.removeAll();
Destruction
instance.destroy()

Releases resources used by an instance. This can be useful when reinitialising Peaks.js within a single page application.

instance = Peaks.init({ ? });

ater:
ance.destroy();
Events

Peaks instances emit events to enable you to extend its behaviour according to your needs.

General

| Event name | Arguments | | ————————- | ————— | | error | Error err |

Media / User interactions

| Event name | Arguments | | —————————– | ————- | | peaks.ready | (none) | | segments.ready (deprecated) | (none) | | user_seek.overview | Number time | | user_seek.zoomview | Number time |

Waveforms

| Event name | Arguments | | ————————- | —————————————————– | | zoom.update | Number currentZoomLevel, Number previousZoomLevel |

Segments

| Event name | Arguments | | ————————- | ————————- | | segments.add | Array<Segment> segments | | segments.remove | Array<Segment> segments | | segments.remove_all | (none) | | segments.dragged | Segment segment |

Points

| Event name | Arguments | | ————————- | ——————— | | points.add | Array<Point> points | | points.remove | Array<Point> points | | points.remove_all | (none) | | points.dragged | Point point |

Building Peaks.js

You might want to build a minified standalone version of Peaks.js, to test a contribution or to run additional tests. The project bundles everything you need to do so.

Prerequisites
clone git@github.com:bbc/peaks.js.git
eaks.js
install
Building

This command will produce a UMD-compatible minified standalone version of Peaks.js, which allows you to use it with AMD or CommonJS module loaders, or even as vanilla JavaScript.

run build

The output of the build is a file named peaks.js, alongside its associated source map.

Live Demo

This command will serve a local demo page containing a single Peaks instance. Look at the file index.html to see an example of Peaks.js in use.

start

Then open http://localhost:9000 in a Web browser.

Testing

npm test should work for simple one time testing.

If you are developing and want to repeatedly run tests in a browser on your machine simply launch npm run test-watch.

License

See COPYING.

This project includes sample audio from the radio show Desert Island Discs, used under the terms of the Creative Commons 3.0 Unported License.

Credits

Copyright 2017 British Broadcasting Corporation


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.