| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369 |
- /**
- * @license Highcharts JS v9.0.1 (2021-02-16)
- *
- * Sonification module
- *
- * (c) 2012-2021 Øystein Moseng
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- (function (factory) {
- if (typeof module === 'object' && module.exports) {
- factory['default'] = factory;
- module.exports = factory;
- } else if (typeof define === 'function' && define.amd) {
- define('highcharts/modules/sonification', ['highcharts'], function (Highcharts) {
- factory(Highcharts);
- factory.Highcharts = Highcharts;
- return factory;
- });
- } else {
- factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
- }
- }(function (Highcharts) {
- var _modules = Highcharts ? Highcharts._modules : {};
- function _registerModule(obj, path, args, fn) {
- if (!obj.hasOwnProperty(path)) {
- obj[path] = fn.apply(null, args);
- }
- }
- _registerModule(_modules, 'Extensions/Sonification/Instrument.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Instrument class for sonification module.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var error = U.error,
- merge = U.merge,
- pick = U.pick,
- uniqueKey = U.uniqueKey;
- /**
- * A set of options for the Instrument class.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.InstrumentOptionsObject
- */ /**
- * The type of instrument. Currently only `oscillator` is supported. Defaults
- * to `oscillator`.
- * @name Highcharts.InstrumentOptionsObject#type
- * @type {string|undefined}
- */ /**
- * The unique ID of the instrument. Generated if not supplied.
- * @name Highcharts.InstrumentOptionsObject#id
- * @type {string|undefined}
- */ /**
- * The master volume multiplier to apply to the instrument, regardless of other
- * volume changes. Defaults to 1.
- * @name Highcharts.InstrumentPlayOptionsObject#masterVolume
- * @type {number|undefined}
- */ /**
- * When using functions to determine frequency or other parameters during
- * playback, this options specifies how often to call the callback functions.
- * Number given in milliseconds. Defaults to 20.
- * @name Highcharts.InstrumentOptionsObject#playCallbackInterval
- * @type {number|undefined}
- */ /**
- * A list of allowed frequencies for this instrument. If trying to play a
- * frequency not on this list, the closest frequency will be used. Set to `null`
- * to allow all frequencies to be used. Defaults to `null`.
- * @name Highcharts.InstrumentOptionsObject#allowedFrequencies
- * @type {Array<number>|undefined}
- */ /**
- * Options specific to oscillator instruments.
- * @name Highcharts.InstrumentOptionsObject#oscillator
- * @type {Highcharts.OscillatorOptionsObject|undefined}
- */
- /**
- * Options for playing an instrument.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.InstrumentPlayOptionsObject
- */ /**
- * The frequency of the note to play. Can be a fixed number, or a function. The
- * function receives one argument: the relative time of the note playing (0
- * being the start, and 1 being the end of the note). It should return the
- * frequency number for each point in time. The poll interval of this function
- * is specified by the Instrument.playCallbackInterval option.
- * @name Highcharts.InstrumentPlayOptionsObject#frequency
- * @type {number|Function}
- */ /**
- * The duration of the note in milliseconds.
- * @name Highcharts.InstrumentPlayOptionsObject#duration
- * @type {number}
- */ /**
- * The minimum frequency to allow. If the instrument has a set of allowed
- * frequencies, the closest frequency is used by default. Use this option to
- * stop too low frequencies from being used.
- * @name Highcharts.InstrumentPlayOptionsObject#minFrequency
- * @type {number|undefined}
- */ /**
- * The maximum frequency to allow. If the instrument has a set of allowed
- * frequencies, the closest frequency is used by default. Use this option to
- * stop too high frequencies from being used.
- * @name Highcharts.InstrumentPlayOptionsObject#maxFrequency
- * @type {number|undefined}
- */ /**
- * The volume of the instrument. Can be a fixed number between 0 and 1, or a
- * function. The function receives one argument: the relative time of the note
- * playing (0 being the start, and 1 being the end of the note). It should
- * return the volume for each point in time. The poll interval of this function
- * is specified by the Instrument.playCallbackInterval option. Defaults to 1.
- * @name Highcharts.InstrumentPlayOptionsObject#volume
- * @type {number|Function|undefined}
- */ /**
- * The panning of the instrument. Can be a fixed number between -1 and 1, or a
- * function. The function receives one argument: the relative time of the note
- * playing (0 being the start, and 1 being the end of the note). It should
- * return the panning value for each point in time. The poll interval of this
- * function is specified by the Instrument.playCallbackInterval option.
- * Defaults to 0.
- * @name Highcharts.InstrumentPlayOptionsObject#pan
- * @type {number|Function|undefined}
- */ /**
- * Callback function to be called when the play is completed.
- * @name Highcharts.InstrumentPlayOptionsObject#onEnd
- * @type {Function|undefined}
- */
- /**
- * @requires module:modules/sonification
- *
- * @interface Highcharts.OscillatorOptionsObject
- */ /**
- * The waveform shape to use for oscillator instruments. Defaults to `sine`.
- * @name Highcharts.OscillatorOptionsObject#waveformShape
- * @type {string|undefined}
- */
- // Default options for Instrument constructor
- var defaultOptions = {
- type: 'oscillator',
- playCallbackInterval: 20,
- masterVolume: 1,
- oscillator: {
- waveformShape: 'sine'
- }
- };
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * The Instrument class. Instrument objects represent an instrument capable of
- * playing a certain pitch for a specified duration.
- *
- * @sample highcharts/sonification/instrument/
- * Using Instruments directly
- * @sample highcharts/sonification/instrument-advanced/
- * Using callbacks for instrument parameters
- *
- * @requires module:modules/sonification
- *
- * @class
- * @name Highcharts.Instrument
- *
- * @param {Highcharts.InstrumentOptionsObject} options
- * Options for the instrument instance.
- */
- function Instrument(options) {
- this.init(options);
- }
- Instrument.prototype.init = function (options) {
- if (!this.initAudioContext()) {
- error(29);
- return;
- }
- this.options = merge(defaultOptions, options);
- this.id = this.options.id = options && options.id || uniqueKey();
- this.masterVolume = this.options.masterVolume || 0;
- // Init the audio nodes
- var ctx = H.audioContext;
- // Note: Destination node can be overridden by setting
- // Highcharts.sonification.Instrument.prototype.destinationNode.
- // This allows for inserting an additional chain of nodes after
- // the default processing.
- var destination = this.destinationNode || ctx.destination;
- this.gainNode = ctx.createGain();
- this.setGain(0);
- this.panNode = ctx.createStereoPanner && ctx.createStereoPanner();
- if (this.panNode) {
- this.setPan(0);
- this.gainNode.connect(this.panNode);
- this.panNode.connect(destination);
- }
- else {
- this.gainNode.connect(destination);
- }
- // Oscillator initialization
- if (this.options.type === 'oscillator') {
- this.initOscillator(this.options.oscillator);
- }
- // Init timer list
- this.playCallbackTimers = [];
- };
- /**
- * Return a copy of an instrument. Only one instrument instance can play at a
- * time, so use this to get a new copy of the instrument that can play alongside
- * it. The new instrument copy will receive a new ID unless one is supplied in
- * options.
- *
- * @function Highcharts.Instrument#copy
- *
- * @param {Highcharts.InstrumentOptionsObject} [options]
- * Options to merge in for the copy.
- *
- * @return {Highcharts.Instrument}
- * A new Instrument instance with the same options.
- */
- Instrument.prototype.copy = function (options) {
- return new Instrument(merge(this.options, { id: null }, options));
- };
- /**
- * Init the audio context, if we do not have one.
- * @private
- * @return {boolean} True if successful, false if not.
- */
- Instrument.prototype.initAudioContext = function () {
- var Context = H.win.AudioContext || H.win.webkitAudioContext,
- hasOldContext = !!H.audioContext;
- if (Context) {
- H.audioContext = H.audioContext || new Context();
- if (!hasOldContext &&
- H.audioContext &&
- H.audioContext.state === 'running') {
- H.audioContext.suspend(); // Pause until we need it
- }
- return !!(H.audioContext &&
- H.audioContext.createOscillator &&
- H.audioContext.createGain);
- }
- return false;
- };
- /**
- * Init an oscillator instrument.
- * @private
- * @param {Highcharts.OscillatorOptionsObject} oscillatorOptions
- * The oscillator options passed to Highcharts.Instrument#init.
- * @return {void}
- */
- Instrument.prototype.initOscillator = function (options) {
- var ctx = H.audioContext;
- this.oscillator = ctx.createOscillator();
- this.oscillator.type = options.waveformShape;
- this.oscillator.connect(this.gainNode);
- this.oscillatorStarted = false;
- };
- /**
- * Set pan position.
- * @private
- * @param {number} panValue
- * The pan position to set for the instrument.
- * @return {void}
- */
- Instrument.prototype.setPan = function (panValue) {
- if (this.panNode) {
- this.panNode.pan.setValueAtTime(panValue, H.audioContext.currentTime);
- }
- };
- /**
- * Set gain level. A maximum of 1.2 is allowed before we emit a warning. The
- * actual volume is not set above this level regardless of input. This function
- * also handles the Instrument's master volume.
- * @private
- * @param {number} gainValue
- * The gain level to set for the instrument.
- * @param {number} [rampTime=0]
- * Gradually change the gain level, time given in milliseconds.
- * @return {void}
- */
- Instrument.prototype.setGain = function (gainValue, rampTime) {
- var gainNode = this.gainNode;
- var newVal = gainValue * this.masterVolume;
- if (gainNode) {
- if (newVal > 1.2) {
- console.warn(// eslint-disable-line
- 'Highcharts sonification warning: ' +
- 'Volume of instrument set too high.');
- newVal = 1.2;
- }
- if (rampTime) {
- gainNode.gain.setValueAtTime(gainNode.gain.value, H.audioContext.currentTime);
- gainNode.gain.linearRampToValueAtTime(newVal, H.audioContext.currentTime + rampTime / 1000);
- }
- else {
- gainNode.gain.setValueAtTime(newVal, H.audioContext.currentTime);
- }
- }
- };
- /**
- * Cancel ongoing gain ramps.
- * @private
- * @return {void}
- */
- Instrument.prototype.cancelGainRamp = function () {
- if (this.gainNode) {
- this.gainNode.gain.cancelScheduledValues(0);
- }
- };
- /**
- * Set the master volume multiplier of the instrument after creation.
- * @param {number} volumeMultiplier
- * The gain level to set for the instrument.
- * @return {void}
- */
- Instrument.prototype.setMasterVolume = function (volumeMultiplier) {
- this.masterVolume = volumeMultiplier || 0;
- };
- /**
- * Get the closest valid frequency for this instrument.
- * @private
- * @param {number} frequency - The target frequency.
- * @param {number} [min] - Minimum frequency to return.
- * @param {number} [max] - Maximum frequency to return.
- * @return {number} The closest valid frequency to the input frequency.
- */
- Instrument.prototype.getValidFrequency = function (frequency, min, max) {
- var validFrequencies = this.options.allowedFrequencies,
- maximum = pick(max,
- Infinity),
- minimum = pick(min, -Infinity);
- return !validFrequencies || !validFrequencies.length ?
- // No valid frequencies for this instrument, return the target
- frequency :
- // Use the valid frequencies and return the closest match
- validFrequencies.reduce(function (acc, cur) {
- // Find the closest allowed value
- return Math.abs(cur - frequency) < Math.abs(acc - frequency) &&
- cur < maximum && cur > minimum ?
- cur : acc;
- }, Infinity);
- };
- /**
- * Clear existing play callback timers.
- * @private
- * @return {void}
- */
- Instrument.prototype.clearPlayCallbackTimers = function () {
- this.playCallbackTimers.forEach(function (timer) {
- clearInterval(timer);
- });
- this.playCallbackTimers = [];
- };
- /**
- * Set the current frequency being played by the instrument. The closest valid
- * frequency between the frequency limits is used.
- * @param {number} frequency
- * The frequency to set.
- * @param {Highcharts.Dictionary<number>} [frequencyLimits]
- * Object with maxFrequency and minFrequency
- * @return {void}
- */
- Instrument.prototype.setFrequency = function (frequency, frequencyLimits) {
- var limits = frequencyLimits || {},
- validFrequency = this.getValidFrequency(frequency,
- limits.min,
- limits.max);
- if (this.options.type === 'oscillator') {
- this.oscillatorPlay(validFrequency);
- }
- };
- /**
- * Play oscillator instrument.
- * @private
- * @param {number} frequency - The frequency to play.
- */
- Instrument.prototype.oscillatorPlay = function (frequency) {
- if (!this.oscillatorStarted) {
- this.oscillator.start();
- this.oscillatorStarted = true;
- }
- this.oscillator.frequency.setValueAtTime(frequency, H.audioContext.currentTime);
- };
- /**
- * Prepare instrument before playing. Resumes the audio context and starts the
- * oscillator.
- * @private
- */
- Instrument.prototype.preparePlay = function () {
- this.setGain(0.001);
- if (H.audioContext.state === 'suspended') {
- H.audioContext.resume();
- }
- if (this.oscillator && !this.oscillatorStarted) {
- this.oscillator.start();
- this.oscillatorStarted = true;
- }
- };
- /**
- * Play the instrument according to options.
- *
- * @sample highcharts/sonification/instrument/
- * Using Instruments directly
- * @sample highcharts/sonification/instrument-advanced/
- * Using callbacks for instrument parameters
- *
- * @function Highcharts.Instrument#play
- *
- * @param {Highcharts.InstrumentPlayOptionsObject} options
- * Options for the playback of the instrument.
- *
- * @return {void}
- */
- Instrument.prototype.play = function (options) {
- var instrument = this,
- duration = options.duration || 0,
- // Set a value, or if it is a function, set it continously as a timer.
- // Pass in the value/function to set, the setter function, and any
- // additional data to pass through to the setter function.
- setOrStartTimer = function (value,
- setter,
- setterData) {
- var target = options.duration,
- currentDurationIx = 0,
- callbackInterval = instrument.options.playCallbackInterval;
- if (typeof value === 'function') {
- var timer = setInterval(function () {
- currentDurationIx++;
- var curTime = (currentDurationIx * callbackInterval / target);
- if (curTime >= 1) {
- instrument[setter](value(1), setterData);
- clearInterval(timer);
- }
- else {
- instrument[setter](value(curTime), setterData);
- }
- }, callbackInterval);
- instrument.playCallbackTimers.push(timer);
- }
- else {
- instrument[setter](value, setterData);
- }
- };
- if (!instrument.id) {
- // No audio support - do nothing
- return;
- }
- // If the AudioContext is suspended we have to resume it before playing
- if (H.audioContext.state === 'suspended' ||
- this.oscillator && !this.oscillatorStarted) {
- instrument.preparePlay();
- // Try again in 10ms
- setTimeout(function () {
- instrument.play(options);
- }, 10);
- return;
- }
- // Clear any existing play timers
- if (instrument.playCallbackTimers.length) {
- instrument.clearPlayCallbackTimers();
- }
- // Clear any gain ramps
- instrument.cancelGainRamp();
- // Clear stop oscillator timer
- if (instrument.stopOscillatorTimeout) {
- clearTimeout(instrument.stopOscillatorTimeout);
- delete instrument.stopOscillatorTimeout;
- }
- // If a note is playing right now, clear the stop timeout, and call the
- // callback.
- if (instrument.stopTimeout) {
- clearTimeout(instrument.stopTimeout);
- delete instrument.stopTimeout;
- if (instrument.stopCallback) {
- // We have a callback for the play we are interrupting. We do not
- // allow this callback to start a new play, because that leads to
- // chaos. We pass in 'cancelled' to indicate that this note did not
- // finish, but still stopped.
- instrument._play = instrument.play;
- instrument.play = function () { };
- instrument.stopCallback('cancelled');
- instrument.play = instrument._play;
- }
- }
- // Stop the note without fadeOut if the duration is too short to hear the
- // note otherwise.
- var immediate = duration < H.sonification.fadeOutDuration + 20;
- // Stop the instrument after the duration of the note
- instrument.stopCallback = options.onEnd;
- var onStop = function () {
- delete instrument.stopTimeout;
- instrument.stop(immediate);
- };
- if (duration) {
- instrument.stopTimeout = setTimeout(onStop, immediate ? duration :
- duration - H.sonification.fadeOutDuration);
- // Play the note
- setOrStartTimer(options.frequency, 'setFrequency', {
- minFrequency: options.minFrequency,
- maxFrequency: options.maxFrequency
- });
- // Set the volume and panning
- setOrStartTimer(pick(options.volume, 1), 'setGain', 4); // Slight ramp
- setOrStartTimer(pick(options.pan, 0), 'setPan');
- }
- else {
- // No note duration, so just stop immediately
- onStop();
- }
- };
- /**
- * Mute an instrument that is playing. If the instrument is not currently
- * playing, this function does nothing.
- *
- * @function Highcharts.Instrument#mute
- */
- Instrument.prototype.mute = function () {
- this.setGain(0.0001, H.sonification.fadeOutDuration * 0.8);
- };
- /**
- * Stop the instrument playing.
- *
- * @function Highcharts.Instrument#stop
- *
- * @param {boolean} immediately
- * Whether to do the stop immediately or fade out.
- *
- * @param {Function} [onStopped]
- * Callback function to be called when the stop is completed.
- *
- * @param {*} [callbackData]
- * Data to send to the onEnd callback functions.
- *
- * @return {void}
- */
- Instrument.prototype.stop = function (immediately, onStopped, callbackData) {
- var instr = this,
- reset = function () {
- // Remove timeout reference
- if (instr.stopOscillatorTimeout) {
- delete instr.stopOscillatorTimeout;
- }
- // The oscillator may have stopped in the meantime here, so allow
- // this function to fail if so.
- try {
- instr.oscillator.stop();
- }
- catch (e) {
- // silent error
- }
- instr.oscillator.disconnect(instr.gainNode);
- // We need a new oscillator in order to restart it
- instr.initOscillator(instr.options.oscillator);
- // Done stopping, call the callback from the stop
- if (onStopped) {
- onStopped(callbackData);
- }
- // Call the callback for the play we finished
- if (instr.stopCallback) {
- instr.stopCallback(callbackData);
- }
- };
- // Clear any existing timers
- if (instr.playCallbackTimers.length) {
- instr.clearPlayCallbackTimers();
- }
- if (instr.stopTimeout) {
- clearTimeout(instr.stopTimeout);
- }
- if (immediately) {
- instr.setGain(0);
- reset();
- }
- else {
- instr.mute();
- // Stop the oscillator after the mute fade-out has finished
- instr.stopOscillatorTimeout =
- setTimeout(reset, H.sonification.fadeOutDuration + 100);
- }
- };
- return Instrument;
- });
- _registerModule(_modules, 'Extensions/Sonification/MusicalFrequencies.js', [], function () {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * List of musical frequencies from C0 to C8.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var frequencies = [
- 16.351597831287414,
- 17.323914436054505,
- 18.354047994837977,
- 19.445436482630058,
- 20.601722307054366,
- 21.826764464562746,
- 23.12465141947715,
- 24.499714748859326,
- 25.956543598746574,
- 27.5,
- 29.13523509488062,
- 30.86770632850775,
- 32.70319566257483,
- 34.64782887210901,
- 36.70809598967594,
- 38.890872965260115,
- 41.20344461410875,
- 43.653528929125486,
- 46.2493028389543,
- 48.999429497718666,
- 51.91308719749314,
- 55,
- 58.27047018976124,
- 61.7354126570155,
- 65.40639132514966,
- 69.29565774421802,
- 73.41619197935188,
- 77.78174593052023,
- 82.4068892282175,
- 87.30705785825097,
- 92.4986056779086,
- 97.99885899543733,
- 103.82617439498628,
- 110,
- 116.54094037952248,
- 123.47082531403103,
- 130.8127826502993,
- 138.59131548843604,
- 146.8323839587038,
- 155.56349186104046,
- 164.81377845643496,
- 174.61411571650194,
- 184.9972113558172,
- 195.99771799087463,
- 207.65234878997256,
- 220,
- 233.08188075904496,
- 246.94165062806206,
- 261.6255653005986,
- 277.1826309768721,
- 293.6647679174076,
- 311.1269837220809,
- 329.6275569128699,
- 349.2282314330039,
- 369.9944227116344,
- 391.99543598174927,
- 415.3046975799451,
- 440,
- 466.1637615180899,
- 493.8833012561241,
- 523.2511306011972,
- 554.3652619537442,
- 587.3295358348151,
- 622.2539674441618,
- 659.2551138257398,
- 698.4564628660078,
- 739.9888454232688,
- 783.9908719634985,
- 830.6093951598903,
- 880,
- 932.3275230361799,
- 987.7666025122483,
- 1046.5022612023945,
- 1108.7305239074883,
- 1174.6590716696303,
- 1244.5079348883237,
- 1318.5102276514797,
- 1396.9129257320155,
- 1479.9776908465376,
- 1567.981743926997,
- 1661.2187903197805,
- 1760,
- 1864.6550460723597,
- 1975.533205024496,
- 2093.004522404789,
- 2217.4610478149766,
- 2349.31814333926,
- 2489.0158697766474,
- 2637.02045530296,
- 2793.825851464031,
- 2959.955381693075,
- 3135.9634878539946,
- 3322.437580639561,
- 3520,
- 3729.3100921447194,
- 3951.066410048992,
- 4186.009044809578 // C8
- ];
- return frequencies;
- });
- _registerModule(_modules, 'Extensions/Sonification/Utilities.js', [_modules['Extensions/Sonification/MusicalFrequencies.js'], _modules['Core/Utilities.js']], function (musicalFrequencies, U) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Utility functions for sonification.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var clamp = U.clamp;
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * The SignalHandler class. Stores signal callbacks (event handlers), and
- * provides an interface to register them, and emit signals. The word "event" is
- * not used to avoid confusion with TimelineEvents.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @class
- * @name Highcharts.SignalHandler
- *
- * @param {Array<string>} supportedSignals
- * List of supported signal names.
- */
- function SignalHandler(supportedSignals) {
- this.init(supportedSignals || []);
- }
- SignalHandler.prototype.init = function (supportedSignals) {
- this.supportedSignals = supportedSignals;
- this.signals = {};
- };
- /**
- * Register a set of signal callbacks with this SignalHandler.
- * Multiple signal callbacks can be registered for the same signal.
- * @private
- * @param {Highcharts.Dictionary<(Function|undefined)>} signals
- * An object that contains a mapping from the signal name to the callbacks. Only
- * supported events are considered.
- * @return {void}
- */
- SignalHandler.prototype.registerSignalCallbacks = function (signals) {
- var signalHandler = this;
- signalHandler.supportedSignals.forEach(function (supportedSignal) {
- var signal = signals[supportedSignal];
- if (signal) {
- (signalHandler.signals[supportedSignal] =
- signalHandler.signals[supportedSignal] || []).push(signal);
- }
- });
- };
- /**
- * Clear signal callbacks, optionally by name.
- * @private
- * @param {Array<string>} [signalNames] - A list of signal names to clear. If
- * not supplied, all signal callbacks are removed.
- * @return {void}
- */
- SignalHandler.prototype.clearSignalCallbacks = function (signalNames) {
- var signalHandler = this;
- if (signalNames) {
- signalNames.forEach(function (signalName) {
- if (signalHandler.signals[signalName]) {
- delete signalHandler.signals[signalName];
- }
- });
- }
- else {
- signalHandler.signals = {};
- }
- };
- /**
- * Emit a signal. Does nothing if the signal does not exist, or has no
- * registered callbacks.
- * @private
- * @param {string} signalNames
- * Name of signal to emit.
- * @param {*} [data]
- * Data to pass to the callback.
- * @return {*}
- */
- SignalHandler.prototype.emitSignal = function (signalName, data) {
- var retval;
- if (this.signals[signalName]) {
- this.signals[signalName].forEach(function (handler) {
- var result = handler(data);
- retval = typeof result !== 'undefined' ? result : retval;
- });
- }
- return retval;
- };
- var utilities = {
- // List of musical frequencies from C0 to C8
- musicalFrequencies: musicalFrequencies,
- // SignalHandler class
- SignalHandler: SignalHandler,
- /**
- * Get a musical scale by specifying the semitones from 1-12 to include.
- * 1: C, 2: C#, 3: D, 4: D#, 5: E, 6: F,
- * 7: F#, 8: G, 9: G#, 10: A, 11: Bb, 12: B
- * @private
- * @param {Array<number>} semitones
- * Array of semitones from 1-12 to include in the scale. Duplicate entries
- * are ignored.
- * @return {Array<number>}
- * Array of frequencies from C0 to C8 that are included in this scale.
- */
- getMusicalScale: function (semitones) {
- return musicalFrequencies.filter(function (freq,
- i) {
- var interval = i % 12 + 1;
- return semitones.some(function (allowedInterval) {
- return allowedInterval === interval;
- });
- });
- },
- /**
- * Calculate the extreme values in a chart for a data prop.
- * @private
- * @param {Highcharts.Chart} chart - The chart
- * @param {string} prop - The data prop to find extremes for
- * @return {Highcharts.RangeObject} Object with min and max properties
- */
- calculateDataExtremes: function (chart, prop) {
- return chart.series.reduce(function (extremes, series) {
- // We use cropped points rather than series.data here, to allow
- // users to zoom in for better fidelity.
- series.points.forEach(function (point) {
- var val = typeof point[prop] !== 'undefined' ?
- point[prop] : point.options[prop];
- extremes.min = Math.min(extremes.min, val);
- extremes.max = Math.max(extremes.max, val);
- });
- return extremes;
- }, {
- min: Infinity,
- max: -Infinity
- });
- },
- /**
- * Translate a value on a virtual axis. Creates a new, virtual, axis with a
- * min and max, and maps the relative value onto this axis.
- * @private
- * @param {number} value
- * The relative data value to translate.
- * @param {Highcharts.RangeObject} DataExtremesObject
- * The possible extremes for this value.
- * @param {object} limits
- * Limits for the virtual axis.
- * @param {boolean} [invert]
- * Invert the virtual axis.
- * @return {number}
- * The value mapped to the virtual axis.
- */
- virtualAxisTranslate: function (value, dataExtremes, limits, invert) {
- var lenValueAxis = dataExtremes.max - dataExtremes.min,
- lenVirtualAxis = Math.abs(limits.max - limits.min),
- valueDelta = invert ?
- dataExtremes.max - value :
- value - dataExtremes.min,
- virtualValueDelta = lenVirtualAxis * valueDelta / lenValueAxis,
- virtualAxisValue = limits.min + virtualValueDelta;
- return lenValueAxis > 0 ?
- clamp(virtualAxisValue, limits.min, limits.max) :
- limits.min;
- }
- };
- return utilities;
- });
- _registerModule(_modules, 'Extensions/Sonification/InstrumentDefinitions.js', [_modules['Extensions/Sonification/Instrument.js'], _modules['Extensions/Sonification/Utilities.js']], function (Instrument, utilities) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Instrument definitions for sonification module.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var instruments = {};
- ['sine', 'square', 'triangle', 'sawtooth'].forEach(function (waveform) {
- // Add basic instruments
- instruments[waveform] = new Instrument({
- oscillator: { waveformShape: waveform }
- });
- // Add musical instruments
- instruments[waveform + 'Musical'] = new Instrument({
- allowedFrequencies: utilities.musicalFrequencies,
- oscillator: { waveformShape: waveform }
- });
- // Add scaled instruments
- instruments[waveform + 'Major'] = new Instrument({
- allowedFrequencies: utilities.getMusicalScale([1, 3, 5, 6, 8, 10, 12]),
- oscillator: { waveformShape: waveform }
- });
- });
- return instruments;
- });
- _registerModule(_modules, 'Extensions/Sonification/Earcon.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Earcons for the sonification module in Highcharts.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var error = U.error,
- merge = U.merge,
- pick = U.pick,
- uniqueKey = U.uniqueKey;
- /**
- * Define an Instrument and the options for playing it.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.EarconInstrument
- */ /**
- * An instrument instance or the name of the instrument in the
- * Highcharts.sonification.instruments map.
- * @name Highcharts.EarconInstrument#instrument
- * @type {string|Highcharts.Instrument}
- */ /**
- * The options to pass to Instrument.play.
- * @name Highcharts.EarconInstrument#playOptions
- * @type {Highcharts.InstrumentPlayOptionsObject}
- */
- /**
- * Options for an Earcon.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.EarconOptionsObject
- */ /**
- * The instruments and their options defining this earcon.
- * @name Highcharts.EarconOptionsObject#instruments
- * @type {Array<Highcharts.EarconInstrument>}
- */ /**
- * The unique ID of the Earcon. Generated if not supplied.
- * @name Highcharts.EarconOptionsObject#id
- * @type {string|undefined}
- */ /**
- * Global panning of all instruments. Overrides all panning on individual
- * instruments. Can be a number between -1 and 1.
- * @name Highcharts.EarconOptionsObject#pan
- * @type {number|undefined}
- */ /**
- * Master volume for all instruments. Volume settings on individual instruments
- * can still be used for relative volume between the instruments. This setting
- * does not affect volumes set by functions in individual instruments. Can be a
- * number between 0 and 1. Defaults to 1.
- * @name Highcharts.EarconOptionsObject#volume
- * @type {number|undefined}
- */ /**
- * Callback function to call when earcon has finished playing.
- * @name Highcharts.EarconOptionsObject#onEnd
- * @type {Function|undefined}
- */
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * The Earcon class. Earcon objects represent a certain sound consisting of
- * one or more instruments playing a predefined sound.
- *
- * @sample highcharts/sonification/earcon/
- * Using earcons directly
- *
- * @requires module:modules/sonification
- *
- * @class
- * @name Highcharts.Earcon
- *
- * @param {Highcharts.EarconOptionsObject} options
- * Options for the Earcon instance.
- */
- function Earcon(options) {
- this.init(options || {});
- }
- Earcon.prototype.init = function (options) {
- this.options = options;
- if (!this.options.id) {
- this.options.id = this.id = uniqueKey();
- }
- this.instrumentsPlaying = {};
- };
- /**
- * Play the earcon, optionally overriding init options.
- *
- * @sample highcharts/sonification/earcon/
- * Using earcons directly
- *
- * @function Highcharts.Earcon#sonify
- *
- * @param {Highcharts.EarconOptionsObject} options
- * Override existing options.
- *
- * @return {void}
- */
- Earcon.prototype.sonify = function (options) {
- var playOptions = merge(this.options,
- options);
- // Find master volume/pan settings
- var masterVolume = pick(playOptions.volume, 1),
- masterPan = playOptions.pan,
- earcon = this,
- playOnEnd = options && options.onEnd,
- masterOnEnd = earcon.options.onEnd;
- // Go through the instruments and play them
- playOptions.instruments.forEach(function (opts) {
- var instrument = typeof opts.instrument === 'string' ?
- H.sonification.instruments[opts.instrument] : opts.instrument,
- instrumentOpts = merge(opts.playOptions),
- instrOnEnd,
- instrumentCopy,
- copyId = '';
- if (instrument && instrument.play) {
- if (opts.playOptions) {
- instrumentOpts.pan = pick(masterPan, instrumentOpts.pan);
- // Handle onEnd
- instrOnEnd = instrumentOpts.onEnd;
- instrumentOpts.onEnd = function () {
- delete earcon.instrumentsPlaying[copyId];
- if (instrOnEnd) {
- instrOnEnd.apply(this, arguments);
- }
- if (!Object.keys(earcon.instrumentsPlaying).length) {
- if (playOnEnd) {
- playOnEnd.apply(this, arguments);
- }
- if (masterOnEnd) {
- masterOnEnd.apply(this, arguments);
- }
- }
- };
- // Play the instrument. Use a copy so we can play multiple at
- // the same time.
- instrumentCopy = instrument.copy();
- instrumentCopy.setMasterVolume(masterVolume);
- copyId = instrumentCopy.id;
- earcon.instrumentsPlaying[copyId] = instrumentCopy;
- instrumentCopy.play(instrumentOpts);
- }
- }
- else {
- error(30);
- }
- });
- };
- /**
- * Cancel any current sonification of the Earcon. Calls onEnd functions.
- *
- * @function Highcharts.Earcon#cancelSonify
- *
- * @param {boolean} [fadeOut=false]
- * Whether or not to fade out as we stop. If false, the earcon is
- * cancelled synchronously.
- *
- * @return {void}
- */
- Earcon.prototype.cancelSonify = function (fadeOut) {
- var playing = this.instrumentsPlaying,
- instrIds = playing && Object.keys(playing);
- if (instrIds && instrIds.length) {
- instrIds.forEach(function (instr) {
- playing[instr].stop(!fadeOut, null, 'cancelled');
- });
- this.instrumentsPlaying = {};
- }
- };
- return Earcon;
- });
- _registerModule(_modules, 'Extensions/Sonification/PointSonify.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Extensions/Sonification/Utilities.js']], function (H, U, utilities) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Code for sonifying single points.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var error = U.error,
- merge = U.merge,
- pick = U.pick;
- /**
- * 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. Alternatively, `'-y'` can be used,
- * which inverts the polarity, so that a higher `y` value results in a lower
- * 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. Alternatively,
- * `'-y'` can be used, in which case the polarity is inverted, and a higher
- * `y` value would result in a shorter 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). Alternatively, `'-x'` can be used, in which case the polarity is
- * inverted, and a higher `x` value would result in a lower panning value
- * (panned further to the left). 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. Alternatively, `'-y'` can be
- * used, in which case the polarity is inverted, and a higher `y` value would
- * result in a lower 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}
- */
- // 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
- };
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * 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.
- *
- * @return {void}
- */
- function pointSonify(options) {
- var _a;
- var point = this,
- chart = point.series.chart,
- masterVolume = pick(options.masterVolume, (_a = chart.options.sonification) === null || _a === void 0 ? void 0 : _a.masterVolume),
- dataExtremes = options.dataExtremes || {},
- // Get the value to pass to instrument.play from the mapping value
- // passed in.
- getMappingValue = function (value,
- makeFunction,
- allowedExtremes) {
- // 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. Potentially with negative polarity.
- if (typeof value === 'string') {
- var hasInvertedPolarity = value.charAt(0) === '-';
- var dataProp = hasInvertedPolarity ? value.slice(1) : value;
- var pointValue = pick(point[dataProp],
- point.options[dataProp]);
- // Find data extremes if we don't have them
- dataExtremes[dataProp] = dataExtremes[dataProp] ||
- utilities.calculateDataExtremes(point.series.chart, dataProp);
- // Find the value
- return utilities.virtualAxisTranslate(pointValue, dataExtremes[dataProp], allowedExtremes, hasInvertedPolarity);
- }
- // Fixed number or something else weird, just use that
- return value;
- };
- // 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 utilities.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' ?
- H.sonification.instruments[instrumentDefinition.instrument] :
- instrumentDefinition.instrument,
- mapping = instrumentDefinition.instrumentMapping || {},
- extremes = merge(defaultInstrumentOptions,
- instrumentDefinition.instrumentOptions),
- id = instrument.id,
- onEnd = function (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) {
- if (typeof masterVolume !== 'undefined') {
- instrument.setMasterVolume(masterVolume);
- }
- 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 {
- 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.
- *
- * @return {void}
- */
- 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
- };
- return pointSonifyFunctions;
- });
- _registerModule(_modules, 'Extensions/Sonification/ChartSonify.js', [_modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js'], _modules['Extensions/Sonification/Utilities.js']], function (H, Point, U, utilities) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Sonification functions for chart/series.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- /**
- * An Earcon configuration, specifying an Earcon and when to play it.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.EarconConfiguration
- */ /**
- * An Earcon instance.
- * @name Highcharts.EarconConfiguration#earcon
- * @type {Highcharts.Earcon}
- */ /**
- * The ID of the point to play the Earcon on.
- * @name Highcharts.EarconConfiguration#onPoint
- * @type {string|undefined}
- */ /**
- * A function to determine whether or not to play this earcon on a point. The
- * function is called for every point, receiving that point as parameter. It
- * should return either a boolean indicating whether or not to play the earcon,
- * or a new Earcon instance - in which case the new Earcon will be played.
- * @name Highcharts.EarconConfiguration#condition
- * @type {Function|undefined}
- */
- /**
- * Options for sonifying a series.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.SonifySeriesOptionsObject
- */ /**
- * The duration for playing the points. Note that points might continue to play
- * after the duration has passed, but no new points will start playing.
- * @name Highcharts.SonifySeriesOptionsObject#duration
- * @type {number}
- */ /**
- * The axis to use for when to play the points. Can be a string with a data
- * property (e.g. `x`), or a function. If it is a function, this function
- * receives the point as argument, and should return a numeric value. The points
- * with the lowest numeric values are then played first, and the time between
- * points will be proportional to the distance between the numeric values.
- * @name Highcharts.SonifySeriesOptionsObject#pointPlayTime
- * @type {string|Function}
- */ /**
- * The instrument definitions for the points in this series.
- * @name Highcharts.SonifySeriesOptionsObject#instruments
- * @type {Array<Highcharts.PointInstrumentObject>}
- */ /**
- * Earcons to add to the series.
- * @name Highcharts.SonifySeriesOptionsObject#earcons
- * @type {Array<Highcharts.EarconConfiguration>|undefined}
- */ /**
- * Optionally provide the minimum/maximum data values for the points. If this is
- * not supplied, it is calculated from all 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.SonifySeriesOptionsObject#dataExtremes
- * @type {Highcharts.Dictionary<Highcharts.RangeObject>|undefined}
- */ /**
- * Callback before a point is played.
- * @name Highcharts.SonifySeriesOptionsObject#onPointStart
- * @type {Function|undefined}
- */ /**
- * Callback after a point has finished playing.
- * @name Highcharts.SonifySeriesOptionsObject#onPointEnd
- * @type {Function|undefined}
- */ /**
- * Callback after the series has played.
- * @name Highcharts.SonifySeriesOptionsObject#onEnd
- * @type {Function|undefined}
- */
- ''; // detach doclets above
- var find = U.find,
- isArray = U.isArray,
- merge = U.merge,
- pick = U.pick,
- splat = U.splat,
- objectEach = U.objectEach;
- /**
- * Get the relative time value of a point.
- * @private
- * @param {Highcharts.Point} point
- * The point.
- * @param {Function|string} timeProp
- * The time axis data prop or the time function.
- * @return {number}
- * The time value.
- */
- function getPointTimeValue(point, timeProp) {
- return typeof timeProp === 'function' ?
- timeProp(point) :
- pick(point[timeProp], point.options[timeProp]);
- }
- /**
- * Get the time extremes of this series. This is handled outside of the
- * dataExtremes, as we always want to just sonify the visible points, and we
- * always want the extremes to be the extremes of the visible points.
- * @private
- * @param {Highcharts.Series} series
- * The series to compute on.
- * @param {Function|string} timeProp
- * The time axis data prop or the time function.
- * @return {Highcharts.RangeObject}
- * Object with min/max extremes for the time values.
- */
- function getTimeExtremes(series, timeProp) {
- // Compute the extremes from the visible points.
- return series.points.reduce(function (acc, point) {
- var value = getPointTimeValue(point,
- timeProp);
- acc.min = Math.min(acc.min, value);
- acc.max = Math.max(acc.max, value);
- return acc;
- }, {
- min: Infinity,
- max: -Infinity
- });
- }
- /**
- * Calculate value extremes for used instrument data properties on a chart.
- * @private
- * @param {Highcharts.Chart} chart
- * The chart to calculate extremes from.
- * @param {Array<Highcharts.PointInstrumentObject>} [instruments]
- * Additional instrument definitions to inspect for data props used, in
- * addition to the instruments defined in the chart options.
- * @param {Highcharts.Dictionary<Highcharts.RangeObject>} [dataExtremes]
- * Predefined extremes for each data prop.
- * @return {Highcharts.Dictionary<Highcharts.RangeObject>}
- * New extremes with data properties mapped to min/max objects.
- */
- function getExtremesForInstrumentProps(chart, instruments, dataExtremes) {
- var _a;
- var allInstrumentDefinitions = (instruments || []).slice(0);
- var defaultInstrumentDef = (_a = chart.options.sonification) === null || _a === void 0 ? void 0 : _a.defaultInstrumentOptions;
- var optionDefToInstrDef = function (optionDef) { return ({
- instrumentMapping: optionDef.mapping
- }); };
- if (defaultInstrumentDef) {
- allInstrumentDefinitions.push(optionDefToInstrDef(defaultInstrumentDef));
- }
- chart.series.forEach(function (series) {
- var _a;
- var instrOptions = (_a = series.options.sonification) === null || _a === void 0 ? void 0 : _a.instruments;
- if (instrOptions) {
- allInstrumentDefinitions = allInstrumentDefinitions.concat(instrOptions.map(optionDefToInstrDef));
- }
- });
- return (allInstrumentDefinitions).reduce(function (newExtremes, instrumentDefinition) {
- Object.keys(instrumentDefinition.instrumentMapping || {}).forEach(function (instrumentParameter) {
- var value = instrumentDefinition.instrumentMapping[instrumentParameter];
- if (typeof value === 'string' && !newExtremes[value]) {
- // This instrument parameter is mapped to a data prop.
- // If we don't have predefined data extremes, find them.
- newExtremes[value] = utilities.calculateDataExtremes(chart, value);
- }
- });
- return newExtremes;
- }, merge(dataExtremes));
- }
- /**
- * Get earcons for the point if there are any.
- * @private
- * @param {Highcharts.Point} point
- * The point to find earcons for.
- * @param {Array<Highcharts.EarconConfiguration>} earconDefinitions
- * Earcons to check.
- * @return {Array<Highcharts.Earcon>}
- * Array of earcons to be played with this point.
- */
- function getPointEarcons(point, earconDefinitions) {
- return earconDefinitions.reduce(function (earcons, earconDefinition) {
- var cond,
- earcon = earconDefinition.earcon;
- if (earconDefinition.condition) {
- // We have a condition. This overrides onPoint
- cond = earconDefinition.condition(point);
- if (cond instanceof H.sonification.Earcon) {
- // Condition returned an earcon
- earcons.push(cond);
- }
- else if (cond) {
- // Condition returned true
- earcons.push(earcon);
- }
- }
- else if (earconDefinition.onPoint &&
- point.id === earconDefinition.onPoint) {
- // We have earcon onPoint
- earcons.push(earcon);
- }
- return earcons;
- }, []);
- }
- /**
- * Utility function to get a new list of instrument options where all the
- * instrument references are copies.
- * @private
- * @param {Array<Highcharts.PointInstrumentObject>} instruments
- * The instrument options.
- * @return {Array<Highcharts.PointInstrumentObject>}
- * Array of copied instrument options.
- */
- function makeInstrumentCopies(instruments) {
- return instruments.map(function (instrumentDef) {
- var instrument = instrumentDef.instrument,
- copy = (typeof instrument === 'string' ?
- H.sonification.instruments[instrument] :
- instrument).copy();
- return merge(instrumentDef, { instrument: copy });
- });
- }
- /**
- * Utility function to apply a master volume to a list of instrument
- * options.
- * @private
- * @param {Array<Highcharts.PointInstrumentObject>} instruments
- * The instrument options. Only options with Instrument object instances
- * will be affected.
- * @param {number} masterVolume
- * The master volume multiplier to apply to the instruments.
- * @return {Array<Highcharts.PointInstrumentObject>}
- * Array of instrument options.
- */
- function applyMasterVolumeToInstruments(instruments, masterVolume) {
- instruments.forEach(function (instrOpts) {
- var instr = instrOpts.instrument;
- if (typeof instr !== 'string') {
- instr.setMasterVolume(masterVolume);
- }
- });
- return instruments;
- }
- /**
- * Utility function to find the duration of the final note in a series.
- * @private
- * @param {Highcharts.Series} series The data series to calculate on.
- * @param {Array<Highcharts.PointInstrumentObject>} instruments The instrument options for this series.
- * @param {Highcharts.Dictionary<Highcharts.RangeObject>} dataExtremes Value extremes for the data series props.
- * @return {number} The duration of the final note in milliseconds.
- */
- function getFinalNoteDuration(series, instruments, dataExtremes) {
- var finalPoint = series.points[series.points.length - 1];
- return instruments.reduce(function (duration, instrument) {
- var mapping = instrument.instrumentMapping.duration;
- var instrumentDuration;
- if (typeof mapping === 'string') {
- instrumentDuration = 0; // Ignore, no easy way to map this
- }
- else if (typeof mapping === 'function') {
- instrumentDuration = mapping(finalPoint, dataExtremes);
- }
- else {
- instrumentDuration = mapping;
- }
- return Math.max(duration, instrumentDuration);
- }, 0);
- }
- /**
- * Create a TimelinePath from a series. Takes the same options as seriesSonify.
- * To intuitively allow multiple series to play simultaneously we make copies of
- * the instruments for each series.
- * @private
- * @param {Highcharts.Series} series
- * The series to build from.
- * @param {Highcharts.SonifySeriesOptionsObject} options
- * The options for building the TimelinePath.
- * @return {Highcharts.TimelinePath}
- * A timeline path with events.
- */
- function buildTimelinePathFromSeries(series, options) {
- // options.timeExtremes is internal and used so that the calculations from
- // chart.sonify can be reused.
- var timeExtremes = options.timeExtremes || getTimeExtremes(series,
- options.pointPlayTime),
- // Compute any data extremes that aren't defined yet
- dataExtremes = getExtremesForInstrumentProps(series.chart,
- options.instruments,
- options.dataExtremes),
- minimumSeriesDurationMs = 10,
- // Get the duration of the final note
- finalNoteDuration = getFinalNoteDuration(series,
- options.instruments,
- dataExtremes),
- // Get time offset for a point, relative to duration
- pointToTime = function (point) {
- return utilities.virtualAxisTranslate(getPointTimeValue(point,
- options.pointPlayTime),
- timeExtremes, { min: 0,
- max: Math.max(options.duration - finalNoteDuration,
- minimumSeriesDurationMs) });
- }, masterVolume = pick(options.masterVolume, 1),
- // Make copies of the instruments used for this series, to allow
- // multiple series with the same instrument to play together
- instrumentCopies = makeInstrumentCopies(options.instruments), instruments = applyMasterVolumeToInstruments(instrumentCopies, masterVolume),
- // Go through the points, convert to events, optionally add Earcons
- timelineEvents = series.points.reduce(function (events, point) {
- var earcons = getPointEarcons(point,
- options.earcons || []),
- time = pointToTime(point);
- return events.concat(
- // Event object for point
- new H.sonification.TimelineEvent({
- eventObject: point,
- time: time,
- id: point.id,
- playOptions: {
- instruments: instruments,
- dataExtremes: dataExtremes,
- masterVolume: masterVolume
- }
- }),
- // Earcons
- earcons.map(function (earcon) {
- return new H.sonification.TimelineEvent({
- eventObject: earcon,
- time: time,
- playOptions: {
- volume: masterVolume
- }
- });
- }));
- }, []);
- // Build the timeline path
- return new H.sonification.TimelinePath({
- events: timelineEvents,
- onStart: function () {
- if (options.onStart) {
- options.onStart(series);
- }
- },
- onEventStart: function (event) {
- var eventObject = event.options && event.options.eventObject;
- if (eventObject instanceof Point) {
- // Check for hidden series
- if (!eventObject.series.visible &&
- !eventObject.series.chart.series.some(function (series) {
- return series.visible;
- })) {
- // We have no visible series, stop the path.
- event.timelinePath.timeline.pause();
- event.timelinePath.timeline.resetCursor();
- return false;
- }
- // Emit onPointStart
- if (options.onPointStart) {
- options.onPointStart(event, eventObject);
- }
- }
- },
- onEventEnd: function (eventData) {
- var eventObject = eventData.event && eventData.event.options &&
- eventData.event.options.eventObject;
- if (eventObject instanceof Point && options.onPointEnd) {
- options.onPointEnd(eventData.event, eventObject);
- }
- },
- onEnd: function () {
- if (options.onEnd) {
- options.onEnd(series);
- }
- },
- targetDuration: options.duration
- });
- }
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * Sonify a series.
- *
- * @sample highcharts/sonification/series-basic/
- * Click on series to sonify
- * @sample highcharts/sonification/series-earcon/
- * Series with earcon
- * @sample highcharts/sonification/point-play-time/
- * Play y-axis by time
- * @sample highcharts/sonification/earcon-on-point/
- * Earcon set on point
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Series#sonify
- *
- * @param {Highcharts.SonifySeriesOptionsObject} [options]
- * The options for sonifying this series. If not provided,
- * uses options set on chart and series.
- *
- * @return {void}
- */
- function seriesSonify(options) {
- var mergedOptions = getSeriesSonifyOptions(this,
- options);
- var timelinePath = buildTimelinePathFromSeries(this,
- mergedOptions);
- var chartSonification = this.chart.sonification;
- // Only one timeline can play at a time. If we want multiple series playing
- // at the same time, use chart.sonify.
- if (chartSonification.timeline) {
- chartSonification.timeline.pause();
- }
- // Store reference to duration
- chartSonification.duration = mergedOptions.duration;
- // Create new timeline for this series, and play it.
- chartSonification.timeline = new H.sonification.Timeline({
- paths: [timelinePath]
- });
- chartSonification.timeline.play();
- }
- /**
- * Utility function to assemble options for creating a TimelinePath from a
- * series when sonifying an entire chart.
- * @private
- * @param {Highcharts.Series} series
- * The series to return options for.
- * @param {Highcharts.RangeObject} dataExtremes
- * Pre-calculated data extremes for the chart.
- * @param {Highcharts.SonificationOptions} chartSonifyOptions
- * Options passed in to chart.sonify.
- * @return {Partial<Highcharts.SonifySeriesOptionsObject>}
- * Options for buildTimelinePathFromSeries.
- */
- function buildChartSonifySeriesOptions(series, dataExtremes, chartSonifyOptions) {
- var _a,
- _b,
- _c;
- var additionalSeriesOptions = chartSonifyOptions.seriesOptions || {};
- var pointPlayTime = ((_c = (_b = (_a = series.chart.options.sonification) === null || _a === void 0 ? void 0 : _a.defaultInstrumentOptions) === null || _b === void 0 ? void 0 : _b.mapping) === null || _c === void 0 ? void 0 : _c.pointPlayTime) || 'x';
- var configOptions = chartOptionsToSonifySeriesOptions(series);
- return merge(
- // Options from chart configuration
- configOptions,
- // Options passed in
- {
- // Calculated dataExtremes for chart
- dataExtremes: dataExtremes,
- // We need to get timeExtremes for each series. We pass this
- // in when building the TimelinePath objects to avoid
- // calculating twice.
- timeExtremes: getTimeExtremes(series, pointPlayTime),
- // Some options we just pass on
- instruments: chartSonifyOptions.instruments || configOptions.instruments,
- onStart: chartSonifyOptions.onSeriesStart || configOptions.onStart,
- onEnd: chartSonifyOptions.onSeriesEnd || configOptions.onEnd,
- earcons: chartSonifyOptions.earcons || configOptions.earcons,
- masterVolume: pick(chartSonifyOptions.masterVolume, configOptions.masterVolume)
- },
- // Merge in the specific series options by ID if any are passed in
- isArray(additionalSeriesOptions) ? (find(additionalSeriesOptions, function (optEntry) {
- return optEntry.id === pick(series.id, series.options.id);
- }) || {}) : additionalSeriesOptions, {
- // Forced options
- pointPlayTime: pointPlayTime
- });
- }
- /**
- * Utility function to normalize the ordering of timeline paths when sonifying
- * a chart.
- * @private
- * @param {string|Array<string|Highcharts.Earcon|Array<string|Highcharts.Earcon>>} orderOptions -
- * Order options for the sonification.
- * @param {Highcharts.Chart} chart - The chart we are sonifying.
- * @param {Function} seriesOptionsCallback
- * A function that takes a series as argument, and returns the series options
- * for that series to be used with buildTimelinePathFromSeries.
- * @return {Array<object|Array<object|Highcharts.TimelinePath>>} If order is
- * sequential, we return an array of objects to create series paths from. If
- * order is simultaneous we return an array of an array with the same. If there
- * is a custom order, we return an array of arrays of either objects (for
- * series) or TimelinePaths (for earcons and delays).
- */
- function buildPathOrder(orderOptions, chart, seriesOptionsCallback) {
- var order;
- if (orderOptions === 'sequential' || orderOptions === 'simultaneous') {
- // Just add the series from the chart
- order = chart.series.reduce(function (seriesList, series) {
- var _a;
- if (series.visible && ((_a = series.options.sonification) === null || _a === void 0 ? void 0 : _a.enabled) !== false) {
- seriesList.push({
- series: series,
- seriesOptions: seriesOptionsCallback(series)
- });
- }
- return seriesList;
- }, []);
- // If order is simultaneous, group all series together
- if (orderOptions === 'simultaneous') {
- order = [order];
- }
- }
- else {
- // We have a specific order, and potentially custom items - like
- // earcons or silent waits.
- order = orderOptions.reduce(function (orderList, orderDef) {
- // Return set of items to play simultaneously. Could be only one.
- var simulItems = splat(orderDef).reduce(function (items,
- item) {
- var itemObject;
- // Is this item a series ID?
- if (typeof item === 'string') {
- var series = chart.get(item);
- if (series.visible) {
- itemObject = {
- series: series,
- seriesOptions: seriesOptionsCallback(series)
- };
- }
- // Is it an earcon? If so, just create the path.
- }
- else if (item instanceof H.sonification.Earcon) {
- // Path with a single event
- itemObject = new H.sonification.TimelinePath({
- events: [new H.sonification.TimelineEvent({
- eventObject: item
- })]
- });
- }
- // Is this item a silent wait? If so, just create the path.
- if (item.silentWait) {
- itemObject = new H.sonification.TimelinePath({
- silentWait: item.silentWait
- });
- }
- // Add to items to play simultaneously
- if (itemObject) {
- items.push(itemObject);
- }
- return items;
- }, []);
- // Add to order list
- if (simulItems.length) {
- orderList.push(simulItems);
- }
- return orderList;
- }, []);
- }
- return order;
- }
- /**
- * Utility function to add a silent wait after all series.
- * @private
- * @param {Array<object|Array<object|TimelinePath>>} order
- * The order of items.
- * @param {number} wait
- * The wait in milliseconds to add.
- * @return {Array<object|Array<object|TimelinePath>>}
- * The order with waits inserted.
- */
- function addAfterSeriesWaits(order, wait) {
- if (!wait) {
- return order;
- }
- return order.reduce(function (newOrder, orderDef, i) {
- var simultaneousPaths = splat(orderDef);
- newOrder.push(simultaneousPaths);
- // Go through the simultaneous paths and see if there is a series there
- if (i < order.length - 1 && // Do not add wait after last series
- simultaneousPaths.some(function (item) {
- return item.series;
- })) {
- // We have a series, meaning we should add a wait after these
- // paths have finished.
- newOrder.push(new H.sonification.TimelinePath({
- silentWait: wait
- }));
- }
- return newOrder;
- }, []);
- }
- /**
- * Utility function to find the total amout of wait time in the TimelinePaths.
- * @private
- * @param {Array<object|Array<object|TimelinePath>>} order - The order of
- * TimelinePaths/items.
- * @return {number} The total time in ms spent on wait paths between playing.
- */
- function getWaitTime(order) {
- return order.reduce(function (waitTime, orderDef) {
- var def = splat(orderDef);
- return waitTime + (def.length === 1 &&
- def[0].options &&
- def[0].options.silentWait || 0);
- }, 0);
- }
- /**
- * Utility function to ensure simultaneous paths have start/end events at the
- * same time, to sync them.
- * @private
- * @param {Array<Highcharts.TimelinePath>} paths - The paths to sync.
- */
- function syncSimultaneousPaths(paths) {
- // Find the extremes for these paths
- var extremes = paths.reduce(function (extremes,
- path) {
- var events = path.events;
- if (events && events.length) {
- extremes.min = Math.min(events[0].time, extremes.min);
- extremes.max = Math.max(events[events.length - 1].time, extremes.max);
- }
- return extremes;
- }, {
- min: Infinity,
- max: -Infinity
- });
- // Go through the paths and add events to make them fit the same timespan
- paths.forEach(function (path) {
- var events = path.events,
- hasEvents = events && events.length,
- eventsToAdd = [];
- if (!(hasEvents && events[0].time <= extremes.min)) {
- eventsToAdd.push(new H.sonification.TimelineEvent({
- time: extremes.min
- }));
- }
- if (!(hasEvents && events[events.length - 1].time >= extremes.max)) {
- eventsToAdd.push(new H.sonification.TimelineEvent({
- time: extremes.max
- }));
- }
- if (eventsToAdd.length) {
- path.addTimelineEvents(eventsToAdd);
- }
- });
- }
- /**
- * Utility function to find the total duration span for all simul path sets
- * that include series.
- * @private
- * @param {Array<object|Array<object|Highcharts.TimelinePath>>} order - The
- * order of TimelinePaths/items.
- * @return {number} The total time value span difference for all series.
- */
- function getSimulPathDurationTotal(order) {
- return order.reduce(function (durationTotal, orderDef) {
- return durationTotal + splat(orderDef).reduce(function (maxPathDuration, item) {
- var timeExtremes = (item.series &&
- item.seriesOptions &&
- item.seriesOptions.timeExtremes);
- return timeExtremes ?
- Math.max(maxPathDuration, timeExtremes.max - timeExtremes.min) : maxPathDuration;
- }, 0);
- }, 0);
- }
- /**
- * Function to calculate the duration in ms for a series.
- * @private
- * @param {number} seriesValueDuration - The duration of the series in value
- * difference.
- * @param {number} totalValueDuration - The total duration of all (non
- * simultaneous) series in value difference.
- * @param {number} totalDurationMs - The desired total duration for all series
- * in milliseconds.
- * @return {number} The duration for the series in milliseconds.
- */
- function getSeriesDurationMs(seriesValueDuration, totalValueDuration, totalDurationMs) {
- // A series spanning the whole chart would get the full duration.
- return utilities.virtualAxisTranslate(seriesValueDuration, { min: 0, max: totalValueDuration }, { min: 0, max: totalDurationMs });
- }
- /**
- * Convert series building objects into paths and return a new list of
- * TimelinePaths.
- * @private
- * @param {Array<object|Array<object|Highcharts.TimelinePath>>} order - The
- * order list.
- * @param {number} duration - Total duration to aim for in milliseconds.
- * @return {Array<Array<Highcharts.TimelinePath>>} Array of TimelinePath objects
- * to play.
- */
- function buildPathsFromOrder(order, duration) {
- // Find time used for waits (custom or after series), and subtract it from
- // available duration.
- var totalAvailableDurationMs = Math.max(duration - getWaitTime(order), 0),
- // Add up simultaneous path durations to find total value span duration
- // of everything
- totalUsedDuration = getSimulPathDurationTotal(order);
- // Go through the order list and convert the items
- return order.reduce(function (allPaths, orderDef) {
- var simultaneousPaths = splat(orderDef).reduce(function (simulPaths,
- item) {
- if (item instanceof H.sonification.TimelinePath) {
- // This item is already a path object
- simulPaths.push(item);
- }
- else if (item.series) {
- // We have a series.
- // We need to set the duration of the series
- item.seriesOptions.duration =
- item.seriesOptions.duration || getSeriesDurationMs(item.seriesOptions.timeExtremes.max -
- item.seriesOptions.timeExtremes.min, totalUsedDuration, totalAvailableDurationMs);
- // Add the path
- simulPaths.push(buildTimelinePathFromSeries(item.series, item.seriesOptions));
- }
- return simulPaths;
- }, []);
- // Add in the simultaneous paths
- allPaths.push(simultaneousPaths);
- return allPaths;
- }, []);
- }
- /**
- * @private
- * @param {Highcharts.Series} series The series to get options for.
- * @param {Highcharts.SonifySeriesOptionsObject} options
- * Options to merge with user options on series/chart and default options.
- * @returns {Array<Highcharts.PointInstrumentObject>} The merged options.
- */
- function getSeriesInstrumentOptions(series, options) {
- var _a,
- _b;
- if (options === null || options === void 0 ? void 0 : options.instruments) {
- return options.instruments;
- }
- var defaultInstrOpts = ((_a = series.chart.options.sonification) === null || _a === void 0 ? void 0 : _a.defaultInstrumentOptions) || {};
- var seriesInstrOpts = ((_b = series.options.sonification) === null || _b === void 0 ? void 0 : _b.instruments) || [{}];
- var removeNullsFromObject = function (obj) {
- objectEach(obj,
- function (val,
- key) {
- if (val === null) {
- delete obj[key];
- }
- });
- };
- // Convert series options to PointInstrumentObjects and merge with
- // default options
- return (seriesInstrOpts).map(function (optionSet) {
- // Allow setting option to null to use default
- removeNullsFromObject(optionSet.mapping || {});
- removeNullsFromObject(optionSet);
- return {
- instrument: optionSet.instrument || defaultInstrOpts.instrument,
- instrumentOptions: merge(defaultInstrOpts, optionSet, {
- // Instrument options are lifted to root in the API options
- // object, so merge all in order to avoid missing any. But
- // remove the following which are not instrumentOptions:
- mapping: void 0,
- instrument: void 0
- }),
- instrumentMapping: merge(defaultInstrOpts.mapping, optionSet.mapping)
- };
- });
- }
- /**
- * Utility function to translate between options set in chart configuration and
- * a SonifySeriesOptionsObject.
- * @private
- * @param {Highcharts.Series} series The series to get options for.
- * @returns {Highcharts.SonifySeriesOptionsObject} Options for chart/series.sonify()
- */
- function chartOptionsToSonifySeriesOptions(series) {
- var _a,
- _b;
- var seriesOpts = series.options.sonification || {};
- var chartOpts = series.chart.options.sonification || {};
- var chartEvents = chartOpts.events || {};
- var seriesEvents = seriesOpts.events || {};
- return {
- onEnd: seriesEvents.onSeriesEnd || chartEvents.onSeriesEnd,
- onStart: seriesEvents.onSeriesStart || chartEvents.onSeriesStart,
- onPointEnd: seriesEvents.onPointEnd || chartEvents.onPointEnd,
- onPointStart: seriesEvents.onPointStart || chartEvents.onPointStart,
- pointPlayTime: (_b = (_a = chartOpts.defaultInstrumentOptions) === null || _a === void 0 ? void 0 : _a.mapping) === null || _b === void 0 ? void 0 : _b.pointPlayTime,
- masterVolume: chartOpts.masterVolume,
- instruments: getSeriesInstrumentOptions(series),
- earcons: seriesOpts.earcons || chartOpts.earcons
- };
- }
- /**
- * @private
- * @param {Highcharts.Series} series The series to get options for.
- * @param {Highcharts.SonifySeriesOptionsObject} options
- * Options to merge with user options on series/chart and default options.
- * @returns {Highcharts.SonifySeriesOptionsObject} The merged options.
- */
- function getSeriesSonifyOptions(series, options) {
- var chartOpts = series.chart.options.sonification;
- var seriesOpts = series.options.sonification;
- return merge({
- duration: (seriesOpts === null || seriesOpts === void 0 ? void 0 : seriesOpts.duration) || (chartOpts === null || chartOpts === void 0 ? void 0 : chartOpts.duration)
- }, chartOptionsToSonifySeriesOptions(series), options);
- }
- /**
- * @private
- * @param {Highcharts.Chart} chart The chart to get options for.
- * @param {Highcharts.SonificationOptions} options
- * Options to merge with user options on chart and default options.
- * @returns {Highcharts.SonificationOptions} The merged options.
- */
- function getChartSonifyOptions(chart, options) {
- var _a,
- _b,
- _c,
- _d,
- _e;
- var chartOpts = chart.options.sonification || {};
- return merge({
- duration: chartOpts.duration,
- afterSeriesWait: chartOpts.afterSeriesWait,
- pointPlayTime: (_b = (_a = chartOpts.defaultInstrumentOptions) === null || _a === void 0 ? void 0 : _a.mapping) === null || _b === void 0 ? void 0 : _b.pointPlayTime,
- order: chartOpts.order,
- onSeriesStart: (_c = chartOpts.events) === null || _c === void 0 ? void 0 : _c.onSeriesStart,
- onSeriesEnd: (_d = chartOpts.events) === null || _d === void 0 ? void 0 : _d.onSeriesEnd,
- onEnd: (_e = chartOpts.events) === null || _e === void 0 ? void 0 : _e.onEnd
- }, options);
- }
- /**
- * Options for sonifying a chart.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.SonificationOptions
- */ /**
- * Duration for sonifying the entire chart. The duration is distributed across
- * the different series intelligently, but does not take earcons into account.
- * It is also possible to set the duration explicitly per series, using
- * `seriesOptions`. Note that points may continue to play after the duration has
- * passed, but no new points will start playing.
- * @name Highcharts.SonificationOptions#duration
- * @type {number}
- */ /**
- * Define the order to play the series in. This can be given as a string, or an
- * array specifying a custom ordering. If given as a string, valid values are
- * `sequential` - where each series is played in order - or `simultaneous`,
- * where all series are played at once. For custom ordering, supply an array as
- * the order. Each element in the array can be either a string with a series ID,
- * an Earcon object, or an object with a numeric `silentWait` property
- * designating a number of milliseconds to wait before continuing. Each element
- * of the array will be played in order. To play elements simultaneously, group
- * the elements in an array.
- * @name Highcharts.SonificationOptions#order
- * @type {string|Array<string|Highcharts.Earcon|Array<string|Highcharts.Earcon>>}
- */ /**
- * The axis to use for when to play the points. Can be a string with a data
- * property (e.g. `x`), or a function. If it is a function, this function
- * receives the point as argument, and should return a numeric value. The points
- * with the lowest numeric values are then played first, and the time between
- * points will be proportional to the distance between the numeric values. This
- * option can not be overridden per series.
- * @name Highcharts.SonificationOptions#pointPlayTime
- * @type {string|Function}
- */ /**
- * Milliseconds of silent waiting to add between series. Note that waiting time
- * is considered part of the sonify duration.
- * @name Highcharts.SonificationOptions#afterSeriesWait
- * @type {number|undefined}
- */ /**
- * Options as given to `series.sonify` to override options per series. If the
- * option is supplied as an array of options objects, the `id` property of the
- * object should correspond to the series' id. If the option is supplied as a
- * single object, the options apply to all series.
- * @name Highcharts.SonificationOptions#seriesOptions
- * @type {Object|Array<object>|undefined}
- */ /**
- * The instrument definitions for the points in this chart.
- * @name Highcharts.SonificationOptions#instruments
- * @type {Array<Highcharts.PointInstrumentObject>|undefined}
- */ /**
- * Earcons to add to the chart. Note that earcons can also be added per series
- * using `seriesOptions`.
- * @name Highcharts.SonificationOptions#earcons
- * @type {Array<Highcharts.EarconConfiguration>|undefined}
- */ /**
- * Optionally provide the minimum/maximum data values for the points. If this is
- * not supplied, it is calculated from all 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.SonificationOptions#dataExtremes
- * @type {Highcharts.Dictionary<Highcharts.RangeObject>|undefined}
- */ /**
- * Callback before a series is played.
- * @name Highcharts.SonificationOptions#onSeriesStart
- * @type {Function|undefined}
- */ /**
- * Callback after a series has finished playing.
- * @name Highcharts.SonificationOptions#onSeriesEnd
- * @type {Function|undefined}
- */ /**
- * Callback after the chart has played.
- * @name Highcharts.SonificationOptions#onEnd
- * @type {Function|undefined}
- */
- /**
- * Sonify a chart.
- *
- * @sample highcharts/sonification/chart-sequential/
- * Sonify a basic chart
- * @sample highcharts/sonification/chart-simultaneous/
- * Sonify series simultaneously
- * @sample highcharts/sonification/chart-custom-order/
- * Custom defined order of series
- * @sample highcharts/sonification/chart-earcon/
- * Earcons on chart
- * @sample highcharts/sonification/chart-events/
- * Sonification events on chart
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#sonify
- *
- * @param {Highcharts.SonificationOptions} [options]
- * The options for sonifying this chart. If not provided,
- * uses options set on chart and series.
- *
- * @return {void}
- */
- function chartSonify(options) {
- var opts = getChartSonifyOptions(this,
- options);
- // Only one timeline can play at a time.
- if (this.sonification.timeline) {
- this.sonification.timeline.pause();
- }
- // Store reference to duration
- this.sonification.duration = opts.duration;
- // Calculate data extremes for the props used
- var dataExtremes = getExtremesForInstrumentProps(this,
- opts.instruments,
- opts.dataExtremes);
- // Figure out ordering of series and custom paths
- var order = buildPathOrder(opts.order,
- this,
- function (series) {
- return buildChartSonifySeriesOptions(series,
- dataExtremes,
- opts);
- });
- // Add waits after simultaneous paths with series in them.
- order = addAfterSeriesWaits(order, opts.afterSeriesWait || 0);
- // We now have a list of either TimelinePath objects or series that need to
- // be converted to TimelinePath objects. Convert everything to paths.
- var paths = buildPathsFromOrder(order,
- opts.duration);
- // Sync simultaneous paths
- paths.forEach(function (simultaneousPaths) {
- syncSimultaneousPaths(simultaneousPaths);
- });
- // We have a set of paths. Create the timeline, and play it.
- this.sonification.timeline = new H.sonification.Timeline({
- paths: paths,
- onEnd: opts.onEnd
- });
- this.sonification.timeline.play();
- }
- /**
- * Get a list of the points currently under cursor.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#getCurrentSonifyPoints
- *
- * @return {Array<Highcharts.Point>}
- * The points currently under the cursor.
- */
- function getCurrentPoints() {
- var cursorObj;
- if (this.sonification.timeline) {
- cursorObj = this.sonification.timeline.getCursor(); // Cursor per pathID
- return Object.keys(cursorObj).map(function (path) {
- // Get the event objects under cursor for each path
- return cursorObj[path].eventObject;
- }).filter(function (eventObj) {
- // Return the events that are points
- return eventObj instanceof Point;
- });
- }
- return [];
- }
- /**
- * Set the cursor to a point or set of points in different series.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#setSonifyCursor
- *
- * @param {Highcharts.Point|Array<Highcharts.Point>} points
- * The point or points to set the cursor to. If setting multiple points
- * under the cursor, the points have to be in different series that are
- * being played simultaneously.
- */
- function setCursor(points) {
- var timeline = this.sonification.timeline;
- if (timeline) {
- splat(points).forEach(function (point) {
- // We created the events with the ID of the points, which makes
- // this easy. Just call setCursor for each ID.
- timeline.setCursor(point.id);
- });
- }
- }
- /**
- * Pause the running sonification.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#pauseSonify
- *
- * @param {boolean} [fadeOut=true]
- * Fade out as we pause to avoid clicks.
- *
- * @return {void}
- */
- function pause(fadeOut) {
- if (this.sonification.timeline) {
- this.sonification.timeline.pause(pick(fadeOut, true));
- }
- else if (this.sonification.currentlyPlayingPoint) {
- this.sonification.currentlyPlayingPoint.cancelSonify(fadeOut);
- }
- }
- /**
- * Resume the currently running sonification. Requires series.sonify or
- * chart.sonify to have been played at some point earlier.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#resumeSonify
- *
- * @param {Function} onEnd
- * Callback to call when play finished.
- *
- * @return {void}
- */
- function resume(onEnd) {
- if (this.sonification.timeline) {
- this.sonification.timeline.play(onEnd);
- }
- }
- /**
- * Play backwards from cursor. Requires series.sonify or chart.sonify to have
- * been played at some point earlier.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#rewindSonify
- *
- * @param {Function} onEnd
- * Callback to call when play finished.
- *
- * @return {void}
- */
- function rewind(onEnd) {
- if (this.sonification.timeline) {
- this.sonification.timeline.rewind(onEnd);
- }
- }
- /**
- * Cancel current sonification and reset cursor.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#cancelSonify
- *
- * @param {boolean} [fadeOut=true]
- * Fade out as we pause to avoid clicks.
- *
- * @return {void}
- */
- function cancel(fadeOut) {
- this.pauseSonify(fadeOut);
- this.resetSonifyCursor();
- }
- /**
- * Reset cursor to start. Requires series.sonify or chart.sonify to have been
- * played at some point earlier.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#resetSonifyCursor
- *
- * @return {void}
- */
- function resetCursor() {
- if (this.sonification.timeline) {
- this.sonification.timeline.resetCursor();
- }
- }
- /**
- * Reset cursor to end. Requires series.sonify or chart.sonify to have been
- * played at some point earlier.
- *
- * @requires module:modules/sonification
- *
- * @function Highcharts.Chart#resetSonifyCursorEnd
- *
- * @return {void}
- */
- function resetCursorEnd() {
- if (this.sonification.timeline) {
- this.sonification.timeline.resetCursorEnd();
- }
- }
- // Export functions
- var chartSonifyFunctions = {
- chartSonify: chartSonify,
- seriesSonify: seriesSonify,
- pause: pause,
- resume: resume,
- rewind: rewind,
- cancel: cancel,
- getCurrentPoints: getCurrentPoints,
- setCursor: setCursor,
- resetCursor: resetCursor,
- resetCursorEnd: resetCursorEnd
- };
- return chartSonifyFunctions;
- });
- _registerModule(_modules, 'Extensions/Sonification/Timeline.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Extensions/Sonification/Utilities.js']], function (H, U, utilities) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * TimelineEvent class definition.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var merge = U.merge,
- splat = U.splat,
- uniqueKey = U.uniqueKey;
- /**
- * A set of options for the TimelineEvent class.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @interface Highcharts.TimelineEventOptionsObject
- */ /**
- * The object we want to sonify when playing the TimelineEvent. Can be any
- * object that implements the `sonify` and `cancelSonify` functions. If this is
- * not supplied, the TimelineEvent is considered a silent event, and the onEnd
- * event is immediately called.
- * @name Highcharts.TimelineEventOptionsObject#eventObject
- * @type {*}
- */ /**
- * Options to pass on to the eventObject when playing it.
- * @name Highcharts.TimelineEventOptionsObject#playOptions
- * @type {object|undefined}
- */ /**
- * The time at which we want this event to play (in milliseconds offset). This
- * is not used for the TimelineEvent.play function, but rather intended as a
- * property to decide when to call TimelineEvent.play. Defaults to 0.
- * @name Highcharts.TimelineEventOptionsObject#time
- * @type {number|undefined}
- */ /**
- * Unique ID for the event. Generated automatically if not supplied.
- * @name Highcharts.TimelineEventOptionsObject#id
- * @type {string|undefined}
- */ /**
- * Callback called when the play has finished.
- * @name Highcharts.TimelineEventOptionsObject#onEnd
- * @type {Function|undefined}
- */
- /* eslint-disable no-invalid-this, valid-jsdoc */
- /**
- * The TimelineEvent class. Represents a sound event on a timeline.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @class
- * @name Highcharts.TimelineEvent
- *
- * @param {Highcharts.TimelineEventOptionsObject} options
- * Options for the TimelineEvent.
- */
- function TimelineEvent(options) {
- this.init(options || {});
- }
- TimelineEvent.prototype.init = function (options) {
- this.options = options;
- this.time = options.time || 0;
- this.id = this.options.id = options.id || uniqueKey();
- };
- /**
- * Play the event. Does not take the TimelineEvent.time option into account,
- * and plays the event immediately.
- *
- * @function Highcharts.TimelineEvent#play
- *
- * @param {Highcharts.TimelineEventOptionsObject} [options]
- * Options to pass in to the eventObject when playing it.
- *
- * @return {void}
- */
- TimelineEvent.prototype.play = function (options) {
- var eventObject = this.options.eventObject,
- masterOnEnd = this.options.onEnd,
- playOnEnd = options && options.onEnd,
- playOptionsOnEnd = this.options.playOptions &&
- this.options.playOptions.onEnd,
- playOptions = merge(this.options.playOptions,
- options);
- if (eventObject && eventObject.sonify) {
- // If we have multiple onEnds defined, use all
- playOptions.onEnd = masterOnEnd || playOnEnd || playOptionsOnEnd ?
- function () {
- var args = arguments;
- [masterOnEnd, playOnEnd, playOptionsOnEnd].forEach(function (onEnd) {
- if (onEnd) {
- onEnd.apply(this, args);
- }
- });
- } : void 0;
- eventObject.sonify(playOptions);
- }
- else {
- if (playOnEnd) {
- playOnEnd();
- }
- if (masterOnEnd) {
- masterOnEnd();
- }
- }
- };
- /**
- * Cancel the sonification of this event. Does nothing if the event is not
- * currently sonifying.
- *
- * @function Highcharts.TimelineEvent#cancel
- *
- * @param {boolean} [fadeOut=false]
- * Whether or not to fade out as we stop. If false, the event is
- * cancelled synchronously.
- */
- TimelineEvent.prototype.cancel = function (fadeOut) {
- this.options.eventObject.cancelSonify(fadeOut);
- };
- /**
- * A set of options for the TimelinePath class.
- *
- * @requires module:modules/
- *
- * @private
- * @interface Highcharts.TimelinePathOptionsObject
- */ /**
- * List of TimelineEvents to play on this track.
- * @name Highcharts.TimelinePathOptionsObject#events
- * @type {Array<Highcharts.TimelineEvent>}
- */ /**
- * If this option is supplied, this path ignores all events and just waits for
- * the specified number of milliseconds before calling onEnd.
- * @name Highcharts.TimelinePathOptionsObject#silentWait
- * @type {number|undefined}
- */ /**
- * Unique ID for this timeline path. Automatically generated if not supplied.
- * @name Highcharts.TimelinePathOptionsObject#id
- * @type {string|undefined}
- */ /**
- * Callback called before the path starts playing.
- * @name Highcharts.TimelinePathOptionsObject#onStart
- * @type {Function|undefined}
- */ /**
- * Callback function to call before an event plays.
- * @name Highcharts.TimelinePathOptionsObject#onEventStart
- * @type {Function|undefined}
- */ /**
- * Callback function to call after an event has stopped playing.
- * @name Highcharts.TimelinePathOptionsObject#onEventEnd
- * @type {Function|undefined}
- */ /**
- * Callback called when the whole path is finished.
- * @name Highcharts.TimelinePathOptionsObject#onEnd
- * @type {Function|undefined}
- */
- /**
- * The TimelinePath class. Represents a track on a timeline with a list of
- * sound events to play at certain times relative to each other.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @class
- * @name Highcharts.TimelinePath
- *
- * @param {Highcharts.TimelinePathOptionsObject} options
- * Options for the TimelinePath.
- */
- function TimelinePath(options) {
- this.init(options);
- }
- TimelinePath.prototype.init = function (options) {
- this.options = options;
- this.id = this.options.id = options.id || uniqueKey();
- this.cursor = 0;
- this.eventsPlaying = {};
- // Handle silent wait, otherwise use events from options
- this.events = options.silentWait ?
- [
- new TimelineEvent({ time: 0 }),
- new TimelineEvent({ time: options.silentWait })
- ] :
- this.options.events;
- // Reference optionally provided by the user that indicates the intended
- // duration of the path. Unused by TimelinePath itself.
- this.targetDuration = options.targetDuration || options.silentWait;
- // We need to sort our events by time
- this.sortEvents();
- // Get map from event ID to index
- this.updateEventIdMap();
- // Signal events to fire
- this.signalHandler = new utilities.SignalHandler(['playOnEnd', 'masterOnEnd', 'onStart', 'onEventStart', 'onEventEnd']);
- this.signalHandler.registerSignalCallbacks(merge(options, { masterOnEnd: options.onEnd }));
- };
- /**
- * Sort the internal event list by time.
- * @private
- */
- TimelinePath.prototype.sortEvents = function () {
- this.events = this.events.sort(function (a, b) {
- return a.time - b.time;
- });
- };
- /**
- * Update the internal eventId to index map.
- * @private
- */
- TimelinePath.prototype.updateEventIdMap = function () {
- this.eventIdMap = this.events.reduce(function (acc, cur, i) {
- acc[cur.id] = i;
- return acc;
- }, {});
- };
- /**
- * Add events to the path. Should not be done while the path is playing.
- * The new events are inserted according to their time property.
- * @private
- * @param {Array<Highcharts.TimelineEvent>} newEvents - The new timeline events
- * to add.
- */
- TimelinePath.prototype.addTimelineEvents = function (newEvents) {
- this.events = this.events.concat(newEvents);
- this.sortEvents(); // Sort events by time
- this.updateEventIdMap(); // Update the event ID to index map
- };
- /**
- * Get the current TimelineEvent under the cursor.
- * @private
- * @return {Highcharts.TimelineEvent} The current timeline event.
- */
- TimelinePath.prototype.getCursor = function () {
- return this.events[this.cursor];
- };
- /**
- * Set the current TimelineEvent under the cursor.
- * @private
- * @param {string} eventId
- * The ID of the timeline event to set as current.
- * @return {boolean}
- * True if there is an event with this ID in the path. False otherwise.
- */
- TimelinePath.prototype.setCursor = function (eventId) {
- var ix = this.eventIdMap[eventId];
- if (typeof ix !== 'undefined') {
- this.cursor = ix;
- return true;
- }
- return false;
- };
- /**
- * Play the timeline from the current cursor.
- * @private
- * @param {Function} onEnd
- * Callback to call when play finished. Does not override other onEnd callbacks.
- * @return {void}
- */
- TimelinePath.prototype.play = function (onEnd) {
- this.pause();
- this.signalHandler.emitSignal('onStart');
- this.signalHandler.clearSignalCallbacks(['playOnEnd']);
- this.signalHandler.registerSignalCallbacks({ playOnEnd: onEnd });
- this.playEvents(1);
- };
- /**
- * Play the timeline backwards from the current cursor.
- * @private
- * @param {Function} onEnd
- * Callback to call when play finished. Does not override other onEnd callbacks.
- * @return {void}
- */
- TimelinePath.prototype.rewind = function (onEnd) {
- this.pause();
- this.signalHandler.emitSignal('onStart');
- this.signalHandler.clearSignalCallbacks(['playOnEnd']);
- this.signalHandler.registerSignalCallbacks({ playOnEnd: onEnd });
- this.playEvents(-1);
- };
- /**
- * Reset the cursor to the beginning.
- * @private
- */
- TimelinePath.prototype.resetCursor = function () {
- this.cursor = 0;
- };
- /**
- * Reset the cursor to the end.
- * @private
- */
- TimelinePath.prototype.resetCursorEnd = function () {
- this.cursor = this.events.length - 1;
- };
- /**
- * Cancel current playing. Leaves the cursor intact.
- * @private
- * @param {boolean} [fadeOut=false] - Whether or not to fade out as we stop. If
- * false, the path is cancelled synchronously.
- */
- TimelinePath.prototype.pause = function (fadeOut) {
- var timelinePath = this;
- // Cancel next scheduled play
- clearTimeout(timelinePath.nextScheduledPlay);
- // Cancel currently playing events
- Object.keys(timelinePath.eventsPlaying).forEach(function (id) {
- if (timelinePath.eventsPlaying[id]) {
- timelinePath.eventsPlaying[id].cancel(fadeOut);
- }
- });
- timelinePath.eventsPlaying = {};
- };
- /**
- * Play the events, starting from current cursor, and going in specified
- * direction.
- * @private
- * @param {number} direction
- * The direction to play, 1 for forwards and -1 for backwards.
- * @return {void}
- */
- TimelinePath.prototype.playEvents = function (direction) {
- var timelinePath = this,
- curEvent = timelinePath.events[this.cursor],
- nextEvent = timelinePath.events[this.cursor + direction],
- timeDiff,
- onEnd = function (signalData) {
- timelinePath.signalHandler.emitSignal('masterOnEnd',
- signalData);
- timelinePath.signalHandler.emitSignal('playOnEnd', signalData);
- };
- // Store reference to path on event
- curEvent.timelinePath = timelinePath;
- // Emit event, cancel if returns false
- if (timelinePath.signalHandler.emitSignal('onEventStart', curEvent) === false) {
- onEnd({
- event: curEvent,
- cancelled: true
- });
- return;
- }
- // Play the current event
- timelinePath.eventsPlaying[curEvent.id] = curEvent;
- curEvent.play({
- onEnd: function (cancelled) {
- var signalData = {
- event: curEvent,
- cancelled: !!cancelled
- };
- // Keep track of currently playing events for cancelling
- delete timelinePath.eventsPlaying[curEvent.id];
- // Handle onEventEnd
- timelinePath.signalHandler.emitSignal('onEventEnd', signalData);
- // Reached end of path?
- if (!nextEvent) {
- onEnd(signalData);
- }
- }
- });
- // Schedule next
- if (nextEvent) {
- timeDiff = Math.abs(nextEvent.time - curEvent.time);
- if (timeDiff < 1) {
- // Play immediately
- timelinePath.cursor += direction;
- timelinePath.playEvents(direction);
- }
- else {
- // Schedule after the difference in ms
- this.nextScheduledPlay = setTimeout(function () {
- timelinePath.cursor += direction;
- timelinePath.playEvents(direction);
- }, timeDiff);
- }
- }
- };
- /* ************************************************************************** *
- * TIMELINE *
- * ************************************************************************** */
- /**
- * A set of options for the Timeline class.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @interface Highcharts.TimelineOptionsObject
- */ /**
- * List of TimelinePaths to play. Multiple paths can be grouped together and
- * played simultaneously by supplying an array of paths in place of a single
- * path.
- * @name Highcharts.TimelineOptionsObject#paths
- * @type {Array<(Highcharts.TimelinePath|Array<Highcharts.TimelinePath>)>}
- */ /**
- * Callback function to call before a path plays.
- * @name Highcharts.TimelineOptionsObject#onPathStart
- * @type {Function|undefined}
- */ /**
- * Callback function to call after a path has stopped playing.
- * @name Highcharts.TimelineOptionsObject#onPathEnd
- * @type {Function|undefined}
- */ /**
- * Callback called when the whole path is finished.
- * @name Highcharts.TimelineOptionsObject#onEnd
- * @type {Function|undefined}
- */
- /**
- * The Timeline class. Represents a sonification timeline with a list of
- * timeline paths with events to play at certain times relative to each other.
- *
- * @requires module:modules/sonification
- *
- * @private
- * @class
- * @name Highcharts.Timeline
- *
- * @param {Highcharts.TimelineOptionsObject} options
- * Options for the Timeline.
- */
- function Timeline(options) {
- this.init(options || {});
- }
- Timeline.prototype.init = function (options) {
- this.options = options;
- this.cursor = 0;
- this.paths = options.paths || [];
- this.pathsPlaying = {};
- this.signalHandler = new utilities.SignalHandler(['playOnEnd', 'masterOnEnd', 'onPathStart', 'onPathEnd']);
- this.signalHandler.registerSignalCallbacks(merge(options, { masterOnEnd: options.onEnd }));
- };
- /**
- * Play the timeline forwards from cursor.
- * @private
- * @param {Function} [onEnd]
- * Callback to call when play finished. Does not override other onEnd callbacks.
- * @return {void}
- */
- Timeline.prototype.play = function (onEnd) {
- this.pause();
- this.signalHandler.clearSignalCallbacks(['playOnEnd']);
- this.signalHandler.registerSignalCallbacks({ playOnEnd: onEnd });
- this.playPaths(1);
- };
- /**
- * Play the timeline backwards from cursor.
- * @private
- * @param {Function} onEnd
- * Callback to call when play finished. Does not override other onEnd callbacks.
- * @return {void}
- */
- Timeline.prototype.rewind = function (onEnd) {
- this.pause();
- this.signalHandler.clearSignalCallbacks(['playOnEnd']);
- this.signalHandler.registerSignalCallbacks({ playOnEnd: onEnd });
- this.playPaths(-1);
- };
- /**
- * Play the timeline in the specified direction.
- * @private
- * @param {number} direction
- * Direction to play in. 1 for forwards, -1 for backwards.
- * @return {void}
- */
- Timeline.prototype.playPaths = function (direction) {
- var timeline = this;
- var signalHandler = timeline.signalHandler;
- if (!timeline.paths.length) {
- var emptySignal = {
- cancelled: false
- };
- signalHandler.emitSignal('playOnEnd', emptySignal);
- signalHandler.emitSignal('masterOnEnd', emptySignal);
- return;
- }
- var curPaths = splat(this.paths[this.cursor]),
- nextPaths = this.paths[this.cursor + direction],
- pathsEnded = 0,
- // Play a path
- playPath = function (path) {
- // Emit signal and set playing state
- signalHandler.emitSignal('onPathStart',
- path);
- timeline.pathsPlaying[path.id] = path;
- // Do the play
- path[direction > 0 ? 'play' : 'rewind'](function (callbackData) {
- // Play ended callback
- // Data to pass to signal callbacks
- var cancelled = callbackData && callbackData.cancelled,
- signalData = {
- path: path,
- cancelled: cancelled
- };
- // Clear state and send signal
- delete timeline.pathsPlaying[path.id];
- signalHandler.emitSignal('onPathEnd', signalData);
- // Handle next paths
- pathsEnded++;
- if (pathsEnded >= curPaths.length) {
- // We finished all of the current paths for cursor.
- if (nextPaths && !cancelled) {
- // We have more paths, move cursor along
- timeline.cursor += direction;
- // Reset upcoming path cursors before playing
- splat(nextPaths).forEach(function (nextPath) {
- nextPath[direction > 0 ? 'resetCursor' : 'resetCursorEnd']();
- });
- // Play next
- timeline.playPaths(direction);
- }
- else {
- // If it is the last path in this direction, call onEnd
- signalHandler.emitSignal('playOnEnd', signalData);
- signalHandler.emitSignal('masterOnEnd', signalData);
- }
- }
- });
- };
- // Go through the paths under cursor and play them
- curPaths.forEach(function (path) {
- if (path) {
- // Store reference to timeline
- path.timeline = timeline;
- // Leave a timeout to let notes fade out before next play
- setTimeout(function () {
- playPath(path);
- }, H.sonification.fadeOutDuration);
- }
- });
- };
- /**
- * Stop the playing of the timeline. Cancels all current sounds, but does not
- * affect the cursor.
- * @private
- * @param {boolean} [fadeOut=false]
- * Whether or not to fade out as we stop. If false, the timeline is cancelled
- * synchronously.
- * @return {void}
- */
- Timeline.prototype.pause = function (fadeOut) {
- var timeline = this;
- // Cancel currently playing events
- Object.keys(timeline.pathsPlaying).forEach(function (id) {
- if (timeline.pathsPlaying[id]) {
- timeline.pathsPlaying[id].pause(fadeOut);
- }
- });
- timeline.pathsPlaying = {};
- };
- /**
- * Reset the cursor to the beginning of the timeline.
- * @private
- * @return {void}
- */
- Timeline.prototype.resetCursor = function () {
- this.paths.forEach(function (paths) {
- splat(paths).forEach(function (path) {
- path.resetCursor();
- });
- });
- this.cursor = 0;
- };
- /**
- * Reset the cursor to the end of the timeline.
- * @private
- * @return {void}
- */
- Timeline.prototype.resetCursorEnd = function () {
- this.paths.forEach(function (paths) {
- splat(paths).forEach(function (path) {
- path.resetCursorEnd();
- });
- });
- this.cursor = this.paths.length - 1;
- };
- /**
- * Set the current TimelineEvent under the cursor. If multiple paths are being
- * played at the same time, this function only affects a single path (the one
- * that contains the eventId that is passed in).
- * @private
- * @param {string} eventId
- * The ID of the timeline event to set as current.
- * @return {boolean}
- * True if the cursor was set, false if no TimelineEvent was found for this ID.
- */
- Timeline.prototype.setCursor = function (eventId) {
- return this.paths.some(function (paths) {
- return splat(paths).some(function (path) {
- return path.setCursor(eventId);
- });
- });
- };
- /**
- * Get the current TimelineEvents under the cursors. This function will return
- * the event under the cursor for each currently playing path, as an object
- * where the path ID is mapped to the TimelineEvent under that path's cursor.
- * @private
- * @return {Highcharts.Dictionary<Highcharts.TimelineEvent>}
- * The TimelineEvents under each path's cursors.
- */
- Timeline.prototype.getCursor = function () {
- return this.getCurrentPlayingPaths().reduce(function (acc, cur) {
- acc[cur.id] = cur.getCursor();
- return acc;
- }, {});
- };
- /**
- * Check if timeline is reset or at start.
- * @private
- * @return {boolean}
- * True if timeline is at the beginning.
- */
- Timeline.prototype.atStart = function () {
- if (this.cursor) {
- return false;
- }
- return !splat(this.paths[0]).some(function (path) {
- return path.cursor;
- });
- };
- /**
- * Get the current TimelinePaths being played.
- * @private
- * @return {Array<Highcharts.TimelinePath>}
- * The TimelinePaths currently being played.
- */
- Timeline.prototype.getCurrentPlayingPaths = function () {
- if (!this.paths.length) {
- return [];
- }
- return splat(this.paths[this.cursor]);
- };
- // Export the classes
- var timelineClasses = {
- TimelineEvent: TimelineEvent,
- TimelinePath: TimelinePath,
- Timeline: Timeline
- };
- return timelineClasses;
- });
- _registerModule(_modules, 'Extensions/Sonification/Options.js', [], function () {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Default options for sonification.
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- // Experimental, disabled by default, not exposed in API
- var options = {
- sonification: {
- enabled: false,
- duration: 2500,
- afterSeriesWait: 700,
- masterVolume: 1,
- order: 'sequential',
- defaultInstrumentOptions: {
- instrument: 'sineMusical',
- // Start at G4 note, end at C6
- minFrequency: 392,
- maxFrequency: 1046,
- mapping: {
- pointPlayTime: 'x',
- duration: 200,
- frequency: 'y'
- }
- }
- }
- };
- return options;
- });
- _registerModule(_modules, 'Extensions/Sonification/Sonification.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js'], _modules['Extensions/Sonification/Instrument.js'], _modules['Extensions/Sonification/InstrumentDefinitions.js'], _modules['Extensions/Sonification/Earcon.js'], _modules['Extensions/Sonification/PointSonify.js'], _modules['Extensions/Sonification/ChartSonify.js'], _modules['Extensions/Sonification/Utilities.js'], _modules['Extensions/Sonification/Timeline.js'], _modules['Extensions/Sonification/Options.js']], function (Chart, H, O, Point, Series, U, Instrument, instruments, Earcon, pointSonifyFunctions, chartSonifyFunctions, utilities, TimelineClasses, sonificationOptions) {
- /* *
- *
- * (c) 2009-2021 Øystein Moseng
- *
- * Sonification module for Highcharts
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var defaultOptions = O.defaultOptions;
- var addEvent = U.addEvent,
- extend = U.extend,
- merge = U.merge;
- // Expose on the Highcharts object
- /**
- * Global classes and objects related to sonification.
- *
- * @requires module:modules/sonification
- *
- * @name Highcharts.sonification
- * @type {Highcharts.SonificationObject}
- */
- /**
- * Global classes and objects related to sonification.
- *
- * @requires module:modules/sonification
- *
- * @interface Highcharts.SonificationObject
- */ /**
- * Note fade-out-time in milliseconds. Most notes are faded out quickly by
- * default if there is time. This is to avoid abrupt stops which will cause
- * perceived clicks.
- * @name Highcharts.SonificationObject#fadeOutDuration
- * @type {number}
- */ /**
- * Utility functions.
- * @name Highcharts.SonificationObject#utilities
- * @private
- * @type {object}
- */ /**
- * The Instrument class.
- * @name Highcharts.SonificationObject#Instrument
- * @type {Function}
- */ /**
- * Predefined instruments, given as an object with a map between the instrument
- * name and the Highcharts.Instrument object.
- * @name Highcharts.SonificationObject#instruments
- * @type {Object}
- */ /**
- * The Earcon class.
- * @name Highcharts.SonificationObject#Earcon
- * @type {Function}
- */ /**
- * The TimelineEvent class.
- * @private
- * @name Highcharts.SonificationObject#TimelineEvent
- * @type {Function}
- */ /**
- * The TimelinePath class.
- * @private
- * @name Highcharts.SonificationObject#TimelinePath
- * @type {Function}
- */ /**
- * The Timeline class.
- * @private
- * @name Highcharts.SonificationObject#Timeline
- * @type {Function}
- */
- H.sonification = {
- fadeOutDuration: 20,
- // Classes and functions
- utilities: utilities,
- Instrument: Instrument,
- instruments: instruments,
- Earcon: Earcon,
- TimelineEvent: TimelineClasses.TimelineEvent,
- TimelinePath: TimelineClasses.TimelinePath,
- Timeline: TimelineClasses.Timeline
- };
- // Add default options
- merge(true, defaultOptions, sonificationOptions);
- // Chart specific
- Point.prototype.sonify = pointSonifyFunctions.pointSonify;
- Point.prototype.cancelSonify = pointSonifyFunctions.pointCancelSonify;
- Series.prototype.sonify = chartSonifyFunctions.seriesSonify;
- extend(Chart.prototype, {
- sonify: chartSonifyFunctions.chartSonify,
- pauseSonify: chartSonifyFunctions.pause,
- resumeSonify: chartSonifyFunctions.resume,
- rewindSonify: chartSonifyFunctions.rewind,
- cancelSonify: chartSonifyFunctions.cancel,
- getCurrentSonifyPoints: chartSonifyFunctions.getCurrentPoints,
- setSonifyCursor: chartSonifyFunctions.setCursor,
- resetSonifyCursor: chartSonifyFunctions.resetCursor,
- resetSonifyCursorEnd: chartSonifyFunctions.resetCursorEnd
- });
- /* eslint-disable no-invalid-this */
- // Prepare charts for sonification on init
- addEvent(Chart, 'init', function () {
- this.sonification = {};
- });
- // Update with chart/series/point updates
- addEvent(Chart, 'update', function (e) {
- var newOptions = e.options.sonification;
- if (newOptions) {
- merge(true, this.options.sonification, newOptions);
- }
- });
- });
- _registerModule(_modules, 'masters/modules/sonification.src.js', [], function () {
- });
- }));
|