UNPKG

@domchristie/needles

Version:

Audio loudness metering for the browser.

147 lines (122 loc) 4.36 kB
import { LoudnessMeter } from './dist/needles.js' ;(function () { var AudioContext = window.AudioContext || window.webkitAudioContext var OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext var audioContext var offlineAudioContext var fileReader = new FileReader() var audio = new Audio() var offlineMeters var liveMeters var bufferSource var elementSource var state var range = 85 var fileInput = document.querySelector('input[type="file"]') var playButton =document.getElementById('play') var pauseButton = document.getElementById('pause') var resetButton = document.getElementById('reset') var stateElement = document.getElementById('state') var momentaryNeedle = document.getElementById('momentary-needle') var momentaryValue = document.getElementById('momentary-value') var shortTermNeedle = document.getElementById('short-term-needle') var shortTermValue = document.getElementById('short-term-value') var integratedNeedle = document.getElementById('integrated-needle') var integratedValue = document.getElementById('integrated-value') fileInput.addEventListener('change', start) start() fileReader.onload = function (event) { setState('Loading') audioContext.decodeAudioData(event.target.result, audioDecoded) } function start () { audioContext = audioContext || new AudioContext() if (fileInput.files[0]) { fileReader.readAsArrayBuffer(fileInput.files[0]) audio.src = window.URL.createObjectURL(fileInput.files[0]) playButton.hidden = false if (!elementSource) { elementSource = audioContext.createMediaElementSource(audio) elementSource.connect(audioContext.destination) liveMeters = createMeters(elementSource) } } } function audioDecoded (buffer) { offlineAudioContext = new OfflineAudioContext( buffer.numberOfChannels, buffer.length, buffer.sampleRate ) bufferSource = offlineAudioContext.createBufferSource() bufferSource.buffer = buffer offlineMeters = createMeters(bufferSource, ['integrated']) setState('Measuring') offlineMeters.start() } playButton.addEventListener('click', function () { audio.play() }) pauseButton.addEventListener('click', function () { audio.pause() }) resetButton.addEventListener('click', function () { if (liveMeters) liveMeters.reset() if (offlineMeters) offlineMeters.reset() }) audio.addEventListener('play', function () { setState('Live') liveMeters.state === 'paused' ? liveMeters.resume() : liveMeters.start() playButton.hidden = true pauseButton.hidden = false }) audio.addEventListener('pause', function () { liveMeters.pause() pauseButton.hidden = true playButton.hidden = false }) audio.addEventListener('canplay', function () { playButton.hidden = false }, { once: true }) audio.addEventListener('ended', function () { liveMeters.reset() }) function createMeters (source, modes) { var loudnessMeter = new LoudnessMeter({ source: source, workerUri: 'dist/needles-worker.js', workletUri: 'dist/needles-worklet.js', modes: modes }) loudnessMeter.on('dataavailable', function (event) { if (state === 'Measuring') setState('') var map = { 'momentary': [momentaryNeedle, momentaryValue], 'short-term': [shortTermNeedle, shortTermValue], 'integrated': [integratedNeedle, integratedValue], } var translate = translateY(scale(event.data.value)) var [needle, value] = map[event.data.mode] needle.style.transform = 'translateY(' + translate + '%)' value.textContent = formattedValue(event.data.value) }) return loudnessMeter } function setState (newState) { state = newState stateElement.textContent = state } // Convert loudness to value between 0 and 1 function scale (loudness) { return (range + loudness) / range } // Translate scaled loudness to a CSS transform: translateY value function translateY (scaled) { if (!Number.isFinite(scaled)) return 100 return (1 - scaled) * 100 } function formattedValue (value) { if (value < -range) value = -Infinity return Number.isFinite(value) ? value.toFixed(1).toString() : '-Inf' } })()