/**
 * @license Highcharts JS v11.1.0 (2023-06-05)
 *
 * Sonification module
 *
 * (c) 2010-2022 Highsoft AS
 * Author: Øystein Moseng
 *
 * License: www.highcharts.com/license
 */
(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) {
    'use strict';
    var _modules = Highcharts ? Highcharts._modules : {};
    function _registerModule(obj, path, args, fn) {
        if (!obj.hasOwnProperty(path)) {
            obj[path] = fn.apply(null, args);

            if (typeof CustomEvent === 'function') {
                window.dispatchEvent(
                    new CustomEvent(
                        'HighchartsModuleLoaded',
                        { detail: { path: path, module: obj[path] }
                    })
                );
            }
        }
    }
    _registerModule(_modules, 'Extensions/Sonification/Options.js', [], function () {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Default options for sonification.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const Options = {
            /**
             * Options for configuring sonification and audio charts. Requires the
             * [sonification module](https://code.highcharts.com/modules/sonification.js)
             * to be loaded.
             *
             * @sample  highcharts/demo/all-instruments
             *          All predefined instruments
             * @sample  highcharts/demo/audio-boxplot
             *          Audio boxplots
             * @sample  highcharts/demo/plotline-context
             *          Context tracks
             * @sample  highcharts/demo/sonification-music
             *          Musical chart
             *
             * @since 11.0.0
             * @requires     modules/sonification
             * @optionparent sonification
             */
            sonification: {
                /**
                 * Global tracks to add to every series.
                 *
                 * Defined as an array of either instrument or speech tracks,
                 * or a combination.
                 *
                 * @type {Array<*>}
                 * @extends sonification.defaultSpeechOptions
                 * @extends sonification.defaultInstrumentOptions
                 * @apioption sonification.globalTracks
                 */
                /**
                 * Rate mapping for speech tracks.
                 * @extends sonification.defaultSpeechOptions.mapping.rate
                 * @apioption sonification.globalTracks.mapping.rate
                 */
                /**
                 * Text mapping for speech tracks.
                 * @extends sonification.defaultSpeechOptions.mapping.text
                 * @apioption sonification.globalTracks.mapping.text
                 */
                /**
                 * Context tracks to add globally, an array of either instrument
                 * tracks, speech tracks, or a mix.
                 *
                 * Context tracks are not tied to data points, but play at a set
                 * interval - either based on time or on prop values.
                 *
                 * @sample  highcharts/demo/plotline-context
                 *          Using contexts
                 * @type {Array<*>}
                 * @extends sonification.globalTracks
                 * @apioption sonification.globalContextTracks
                 */
                /**
                 * Set a context track to play periodically every `timeInterval`
                 * milliseconds while the sonification is playing.
                 *
                 * @sample  highcharts/demo/plotline-context
                 *          Using contexts
                 * @type {number}
                 * @apioption sonification.globalContextTracks.timeInterval
                 */
                /**
                 * Set a context track to play periodically every `valueInterval`
                 * units of a data property `valueProp` while the sonification is
                 * playing.
                 *
                 * For example, setting `valueProp` to `x` and `valueInterval` to 5
                 * will play the context track for every 5th X value.
                 *
                 * The context audio events will be mapped to time according to the
                 * prop value relative to the min/max values for that prop.
                 *
                 * @sample  highcharts/demo/plotline-context
                 *          Using contexts
                 * @type {number}
                 * @apioption sonification.globalContextTracks.valueInterval
                 */
                /**
                 * The point property to play context for when using `valueInterval`.
                 * @type {string}
                 * @default "x"
                 * @apioption sonification.globalContextTracks.valueProp
                 */
                /**
                 * How to map context events to time when using the `valueInterval`
                 * option.
                 * @type {"linear"|"logarithmic"}
                 * @default "linear"
                 * @apioption sonification.globalContextTracks.valueMapFunction
                 */
                /**
                 * Set up event handlers for the sonification
                 * @apioption sonification.events
                 */
                /**
                 * Called on play.
                 *
                 * A context object is passed to the function, with properties `chart`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onPlay
                 */
                /**
                 * Called on pause, cancel, or if play is completed.
                 *
                 * A context object is passed to the function, with properties `chart`,
                 * `timeline` and `pointsPlayed`. `pointsPlayed` is an array of `Point`
                 * objects, referencing data points that were related to the audio
                 * events played.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onStop
                 */
                /**
                 * Called when play is completed.
                 *
                 * A context object is passed to the function, with properties `chart`,
                 * `timeline` and `pointsPlayed`. `pointsPlayed` is an array of `Point`
                 * objects, referencing data points that were related to the audio
                 * events played.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onEnd
                 */
                /**
                 * Called immediately when a play is requested.
                 *
                 * A context object is passed to the function, with properties `chart`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.beforePlay
                 */
                /**
                 * Called before updating the sonification.
                 *
                 * A context object is passed to the function, with properties `chart`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.beforeUpdate
                 */
                /**
                 * Called after updating the sonification.
                 *
                 * A context object is passed to the function, with properties `chart`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.afterUpdate
                 */
                /**
                 * Called on the beginning of playing a series.
                 *
                 * A context object is passed to the function, with properties `series`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onSeriesStart
                 */
                /**
                 * Called when finished playing a series.
                 *
                 * A context object is passed to the function, with properties `series`
                 * and `timeline`.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onSeriesEnd
                 */
                /**
                 * Called when attempting to play an adjacent point or series, and
                 * there is none.
                 *
                 * By default a percussive sound is played.
                 *
                 * A context object is passed to the function, with properties `chart`,
                 * `timeline`, and `attemptedNext`. `attemptedNext` is a boolean
                 * property that is `true` if the boundary hit was from trying to play
                 * the next series/point, and `false` if it was from trying to play the
                 * previous.
                 *
                 * @type {Function}
                 * @apioption sonification.events.onBoundaryHit
                 */
                /**
                 * Enable sonification functionality for the chart.
                 */
                enabled: true,
                /**
                 * The total duration of the sonification, in milliseconds.
                 */
                duration: 6000,
                /**
                 * The time to wait in milliseconds after each data series when playing
                 * the series one after the other.
                 * @see [order](#sonification.order)
                 * @sample  highcharts/sonification/chart-earcon
                 *          Notification after series
                 */
                afterSeriesWait: 700,
                /**
                 * How long to wait between each recomputation of the sonification, if
                 * the chart updates rapidly. This avoids slowing down processes like
                 * panning. Given in milliseconds.
                 */
                updateInterval: 200,
                /**
                 * Overall/master volume for the sonification, from 0 to 1.
                 */
                masterVolume: 0.7,
                /**
                 * What order to play the data series in, either `sequential` where
                 * the series play individually one after the other, or `simultaneous`
                 * where the series play all at once.
                 * @sample  highcharts/sonification/chart-simultaneous
                 *          Simultaneous sonification
                 * @type  {"sequential"|"simultaneous"}
                 */
                order: 'sequential',
                /**
                 * Show tooltip as the chart plays.
                 *
                 * Note that if multiple tracks that play at different times try to
                 * show the tooltip, it can be glitchy, so it is recommended in
                 * those cases to turn this on/off for individual tracks using the
                 * [showPlayMarker](#plotOptions.series.sonification.tracks.showPlayMarker)
                 * option.
                 *
                 * @see [showCrosshair](#sonification.showCrosshair)
                 */
                showTooltip: true,
                /**
                 * Show X and Y axis crosshairs (if they exist) as the chart plays.
                 *
                 * Note that if multiple tracks that play at different times try to
                 * show the crosshairs, it can be glitchy, so it is recommended in
                 * those cases to turn this on/off for individual tracks using the
                 * [showPlayMarker](#plotOptions.series.sonification.tracks.showPlayMarker)
                 * option.
                 *
                 * @see [showTooltip](#sonification.showTooltip)
                 * @see [crosshair](#xAxis.crosshair)
                 */
                showCrosshair: true,
                /**
                 * Options for grouping data points together when sonifying. This
                 * allows for the visual presentation to contain more points than what
                 * is being played. If not enabled, all visible / uncropped points are
                 * played.
                 *
                 * @see [series.cropThreshold](#plotOptions.series.cropThreshold)
                 */
                pointGrouping: {
                    /**
                     * Whether or not to group points
                     */
                    enabled: true,
                    /**
                     * The size of each group in milliseconds. Audio events closer than
                     * this are grouped together.
                     */
                    groupTimespan: 15,
                    /**
                     * The grouping algorithm, deciding which points to keep when
                     * grouping a set of points together. By default `"minmax"` is
                     * used, which keeps both the minimum and maximum points.
                     *
                     * The other algorithms will either keep the first point in the
                     * group (time wise), last point, middle point, or both the first
                     * and the last point.
                     *
                     * The timing of the resulting point(s) is then adjusted to play
                     * evenly, regardless of its original position within the group.
                     *
                     * @type {"minmax"|"first"|"last"|"middle"|"firstlast"}
                     */
                    algorithm: 'minmax',
                    /**
                     * The data property for each point to compare when deciding which
                     * points to keep in the group.
                     *
                     * By default it is "y", which means that if the `"minmax"`
                     * algorithm is used, the two points the group with the lowest and
                     * highest `y` value will be kept, and the others not played.
                     */
                    prop: 'y'
                },
                /**
                 * Default sonification options for all instrument tracks.
                 *
                 * If specific options are also set on individual tracks or per
                 * series, those will override these options.
                 *
                 * @sample  highcharts/sonification/point-sonify
                 *          Sonify points on click
                 */
                defaultInstrumentOptions: {
                    /**
                     * Round pitch mapping to musical notes.
                     *
                     * If `false`, will play the exact mapped note, even if it is out
                     * of tune compared to the musical notes as defined by 440Hz
                     * standard tuning.
                     */
                    roundToMusicalNotes: true,
                    /**
                     * Type of track. Always `"instrument"` for instrument tracks, and
                     * `"speech"` for speech tracks.
                     * @type {"instrument"|"speech"}
                     * @default instrument
                     * @apioption sonification.defaultInstrumentOptions.type
                     */
                    /**
                     * Show play marker (tooltip and/or crosshair) for a track.
                     * @type {boolean}
                     * @default true
                     * @apioption sonification.defaultInstrumentOptions.showPlayMarker
                     */
                    /**
                     * Name to use for a track when exporting to MIDI.
                     * By default it uses the series name if the track is related to
                     * a series.
                     * @type {string}
                     * @apioption sonification.defaultInstrumentOptions.midiName
                     */
                    /**
                     * Options for point grouping, specifically for instrument tracks.
                     * @extends sonification.pointGrouping
                     * @apioption sonification.defaultInstrumentOptions.pointGrouping
                     */
                    /**
                     * Define a condition for when a track should be active and not.
                     *
                     * Can either be a function callback or a configuration object.
                     *
                     * If a function is used, it should return a `boolean` for whether
                     * or not the track should be active. The function is called for
                     * each audio event, and receives a parameter object with `time`,
                     * and potentially `point` and `value` properties depending on the
                     * track. `point` is available if the audio event is related to a
                     * data point. `value` is available if the track is used as a
                     * context track, and `valueInterval` is used.
                     *
                     * @sample  highcharts/sonification/mapping-zones
                     *          Mapping zones
                     * @type {Function|object}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen
                     */
                    /**
                     * Track is only active when `prop` is above or at this value.
                     * @type {number}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen.min
                     */
                    /**
                     * Track is only active when `prop` is below or at this value.
                     * @type {number}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen.max
                     */
                    /**
                     * Track is only active when `prop` was below, and is now at or
                     * above this value.
                     *
                     * If both `crossingUp` and `crossingDown` are defined, the track
                     * is active if either condition is met.
                     * @type {number}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen.crossingUp
                     */
                    /**
                     * Track is only active when `prop` was above, and is now at or
                     * below this value.
                     *
                     * If both `crossingUp` and `crossingDown` are defined, the track
                     * is active if either condition is met.
                     * @type {number}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen.crossingDown
                     */
                    /**
                     * The point property to compare, for example `y` or `x`.
                     * @type {string}
                     * @apioption sonification.defaultInstrumentOptions.activeWhen.prop
                     */
                    /**
                     * Instrument to use for playing.
                     *
                     * Can either be a string referencing a synth preset, or it can be
                     * a synth configuration object.
                     *
                     * @sample  highcharts/demo/all-instruments
                     *          Overview of available presets
                     * @sample  highcharts/sonification/custom-instrument
                     *          Custom instrument
                     *
                     * @type {string|Highcharts.SynthPatchOptionsObject}
                     */
                    instrument: 'piano',
                    /**
                     * Mapping options for the audio parameters.
                     *
                     * All parameters can be either:
                     *  - A string, referencing a point property to map to.
                     *  - A number, setting the value of the audio parameter directly.
                     *  - A callback function, returning the value programmatically.
                     *  - An object defining detailed configuration of the mapping.
                     *
                     * If a function is used, it should return the desired value for
                     * the audio parameter. The function is called for each audio event
                     * to be played, and receives a context object parameter with
                     * `time`, and potentially `point` and `value` depending on the
                     * track. `point` is available if the audio event is related to a
                     * data point, and `value` is available if the track is used for a
                     * context track using `valueInterval`.
                     *
                     * @sample  highcharts/sonification/mapping-overview
                     *          Overview of common mapping parameters
                     * @sample  highcharts/sonification/pitch-mapping
                     *          Various types of mapping used
                     * @sample  highcharts/sonification/polarity-invert
                     *          Inverted mapping to property
                     * @sample  highcharts/sonification/log-mapping
                     *          Logarithmic mapping to property
                     */
                    mapping: {
                        /**
                         * The volume of notes, from 0 to 1.
                         * @default 1
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.volume
                         */
                        /**
                         * Frequency in Hertz of notes. Overrides pitch mapping if set.
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.frequency
                         */
                        /**
                         * Milliseconds to wait before playing, comes in addition to
                         * the time determined by the `time` mapping.
                         *
                         * Can also be negative to play before the mapped time.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.playDelay
                         */
                        /**
                         * Mapping options for tremolo effects.
                         *
                         * Tremolo is repeated changes of volume over time.
                         *
                         * @apioption sonification.defaultInstrumentOptions.mapping.tremolo
                         */
                        /**
                         * Map to tremolo depth, from 0 to 1.
                         *
                         * This determines the intensity of the tremolo effect, how
                         * much the volume changes.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.tremolo.depth
                         */
                        /**
                         * Map to tremolo speed, from 0 to 1.
                         *
                         * This determines the speed of the tremolo effect, how fast
                         * the volume changes.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.tremolo.speed
                         */
                        /**
                         * Mapping options for the lowpass filter.
                         *
                         * A lowpass filter lets low frequencies through, but stops high
                         * frequencies, making the sound more dull.
                         *
                         * @apioption sonification.defaultInstrumentOptions.mapping.lowpass
                         */
                        /**
                         * Map to filter frequency in Hertz from 1 to 20,000Hz.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.lowpass.frequency
                         */
                        /**
                         * Map to filter resonance in dB. Can be negative to cause a
                         * dip, or positive to cause a bump.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultInstrumentOptions.mapping.lowpass.resonance
                         */
                        /**
                         * Mapping options for the highpass filter.
                         *
                         * A highpass filter lets high frequencies through, but stops
                         * low frequencies, making the sound thinner.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.lowpass
                         * @apioption sonification.defaultInstrumentOptions.mapping.highpass
                         */
                        /**
                         * Time mapping determines what time each point plays. It is
                         * defined as an offset in milliseconds, where 0 means it
                         * plays immediately when the chart is sonified.
                         *
                         * By default time is mapped to `x`, meaning points with the
                         * lowest `x` value plays first, and points with the highest
                         * `x` value plays last.
                         *
                         * Can be set to a fixed value, a prop to map to, a function,
                         * or a mapping object.
                         *
                         * @sample  highcharts/sonification/point-play-time
                         *          Play points in order of Y value
                         * @default "x"
                         * @type {string|number|Function|object}
                         */
                        time: 'x',
                        /**
                         * A point property to map the mapping parameter to.
                         *
                         * A negative sign `-` can be placed before the property name
                         * to make mapping inverted.
                         *
                         * @sample  highcharts/sonification/polarity-invert
                         *          Inverted mapping to property
                         * @type {string}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.mapTo
                         */
                        /**
                         * The minimum value for the audio parameter. This is the
                         * lowest value the audio parameter will be mapped to.
                         * @type {number}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.min
                         */
                        /**
                         * The maximum value for the audio parameter. This is the
                         * highest value the audio parameter will be mapped to.
                         * @type {number}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.max
                         */
                        /**
                         * What data values to map the parameter within.
                         *
                         * Mapping within `"series"` will make the lowest value point
                         * in the series map to the min audio parameter value, and the
                         * highest value will map to the max audio parameter.
                         *
                         * Mapping within `"chart"` will make the lowest value point in
                         * the whole chart map to the min audio parameter value, and
                         * the highest value in the whole chart will map to the max
                         * audio parameter.
                         *
                         * You can also map within the X or Y axis of each series.
                         *
                         * @sample  highcharts/sonification/mapping-within
                         *          Mapping within demonstrated
                         * @type {"chart"|"series"|"xAxis"|"yAxis"}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.within
                         */
                        /**
                         * How to perform the mapping.
                         * @sample  highcharts/sonification/log-mapping
                         *          Logarithmic mapping to property
                         * @type {"linear"|"logarithmic"}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.mapFunction
                         */
                        /**
                         * A fixed value to use for the prop when mapping.
                         *
                         * For example, if mapping to `y`, setting value to `4` will
                         * map as if all points had a y value of 4.
                         *
                         * @sample  highcharts/demo/plotline-context
                         *          Map to fixed y value
                         * @type {number}
                         * @apioption sonification.defaultInstrumentOptions.mapping.time.value
                         */
                        /**
                         * Pan refers to the stereo panning position of the sound.
                         * It is defined from -1 (left) to 1 (right).
                         *
                         * By default it is mapped to `x`, making the sound move from
                         * left to right as the chart plays.
                         *
                         * Can be set to a fixed value, a prop to map to, a function,
                         * or a mapping object.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @default "x"
                         */
                        pan: 'x',
                        /**
                         * Note duration determines for how long a note plays, in
                         * milliseconds.
                         *
                         * It only affects instruments that are able to play
                         * continuous sustained notes. Examples of these instruments
                         * from the presets include `flute`, `saxophone`, `trumpet`,
                         * `sawsynth`, `wobble`, `basic1`, `basic2`, `sine`,
                         * `sineGlide`, `triangle`, `square`, `sawtooth`, `noise`,
                         * `filteredNoise`, and `wind`.
                         *
                         * Can be set to a fixed value, a prop to map to, a function,
                         * or a mapping object.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @default 200
                         */
                        noteDuration: 200,
                        /**
                         * Musical pitch refers to how high or low notes are played.
                         *
                         * By default it is mapped to `y` so low `y` values are played
                         * with a lower pitch, and high values are played with a higher
                         * pitch.
                         *
                         * Pitch mapping has a few extra features compared to other
                         * audio parameters.
                         *
                         * Firstly, it accepts not only number values, but also string
                         * values denoting note names. These are given in the form
                         * `<note><octave>`, for example `"c6"` or `"F#2"`.
                         *
                         * Secondly, it is possible to map pitch to an array of notes.
                         * In this case, the `[gapBetweenNotes](#sonification.defaultInstrumentOptions.mapping.gapBetweenNotes)`
                         * mapping determines the delay between these notes.
                         *
                         * Thirdly, it is possible to define a musical scale to follow
                         * when mapping.
                         *
                         * Can be set to a fixed value, an array, a prop to map to, a
                         * function, or a mapping object.
                         *
                         * @sample  highcharts/sonification/pitch-mapping
                         *          Various types of mapping used
                         * @sample  highcharts/sonification/polarity-invert
                         *          Inverted mapping to property
                         * @sample  highcharts/sonification/log-mapping
                         *          Logarithmic mapping to property
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @type {string|number|Function|object|Array<string|number>}
                         */
                        pitch: {
                            mapTo: 'y',
                            min: 'c2',
                            max: 'c6',
                            within: 'yAxis'
                        },
                        /**
                         * Map pitches to a musical scale. The scale is defined as an
                         * array of semitone offsets from the root note.
                         *
                         * @sample  highcharts/sonification/all-scales
                         *          Predefined scale presets
                         * @type {Array<number>}
                         * @apioption sonification.defaultInstrumentOptions.mapping.pitch.scale
                         */
                        /**
                         * Gap in milliseconds between notes if pitch is mapped to an
                         * array of notes.
                         *
                         * Can be set to a fixed value, a prop to map to, a function,
                         * or a mapping object.
                         *
                         * @sample  maps/demo/audio-map
                         *          Mapping to gap between notes
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @default 100
                         */
                        gapBetweenNotes: 100
                    }
                },
                /**
                 * Default sonification options for all speech tracks.
                 *
                 * If specific options are also set on individual tracks or per
                 * series, those will override these options.
                 *
                 * @sample  highcharts/sonification/speak-values
                 *          Speak values
                 * @extends sonification.defaultInstrumentOptions
                 * @excluding roundToMusicalNotes, midiName, instrument
                 */
                defaultSpeechOptions: {
                    /**
                     * Type of track. Always `"instrument"` for instrument tracks, and
                     * `"speech"` for speech tracks.
                     * @type {"instrument"|"speech"}
                     * @default speech
                     * @apioption sonification.defaultSpeechOptions.type
                     */
                    /**
                     * Name of the voice synthesis to prefer for speech tracks.
                     *
                     * If not available, falls back to the default voice for the
                     * selected language.
                     *
                     * Different platforms provide different voices for web speech
                     * synthesis.
                     *
                     * @type {string}
                     * @apioption sonification.defaultSpeechOptions.preferredVoice
                     */
                    /**
                     * The language to speak in for speech tracks, as an IETF BCP 47
                     * language tag.
                     *
                     * @sample  maps/demo/audio-map
                     *          French language speech
                     */
                    language: 'en-US',
                    /**
                     * Mapping configuration for the speech/audio parameters.
                     *
                     * All parameters except `text` can be either:
                     *  - A string, referencing a point property to map to.
                     *  - A number, setting the value of the speech parameter directly.
                     *  - A callback function, returning the value programmatically.
                     *  - An object defining detailed configuration of the mapping.
                     *
                     * If a function is used, it should return the desired value for
                     * the speech parameter. The function is called for each speech
                     * event to be played, and receives a context object parameter with
                     * `time`, and potentially `point` and `value` depending on the
                     * track. `point` is available if the audio event is related to a
                     * data point, and `value` is available if the track is used for a
                     * context track using `valueInterval`.
                     *
                     * @extends sonification.defaultInstrumentOptions.mapping
                     * @excluding frequency, gapBetweenNotes, highpass, lowpass, tremolo,
                     *  noteDuration, pan
                     * @apioption sonification.defaultSpeechOptions.mapping
                     */
                    mapping: {
                        /**
                         * Milliseconds to wait before playing, comes in addition to
                         * the time determined by the `time` mapping.
                         *
                         * Can also be negative to play before the mapped time.
                         *
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @apioption sonification.defaultSpeechOptions.mapping.playDelay
                         */
                        /**
                         * Speech pitch (how high/low the voice is) multiplier.
                         * @sample  highcharts/sonification/speak-values
                         *          Speak values
                         * @default 1
                         * @type {string|number|Function|object}
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @excluding scale
                         * @apioption sonification.defaultSpeechOptions.mapping.pitch
                         */
                        /**
                         * @default undefined
                         * @apioption sonification.defaultSpeechOptions.mapping.pitch.mapTo
                         */
                        /**
                         * @default undefined
                         * @apioption sonification.defaultSpeechOptions.mapping.pitch.min
                         */
                        /**
                         * @default undefined
                         * @apioption sonification.defaultSpeechOptions.mapping.pitch.max
                         */
                        /**
                         * @default undefined
                         * @apioption sonification.defaultSpeechOptions.mapping.pitch.within
                         */
                        /**
                         * The text to announce for speech tracks. Can either be a
                         * format string or a function.
                         *
                         * If it is a function, it should return the format string to
                         * announce. The function is called for each audio event, and
                         * receives a parameter object with `time`, and potentially
                         * `point` and `value` properties depending on the track.
                         * `point` is available if the audio event is related to a data
                         * point. `value` is available if the track is used as a
                         * context track, and `valueInterval` is used.
                         *
                         * If it is a format string, in addition to normal string
                         * content, format values can be accessed using bracket
                         * notation. For example `"Value is {point.y}%"`.
                         *
                         * `time`, `point` and `value` are available to the format
                         * strings similarly to with functions. Nested properties can
                         * be accessed with dot notation, for example
                         * `"Density: {point.options.custom.density}"`
                         *
                         * @sample  highcharts/sonification/speak-values
                         *          Speak values
                         * @type {string|Function}
                         * @apioption sonification.defaultSpeechOptions.mapping.text
                         */
                        /**
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @default "x"
                         */
                        time: 'x',
                        /**
                         * Speech rate (speed) multiplier.
                         * @extends sonification.defaultInstrumentOptions.mapping.time
                         * @default 1.3
                         */
                        rate: 1.3,
                        /**
                         * Volume of the speech announcement.
                         * @extends sonification.defaultInstrumentOptions.mapping.volume
                         * @default 0.4
                         */
                        volume: 0.4
                    },
                    pointGrouping: {
                        algorithm: 'last'
                    }
                }
            },
            exporting: {
                menuItemDefinitions: {
                    downloadMIDI: {
                        textKey: 'downloadMIDI',
                        onclick: function () {
                            if (this.sonification) {
                                this.sonification.downloadMIDI();
                            }
                        }
                    },
                    playAsSound: {
                        textKey: 'playAsSound',
                        onclick: function () {
                            const s = this.sonification;
                            if (s && s.isPlaying()) {
                                s.cancel();
                            }
                            else {
                                this.sonify();
                            }
                        }
                    }
                }
            },
            /**
             * @optionparent lang
             * @private
             */
            lang: {
                /**
                 * The text for the MIDI download menu item in the export menu.
                 * @requires modules/sonification
                 * @since 11.0.0
                 */
                downloadMIDI: 'Download MIDI',
                /**
                 * The text for the Play as sound menu item in the export menu.
                 * @requires modules/sonification
                 * @since 11.0.0
                 */
                playAsSound: 'Play as sound'
            }
        };
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * Sonification/audio chart options for a series.
         *
         * @since 11.0.0
         * @requires   modules/sonification
         * @apioption  plotOptions.series.sonification
         */
        /**
         * Whether or not sonification is enabled for this series.
         * @type {boolean}
         * @default true
         * @apioption  plotOptions.series.sonification.enabled
         */
        /**
         * Context tracks for this series. Context tracks are tracks that are not
         * tied to data points.
         *
         * Given as an array of instrument tracks, speech tracks, or a mix of both.
         *
         * @type {Array<*>}
         * @extends sonification.globalContextTracks
         * @apioption  plotOptions.series.sonification.contextTracks
         */
        /**
         * Tracks for this series.
         *
         * Given as an array of instrument tracks, speech tracks, or a mix of both.
         *
         * @type {Array<*>}
         * @extends sonification.globalTracks
         * @apioption  plotOptions.series.sonification.tracks
         */
        /**
         * Default options for all this series' instrument tracks.
         *
         * @extends sonification.defaultInstrumentOptions
         * @apioption  plotOptions.series.sonification.defaultInstrumentOptions
         */
        /**
         * Default options for all this series' speech tracks.
         *
         * @extends sonification.defaultSpeechOptions
         * @apioption  plotOptions.series.sonification.defaultSpeechOptions
         */
        /**
         * Sonification point grouping options for this series.
         *
         * @extends sonification.pointGrouping
         * @apioption  plotOptions.series.sonification.pointGrouping
         */
        /**
         * Event context object sent to sonification chart events.
         * @requires modules/sonification
         * @interface Highcharts.SonificationChartEventCallbackContext
         */ /**
        * The relevant chart
        * @name Highcharts.SonificationChartEventCallbackContext#chart
        * @type {Highcharts.Chart|undefined}
        */ /**
        * The points that were played, if any
        * @name Highcharts.SonificationChartEventCallbackContext#pointsPlayed
        * @type {Array<Highcharts.Point>|undefined}
        */ /**
        * The playing timeline object with advanced and internal content
        * @name Highcharts.SonificationChartEventCallbackContext#timeline
        * @type {object|undefined}
        */
        /**
         * Event context object sent to sonification series events.
         * @requires modules/sonification
         * @interface Highcharts.SonificationSeriesEventCallbackContext
         */ /**
        * The relevant series
        * @name Highcharts.SonificationSeriesEventCallbackContext#series
        * @type {Highcharts.Series|undefined}
        */ /**
        * The playing timeline object with advanced and internal content
        * @name Highcharts.SonificationSeriesEventCallbackContext#timeline
        * @type {object|undefined}
        */
        /**
         * Callback function for sonification events on chart.
         * @callback Highcharts.SonificationChartEventCallback
         * @param {Highcharts.SonificationChartEventCallbackContext} e Sonification chart event context
         */
        /**
         * Callback function for sonification events on series.
         * @callback Highcharts.SonificationSeriesEventCallback
         * @param {Highcharts.SonificationSeriesEventCallbackContext} e Sonification series event context
         */
        (''); // Keep above doclets in JS file

        return Options;
    });
    _registerModule(_modules, 'Extensions/Sonification/SynthPatch.js', [_modules['Core/Utilities.js']], function (U) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Class representing a Synth Patch, used by Instruments in the
         *  sonification.js module.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const { clamp, defined, pick } = U;
        /**
         * Get the multipler value from a pitch tracked multiplier. The parameter
         * specifies the multiplier at ca 3200Hz. It is 1 at ca 50Hz. In between
         * it is mapped logarithmically.
         * @private
         * @param {number} multiplier The multipler to track.
         * @param {number} freq The current frequency.
         */
        function getPitchTrackedMultiplierVal(multiplier, freq) {
            const a = 0.2414 * multiplier - 0.2414, b = (3.5 - 1.7 * multiplier) / 1.8;
            return a * Math.log(freq) + b;
        }
        /**
         * Schedule a mini ramp to volume at time - avoid clicks/pops.
         * @private
         * @param {Object} gainNode The gain node to schedule for.
         * @param {number} time The time in seconds to start ramp.
         * @param {number} vol The volume to ramp to.
         */
        function miniRampToVolAtTime(gainNode, time, vol) {
            gainNode.gain.cancelScheduledValues(time);
            gainNode.gain.setTargetAtTime(vol, time, SynthPatch.stopRampTime / 4);
            gainNode.gain.setValueAtTime(vol, time + SynthPatch.stopRampTime);
        }
        /**
         * Schedule a gain envelope for a gain node.
         * @private
         * @param {Array<Object>} envelope The envelope to schedule.
         * @param {string} type Type of envelope, attack or release.
         * @param {number} time At what time (in seconds) to start envelope.
         * @param {Object} gainNode The gain node to schedule on.
         * @param {number} [volumeMultiplier] Volume multiplier for the envelope.
         */
        function scheduleGainEnvelope(envelope, type, time, gainNode, volumeMultiplier = 1) {
            const isAtk = type === 'attack', gain = gainNode.gain;
            gain.cancelScheduledValues(time);
            if (!envelope.length) {
                miniRampToVolAtTime(gainNode, time, isAtk ? volumeMultiplier : 0);
                return;
            }
            if (envelope[0].t > 1) {
                envelope.unshift({ t: 0, vol: isAtk ? 0 : 1 });
            }
            envelope.forEach((ep, ix) => {
                const prev = envelope[ix - 1], delta = prev ? (ep.t - prev.t) / 1000 : 0, startTime = time + (prev ? prev.t / 1000 + SynthPatch.stopRampTime : 0);
                gain.setTargetAtTime(ep.vol * volumeMultiplier, startTime, Math.max(delta, SynthPatch.stopRampTime) / 2);
            });
        }
        /**
         * Internal class used by Oscillator, representing a Pulse Oscillator node.
         * Combines two sawtooth oscillators to create a pulse by phase inverting and
         * delaying one of them.
         * @class
         * @private
         */
        class PulseOscNode {
            constructor(context, options) {
                this.pulseWidth = Math.min(Math.max(0, options.pulseWidth || 0.5));
                const makeOsc = () => new OscillatorNode(context, {
                    type: 'sawtooth',
                    detune: options.detune,
                    frequency: Math.max(1, options.frequency || 350)
                });
                this.sawOscA = makeOsc();
                this.sawOscB = makeOsc();
                this.phaseInverter = new GainNode(context, { gain: -1 });
                this.masterGain = new GainNode(context);
                this.delayNode = new DelayNode(context, {
                    delayTime: this.pulseWidth / this.sawOscA.frequency.value
                });
                this.sawOscA.connect(this.masterGain);
                this.sawOscB.connect(this.phaseInverter);
                this.phaseInverter.connect(this.delayNode);
                this.delayNode.connect(this.masterGain);
            }
            connect(destination) {
                this.masterGain.connect(destination);
            }
            // Polymorph with normal osc.frequency API
            getFrequencyFacade() {
                const pulse = this;
                return {
                    cancelScheduledValues(fromTime) {
                        pulse.sawOscA.frequency.cancelScheduledValues(fromTime);
                        pulse.sawOscB.frequency.cancelScheduledValues(fromTime);
                        pulse.delayNode.delayTime.cancelScheduledValues(fromTime);
                        return pulse.sawOscA.frequency;
                    },
                    setValueAtTime(frequency, time) {
                        this.cancelScheduledValues(time);
                        pulse.sawOscA.frequency.setValueAtTime(frequency, time);
                        pulse.sawOscB.frequency.setValueAtTime(frequency, time);
                        pulse.delayNode.delayTime.setValueAtTime(Math.round(10000 * pulse.pulseWidth / frequency) / 10000, time);
                        return pulse.sawOscA.frequency;
                    },
                    setTargetAtTime(frequency, time, timeConstant) {
                        this.cancelScheduledValues(time);
                        pulse.sawOscA.frequency
                            .setTargetAtTime(frequency, time, timeConstant);
                        pulse.sawOscB.frequency
                            .setTargetAtTime(frequency, time, timeConstant);
                        pulse.delayNode.delayTime.setTargetAtTime(Math.round(10000 * pulse.pulseWidth / frequency) / 10000, time, timeConstant);
                        return pulse.sawOscA.frequency;
                    }
                };
            }
            getPWMTarget() {
                return this.delayNode.delayTime;
            }
            start() {
                this.sawOscA.start();
                this.sawOscB.start();
            }
            stop(time) {
                this.sawOscA.stop(time);
                this.sawOscB.stop(time);
            }
        }
        /**
         * Internal class used by SynthPatch
         * @class
         * @private
         */
        class Oscillator {
            constructor(audioContext, options, destination) {
                this.audioContext = audioContext;
                this.options = options;
                this.fmOscillatorIx = options.fmOscillator;
                this.vmOscillatorIx = options.vmOscillator;
                this.createSoundSource();
                this.createGain();
                this.createFilters();
                this.createVolTracking();
                if (destination) {
                    this.connect(destination);
                }
            }
            // Connect the node tree from destination down to oscillator,
            // depending on which nodes exist. Done automatically unless
            // no destination was passed to constructor.
            connect(destination) {
                [
                    this.lowpassNode,
                    this.highpassNode,
                    this.volTrackingNode,
                    this.vmNode,
                    this.gainNode,
                    this.whiteNoise,
                    this.pulseNode,
                    this.oscNode
                ].reduce((prev, cur) => (cur ?
                    (cur.connect(prev), cur) :
                    prev), destination);
            }
            start() {
                if (this.oscNode) {
                    this.oscNode.start();
                }
                if (this.whiteNoise) {
                    this.whiteNoise.start();
                }
                if (this.pulseNode) {
                    this.pulseNode.start();
                }
            }
            stopAtTime(time) {
                if (this.oscNode) {
                    this.oscNode.stop(time);
                }
                if (this.whiteNoise) {
                    this.whiteNoise.stop(time);
                }
                if (this.pulseNode) {
                    this.pulseNode.stop(time);
                }
            }
            setFreqAtTime(time, frequency, glideDuration = 0) {
                const opts = this.options, f = clamp(pick(opts.fixedFrequency, frequency) *
                    (opts.freqMultiplier || 1), 0, 21000), oscTarget = this.getOscTarget(), timeConstant = glideDuration / 5000;
                if (oscTarget) {
                    oscTarget.cancelScheduledValues(time);
                    if (glideDuration && time - (this.lastUpdateTime || -1) > 0.01) {
                        oscTarget.setTargetAtTime(f, time, timeConstant);
                        oscTarget.setValueAtTime(f, time + timeConstant);
                    }
                    else {
                        oscTarget.setValueAtTime(f, time);
                    }
                }
                this.scheduleVolTrackingChange(f, time, glideDuration);
                this.scheduleFilterTrackingChange(f, time, glideDuration);
                this.lastUpdateTime = time;
            }
            // Get target for FM synthesis if another oscillator wants to modulate.
            // Pulse nodes don't do FM, but do PWM instead.
            getFMTarget() {
                return this.oscNode && this.oscNode.detune ||
                    this.whiteNoise && this.whiteNoise.detune ||
                    this.pulseNode && this.pulseNode.getPWMTarget();
            }
            // Get target for volume modulation if another oscillator wants to modulate.
            getVMTarget() {
                return this.vmNode && this.vmNode.gain;
            }
            // Schedule one of the osciallator envelopes at a specified time in
            // seconds (in AudioContext timespace).
            runEnvelopeAtTime(type, time) {
                if (!this.gainNode) {
                    return;
                }
                const env = (type === 'attack' ? this.options.attackEnvelope :
                    this.options.releaseEnvelope) || [];
                scheduleGainEnvelope(env, type, time, this.gainNode, this.options.volume);
            }
            // Cancel any envelopes or frequency changes currently scheduled
            cancelScheduled() {
                if (this.gainNode) {
                    this.gainNode.gain
                        .cancelScheduledValues(this.audioContext.currentTime);
                }
                const oscTarget = this.getOscTarget();
                if (oscTarget) {
                    oscTarget.cancelScheduledValues(0);
                }
                if (this.lowpassNode) {
                    this.lowpassNode.frequency.cancelScheduledValues(0);
                }
                if (this.highpassNode) {
                    this.highpassNode.frequency.cancelScheduledValues(0);
                }
                if (this.volTrackingNode) {
                    this.volTrackingNode.gain.cancelScheduledValues(0);
                }
            }
            // Set the pitch dependent volume to fit some frequency at some time
            scheduleVolTrackingChange(frequency, time, glideDuration) {
                if (this.volTrackingNode) {
                    const v = getPitchTrackedMultiplierVal(this.options.volumePitchTrackingMultiplier || 1, frequency), rampTime = glideDuration ? glideDuration / 1000 :
                        SynthPatch.stopRampTime;
                    this.volTrackingNode.gain.cancelScheduledValues(time);
                    this.volTrackingNode.gain.setTargetAtTime(v, time, rampTime / 5);
                    this.volTrackingNode.gain.setValueAtTime(v, time + rampTime);
                }
            }
            // Set the pitch dependent filter frequency to fit frequency at some time
            scheduleFilterTrackingChange(frequency, time, glideDuration) {
                const opts = this.options, rampTime = glideDuration ? glideDuration / 1000 :
                    SynthPatch.stopRampTime, scheduleFilterTarget = (filterNode, filterOptions) => {
                    const multiplier = getPitchTrackedMultiplierVal(filterOptions.frequencyPitchTrackingMultiplier || 1, frequency), f = clamp((filterOptions.frequency || 1000) * multiplier, 0, 21000);
                    filterNode.frequency.cancelScheduledValues(time);
                    filterNode.frequency.setTargetAtTime(f, time, rampTime / 5);
                    filterNode.frequency.setValueAtTime(f, time + rampTime);
                };
                if (this.lowpassNode && opts.lowpass) {
                    scheduleFilterTarget(this.lowpassNode, opts.lowpass);
                }
                if (this.highpassNode && opts.highpass) {
                    scheduleFilterTarget(this.highpassNode, opts.highpass);
                }
            }
            createGain() {
                const opts = this.options, needsGainNode = defined(opts.volume) ||
                    opts.attackEnvelope && opts.attackEnvelope.length ||
                    opts.releaseEnvelope && opts.releaseEnvelope.length;
                if (needsGainNode) {
                    this.gainNode = new GainNode(this.audioContext, {
                        gain: pick(opts.volume, 1)
                    });
                }
                // We always need VM gain, so make that
                this.vmNode = new GainNode(this.audioContext);
            }
            // Create the oscillator or audio buffer acting as the sound source
            createSoundSource() {
                const opts = this.options, ctx = this.audioContext, frequency = (opts.fixedFrequency || 0) *
                    (opts.freqMultiplier || 1);
                if (opts.type === 'whitenoise') {
                    const bSize = ctx.sampleRate * 2, buffer = ctx.createBuffer(1, bSize, ctx.sampleRate), data = buffer.getChannelData(0);
                    for (let i = 0; i < bSize; ++i) {
                        // More pleasant "white" noise with less variance than -1 to +1
                        data[i] = Math.random() * 1.2 - 0.6;
                    }
                    const wn = this.whiteNoise = ctx.createBufferSource();
                    wn.buffer = buffer;
                    wn.loop = true;
                }
                else if (opts.type === 'pulse') {
                    this.pulseNode = new PulseOscNode(ctx, {
                        detune: opts.detune,
                        pulseWidth: opts.pulseWidth,
                        frequency
                    });
                }
                else {
                    this.oscNode = new OscillatorNode(ctx, {
                        type: opts.type || 'sine',
                        detune: opts.detune,
                        frequency
                    });
                }
            }
            // Lowpass/Highpass filters
            createFilters() {
                const opts = this.options;
                if (opts.lowpass && opts.lowpass.frequency) {
                    this.lowpassNode = new BiquadFilterNode(this.audioContext, {
                        type: 'lowpass',
                        Q: opts.lowpass.Q || 1,
                        frequency: opts.lowpass.frequency
                    });
                }
                if (opts.highpass && opts.highpass.frequency) {
                    this.highpassNode = new BiquadFilterNode(this.audioContext, {
                        type: 'highpass',
                        Q: opts.highpass.Q || 1,
                        frequency: opts.highpass.frequency
                    });
                }
            }
            // Gain node used for frequency dependent volume tracking
            createVolTracking() {
                const opts = this.options;
                if (opts.volumePitchTrackingMultiplier &&
                    opts.volumePitchTrackingMultiplier !== 1) {
                    this.volTrackingNode = new GainNode(this.audioContext, {
                        gain: 1
                    });
                }
            }
            // Get the oscillator frequency target
            getOscTarget() {
                return this.oscNode ? this.oscNode.frequency :
                    this.pulseNode && this.pulseNode.getFrequencyFacade();
            }
        }
        /**
         * The SynthPatch class. This class represents an instance and configuration
         * of the built-in Highcharts synthesizer. It can be used to play various
         * generated sounds.
         *
         * @sample highcharts/sonification/manual-using-synth
         *         Using Synth directly to sonify manually
         * @sample highcharts/sonification/custom-instrument
         *         Using custom Synth options with chart
         *
         * @requires modules/sonification
         *
         * @class
         * @name Highcharts.SynthPatch
         *
         * @param {AudioContext} audioContext
         *        The AudioContext to use.
         * @param {Highcharts.SynthPatchOptionsObject} options
         *        Configuration for the synth.
         */
        class SynthPatch {
            constructor(audioContext, options) {
                this.audioContext = audioContext;
                this.options = options;
                this.eqNodes = [];
                this.midiInstrument = options.midiInstrument || 1;
                this.outputNode = new GainNode(audioContext, { gain: 0 });
                this.createEqChain(this.outputNode);
                const inputNode = this.eqNodes.length ?
                    this.eqNodes[0] : this.outputNode;
                this.oscillators = (this.options.oscillators || []).map((oscOpts) => new Oscillator(audioContext, oscOpts, defined(oscOpts.fmOscillator) || defined(oscOpts.vmOscillator) ?
                    void 0 : inputNode));
                // Now that we have all oscillators, connect the ones
                // that are used for modulation.
                this.oscillators.forEach((osc) => {
                    const connectTarget = (targetFunc, targetOsc) => {
                        if (targetOsc) {
                            const target = targetOsc[targetFunc]();
                            if (target) {
                                osc.connect(target);
                            }
                        }
                    };
                    if (defined(osc.fmOscillatorIx)) {
                        connectTarget('getFMTarget', this.oscillators[osc.fmOscillatorIx]);
                    }
                    if (defined(osc.vmOscillatorIx)) {
                        connectTarget('getVMTarget', this.oscillators[osc.vmOscillatorIx]);
                    }
                });
            }
            /**
             * Start the oscillators, but don't output sound.
             * @function Highcharts.SynthPatch#startSilently
             */
            startSilently() {
                this.outputNode.gain.value = 0;
                this.oscillators.forEach((o) => o.start());
            }
            /**
             * Stop the synth. It can't be started again.
             * @function Highcharts.SynthPatch#stop
             */
            stop() {
                const curTime = this.audioContext.currentTime, endTime = curTime + SynthPatch.stopRampTime;
                miniRampToVolAtTime(this.outputNode, curTime, 0);
                this.oscillators.forEach((o) => o.stopAtTime(endTime));
                this.outputNode.disconnect();
            }
            /**
             * Mute sound at time (in seconds).
             * Will still run release envelope. Note: If scheduled multiple times in
             * succession, the release envelope will run, and that could make sound.
             * @function Highcharts.SynthPatch#silenceAtTime
             * @param {number} time Time offset from now, in seconds
             */
            silenceAtTime(time) {
                if (!time && this.outputNode.gain.value < 0.01) {
                    this.outputNode.gain.value = 0;
                    return; // Skip if not needed
                }
                this.releaseAtTime((time || 0) + this.audioContext.currentTime);
            }
            /**
             * Mute sound immediately.
             * @function Highcharts.SynthPatch#mute
             */
            mute() {
                this.cancelScheduled();
                miniRampToVolAtTime(this.outputNode, this.audioContext.currentTime, 0);
            }
            /**
             * Play a frequency at time (in seconds).
             * Time denotes when the attack ramp starts. Note duration is given in
             * milliseconds. If note duration is not given, the note plays indefinitely.
             * @function Highcharts.SynthPatch#silenceAtTime
             * @param {number} time Time offset from now, in seconds
             * @param {number} frequency The frequency to play at
             * @param {number|undefined} noteDuration Duration to play, in milliseconds
             */
            playFreqAtTime(time, frequency, noteDuration) {
                const t = (time || 0) + this.audioContext.currentTime, opts = this.options;
                this.oscillators.forEach((o) => {
                    o.setFreqAtTime(t, frequency, opts.noteGlideDuration);
                    o.runEnvelopeAtTime('attack', t);
                });
                scheduleGainEnvelope(opts.masterAttackEnvelope || [], 'attack', t, this.outputNode, opts.masterVolume);
                if (noteDuration) {
                    this.releaseAtTime(t + noteDuration / 1000);
                }
            }
            /**
             * Cancel any scheduled actions
             * @function Highcharts.SynthPatch#cancelScheduled
             */
            cancelScheduled() {
                this.outputNode.gain.cancelScheduledValues(this.audioContext.currentTime);
                this.oscillators.forEach((o) => o.cancelScheduled());
            }
            /**
             * Connect the SynthPatch output to an audio node / destination.
             * @function Highcharts.SynthPatch#connect
             * @param {AudioNode} destinationNode The node to connect to.
             * @return {AudioNode} The destination node, to allow chaining.
             */
            connect(destinationNode) {
                return this.outputNode.connect(destinationNode);
            }
            /**
             * Create nodes for master EQ
             * @private
             */
            createEqChain(outputNode) {
                this.eqNodes = (this.options.eq || []).map((eqDef) => new BiquadFilterNode(this.audioContext, Object.assign({ type: 'peaking' }, eqDef)));
                // Connect nodes
                this.eqNodes.reduceRight((chain, node) => {
                    node.connect(chain);
                    return node;
                }, outputNode);
            }
            /**
             * Fade by release envelopes at time
             * @private
             */
            releaseAtTime(time) {
                let maxReleaseDuration = 0;
                this.oscillators.forEach((o) => {
                    const env = o.options.releaseEnvelope;
                    if (env && env.length) {
                        maxReleaseDuration = Math.max(maxReleaseDuration, env[env.length - 1].t);
                        o.runEnvelopeAtTime('release', time);
                    }
                });
                const masterEnv = this.options.masterReleaseEnvelope || [];
                if (masterEnv.length) {
                    scheduleGainEnvelope(masterEnv, 'release', time, this.outputNode, this.options.masterVolume);
                    maxReleaseDuration = Math.max(maxReleaseDuration, masterEnv[masterEnv.length - 1].t);
                }
                miniRampToVolAtTime(this.outputNode, time + maxReleaseDuration / 1000, 0);
            }
        }
        SynthPatch.stopRampTime = 0.012; // Ramp time to 0 when stopping sound
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * An EQ filter definition for a low/highpass filter.
         * @requires modules/sonification
         * @interface Highcharts.SynthPatchPassFilter
         */ /**
        * Filter frequency.
        * @name Highcharts.SynthPatchPassFilter#frequency
        * @type {number|undefined}
        */ /**
        * A pitch tracking multiplier similarly to the one for oscillator volume.
        * Affects the filter frequency.
        * @name Highcharts.SynthPatchPassFilter#frequencyPitchTrackingMultiplier
        * @type {number|undefined}
        */ /**
        * Filter resonance bump/dip in dB. Defaults to 0.
        * @name Highcharts.SynthPatchPassFilter#Q
        * @type {number|undefined}
        */
        /**
         * @typedef {Highcharts.Record<"t"|"vol",number>} Highcharts.SynthEnvelopePoint
         * @requires modules/sonification
         */
        /**
         * @typedef {Array<Highcharts.SynthEnvelopePoint>} Highcharts.SynthEnvelope
         * @requires modules/sonification
         */
        /**
         * @typedef {"sine"|"square"|"sawtooth"|"triangle"|"whitenoise"|"pulse"} Highcharts.SynthPatchOscillatorType
         * @requires modules/sonification
         */
        /**
         * Configuration for an oscillator for the synth.
         * @requires modules/sonification
         * @interface Highcharts.SynthPatchOscillatorOptionsObject
         */ /**
        * The type of oscillator. This describes the waveform of the oscillator.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#type
        * @type {Highcharts.SynthPatchOscillatorType|undefined}
        */ /**
        * A volume modifier for the oscillator. Defaults to 1.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#volume
        * @type {number|undefined}
        */ /**
        * A multiplier for the input frequency of the oscillator. Defaults to 1. If
        * this is for example set to 4, an input frequency of 220Hz will cause the
        * oscillator to play at 880Hz.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#freqMultiplier
        * @type {number|undefined}
        */ /**
        * Play a fixed frequency for the oscillator - ignoring input frequency. The
        * frequency multiplier is still applied.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#fixedFrequency
        * @type {number|undefined}
        */ /**
        * Applies a detuning of all frequencies. Set in cents. Defaults to 0.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#detune
        * @type {number|undefined}
        */ /**
        * Width of the pulse waveform. Only applies to "pulse" type oscillators. A
        * width of 0.5 is roughly equal to a square wave. This is the default.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#pulseWidth
        * @type {number|undefined}
        */ /**
        * Index of another oscillator to use as carrier, with this oscillator being
        * used as a volume modulator. The first oscillator in the array has index 0,
        * and so on. This option can be used to produce tremolo effects.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#vmOscillator
        * @type {number|undefined}
        */ /**
        * Index of another oscillator to use as carrier, with this oscillator being
        * used as a frequency modulator. Note: If the carrier is a pulse oscillator,
        * the modulation will be on pulse width instead of frequency, allowing for
        * PWM effects.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#fmOscillator
        * @type {number|undefined}
        */ /**
        * A tracking multiplier used for frequency dependent behavior. For example, by
        * setting the volume tracking multiplier to 0.01, the volume will be lower at
        * higher notes. The multiplier is a logarithmic function, where 1 is at ca
        * 50Hz, and you define the output multiplier for an input frequency around
        * 3.2kHz.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#volumePitchTrackingMultiplier
        * @type {number|undefined}
        */ /**
        * Volume envelope for note attack, specific to this oscillator.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#attackEnvelope
        * @type {Highcharts.SynthEnvelope|undefined}
        */ /**
        * Volume envelope for note release, specific to this oscillator.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#releaseEnvelope
        * @type {Highcharts.SynthEnvelope|undefined}
        */ /**
        * Highpass filter options for the oscillator.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#highpass
        * @type {Highcharts.SynthPatchPassFilter|undefined}
        */ /**
        * Lowpass filter options for the oscillator.
        * @name Highcharts.SynthPatchOscillatorOptionsObject#lowpass
        * @type {Highcharts.SynthPatchPassFilter|undefined}
        */
        /**
         * An EQ filter definition for a bell filter.
         * @requires modules/sonification
         * @interface Highcharts.SynthPatchEQFilter
         */ /**
        * Filter frequency.
        * @name Highcharts.SynthPatchEQFilter#frequency
        * @type {number|undefined}
        */ /**
        * Filter gain. Defaults to 0.
        * @name Highcharts.SynthPatchEQFilter#gain
        * @type {number|undefined}
        */ /**
        * Filter Q. Defaults to 1. Lower numbers mean a wider bell.
        * @name Highcharts.SynthPatchEQFilter#Q
        * @type {number|undefined}
        */
        /**
         * A set of options for the SynthPatch class.
         *
         * @requires modules/sonification
         *
         * @interface Highcharts.SynthPatchOptionsObject
         */ /**
        * Global volume modifier for the synth. Defaults to 1. Note that if the total
        * volume of all oscillators is too high, the browser's audio engine can
        * distort.
        * @name Highcharts.SynthPatchOptionsObject#masterVolume
        * @type {number|undefined}
        */ /**
        * Time in milliseconds to glide between notes. Causes a glissando effect.
        * @name Highcharts.SynthPatchOptionsObject#noteGlideDuration
        * @type {number|undefined}
        */ /**
        * MIDI instrument ID for the synth. Used with MIDI export of Timelines to have
        * tracks open with a similar instrument loaded when imported into other
        * applications. Defaults to 1, "Acoustic Grand Piano".
        * @name Highcharts.SynthPatchOptionsObject#midiInstrument
        * @type {number|undefined}
        */ /**
        * Volume envelope for the overall attack of a note - what happens to the
        * volume when a note first plays. If the volume goes to 0 in the attack
        * envelope, the synth will not be able to play the note continuously/
        * sustained, and the notes will be staccato.
        * @name Highcharts.SynthPatchOptionsObject#masterAttackEnvelope
        * @type {Highcharts.SynthEnvelope|undefined}
        */ /**
        * Volume envelope for the overall release of a note - what happens to the
        * volume when a note stops playing. If the release envelope starts at a higher
        * volume than the attack envelope ends, the volume will first rise to that
        * volume before falling when releasing a note. If the note is released while
        * the attack envelope is still in effect, the attack envelope is interrupted,
        * and the release envelope plays instead.
        * @name Highcharts.SynthPatchOptionsObject#masterReleaseEnvelope
        * @type {Highcharts.SynthEnvelope|undefined}
        */ /**
        * Master EQ filters for the synth, affecting the overall sound.
        * @name Highcharts.SynthPatchOptionsObject#eq
        * @type {Array<Highcharts.SynthPatchEQFilter>|undefined}
        */ /**
        * Array of oscillators to add to the synth.
        * @name Highcharts.SynthPatchOptionsObject#oscillators
        * @type {Array<Highcharts.SynthPatchOscillatorOptionsObject>|undefined}
        */
        (''); // Keep above doclets in JS file

        return SynthPatch;
    });
    _registerModule(_modules, 'Extensions/Sonification/InstrumentPresets.js', [], function () {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Presets for SynthPatch.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const InstrumentPresets = {
            // PIANO ----------------------------
            piano: {
                masterVolume: 0.45,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.71 },
                    { t: 40, vol: 0.79 },
                    { t: 82, vol: 0.64 },
                    { t: 147, vol: 0.29 },
                    { t: 260, vol: 0.15 },
                    { t: 417, vol: 0.05 },
                    { t: 589, vol: 0 }
                ],
                eq: [
                    { frequency: 200, Q: 0.7, gain: 6 },
                    { frequency: 450, gain: 6 },
                    { frequency: 1300, gain: 2 },
                    { frequency: 2600, Q: 0.8, gain: 8 },
                    { frequency: 3500, Q: 0.8, gain: 6 },
                    { frequency: 6200, Q: 0.8, gain: 10 },
                    { frequency: 8000, gain: -23 },
                    { frequency: 10000, Q: 0.4, gain: -12 }
                ],
                oscillators: [{
                        type: 'pulse',
                        volume: 0.5,
                        pulseWidth: 0.55,
                        volumePitchTrackingMultiplier: 0.1,
                        lowpass: {
                            frequency: 4.5,
                            frequencyPitchTrackingMultiplier: 900,
                            Q: -2
                        },
                        highpass: { frequency: 270 },
                        attackEnvelope: [{ t: 1, vol: 1 }],
                        releaseEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 282, vol: 0.64 },
                            { t: 597, vol: 0 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 0.8,
                        lowpass: { frequency: 400 },
                        highpass: { frequency: 300 },
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 19, vol: 0 }
                        ]
                    }]
            },
            // PLUCKED --------------------------
            plucked: {
                masterVolume: 0.5,
                midiInstrument: 25,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.71 },
                    { t: 4, vol: 0.71 },
                    { t: 31, vol: 0.4 },
                    { t: 109, vol: 0.12 },
                    { t: 234, vol: 0.04 },
                    { t: 442, vol: 0 }
                ],
                eq: [
                    { frequency: 800, gain: -8 },
                    { frequency: 1400, Q: 4, gain: 4 },
                    { frequency: 1600, gain: -14 },
                    { frequency: 2200, gain: -8 },
                    { frequency: 3600, gain: -2 },
                    { frequency: 6400, Q: 2, gain: -6 }
                ],
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.9,
                        volumePitchTrackingMultiplier: 0.6,
                        highpass: { frequency: 100 },
                        lowpass: { frequency: 8000 },
                        releaseEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 315, vol: 0.56 },
                            { t: 550, vol: 0 }
                        ]
                    }]
            },
            // FLUTE ----------------------------
            flute: {
                masterVolume: 1.1,
                midiInstrument: 74,
                noteGlideDuration: 30,
                masterAttackEnvelope: [
                    { t: 0, vol: 0 },
                    { t: 29, vol: 1 },
                    { t: 76, vol: 0.48 },
                    { t: 600, vol: 0.36 }
                ],
                masterReleaseEnvelope: [
                    { t: 1, vol: 0.36 },
                    { t: 24, vol: 0.15 },
                    { t: 119, vol: 0 }
                ],
                eq: [
                    { frequency: 150, Q: 0.6, gain: -10 },
                    { frequency: 500, gain: 4 },
                    { frequency: 1100, gain: -4 },
                    { frequency: 2200, gain: -14 },
                    { frequency: 5000, gain: 8 },
                    { frequency: 6400, gain: 10 },
                    { frequency: 8000, gain: 12 },
                    { frequency: 10800, gain: 8 }
                ],
                oscillators: [{
                        type: 'triangle',
                        volume: 1,
                        volumePitchTrackingMultiplier: 0.4,
                        lowpass: {
                            frequency: 12,
                            frequencyPitchTrackingMultiplier: 100
                        },
                        highpass: {
                            frequency: 200
                        }
                    }, {
                        type: 'sine',
                        fixedFrequency: 5,
                        volume: 0.2,
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 48, vol: 0 },
                            { t: 225, vol: 0.05 },
                            { t: 600, vol: 0.77 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 0.13,
                        lowpass: {
                            frequency: 9000,
                            Q: 3
                        },
                        highpass: {
                            frequency: 6000,
                            Q: 3
                        },
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 0, vol: 0 },
                            { t: 26, vol: 1 },
                            { t: 93, vol: 0.8 }
                        ]
                    }]
            },
            // LEAD -----------------------------
            lead: {
                masterVolume: 1,
                midiInstrument: 20,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.81 },
                    { t: 98, vol: 0.5 },
                    { t: 201, vol: 0.18 },
                    { t: 377, vol: 0.04 },
                    { t: 586, vol: 0 },
                    { t: 586, vol: 0 }
                ],
                eq: [
                    { frequency: 200, gain: -6 },
                    { frequency: 400, gain: -8 },
                    { frequency: 800, Q: 0.5, gain: -10 },
                    { frequency: 1200, gain: 4 },
                    { frequency: 3600, gain: -4 },
                    { frequency: 4200, gain: -12 },
                    { frequency: 7400, gain: -14 },
                    { frequency: 10000, gain: 2 }
                ],
                oscillators: [{
                        type: 'triangle',
                        volume: 1.1,
                        volumePitchTrackingMultiplier: 0.6,
                        lowpass: { frequency: 5000 },
                        highpass: { frequency: 100 }
                    }, {
                        type: 'sawtooth',
                        volume: 0.4,
                        lowpass: { frequency: 7000 },
                        highpass: { frequency: 800, Q: 6 },
                        releaseEnvelope: [
                            { t: 0, vol: 0.99 },
                            { t: 200, vol: 0.83 },
                            { t: 495, vol: 0 }
                        ]
                    }]
            },
            // VIBRAPHONE -----------------------
            vibraphone: {
                masterVolume: 1,
                midiInstrument: 12,
                masterAttackEnvelope: [
                    { t: 1, vol: 0 },
                    { t: 10, vol: 0.63 },
                    { t: 82, vol: 0.64 },
                    { t: 149, vol: 0.26 },
                    { t: 600, vol: 0 }
                ],
                eq: [
                    { frequency: 200, Q: 0.8, gain: -12 },
                    { frequency: 400, gain: -4 },
                    { frequency: 1600, Q: 0.5, gain: 6 },
                    { frequency: 2200, Q: 0.5, gain: 6 },
                    { frequency: 6400, gain: 4 },
                    { frequency: 12800, gain: 4 }
                ],
                oscillators: [{
                        type: 'sine',
                        volume: 1.5,
                        volumePitchTrackingMultiplier: 0.0000001,
                        attackEnvelope: [{ t: 1, vol: 1 }],
                        releaseEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 146, vol: 0.39 },
                            { t: 597, vol: 0 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 0.03,
                        volumePitchTrackingMultiplier: 0.0001,
                        lowpass: {
                            frequency: 900
                        },
                        highpass: {
                            frequency: 800
                        },
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 9, vol: 0 }
                        ]
                    }, {
                        type: 'sine',
                        freqMultiplier: 4,
                        volume: 0.15,
                        volumePitchTrackingMultiplier: 0.0001
                    }, {
                        type: 'sine',
                        fixedFrequency: 3,
                        volume: 6,
                        fmOscillator: 0,
                        releaseEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 190, vol: 0.41 },
                            { t: 600, vol: 0 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 6,
                        volume: 3,
                        fmOscillator: 2
                    }, {
                        type: 'sine',
                        freqMultiplier: 9,
                        volume: 0.0005,
                        volumePitchTrackingMultiplier: 0.0001,
                        releaseEnvelope: [
                            { t: 1, vol: 0.97 },
                            { t: 530, vol: 0 }
                        ]
                    }]
            },
            // SAXOPHONE ------------------------
            saxophone: {
                masterVolume: 1,
                midiInstrument: 67,
                noteGlideDuration: 10,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.57 },
                    { t: 35, vol: 1 },
                    { t: 87, vol: 0.84 },
                    { t: 111, vol: 0.6 },
                    { t: 296, vol: 0.49 },
                    { t: 600, vol: 0.58 }
                ],
                masterReleaseEnvelope: [
                    { t: 1, vol: 0.58 },
                    { t: 47, vol: 0.16 },
                    { t: 119, vol: 0 }
                ],
                eq: [
                    { frequency: 200, gain: -2 },
                    { frequency: 600, gain: 2 },
                    { frequency: 800, gain: -10 },
                    { frequency: 1100, gain: -2 },
                    { frequency: 2200, gain: -2 },
                    { frequency: 3500, gain: 10 },
                    { frequency: 12800, gain: 4 }
                ],
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.45,
                        volumePitchTrackingMultiplier: 0.06,
                        lowpass: {
                            frequency: 18,
                            frequencyPitchTrackingMultiplier: 200
                        },
                        highpass: {
                            frequency: 300
                        }
                    }, {
                        type: 'whitenoise',
                        fixedFrequency: 1,
                        volume: 0.4,
                        highpass: {
                            frequency: 7000
                        },
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 51, vol: 1 },
                            { t: 86, vol: 0.84 },
                            { t: 500, vol: 0.78 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 4,
                        volume: 2,
                        fmOscillator: 0,
                        attackEnvelope: [
                            { t: 0, vol: 0 },
                            { t: 15, vol: 0.94 },
                            { t: 79, vol: 1 },
                            { t: 172, vol: 0.47 },
                            { t: 500, vol: 0.26 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 7,
                        volume: 6,
                        fmOscillator: 0,
                        attackEnvelope: [
                            { t: 0, vol: 0 },
                            { t: 25, vol: 0.99 },
                            { t: 85, vol: 0 },
                            { t: 85, vol: 0 },
                            { t: 387, vol: 0.02 },
                            { t: 511, vol: 0.43 },
                            { t: 600, vol: 0 }
                        ]
                    }]
            },
            // TRUMPET ------------------------
            trumpet: {
                masterVolume: 0.3,
                midiInstrument: 57,
                noteGlideDuration: 40,
                masterAttackEnvelope: [
                    { t: 1, vol: 0 },
                    { t: 17, vol: 1 },
                    { t: 42, vol: 0.85 },
                    { t: 76, vol: 1 },
                    { t: 202, vol: 0.65 },
                    { t: 226, vol: 0.86 },
                    { t: 282, vol: 0.63 }
                ],
                masterReleaseEnvelope: [
                    { t: 1, vol: 0.62 },
                    { t: 34, vol: 0.14 },
                    { t: 63, vol: 0.21 },
                    { t: 96, vol: 0 }
                ],
                eq: [
                    { frequency: 200, Q: 0.6, gain: 10 },
                    { frequency: 600, Q: 0.5, gain: 6 },
                    { frequency: 1500, Q: 0.7, gain: 14 },
                    { frequency: 3200, Q: 2, gain: 8 },
                    { frequency: 3800, Q: 0.8, gain: 10 },
                    { frequency: 6200, gain: 12 },
                    { frequency: 8400, gain: -20 },
                    { frequency: 12800, Q: 0.5, gain: -18 }
                ],
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.15,
                        pulseWidth: 0.5,
                        volumePitchTrackingMultiplier: 0.5,
                        lowpass: { frequency: 1900, Q: 3 }
                    }, {
                        type: 'sine',
                        fixedFrequency: 6,
                        volume: 0.2,
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 102, vol: 0.13 },
                            { t: 556, vol: 0.24 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 0.45,
                        highpass: { frequency: 7000, Q: 9 },
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 89, vol: 0.51 },
                            { t: 577, vol: 0.29 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 5.7,
                        volume: 20,
                        fmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 89, vol: 1 },
                            { t: 137, vol: 0.46 },
                            { t: 283, vol: 0.15 },
                            { t: 600, vol: 0.28 }
                        ]
                    }]
            },
            // SAWSYNTH --------------------------
            sawsynth: {
                masterVolume: 0.3,
                midiInstrument: 51,
                noteGlideDuration: 40,
                masterAttackEnvelope: [
                    { t: 0, vol: 0.6 },
                    { t: 9, vol: 1 },
                    { t: 102, vol: 0.48 }
                ],
                eq: [{ frequency: 200, gain: -6 }],
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.4,
                        volumePitchTrackingMultiplier: 0.3
                    }, {
                        type: 'sawtooth',
                        volume: 0.4,
                        detune: 11,
                        volumePitchTrackingMultiplier: 0.3
                    }, {
                        type: 'sawtooth',
                        volume: 0.4,
                        detune: -11,
                        volumePitchTrackingMultiplier: 0.3
                    }]
            },
            // BASIC1 ---------------------------
            basic1: {
                masterVolume: 1,
                noteGlideDuration: 0,
                masterReleaseEnvelope: [
                    { t: 1, vol: 0.36 },
                    { t: 24, vol: 0.15 },
                    { t: 119, vol: 0 }
                ],
                eq: [
                    { frequency: 150, Q: 0.6, gain: -12 },
                    { frequency: 1100, gain: -2 },
                    { frequency: 2200, gain: -16 },
                    { frequency: 5000, gain: 8 },
                    { frequency: 6400, gain: 10 },
                    { frequency: 8000, gain: 12 },
                    { frequency: 10800, gain: 8 }
                ],
                oscillators: [{
                        type: 'triangle',
                        volume: 1,
                        volumePitchTrackingMultiplier: 0.05,
                        lowpass: { frequency: 17, frequencyPitchTrackingMultiplier: 100 },
                        highpass: { frequency: 200 }
                    }, {
                        type: 'whitenoise',
                        volume: 0.04,
                        lowpass: { frequency: 9000, Q: 3 },
                        highpass: { frequency: 6000, Q: 3 },
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 0, vol: 0 },
                            { t: 26, vol: 1 },
                            { t: 71, vol: 0.73 }
                        ]
                    }]
            },
            // BASIC2 ---------------------------
            basic2: {
                masterVolume: 0.3,
                eq: [
                    { frequency: 200, Q: 0.7, gain: 6 },
                    { frequency: 450, gain: 2 },
                    { frequency: 1300, gain: -2 },
                    { frequency: 2600, Q: 0.8, gain: 6 },
                    { frequency: 3500, Q: 0.8, gain: 6 },
                    { frequency: 6200, Q: 0.8, gain: 10 },
                    { frequency: 8000, gain: -18 },
                    { frequency: 10000, Q: 0.4, gain: -12 }
                ],
                oscillators: [{
                        type: 'pulse',
                        volume: 0.4,
                        pulseWidth: 0.55,
                        volumePitchTrackingMultiplier: 0.1,
                        lowpass: {
                            frequency: 4.5,
                            frequencyPitchTrackingMultiplier: 900,
                            Q: -2
                        },
                        highpass: { frequency: 270 }
                    }]
            },
            // CHORD -------------------------------
            chord: {
                masterVolume: 1,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.79 },
                    { t: 27, vol: 0.86 },
                    { t: 62, vol: 0.81 },
                    { t: 150, vol: 0.35 },
                    { t: 408, vol: 0.04 },
                    { t: 600, vol: 0 }
                ],
                eq: [
                    { frequency: 200, gain: -8 },
                    { frequency: 600, Q: 2, gain: 4 },
                    { frequency: 800, gain: -10 },
                    { frequency: 1600, gain: -2 },
                    { frequency: 2200, gain: -6 },
                    { frequency: 3600, Q: 0.7, gain: -2 },
                    { frequency: 6400, gain: 6 },
                    { frequency: 12800, gain: 6 }
                ],
                oscillators: [{
                        type: 'triangle',
                        volume: 1.1,
                        volumePitchTrackingMultiplier: 0.05,
                        lowpass: { frequency: 8000 },
                        highpass: { frequency: 100 },
                        releaseEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 315, vol: 0.56 },
                            { t: 540, vol: 0 }
                        ]
                    }, {
                        type: 'triangle',
                        freqMultiplier: 1.17,
                        volume: 0.4,
                        volumePitchTrackingMultiplier: 0.07,
                        lowpass: { frequency: 5000 },
                        highpass: { frequency: 100 },
                        releaseEnvelope: [
                            { t: 0, vol: 1 },
                            { t: 476, vol: 0 }
                        ]
                    }, {
                        type: 'triangle',
                        freqMultiplier: 1.58333,
                        volume: 0.7,
                        volumePitchTrackingMultiplier: 0.02,
                        highpass: { frequency: 200 },
                        releaseEnvelope: [
                            { t: 0, vol: 1 },
                            { t: 422, vol: 0.56 },
                            { t: 577, vol: 0 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 10,
                        volume: 4,
                        fmOscillator: 0,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 157, vol: 0.65 }
                        ]
                    }, {
                        type: 'sine',
                        fixedFrequency: 5,
                        volume: 0.3,
                        vmOscillator: 2,
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 155, vol: 0.91 },
                            { t: 289, vol: 0.78 }
                        ]
                    }]
            },
            // WOBBLE ---------------------------
            wobble: {
                masterVolume: 0.9,
                masterReleaseEnvelope: [
                    { t: 1, vol: 0.36 },
                    { t: 24, vol: 0.15 },
                    { t: 119, vol: 0 }
                ],
                eq: [
                    { frequency: 150, Q: 0.6, gain: -12 },
                    { frequency: 1100, gain: -2 },
                    { frequency: 2200, gain: -16 },
                    { frequency: 5000, gain: 8 },
                    { frequency: 6400, gain: 10 },
                    { frequency: 8000, gain: 12 },
                    { frequency: 10800, gain: 8 }
                ],
                oscillators: [{
                        type: 'triangle',
                        volume: 0.9,
                        volumePitchTrackingMultiplier: 0.1,
                        lowpass: { frequency: 17, frequencyPitchTrackingMultiplier: 100 },
                        highpass: { frequency: 200 }
                    }, {
                        type: 'whitenoise',
                        volume: 0.04,
                        lowpass: { frequency: 9000, Q: 3 },
                        highpass: { frequency: 6000, Q: 3 },
                        vmOscillator: 0,
                        attackEnvelope: [
                            { t: 0, vol: 0 },
                            { t: 26, vol: 1 },
                            { t: 71, vol: 0.73 }
                        ]
                    }, {
                        type: 'sine',
                        freqMultiplier: 0.011,
                        volume: 30,
                        fmOscillator: 0
                    }]
            },
            // SINE -----------------------------
            sine: {
                masterVolume: 1,
                oscillators: [{
                        type: 'sine',
                        volumePitchTrackingMultiplier: 0.07
                    }]
            },
            // SINE GLIDE -----------------------
            sineGlide: {
                masterVolume: 1,
                noteGlideDuration: 100,
                oscillators: [{
                        type: 'sine',
                        volumePitchTrackingMultiplier: 0.07
                    }]
            },
            // TRIANGLE -------------------------
            triangle: {
                masterVolume: 0.5,
                oscillators: [{
                        type: 'triangle',
                        volume: 1,
                        volumePitchTrackingMultiplier: 0.07
                    }]
            },
            // SAWTOOTH -------------------------
            sawtooth: {
                masterVolume: 0.25,
                midiInstrument: 82,
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.3,
                        volumePitchTrackingMultiplier: 0.07
                    }]
            },
            // SQUARE ---------------------------
            square: {
                masterVolume: 0.3,
                midiInstrument: 81,
                oscillators: [{
                        type: 'square',
                        volume: 0.2,
                        volumePitchTrackingMultiplier: 0.07
                    }]
            },
            // PERCUSSION INSTRUMENTS ----------
            chop: {
                masterVolume: 1,
                midiInstrument: 116,
                masterAttackEnvelope: [{ t: 1, vol: 1 }, { t: 44, vol: 0 }],
                oscillators: [{
                        type: 'whitenoise',
                        volume: 1,
                        lowpass: { frequency: 600 },
                        highpass: { frequency: 200 }
                    }]
            },
            shaker: {
                masterVolume: 0.4,
                midiInstrument: 116,
                masterAttackEnvelope: [{ t: 1, vol: 1 }, { t: 44, vol: 0 }],
                oscillators: [{
                        type: 'whitenoise',
                        volume: 1,
                        lowpass: { frequency: 6500 },
                        highpass: { frequency: 5000 }
                    }]
            },
            step: {
                masterVolume: 1,
                midiInstrument: 116,
                masterAttackEnvelope: [{ t: 1, vol: 1 }, { t: 44, vol: 0 }],
                eq: [
                    { frequency: 200, gain: -1 },
                    { frequency: 400, gain: -14 },
                    { frequency: 800, gain: 8 },
                    { frequency: 1000, Q: 5, gain: -24 },
                    { frequency: 1600, gain: 8 },
                    { frequency: 2200, gain: -10 },
                    { frequency: 5400, gain: 4 },
                    { frequency: 12800, gain: -36 }
                ],
                oscillators: [{
                        type: 'whitenoise',
                        volume: 1.5,
                        lowpass: { frequency: 300 },
                        highpass: { frequency: 100, Q: 6 }
                    }]
            },
            kick: {
                masterVolume: 0.55,
                masterAttackEnvelope: [
                    { t: 1, vol: 0.8 },
                    { t: 15, vol: 1 },
                    { t: 45, vol: 0.35 },
                    { t: 121, vol: 0.11 },
                    { t: 242, vol: 0 }
                ],
                eq: [
                    { frequency: 50, gain: 6 },
                    { frequency: 400, gain: -18 },
                    { frequency: 1600, gain: 18 }
                ],
                oscillators: [{
                        type: 'triangle',
                        fixedFrequency: 90,
                        volume: 1,
                        lowpass: { frequency: 300 },
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 6, vol: 1 },
                            { t: 45, vol: 0.01 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 0.4,
                        lowpass: { frequency: 200 },
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 30, vol: 0 }
                        ]
                    }, {
                        type: 'triangle',
                        freqMultiplier: 0.1,
                        volume: 1,
                        lowpass: { frequency: 200 }
                    }]
            },
            shortnote: {
                masterVolume: 0.8,
                midiInstrument: 116,
                masterAttackEnvelope: [
                    { t: 1, vol: 1 },
                    { t: 15, vol: 0 }
                ],
                eq: [
                    { frequency: 400, gain: -4 },
                    { frequency: 800, gain: -12 },
                    { frequency: 2400, gain: 4 },
                    { frequency: 7200, gain: -20 },
                    { frequency: 1000, Q: 5, gain: -12 },
                    { frequency: 5400, gain: -32 },
                    { frequency: 12800, gain: -14 }
                ],
                oscillators: [{
                        type: 'sawtooth',
                        volume: 0.6,
                        lowpass: { frequency: 1000 }
                    }, {
                        type: 'whitenoise',
                        volume: 0.2,
                        lowpass: { frequency: 10000 },
                        highpass: { frequency: 7000 },
                        attackEnvelope: [
                            { t: 1, vol: 1 },
                            { t: 10, vol: 0 }
                        ]
                    }, {
                        type: 'whitenoise',
                        volume: 1.3,
                        lowpass: { frequency: 700, Q: 4 },
                        highpass: { frequency: 250 }
                    }]
            },
            // NOISE ----------------------------
            noise: {
                masterVolume: 0.3,
                midiInstrument: 122,
                oscillators: [{
                        type: 'whitenoise'
                    }]
            },
            // FILTERED NOISE -------------------
            filteredNoise: {
                masterVolume: 0.3,
                midiInstrument: 122,
                eq: [
                    { frequency: 1600, gain: -8 },
                    { frequency: 2200, gain: -4 }
                ],
                oscillators: [{
                        type: 'whitenoise',
                        lowpass: {
                            frequency: 5,
                            frequencyPitchTrackingMultiplier: 1300,
                            Q: 6
                        },
                        highpass: {
                            frequency: 5,
                            frequencyPitchTrackingMultiplier: 300,
                            Q: 6
                        }
                    }]
            },
            // WIND -------------------------------
            wind: {
                masterVolume: 0.75,
                midiInstrument: 122,
                noteGlideDuration: 150,
                masterReleaseEnvelope: [
                    { t: 0, vol: 1 },
                    { t: 124, vol: 0.24 },
                    { t: 281, vol: 0 }
                ],
                oscillators: [{
                        type: 'whitenoise',
                        volume: 1,
                        lowpass: {
                            frequency: 100,
                            frequencyPitchTrackingMultiplier: 6,
                            Q: 23
                        },
                        highpass: {
                            frequency: 170,
                            frequencyPitchTrackingMultiplier: 6
                        }
                    }, {
                        type: 'sine',
                        freqMultiplier: 0.016,
                        volume: 1000,
                        fmOscillator: 0
                    }]
            }
        };
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * @typedef {"piano"|"plucked"|"flute"|"lead"|"vibraphone"|"saxophone"|"trumpet"|"sawsynth"|"basic1"|"basic2"|"chord"|"wobble"|"sine"|"sineGlide"|"triangle"|"sawtooth"|"square"|"chop"|"shaker"|"step"|"kick"|"shortnote"|"noise"|"filteredNoise"|"wind"} Highcharts.SonificationSynthPreset
         * @requires modules/sonification
         */
        (''); // Keep above doclets in JS file

        return InstrumentPresets;
    });
    _registerModule(_modules, 'Extensions/Sonification/SonificationInstrument.js', [_modules['Extensions/Sonification/SynthPatch.js'], _modules['Extensions/Sonification/InstrumentPresets.js'], _modules['Core/Utilities.js']], function (SynthPatch, InstrumentPresets, U) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Class representing an Instrument with mappable parameters for sonification.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const { defined, extend } = U;
        /**
         * The SonificationInstrument class. This class represents an instrument with
         * mapping capabilities. The instrument wraps a SynthPatch object, and extends
         * it with functionality such as panning, tremolo, and global low/highpass
         * filters.
         *
         * @sample highcharts/sonification/instrument-raw
         *         Using SonificationInstrument directly, with no chart.
         *
         * @requires modules/sonification
         *
         * @class
         * @name Highcharts.SonificationInstrument
         *
         * @param {AudioContext} audioContext
         *        The AudioContext to use.
         * @param {AudioNode} outputNode
         *        The destination node to connect to.
         * @param {Highcharts.SonificationInstrumentOptionsObject} options
         *        Configuration for the instrument.
         */
        class SonificationInstrument {
            constructor(audioContext, outputNode, options) {
                this.audioContext = audioContext;
                this.curParams = {};
                this.midiTrackName = options.midiTrackName;
                this.masterVolNode = new GainNode(audioContext);
                this.masterVolNode.connect(outputNode);
                this.volumeNode = new GainNode(audioContext);
                this.createNodesFromCapabilities(extend({
                    pan: true
                }, options.capabilities || {}));
                this.connectCapabilityNodes(this.volumeNode, this.masterVolNode);
                this.synthPatch = new SynthPatch(audioContext, typeof options.synthPatch === 'string' ?
                    InstrumentPresets[options.synthPatch] : options.synthPatch);
                this.midiInstrument = this.synthPatch.midiInstrument || 1;
                this.synthPatch.startSilently();
                this.synthPatch.connect(this.volumeNode);
            }
            /**
             * Set the overall volume.
             * @function Highcharts.SonificationInstrument#setMasterVolume
             * @param {number} volume The volume to set, from 0 to 1.
             */
            setMasterVolume(volume) {
                this.masterVolNode.gain.setTargetAtTime(volume, 0, SonificationInstrument.rampTime);
            }
            /**
             * Schedule an instrument event at a given time offset, whether it is
             * playing a note or changing the parameters of the instrument.
             * @function Highcharts.SonificationInstrument#scheduleEventAtTime
             * @param {number} time Time is given in seconds, where 0 is now.
             * @param {Highcharts.SonificationInstrumentScheduledEventOptionsObject} params
             * Parameters for the instrument event.
             */
            scheduleEventAtTime(time, params) {
                const mergedParams = extend(this.curParams, params), freq = defined(params.frequency) ?
                    params.frequency : defined(params.note) ?
                    SonificationInstrument.musicalNoteToFrequency(params.note) :
                    220;
                if (defined(freq)) {
                    this.synthPatch.playFreqAtTime(time, freq, mergedParams.noteDuration);
                }
                if (defined(mergedParams.tremoloDepth) ||
                    defined(mergedParams.tremoloSpeed)) {
                    this.setTremoloAtTime(time, mergedParams.tremoloDepth, mergedParams.tremoloSpeed);
                }
                if (defined(mergedParams.pan)) {
                    this.setPanAtTime(time, mergedParams.pan);
                }
                if (defined(mergedParams.volume)) {
                    this.setVolumeAtTime(time, mergedParams.volume);
                }
                if (defined(mergedParams.lowpassFreq) ||
                    defined(mergedParams.lowpassResonance)) {
                    this.setFilterAtTime('lowpass', time, mergedParams.lowpassFreq, mergedParams.lowpassResonance);
                }
                if (defined(mergedParams.highpassFreq) ||
                    defined(mergedParams.highpassResonance)) {
                    this.setFilterAtTime('highpass', time, mergedParams.highpassFreq, mergedParams.highpassResonance);
                }
            }
            /**
             * Schedule silencing the instrument at a given time offset.
             * @function Highcharts.SonificationInstrument#silenceAtTime
             * @param {number} time Time is given in seconds, where 0 is now.
             */
            silenceAtTime(time) {
                this.synthPatch.silenceAtTime(time);
            }
            /**
             * Cancel currently playing sounds and any scheduled actions.
             * @function Highcharts.SonificationInstrument#cancel
             */
            cancel() {
                this.synthPatch.mute();
                [
                    this.tremoloDepth && this.tremoloDepth.gain,
                    this.tremoloOsc && this.tremoloOsc.frequency,
                    this.lowpassNode && this.lowpassNode.frequency,
                    this.lowpassNode && this.lowpassNode.Q,
                    this.highpassNode && this.highpassNode.frequency,
                    this.highpassNode && this.highpassNode.Q,
                    this.panNode && this.panNode.pan,
                    this.volumeNode.gain
                ].forEach((p) => (p && p.cancelScheduledValues(0)));
            }
            /**
             * Stop instrument and destroy it, cleaning up used resources.
             * @function Highcharts.SonificationInstrument#destroy
             */
            destroy() {
                this.cancel();
                this.synthPatch.stop();
                if (this.tremoloOsc) {
                    this.tremoloOsc.stop();
                }
                [
                    this.tremoloDepth, this.tremoloOsc, this.lowpassNode,
                    this.highpassNode, this.panNode, this.volumeNode,
                    this.masterVolNode
                ].forEach(((n) => n && n.disconnect()));
            }
            /**
             * Schedule a pan value at a given time offset.
             * @private
             */
            setPanAtTime(time, pan) {
                if (this.panNode) {
                    this.panNode.pan.setTargetAtTime(pan, time + this.audioContext.currentTime, SonificationInstrument.rampTime);
                }
            }
            /**
             * Schedule a filter configuration at a given time offset.
             * @private
             */
            setFilterAtTime(filter, time, frequency, resonance) {
                const node = this[filter + 'Node'], audioTime = this.audioContext.currentTime + time;
                if (node) {
                    if (defined(resonance)) {
                        node.Q.setTargetAtTime(resonance, audioTime, SonificationInstrument.rampTime);
                    }
                    if (defined(frequency)) {
                        node.frequency.setTargetAtTime(frequency, audioTime, SonificationInstrument.rampTime);
                    }
                }
            }
            /**
             * Schedule a volume value at a given time offset.
             * @private
             */
            setVolumeAtTime(time, volume) {
                if (this.volumeNode) {
                    this.volumeNode.gain.setTargetAtTime(volume, time + this.audioContext.currentTime, SonificationInstrument.rampTime);
                }
            }
            /**
             * Schedule a tremolo configuration at a given time offset.
             * @private
             */
            setTremoloAtTime(time, depth, speed) {
                const audioTime = this.audioContext.currentTime + time;
                if (this.tremoloDepth && defined(depth)) {
                    this.tremoloDepth.gain.setTargetAtTime(depth, audioTime, SonificationInstrument.rampTime);
                }
                if (this.tremoloOsc && defined(speed)) {
                    this.tremoloOsc.frequency.setTargetAtTime(15 * speed, audioTime, SonificationInstrument.rampTime);
                }
            }
            /**
             * Create audio nodes according to instrument capabilities
             * @private
             */
            createNodesFromCapabilities(capabilities) {
                const ctx = this.audioContext;
                if (capabilities.pan) {
                    this.panNode = new StereoPannerNode(ctx);
                }
                if (capabilities.tremolo) {
                    this.tremoloOsc = new OscillatorNode(ctx, {
                        type: 'sine',
                        frequency: 3
                    });
                    this.tremoloDepth = new GainNode(ctx);
                    this.tremoloOsc.connect(this.tremoloDepth);
                    this.tremoloDepth.connect(this.masterVolNode.gain);
                    this.tremoloOsc.start();
                }
                if (capabilities.filters) {
                    this.lowpassNode = new BiquadFilterNode(ctx, {
                        type: 'lowpass',
                        frequency: 20000
                    });
                    this.highpassNode = new BiquadFilterNode(ctx, {
                        type: 'highpass',
                        frequency: 0
                    });
                }
            }
            /**
             * Connect audio node chain from output down to input, depending on which
             * nodes exist.
             * @private
             */
            connectCapabilityNodes(input, output) {
                [
                    this.panNode,
                    this.lowpassNode,
                    this.highpassNode,
                    input
                ].reduce((prev, cur) => (cur ?
                    (cur.connect(prev), cur) :
                    prev), output);
            }
            /**
             * Get number of notes from C0 from a string like "F#4"
             * @static
             * @private
             */
            static noteStringToC0Distance(note) {
                // eslint-disable-next-line require-unicode-regexp
                const match = note.match(/^([a-g][#b]?)([0-8])$/i), semitone = match ? match[1] : 'a', wholetone = semitone[0].toLowerCase(), accidental = semitone[1], octave = match ? parseInt(match[2], 10) : 4, accidentalOffset = accidental === '#' ?
                    1 : accidental === 'b' ? -1 : 0;
                return ({
                    c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11
                }[wholetone] || 0) + accidentalOffset + octave * 12;
            }
            /**
             * Convert a note value to a frequency.
             * @static
             * @function Highcharts.SonificationInstrument#musicalNoteToFrequency
             * @param {string|number} note
             * Note to convert. Can be a string 'c0' to 'b8' or a number of semitones
             * from c0.
             * @return {number} The converted frequency
             */
            static musicalNoteToFrequency(note) {
                const notesFromC0 = typeof note === 'string' ?
                    this.noteStringToC0Distance(note) : note;
                return 16.3516 * Math.pow(2, Math.min(notesFromC0, 107) / 12);
            }
        }
        SonificationInstrument.rampTime = SynthPatch.stopRampTime / 4;
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API definitions
         *
         * */
        /**
         * Capabilities configuration for a SonificationInstrument.
         * @requires modules/sonification
         * @interface Highcharts.SonificationInstrumentCapabilitiesOptionsObject
         */ /**
        * Whether or not instrument should be able to pan. Defaults to `true`.
        * @name Highcharts.SonificationInstrumentCapabilitiesOptionsObject#pan
        * @type {boolean|undefined}
        */ /**
        * Whether or not instrument should be able to use tremolo effects. Defaults
        * to `false`.
        * @name Highcharts.SonificationInstrumentCapabilitiesOptionsObject#tremolo
        * @type {boolean|undefined}
        */ /**
        * Whether or not instrument should be able to use filter effects. Defaults
        * to `false`.
        * @name Highcharts.SonificationInstrumentCapabilitiesOptionsObject#filters
        * @type {boolean|undefined}
        */
        /**
         * Configuration for a SonificationInstrument.
         * @requires modules/sonification
         * @interface Highcharts.SonificationInstrumentOptionsObject
         */ /**
        * The synth configuration for the instrument. Can be either a string,
        * referencing the instrument presets, or an actual SynthPatch configuration.
        * @name Highcharts.SonificationInstrumentOptionsObject#synthPatch
        * @type {Highcharts.SonificationSynthPreset|Highcharts.SynthPatchOptionsObject}
        * @sample highcharts/demo/all-instruments
        *      All instrument presets
        * @sample highcharts/sonification/custom-instrument
        *      Custom instrument preset
        */ /**
        * Define additional capabilities for the instrument, such as panning, filters,
        * and tremolo effects.
        * @name Highcharts.SonificationInstrumentOptionsObject#capabilities
        * @type {Highcharts.SonificationInstrumentCapabilitiesOptionsObject|undefined}
        */ /**
        * A track name to use for this instrument in MIDI export.
        * @name Highcharts.SonificationInstrumentOptionsObject#midiTrackName
        * @type {string|undefined}
        */
        /**
         * Options for a scheduled event for a SonificationInstrument
         * @requires modules/sonification
         * @interface Highcharts.SonificationInstrumentScheduledEventOptionsObject
         */ /**
        * Number of semitones from c0, or a note string - such as "c4" or "F#6".
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#note
        * @type {number|string|undefined}
        */ /**
        * Note frequency in Hertz. Overrides note, if both are given.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#frequency
        * @type {number|undefined}
        */ /**
        * Duration to play the note in milliseconds. If not given, the note keeps
        * playing indefinitely
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#noteDuration
        * @type {number|undefined}
        */ /**
        * Depth/intensity of the tremolo effect - which is a periodic change in
        * volume. From 0 to 1.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#tremoloDepth
        * @type {number|undefined}
        */ /**
        * Speed of the tremolo effect, from 0 to 1.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#tremoloSpeed
        * @type {number|undefined}
        */ /**
        * Stereo panning value, from -1 (left) to 1 (right).
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#pan
        * @type {number|undefined}
        */ /**
        * Volume of the instrument, from 0 to 1. Can be set independent of the
        * master/overall volume.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#volume
        * @type {number|undefined}
        */ /**
        * Frequency of the lowpass filter, in Hertz.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#lowpassFreq
        * @type {number|undefined}
        */ /**
        * Resonance of the lowpass filter, in dB. Can be negative for a dip, or
        * positive for a bump.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#lowpassResonance
        * @type {number|undefined}
        */ /**
        * Frequency of the highpass filter, in Hertz.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#highpassFreq
        * @type {number|undefined}
        */ /**
        * Resonance of the highpass filter, in dB. Can be negative for a dip, or
        * positive for a bump.
        * @name Highcharts.SonificationInstrumentScheduledEventOptionsObject#highpassResonance
        * @type {number|undefined}
        */
        (''); // Keep above doclets in JS file

        return SonificationInstrument;
    });
    _registerModule(_modules, 'Extensions/Sonification/SonificationSpeaker.js', [_modules['Core/Utilities.js']], function (U) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Class representing a speech synthesis voice.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const { pick } = U;
        /**
         * The SonificationSpeaker class. This class represents an announcer using
         * speech synthesis. It allows for scheduling speech announcements, as well
         * as speech parameter changes - including rate, volume and pitch.
         *
         * @sample highcharts/demo/sonification-navigation
         *         Demo using SonificationSpeaker directly for some announcements
         *
         * @requires modules/sonification
         *
         * @class
         * @name Highcharts.SonificationSpeaker
         *
         * @param {Highcharts.SonificationSpeakerOptionsObject} options
         *        Configuration for the speaker
         */
        class SonificationSpeaker {
            constructor(options) {
                this.options = options;
                this.masterVolume = 1;
                this.synthesis = window.speechSynthesis;
                if (typeof speechSynthesis.onvoiceschanged !== 'undefined') {
                    speechSynthesis.onvoiceschanged = this.setVoice.bind(this);
                }
                this.setVoice();
                this.scheduled = [];
            }
            /**
             * Say a message using the speaker voice. Interrupts other currently
             * speaking announcements from this speaker.
             * @function Highcharts.SonificationSpeaker#say
             * @param {string} message The message to speak.
             * @param {SonificationSpeakerOptionsObject} [options]
             * Optionally override spaker configuration.
             */
            say(message, options) {
                if (this.synthesis) {
                    this.synthesis.cancel();
                    const utterance = new SpeechSynthesisUtterance(message);
                    if (this.voice) {
                        utterance.voice = this.voice;
                    }
                    utterance.rate = options && options.rate || this.options.rate || 1;
                    utterance.pitch = options && options.pitch ||
                        this.options.pitch || 1;
                    utterance.volume = pick(options && options.volume, this.options.volume, 1) * this.masterVolume;
                    this.synthesis.speak(utterance);
                }
            }
            /**
             * Schedule a message using the speaker voice.
             * @function Highcharts.SonificationSpeaker#sayAtTime
             * @param {number} time
             * The time offset to speak at, in milliseconds from now.
             * @param {string} message
             * The message to speak.
             * @param {SonificationSpeakerOptionsObject} [options]
             * Optionally override spaker configuration.
             */
            sayAtTime(time, message, options) {
                this.scheduled.push(setTimeout(this.say.bind(this, message, options), time));
            }
            /**
             * Clear scheduled announcements, and stop current speech.
             * @function Highcharts.SonificationSpeaker#cancel
             */
            cancel() {
                this.scheduled.forEach(clearTimeout);
                this.scheduled = [];
                this.synthesis.cancel();
            }
            /**
             * Stop speech and release any used resources
             * @private
             */
            destroy() {
                // Ran on TimelineChannel.destroy
                // (polymorphism with SonificationInstrument).
                // Currently all we need to do is cancel.
                this.cancel();
            }
            /**
             * Set speaker overall/master volume modifier. This affects all
             * announcements, and applies in addition to the individual announcement
             * volume.
             * @function Highcharts.SonificationSpeaker#setMasterVolume
             * @param {number} vol Volume from 0 to 1.
             */
            setMasterVolume(vol) {
                this.masterVolume = vol;
            }
            /**
             * Set the active synthesis voice for the speaker.
             * @private
             */
            setVoice() {
                if (this.synthesis) {
                    const name = this.options.name, lang = this.options.language || 'en-US', voices = this.synthesis.getVoices(), len = voices.length;
                    let langFallback;
                    for (let i = 0; i < len; ++i) {
                        if (name && voices[i].name === name) {
                            this.voice = voices[i];
                            return;
                        }
                        if (!langFallback && voices[i].lang === lang) {
                            langFallback = voices[i];
                            if (!name) {
                                break;
                            }
                        }
                    }
                    this.voice = langFallback;
                }
            }
        }
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * Configuration for a SonificationSpeaker.
         * @requires modules/sonification
         * @interface Highcharts.SonificationSpeakerOptionsObject
         */ /**
        * Name of the voice synthesis to use. If not found, reverts to the default
        * voice for the language chosen.
        * @name Highcharts.SonificationSpeakerOptionsObject#name
        * @type {string|undefined}
        */ /**
        * The language of the voice synthesis. Defaults to `"en-US"`.
        * @name Highcharts.SonificationSpeakerOptionsObject#language
        * @type {string|undefined}
        */ /**
        * The pitch modifier of the voice. Defaults to `1`. Set higher for a higher
        * voice pitch.
        * @name Highcharts.SonificationSpeakerOptionsObject#pitch
        * @type {number|undefined}
        */ /**
        * The speech rate modifier. Defaults to `1`.
        * @name Highcharts.SonificationSpeakerOptionsObject#rate
        * @type {number|undefined}
        */ /**
        * The speech volume, from 0 to 1. Defaults to `1`.
        * @name Highcharts.SonificationSpeakerOptionsObject#volume
        * @type {number|undefined}
        */
        (''); // Keep above doclets in JS file

        return SonificationSpeaker;
    });
    _registerModule(_modules, 'Extensions/Sonification/TimelineChannel.js', [], function () {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Class representing a TimelineChannel with sonification events to play.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        /**
         * Represents a channel of TimelineEvents for an engine (either an instrument
         * or a speaker).
         * @private
         */
        class TimelineChannel {
            constructor(type, engine, showPlayMarker = false, events, muted) {
                this.type = type;
                this.engine = engine;
                this.showPlayMarker = showPlayMarker;
                this.muted = muted;
                this.events = events || [];
            }
            addEvent(event) {
                const lastEvent = this.events[this.events.length - 1];
                if (lastEvent && event.time < lastEvent.time) {
                    // Ensure we are sorted by time, so insert at the right place
                    let i = this.events.length;
                    while (i-- && this.events[i].time > event.time) { /* */ }
                    this.events.splice(i + 1, 0, event);
                }
                else {
                    this.events.push(event);
                }
                return event;
            }
            mute() {
                this.muted = true;
            }
            unmute() {
                this.muted = false;
            }
            cancel() {
                this.engine.cancel();
            }
            destroy() {
                this.engine.destroy();
            }
        }
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * A TimelineEvent object represents a scheduled audio event to play for a
         * SonificationTimeline.
         * @requires modules/sonification
         * @interface Highcharts.SonificationTimelineEvent
         */ /**
        * Time is given in milliseconds, where 0 is now.
        * @name Highcharts.SonificationTimelineEvent#time
        * @type {number}
        */ /**
        * A reference to a data point related to the TimelineEvent. Populated when
        * sonifying points.
        * @name Highcharts.SonificationTimelineEvent#relatedPoint
        * @type {Highcharts.Point|undefined}
        */ /**
        * Options for an instrument event to be played.
        * @name Highcharts.SonificationTimelineEvent#instrumentEventOptions
        * @type {Highcharts.SonificationInstrumentScheduledEventOptionsObject|undefined}
        */ /**
        * Options for a speech event to be played.
        * @name Highcharts.SonificationTimelineEvent#speechOptions
        * @type {Highcharts.SonificationSpeakerOptionsObject|undefined}
        */ /**
        * The message to speak for speech events.
        * @name Highcharts.SonificationTimelineEvent#message
        * @type {string|undefined}
        */ /**
        * Callback to call when playing the event.
        * @name Highcharts.SonificationTimelineEvent#callback
        * @type {Function|undefined}
        */
        (''); // Keep above doclets in JS file

        return TimelineChannel;
    });
    _registerModule(_modules, 'Extensions/Sonification/MIDI.js', [_modules['Extensions/Sonification/SonificationInstrument.js'], _modules['Core/Utilities.js']], function (SonificationInstrument, U) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Small MIDI file writer for sonification export.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        /* eslint-disable no-multi-spaces */
        const { pick } = U;
        const freqToNote = (f) => Math.round(12 * Math.log(f) / Math.LN2 - 48.37632), b = (byte, n) => n >>> 8 * byte & 0xFF, getHeader = (nTracks) => [
            0x4D, 0x54, 0x68, 0x64,
            0, 0, 0, 6,
            0, nTracks > 1 ? 1 : 0,
            b(1, nTracks), b(0, nTracks),
            // SMTPE: 0xE7 0x28
            // -25/40 time div gives us millisecond SMTPE, but not widely supported.
            1, 0xF4 // HD_TIMEDIV, 500 ticks per beat = millisecond at 120bpm
        ], timeInfo = [0, 0xFF, 0x51, 0x03, 0x07, 0xA1, 0x20], // META_TEMPO
        varLenEnc = (n) => {
            let buf = n & 0x7F;
            const res = [];
            while (n >>= 7) { // eslint-disable-line no-cond-assign
                buf <<= 8;
                buf |= (n & 0x7F) | 0x80;
            }
            while (true) { // eslint-disable-line no-constant-condition
                res.push(buf & 0xFF);
                if (buf & 0x80) {
                    buf >>= 8;
                }
                else {
                    break;
                }
            }
            return res;
        }, toMIDIEvents = (events) => {
            let cachedVel, cachedDur;
            const res = [], add = (el) => {
                let ix = res.length;
                while (ix-- && res[ix].timeMS > el.timeMS) { /* */ }
                res.splice(ix + 1, 0, el);
            };
            events.forEach((e) => {
                const o = e.instrumentEventOptions || {}, t = e.time, dur = cachedDur = pick(o.noteDuration, cachedDur), tNOF = dur && e.time + dur, ctrl = [{
                        valMap: (n) => 64 + 63 * n & 0x7F,
                        data: {
                            0x0A: o.pan,
                            0x5C: o.tremoloDepth,
                            0x5D: o.tremoloSpeed
                        }
                    }, {
                        valMap: (n) => 127 * n / 20000 & 0x7F,
                        data: {
                            0x4A: o.lowpassFreq,
                            0x4B: o.highpassFreq
                        }
                    }, {
                        valMap: (n) => 63 * Math.min(18, Math.max(-18, n)) / 18 + 63 & 0x7F,
                        data: {
                            0x47: o.lowpassResonance,
                            0x4C: o.highpassResonance
                        }
                    }], v = cachedVel = o.volume === void 0 ?
                    pick(cachedVel, 127) : 127 * o.volume & 0x7F, freq = o.frequency, note = o.note || 0, noteVal = 12 + (freq ? freqToNote(freq) : // MIDI note #0 is C-1
                    typeof note === 'string' ? SonificationInstrument
                        .noteStringToC0Distance(note) : note) & 0x7F;
                // CTRL_CHANGE events
                ctrl.forEach((ctrlDef) => Object.keys(ctrlDef.data)
                    .forEach((ctrlSignal) => {
                    const val = ctrlDef.data[ctrlSignal];
                    if (val !== void 0) {
                        add({
                            timeMS: t,
                            type: 'CTRL_CHG',
                            data: [0xB0, parseInt(ctrlSignal, 10),
                                ctrlDef.valMap(val)]
                        });
                    }
                }));
                // NON/NOF
                if (tNOF) {
                    add({ timeMS: t, type: 'NON', data: [0x90, noteVal, v] });
                    add({ timeMS: tNOF, type: 'NOF', data: [0x80, noteVal, v] });
                }
            });
            return res;
        }, getMetaEvents = (midiTrackName, midiInstrument) => {
            const events = [];
            if (midiInstrument) {
                // Program Change MIDI event
                events.push(0, 0xC0, midiInstrument & 0x7F);
            }
            if (midiTrackName) {
                // Track name meta event
                const textArr = [];
                for (let i = 0; i < midiTrackName.length; ++i) {
                    const code = midiTrackName.charCodeAt(i);
                    if (code < 128) { // Keep ASCII only
                        textArr.push(code);
                    }
                }
                return events.concat([0, 0xFF, 0x03], varLenEnc(textArr.length), textArr);
            }
            return events;
        }, getTrackChunk = (events, addTimeInfo, midiTrackName, midiInstrument) => {
            let prevTime = 0;
            const metaEvents = getMetaEvents(midiTrackName, midiInstrument), trackEvents = toMIDIEvents(events).reduce((data, e) => {
                const t = varLenEnc(e.timeMS - prevTime);
                prevTime = e.timeMS;
                return data.concat(t, e.data);
            }, []);
            const trackEnd = [0, 0xFF, 0x2F, 0], size = (addTimeInfo ? timeInfo.length : 0) +
                metaEvents.length +
                trackEvents.length + trackEnd.length;
            return [
                0x4D, 0x54, 0x72, 0x6B,
                b(3, size), b(2, size),
                b(1, size), b(0, size)
            ].concat(addTimeInfo ? timeInfo : [], metaEvents, trackEvents, trackEnd // SYSEX_TRACK_END
            );
        };
        /**
         * Get MIDI data from a set of Timeline instrument channels.
         *
         * Outputs multi-track MIDI for Timelines with multiple channels.
         *
         * @private
         */
        function toMIDI(channels) {
            const channelsToAdd = channels.filter((c) => !!c.events.length), numCh = channelsToAdd.length, multiCh = numCh > 1;
            return new Uint8Array(getHeader(multiCh ? numCh + 1 : numCh).concat(multiCh ? getTrackChunk([], true) : [], // Time info only
            channelsToAdd.reduce((chunks, channel) => {
                const engine = channel.engine;
                return chunks.concat(getTrackChunk(channel.events, !multiCh, engine.midiTrackName, engine.midiInstrument));
            }, [])));
        }

        return toMIDI;
    });
    _registerModule(_modules, 'Extensions/DownloadURL.js', [_modules['Core/Globals.js']], function (Highcharts) {
        /* *
         *
         *  (c) 2015-2021 Oystein Moseng
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         *  Mixin for downloading content in the browser
         *
         * */
        const { isSafari } = Highcharts;
        const win = Highcharts.win, doc = win.document, domurl = win.URL || win.webkitURL || win;
        /**
         * Convert base64 dataURL to Blob if supported, otherwise returns undefined.
         * @private
         * @function Highcharts.dataURLtoBlob
         * @param {string} dataURL
         *        URL to convert
         * @return {string|undefined}
         *         Blob
         */
        const dataURLtoBlob = Highcharts.dataURLtoBlob = function (dataURL) {
            const parts = dataURL
                .replace(/filename=.*;/, '')
                .match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
            if (parts &&
                parts.length > 3 &&
                (win.atob) &&
                win.ArrayBuffer &&
                win.Uint8Array &&
                win.Blob &&
                (domurl.createObjectURL)) {
                // Try to convert data URL to Blob
                const binStr = win.atob(parts[3]), buf = new win.ArrayBuffer(binStr.length), binary = new win.Uint8Array(buf);
                for (let i = 0; i < binary.length; ++i) {
                    binary[i] = binStr.charCodeAt(i);
                }
                const blob = new win.Blob([binary], { 'type': parts[1] });
                return domurl.createObjectURL(blob);
            }
        };
        /**
         * Download a data URL in the browser. Can also take a blob as first param.
         *
         * @private
         * @function Highcharts.downloadURL
         * @param {string|global.URL} dataURL
         *        The dataURL/Blob to download
         * @param {string} filename
         *        The name of the resulting file (w/extension)
         * @return {void}
         */
        const downloadURL = Highcharts.downloadURL = function (dataURL, filename) {
            const nav = win.navigator, a = doc.createElement('a');
            // IE specific blob implementation
            // Don't use for normal dataURLs
            if (typeof dataURL !== 'string' &&
                !(dataURL instanceof String) &&
                nav.msSaveOrOpenBlob) {
                nav.msSaveOrOpenBlob(dataURL, filename);
                return;
            }
            dataURL = `${dataURL}`;
            // Some browsers have limitations for data URL lengths. Try to convert to
            // Blob or fall back. Edge always needs that blob.
            const isOldEdgeBrowser = /Edge\/\d+/.test(nav.userAgent);
            // Safari on iOS needs Blob in order to download PDF
            const safariBlob = (isSafari &&
                typeof dataURL === 'string' &&
                dataURL.indexOf('data:application/pdf') === 0);
            if (safariBlob || isOldEdgeBrowser || dataURL.length > 2000000) {
                dataURL = dataURLtoBlob(dataURL) || '';
                if (!dataURL) {
                    throw new Error('Failed to convert to blob');
                }
            }
            // Try HTML5 download attr if supported
            if (typeof a.download !== 'undefined') {
                a.href = dataURL;
                a.download = filename; // HTML5 download attribute
                doc.body.appendChild(a);
                a.click();
                doc.body.removeChild(a);
            }
            else {
                // No download attr, just opening data URI
                try {
                    const windowRef = win.open(dataURL, 'chart');
                    if (typeof windowRef === 'undefined' || windowRef === null) {
                        throw new Error('Failed to open window');
                    }
                }
                catch (e) {
                    // window.open failed, trying location.href
                    win.location.href = dataURL;
                }
            }
        };
        const DownloadURL = {
            dataURLtoBlob,
            downloadURL
        };

        return DownloadURL;
    });
    _registerModule(_modules, 'Extensions/Sonification/SonificationTimeline.js', [_modules['Extensions/Sonification/TimelineChannel.js'], _modules['Extensions/Sonification/MIDI.js'], _modules['Extensions/DownloadURL.js'], _modules['Core/Utilities.js']], function (TimelineChannel, toMIDI, DU, U) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Class representing a Timeline with sonification events to play.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const { downloadURL } = DU;
        const { defined, find, merge } = U;
        /**
         * Get filtered channels. Timestamps are compensated, so that the first
         * event starts immediately.
         * @private
         */
        function filterChannels(filter, channels) {
            const filtered = channels.map((channel) => {
                channel.cancel();
                return {
                    channel,
                    filteredEvents: channel.muted ?
                        [] : channel.events.filter(filter)
                };
            }), minTime = filtered.reduce((acc, cur) => Math.min(acc, cur.filteredEvents.length ?
                cur.filteredEvents[0].time : Infinity), Infinity);
            return filtered.map((c) => (new TimelineChannel(c.channel.type, c.channel.engine, c.channel.showPlayMarker, c.filteredEvents.map((e) => merge(e, { time: e.time - minTime })), c.channel.muted)));
        }
        /**
         * The SonificationTimeline class. This class represents a timeline of
         * audio events scheduled to play. It provides functionality for manipulating
         * and navigating the timeline.
         * @private
         */
        class SonificationTimeline {
            constructor(options, chart) {
                this.chart = chart;
                this.isPaused = false;
                this.isPlaying = false;
                this.channels = [];
                this.scheduledCallbacks = [];
                this.playTimestamp = 0;
                this.resumeFromTime = 0;
                this.options = options || {};
            }
            // Add a channel, optionally with events, to be played.
            // Note: Only one speech channel is supported at a time.
            addChannel(type, engine, showPlayMarker = false, events) {
                if (type === 'instrument' &&
                    !engine.scheduleEventAtTime ||
                    type === 'speech' &&
                        !engine.sayAtTime) {
                    throw new Error('Highcharts Sonification: Invalid channel engine.');
                }
                const channel = new TimelineChannel(type, engine, showPlayMarker, events);
                this.channels.push(channel);
                return channel;
            }
            // Play timeline, optionally filtering out only some of the events to play.
            // Note that if not all instrument parameters are updated on each event,
            // parameters may update differently depending on the events filtered out,
            // since some of the events that update parameters can be filtered out too.
            // The filterPersists argument determines whether or not the filter persists
            // after e.g. pausing and resuming. Usually this should be true.
            play(filter, filterPersists = true, resetAfter = true, onEnd) {
                if (this.isPlaying) {
                    this.cancel();
                }
                else {
                    this.clearScheduledCallbacks();
                }
                this.onEndArgument = onEnd;
                this.playTimestamp = Date.now();
                this.resumeFromTime = 0;
                this.isPaused = false;
                this.isPlaying = true;
                const skipThreshold = this.options.skipThreshold || 2, onPlay = this.options.onPlay, showTooltip = this.options.showTooltip, showCrosshair = this.options.showCrosshair, channels = filter ?
                    filterChannels(filter, this.playingChannels || this.channels) :
                    this.channels, getEventKeysSignature = (e) => Object.keys(e.speechOptions || {})
                    .concat(Object.keys(e.instrumentEventOptions || {}))
                    .join(), pointsPlayed = [];
                if (filterPersists) {
                    this.playingChannels = channels;
                }
                if (onPlay) {
                    onPlay({ chart: this.chart, timeline: this });
                }
                let maxTime = 0;
                channels.forEach((channel) => {
                    if (channel.muted) {
                        return;
                    }
                    const numEvents = channel.events.length;
                    let lastCallbackTime = -Infinity, lastEventTime = -Infinity, lastEventKeys = '';
                    maxTime = Math.max(channel.events[numEvents - 1] &&
                        channel.events[numEvents - 1].time || 0, maxTime);
                    for (let i = 0; i < numEvents; ++i) {
                        const e = channel.events[i], keysSig = getEventKeysSignature(e);
                        // Optimize by skipping extremely close events (<2ms apart by
                        // default), as long as they don't introduce new event options
                        if (keysSig === lastEventKeys &&
                            e.time - lastEventTime < skipThreshold) {
                            continue;
                        }
                        lastEventKeys = keysSig;
                        lastEventTime = e.time;
                        if (channel.type === 'instrument') {
                            channel.engine
                                .scheduleEventAtTime(e.time / 1000, e.instrumentEventOptions || {});
                        }
                        else {
                            channel.engine.sayAtTime(e.time, e.message || '', e.speechOptions || {});
                        }
                        const point = e.relatedPoint, chart = point && point.series && point.series.chart, needsCallback = e.callback ||
                            point && (showTooltip || showCrosshair) &&
                                channel.showPlayMarker !== false &&
                                (e.time - lastCallbackTime > 50 || i === numEvents - 1);
                        if (point) {
                            pointsPlayed.push(point);
                        }
                        if (needsCallback) {
                            this.scheduledCallbacks.push(setTimeout(() => {
                                if (e.callback) {
                                    e.callback();
                                }
                                if (point) {
                                    if (showCrosshair) {
                                        const s = point.series;
                                        if (s && s.xAxis && s.xAxis.crosshair) {
                                            s.xAxis.drawCrosshair(void 0, point);
                                        }
                                        if (s && s.yAxis && s.yAxis.crosshair) {
                                            s.yAxis.drawCrosshair(void 0, point);
                                        }
                                    }
                                    if (showTooltip && !(
                                    // Don't re-hover if shared tooltip
                                    chart && chart.hoverPoints &&
                                        chart.hoverPoints.length > 1 &&
                                        find(chart.hoverPoints, (p) => p === point) &&
                                        // Stock issue w/Navigator
                                        point.onMouseOver)) {
                                        point.onMouseOver();
                                    }
                                }
                            }, e.time));
                            lastCallbackTime = e.time;
                        }
                    }
                });
                const onEndOpt = this.options.onEnd, onStop = this.options.onStop;
                this.scheduledCallbacks.push(setTimeout(() => {
                    const chart = this.chart, context = { chart, timeline: this, pointsPlayed };
                    this.isPlaying = false;
                    if (resetAfter) {
                        this.resetPlayState();
                    }
                    if (onStop) {
                        onStop(context);
                    }
                    if (onEndOpt) {
                        onEndOpt(context);
                    }
                    if (onEnd) {
                        onEnd(context);
                    }
                    if (chart) {
                        if (chart.tooltip) {
                            chart.tooltip.hide(0);
                        }
                        if (chart.hoverSeries) {
                            chart.hoverSeries.onMouseOut();
                        }
                        chart.axes.forEach((a) => a.hideCrosshair());
                    }
                }, maxTime + 250));
                this.resumeFromTime = filterPersists ? maxTime : this.getLength();
            }
            // Pause for later resuming. Returns current timestamp to resume from.
            pause() {
                this.isPaused = true;
                this.cancel();
                this.resumeFromTime = Date.now() - this.playTimestamp - 10;
                return this.resumeFromTime;
            }
            // Get current time
            getCurrentTime() {
                return this.isPlaying ?
                    Date.now() - this.playTimestamp :
                    this.resumeFromTime;
            }
            // Get length of timeline in milliseconds
            getLength() {
                return this.channels.reduce((maxTime, channel) => {
                    const lastEvent = channel.events[channel.events.length - 1];
                    return lastEvent ? Math.max(lastEvent.time, maxTime) : maxTime;
                }, 0);
            }
            // Resume from paused
            resume() {
                if (this.playingChannels) {
                    const resumeFrom = this.resumeFromTime - 50;
                    this.play((e) => e.time > resumeFrom, false, false, this.onEndArgument);
                    this.playTimestamp -= resumeFrom;
                }
                else {
                    this.play(void 0, false, false, this.onEndArgument);
                }
            }
            // Play a short moment, then pause, setting the cursor to the final
            // event's time.
            anchorPlayMoment(eventFilter, onEnd) {
                if (this.isPlaying) {
                    this.pause();
                }
                let finalEventTime = 0;
                this.play((e, ix, arr) => {
                    // We have to keep track of final event time ourselves, since
                    // play() messes with the time internally upon filtering.
                    const res = eventFilter(e, ix, arr);
                    if (res && e.time > finalEventTime) {
                        finalEventTime = e.time;
                    }
                    return res;
                }, false, false, onEnd);
                this.playingChannels = this.playingChannels || this.channels;
                this.isPaused = true;
                this.isPlaying = false;
                this.resumeFromTime = finalEventTime;
            }
            // Play event(s) occurring next/prev from paused state.
            playAdjacent(next, onEnd, onBoundaryHit, eventFilter) {
                if (this.isPlaying) {
                    this.pause();
                }
                const fromTime = this.resumeFromTime, closestTime = this.channels.reduce((time, channel) => {
                    // Adapted binary search since events are sorted by time
                    const events = eventFilter ?
                        channel.events.filter(eventFilter) : channel.events;
                    let s = 0, e = events.length, lastValidTime = time;
                    while (s < e) {
                        const mid = (s + e) >> 1, t = events[mid].time, cmp = t - fromTime;
                        if (cmp > 0) { // ahead
                            if (next && t < lastValidTime) {
                                lastValidTime = t;
                            }
                            e = mid;
                        }
                        else if (cmp < 0) { // behind
                            if (!next && t > lastValidTime) {
                                lastValidTime = t;
                            }
                            s = mid + 1;
                        }
                        else { // same as from time
                            if (next) {
                                s = mid + 1;
                            }
                            else {
                                e = mid;
                            }
                        }
                    }
                    return lastValidTime;
                }, next ? Infinity : -Infinity), margin = 0.02;
                if (closestTime === Infinity || closestTime === -Infinity) {
                    if (onBoundaryHit) {
                        onBoundaryHit({
                            chart: this.chart, timeline: this, attemptedNext: next
                        });
                    }
                    return;
                }
                this.anchorPlayMoment((e, ix, arr) => {
                    const withinTime = next ?
                        e.time > fromTime && e.time <= closestTime + margin :
                        e.time < fromTime && e.time >= closestTime - margin;
                    return eventFilter ? withinTime && eventFilter(e, ix, arr) :
                        withinTime;
                }, onEnd);
            }
            // Play event with related point, where the value of a prop on the
            // related point is closest to a target value.
            // Note: not very efficient.
            playClosestToPropValue(prop, targetVal, onEnd, onBoundaryHit, eventFilter) {
                const filter = (e, ix, arr) => !!(eventFilter ?
                    eventFilter(e, ix, arr) && e.relatedPoint :
                    e.relatedPoint);
                let closestValDiff = Infinity, closestEvent = null;
                (this.playingChannels || this.channels).forEach((channel) => {
                    const events = channel.events;
                    let i = events.length;
                    while (i--) {
                        if (!filter(events[i], i, events)) {
                            continue;
                        }
                        const val = events[i].relatedPoint[prop], diff = defined(val) && Math.abs(targetVal - val);
                        if (diff !== false && diff < closestValDiff) {
                            closestValDiff = diff;
                            closestEvent = events[i];
                        }
                    }
                });
                if (closestEvent) {
                    this.play((e) => !!(closestEvent &&
                        e.time < closestEvent.time + 1 &&
                        e.time > closestEvent.time - 1 &&
                        e.relatedPoint === closestEvent.relatedPoint), false, false, onEnd);
                    this.playingChannels = this.playingChannels || this.channels;
                    this.isPaused = true;
                    this.isPlaying = false;
                    this.resumeFromTime = closestEvent.time;
                }
                else if (onBoundaryHit) {
                    onBoundaryHit({ chart: this.chart, timeline: this });
                }
            }
            // Get timeline events that are related to a certain point.
            // Note: Point grouping may cause some points not to have a
            //  related point in the timeline.
            getEventsForPoint(point) {
                return this.channels.reduce((events, channel) => {
                    const pointEvents = channel.events
                        .filter((e) => e.relatedPoint === point);
                    return events.concat(pointEvents);
                }, []);
            }
            // Divide timeline into 100 parts of equal time, and play one of them.
            // Used for scrubbing.
            // Note: Should be optimized?
            playSegment(segment, onEnd) {
                const numSegments = 100;
                const eventTimes = {
                    first: Infinity,
                    last: -Infinity
                };
                this.channels.forEach((c) => {
                    if (c.events.length) {
                        eventTimes.first = Math.min(c.events[0].time, eventTimes.first);
                        eventTimes.last = Math.max(c.events[c.events.length - 1].time, eventTimes.last);
                    }
                });
                if (eventTimes.first < Infinity) {
                    const segmentSize = (eventTimes.last - eventTimes.first) / numSegments, fromTime = eventTimes.first + segment * segmentSize, toTime = fromTime + segmentSize;
                    // Binary search, do we have any events within time range?
                    if (!this.channels.some((c) => {
                        const events = c.events;
                        let s = 0, e = events.length;
                        while (s < e) {
                            const mid = (s + e) >> 1, t = events[mid].time;
                            if (t < fromTime) { // behind
                                s = mid + 1;
                            }
                            else if (t > toTime) { // ahead
                                e = mid;
                            }
                            else {
                                return true;
                            }
                        }
                        return false;
                    })) {
                        return; // If not, don't play - avoid cancelling current play
                    }
                    this.play((e) => e.time >= fromTime && e.time <= toTime, false, false, onEnd);
                    this.playingChannels = this.playingChannels || this.channels;
                    this.isPaused = true;
                    this.isPlaying = false;
                    this.resumeFromTime = toTime;
                }
            }
            // Get last played / current point
            // Since events are scheduled we can't just store points as we play them
            getLastPlayedPoint(filter) {
                const curTime = this.getCurrentTime(), channels = this.playingChannels || this.channels;
                let closestDiff = Infinity, closestPoint = null;
                channels.forEach((c) => {
                    const events = c.events.filter((e, ix, arr) => !!(e.relatedPoint && e.time <= curTime &&
                        (!filter || filter(e, ix, arr)))), closestEvent = events[events.length - 1];
                    if (closestEvent) {
                        const closestTime = closestEvent.time, diff = Math.abs(closestTime - curTime);
                        if (diff < closestDiff) {
                            closestDiff = diff;
                            closestPoint = closestEvent.relatedPoint;
                        }
                    }
                });
                return closestPoint;
            }
            // Reset play/pause state so that a later call to resume() will start over
            reset() {
                if (this.isPlaying) {
                    this.cancel();
                }
                this.resetPlayState();
            }
            cancel() {
                const onStop = this.options.onStop;
                if (onStop) {
                    onStop({ chart: this.chart, timeline: this });
                }
                this.isPlaying = false;
                this.channels.forEach((c) => c.cancel());
                if (this.playingChannels && this.playingChannels !== this.channels) {
                    this.playingChannels.forEach((c) => c.cancel());
                }
                this.clearScheduledCallbacks();
                this.resumeFromTime = 0;
            }
            destroy() {
                this.cancel();
                if (this.playingChannels && this.playingChannels !== this.channels) {
                    this.playingChannels.forEach((c) => c.destroy());
                }
                this.channels.forEach((c) => c.destroy());
            }
            setMasterVolume(vol) {
                this.channels.forEach((c) => c.engine.setMasterVolume(vol));
            }
            getMIDIData() {
                return toMIDI(this.channels.filter((c) => c.type === 'instrument'));
            }
            downloadMIDI(filename) {
                const data = this.getMIDIData(), name = (filename ||
                    this.chart &&
                        this.chart.options.title &&
                        this.chart.options.title.text ||
                    'chart') + '.mid', blob = new Blob([data], { type: 'application/octet-stream' }), url = window.URL.createObjectURL(blob);
                downloadURL(url, name);
                window.URL.revokeObjectURL(url);
            }
            resetPlayState() {
                delete this.playingChannels;
                delete this.onEndArgument;
                this.playTimestamp = this.resumeFromTime = 0;
                this.isPaused = false;
            }
            clearScheduledCallbacks() {
                this.scheduledCallbacks.forEach(clearTimeout);
                this.scheduledCallbacks = [];
            }
        }
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * Filter callback for filtering timeline events on a SonificationTimeline.
         *
         * @callback Highcharts.SonificationTimelineFilterCallback
         *
         * @param {Highcharts.SonificationTimelineEvent} e TimelineEvent being filtered
         *
         * @param {number} ix Index of TimelineEvent in current event array
         *
         * @param {Array<Highcharts.SonificationTimelineEvent>} arr The current event array
         *
         * @return {boolean}
         * The function should return true if the TimelineEvent should be included,
         * false otherwise.
         */
        (''); // Keep above doclets in JS file

        return SonificationTimeline;
    });
    _registerModule(_modules, 'Extensions/Sonification/TimelineFromChart.js', [_modules['Extensions/Sonification/SonificationTimeline.js'], _modules['Extensions/Sonification/SonificationInstrument.js'], _modules['Extensions/Sonification/SonificationSpeaker.js'], _modules['Core/Utilities.js'], _modules['Core/Templating.js']], function (SonificationTimeline, SonificationInstrument, SonificationSpeaker, U, T) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Build a timeline from a chart.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const { clamp, defined, extend, getNestedProperty, merge, pick } = U;
        const { format } = T;
        const isNoteDefinition = (str) => 
        // eslint-disable-next-line require-unicode-regexp
        (/^([a-g][#b]?)[0-8]$/i).test(str);
        /**
         * Get the value of a point property from string.
         * @private
         */
        function getPointPropValue(point, prop) {
            let ret;
            if (prop) {
                ret = point[prop];
                if (typeof ret === 'number') {
                    return ret;
                }
                ret = getNestedProperty(prop, point);
            }
            return typeof ret === 'number' ? ret : void 0;
        }
        /**
         * Get chart wide min/max for a set of props, as well as per
         * series min/max for selected props.
         * @private
         */
        function getChartExtremesForProps(chart, props, perSeriesProps) {
            const series = chart.series, numProps = props.length, numSeriesProps = perSeriesProps.length, initCache = (propList) => propList.reduce((cache, prop) => {
                ((cache[prop] = { min: Infinity, max: -Infinity }), cache);
                return cache;
            }, {}), updateCache = (cache, point, prop) => {
                let val = point[prop];
                if (val === void 0) {
                    val = getNestedProperty(prop, point);
                }
                if (typeof val === 'number') {
                    cache[prop].min = Math.min(cache[prop].min, val);
                    cache[prop].max = Math.max(cache[prop].max, val);
                }
            }, globalExtremes = initCache(props);
            let i = series.length;
            const allSeriesExtremes = new Array(i);
            while (i--) {
                const seriesExtremes = initCache(perSeriesProps);
                const opts = series[i].options;
                if (!series[i].visible ||
                    opts && opts.sonification && opts.sonification.enabled === false) {
                    continue;
                }
                const points = series[i].points || [];
                let j = points.length;
                while (j--) {
                    let k = numProps;
                    while (k--) {
                        updateCache(globalExtremes, points[j], props[k]);
                    }
                    k = numSeriesProps;
                    while (k--) {
                        updateCache(seriesExtremes, points[j], perSeriesProps[k]);
                    }
                }
                allSeriesExtremes[i] = seriesExtremes;
            }
            return {
                globalExtremes,
                seriesExtremes: allSeriesExtremes
            };
        }
        /**
         * Build a cache of prop extremes for the chart. Goes through
         * options to find out which props are needed.
         * @private
         */
        function getPropMetrics(chart) {
            const globalOpts = chart.options.sonification ||
                {}, defaultInstrMapping = (globalOpts.defaultInstrumentOptions || {})
                .mapping || { time: 'x', pitch: 'y' }, defaultSpeechMapping = globalOpts.defaultSpeechOptions &&
                globalOpts.defaultSpeechOptions.mapping || {}, seriesTimeProps = [], commonTimeProps = {}, addTimeProp = (prop, seriesIx) => {
                if (seriesIx !== null) {
                    seriesTimeProps[seriesIx] =
                        seriesTimeProps[seriesIx] || {};
                    seriesTimeProps[seriesIx][prop] = true;
                }
                else {
                    commonTimeProps[prop] = true;
                }
            }, props = {}, perSeriesProps = {}, addPropFromMappingParam = (param, val, seriesIx) => {
                const removeInvertedFlag = (s) => (s.charAt(0) === '-' ? s.slice(1) : s);
                if (typeof val === 'string' && param !== 'text') {
                    if (param === 'pitch' && isNoteDefinition(val)) {
                        return;
                    }
                    if (param === 'time') {
                        perSeriesProps[val] = true;
                        addTimeProp(val, seriesIx);
                    }
                    props[removeInvertedFlag(val)] = true;
                    return;
                }
                const paramOpts = val;
                if (paramOpts && paramOpts.mapTo &&
                    typeof paramOpts.mapTo === 'string') {
                    const mapTo = removeInvertedFlag(paramOpts.mapTo);
                    if (param === 'time') {
                        addTimeProp(mapTo, seriesIx);
                    }
                    if (param === 'time' || paramOpts.within === 'series') {
                        perSeriesProps[mapTo] = true;
                    }
                    props[mapTo] = true;
                    return;
                }
                if (['tremolo', 'lowpass', 'highpass'].indexOf(param) > -1 &&
                    typeof val === 'object') {
                    Object.keys(val).forEach((subParam) => addPropFromMappingParam(subParam, val[subParam], seriesIx));
                }
            }, addPropsFromMappingOptions = (mapping, seriesIx) => {
                (Object.keys(mapping)).forEach((param) => addPropFromMappingParam(param, mapping[param], seriesIx));
            }, addPropsFromContextTracks = (tracks) => tracks.forEach((track) => {
                props[track.valueProp || 'x'] =
                    perSeriesProps[track.valueProp || 'x'] = true;
            });
            addPropsFromMappingOptions(defaultInstrMapping, null);
            addPropsFromMappingOptions(defaultSpeechMapping, null);
            addPropsFromContextTracks(globalOpts.globalContextTracks || []);
            const hasCommonTimeProps = Object.keys(commonTimeProps).length;
            chart.series.forEach((series) => {
                const sOpts = series.options.sonification;
                if (series.visible && !(sOpts && sOpts.enabled === false)) {
                    if (hasCommonTimeProps) {
                        seriesTimeProps[series.index] = merge(commonTimeProps);
                    }
                    if (sOpts) {
                        const defaultInstrMapping = (sOpts.defaultInstrumentOptions || {}).mapping, defaultSpeechMapping = (sOpts.defaultSpeechOptions || {}).mapping;
                        if (defaultInstrMapping) {
                            addPropsFromMappingOptions(defaultInstrMapping, series.index);
                        }
                        if (defaultSpeechMapping) {
                            addPropsFromMappingOptions(defaultSpeechMapping, series.index);
                        }
                        addPropsFromContextTracks(sOpts.contextTracks || []);
                        (sOpts.tracks || [])
                            .concat(sOpts.contextTracks || [])
                            .forEach((trackOpts) => {
                            if (trackOpts.mapping) {
                                addPropsFromMappingOptions(trackOpts.mapping, series.index);
                            }
                        });
                    }
                }
            });
            return Object.assign({ seriesTimeProps }, getChartExtremesForProps(chart, Object.keys(props), Object.keys(perSeriesProps)));
        }
        /**
         * Map a relative value onto a virtual axis.
         * @private
         */
        function mapToVirtualAxis(value, valueExtremes, virtualAxisExtremes, invert, logarithmic // Virtual axis is logarithmic
        ) {
            const lenValueAxis = valueExtremes.max - valueExtremes.min;
            if (lenValueAxis <= 0) {
                return virtualAxisExtremes.min;
            }
            const lenVirtualAxis = virtualAxisExtremes.max - virtualAxisExtremes.min, valueDelta = value - valueExtremes.min;
            let virtualValueDelta = lenVirtualAxis * valueDelta / lenValueAxis;
            if (logarithmic) {
                const log = valueExtremes.min > 0 ?
                    // Normal log formula
                    (x) => Math.log(x) / Math.LOG10E :
                    // Negative logarithmic support needed
                    (x) => {
                        let adjustedNum = Math.abs(x);
                        if (adjustedNum < 10) {
                            adjustedNum += (10 - adjustedNum) / 10;
                        }
                        const res = Math.log(adjustedNum) / Math.LN10;
                        return x < 0 ? -res : res;
                    };
                const logValMin = log(valueExtremes.min);
                virtualValueDelta = lenVirtualAxis *
                    (log(value) - logValMin) /
                    (log(valueExtremes.max) - logValMin);
            }
            const val = invert ?
                virtualAxisExtremes.max - virtualValueDelta :
                virtualAxisExtremes.min + virtualValueDelta;
            return clamp(val, virtualAxisExtremes.min, virtualAxisExtremes.max);
        }
        /**
         * Get the value of a mapped parameter for a point.
         * @private
         */
        function getMappingParameterValue(context, propMetrics, useSeriesExtremes, defaultMapping, mappingOptions, contextValueProp) {
            if (typeof mappingOptions === 'number') {
                return mappingOptions;
            }
            if (typeof mappingOptions === 'function') {
                return mappingOptions(extend({ time: 0 }, context));
            }
            let mapTo = mappingOptions, mapFunc = defaultMapping.mapFunction, min = defaultMapping.min, max = defaultMapping.max, within = defaultMapping.within, scale;
            if (typeof mappingOptions === 'object') {
                mapTo = mappingOptions.mapTo;
                mapFunc = mappingOptions.mapFunction || mapFunc;
                min = pick(mappingOptions.min, min);
                max = pick(mappingOptions.max, max);
                within = mappingOptions.within || defaultMapping.within;
                scale = mappingOptions.scale;
            }
            if (!mapTo) {
                return null;
            }
            const isInverted = mapTo.charAt(0) === '-';
            if (isInverted) {
                mapTo = mapTo.slice(1);
            }
            let value = context.value;
            const useContextValue = mapTo === 'value' && value !== void 0 &&
                contextValueProp;
            if (!useContextValue) {
                const fixedValue = mappingOptions.value;
                if (fixedValue !== void 0) {
                    value = fixedValue;
                }
                else {
                    if (!context.point) {
                        return null;
                    }
                    value = context.point[mapTo];
                }
                if (value === void 0) {
                    value = getNestedProperty(mapTo, context.point);
                }
            }
            if (typeof value !== 'number' || value === null) {
                return null;
            }
            // Figure out extremes for this mapping
            let extremes = null;
            if (context.point) {
                if (within === 'xAxis' || within === 'yAxis') {
                    const axis = context.point.series[within];
                    if (axis && defined(axis.dataMin) && defined(axis.dataMax)) {
                        extremes = {
                            min: axis.dataMin,
                            max: axis.dataMax
                        };
                    }
                }
                else if ((within === 'series' || useSeriesExtremes) &&
                    context.point.series) {
                    extremes = propMetrics.seriesExtremes[context.point.series.index][useContextValue ? contextValueProp : mapTo];
                }
            }
            if (!extremes) { // Chart extremes
                extremes = propMetrics.globalExtremes[useContextValue ? contextValueProp : mapTo];
            }
            if (scale) {
                // Build a musical scale from array
                const scaleAxis = [], minOctave = Math.floor(min / 12), maxOctave = Math.ceil(max / 12) + 1, lenScale = scale.length;
                for (let octave = minOctave; octave < maxOctave; ++octave) {
                    for (let scaleIx = 0; scaleIx < lenScale; ++scaleIx) {
                        const note = 12 * octave + scale[scaleIx];
                        if (note >= min && note <= max) {
                            scaleAxis.push(note);
                        }
                    }
                }
                // Map to the scale
                const noteNum = mapToVirtualAxis(value, extremes, { min: 0, max: scaleAxis.length - 1 }, isInverted, mapFunc === 'logarithmic');
                return scaleAxis[Math.round(noteNum)];
            }
            return mapToVirtualAxis(value, extremes, { min, max }, isInverted, mapFunc === 'logarithmic');
        }
        /**
         * Get mapping parameter value with defined fallback and defaults.
         * @private
         */
        function getParamValWithDefault(context, propMetrics, useSeriesExtremes, mappingParamOptions, fallback, defaults, contextValueProp) {
            return pick(getMappingParameterValue(context, propMetrics, useSeriesExtremes, extend({
                min: 0, max: 1, mapTo: 'y', mapFunction: 'linear', within: 'chart'
            }, (defaults || {})), mappingParamOptions, contextValueProp), fallback);
        }
        /**
         * Get time value for a point event.
         * @private
         */
        function getPointTime(point, startTime, duration, timeMappingOptions, propMetrics, useSeriesExtremes) {
            const time = getParamValWithDefault({ point, time: 0 }, propMetrics, useSeriesExtremes, timeMappingOptions, 0, { min: 0, max: duration, mapTo: 'x' });
            return time + startTime;
        }
        /**
         * Get duration for a series
         * @private
         */
        function getAvailableDurationForSeries(series, totalDuration, propMetrics, afterSeriesWait) {
            let timeProp, seriesDuration;
            const availableDuration = totalDuration -
                (series.chart.series.length - 1) * afterSeriesWait, hasGlobalTimeProp = propMetrics.seriesTimeProps.every((timeProps) => {
                const props = Object.keys(timeProps);
                if (props.length > 1) {
                    return false;
                }
                if (!timeProp) {
                    timeProp = props[0];
                }
                return timeProp === props[0];
            });
            if (hasGlobalTimeProp) {
                // Chart-wide single time prop, use time prop extremes
                const seriesExtremes = propMetrics
                    .seriesExtremes[series.index][timeProp], seriesTimeLen = seriesExtremes.max - seriesExtremes.min, totalTimeLen = propMetrics.seriesExtremes.reduce((sum, s) => (s[timeProp] ?
                    sum + s[timeProp].max - s[timeProp].min :
                    sum), 0);
                seriesDuration = Math.round(seriesTimeLen / totalTimeLen * availableDuration);
            }
            else {
                // No common time prop, so use percent of total points
                const totalPoints = series.chart.series.reduce((sum, s) => sum + s.points.length, 0);
                seriesDuration = Math.round((series.points || []).length / totalPoints * availableDuration);
            }
            return Math.max(50, seriesDuration);
        }
        /**
         * Build and add a track to the timeline.
         * @private
         */
        function addTimelineChannelFromTrack(timeline, audioContext, destinationNode, options) {
            const speechOpts = options, instrMappingOpts = (options.mapping || {}), engine = options.type === 'speech' ?
                new SonificationSpeaker({
                    language: speechOpts.language,
                    name: speechOpts.preferredVoice
                }) :
                new SonificationInstrument(audioContext, destinationNode, {
                    capabilities: {
                        pan: !!instrMappingOpts.pan,
                        tremolo: !!instrMappingOpts.tremolo,
                        filters: !!(instrMappingOpts.highpass ||
                            instrMappingOpts.lowpass)
                    },
                    synthPatch: options.instrument,
                    midiTrackName: options.midiName
                });
            return timeline.addChannel(options.type || 'instrument', engine, pick(options.showPlayMarker, true));
        }
        /**
         * Add event from a point to a mapped instrument track.
         * @private
         */
        function addMappedInstrumentEvent(context, channel, mappingOptions, propMetrics, roundToMusicalNotes, contextValueProp) {
            const getParam = (param, fallback, defaults, parent) => getParamValWithDefault(context, propMetrics, false, (parent || mappingOptions)[param], fallback, defaults, contextValueProp);
            const eventsAdded = [], eventOpts = {
                noteDuration: getParam('noteDuration', 200, { min: 40, max: 1000 }),
                pan: getParam('pan', 0, { min: -1, max: 1 }),
                volume: getParam('volume', 1, { min: 0.1, max: 1 })
            };
            if (mappingOptions.frequency) {
                eventOpts.frequency = getParam('frequency', 440, { min: 50, max: 6000 });
            }
            if (mappingOptions.lowpass) {
                eventOpts.lowpassFreq = getParam('frequency', 20000, { min: 0, max: 20000 }, mappingOptions.lowpass);
                eventOpts.lowpassResonance = getParam('resonance', 0, { min: -6, max: 12 }, mappingOptions.lowpass);
            }
            if (mappingOptions.highpass) {
                eventOpts.highpassFreq = getParam('frequency', 20000, { min: 0, max: 20000 }, mappingOptions.highpass);
                eventOpts.highpassResonance = getParam('resonance', 0, { min: -6, max: 12 }, mappingOptions.highpass);
            }
            if (mappingOptions.tremolo) {
                eventOpts.tremoloDepth = getParam('depth', 0, { min: 0, max: 0.8 }, mappingOptions.tremolo);
                eventOpts.tremoloSpeed = getParam('speed', 0, { min: 0, max: 0.8 }, mappingOptions.tremolo);
            }
            const gapBetweenNotes = getParam('gapBetweenNotes', 150, { min: 50, max: 1000 }), playDelay = getParam('playDelay', 0, { max: 200 });
            const addNoteEvent = (noteDef, ix = 0) => {
                let opts = noteDef;
                if (noteDef.mapTo) {
                    // Transform the pitch mapping options to normal mapping options
                    if (typeof noteDef.min === 'string') {
                        opts.min = SonificationInstrument
                            .noteStringToC0Distance(noteDef.min);
                    }
                    if (typeof noteDef.max === 'string') {
                        opts.max = SonificationInstrument
                            .noteStringToC0Distance(noteDef.max);
                    }
                }
                else if (typeof noteDef === 'string' && isNoteDefinition(noteDef)) {
                    opts = SonificationInstrument.noteStringToC0Distance(noteDef);
                }
                eventOpts.note = getParamValWithDefault(context, propMetrics, false, opts, -1, { min: 0, max: 107 }, contextValueProp);
                if (eventOpts.note > -1) {
                    if (roundToMusicalNotes) {
                        eventOpts.note = Math.round(eventOpts.note);
                    }
                    eventsAdded.push(channel.addEvent({
                        time: context.time + playDelay + gapBetweenNotes * ix,
                        relatedPoint: context.point,
                        instrumentEventOptions: ix !== void 0 ?
                            extend({}, eventOpts) : eventOpts
                    }));
                }
            };
            if (mappingOptions.pitch &&
                mappingOptions.pitch.constructor === Array) {
                mappingOptions.pitch.forEach(addNoteEvent);
            }
            else if (mappingOptions.pitch) {
                addNoteEvent(mappingOptions.pitch);
            }
            else if (mappingOptions.frequency) {
                eventsAdded.push(channel.addEvent({
                    time: context.time + playDelay,
                    relatedPoint: context.point,
                    instrumentEventOptions: eventOpts
                }));
            }
            return eventsAdded;
        }
        /**
         * Get the message value to speak for a point.
         * @private
         */
        function getSpeechMessageValue(context, messageParam) {
            return format(typeof messageParam === 'function' ?
                messageParam(context) :
                messageParam, context, context.point && context.point.series.chart);
        }
        /**
         * Add an event from a point to a mapped speech track.
         * @private
         */
        function addMappedSpeechEvent(context, channel, mappingOptions, propMetrics, contextValueProp) {
            const getParam = (param, fallback, defaults) => getParamValWithDefault(context, propMetrics, false, mappingOptions[param], fallback, defaults, contextValueProp);
            const playDelay = getParam('playDelay', 0, { max: 200 }), pitch = getParam('pitch', 1, { min: 0.3, max: 2 }), rate = getParam('rate', 1, { min: 0.4, max: 4 }), volume = getParam('volume', 1, { min: 0.1 }), message = getSpeechMessageValue(context, mappingOptions.text);
            if (message) {
                return channel.addEvent({
                    time: context.time + playDelay,
                    relatedPoint: context.point,
                    speechOptions: {
                        pitch,
                        rate,
                        volume
                    },
                    message
                });
            }
        }
        /**
         * Add events to a channel for a point&track combo.
         * @private
         */
        function addMappedEventForPoint(context, channel, trackOptions, propMetrics) {
            let eventsAdded = [];
            if (trackOptions.type === 'speech' && trackOptions.mapping) {
                const eventAdded = addMappedSpeechEvent(context, channel, trackOptions.mapping, propMetrics);
                if (eventAdded) {
                    eventsAdded = [eventAdded];
                }
            }
            else if (trackOptions.mapping) {
                eventsAdded = addMappedInstrumentEvent(context, channel, trackOptions.mapping, propMetrics, pick(trackOptions
                    .roundToMusicalNotes, true));
            }
            return eventsAdded;
        }
        /**
         * Get a reduced set of points from a list, depending on grouping opts.
         * @private
         */
        function getGroupedPoints(pointGroupOpts, points) {
            const alg = pointGroupOpts.algorithm || 'minmax', r = (ix) => (points[ix] ? [points[ix].point] : []);
            if (alg === 'first') {
                return r(0);
            }
            if (alg === 'last') {
                return r(points.length - 1);
            }
            if (alg === 'middle') {
                return r(points.length >> 1);
            }
            if (alg === 'firstlast') {
                return r(0).concat(r(points.length - 1));
            }
            if (alg === 'minmax') {
                const prop = pointGroupOpts.prop || 'y';
                let min, max, minVal, maxVal;
                points.forEach((p) => {
                    const val = getPointPropValue(p.point, prop);
                    if (val === void 0) {
                        return;
                    }
                    if (!min || val < minVal) {
                        min = p;
                        minVal = val;
                    }
                    if (!max || val > maxVal) {
                        max = p;
                        maxVal = val;
                    }
                });
                if (min && max) {
                    if (min.point === max.point) {
                        return [min.point];
                    }
                    return min.time > max.time ?
                        [max.point, min.point] :
                        [min.point, max.point];
                }
            }
            return [];
        }
        /**
         * Should a track be active for this event?
         * @private
         */
        function isActive(context, activeWhen, lastPropValue) {
            if (typeof activeWhen === 'function') {
                return activeWhen(context);
            }
            if (typeof activeWhen === 'object') {
                const prop = activeWhen.prop, val = pick(context.value, context.point && getPointPropValue(context.point, prop));
                if (typeof val !== 'number') {
                    return false;
                }
                let crossingOk = true;
                const crossingUp = activeWhen.crossingUp, crossingDown = activeWhen.crossingDown, hasLastValue = typeof lastPropValue === 'number';
                if (crossingUp && crossingDown) {
                    crossingOk = hasLastValue && (lastPropValue < crossingUp && val >= crossingUp ||
                        lastPropValue > crossingDown && val <= crossingDown);
                }
                else {
                    crossingOk = (crossingUp === void 0 ||
                        hasLastValue && lastPropValue < crossingUp &&
                            val >= crossingUp) && (crossingDown === void 0 ||
                        hasLastValue && lastPropValue > crossingDown &&
                            val <= crossingDown);
                }
                const max = pick(activeWhen.max, Infinity), min = pick(activeWhen.min, -Infinity);
                return val <= max && val >= min && crossingOk;
            }
            return true;
        }
        /**
         * Build a new timeline object from a chart.
         * @private
         */
        function timelineFromChart(audioContext, destinationNode, chart) {
            const options = chart.options.sonification ||
                {}, defaultInstrOpts = options.defaultInstrumentOptions, defaultSpeechOpts = options.defaultSpeechOptions, defaultPointGroupOpts = merge({
                enabled: true,
                groupTimespan: 15,
                algorithm: 'minmax',
                prop: 'y'
            }, options.pointGrouping), globalTracks = options.globalTracks || [], globalContextTracks = options.globalContextTracks || [], isSequential = options.order === 'sequential', 
            // Slight margin for note end
            totalDuration = Math.max(50, options.duration - 300), afterSeriesWait = options.afterSeriesWait, eventOptions = options.events || {}, propMetrics = getPropMetrics(chart), timeline = new SonificationTimeline({
                onPlay: eventOptions.onPlay,
                onEnd: eventOptions.onEnd,
                onStop: eventOptions.onStop,
                showCrosshair: options.showCrosshair,
                showTooltip: options.showTooltip
            }, chart);
            // Expose PropMetrics for tests
            if (chart.sonification) {
                chart.sonification.propMetrics = propMetrics;
            }
            let startTime = 0;
            chart.series.forEach((series, seriesIx) => {
                const sOptions = series.options.sonification ||
                    {};
                if (series.visible && sOptions.enabled !== false) {
                    const seriesDuration = isSequential ? getAvailableDurationForSeries(series, totalDuration, propMetrics, afterSeriesWait) : totalDuration, seriesDefaultInstrOpts = merge(defaultInstrOpts, sOptions.defaultInstrumentOptions), seriesDefaultSpeechOpts = merge(defaultSpeechOpts, sOptions.defaultSpeechOptions), seriesPointGroupOpts = merge(defaultPointGroupOpts, sOptions.pointGrouping), mainTracks = (sOptions.tracks || [seriesDefaultInstrOpts])
                        .concat(globalTracks), hasAddedSeries = !!timeline.channels.length, contextTracks = hasAddedSeries && !isSequential ?
                        sOptions.contextTracks || [] :
                        (sOptions.contextTracks || []).concat(globalContextTracks), eventsAdded = [];
                    // For crossing threshold notifications
                    let lastPropValue;
                    // Add events for the mapped tracks
                    mainTracks.forEach((trackOpts) => {
                        const mergedOpts = merge({
                            pointGrouping: seriesPointGroupOpts,
                            midiName: trackOpts.midiName || series.name
                        }, trackOpts.type === 'speech' ?
                            seriesDefaultSpeechOpts : seriesDefaultInstrOpts, trackOpts), pointGroupOpts = mergedOpts.pointGrouping, activeWhen = mergedOpts.activeWhen, updateLastPropValue = (point) => {
                            if (typeof activeWhen === 'object' &&
                                activeWhen.prop) {
                                lastPropValue = getPointPropValue(point, activeWhen.prop);
                            }
                        };
                        const channel = addTimelineChannelFromTrack(timeline, audioContext, destinationNode, mergedOpts), add = (c) => eventsAdded.push(
                        // Note arrays add multiple events
                        ...addMappedEventForPoint(c, channel, mergedOpts, propMetrics));
                        // Go through the points and add events to channel
                        let pointGroup = [], pointGroupTime = 0;
                        const addCurrentPointGroup = (groupSpanTime) => {
                            if (pointGroup.length === 1) {
                                add({
                                    point: pointGroup[0].point,
                                    time: pointGroupTime + groupSpanTime / 2
                                });
                            }
                            else {
                                const points = getGroupedPoints(pointGroupOpts, pointGroup), t = groupSpanTime / points.length;
                                points.forEach((p, ix) => add({
                                    point: p,
                                    time: pointGroupTime + t / 2 + t * ix
                                }));
                            }
                            pointGroup = [];
                        };
                        (series.points || []).forEach((point, pointIx) => {
                            const isLastPoint = pointIx === series.points.length - 1;
                            const time = getPointTime(point, startTime, seriesDuration, mergedOpts.mapping && mergedOpts.mapping.time || 0, propMetrics, isSequential);
                            const context = { point, time };
                            // Is this point active?
                            if (!mergedOpts.mapping ||
                                !isActive(context, activeWhen, lastPropValue)) {
                                updateLastPropValue(point);
                                // Remaining points in group
                                if (isLastPoint && pointGroup.length) {
                                    addCurrentPointGroup(pointGroup[pointGroup.length - 1].time -
                                        pointGroup[0].time);
                                }
                                return;
                            }
                            updateLastPropValue(point);
                            // Add the events
                            if (!pointGroupOpts.enabled) {
                                add(context);
                            }
                            else {
                                const dT = time - pointGroupTime, groupSpan = pointGroupOpts.groupTimespan, spanTime = isLastPoint &&
                                    dT <= groupSpan ? dT : groupSpan;
                                if (isLastPoint || dT > groupSpan) {
                                    if (dT <= groupSpan) {
                                        // Only happens if last point is within group
                                        pointGroup.push(context);
                                    }
                                    addCurrentPointGroup(spanTime);
                                    pointGroupTime = Math.floor(time / groupSpan) *
                                        groupSpan;
                                    if (isLastPoint && dT > groupSpan) {
                                        add({
                                            point: context.point,
                                            time: pointGroupTime + spanTime / 2
                                        });
                                    }
                                    else {
                                        pointGroup = [context];
                                    }
                                }
                                else {
                                    pointGroup.push(context);
                                }
                            }
                        });
                    });
                    // Add callbacks to first/last events
                    const firstEvent = eventsAdded.reduce((first, e) => (e.time < first.time ? e : first), { time: Infinity });
                    const lastEvent = eventsAdded.reduce((last, e) => (e.time > last.time ? e : last), { time: -Infinity });
                    firstEvent.callback = eventOptions.onSeriesStart ?
                        eventOptions.onSeriesStart.bind(null, { series, timeline }) :
                        void 0;
                    lastEvent.callback = eventOptions.onSeriesEnd ?
                        eventOptions.onSeriesEnd.bind(null, { series, timeline }) :
                        void 0;
                    // Add the context tracks that are not related to points
                    contextTracks.forEach((trackOpts) => {
                        const mergedOpts = trackOpts.type === 'speech' ?
                            merge(defaultSpeechOpts, trackOpts) :
                            merge(defaultInstrOpts, {
                                mapping: { pitch: { mapTo: 'value' } }
                            }, trackOpts);
                        const contextChannel = addTimelineChannelFromTrack(timeline, audioContext, destinationNode, mergedOpts);
                        lastPropValue = void 0;
                        const { timeInterval, valueInterval } = mergedOpts, valueProp = mergedOpts.valueProp || 'x', activeWhen = mergedOpts.activeWhen, contextExtremes = propMetrics
                            .seriesExtremes[seriesIx][valueProp], addContextEvent = (time, value) => {
                            if (!mergedOpts.mapping ||
                                !isActive({ time, value }, typeof activeWhen === 'object' ?
                                    extend({ prop: valueProp }, activeWhen) :
                                    activeWhen, lastPropValue)) {
                                lastPropValue = value;
                                return;
                            }
                            lastPropValue = value;
                            if (mergedOpts.type === 'speech') {
                                addMappedSpeechEvent({ time, value }, contextChannel, mergedOpts.mapping, propMetrics, valueProp);
                            }
                            else {
                                addMappedInstrumentEvent({ time, value }, contextChannel, mergedOpts.mapping, propMetrics, pick(mergedOpts.roundToMusicalNotes, true), valueProp);
                            }
                        };
                        if (timeInterval) {
                            let time = 0;
                            while (time <= seriesDuration) {
                                const val = mapToVirtualAxis(time, { min: 0, max: seriesDuration }, contextExtremes);
                                addContextEvent(time + startTime, val);
                                time += timeInterval;
                            }
                        }
                        if (valueInterval) {
                            let val = contextExtremes.min;
                            while (val <= contextExtremes.max) {
                                const time = mapToVirtualAxis(val, contextExtremes, { min: 0, max: seriesDuration }, false, mergedOpts.valueMapFunction === 'logarithmic');
                                addContextEvent(time + startTime, val);
                                val += valueInterval;
                            }
                        }
                    });
                    if (isSequential) {
                        startTime += seriesDuration + afterSeriesWait;
                    }
                }
            });
            return timeline;
        }
        /* *
         *
         *  Default Export
         *
         * */

        return timelineFromChart;
    });
    _registerModule(_modules, 'Extensions/Sonification/Sonification.js', [_modules['Core/Defaults.js'], _modules['Core/Utilities.js'], _modules['Core/Globals.js'], _modules['Extensions/Sonification/Options.js'], _modules['Extensions/Sonification/SonificationInstrument.js'], _modules['Extensions/Sonification/SonificationSpeaker.js'], _modules['Extensions/Sonification/SynthPatch.js'], _modules['Extensions/Sonification/InstrumentPresets.js'], _modules['Extensions/Sonification/TimelineFromChart.js']], function (D, U, H, defaultSonificationOptions, SonificationInstrument, SonificationSpeaker, SynthPatch, InstrumentPresets, timelineFromChart) {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Sonification module.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        /* *
         *
         *  Imports
         *
         * */
        const { defaultOptions, getOptions } = D;
        const { addEvent, extend, fireEvent, merge, pick } = U;
        const { doc, win } = H;
        /**
         * The Sonification class. This class represents a chart's sonification
         * capabilities. A chart automatically gets an instance of this class when
         * applicable.
         *
         * @sample highcharts/sonification/chart-events
         *         Basic demo accessing some of the chart.sonification methods.
         * @sample highcharts/demo/sonification-navigation
         *         More advanced demo using more functionality.
         *
         * @requires modules/sonification
         *
         * @class
         * @name Highcharts.Sonification
         *
         * @param {Highcharts.Chart} chart The chart to tie the sonification to
         */
        class Sonification {
            constructor(chart) {
                this.chart = chart;
                this.retryContextCounter = 0;
                this.lastUpdate = 0;
                this.unbindKeydown = addEvent(doc, 'keydown', function (e) {
                    if (chart && chart.sonification &&
                        (e.key === 'Esc' || e.key === 'Escape')) {
                        chart.sonification.cancel();
                    }
                });
                try {
                    this.audioContext = new win.AudioContext();
                    // eslint-disable-next-line @typescript-eslint/no-floating-promises
                    this.audioContext.suspend();
                    this.audioDestination = this.audioContext.destination;
                }
                catch (e) { /* ignore */ }
            }
            /**
             * Set the audio destination node to something other than the default
             * output. This allows for inserting custom WebAudio chains after the
             * sonification.
             * @function Highcharts.Sonification#setAudioDestination
             * @param {AudioDestinationNode} audioDestination The destination node
             */
            setAudioDestination(audioDestination) {
                this.audioDestination = audioDestination;
                this.update();
            }
            /**
             * Check if sonification is playing currently
             * @function Highcharts.Sonification#isPlaying
             * @return {boolean} `true` if currently playing, `false` if not
             */
            isPlaying() {
                return !!this.timeline && this.timeline.isPlaying;
            }
            /**
             * Divide timeline into 100 parts of equal time, and play one of them.
             * Can be used for scrubbing navigation.
             * @function Highcharts.Sonification#playSegment
             *
             * @sample highcharts/sonification/scrubbing
             *         Scrubbing with slider
             *
             * @param {number} segment The segment to play, from 0 to 100
             * @param {Highcharts.SonificationChartEventCallback} [onEnd] Callback to call after play completed
             */
            playSegment(segment, onEnd) {
                if (!this.ready(this.playSegment.bind(this, segment, onEnd))) {
                    return;
                }
                if (this.timeline) {
                    this.timeline.playSegment(segment, onEnd);
                }
            }
            /**
             * Play point(s)/event(s) adjacent to current timeline cursor location.
             * @function Highcharts.Sonification#playAdjacent
             *
             * @sample highcharts/demo/sonification-navigation
             *         Sonification keyboard navigation
             *
             * @param {number} next Pass `true` to play next point, `false` for previous
             * @param {Highcharts.SonificationChartEventCallback} [onEnd]
             * Callback to call after play completed
             * @param {Highcharts.SonificationTimelineFilterCallback} [eventFilter]
             * Filter to apply to the events before finding adjacent to play
             */
            playAdjacent(next, onEnd, eventFilter) {
                if (!this.ready(this.playAdjacent.bind(this, next, onEnd, eventFilter))) {
                    return;
                }
                if (this.timeline) {
                    const opts = this.chart.options.sonification, onHit = opts && opts.events && opts.events.onBoundaryHit;
                    if (!onHit) {
                        this.initBoundaryInstrument();
                    }
                    this.timeline.playAdjacent(next, onEnd, onHit || (() => {
                        this.defaultBoundaryHit();
                    }), eventFilter);
                }
            }
            /**
             * Play next/previous series, picking the point closest to a prop value
             * from last played point. By default picks the point in the adjacent
             * series with the closest x value as the last played point.
             * @function Highcharts.Sonification#playAdjacentSeries
             *
             * @sample highcharts/demo/sonification-navigation
             *         Sonification keyboard navigation
             *
             * @param {number} next Pass `true` to play next series, `false` for previous
             * @param {string} [prop] Prop to find closest value of, defaults to `x`.
             * @param {Highcharts.SonificationChartEventCallback} [onEnd]
             * Callback to call after play completed
             *
             * @return {Highcharts.Series|null} The played series, or `null` if none found
             */
            playAdjacentSeries(next, prop = 'x', onEnd) {
                const lastPlayed = this.getLastPlayedPoint();
                if (lastPlayed) {
                    const targetSeriesIx = lastPlayed.series.index + (next ? 1 : -1);
                    this.playClosestToProp(prop, lastPlayed[prop], (e) => !!e.relatedPoint &&
                        e.relatedPoint.series.index === targetSeriesIx, onEnd);
                    return this.chart.series[targetSeriesIx] || null;
                }
                return null;
            }
            /**
             * Play point(s)/event(s) closest to a prop relative to a reference value.
             * @function Highcharts.Sonification#playClosestToProp
             *
             * @param {string} prop Prop to compare.
             * @param {number} targetValue Target value to find closest value of.
             * @param {Highcharts.SonificationTimelineFilterCallback} [targetFilter]
             * Filter to apply to the events before finding closest point(s)
             * @param {Highcharts.SonificationChartEventCallback} [onEnd]
             * Callback to call after play completed
             */
            playClosestToProp(prop, targetValue, targetFilter, onEnd) {
                if (!this.ready(this.playClosestToProp.bind(this, prop, targetValue, targetFilter, onEnd))) {
                    return;
                }
                if (this.timeline) {
                    const opts = this.chart.options.sonification, onHit = opts && opts.events && opts.events.onBoundaryHit;
                    if (!onHit) {
                        this.initBoundaryInstrument();
                    }
                    this.timeline.playClosestToPropValue(prop, targetValue, onEnd, onHit || (() => this.defaultBoundaryHit()), targetFilter);
                }
            }
            /**
             * Get last played point
             * @function Highcharts.Sonification#getLastPlayedPoint
             *
             * @sample highcharts/demo/sonification-navigation
             *         Sonification keyboard navigation
             *
             * @return {Highcharts.Point|null} The point, or null if none
             */
            getLastPlayedPoint() {
                if (this.timeline) {
                    return this.timeline.getLastPlayedPoint();
                }
                return null;
            }
            /**
             * Play a note with a specific instrument, and optionally a time offset.
             * @function Highcharts.Sonification#playNote
             *
             * @sample highcharts/sonification/chart-events
             *         Custom notifications
             *
             * @param {Highcharts.SonificationSynthPreset|Highcharts.SynthPatchOptionsObject} instrument
             * The instrument to play. Can be either a string referencing the
             * instrument presets, or an actual SynthPatch configuration.
             * @param {Highcharts.SonificationInstrumentScheduledEventOptionsObject} options
             * Configuration for the instrument event to play.
             * @param {number} [delayMs]
             * Time offset from now, in milliseconds. Defaults to 0.
             */
            playNote(instrument, options, delayMs = 0) {
                if (!this.ready(this.playNote.bind(this, instrument, options))) {
                    return;
                }
                const duration = options.noteDuration = options.noteDuration || 500;
                const instr = new SonificationInstrument(this.audioContext, this.audioDestination, {
                    synthPatch: instrument,
                    capabilities: {
                        filters: true,
                        tremolo: true,
                        pan: true
                    }
                });
                instr.scheduleEventAtTime(delayMs / 1000, options);
                setTimeout(() => instr && instr.destroy(), delayMs + duration + 500);
            }
            /**
             * Speak a text string, optionally with a custom speaker configuration
             * @function Highcharts.Sonification#speak
             *
             * @sample highcharts/sonification/chart-events
             *         Custom notifications
             *
             * @param {string} text Text to announce
             * @param {Highcharts.SonificationSpeakerOptionsObject} [speakerOptions]
             * Options for the announcement
             * @param {number} [delayMs]
             * Time offset from now, in milliseconds. Defaults to 0.
             */
            speak(text, speakerOptions, delayMs = 0) {
                const speaker = new SonificationSpeaker(merge({
                    language: 'en-US',
                    rate: 1.5,
                    volume: 0.4
                }, speakerOptions || {}));
                speaker.sayAtTime(delayMs, text);
            }
            /**
             * Cancel current playing audio and reset the timeline.
             * @function Highcharts.Sonification#cancel
             */
            cancel() {
                if (this.timeline) {
                    this.timeline.cancel();
                }
                fireEvent(this, 'cancel');
            }
            /**
             * Start download of a MIDI file export of the timeline.
             * @function Highcharts.Sonification#downloadMIDI
             */
            downloadMIDI() {
                if (!this.ready(this.downloadMIDI.bind(this))) {
                    return;
                }
                if (this.timeline) {
                    this.timeline.reset();
                    this.timeline.downloadMIDI();
                }
            }
            /**
             * Implementation of chart.sonify
             * @private
             */
            sonifyChart(resetAfter, onEnd) {
                if (!this.ready(this.sonifyChart.bind(this, resetAfter, onEnd))) {
                    return;
                }
                if (this.timeline) {
                    this.timeline.reset();
                    this.beforePlay();
                    this.timeline.play(void 0, void 0, resetAfter, onEnd);
                }
            }
            /**
             * Implementation of series.sonify
             * @private
             */
            sonifySeries(series, resetAfter, onEnd) {
                if (!this.ready(this.sonifySeries.bind(this, series, resetAfter, onEnd))) {
                    return;
                }
                if (this.timeline) {
                    this.timeline.reset();
                    this.beforePlay();
                    this.timeline.play((e) => !!e.relatedPoint && e.relatedPoint.series === series, void 0, resetAfter, onEnd);
                }
            }
            /**
             * Implementation of point.sonify
             * @private
             */
            sonifyPoint(point, onEnd) {
                if (!this.ready(this.sonifyPoint.bind(this, point, onEnd))) {
                    return;
                }
                if (this.timeline) {
                    this.timeline.reset();
                    this.beforePlay();
                    this.timeline.anchorPlayMoment((e) => e.relatedPoint === point, onEnd);
                }
            }
            /**
             * Set the overall/master volume for the sonification.
             * Usually handled through chart update.
             * @private
             */
            setMasterVolume(vol) {
                if (this.timeline) {
                    this.timeline.setMasterVolume(vol);
                }
            }
            /**
             * Destroy the sonification capabilities
             * @private
             */
            destroy() {
                this.unbindKeydown();
                if (this.timeline) {
                    this.timeline.destroy();
                    delete this.timeline;
                }
                if (this.boundaryInstrument) {
                    this.boundaryInstrument.stop();
                }
                if (this.audioContext) {
                    // eslint-disable-next-line @typescript-eslint/no-floating-promises
                    this.audioContext.close();
                    delete this.audioContext;
                }
            }
            /**
             * Update the timeline with latest chart changes. Usually handled
             * automatically. Note that the [sonification.updateInterval](https://api.highcharts.com/highcharts/sonification.updateInterval)
             * option can stop updates from happening in rapid succession, including
             * manual calls to this function.
             * @private
             */
            update() {
                const sOpts = this.chart.options && this.chart.options.sonification;
                if (!this.ready(this.update.bind(this)) || !sOpts) {
                    return;
                }
                // Don't update too often, it gets performance intensive
                const now = Date.now(), updateInterval = sOpts.updateInterval;
                if (now - this.lastUpdate < updateInterval && !this.forceReady) {
                    clearTimeout(this.scheduledUpdate);
                    this.scheduledUpdate = setTimeout(this.update.bind(this), updateInterval / 2);
                    return;
                }
                const events = sOpts.events || {};
                if (events.beforeUpdate) {
                    events.beforeUpdate({ chart: this.chart, timeline: this.timeline });
                }
                this.lastUpdate = now;
                if (this.timeline) {
                    this.timeline.destroy();
                }
                if (this.audioContext && this.audioDestination) {
                    this.timeline = timelineFromChart(this.audioContext, this.audioDestination, this.chart);
                    const sOpts = this.chart.options.sonification;
                    this.timeline.setMasterVolume(pick(sOpts && sOpts.masterVolume, 1));
                }
                if (events.afterUpdate) {
                    events.afterUpdate({ chart: this.chart, timeline: this.timeline });
                }
            }
            /**
             * Only continue if sonification enabled. If audioContext is
             * suspended, retry up to 20 times with a small delay.
             * @private
             */
            ready(whenReady) {
                if (!this.audioContext ||
                    !this.audioDestination ||
                    !this.chart.options ||
                    this.chart.options.sonification &&
                        this.chart.options.sonification.enabled === false) {
                    return false;
                }
                if (this.audioContext.state === 'suspended' && !this.forceReady) {
                    if (this.retryContextCounter++ < 20) {
                        setTimeout(() => {
                            if (this.audioContext &&
                                this.audioContext.state === 'suspended') {
                                // eslint-disable-next-line @typescript-eslint/no-floating-promises
                                this.audioContext.resume().then(whenReady);
                            }
                            else {
                                whenReady();
                            }
                        }, 5);
                    }
                    return false;
                }
                this.retryContextCounter = 0;
                return true;
            }
            /**
             * Call beforePlay event handler if exists
             * @private
             */
            beforePlay() {
                const opts = this.chart.options.sonification, beforePlay = opts && opts.events && opts.events.beforePlay;
                if (beforePlay) {
                    beforePlay({ chart: this.chart, timeline: this.timeline });
                }
            }
            /**
             * Initialize the builtin boundary hit instrument
             * @private
             */
            initBoundaryInstrument() {
                if (!this.boundaryInstrument) {
                    this.boundaryInstrument = new SynthPatch(this.audioContext, merge(InstrumentPresets.chop, { masterVolume: 0.3 }));
                    this.boundaryInstrument.startSilently();
                    this.boundaryInstrument.connect(this.audioDestination);
                }
            }
            /**
             * The default boundary hit sound
             * @private
             */
            defaultBoundaryHit() {
                if (this.boundaryInstrument) {
                    this.boundaryInstrument.playFreqAtTime(0.1, 1, 200);
                    this.boundaryInstrument.playFreqAtTime(0.2, 1, 200);
                }
            }
        }
        (function (Sonification) {
            const composedClasses = [];
            /**
             * Update sonification object on chart.
             * @private
             */
            function updateSonificationEnabled() {
                const sonification = this.sonification, sOptions = this.options && this.options.sonification;
                if (sOptions && sOptions.enabled) {
                    if (sonification) {
                        sonification.update();
                    }
                    else {
                        this.sonification = new Sonification(this);
                        this.sonification.update();
                    }
                }
                else if (sonification) {
                    sonification.destroy();
                    delete this.sonification;
                }
            }
            /**
             * Destroy with chart.
             * @private
             */
            function chartOnDestroy() {
                if (this && this.sonification) {
                    this.sonification.destroy();
                }
            }
            /**
             * Update on render
             * @private
             */
            function chartOnRender() {
                if (this.updateSonificationEnabled) {
                    this.updateSonificationEnabled();
                }
            }
            /**
             * Update
             * @private
             */
            function chartOnUpdate(e) {
                const newOptions = e.options.sonification;
                if (newOptions) {
                    merge(true, this.options.sonification, newOptions);
                    chartOnRender.call(this);
                }
            }
            /**
             * Compose
             * @private
             */
            function compose(ChartClass, SeriesClass, PointClass) {
                // Extend chart
                if (composedClasses.indexOf(ChartClass) === -1) {
                    composedClasses.push(ChartClass);
                    extend(ChartClass.prototype, {
                        updateSonificationEnabled,
                        sonify: function (onEnd) {
                            if (this.sonification) {
                                this.sonification.sonifyChart(false, onEnd);
                            }
                        },
                        toggleSonify: function (reset = true, onEnd) {
                            if (!this.sonification) {
                                return;
                            }
                            const timeline = this.sonification.timeline;
                            if (win.speechSynthesis) {
                                win.speechSynthesis.cancel();
                            }
                            if (timeline && this.sonification.isPlaying()) {
                                if (reset) {
                                    this.sonification.cancel();
                                }
                                else {
                                    timeline.pause();
                                }
                            }
                            else if (timeline && timeline.isPaused) {
                                timeline.resume();
                            }
                            else {
                                this.sonification.sonifyChart(reset, onEnd);
                            }
                        }
                    });
                    addEvent(ChartClass, 'destroy', chartOnDestroy);
                    addEvent(ChartClass, 'render', chartOnRender);
                    addEvent(ChartClass, 'update', chartOnUpdate);
                }
                // Extend series
                if (composedClasses.indexOf(SeriesClass) === -1) {
                    composedClasses.push(SeriesClass);
                    SeriesClass.prototype.sonify = function (onEnd) {
                        if (this.chart.sonification) {
                            this.chart.sonification.sonifySeries(this, false, onEnd);
                        }
                    };
                }
                // Extend points
                if (composedClasses.indexOf(PointClass) === -1) {
                    composedClasses.push(PointClass);
                    PointClass.prototype.sonify = function (onEnd) {
                        if (this.series.chart.sonification) {
                            this.series.chart.sonification.sonifyPoint(this, onEnd);
                        }
                    };
                }
                // Add items to the exporting menu
                const exportingOptions = getOptions().exporting;
                if (exportingOptions &&
                    exportingOptions.buttons &&
                    exportingOptions.buttons.contextButton.menuItems) {
                    exportingOptions.buttons.contextButton.menuItems.push('separator', 'downloadMIDI', 'playAsSound');
                }
            }
            Sonification.compose = compose;
        })(Sonification || (Sonification = {}));
        // Add default options
        merge(true, defaultOptions, defaultSonificationOptions);
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * Play a sonification of a chart.
         *
         * @function Highcharts.Chart#sonify
         * @param {Highcharts.SonificationChartEventCallback} [onEnd]
         * Callback to call after play completed
         *
         * @requires modules/sonification
         */
        /**
         * Play/pause sonification of a chart.
         *
         * @function Highcharts.Chart#toggleSonify
         *
         * @param {boolean} [reset]
         * Reset the playing cursor after play completed. Defaults to `true`.
         * @param {Highcharts.SonificationChartEventCallback} [onEnd]
         * Callback to call after play completed
         *
         * @requires modules/sonification
         */
        /**
         * Play a sonification of a series.
         *
         * @function Highcharts.Series#sonify
         * @param {Highcharts.SonificationChartEventCallback} [onEnd]
         * Callback to call after play completed
         *
         * @requires modules/sonification
         */
        /**
         * Play a sonification of a point.
         *
         * @function Highcharts.Point#sonify
         * @param {Highcharts.SonificationChartEventCallback} [onEnd]
         * Callback to call after play completed
         *
         * @requires modules/sonification
         */
        /**
         * Sonification capabilities for the chart.
         *
         * @name Highcharts.Chart#sonification
         * @type {Highcharts.Sonification|undefined}
         *
         * @requires modules/sonification
         */
        /**
         * Collection of Sonification classes and objects.
         * @requires modules/sonification
         * @interface Highcharts.SonificationGlobalObject
         */ /**
        * SynthPatch presets
        * @name Highcharts.SonificationGlobalObject#InstrumentPresets
        * @type {Record<Highcharts.SonificationSynthPreset,Highcharts.SynthPatchOptionsObject>|undefined}
        */ /**
        * Musical scale presets
        * @name Highcharts.SonificationGlobalObject#Scales
        * @type {Highcharts.SonificationScalePresetsObject|undefined}
        */ /**
        * SynthPatch class
        * @name Highcharts.SonificationGlobalObject#SynthPatch
        * @type {Highcharts.SynthPatch|undefined}
        */ /**
        * SonificationInstrument class
        * @name Highcharts.SonificationGlobalObject#SonificationInstrument
        * @type {Highcharts.SonificationInstrument|undefined}
        */ /**
        * SonificationSpeaker class
        * @name Highcharts.SonificationGlobalObject#SonificationSpeaker
        * @type {Highcharts.SonificationSpeaker|undefined}
        */
        /**
         * Global Sonification classes and objects.
         *
         * @name Highcharts.sonification
         * @type {Highcharts.SonificationGlobalObject}
         *
         * @requires modules/sonification
         */
        (''); // Keep above doclets in JS file

        return Sonification;
    });
    _registerModule(_modules, 'Extensions/Sonification/Scales.js', [], function () {
        /* *
         *
         *  (c) 2009-2022 Øystein Moseng
         *
         *  Musical scales for sonification.
         *
         *  License: www.highcharts.com/license
         *
         *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
         *
         * */
        const Scales = {
            minor: [0, 2, 3, 5, 7, 8, 10],
            dorian: [0, 2, 3, 5, 7, 9, 10],
            harmonicMinor: [0, 2, 3, 5, 7, 8, 11],
            phrygian: [0, 1, 3, 5, 7, 8, 11],
            major: [0, 2, 4, 5, 7, 9, 11],
            lydian: [0, 2, 4, 6, 7, 9, 11],
            mixolydian: [0, 2, 4, 5, 7, 9, 10],
            majorPentatonic: [0, 2, 4, 7, 9],
            minorPentatonic: [0, 3, 5, 7, 10]
        };
        /* *
         *
         *  Default Export
         *
         * */
        /* *
         *
         *  API declarations
         *
         * */
        /**
         * Preset scales for pitch mapping.
         * @requires modules/sonification
         * @interface Highcharts.SonificationScalePresetsObject
         */ /**
        * Minor scale (aeolian)
        * @name Highcharts.SonificationScalePresetsObject#minor
        * @type {Array<number>}
        */ /**
        * Dorian scale
        * @name Highcharts.SonificationScalePresetsObject#dorian
        * @type {Array<number>}
        */ /**
        * Harmonic minor scale
        * @name Highcharts.SonificationScalePresetsObject#harmonicMinor
        * @type {Array<number>}
        */ /**
        * Phrygian scale
        * @name Highcharts.SonificationScalePresetsObject#phrygian
        * @type {Array<number>}
        */ /**
        * Major (ionian) scale
        * @name Highcharts.SonificationScalePresetsObject#major
        * @type {Array<number>}
        */ /**
        * Lydian scale
        * @name Highcharts.SonificationScalePresetsObject#lydian
        * @type {Array<number>}
        */ /**
        * Mixolydian scale
        * @name Highcharts.SonificationScalePresetsObject#mixolydian
        * @type {Array<number>}
        */ /**
        * Major pentatonic scale
        * @name Highcharts.SonificationScalePresetsObject#majorPentatonic
        * @type {Array<number>}
        */ /**
        * Minor pentatonic scale
        * @name Highcharts.SonificationScalePresetsObject#minorPentatonic
        * @type {Array<number>}
        */
        (''); // Keep above doclets in JS file

        return Scales;
    });
    _registerModule(_modules, 'masters/modules/sonification.src.js', [_modules['Core/Globals.js'], _modules['Extensions/Sonification/Sonification.js'], _modules['Extensions/Sonification/SynthPatch.js'], _modules['Extensions/Sonification/InstrumentPresets.js'], _modules['Extensions/Sonification/Scales.js'], _modules['Extensions/Sonification/SonificationInstrument.js'], _modules['Extensions/Sonification/SonificationSpeaker.js'], _modules['Extensions/Sonification/SonificationTimeline.js']], function (Highcharts, Sonification, SynthPatch, InstrumentPresets, Scales, SonificationInstrument, SonificationSpeaker, SonificationTimeline) {

        const G = Highcharts;
        // Global objects
        G.sonification = {
            InstrumentPresets,
            Scales,
            SynthPatch,
            SonificationInstrument,
            SonificationSpeaker,
            SonificationTimeline,
            Sonification
        };
        Sonification.compose(G.Chart, G.Series, G.Point);

    });
}));