// Create an instance var wavesurfer; // Init & load audio file document.addEventListener('DOMContentLoaded', function() { // Init wavesurfer = WaveSurfer.create({ container: document.querySelector('#waveform'), height: 100, pixelRatio: 1, minPxPerSec: 100, scrollParent: true, normalize: true, splitChannels: false, backend: 'MediaElement', plugins: [ WaveSurfer.regions.create(), WaveSurfer.minimap.create({ height: 30, waveColor: '#ddd', progressColor: '#999' }), WaveSurfer.timeline.create({ container: '#wave-timeline' }), WaveSurfer.cursor.create() ] }); // Load audio from existing media element let mediaElt = document.querySelector('video'); wavesurfer.on('error', function(e) { console.warn(e); }); wavesurfer.load(mediaElt); wavesurfer.on('ready', function() { wavesurfer.enableDragSelection({ color: randomColor(0.25) }); wavesurfer.util .fetchFile({ responseType: 'json', url: '../media/nasa.json' }) .on('success', function(data) { loadRegions(data); saveRegions(); }); }); wavesurfer.on('region-click', function(region, e) { e.stopPropagation(); // Play on click, loop on shift click e.shiftKey ? region.playLoop() : region.play(); }); wavesurfer.on('region-click', editAnnotation); wavesurfer.on('region-update-end', saveRegions); wavesurfer.on('region-updated', saveRegions); wavesurfer.on('region-removed', saveRegions); wavesurfer.on('region-in', showNote); wavesurfer.on('region-out', hideNote); wavesurfer.on('region-play', function(region) { region.once('out', function() { wavesurfer.play(region.start); wavesurfer.pause(); }); }); /* Toggle play/pause buttons. */ let playButton = document.querySelector('#play'); let pauseButton = document.querySelector('#pause'); wavesurfer.on('play', function() { playButton.style.display = 'none'; pauseButton.style.display = 'block'; }); wavesurfer.on('pause', function() { playButton.style.display = 'block'; pauseButton.style.display = 'none'; }); }); /** * Save annotations to localStorage. */ function saveRegions() { localStorage.regions = JSON.stringify( Object.keys(wavesurfer.regions.list).map(function(id) { let region = wavesurfer.regions.list[id]; return { start: region.start, end: region.end, attributes: region.attributes, data: region.data }; }) ); } /** * Load regions from localStorage. */ function loadRegions(regions) { regions.forEach(function(region) { region.color = randomColor(0.25); wavesurfer.addRegion(region); }); } /** * Extract regions separated by silence. */ function extractRegions(peaks, duration) { // Silence params let minValue = 0.0015; let minSeconds = 0.25; let length = peaks.length; let coef = duration / length; let minLen = minSeconds / coef; // Gather silence indeces let silences = []; Array.prototype.forEach.call(peaks, function(val, index) { if (Math.abs(val) <= minValue) { silences.push(index); } }); // Cluster silence values let clusters = []; silences.forEach(function(val, index) { if (clusters.length && val == silences[index - 1] + 1) { clusters[clusters.length - 1].push(val); } else { clusters.push([val]); } }); // Filter silence clusters by minimum length let fClusters = clusters.filter(function(cluster) { return cluster.length >= minLen; }); // Create regions on the edges of silences let regions = fClusters.map(function(cluster, index) { let next = fClusters[index + 1]; return { start: cluster[cluster.length - 1], end: next ? next[0] : length - 1 }; }); // Add an initial region if the audio doesn't start with silence let firstCluster = fClusters[0]; if (firstCluster && firstCluster[0] != 0) { regions.unshift({ start: 0, end: firstCluster[firstCluster.length - 1] }); } // Filter regions by minimum length let fRegions = regions.filter(function(reg) { return reg.end - reg.start >= minLen; }); // Return time-based regions return fRegions.map(function(reg) { return { start: Math.round(reg.start * coef * 100) / 100, end: Math.round(reg.end * coef * 100) / 100 }; }); } /** * Random RGBA color. */ function randomColor(alpha) { return ( 'rgba(' + [ ~~(Math.random() * 255), ~~(Math.random() * 255), ~~(Math.random() * 255), alpha || 1 ] + ')' ); } /** * Edit annotation for a region. */ function editAnnotation(region) { let form = document.forms.edit; form.style.opacity = 1; (form.elements.start.value = Math.round(region.start * 100) / 100), (form.elements.end.value = Math.round(region.end * 100) / 100); form.elements.note.value = region.data.note || ''; form.onsubmit = function(e) { e.preventDefault(); region.update({ start: form.elements.start.value, end: form.elements.end.value, data: { note: form.elements.note.value } }); form.style.opacity = 0; }; form.onreset = function() { form.style.opacity = 0; form.dataset.region = null; }; form.dataset.region = region.id; } /** * Display annotation. */ function showNote(region) { if (!showNote.el) { showNote.el = document.querySelector('#subtitle'); } showNote.el.style.color = 'Red'; showNote.el.style.fontSize = 'large'; showNote.el.textContent = region.data.note || '–'; } function hideNote(region) { if (!hideNote.el) { hideNote.el = document.querySelector('#subtitle'); } hideNote.el.style.color = 'Red'; hideNote.el.style.fontSize = 'large'; hideNote.el.textContent = '–'; } /** * Bind controls. */ window.GLOBAL_ACTIONS['delete-region'] = function() { let form = document.forms.edit; let regionId = form.dataset.region; if (regionId) { wavesurfer.regions.list[regionId].remove(); form.reset(); } }; window.GLOBAL_ACTIONS['export'] = function() { window.open( 'data:application/json;charset=utf-8,' + encodeURIComponent(localStorage.regions) ); };