npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

waveform-data

v4.5.0

Published

Audio Waveform Data Manipulation API – resample, offset and segment waveform data in JavaScript

Downloads

46,578

Readme

Build Status npm

waveform-data.js

waveform-data.js is a JavaScript library for creating zoomable representations of audio waveforms to enable visualisation of audio content.

waveform-data.js is part of a BBC R&D Browser-based audio waveform visualisation software family:

  • audiowaveform: C++ program that generates waveform data files from MP3 or WAV format audio.
  • audio_waveform-ruby: A Ruby gem that can read and write waveform data files.
  • waveform-data.js: JavaScript library that provides access to precomputed waveform data files, or can generate waveform data using the Web Audio API.
  • peaks.js: JavaScript UI component for interacting with waveforms.

We use these projects within the BBC in applications such as the BBC World Service Radio Archive and browser-based editing and sharing tools for BBC content editors.

Example of what it helps to build

Install

Use npm to install waveform-data.js, for both Node.js and browser-based applications:

npm install --save waveform-data

Usage and examples

waveform-data.js is available as a UMD module so it can be used from a <script> tag, or as a RequireJS or CommonJS module. See dist/waveform-data.js and dist/waveform-data.min.js.

Importing waveform-data.js

Using a script tag

Simply add waveform-data.js in a script tag in your HTML page:

<!DOCTYPE html>
<html>
  <body>
    <script src="/path/to/waveform-data.js"></script>
    <script>
      var waveform = new WaveformData(...);
    </script>
  </body>
</html>

Using ES6

An ES6 module build is provided for use with bundlers such as Webpack and Rollup. See dist/waveform-data.esm.js.

import WaveformData from 'waveform-data';

Using RequireJS

The UMD bundle can be used with RequireJS:

define(['WaveformData'], function(WaveformData) {
  // ...
});

Using CommonJS (Node.js)

A CommonJS build is provided for use with Node.js. See dist/waveform-data.cjs.js.

const WaveformData = require('waveform-data');

Receive binary waveform data

You can create and initialise a WaveformData object from waveform data in either binary or JSON format, using the Fetch API, as follows.

Binary format

Use audiowaveform to generate binary format waveform data, using a command such as:

audiowaveform -i track.mp3 -o track.dat -b 8 -z 256

Copy the waveform data file track.dat to your web server, then use the following code in your web application to request the waveform data:

fetch('https://example.com/waveforms/track.dat')
  .then(response => response.arrayBuffer())
  .then(buffer => WaveformData.create(buffer))
  .then(waveform => {
    console.log(`Waveform has ${waveform.channels} channels`);
    console.log(`Waveform has length ${waveform.length} points`);
  });

JSON format

Alternatively, audiowaveform can generate waveform data in JSON format:

audiowaveform -i track.mp3 -o track.json -b 8 -z 256

Use the following code to request the waveform data:

fetch('https://example.com/waveforms/track.json')
  .then(response => response.json())
  .then(json => WaveformData.create(json))
  .then(waveform => {
    console.log(`Waveform has ${waveform.channels} channels`);
    console.log(`Waveform has length ${waveform.length} points`);
  });

Using the Web Audio API

You can also create waveform data from audio in the browser, using the Web Audio API.

As input, you can either use an ArrayBuffer containing the original encoded audio (e.g., in MP3, Ogg Vorbis, or WAV format), or an AudioBuffer containing the decoded audio samples.

Note that this approach is generally less efficient than pre-processing the audio server-side, using audiowaveform.

Waveform data is created in two steps:

  • If you pass an ArrayBuffer containing encoded audio, the audio is decoded using the Web Audio API's decodeAudioData method. This must done on the browser's UI thread, so will be a blocking operation.

  • The decoded audio is processed to produce the waveform data. To avoid further blocking the browser's UI thread, by default this step is done using a Web Worker, if supported by the browser. You can disable the worker and run the processing in the main thread by setting disable_worker to true in the options.

const audioContext = new AudioContext();

fetch('https://example.com/audio/track.ogg')
  .then(response => response.arrayBuffer())
  .then(buffer => {
    const options = {
      audio_context: audioContext,
      array_buffer: buffer,
      scale: 128
    };

    return new Promise((resolve, reject) => {
      WaveformData.createFromAudio(options, (err, waveform) => {
        if (err) {
          reject(err);
        }
        else {
          resolve(waveform);
        }
      });
    });
  })
  .then(waveform => {
    console.log(`Waveform has ${waveform.channels} channels`);
    console.log(`Waveform has length ${waveform.length} points`);
  });

If you have an AudioBuffer containing decoded audio samples, e.g., from AudioContext.decodeAudioData then you can pass this directly to WaveformData.createFromAudio:

const audioContext = new AudioContext();

audioContext.decodeAudioData(arrayBuffer)
  .then((audioBuffer) => {
    const options = {
      audio_context: audioContext,
      audio_buffer: audioBuffer,
      scale: 128
    };

    return new Promise((resolve, reject) => {
      WaveformData.createFromAudio(options, (err, waveform) => {
        if (err) {
          reject(err);
        }
        else {
          resolve(waveform);
        }
      });
    });
  })
  .then(waveform => {
    console.log(`Waveform has ${waveform.channels} channels`);
    console.log(`Waveform has length ${waveform.length} points`);
  });

Drawing a waveform image

Once you've created a WaveformData object, you can use it to draw a waveform image, using the Canvas API or a visualization library such as D3.js.

Canvas example

const waveform = WaveformData.create(raw_data);

const scaleY = (amplitude, height) => {
  const range = 256;
  const offset = 128;

  return height - ((amplitude + offset) * height) / range;
}

const ctx = canvas.getContext('2d');
ctx.beginPath();

const channel = waveform.channel(0);

// Loop forwards, drawing the upper half of the waveform
for (let x = 0; x < waveform.length; x++) {
  const val = channel.max_sample(x);

  ctx.lineTo(x + 0.5, scaleY(val, canvas.height) + 0.5);
}

// Loop backwards, drawing the lower half of the waveform
for (let x = waveform.length - 1; x >= 0; x--) {
  const val = channel.min_sample(x);

  ctx.lineTo(x + 0.5, scaleY(val, canvas.height) + 0.5);
}

ctx.closePath();
ctx.stroke();
ctx.fill();

D3.js example

See demo/d3.html.

HTML

<div id="waveform-container"></div>

JavaScript

const waveform = WaveformData.create(raw_data);
const channel = waveform.channel(0);
const container = d3.select('#waveform-container');
const x = d3.scaleLinear();
const y = d3.scaleLinear();
const offsetX = 100;

const min = channel.min_array();
const max = channel.max_array();

x.domain([0, waveform.length]).rangeRound([0, 1000]);
y.domain([d3.min(min), d3.max(max)]).rangeRound([offsetX, -offsetX]);

const area = d3.svg.area()
  .x((d, i) => x(i))
  .y0((d, i) => y(min[i]))
  .y1((d, i) => y(d));

const graph = container.append('svg')
  .style('width', '1000px')
  .style('height', '200px')
  .datum(max)
  .append('path')
  .attr('transform', () => `translate(0, ${offsetX})`)
  .attr('d', area)
  .attr('stroke', 'black');

In Node.js

You can use waveform-data.js to consume or generate waveform data from a Node.js application, e.g., a web server.

const WaveformData = require('waveform-data');
const express = require('express');
const fs = require('fs');
const app = express();

app.get('/waveforms/:id.json', (req, res) => {
  res.set('Content-Type', 'application/json');

  fs.createReadStream(`path/to/${req.params.id}.json`)
    .pipe(res);
});

The following example shows a Node.js command-line application that requests waveform data from a web API and resamples it to a width of 2000 pixels.

#!/usr/bin/env node

// Save as: app/bin/cli-resampler.js

const WaveformData = require('waveform-data');
const request = require('superagent');
const args = require('yargs').argv;

request.get(`https://api.example.com/waveforms/${args.waveformid}.json`)
  .then(response => {
    const waveform = WaveformData.create(response.body);
    const resampledWaveform = waveform.resample({ width: 2000 });
    const channel = resampledWaveform.channel(0);

    process.stdout.write(JSON.stringify({
      min: channel.min_array(),
      max: channel.max_array()
    }));
});

Usage: ./app/bin/cli-resampler.js --waveformid=1337

Data format

The file format used and consumed by WaveformData is documented here as part of the audiowaveform project.

JavaScript API

Please refer here for full API documentation.

Browser support

Any browser supporting ECMAScript 5 will be enough to use the library - think Array.forEach:

  • IE9+, Firefox Stable, Chrome Stable, Safari 6+ are fully supported;
  • IE10+ is required for the TypedArray Adapter;
  • Firefox 23+ and Webkit/Blink browsers are required for Web Audio API support.

Development

To develop the code, install Node.js and npm. After obtaining the waveform-data.js source code, run npm install to install Node.js package dependencies.

Credits

This library was written by:

Thank you to all our contributors.

This program contains code adapted from Audacity, used with permission.

License

See LICENSE for details.

Contributing

Every contribution is welcomed, either it's code, idea or a merci!

Guidelines are provided and every commit is tested against unit tests using Karma runner and the Chai assertion library.

Copyright

Copyright 2024 British Broadcasting Corporation