/* *
|
*
|
* (c) 2009-2019 Øystein Moseng
|
*
|
* Code for sonifying single points.
|
*
|
* License: www.highcharts.com/license
|
*
|
* */
|
|
/**
|
* Define the parameter mapping for an instrument.
|
*
|
* @requires module:modules/sonification
|
*
|
* @interface Highcharts.PointInstrumentMappingObject
|
*/ /**
|
* Define the volume of the instrument. This can be a string with a data
|
* property name, e.g. `'y'`, in which case this data property is used to define
|
* the volume relative to the `y`-values of the other points. A higher `y` value
|
* would then result in a higher volume. This option can also be a fixed number
|
* or a function. If it is a function, this function is called in regular
|
* intervals while the note is playing. It receives three arguments: The point,
|
* the dataExtremes, and the current relative time - where 0 is the beginning of
|
* the note and 1 is the end. The function should return the volume of the note
|
* as a number between 0 and 1.
|
* @name Highcharts.PointInstrumentMappingObject#volume
|
* @type {string|number|Function}
|
*/ /**
|
* Define the duration of the notes for this instrument. This can be a string
|
* with a data property name, e.g. `'y'`, in which case this data property is
|
* used to define the duration relative to the `y`-values of the other points. A
|
* higher `y` value would then result in a longer duration. This option can also
|
* be a fixed number or a function. If it is a function, this function is called
|
* once before the note starts playing, and should return the duration in
|
* milliseconds. It receives two arguments: The point, and the dataExtremes.
|
* @name Highcharts.PointInstrumentMappingObject#duration
|
* @type {string|number|Function}
|
*/ /**
|
* Define the panning of the instrument. This can be a string with a data
|
* property name, e.g. `'x'`, in which case this data property is used to define
|
* the panning relative to the `x`-values of the other points. A higher `x`
|
* value would then result in a higher panning value (panned further to the
|
* right). This option can also be a fixed number or a function. If it is a
|
* function, this function is called in regular intervals while the note is
|
* playing. It receives three arguments: The point, the dataExtremes, and the
|
* current relative time - where 0 is the beginning of the note and 1 is the
|
* end. The function should return the panning of the note as a number between
|
* -1 and 1.
|
* @name Highcharts.PointInstrumentMappingObject#pan
|
* @type {string|number|Function|undefined}
|
*/ /**
|
* Define the frequency of the instrument. This can be a string with a data
|
* property name, e.g. `'y'`, in which case this data property is used to define
|
* the frequency relative to the `y`-values of the other points. A higher `y`
|
* value would then result in a higher frequency. This option can also be a
|
* fixed number or a function. If it is a function, this function is called in
|
* regular intervals while the note is playing. It receives three arguments:
|
* The point, the dataExtremes, and the current relative time - where 0 is the
|
* beginning of the note and 1 is the end. The function should return the
|
* frequency of the note as a number (in Hz).
|
* @name Highcharts.PointInstrumentMappingObject#frequency
|
* @type {string|number|Function}
|
*/
|
|
/**
|
* @requires module:modules/sonification
|
*
|
* @interface Highcharts.PointInstrumentOptionsObject
|
*/ /**
|
* The minimum duration for a note when using a data property for duration. Can
|
* be overridden by using either a fixed number or a function for
|
* instrumentMapping.duration. Defaults to 20.
|
* @name Highcharts.PointInstrumentOptionsObject#minDuration
|
* @type {number|undefined}
|
*/ /**
|
* The maximum duration for a note when using a data property for duration. Can
|
* be overridden by using either a fixed number or a function for
|
* instrumentMapping.duration. Defaults to 2000.
|
* @name Highcharts.PointInstrumentOptionsObject#maxDuration
|
* @type {number|undefined}
|
*/ /**
|
* The minimum pan value for a note when using a data property for panning. Can
|
* be overridden by using either a fixed number or a function for
|
* instrumentMapping.pan. Defaults to -1 (fully left).
|
* @name Highcharts.PointInstrumentOptionsObject#minPan
|
* @type {number|undefined}
|
*/ /**
|
* The maximum pan value for a note when using a data property for panning. Can
|
* be overridden by using either a fixed number or a function for
|
* instrumentMapping.pan. Defaults to 1 (fully right).
|
* @name Highcharts.PointInstrumentOptionsObject#maxPan
|
* @type {number|undefined}
|
*/ /**
|
* The minimum volume for a note when using a data property for volume. Can be
|
* overridden by using either a fixed number or a function for
|
* instrumentMapping.volume. Defaults to 0.1.
|
* @name Highcharts.PointInstrumentOptionsObject#minVolume
|
* @type {number|undefined}
|
*/ /**
|
* The maximum volume for a note when using a data property for volume. Can be
|
* overridden by using either a fixed number or a function for
|
* instrumentMapping.volume. Defaults to 1.
|
* @name Highcharts.PointInstrumentOptionsObject#maxVolume
|
* @type {number|undefined}
|
*/ /**
|
* The minimum frequency for a note when using a data property for frequency.
|
* Can be overridden by using either a fixed number or a function for
|
* instrumentMapping.frequency. Defaults to 220.
|
* @name Highcharts.PointInstrumentOptionsObject#minFrequency
|
* @type {number|undefined}
|
*/ /**
|
* The maximum frequency for a note when using a data property for frequency.
|
* Can be overridden by using either a fixed number or a function for
|
* instrumentMapping.frequency. Defaults to 2200.
|
* @name Highcharts.PointInstrumentOptionsObject#maxFrequency
|
* @type {number|undefined}
|
*/
|
|
/**
|
* An instrument definition for a point, specifying the instrument to play and
|
* how to play it.
|
*
|
* @interface Highcharts.PointInstrumentObject
|
*/ /**
|
* An Instrument instance or the name of the instrument in the
|
* Highcharts.sonification.instruments map.
|
* @name Highcharts.PointInstrumentObject#instrument
|
* @type {Highcharts.Instrument|string}
|
*/ /**
|
* Mapping of instrument parameters for this instrument.
|
* @name Highcharts.PointInstrumentObject#instrumentMapping
|
* @type {Highcharts.PointInstrumentMappingObject}
|
*/ /**
|
* Options for this instrument.
|
* @name Highcharts.PointInstrumentObject#instrumentOptions
|
* @type {Highcharts.PointInstrumentOptionsObject|undefined}
|
*/ /**
|
* Callback to call when the instrument has stopped playing.
|
* @name Highcharts.PointInstrumentObject#onEnd
|
* @type {Function|undefined}
|
*/
|
|
/**
|
* Options for sonifying a point.
|
* @interface Highcharts.PointSonifyOptionsObject
|
*/ /**
|
* The instrument definitions for this point.
|
* @name Highcharts.PointSonifyOptionsObject#instruments
|
* @type {Array<Highcharts.PointInstrumentObject>}
|
*/ /**
|
* Optionally provide the minimum/maximum values for the points. If this is not
|
* supplied, it is calculated from the points in the chart on demand. This
|
* option is supplied in the following format, as a map of point data properties
|
* to objects with min/max values:
|
* ```js
|
* dataExtremes: {
|
* y: {
|
* min: 0,
|
* max: 100
|
* },
|
* z: {
|
* min: -10,
|
* max: 10
|
* }
|
* // Properties used and not provided are calculated on demand
|
* }
|
* ```
|
* @name Highcharts.PointSonifyOptionsObject#dataExtremes
|
* @type {object|undefined}
|
*/ /**
|
* Callback called when the sonification has finished.
|
* @name Highcharts.PointSonifyOptionsObject#onEnd
|
* @type {Function|undefined}
|
*/
|
|
'use strict';
|
|
Object.defineProperty(exports, "__esModule", {
|
value: true
|
});
|
|
var _Globals = require('../../parts/Globals.js');
|
|
var _Globals2 = _interopRequireDefault(_Globals);
|
|
var _utilities = require('utilities.js');
|
|
var _utilities2 = _interopRequireDefault(_utilities);
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
// Defaults for the instrument options
|
// NOTE: Also change defaults in Highcharts.PointInstrumentOptionsObject if
|
// making changes here.
|
var defaultInstrumentOptions = {
|
minDuration: 20,
|
maxDuration: 2000,
|
minVolume: 0.1,
|
maxVolume: 1,
|
minPan: -1,
|
maxPan: 1,
|
minFrequency: 220,
|
maxFrequency: 2200
|
};
|
|
/**
|
* Sonify a single point.
|
*
|
* @sample highcharts/sonification/point-basic/
|
* Click on points to sonify
|
* @sample highcharts/sonification/point-advanced/
|
* Sonify bubbles
|
*
|
* @requires module:modules/sonification
|
*
|
* @function Highcharts.Point#sonify
|
*
|
* @param {Highcharts.PointSonifyOptionsObject} options
|
* Options for the sonification of the point.
|
*/
|
function pointSonify(options) {
|
var point = this,
|
chart = point.series.chart,
|
dataExtremes = options.dataExtremes || {},
|
|
// Get the value to pass to instrument.play from the mapping value
|
// passed in.
|
getMappingValue = function getMappingValue(value, makeFunction, allowedExtremes, allowedValues) {
|
// Fixed number, just use that
|
if (typeof value === 'number' || value === undefined) {
|
return value;
|
}
|
// Function. Return new function if we try to use callback,
|
// otherwise call it now and return result.
|
if (typeof value === 'function') {
|
return makeFunction ? function (time) {
|
return value(point, dataExtremes, time);
|
} : value(point, dataExtremes);
|
}
|
// String, this is a data prop.
|
if (typeof value === 'string') {
|
// Find data extremes if we don't have them
|
dataExtremes[value] = dataExtremes[value] || _utilities2.default.calculateDataExtremes(point.series.chart, value);
|
// Find the value
|
return _utilities2.default.virtualAxisTranslate(_Globals2.default.pick(point[value], point.options[value]), dataExtremes[value], allowedExtremes, allowedValues);
|
}
|
};
|
|
// Register playing point on chart
|
chart.sonification.currentlyPlayingPoint = point;
|
|
// Keep track of instruments playing
|
point.sonification = point.sonification || {};
|
point.sonification.instrumentsPlaying = point.sonification.instrumentsPlaying || {};
|
|
// Register signal handler for the point
|
var signalHandler = point.sonification.signalHandler = point.sonification.signalHandler || new _utilities2.default.SignalHandler(['onEnd']);
|
|
signalHandler.clearSignalCallbacks();
|
signalHandler.registerSignalCallbacks({ onEnd: options.onEnd });
|
|
// If we have a null point or invisible point, just return
|
if (point.isNull || !point.visible || !point.series.visible) {
|
signalHandler.emitSignal('onEnd');
|
return;
|
}
|
|
// Go through instruments and play them
|
options.instruments.forEach(function (instrumentDefinition) {
|
var instrument = typeof instrumentDefinition.instrument === 'string' ? _Globals2.default.sonification.instruments[instrumentDefinition.instrument] : instrumentDefinition.instrument,
|
mapping = instrumentDefinition.instrumentMapping || {},
|
extremes = _Globals2.default.merge(defaultInstrumentOptions, instrumentDefinition.instrumentOptions),
|
id = instrument.id,
|
onEnd = function onEnd(cancelled) {
|
// Instrument on end
|
if (instrumentDefinition.onEnd) {
|
instrumentDefinition.onEnd.apply(this, arguments);
|
}
|
|
// Remove currently playing point reference on chart
|
if (chart.sonification && chart.sonification.currentlyPlayingPoint) {
|
delete chart.sonification.currentlyPlayingPoint;
|
}
|
|
// Remove reference from instruments playing
|
if (point.sonification && point.sonification.instrumentsPlaying) {
|
delete point.sonification.instrumentsPlaying[id];
|
|
// This was the last instrument?
|
if (!Object.keys(point.sonification.instrumentsPlaying).length) {
|
signalHandler.emitSignal('onEnd', cancelled);
|
}
|
}
|
};
|
|
// Play the note on the instrument
|
if (instrument && instrument.play) {
|
point.sonification.instrumentsPlaying[instrument.id] = instrument;
|
instrument.play({
|
frequency: getMappingValue(mapping.frequency, true, { min: extremes.minFrequency, max: extremes.maxFrequency }),
|
duration: getMappingValue(mapping.duration, false, { min: extremes.minDuration, max: extremes.maxDuration }),
|
pan: getMappingValue(mapping.pan, true, { min: extremes.minPan, max: extremes.maxPan }),
|
volume: getMappingValue(mapping.volume, true, { min: extremes.minVolume, max: extremes.maxVolume }),
|
onEnd: onEnd,
|
minFrequency: extremes.minFrequency,
|
maxFrequency: extremes.maxFrequency
|
});
|
} else {
|
_Globals2.default.error(30);
|
}
|
});
|
}
|
|
/**
|
* Cancel sonification of a point. Calls onEnd functions.
|
*
|
* @requires module:modules/sonification
|
*
|
* @function Highcharts.Point#cancelSonify
|
*
|
* @param {boolean} [fadeOut=false]
|
* Whether or not to fade out as we stop. If false, the points are
|
* cancelled synchronously.
|
*/
|
function pointCancelSonify(fadeOut) {
|
var playing = this.sonification && this.sonification.instrumentsPlaying,
|
instrIds = playing && Object.keys(playing);
|
|
if (instrIds && instrIds.length) {
|
instrIds.forEach(function (instr) {
|
playing[instr].stop(!fadeOut, null, 'cancelled');
|
});
|
this.sonification.instrumentsPlaying = {};
|
this.sonification.signalHandler.emitSignal('onEnd', 'cancelled');
|
}
|
}
|
|
var pointSonifyFunctions = {
|
pointSonify: pointSonify,
|
pointCancelSonify: pointCancelSonify
|
};
|
|
exports.default = pointSonifyFunctions;
|