UNPKG

essentia.js

Version:

JavaScript library for music/audio analysis and processing, powered by Essentia WebAssembly

351 lines (341 loc) 14.8 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.EssentiaPlot = {})); }(this, (function (exports) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } /** * @license * Copyright (C) 2006-2020 Music Technology Group - Universitat Pompeu Fabra * * This file is part of Essentia * * Essentia is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation (FSF), either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the Affero GNU General Public License * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ // default plot config for EssentiaPlot base class var PlotConfig = { isPlotting: false, startTimeIndex: 0 }; // default layout settings for melody contour plots var LayoutMelodyContourPlot = { title: "Melody Contour", plot_bgcolor: "transparent", paper_bgcolor: "#FCF7F7", autosize: false, width: 670, height: 300, xaxis: { type: "time", title: "Time" }, yaxis: { autorange: false, range: [30, 3000], type: "linear", title: "Frequency (Hz)" } }; // default layout settings for chroma heatmap plot var LayoutChromaPlot = { title: "", plot_bgcolor: "transparent", paper_bgcolor: "#FCF7F7", autosize: false, width: 670, height: 300, xaxis: { autorange: true, time: 'Time', title: 'Time' }, yaxis: { title: 'Pitch class', range: [0, 11] }, }; // default layout settings for spectrogram heatmap plot var LayoutSpectrogramPlot = { title: "", plot_bgcolor: "transparent", paper_bgcolor: "#FCF7F7", autosize: false, width: 670, height: 300, xaxis: { title: 'Time', autorange: true, time: 'Time', }, yaxis: { title: 'Bands', range: null, type: 'linear', }, }; /** * Base class for essentia.js-plot* * @class */ var EssentiaPlot = /** @class */ (function () { /** *Creates an instance of EssentiaPlot. * @param {*} Plotly plotly.js global import object (see https://plotly.com/javascript/) * @param {*} [options=CONFIG] config options for the plot * @constructs */ function EssentiaPlot(Plotly, options) { if (options === void 0) { options = PlotConfig; } this.Plotly = Plotly; this.options = options; this.isPlotting = options.isPlotting; this.startTimeIndex = options.startTimeIndex; } /** * Returns evenly spaced samples, calculated over the interval [start, stop]. * @param {*} start The starting value of the sequence. * @param {*} stop The end value of the sequence * @param {*} num Number of samples to generate. Must be non-negative. * @returns {Array} * @memberof EssentiaPlot */ EssentiaPlot.prototype.makeLinearSpace = function (start, stop, num) { if (typeof num === "undefined") num = Math.max(Math.round(stop - start) + 1, 1); if (num < 2) { return num === 1 ? [start] : []; } var i, ret = Array(num); num--; for (i = num; i >= 0; i--) { ret[i] = (i * stop + (num - i) * start) / num; } return ret; }; return EssentiaPlot; }()); /** * @class PlotMelodyContour * @extends {EssentiaPlot} */ var PlotMelodyContour = /** @class */ (function (_super) { __extends(PlotMelodyContour, _super); /** * Creates an instance of PlotMelodyContour * @param {*} Plotly plotly.js global object import (see https://plotly.com/javascript/) * @param {string} divId HTML div container id * @param {*} [plotLayout=LayoutMelodyContour] * @constructs */ function PlotMelodyContour(Plotly, divId, plotLayout) { if (plotLayout === void 0) { plotLayout = LayoutMelodyContourPlot; } var _this = _super.call(this, Plotly) || this; _this.Plotly = Plotly; _this.divId = divId; _this.plotLayout = plotLayout; return _this; } /** * Create the single line plot with the given input array using Plotly.js * @method * @param {Float32Array} featureArray 1D feature input array * @param {string} plotTitle title of the plot * @param {number} audioFrameSize length of input audio data in samples * @param {number} audioSampleRate sample rate of input audio * @memberof PlotMelodyContour */ PlotMelodyContour.prototype.create = function (featureArray, plotTitle, audioFrameSize, audioSampleRate) { this.plotLayout.title = plotTitle; // time axis var timeAxis = this.makeLinearSpace(this.startTimeIndex, audioFrameSize / audioSampleRate, featureArray.length); // Create a plotly plot instance if a plot hasn't been created before if (!this.isPlotting) { this.Plotly.newPlot(this.divId, [{ x: timeAxis, y: featureArray, mode: 'lines', line: { color: '#2B6FAC', width: 2 } }], this.plotLayout); this.isPlotting = true; this.startTimeIndex = timeAxis[timeAxis.length - 1]; } else { timeAxis = this.makeLinearSpace(this.startTimeIndex, this.startTimeIndex + (audioFrameSize / audioSampleRate), featureArray.length); this.startTimeIndex = timeAxis[timeAxis.length - 1]; this.Plotly.extendTraces(this.divId, { x: [timeAxis], y: [featureArray], }, [0]); } }; /** * Destroy the existing Plotly traces * @method * @memberof PlotMelodyContour */ PlotMelodyContour.prototype.destroy = function () { this.Plotly.deleteTraces(this.divId, 0); this.isPlotting = false; this.startTimeIndex = 0; }; return PlotMelodyContour; }(EssentiaPlot)); /** * @class PlotHeatmap * @extends {EssentiaPlot} */ var PlotHeatmap = /** @class */ (function (_super) { __extends(PlotHeatmap, _super); /** *Creates an instance of PlotHeatmap * @param {*} Plotly plotly.js global object import (see https://plotly.com/javascript/) * @param {string} divId HTML div container id * @param {string} [plotType='chroma'] type of plot to configure the y-axis * @param {*} [plotLayout=LayoutSpectrogramPlot] * @constructs */ function PlotHeatmap(Plotly, divId, plotType, plotLayout) { if (plotType === void 0) { plotType = "chroma"; } if (plotLayout === void 0) { plotLayout = LayoutSpectrogramPlot; } var _this = _super.call(this, Plotly) || this; _this.Plotly = Plotly; _this.divId = divId; _this.plotType = plotType; _this.plotLayout = plotLayout; if (plotType === "chroma") { // we set chroma bin labels as yAxis for the heatmap _this.yAxis = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; } else if (plotType === "spectrogram") { _this.yAxis = null; } else { throw "Invalid value for argument 'plotType'. Should be either 'chroma' or 'spectrogram'"; } return _this; } /** * Create Plotly.js heatmap plot with given input array and type * @param {Array} featureArray 2D feature array where 'x' axis denotes temporal evolution of features * @param {string} plotTitle title of the plot * @param {*} audioFrameSize length of input audio data in samples * @param {*} audioSampleRate sample rate of input audio * @param {*} [hopSize=0] hopSize used for the feture extraction if applies. * @param {string} [colorscale='Jet'] * @memberof PlotHeatmap */ PlotHeatmap.prototype.create = function (featureArray, plotTitle, audioFrameSize, audioSampleRate, hopSize, colorscale) { if (colorscale === void 0) { colorscale = 'Jet'; } this.plotLayout.title = plotTitle; if (this.plotType === "spectrogram") { var numBands = featureArray[0].length; this.plotLayout.yaxis.range = [0, numBands + 1]; } if (!this.isPlotting) { var heatmapFeature = void 0; var timeAxis = void 0; if ((featureArray[0].constructor === Array) || (featureArray[0].constructor === Float32Array)) { if (featureArray.length == 1) { heatmapFeature = featureArray; timeAxis = [this.startTimeIndex + hopSize / audioSampleRate, this.startTimeIndex + audioFrameSize / audioSampleRate]; } else { heatmapFeature = featureArray; timeAxis = this.makeLinearSpace(this.startTimeIndex, audioFrameSize / audioSampleRate, heatmapFeature.length); } } else { throw "Got 1D array as input, expect a 2D array..."; } var data = { x: timeAxis, y: this.yAxis, z: heatmapFeature, colorscale: colorscale, type: 'heatmap', transpose: true, }; this.Plotly.newPlot(this.divId, [data], this.plotLayout); this.isPlotting = true; this.startTimeIndex = timeAxis[timeAxis.length - 1]; } else { // realtime mode var heatmapFeature = void 0; var timeAxis = void 0; if ((featureArray[0].constructor === Array) || (featureArray[0].constructor === Float32Array)) { if (featureArray.length == 1) { heatmapFeature = featureArray; timeAxis = [this.startTimeIndex + hopSize / audioSampleRate, this.startTimeIndex + audioFrameSize / audioSampleRate]; } else { heatmapFeature = featureArray; timeAxis = this.makeLinearSpace(this.startTimeIndex, audioFrameSize / audioSampleRate, heatmapFeature.length); } } else { throw "Got 1D array as input, expect a 2D array..."; } this.startTimeIndex = timeAxis[timeAxis.length - 1]; // realtime mode this.Plotly.extendTraces(this.divId, { x: [timeAxis], z: [featureArray], }, [0]); } }; /** * Destroy the existing Plotly plot traces * @method * @memberof PlotHeatmap */ PlotHeatmap.prototype.destroy = function () { this.Plotly.deleteTraces(this.divId, 0); this.isPlotting = false; this.startTimeIndex = 0; }; return PlotHeatmap; }(EssentiaPlot)); exports.EssentiaPlot = EssentiaPlot; exports.LayoutChromaPlot = LayoutChromaPlot; exports.LayoutMelodyContourPlot = LayoutMelodyContourPlot; exports.LayoutSpectrogramPlot = LayoutSpectrogramPlot; exports.PlotConfig = PlotConfig; exports.PlotHeatmap = PlotHeatmap; exports.PlotMelodyContour = PlotMelodyContour; Object.defineProperty(exports, '__esModule', { value: true }); })));