adsr-gain-node
Version:
A simple and small nodejs module for creating an adsr gain node
374 lines (317 loc) • 25.3 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var options = {
attackAmp: {
label: 'Attack Amp',
value: 0.1,
min: 0,
max: 1,
step: 0.001
},
decayAmp: {
label: 'Decay Amp',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
sustainAmp: {
label: 'Sustain Amp',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
releaseAmp: {
label: 'Release Amp',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
attackTime: {
label: 'Attack Time',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
decayTime: {
label: 'Decay Time',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
sustainTime: {
label: 'Sustain Time',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
releaseTime: {
label: 'Release Time',
value: 0.1,
min: 0,
max: 1,
step: 0.01
},
adsrInterval: {
value: 1
}
}
function getAdsrFormHtml (defaultValues) {
setOptionsDefaultValues(defaultValues)
return `
<form id="adsr">
${getAdsrFormParts(options)}
</form>`
}
function setOptionsDefaultValues (values) {
for(var i in values) {
options[i].value = values[i]
}
}
function getAdsrFormParts(options) {
var str = '';
for(var name in options) {
if (name == 'adsrInterval') {
str += getAdsrFormPartAdsrInterval(options);
} else {
str += getAdsrFormPart(name, options[name]);
}
}
return str;
}
function getAdsrFormPartAdsrInterval (options) {
return `
<div>
<span class="adsr-label">Time interval</span>
<input
name="adsrInterval"
id="adsr-interval"
size="1"
type="text"
maxlength="4"
value="${options.adsrInterval.value}" />
<span> second(s)</span>
</div>
`;
}
function getAdsrFormPart(name, option) {
return `
<div>
<span class="adsr-label">${option.label}</span>
<span class="adsr-input">
<input
name = "${name}"
type="range"
max="${option.max}"
min="${option.min}"
step="${option.step}"
value="${option.value}"
>
</span>
</div>
`;
}
function adsrPreventSubmit () {
var elem = document.getElementById('adsr-interval');
elem.addEventListener('keydown' , function (e) {
if (e.keyIdentifier == 'U+000A' || e.keyIdentifier == 'Enter' || e.keyCode == 13) {
e.preventDefault();
}
});
}
function getAdsrTimeInterval () {
var interval = document.getElementById('adsr-interval').value
interval = parseFloat(interval)
if (!interval) {
interval = 1;
}
return interval;
}
function getFormValuesRaw () {
var values = getFormValues('raw');
// values.timeInterval = getTimeInterval();
return values;
}
function getFormValues (raw) {
var elems = document.getElementById("adsr").elements;
var interval = getAdsrTimeInterval();
var ret = {};
for(var i = 0; i < elems.length; i++ ) {
var name = elems[i].name;
var value = parseFloat(elems[i].value);
if (value == 0) {
value = 0.0001
}
ret[name] = value
if (raw) continue;
if (name == 'sustainTime' ||
name == 'releaseTime' ||
name == 'decayTime' ||
name == 'attackTime') {
ret[name] = interval * ret[name]
}
}
return ret;
}
function insertHTML (elem, defaultValues) {
var adsrHtml = getAdsrFormHtml(defaultValues);
elem.insertAdjacentHTML( 'afterbegin', adsrHtml);
adsrPreventSubmit()
}
module.exports.insertHTML = insertHTML
module.exports.getFormValues = getFormValues
module.exports.getFormValuesRaw = getFormValuesRaw
},{}],2:[function(require,module,exports){
function AdsrGainNode(ctx) {
this.ctx = ctx;
this.mode = 'exponentialRampToValueAtTime';
// this.mode = 'linearRampToValueAtTime';
this.options = {
attackAmp: 0.1,
decayAmp: 0.3,
sustainAmp: 0.7,
releaseAmp: 0.01,
attackTime: 0.1,
decayTime: 0.2,
sustainTime: 1.0,
releaseTime: 3.4,
autoRelease: true
};
/**
* Set options or use defaults
* @param {object} options
*/
this.setOptions = function (options) {
this.options = Object.assign(this.options, options);
};
this.gainNode
this.audioTime
/**
* Get a gain node from defined options
* @param {float} audioTime an audio context time stamp
*/
this.getGainNode = (audioTime) => {
this.gainNode = this.ctx.createGain();
this.audioTime = audioTime
// Firefox does not like 0 -> therefor 0.0000001
this.gainNode.gain.setValueAtTime(0.0000001, audioTime)
// Attack
this.gainNode.gain[this.mode](
this.options.attackAmp,
audioTime + this.options.attackTime)
// Decay
this.gainNode.gain[this.mode](
this.options.decayAmp,
audioTime + this.options.attackTime + this.options.decayTime)
// Sustain
this.gainNode.gain[this.mode](
this.options.sustainAmp,
audioTime + this.options.attackTime + this.options.sustainTime)
// Check if auto-release
// Then calculate when note should stop
if (this.options.autoRelease) {
this.gainNode.gain[this.mode](
this.options.releaseAmp,
audioTime + this.releaseTime()
)
// Disconnect the gain node
this.disconnect(audioTime + this.releaseTime())
}
return this.gainNode;
};
/**
* Release the note dynamicaly
* E.g. if your are making a keyboard, and you want the note
* to be released according to current audio time + the ADSR release time
*/
this.releaseNow = () => {
this.gainNode.gain[this.mode](
this.options.releaseAmp,
this.ctx.currentTime + this.options.releaseTime)
this.disconnect(this.options.releaseTime)
}
/**
* Get release time according to the adsr release time
*/
this.releaseTime = function() {
return this.options.attackTime + this.options.decayTime + this.options.sustainTime + this.options.releaseTime
}
/**
* Get release time according to 'now'
*/
this.releaseTimeNow = function () {
return this.ctx.currentTime + this.releaseTime()
}
/**
*
* @param {float} disconnectTime the time when gainNode should disconnect
*/
this.disconnect = (disconnectTime) => {
setTimeout( () => {
this.gainNode.disconnect();
},
disconnectTime * 1000);
};
}
module.exports = AdsrGainNode;
},{}],3:[function(require,module,exports){
var adsrGainNode = require('./index')
var adsrForm = require('./adsrForm')
var audioCtx = new AudioContext();
var oscillator = audioCtx.createOscillator();
// Helper function to get new gain node
function getADSR () {
let adsr = new adsrGainNode(audioCtx);
let options = adsrForm.getFormValues()
adsr.setOptions(options)
console.log(adsrForm.getFormValuesRaw())
return adsr
}
// Begin time for gain
var nowTime = audioCtx.currentTime
function playNoteIn (inTime) {
if(audioCtx.state === 'suspended') {
audioCtx.resume().then(function() {
console.log('Resumed')
});
}
let adsr = getADSR()
let gainNode = adsr.getGainNode(audioCtx.currentTime + inTime );
let oscillator = audioCtx.createOscillator();
// Connect the oscillator to the gain node
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
// Start
oscillator.start(audioCtx.currentTime + inTime);
// Stop oscillator according to the ADSR
let endTime = adsr.releaseTime() + audioCtx.currentTime
oscillator.stop(endTime)
}
var defaultValues = {
attackAmp: 0.1,
decayAmp: 0.3,
sustainAmp: 0.7,
releaseAmp: 0.01,
attackTime: 0.1,
decayTime: 0.2,
sustainTime: 1.0,
releaseTime: 0.1,
adsrInterval: 2.1,
// all above values are between 0 and 1.
// Except adsrInterval which are multiplied
// With the time constants.
};
document.addEventListener("DOMContentLoaded", function(event) {
var elem = document.getElementById('adsr-parent')
adsrForm.insertHTML(elem, defaultValues)
var play = document.getElementById('play')
play.addEventListener('click', function () {
playNoteIn(0)
})
});
},{"./adsrForm":1,"./index":2}]},{},[3])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Vzci9saWIvbm9kZV9tb2R1bGVzL3dhdGNoaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJhZHNyRm9ybS5qcyIsImluZGV4LmpzIiwidGVzdC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsInZhciBvcHRpb25zID0ge1xuICAgIGF0dGFja0FtcDoge1xuICAgICAgICBsYWJlbDogJ0F0dGFjayBBbXAnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDAxXG4gICAgfSxcbiAgICBkZWNheUFtcDogeyBcbiAgICAgICAgbGFiZWw6ICdEZWNheSBBbXAnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LFxuICAgIHN1c3RhaW5BbXA6IHsgXG4gICAgICAgIGxhYmVsOiAnU3VzdGFpbiBBbXAnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LFxuICAgIHJlbGVhc2VBbXA6IHsgXG4gICAgICAgIGxhYmVsOiAnUmVsZWFzZSBBbXAnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LFxuICAgIGF0dGFja1RpbWU6IHsgXG4gICAgICAgIGxhYmVsOiAnQXR0YWNrIFRpbWUnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LFxuICAgIGRlY2F5VGltZTogeyBcbiAgICAgICAgbGFiZWw6ICdEZWNheSBUaW1lJyxcbiAgICAgICAgdmFsdWU6IDAuMSwgXG4gICAgICAgIG1pbjogMCxcbiAgICAgICAgbWF4OiAxLFxuICAgICAgICBzdGVwOiAwLjAxXG4gICAgfSxcbiAgICBzdXN0YWluVGltZTogeyBcbiAgICAgICAgbGFiZWw6ICdTdXN0YWluIFRpbWUnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LCBcbiAgICByZWxlYXNlVGltZTogeyBcbiAgICAgICAgbGFiZWw6ICdSZWxlYXNlIFRpbWUnLFxuICAgICAgICB2YWx1ZTogMC4xLCBcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEsXG4gICAgICAgIHN0ZXA6IDAuMDFcbiAgICB9LFxuICAgIGFkc3JJbnRlcnZhbDoge1xuICAgICAgICB2YWx1ZTogMVxuICAgIH1cbn1cblxuZnVuY3Rpb24gZ2V0QWRzckZvcm1IdG1sIChkZWZhdWx0VmFsdWVzKSB7XG5cbiAgICBzZXRPcHRpb25zRGVmYXVsdFZhbHVlcyhkZWZhdWx0VmFsdWVzKVxuXG4gICAgcmV0dXJuIGBcbiAgICA8Zm9ybSBpZD1cImFkc3JcIj5cbiAgICAgICAgJHtnZXRBZHNyRm9ybVBhcnRzKG9wdGlvbnMpfVxuICAgIDwvZm9ybT5gXG59XG5cbmZ1bmN0aW9uIHNldE9wdGlvbnNEZWZhdWx0VmFsdWVzICh2YWx1ZXMpIHtcbiAgICBmb3IodmFyIGkgaW4gdmFsdWVzKSB7XG4gICAgICAgIG9wdGlvbnNbaV0udmFsdWUgPSB2YWx1ZXNbaV1cbiAgICB9XG5cbn1cblxuZnVuY3Rpb24gZ2V0QWRzckZvcm1QYXJ0cyhvcHRpb25zKSB7XG4gICAgdmFyIHN0ciA9ICcnO1xuICAgIGZvcih2YXIgbmFtZSBpbiBvcHRpb25zKSB7XG4gICAgICAgIGlmIChuYW1lID09ICdhZHNySW50ZXJ2YWwnKSB7XG4gICAgICAgICAgICBzdHIgKz0gZ2V0QWRzckZvcm1QYXJ0QWRzckludGVydmFsKG9wdGlvbnMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc3RyICs9IGdldEFkc3JGb3JtUGFydChuYW1lLCBvcHRpb25zW25hbWVdKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzdHI7XG59XG5cbmZ1bmN0aW9uIGdldEFkc3JGb3JtUGFydEFkc3JJbnRlcnZhbCAob3B0aW9ucykge1xuICAgIHJldHVybiBgXG4gICAgPGRpdj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJhZHNyLWxhYmVsXCI+VGltZSBpbnRlcnZhbDwvc3Bhbj5cbiAgICAgICAgPGlucHV0IFxuICAgICAgICAgICAgbmFtZT1cImFkc3JJbnRlcnZhbFwiIFxuICAgICAgICAgICAgaWQ9XCJhZHNyLWludGVydmFsXCIgXG4gICAgICAgICAgICBzaXplPVwiMVwiIFxuICAgICAgICAgICAgdHlwZT1cInRleHRcIiBcbiAgICAgICAgICAgIG1heGxlbmd0aD1cIjRcIiBcbiAgICAgICAgICAgIHZhbHVlPVwiJHtvcHRpb25zLmFkc3JJbnRlcnZhbC52YWx1ZX1cIiAvPlxuICAgICAgICA8c3Bhbj4gc2Vjb25kKHMpPC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIGA7XG59XG5cbmZ1bmN0aW9uIGdldEFkc3JGb3JtUGFydChuYW1lLCBvcHRpb24pIHtcbiAgICByZXR1cm4gYFxuICAgIDxkaXY+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiYWRzci1sYWJlbFwiPiR7b3B0aW9uLmxhYmVsfTwvc3Bhbj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJhZHNyLWlucHV0XCI+XG4gICAgICAgICAgICA8aW5wdXQgXG4gICAgICAgICAgICAgICAgbmFtZSA9IFwiJHtuYW1lfVwiIFxuICAgICAgICAgICAgICAgIHR5cGU9XCJyYW5nZVwiIFxuICAgICAgICAgICAgICAgIG1heD1cIiR7b3B0aW9uLm1heH1cIiBcbiAgICAgICAgICAgICAgICBtaW49XCIke29wdGlvbi5taW59XCIgXG4gICAgICAgICAgICAgICAgc3RlcD1cIiR7b3B0aW9uLnN0ZXB9XCIgXG4gICAgICAgICAgICAgICAgdmFsdWU9XCIke29wdGlvbi52YWx1ZX1cIlxuICAgICAgICAgICAgPlxuICAgICAgICA8L3NwYW4+XG4gICAgPC9kaXY+XG4gICAgYDtcbn1cblxuZnVuY3Rpb24gYWRzclByZXZlbnRTdWJtaXQgKCkge1xuXG4gICAgdmFyIGVsZW0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYWRzci1pbnRlcnZhbCcpO1xuICAgIGVsZW0uYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicgLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICBpZiAoZS5rZXlJZGVudGlmaWVyID09ICdVKzAwMEEnIHx8IGUua2V5SWRlbnRpZmllciA9PSAnRW50ZXInIHx8IGUua2V5Q29kZSA9PSAxMykge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9XG4gICAgfSk7XG59XG5cbmZ1bmN0aW9uIGdldEFkc3JUaW1lSW50ZXJ2YWwgKCkge1xuICAgIHZhciBpbnRlcnZhbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdhZHNyLWludGVydmFsJykudmFsdWVcbiAgICBpbnRlcnZhbCA9IHBhcnNlRmxvYXQoaW50ZXJ2YWwpXG4gICAgaWYgKCFpbnRlcnZhbCkge1xuICAgICAgICBpbnRlcnZhbCA9IDE7XG4gICAgfVxuICAgIHJldHVybiBpbnRlcnZhbDtcbn1cblxuZnVuY3Rpb24gZ2V0Rm9ybVZhbHVlc1JhdyAoKSB7XG4gICAgdmFyIHZhbHVlcyA9IGdldEZvcm1WYWx1ZXMoJ3JhdycpO1xuICAgIC8vIHZhbHVlcy50aW1lSW50ZXJ2YWwgPSBnZXRUaW1lSW50ZXJ2YWwoKTtcbiAgICByZXR1cm4gdmFsdWVzO1xufVxuXG5mdW5jdGlvbiBnZXRGb3JtVmFsdWVzIChyYXcpIHtcbiAgICB2YXIgZWxlbXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImFkc3JcIikuZWxlbWVudHM7XG4gICAgXG4gICAgdmFyIGludGVydmFsID0gZ2V0QWRzclRpbWVJbnRlcnZhbCgpO1xuXG4gICAgdmFyIHJldCA9IHt9O1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBlbGVtcy5sZW5ndGg7IGkrKyApIHtcbiAgICAgICAgdmFyIG5hbWUgPSBlbGVtc1tpXS5uYW1lO1xuICAgICAgICB2YXIgdmFsdWUgPSBwYXJzZUZsb2F0KGVsZW1zW2ldLnZhbHVlKTtcbiAgICAgICAgaWYgKHZhbHVlID09IDApIHtcbiAgICAgICAgICAgIHZhbHVlID0gMC4wMDAxXG4gICAgICAgIH1cblxuICAgICAgICByZXRbbmFtZV0gPSB2YWx1ZVxuICAgICAgICBpZiAocmF3KSBjb250aW51ZTtcblxuICAgICAgICBpZiAobmFtZSA9PSAnc3VzdGFpblRpbWUnIHx8IFxuICAgICAgICAgICAgbmFtZSA9PSAncmVsZWFzZVRpbWUnICB8fCBcbiAgICAgICAgICAgIG5hbWUgPT0gJ2RlY2F5VGltZScgfHwgXG4gICAgICAgICAgICBuYW1lID09ICdhdHRhY2tUaW1lJykge1xuICAgICAgICAgICAgcmV0W25hbWVdID0gaW50ZXJ2YWwgKiByZXRbbmFtZV1cbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmV0O1xufVxuXG5mdW5jdGlvbiBpbnNlcnRIVE1MIChlbGVtLCBkZWZhdWx0VmFsdWVzKSB7XG4gICAgdmFyIGFkc3JIdG1sID0gZ2V0QWRzckZvcm1IdG1sKGRlZmF1bHRWYWx1ZXMpO1xuICAgIGVsZW0uaW5zZXJ0QWRqYWNlbnRIVE1MKCAnYWZ0ZXJiZWdpbicsIGFkc3JIdG1sKTtcbiAgICBhZHNyUHJldmVudFN1Ym1pdCgpXG59XG5cblxubW9kdWxlLmV4cG9ydHMuaW5zZXJ0SFRNTCA9IGluc2VydEhUTUxcbm1vZHVsZS5leHBvcnRzLmdldEZvcm1WYWx1ZXMgPSBnZXRGb3JtVmFsdWVzXG5tb2R1bGUuZXhwb3J0cy5nZXRGb3JtVmFsdWVzUmF3ID0gZ2V0Rm9ybVZhbHVlc1Jhd1xuXG4iLCJmdW5jdGlvbiBBZHNyR2Fpbk5vZGUoY3R4KSB7XG5cbiAgICB0aGlzLmN0eCA9IGN0eDtcblxuICAgIHRoaXMubW9kZSA9ICdleHBvbmVudGlhbFJhbXBUb1ZhbHVlQXRUaW1lJztcbiAgICAvLyB0aGlzLm1vZGUgPSAnbGluZWFyUmFtcFRvVmFsdWVBdFRpbWUnO1xuXG4gICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgICBhdHRhY2tBbXA6IDAuMSwgXG4gICAgICAgIGRlY2F5QW1wOiAwLjMsXG4gICAgICAgIHN1c3RhaW5BbXA6IDAuNyxcbiAgICAgICAgcmVsZWFzZUFtcDogMC4wMSxcbiAgICAgICAgYXR0YWNrVGltZTogMC4xLFxuICAgICAgICBkZWNheVRpbWU6IDAuMixcbiAgICAgICAgc3VzdGFpblRpbWU6IDEuMCwgXG4gICAgICAgIHJlbGVhc2VUaW1lOiAzLjQsXG4gICAgICAgIGF1dG9SZWxlYXNlOiB0cnVlXG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNldCBvcHRpb25zIG9yIHVzZSBkZWZhdWx0c1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIFxuICAgICAqL1xuICAgIHRoaXMuc2V0T3B0aW9ucyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHRoaXMub3B0aW9ucyA9IE9iamVjdC5hc3NpZ24odGhpcy5vcHRpb25zLCBvcHRpb25zKTtcbiAgICB9O1xuXG4gICAgdGhpcy5nYWluTm9kZVxuICAgIHRoaXMuYXVkaW9UaW1lXG4gICAgXG4gICAgLyoqXG4gICAgICogR2V0IGEgZ2FpbiBub2RlIGZyb20gZGVmaW5lZCBvcHRpb25zXG4gICAgICogQHBhcmFtIHtmbG9hdH0gYXVkaW9UaW1lIGFuIGF1ZGlvIGNvbnRleHQgdGltZSBzdGFtcFxuICAgICAqL1xuICAgIHRoaXMuZ2V0R2Fpbk5vZGUgPSAgKGF1ZGlvVGltZSkgPT4ge1xuXG4gICAgICAgIHRoaXMuZ2Fpbk5vZGUgPSB0aGlzLmN0eC5jcmVhdGVHYWluKCk7XG4gICAgICAgIHRoaXMuYXVkaW9UaW1lID0gYXVkaW9UaW1lXG5cbiAgICAgICAgLy8gRmlyZWZveCBkb2VzIG5vdCBsaWtlIDAgLT4gdGhlcmVmb3IgMC4wMDAwMDAxXG4gICAgICAgIHRoaXMuZ2Fpbk5vZGUuZ2Fpbi5zZXRWYWx1ZUF0VGltZSgwLjAwMDAwMDEsIGF1ZGlvVGltZSkgICAgICAgIFxuICAgICAgICBcbiAgICAgICAgLy8gQXR0YWNrXG4gICAgICAgIHRoaXMuZ2Fpbk5vZGUuZ2Fpblt0aGlzLm1vZGVdKFxuICAgICAgICAgICAgdGhpcy5vcHRpb25zLmF0dGFja0FtcCwgXG4gICAgICAgICAgICBhdWRpb1RpbWUgKyB0aGlzLm9wdGlvbnMuYXR0YWNrVGltZSlcblxuICAgICAgICAvLyBEZWNheVxuICAgICAgICB0aGlzLmdhaW5Ob2RlLmdhaW5bdGhpcy5tb2RlXShcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy5kZWNheUFtcCwgXG4gICAgICAgICAgICBhdWRpb1RpbWUgKyB0aGlzLm9wdGlvbnMuYXR0YWNrVGltZSArIHRoaXMub3B0aW9ucy5kZWNheVRpbWUpXG5cbiAgICAgICAgLy8gU3VzdGFpblxuICAgICAgICB0aGlzLmdhaW5Ob2RlLmdhaW5bdGhpcy5tb2RlXShcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy5zdXN0YWluQW1wLCBcbiAgICAgICAgICAgIGF1ZGlvVGltZSArIHRoaXMub3B0aW9ucy5hdHRhY2tUaW1lICsgdGhpcy5vcHRpb25zLnN1c3RhaW5UaW1lKVxuIFxuICAgICAgICAvLyBDaGVjayBpZiBhdXRvLXJlbGVhc2VcbiAgICAgICAgLy8gVGhlbiBjYWxjdWxhdGUgd2hlbiBub3RlIHNob3VsZCBzdG9wXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuYXV0b1JlbGVhc2UpIHtcbiAgICAgICAgICAgIHRoaXMuZ2Fpbk5vZGUuZ2Fpblt0aGlzLm1vZGVdKFxuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucy5yZWxlYXNlQW1wLFxuICAgICAgICAgICAgICAgIGF1ZGlvVGltZSArIHRoaXMucmVsZWFzZVRpbWUoKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBEaXNjb25uZWN0IHRoZSBnYWluIG5vZGUgXG4gICAgICAgICAgICB0aGlzLmRpc2Nvbm5lY3QoYXVkaW9UaW1lICsgdGhpcy5yZWxlYXNlVGltZSgpKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmdhaW5Ob2RlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZWxlYXNlIHRoZSBub3RlIGR5bmFtaWNhbHlcbiAgICAgKiBFLmcuIGlmIHlvdXIgYXJlIG1ha2luZyBhIGtleWJvYXJkLCBhbmQgeW91IHdhbnQgdGhlIG5vdGVcbiAgICAgKiB0byBiZSByZWxlYXNlZCBhY2NvcmRpbmcgdG8gY3VycmVudCBhdWRpbyB0aW1lICsgdGhlIEFEU1IgcmVsZWFzZSB0aW1lIFxuICAgICAqL1xuICAgIHRoaXMucmVsZWFzZU5vdyA9ICgpID0+IHtcbiAgICAgICAgdGhpcy5nYWluTm9kZS5nYWluW3RoaXMubW9kZV0oXG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMucmVsZWFzZUFtcCxcbiAgICAgICAgICAgIHRoaXMuY3R4LmN1cnJlbnRUaW1lICsgdGhpcy5vcHRpb25zLnJlbGVhc2VUaW1lKSBcbiAgICAgICAgdGhpcy5kaXNjb25uZWN0KHRoaXMub3B0aW9ucy5yZWxlYXNlVGltZSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgcmVsZWFzZSB0aW1lIGFjY29yZGluZyB0byB0aGUgYWRzciByZWxlYXNlIHRpbWVcbiAgICAgKi9cbiAgICB0aGlzLnJlbGVhc2VUaW1lID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMuYXR0YWNrVGltZSArIHRoaXMub3B0aW9ucy5kZWNheVRpbWUgKyB0aGlzLm9wdGlvbnMuc3VzdGFpblRpbWUgKyB0aGlzLm9wdGlvbnMucmVsZWFzZVRpbWVcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgcmVsZWFzZSB0aW1lIGFjY29yZGluZyB0byAnbm93J1xuICAgICAqL1xuICAgIHRoaXMucmVsZWFzZVRpbWVOb3cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmN0eC5jdXJyZW50VGltZSArIHRoaXMucmVsZWFzZVRpbWUoKVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcGFyYW0ge2Zsb2F0fSBkaXNjb25uZWN0VGltZSB0aGUgdGltZSB3aGVuIGdhaW5Ob2RlIHNob3VsZCBkaXNjb25uZWN0IFxuICAgICAqL1xuICAgIHRoaXMuZGlzY29ubmVjdCA9IChkaXNjb25uZWN0VGltZSkgPT4ge1xuICAgICAgICBzZXRUaW1lb3V0KCAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmdhaW5Ob2RlLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgfSxcbiAgICAgICAgZGlzY29ubmVjdFRpbWUgKiAxMDAwKTtcbiAgICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IEFkc3JHYWluTm9kZTtcbiIsInZhciBhZHNyR2Fpbk5vZGUgPSByZXF1aXJlKCcuL2luZGV4JylcbnZhciBhZHNyRm9ybSA9IHJlcXVpcmUoJy4vYWRzckZvcm0nKVxuXG52YXIgYXVkaW9DdHggPSBuZXcgQXVkaW9Db250ZXh0KCk7XG52YXIgb3NjaWxsYXRvciA9IGF1ZGlvQ3R4LmNyZWF0ZU9zY2lsbGF0b3IoKTtcblxuLy8gSGVscGVyIGZ1bmN0aW9uIHRvIGdldCBuZXcgZ2FpbiBub2RlXG5mdW5jdGlvbiBnZXRBRFNSICgpIHtcbiAgICBsZXQgYWRzciA9IG5ldyBhZHNyR2Fpbk5vZGUoYXVkaW9DdHgpO1xuICAgIGxldCBvcHRpb25zID0gYWRzckZvcm0uZ2V0Rm9ybVZhbHVlcygpXG4gICAgYWRzci5zZXRPcHRpb25zKG9wdGlvbnMpXG4gICAgY29uc29sZS5sb2coYWRzckZvcm0uZ2V0Rm9ybVZhbHVlc1JhdygpKVxuICAgIHJldHVybiBhZHNyXG59XG5cbi8vIEJlZ2luIHRpbWUgZm9yIGdhaW5cbnZhciBub3dUaW1lID0gYXVkaW9DdHguY3VycmVudFRpbWVcblxuZnVuY3Rpb24gcGxheU5vdGVJbiAoaW5UaW1lKSB7XG5cbiAgICBpZihhdWRpb0N0eC5zdGF0ZSA9PT0gJ3N1c3BlbmRlZCcpIHtcbiAgICAgICAgYXVkaW9DdHgucmVzdW1lKCkudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdSZXN1bWVkJylcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgbGV0IGFkc3IgPSBnZXRBRFNSKClcbiAgICBsZXQgZ2Fpbk5vZGUgPSBhZHNyLmdldEdhaW5Ob2RlKGF1ZGlvQ3R4LmN1cnJlbnRUaW1lICsgaW5UaW1lICk7XG5cbiAgICBsZXQgb3NjaWxsYXRvciA9IGF1ZGlvQ3R4LmNyZWF0ZU9zY2lsbGF0b3IoKTtcblxuICAgIC8vIENvbm5lY3QgdGhlIG9zY2lsbGF0b3IgdG8gdGhlIGdhaW4gbm9kZVxuICAgIG9zY2lsbGF0b3IuY29ubmVjdChnYWluTm9kZSk7XG4gICAgZ2Fpbk5vZGUuY29ubmVjdChhdWRpb0N0eC5kZXN0aW5hdGlvbik7XG5cbiAgICAvLyBTdGFydFxuICAgIG9zY2lsbGF0b3Iuc3RhcnQoYXVkaW9DdHguY3VycmVudFRpbWUgKyBpblRpbWUpO1xuXG4gICAgLy8gU3RvcCBvc2NpbGxhdG9yIGFjY29yZGluZyB0byB0aGUgQURTUlxuICAgIGxldCBlbmRUaW1lID0gYWRzci5yZWxlYXNlVGltZSgpICsgYXVkaW9DdHguY3VycmVudFRpbWVcbiAgICBvc2NpbGxhdG9yLnN0b3AoZW5kVGltZSlcbn1cblxudmFyIGRlZmF1bHRWYWx1ZXMgPSB7XG4gICAgYXR0YWNrQW1wOiAwLjEsIFxuICAgIGRlY2F5QW1wOiAwLjMsXG4gICAgc3VzdGFpbkFtcDogMC43LFxuICAgIHJlbGVhc2VBbXA6IDAuMDEsXG4gICAgYXR0YWNrVGltZTogMC4xLFxuICAgIGRlY2F5VGltZTogMC4yLFxuICAgIHN1c3RhaW5UaW1lOiAxLjAsIFxuICAgIHJlbGVhc2VUaW1lOiAwLjEsXG4gICAgYWRzckludGVydmFsOiAyLjEsIFxuICAgIC8vIGFsbCBhYm92ZSB2YWx1ZXMgYXJlIGJldHdlZW4gMCBhbmQgMS5cbiAgICAvLyBFeGNlcHQgYWRzckludGVydmFsIHdoaWNoIGFyZSBtdWx0aXBsaWVkXG4gICAgLy8gV2l0aCB0aGUgdGltZSBjb25zdGFudHMuXG59O1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFwiRE9NQ29udGVudExvYWRlZFwiLCBmdW5jdGlvbihldmVudCkgeyBcbiAgICB2YXIgZWxlbSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdhZHNyLXBhcmVudCcpXG4gICAgYWRzckZvcm0uaW5zZXJ0SFRNTChlbGVtLCBkZWZhdWx0VmFsdWVzKVxuXG4gICAgdmFyIHBsYXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGxheScpXG4gICAgcGxheS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcGxheU5vdGVJbigwKVxuICAgIH0pXG59KTtcbiJdfQ==