speechrecorderng 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/action/action.mjs +73 -0
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer.mjs +163 -163
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_input_stream.mjs +85 -85
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_random_access_stream.mjs +15 -15
- package/esm2022/lib/audio/audio_data_holder.mjs +264 -0
- package/{esm2020 → esm2022}/lib/audio/audio_display.mjs +93 -93
- package/{esm2020 → esm2022}/lib/audio/audio_player.mjs +213 -213
- package/esm2022/lib/audio/capture/capture.mjs +855 -0
- package/esm2022/lib/audio/context.mjs +79 -0
- package/{esm2020 → esm2022}/lib/audio/dsp/level_measure.mjs +515 -515
- package/{esm2020 → esm2022}/lib/audio/format.mjs +16 -16
- package/esm2022/lib/audio/impl/wavformat.mjs +6 -0
- package/{esm2020 → esm2022}/lib/audio/impl/wavreader.mjs +133 -133
- package/esm2022/lib/audio/impl/wavwriter.mjs +105 -0
- package/esm2022/lib/audio/inddb_audio_buffer.mjs +508 -0
- package/{esm2020 → esm2022}/lib/audio/io/stream.mjs +58 -58
- package/esm2022/lib/audio/net_audio_buffer.mjs +293 -0
- package/{esm2020 → esm2022}/lib/audio/persistor.mjs +80 -80
- package/{esm2020 → esm2022}/lib/audio/playback/array_audio_buffer_source_node.mjs +125 -125
- package/esm2022/lib/audio/playback/audio_source_node.mjs +18 -0
- package/esm2022/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
- package/{esm2020 → esm2022}/lib/audio/playback/inddb_audio_buffer_source_node.mjs +166 -166
- package/{esm2020 → esm2022}/lib/audio/playback/net_audio_buffer_source_node.mjs +217 -217
- package/esm2022/lib/audio/playback/player.mjs +402 -0
- package/esm2022/lib/audio/ui/audio_canvas_layer_comp.mjs +347 -0
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_control.mjs +65 -65
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_scroll_pane.mjs +139 -139
- package/{esm2020 → esm2022}/lib/audio/ui/audiosignal.mjs +524 -524
- package/{esm2020 → esm2022}/lib/audio/ui/common.mjs +18 -18
- package/esm2022/lib/audio/ui/container.mjs +414 -0
- package/{esm2020 → esm2022}/lib/audio/ui/livelevel.mjs +352 -352
- package/esm2022/lib/audio/ui/scroll_pane_horizontal.mjs +11 -0
- package/esm2022/lib/audio/ui/sonagram.mjs +900 -0
- package/esm2022/lib/db/inddb.mjs +120 -0
- package/esm2022/lib/dsp/utils.mjs +48 -0
- package/{esm2020 → esm2022}/lib/io/BinaryReader.mjs +84 -84
- package/esm2022/lib/io/BinaryWriter.mjs +74 -0
- package/{esm2020 → esm2022}/lib/io/stream.mjs +258 -258
- package/{esm2020 → esm2022}/lib/math/2d/geometry.mjs +27 -27
- package/esm2022/lib/math/complex.mjs +58 -0
- package/{esm2020 → esm2022}/lib/math/dft.mjs +195 -195
- package/{esm2020 → esm2022}/lib/media/utils.mjs +13 -13
- package/esm2022/lib/net/uploader.mjs +367 -0
- package/{esm2020 → esm2022}/lib/recorder_component.mjs +64 -64
- package/esm2022/lib/speechrecorder/project/project.mjs +49 -0
- package/esm2022/lib/speechrecorder/project/project.service.mjs +64 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/recording.mjs +123 -123
- package/esm2022/lib/speechrecorder/recordings/basic_recording.service.mjs +218 -0
- package/esm2022/lib/speechrecorder/recordings/recordings.service.mjs +1014 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.mjs +114 -114
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.service.mjs +47 -47
- package/esm2022/lib/speechrecorder/session/audiorecorder.mjs +1179 -0
- package/esm2022/lib/speechrecorder/session/basicrecorder.mjs +668 -0
- package/esm2022/lib/speechrecorder/session/controlpanel.mjs +416 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/item.mjs +29 -29
- package/{esm2020 → esm2022}/lib/speechrecorder/session/progress.mjs +69 -69
- package/{esm2020 → esm2022}/lib/speechrecorder/session/prompting.mjs +571 -571
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recorder_combi_pane.mjs +65 -65
- package/esm2022/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recording_list.mjs +103 -103
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +63 -63
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +51 -51
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +104 -104
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +381 -381
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file.mjs +67 -67
- package/esm2022/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +288 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session.mjs +1 -1
- package/esm2022/lib/speechrecorder/session/session.service.mjs +69 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session_finished_dialog.mjs +29 -29
- package/esm2022/lib/speechrecorder/session/sessionmanager.mjs +1386 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/warning_bar.mjs +28 -28
- package/{esm2020 → esm2022}/lib/speechrecorder/spruploader.mjs +22 -22
- package/{esm2020 → esm2022}/lib/speechrecorder/startstopsignal/startstopsignal.mjs +1 -1
- package/esm2022/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +57 -0
- package/{esm2020 → esm2022}/lib/speechrecorderng.component.mjs +388 -388
- package/{esm2020 → esm2022}/lib/speechrecorderng.module.mjs +100 -100
- package/{esm2020 → esm2022}/lib/spr.config.mjs +26 -26
- package/{esm2020 → esm2022}/lib/spr.module.version.mjs +2 -2
- package/{esm2020 → esm2022}/lib/ui/canvas_layer_comp.mjs +38 -38
- package/{esm2020 → esm2022}/lib/ui/message_dialog.mjs +30 -30
- package/esm2022/lib/ui/recordingitem_display.mjs +253 -0
- package/{esm2020 → esm2022}/lib/ui/responsive_component.mjs +24 -24
- package/{esm2020 → esm2022}/lib/utils/scrollIntoViewToBottom.mjs +23 -23
- package/esm2022/lib/utils/ua-parser.mjs +190 -0
- package/esm2022/lib/utils/utils.mjs +105 -0
- package/esm2022/lib/utils/wake_lock.mjs +114 -0
- package/{esm2020 → esm2022}/lib/utils/wake_lock_media.mjs +1 -1
- package/{esm2020 → esm2022}/public-api.mjs +33 -33
- package/{esm2020 → esm2022}/speechrecorderng.mjs +4 -4
- package/{fesm2020 → fesm2022}/speechrecorderng.mjs +16518 -16454
- package/fesm2022/speechrecorderng.mjs.map +1 -0
- package/index.d.ts +5 -5
- package/lib/action/action.d.ts +27 -27
- package/lib/audio/array_audio_buffer.d.ts +31 -31
- package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -14
- package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -9
- package/lib/audio/audio_data_holder.d.ts +84 -84
- package/lib/audio/audio_display.d.ts +36 -36
- package/lib/audio/audio_player.d.ts +47 -47
- package/lib/audio/capture/capture.d.ts +67 -67
- package/lib/audio/context.d.ts +6 -6
- package/lib/audio/dsp/level_measure.d.ts +60 -60
- package/lib/audio/format.d.ts +11 -11
- package/lib/audio/impl/wavformat.d.ts +5 -5
- package/lib/audio/impl/wavreader.d.ts +16 -16
- package/lib/audio/impl/wavwriter.d.ts +13 -13
- package/lib/audio/inddb_audio_buffer.d.ts +68 -68
- package/lib/audio/io/stream.d.ts +28 -28
- package/lib/audio/net_audio_buffer.d.ts +57 -57
- package/lib/audio/persistor.d.ts +29 -29
- package/lib/audio/playback/array_audio_buffer_source_node.d.ts +18 -18
- package/lib/audio/playback/audio_source_node.d.ts +10 -10
- package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -4
- package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -21
- package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -27
- package/lib/audio/playback/player.d.ts +65 -65
- package/lib/audio/ui/audio_canvas_layer_comp.d.ts +68 -68
- package/lib/audio/ui/audio_display_control.d.ts +25 -25
- package/lib/audio/ui/audio_display_scroll_pane.d.ts +26 -26
- package/lib/audio/ui/audiosignal.d.ts +30 -30
- package/lib/audio/ui/common.d.ts +11 -11
- package/lib/audio/ui/container.d.ts +63 -63
- package/lib/audio/ui/livelevel.d.ts +60 -60
- package/lib/audio/ui/scroll_pane_horizontal.d.ts +5 -5
- package/lib/audio/ui/sonagram.d.ts +37 -37
- package/lib/db/inddb.d.ts +21 -21
- package/lib/dsp/utils.d.ts +19 -19
- package/lib/io/BinaryReader.d.ts +18 -18
- package/lib/io/BinaryWriter.d.ts +15 -15
- package/lib/io/stream.d.ts +59 -59
- package/lib/math/2d/geometry.d.ts +18 -18
- package/lib/math/complex.d.ts +17 -17
- package/lib/math/dft.d.ts +22 -22
- package/lib/media/utils.d.ts +3 -3
- package/lib/net/uploader.d.ts +81 -81
- package/lib/recorder_component.d.ts +16 -16
- package/lib/speechrecorder/project/project.d.ts +59 -59
- package/lib/speechrecorder/project/project.service.d.ts +21 -21
- package/lib/speechrecorder/recording.d.ts +51 -51
- package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -27
- package/lib/speechrecorder/recordings/recordings.service.d.ts +48 -48
- package/lib/speechrecorder/script/script.d.ts +80 -79
- package/lib/speechrecorder/script/script.service.d.ts +16 -16
- package/lib/speechrecorder/session/audiorecorder.d.ts +122 -122
- package/lib/speechrecorder/session/basicrecorder.d.ts +139 -139
- package/lib/speechrecorder/session/controlpanel.d.ts +109 -107
- package/lib/speechrecorder/session/item.d.ts +12 -12
- package/lib/speechrecorder/session/progress.d.ts +17 -17
- package/lib/speechrecorder/session/prompting.d.ts +121 -121
- package/lib/speechrecorder/session/recorder_combi_pane.d.ts +32 -32
- package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -33
- package/lib/speechrecorder/session/recording_list.d.ts +24 -24
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +15 -15
- package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +20 -20
- package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +31 -31
- package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +55 -55
- package/lib/speechrecorder/session/recordingfile/recording-file.d.ts +7 -7
- package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +24 -24
- package/lib/speechrecorder/session/session.d.ts +15 -15
- package/lib/speechrecorder/session/session.service.d.ts +20 -20
- package/lib/speechrecorder/session/session_finished_dialog.d.ts +10 -10
- package/lib/speechrecorder/session/sessionmanager.d.ts +129 -123
- package/lib/speechrecorder/session/warning_bar.d.ts +8 -8
- package/lib/speechrecorder/spruploader.d.ts +11 -11
- package/lib/speechrecorder/startstopsignal/startstopsignal.d.ts +10 -10
- package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +11 -11
- package/lib/speechrecorderng.component.d.ts +61 -61
- package/lib/speechrecorderng.module.d.ts +56 -56
- package/lib/spr.config.d.ts +17 -17
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/ui/canvas_layer_comp.d.ts +13 -13
- package/lib/ui/message_dialog.d.ts +10 -10
- package/lib/ui/recordingitem_display.d.ts +84 -84
- package/lib/ui/responsive_component.d.ts +9 -9
- package/lib/utils/scrollIntoViewToBottom.d.ts +9 -9
- package/lib/utils/ua-parser.d.ts +43 -43
- package/lib/utils/utils.d.ts +19 -19
- package/lib/utils/wake_lock.d.ts +23 -23
- package/lib/utils/wake_lock_media.d.ts +1 -1
- package/package.json +17 -23
- package/public-api.d.ts +33 -33
- package/esm2020/lib/action/action.mjs +0 -73
- package/esm2020/lib/audio/audio_data_holder.mjs +0 -264
- package/esm2020/lib/audio/capture/capture.mjs +0 -855
- package/esm2020/lib/audio/context.mjs +0 -79
- package/esm2020/lib/audio/impl/wavformat.mjs +0 -6
- package/esm2020/lib/audio/impl/wavwriter.mjs +0 -105
- package/esm2020/lib/audio/inddb_audio_buffer.mjs +0 -508
- package/esm2020/lib/audio/net_audio_buffer.mjs +0 -293
- package/esm2020/lib/audio/playback/audio_source_node.mjs +0 -18
- package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +0 -167
- package/esm2020/lib/audio/playback/player.mjs +0 -402
- package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +0 -347
- package/esm2020/lib/audio/ui/container.mjs +0 -414
- package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +0 -11
- package/esm2020/lib/audio/ui/sonagram.mjs +0 -900
- package/esm2020/lib/db/inddb.mjs +0 -120
- package/esm2020/lib/dsp/utils.mjs +0 -48
- package/esm2020/lib/io/BinaryWriter.mjs +0 -74
- package/esm2020/lib/math/complex.mjs +0 -58
- package/esm2020/lib/net/uploader.mjs +0 -367
- package/esm2020/lib/speechrecorder/project/project.mjs +0 -49
- package/esm2020/lib/speechrecorder/project/project.service.mjs +0 -64
- package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +0 -216
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +0 -1014
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +0 -1179
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +0 -666
- package/esm2020/lib/speechrecorder/session/controlpanel.mjs +0 -409
- package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +0 -195
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +0 -288
- package/esm2020/lib/speechrecorder/session/session.service.mjs +0 -69
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +0 -1333
- package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +0 -57
- package/esm2020/lib/ui/recordingitem_display.mjs +0 -253
- package/esm2020/lib/utils/ua-parser.mjs +0 -190
- package/esm2020/lib/utils/utils.mjs +0 -105
- package/esm2020/lib/utils/wake_lock.mjs +0 -114
- package/fesm2015/speechrecorderng.mjs +0 -17708
- package/fesm2015/speechrecorderng.mjs.map +0 -1
- package/fesm2020/speechrecorderng.mjs.map +0 -1
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
import { AsyncEditFloat32ArrayInputStream } from "../../io/stream";
|
|
2
|
-
import { ArrayAudioBufferSourceNode } from "./array_audio_buffer_source_node";
|
|
3
|
-
import { EMPTY, expand, Observable } from "rxjs";
|
|
4
|
-
import { AudioSourceNode } from "./audio_source_node";
|
|
5
|
-
import { NetAudioInputStream } from "../net_audio_buffer";
|
|
6
|
-
export class NetAudioBufferSourceNode extends AudioSourceNode {
|
|
7
|
-
constructor(context) {
|
|
8
|
-
super(context, 'audio-source-worklet');
|
|
9
|
-
this._bufferFillSeconds = AudioSourceNode.DEFAULT_BUFFER_FILL_SECONDS;
|
|
10
|
-
this.bufferFillFrames = 0;
|
|
11
|
-
this._streamReadFrameLen = AudioSourceNode.DEFAULT_STREAM_READ_FRAME_LEN * 8; // Much overhead fetching small buffers from indexed db, use larger buffer (8192).
|
|
12
|
-
this._netAudioBuffer = null;
|
|
13
|
-
this._audioInputStream = null;
|
|
14
|
-
this._aisBufs = null;
|
|
15
|
-
this._active = false;
|
|
16
|
-
this.stalled = false;
|
|
17
|
-
this._endOfStream = false;
|
|
18
|
-
this.readDataSubscription = null;
|
|
19
|
-
this.filledFrames = 0;
|
|
20
|
-
this.stalledStartTime = null;
|
|
21
|
-
this.stalledTime = 0;
|
|
22
|
-
this.stopEndTime = null;
|
|
23
|
-
this.channelCountMode = 'explicit';
|
|
24
|
-
this.port.onmessage = (msgEv) => {
|
|
25
|
-
if (msgEv.data) {
|
|
26
|
-
let evType = msgEv.data.eventType;
|
|
27
|
-
if (evType) {
|
|
28
|
-
if ('bufferNotification' === evType) {
|
|
29
|
-
this.filledFrames = msgEv.data.filledFrames;
|
|
30
|
-
//console.debug("IndexedDbAudioBufferSourceNode: Buffer notification: filled frames: " + this.filledFrames);
|
|
31
|
-
if (!this._endOfStream) {
|
|
32
|
-
this.fillBufferObs().subscribe();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
else if ('ended' === evType) {
|
|
36
|
-
//console.debug("Inddb audio source ended playback.");
|
|
37
|
-
let drainTime = 0;
|
|
38
|
-
if (this._netAudioBuffer?.sampleRate) {
|
|
39
|
-
drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._netAudioBuffer.sampleRate;
|
|
40
|
-
}
|
|
41
|
-
//let dstAny:any=this.context.destination;
|
|
42
|
-
//console.debug('Destination node tail-time: '+dstAny['tail-time']);
|
|
43
|
-
window.setTimeout(() => {
|
|
44
|
-
this.stopEndTime = this.context.currentTime;
|
|
45
|
-
this._active = false;
|
|
46
|
-
this.onended?.call(this);
|
|
47
|
-
}, drainTime * 1000);
|
|
48
|
-
}
|
|
49
|
-
else if ('stalled' === evType) {
|
|
50
|
-
console.debug('Playback stalled...');
|
|
51
|
-
this.stalled = true;
|
|
52
|
-
this.stalledStartTime = this.context.currentTime;
|
|
53
|
-
}
|
|
54
|
-
else if ('resumed' === evType) {
|
|
55
|
-
console.debug('Playback resumed after stall.');
|
|
56
|
-
this.stalled = false;
|
|
57
|
-
if (this.stalledStartTime != null) {
|
|
58
|
-
this.stalledTime += this.context.currentTime - this.stalledStartTime;
|
|
59
|
-
this.stalledStartTime = null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
fillBufferObs(frameOffset) {
|
|
67
|
-
return new Observable(subscriber => {
|
|
68
|
-
if (frameOffset) {
|
|
69
|
-
subscriber.error(new Error("Starting playback from position not equal zero not supported yet."));
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
let filled = this.filledFrames;
|
|
73
|
-
let bufLen = 0;
|
|
74
|
-
if (this._audioInputStream && this._aisBufs && (this.readDataSubscription == null || this.readDataSubscription?.closed)) {
|
|
75
|
-
this.readDataSubscription = this._audioInputStream.readObs(this._aisBufs).pipe(expand((read) => {
|
|
76
|
-
if (read && this._aisBufs) {
|
|
77
|
-
let trBuffers = new Array(this.channelCount);
|
|
78
|
-
for (let ch = 0; ch < this.channelCount; ch++) {
|
|
79
|
-
let adCh = this._aisBufs[ch];
|
|
80
|
-
let adChCopy = new Float32Array(read);
|
|
81
|
-
bufLen = adChCopy.length;
|
|
82
|
-
if (read === adCh.length) {
|
|
83
|
-
adChCopy.set(adCh);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// Note: slice() does not work here, since it returns a shallow copy.
|
|
87
|
-
for (let i = 0; i < read; i++) {
|
|
88
|
-
adChCopy[i] = adCh[i];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
trBuffers[ch] = adChCopy.buffer;
|
|
92
|
-
}
|
|
93
|
-
this.port.postMessage({
|
|
94
|
-
cmd: 'data',
|
|
95
|
-
chs: this.channelCount,
|
|
96
|
-
audioData: trBuffers
|
|
97
|
-
}, trBuffers);
|
|
98
|
-
filled += read;
|
|
99
|
-
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Sent "+read+" frames to audio source worklet. Filled: "+filled+", to fill: "+this.bufferFillFrames);
|
|
100
|
-
if (this._audioInputStream && filled < this.bufferFillFrames) {
|
|
101
|
-
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Next inddb audio input stream readObs");
|
|
102
|
-
return this._audioInputStream.readObs(this._aisBufs);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
if (this.stalled) {
|
|
106
|
-
this.port.postMessage({
|
|
107
|
-
cmd: 'continue'
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
return EMPTY;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Return EMPTY (read: "+read+")");
|
|
115
|
-
this._endOfStream = true;
|
|
116
|
-
this.port.postMessage({
|
|
117
|
-
cmd: 'endOfStream'
|
|
118
|
-
});
|
|
119
|
-
return EMPTY;
|
|
120
|
-
}
|
|
121
|
-
})).subscribe(subscriber);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
get netAudioBuffer() {
|
|
127
|
-
return this._netAudioBuffer;
|
|
128
|
-
}
|
|
129
|
-
set netAudioBuffer(value) {
|
|
130
|
-
this._netAudioBuffer = value;
|
|
131
|
-
if (this._netAudioBuffer?.channelCount) {
|
|
132
|
-
this.channelCount = this._netAudioBuffer?.channelCount;
|
|
133
|
-
this.bufferFillFrames = this._netAudioBuffer.sampleRate * this._bufferFillSeconds;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
get bufferFillSeconds() {
|
|
137
|
-
return this._bufferFillSeconds;
|
|
138
|
-
}
|
|
139
|
-
set bufferFillSeconds(value) {
|
|
140
|
-
this._bufferFillSeconds = value;
|
|
141
|
-
}
|
|
142
|
-
start(when, offset, duration) {
|
|
143
|
-
if (when) {
|
|
144
|
-
throw Error("when parameter currently not supported by IndexedDbAudioBufferSourceNode class");
|
|
145
|
-
}
|
|
146
|
-
this._playStartTime = null;
|
|
147
|
-
this.stopEndTime = null;
|
|
148
|
-
this.stalledTime = 0;
|
|
149
|
-
this.stalledStartTime = null;
|
|
150
|
-
if (this._netAudioBuffer) {
|
|
151
|
-
let arrAis = new NetAudioInputStream(this._netAudioBuffer);
|
|
152
|
-
if (offset === undefined && duration === undefined) {
|
|
153
|
-
this._audioInputStream = arrAis;
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
let offsetFrames = 0;
|
|
157
|
-
let durationFrames = undefined;
|
|
158
|
-
if (offset !== undefined) {
|
|
159
|
-
offsetFrames = Math.floor(offset * this._netAudioBuffer.sampleRate);
|
|
160
|
-
}
|
|
161
|
-
if (duration !== undefined) {
|
|
162
|
-
durationFrames = Math.floor(duration * this._netAudioBuffer.sampleRate);
|
|
163
|
-
}
|
|
164
|
-
//console.error("Playback selection not supported yet!");
|
|
165
|
-
this._audioInputStream = new AsyncEditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
|
|
166
|
-
}
|
|
167
|
-
let chs = this._netAudioBuffer.channelCount;
|
|
168
|
-
this._aisBufs = new Array(chs);
|
|
169
|
-
for (let ch = 0; ch < chs; ch++) {
|
|
170
|
-
this._aisBufs[ch] = new Float32Array(this._streamReadFrameLen);
|
|
171
|
-
}
|
|
172
|
-
this.fillBufferObs().subscribe({
|
|
173
|
-
complete: () => {
|
|
174
|
-
//console.debug("IndexedDbAudioBufferSourceNode::start: Async play buffer fill completed. Sending start command to audio worklet.");
|
|
175
|
-
if (this._active) {
|
|
176
|
-
this.port.postMessage({ cmd: 'start' });
|
|
177
|
-
if (offset) {
|
|
178
|
-
this._playStartTime = this.context.currentTime - offset;
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
this._playStartTime = this.context.currentTime;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
this._active = true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
stop() {
|
|
190
|
-
this._active = false;
|
|
191
|
-
this.port.postMessage({ cmd: 'stop' });
|
|
192
|
-
this.onended?.call(this);
|
|
193
|
-
}
|
|
194
|
-
get playPositionTime() {
|
|
195
|
-
let ppt = null;
|
|
196
|
-
if (this._playStartTime !== null) {
|
|
197
|
-
if (this._active) {
|
|
198
|
-
if (this.stalledStartTime == null) {
|
|
199
|
-
ppt = this.context.currentTime - this._playStartTime - this.stalledTime;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
ppt = this.stalledStartTime - this._playStartTime - this.stalledTime;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
if (this.stopEndTime != null) {
|
|
207
|
-
// if(this._netAudioBuffer?.frameLen !=null && this._netAudioBuffer.sampleRate) {
|
|
208
|
-
// ppt = this._netAudioBuffer?.frameLen / this._netAudioBuffer?.sampleRate;
|
|
209
|
-
// }else{
|
|
210
|
-
ppt = this.stopEndTime - this._playStartTime - this.stalledTime;
|
|
211
|
-
//}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return ppt;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
1
|
+
import { AsyncEditFloat32ArrayInputStream } from "../../io/stream";
|
|
2
|
+
import { ArrayAudioBufferSourceNode } from "./array_audio_buffer_source_node";
|
|
3
|
+
import { EMPTY, expand, Observable } from "rxjs";
|
|
4
|
+
import { AudioSourceNode } from "./audio_source_node";
|
|
5
|
+
import { NetAudioInputStream } from "../net_audio_buffer";
|
|
6
|
+
export class NetAudioBufferSourceNode extends AudioSourceNode {
|
|
7
|
+
constructor(context) {
|
|
8
|
+
super(context, 'audio-source-worklet');
|
|
9
|
+
this._bufferFillSeconds = AudioSourceNode.DEFAULT_BUFFER_FILL_SECONDS;
|
|
10
|
+
this.bufferFillFrames = 0;
|
|
11
|
+
this._streamReadFrameLen = AudioSourceNode.DEFAULT_STREAM_READ_FRAME_LEN * 8; // Much overhead fetching small buffers from indexed db, use larger buffer (8192).
|
|
12
|
+
this._netAudioBuffer = null;
|
|
13
|
+
this._audioInputStream = null;
|
|
14
|
+
this._aisBufs = null;
|
|
15
|
+
this._active = false;
|
|
16
|
+
this.stalled = false;
|
|
17
|
+
this._endOfStream = false;
|
|
18
|
+
this.readDataSubscription = null;
|
|
19
|
+
this.filledFrames = 0;
|
|
20
|
+
this.stalledStartTime = null;
|
|
21
|
+
this.stalledTime = 0;
|
|
22
|
+
this.stopEndTime = null;
|
|
23
|
+
this.channelCountMode = 'explicit';
|
|
24
|
+
this.port.onmessage = (msgEv) => {
|
|
25
|
+
if (msgEv.data) {
|
|
26
|
+
let evType = msgEv.data.eventType;
|
|
27
|
+
if (evType) {
|
|
28
|
+
if ('bufferNotification' === evType) {
|
|
29
|
+
this.filledFrames = msgEv.data.filledFrames;
|
|
30
|
+
//console.debug("IndexedDbAudioBufferSourceNode: Buffer notification: filled frames: " + this.filledFrames);
|
|
31
|
+
if (!this._endOfStream) {
|
|
32
|
+
this.fillBufferObs().subscribe();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if ('ended' === evType) {
|
|
36
|
+
//console.debug("Inddb audio source ended playback.");
|
|
37
|
+
let drainTime = 0;
|
|
38
|
+
if (this._netAudioBuffer?.sampleRate) {
|
|
39
|
+
drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._netAudioBuffer.sampleRate;
|
|
40
|
+
}
|
|
41
|
+
//let dstAny:any=this.context.destination;
|
|
42
|
+
//console.debug('Destination node tail-time: '+dstAny['tail-time']);
|
|
43
|
+
window.setTimeout(() => {
|
|
44
|
+
this.stopEndTime = this.context.currentTime;
|
|
45
|
+
this._active = false;
|
|
46
|
+
this.onended?.call(this);
|
|
47
|
+
}, drainTime * 1000);
|
|
48
|
+
}
|
|
49
|
+
else if ('stalled' === evType) {
|
|
50
|
+
console.debug('Playback stalled...');
|
|
51
|
+
this.stalled = true;
|
|
52
|
+
this.stalledStartTime = this.context.currentTime;
|
|
53
|
+
}
|
|
54
|
+
else if ('resumed' === evType) {
|
|
55
|
+
console.debug('Playback resumed after stall.');
|
|
56
|
+
this.stalled = false;
|
|
57
|
+
if (this.stalledStartTime != null) {
|
|
58
|
+
this.stalledTime += this.context.currentTime - this.stalledStartTime;
|
|
59
|
+
this.stalledStartTime = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
fillBufferObs(frameOffset) {
|
|
67
|
+
return new Observable(subscriber => {
|
|
68
|
+
if (frameOffset) {
|
|
69
|
+
subscriber.error(new Error("Starting playback from position not equal zero not supported yet."));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
let filled = this.filledFrames;
|
|
73
|
+
let bufLen = 0;
|
|
74
|
+
if (this._audioInputStream && this._aisBufs && (this.readDataSubscription == null || this.readDataSubscription?.closed)) {
|
|
75
|
+
this.readDataSubscription = this._audioInputStream.readObs(this._aisBufs).pipe(expand((read) => {
|
|
76
|
+
if (read && this._aisBufs) {
|
|
77
|
+
let trBuffers = new Array(this.channelCount);
|
|
78
|
+
for (let ch = 0; ch < this.channelCount; ch++) {
|
|
79
|
+
let adCh = this._aisBufs[ch];
|
|
80
|
+
let adChCopy = new Float32Array(read);
|
|
81
|
+
bufLen = adChCopy.length;
|
|
82
|
+
if (read === adCh.length) {
|
|
83
|
+
adChCopy.set(adCh);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Note: slice() does not work here, since it returns a shallow copy.
|
|
87
|
+
for (let i = 0; i < read; i++) {
|
|
88
|
+
adChCopy[i] = adCh[i];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
trBuffers[ch] = adChCopy.buffer;
|
|
92
|
+
}
|
|
93
|
+
this.port.postMessage({
|
|
94
|
+
cmd: 'data',
|
|
95
|
+
chs: this.channelCount,
|
|
96
|
+
audioData: trBuffers
|
|
97
|
+
}, trBuffers);
|
|
98
|
+
filled += read;
|
|
99
|
+
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Sent "+read+" frames to audio source worklet. Filled: "+filled+", to fill: "+this.bufferFillFrames);
|
|
100
|
+
if (this._audioInputStream && filled < this.bufferFillFrames) {
|
|
101
|
+
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Next inddb audio input stream readObs");
|
|
102
|
+
return this._audioInputStream.readObs(this._aisBufs);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (this.stalled) {
|
|
106
|
+
this.port.postMessage({
|
|
107
|
+
cmd: 'continue'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return EMPTY;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
//console.debug("IndexedDbAudioBufferSourceNode::fillBufferObs: Return EMPTY (read: "+read+")");
|
|
115
|
+
this._endOfStream = true;
|
|
116
|
+
this.port.postMessage({
|
|
117
|
+
cmd: 'endOfStream'
|
|
118
|
+
});
|
|
119
|
+
return EMPTY;
|
|
120
|
+
}
|
|
121
|
+
})).subscribe(subscriber);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
get netAudioBuffer() {
|
|
127
|
+
return this._netAudioBuffer;
|
|
128
|
+
}
|
|
129
|
+
set netAudioBuffer(value) {
|
|
130
|
+
this._netAudioBuffer = value;
|
|
131
|
+
if (this._netAudioBuffer?.channelCount) {
|
|
132
|
+
this.channelCount = this._netAudioBuffer?.channelCount;
|
|
133
|
+
this.bufferFillFrames = this._netAudioBuffer.sampleRate * this._bufferFillSeconds;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
get bufferFillSeconds() {
|
|
137
|
+
return this._bufferFillSeconds;
|
|
138
|
+
}
|
|
139
|
+
set bufferFillSeconds(value) {
|
|
140
|
+
this._bufferFillSeconds = value;
|
|
141
|
+
}
|
|
142
|
+
start(when, offset, duration) {
|
|
143
|
+
if (when) {
|
|
144
|
+
throw Error("when parameter currently not supported by IndexedDbAudioBufferSourceNode class");
|
|
145
|
+
}
|
|
146
|
+
this._playStartTime = null;
|
|
147
|
+
this.stopEndTime = null;
|
|
148
|
+
this.stalledTime = 0;
|
|
149
|
+
this.stalledStartTime = null;
|
|
150
|
+
if (this._netAudioBuffer) {
|
|
151
|
+
let arrAis = new NetAudioInputStream(this._netAudioBuffer);
|
|
152
|
+
if (offset === undefined && duration === undefined) {
|
|
153
|
+
this._audioInputStream = arrAis;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
let offsetFrames = 0;
|
|
157
|
+
let durationFrames = undefined;
|
|
158
|
+
if (offset !== undefined) {
|
|
159
|
+
offsetFrames = Math.floor(offset * this._netAudioBuffer.sampleRate);
|
|
160
|
+
}
|
|
161
|
+
if (duration !== undefined) {
|
|
162
|
+
durationFrames = Math.floor(duration * this._netAudioBuffer.sampleRate);
|
|
163
|
+
}
|
|
164
|
+
//console.error("Playback selection not supported yet!");
|
|
165
|
+
this._audioInputStream = new AsyncEditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
|
|
166
|
+
}
|
|
167
|
+
let chs = this._netAudioBuffer.channelCount;
|
|
168
|
+
this._aisBufs = new Array(chs);
|
|
169
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
170
|
+
this._aisBufs[ch] = new Float32Array(this._streamReadFrameLen);
|
|
171
|
+
}
|
|
172
|
+
this.fillBufferObs().subscribe({
|
|
173
|
+
complete: () => {
|
|
174
|
+
//console.debug("IndexedDbAudioBufferSourceNode::start: Async play buffer fill completed. Sending start command to audio worklet.");
|
|
175
|
+
if (this._active) {
|
|
176
|
+
this.port.postMessage({ cmd: 'start' });
|
|
177
|
+
if (offset) {
|
|
178
|
+
this._playStartTime = this.context.currentTime - offset;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this._playStartTime = this.context.currentTime;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
this._active = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
stop() {
|
|
190
|
+
this._active = false;
|
|
191
|
+
this.port.postMessage({ cmd: 'stop' });
|
|
192
|
+
this.onended?.call(this);
|
|
193
|
+
}
|
|
194
|
+
get playPositionTime() {
|
|
195
|
+
let ppt = null;
|
|
196
|
+
if (this._playStartTime !== null) {
|
|
197
|
+
if (this._active) {
|
|
198
|
+
if (this.stalledStartTime == null) {
|
|
199
|
+
ppt = this.context.currentTime - this._playStartTime - this.stalledTime;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
ppt = this.stalledStartTime - this._playStartTime - this.stalledTime;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
if (this.stopEndTime != null) {
|
|
207
|
+
// if(this._netAudioBuffer?.frameLen !=null && this._netAudioBuffer.sampleRate) {
|
|
208
|
+
// ppt = this._netAudioBuffer?.frameLen / this._netAudioBuffer?.sampleRate;
|
|
209
|
+
// }else{
|
|
210
|
+
ppt = this.stopEndTime - this._playStartTime - this.stalledTime;
|
|
211
|
+
//}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return ppt;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
218
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmV0X2F1ZGlvX2J1ZmZlcl9zb3VyY2Vfbm9kZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3NwZWVjaHJlY29yZGVybmcvc3JjL2xpYi9hdWRpby9wbGF5YmFjay9uZXRfYXVkaW9fYnVmZmVyX3NvdXJjZV9ub2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxnQ0FBZ0MsRUFBK0IsTUFBTSxpQkFBaUIsQ0FBQztBQUMvRixPQUFPLEVBQUMsMEJBQTBCLEVBQUMsTUFBTSxrQ0FBa0MsQ0FBQztBQUM1RSxPQUFPLEVBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQWUsTUFBTSxNQUFNLENBQUM7QUFDN0QsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3BELE9BQU8sRUFBaUIsbUJBQW1CLEVBQUMsTUFBTSxxQkFBcUIsQ0FBQztBQUV4RSxNQUFNLE9BQU8sd0JBQXlCLFNBQVEsZUFBZTtJQW1CM0QsWUFBWSxPQUFxQjtRQUUvQixLQUFLLENBQUMsT0FBTyxFQUFFLHNCQUFzQixDQUFDLENBQUM7UUFuQmpDLHVCQUFrQixHQUFHLGVBQWUsQ0FBQywyQkFBMkIsQ0FBQztRQUNqRSxxQkFBZ0IsR0FBRyxDQUFDLENBQUM7UUFDckIsd0JBQW1CLEdBQUMsZUFBZSxDQUFDLDZCQUE2QixHQUFHLENBQUMsQ0FBQyxDQUFFLGtGQUFrRjtRQUMxSixvQkFBZSxHQUFxQixJQUFJLENBQUM7UUFDekMsc0JBQWlCLEdBQW1DLElBQUksQ0FBQztRQUN6RCxhQUFRLEdBQXFCLElBQUksQ0FBQztRQUNsQyxZQUFPLEdBQUMsS0FBSyxDQUFDO1FBQ2QsWUFBTyxHQUFDLEtBQUssQ0FBQztRQUNkLGlCQUFZLEdBQUMsS0FBSyxDQUFDO1FBQ25CLHlCQUFvQixHQUFtQixJQUFJLENBQUM7UUFDNUMsaUJBQVksR0FBRyxDQUFDLENBQUM7UUFFakIscUJBQWdC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QUFDO1FBQ3JDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRCxJQUFJLGdCQUFnQjtRQUNsQixJQUFJLEdBQUcsR0FBYSxJQUFJLENBQUM7UUFDekIsSUFBRyxJQUFJLENBQUMsY0FBYyxLQUFHLElBQUksRUFBRTtZQUM3QixJQUFHLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ2YsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLElBQUksSUFBSSxFQUFFO29CQUNqQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO2lCQUN6RTtxQkFBTTtvQkFDTCxHQUFHLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztpQkFDdEU7YUFDRjtpQkFBSTtnQkFDSCxJQUFHLElBQUksQ0FBQyxXQUFXLElBQUUsSUFBSSxFQUFDO29CQUN4QixpRkFBaUY7b0JBQ2pGLDZFQUE2RTtvQkFDN0UsU0FBUztvQkFDUCxHQUFHLEdBQUcsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7b0JBQ2xFLEdBQUc7aUJBQ0o7YUFDRjtTQUNGO1FBQ0QsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDO0NBRUYiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0FzeW5jRWRpdEZsb2F0MzJBcnJheUlucHV0U3RyZWFtLCBBc3luY0Zsb2F0MzJBcnJheUlucHV0U3RyZWFtfSBmcm9tIFwiLi4vLi4vaW8vc3RyZWFtXCI7XHJcbmltcG9ydCB7QXJyYXlBdWRpb0J1ZmZlclNvdXJjZU5vZGV9IGZyb20gXCIuL2FycmF5X2F1ZGlvX2J1ZmZlcl9zb3VyY2Vfbm9kZVwiO1xyXG5pbXBvcnQge0VNUFRZLCBleHBhbmQsIE9ic2VydmFibGUsIFN1YnNjcmlwdGlvbn0gZnJvbSBcInJ4anNcIjtcclxuaW1wb3J0IHtBdWRpb1NvdXJjZU5vZGV9IGZyb20gXCIuL2F1ZGlvX3NvdXJjZV9ub2RlXCI7XHJcbmltcG9ydCB7TmV0QXVkaW9CdWZmZXIsIE5ldEF1ZGlvSW5wdXRTdHJlYW19IGZyb20gXCIuLi9uZXRfYXVkaW9fYnVmZmVyXCI7XHJcblxyXG5leHBvcnQgY2xhc3MgTmV0QXVkaW9CdWZmZXJTb3VyY2VOb2RlIGV4dGVuZHMgQXVkaW9Tb3VyY2VOb2RlIHtcclxuXHJcbiAgcHJpdmF0ZSBfYnVmZmVyRmlsbFNlY29uZHMgPSBBdWRpb1NvdXJjZU5vZGUuREVGQVVMVF9CVUZGRVJfRklMTF9TRUNPTkRTO1xyXG4gIHByaXZhdGUgYnVmZmVyRmlsbEZyYW1lcyA9IDA7XHJcbiAgcHJpdmF0ZSBfc3RyZWFtUmVhZEZyYW1lTGVuPUF1ZGlvU291cmNlTm9kZS5ERUZBVUxUX1NUUkVBTV9SRUFEX0ZSQU1FX0xFTiAqIDg7ICAvLyBNdWNoIG92ZXJoZWFkIGZldGNoaW5nIHNtYWxsIGJ1ZmZlcnMgZnJvbSBpbmRleGVkIGRiLCB1c2UgbGFyZ2VyIGJ1ZmZlciAoODE5MikuXHJcbiAgcHJpdmF0ZSBfbmV0QXVkaW9CdWZmZXI6TmV0QXVkaW9CdWZmZXJ8bnVsbD1udWxsO1xyXG4gIHByaXZhdGUgX2F1ZGlvSW5wdXRTdHJlYW06QXN5bmNGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbXxudWxsPW51bGw7XHJcbiAgcHJpdmF0ZSBfYWlzQnVmczpGbG9hdDMyQXJyYXlbXXxudWxsPW51bGw7XHJcbiAgcHJpdmF0ZSBfYWN0aXZlPWZhbHNlO1xyXG4gIHByaXZhdGUgc3RhbGxlZD1mYWxzZTtcclxuICBwcml2YXRlIF9lbmRPZlN0cmVhbT1mYWxzZTtcclxuICBwcml2YXRlIHJlYWREYXRhU3Vic2NyaXB0aW9uOlN1YnNjcmlwdGlvbnxudWxsPW51bGw7XHJcbiAgcHJpdmF0ZSBmaWxsZWRGcmFtZXMgPSAwO1xyXG5cclxuICBwcml2YXRlIHN0YWxsZWRTdGFydFRpbWU6bnVtYmVyfG51bGw9bnVsbDtcclxuICBwcml2YXRlIHN0YWxsZWRUaW1lOm51bWJlcj0wO1xyXG5cclxuICBwcml2YXRlIHN0b3BFbmRUaW1lOm51bWJlcnxudWxsPW51bGw7XHJcblxyXG4gIGNvbnN0cnVjdG9yKGNvbnRleHQ6IEF1ZGlvQ29udGV4dCkge1xyXG5cclxuICAgIHN1cGVyKGNvbnRleHQsICdhdWRpby1zb3VyY2Utd29ya2xldCcpO1xyXG4gICAgdGhpcy5jaGFubmVsQ291bnRNb2RlID0gJ2V4cGxpY2l0JztcclxuICAgIHRoaXMucG9ydC5vbm1lc3NhZ2UgPSAobXNnRXY6IE1lc3NhZ2VFdmVudCkgPT4ge1xyXG4gICAgICBpZiAobXNnRXYuZGF0YSkge1xyXG4gICAgICAgIGxldCBldlR5cGUgPSBtc2dFdi5kYXRhLmV2ZW50VHlwZTtcclxuICAgICAgICBpZiAoZXZUeXBlKSB7XHJcbiAgICAgICAgICBpZiAoJ2J1ZmZlck5vdGlmaWNhdGlvbicgPT09IGV2VHlwZSkge1xyXG4gICAgICAgICAgICB0aGlzLmZpbGxlZEZyYW1lcyA9IG1zZ0V2LmRhdGEuZmlsbGVkRnJhbWVzO1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJJbmRleGVkRGJBdWRpb0J1ZmZlclNvdXJjZU5vZGU6IEJ1ZmZlciBub3RpZmljYXRpb246IGZpbGxlZCBmcmFtZXM6IFwiICsgdGhpcy5maWxsZWRGcmFtZXMpO1xyXG4gICAgICAgICAgICBpZighdGhpcy5fZW5kT2ZTdHJlYW0pIHtcclxuICAgICAgICAgICAgICB0aGlzLmZpbGxCdWZmZXJPYnMoKS5zdWJzY3JpYmUoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIGlmICgnZW5kZWQnID09PSBldlR5cGUpIHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiSW5kZGIgYXVkaW8gc291cmNlIGVuZGVkIHBsYXliYWNrLlwiKTtcclxuICAgICAgICAgICAgbGV0IGRyYWluVGltZSA9IDA7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLl9uZXRBdWRpb0J1ZmZlcj8uc2FtcGxlUmF0ZSkge1xyXG4gICAgICAgICAgICAgIGRyYWluVGltZSA9IEFycmF5QXVkaW9CdWZmZXJTb3VyY2VOb2RlLlFVQU5UVU1fRlJBTUVfTEVOIC8gdGhpcy5fbmV0QXVkaW9CdWZmZXIuc2FtcGxlUmF0ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAvL2xldCBkc3RBbnk6YW55PXRoaXMuY29udGV4dC5kZXN0aW5hdGlvbjtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdEZXN0aW5hdGlvbiBub2RlIHRhaWwtdGltZTogJytkc3RBbnlbJ3RhaWwtdGltZSddKTtcclxuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHRoaXMuc3RvcEVuZFRpbWU9dGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lO1xyXG4gICAgICAgICAgICAgIHRoaXMuX2FjdGl2ZT1mYWxzZTtcclxuICAgICAgICAgICAgICB0aGlzLm9uZW5kZWQ/LmNhbGwodGhpcyk7XHJcbiAgICAgICAgICAgIH0sIGRyYWluVGltZSAqIDEwMDApO1xyXG5cclxuICAgICAgICAgIH1lbHNlIGlmICgnc3RhbGxlZCcgPT09IGV2VHlwZSkge1xyXG4gICAgICAgICAgICBjb25zb2xlLmRlYnVnKCdQbGF5YmFjayBzdGFsbGVkLi4uJyk7XHJcbiAgICAgICAgICAgIHRoaXMuc3RhbGxlZD10cnVlO1xyXG4gICAgICAgICAgICB0aGlzLnN0YWxsZWRTdGFydFRpbWU9dGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lO1xyXG4gICAgICAgICAgfWVsc2UgaWYgKCdyZXN1bWVkJyA9PT0gZXZUeXBlKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZGVidWcoJ1BsYXliYWNrIHJlc3VtZWQgYWZ0ZXIgc3RhbGwuJyk7XHJcbiAgICAgICAgICAgIHRoaXMuc3RhbGxlZD1mYWxzZTtcclxuICAgICAgICAgICAgaWYodGhpcy5zdGFsbGVkU3RhcnRUaW1lIT1udWxsKSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5zdGFsbGVkVGltZSArPSB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgLSB0aGlzLnN0YWxsZWRTdGFydFRpbWU7XHJcbiAgICAgICAgICAgICAgdGhpcy5zdGFsbGVkU3RhcnRUaW1lID0gbnVsbDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBmaWxsQnVmZmVyT2JzKGZyYW1lT2Zmc2V0PzpudW1iZXIpOk9ic2VydmFibGU8bnVtYmVyfG51bGw+IHtcclxuXHJcbiAgICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTxudW1iZXIgfCBudWxsPihzdWJzY3JpYmVyID0+IHtcclxuICAgICAgICBpZihmcmFtZU9mZnNldCl7XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKG5ldyBFcnJvcihcIlN0YXJ0aW5nIHBsYXliYWNrIGZyb20gcG9zaXRpb24gbm90IGVxdWFsIHplcm8gbm90IHN1cHBvcnRlZCB5ZXQuXCIpKTtcclxuICAgICAgICB9ZWxzZSB7XHJcbiAgICAgICAgICBsZXQgZmlsbGVkID0gdGhpcy5maWxsZWRGcmFtZXM7XHJcbiAgICAgICAgICBsZXQgYnVmTGVuID0gMDtcclxuICAgICAgICAgIGlmICh0aGlzLl9hdWRpb0lucHV0U3RyZWFtICYmIHRoaXMuX2Fpc0J1ZnMgJiYgKHRoaXMucmVhZERhdGFTdWJzY3JpcHRpb249PW51bGwgfHwgdGhpcy5yZWFkRGF0YVN1YnNjcmlwdGlvbj8uY2xvc2VkKSkge1xyXG4gICAgICAgICAgICB0aGlzLnJlYWREYXRhU3Vic2NyaXB0aW9uPXRoaXMuX2F1ZGlvSW5wdXRTdHJlYW0ucmVhZE9icyh0aGlzLl9haXNCdWZzKS5waXBlKFxyXG4gICAgICAgICAgICAgIGV4cGFuZCgocmVhZCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICBpZiAocmVhZCAmJiB0aGlzLl9haXNCdWZzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgbGV0IHRyQnVmZmVycyA9IG5ldyBBcnJheTxhbnk+KHRoaXMuY2hhbm5lbENvdW50KTtcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICAgICAgICAgIGxldCBhZENoID0gdGhpcy5fYWlzQnVmc1tjaF07XHJcbiAgICAgICAgICAgICAgICAgICAgICBsZXQgYWRDaENvcHkgPSBuZXcgRmxvYXQzMkFycmF5KHJlYWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgYnVmTGVuID0gYWRDaENvcHkubGVuZ3RoO1xyXG4gICAgICAgICAgICAgICAgICAgICAgaWYocmVhZD09PWFkQ2gubGVuZ3RoKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGFkQ2hDb3B5LnNldChhZENoKTtcclxuICAgICAgICAgICAgICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBOb3RlOiBzbGljZSgpIGRvZXMgbm90IHdvcmsgaGVyZSwgc2luY2UgaXQgcmV0dXJucyBhIHNoYWxsb3cgY29weS5cclxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yKGxldCBpPTA7aTxyZWFkO2krKyl7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgYWRDaENvcHlbaV09YWRDaFtpXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgdHJCdWZmZXJzW2NoXSA9IGFkQ2hDb3B5LmJ1ZmZlcjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgICAgICAgIGNtZDogJ2RhdGEnLFxyXG4gICAgICAgICAgICAgICAgICAgICAgY2hzOiB0aGlzLmNoYW5uZWxDb3VudCxcclxuICAgICAgICAgICAgICAgICAgICAgIGF1ZGlvRGF0YTogdHJCdWZmZXJzXHJcbiAgICAgICAgICAgICAgICAgICAgfSwgdHJCdWZmZXJzKTtcclxuICAgICAgICAgICAgICAgICAgICBmaWxsZWQgKz0gcmVhZDtcclxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJJbmRleGVkRGJBdWRpb0J1ZmZlclNvdXJjZU5vZGU6OmZpbGxCdWZmZXJPYnM6IFNlbnQgXCIrcmVhZCtcIiBmcmFtZXMgdG8gYXVkaW8gc291cmNlIHdvcmtsZXQuIEZpbGxlZDogXCIrZmlsbGVkK1wiLCB0byBmaWxsOiBcIit0aGlzLmJ1ZmZlckZpbGxGcmFtZXMpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLl9hdWRpb0lucHV0U3RyZWFtICYmIGZpbGxlZCA8IHRoaXMuYnVmZmVyRmlsbEZyYW1lcykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiSW5kZXhlZERiQXVkaW9CdWZmZXJTb3VyY2VOb2RlOjpmaWxsQnVmZmVyT2JzOiBOZXh0IGluZGRiIGF1ZGlvIGlucHV0IHN0cmVhbSByZWFkT2JzXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2F1ZGlvSW5wdXRTdHJlYW0ucmVhZE9icyh0aGlzLl9haXNCdWZzKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5zdGFsbGVkKXtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICBjbWQ6ICdjb250aW51ZSdcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEVNUFRZO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJJbmRleGVkRGJBdWRpb0J1ZmZlclNvdXJjZU5vZGU6OmZpbGxCdWZmZXJPYnM6IFJldHVybiBFTVBUWSAocmVhZDogXCIrcmVhZCtcIilcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fZW5kT2ZTdHJlYW09dHJ1ZTtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2Uoe1xyXG4gICAgICAgICAgICAgICAgICAgICAgY21kOiAnZW5kT2ZTdHJlYW0nXHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEVNUFRZO1xyXG4gICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICApLnN1YnNjcmliZShzdWJzY3JpYmVyKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG4gIH1cclxuXHJcblxyXG4gIGdldCBuZXRBdWRpb0J1ZmZlcigpOiBOZXRBdWRpb0J1ZmZlciB8IG51bGwge1xyXG4gICAgcmV0dXJuIHRoaXMuX25ldEF1ZGlvQnVmZmVyXHJcbiAgfVxyXG5cclxuICBzZXQgbmV0QXVkaW9CdWZmZXIodmFsdWU6IE5ldEF1ZGlvQnVmZmVyIHwgbnVsbCkge1xyXG4gICAgdGhpcy5fbmV0QXVkaW9CdWZmZXIgPSB2YWx1ZTtcclxuICAgIGlmICh0aGlzLl9uZXRBdWRpb0J1ZmZlcj8uY2hhbm5lbENvdW50KSB7XHJcbiAgICAgIHRoaXMuY2hhbm5lbENvdW50ID0gdGhpcy5fbmV0QXVkaW9CdWZmZXI/LmNoYW5uZWxDb3VudDtcclxuICAgICAgdGhpcy5idWZmZXJGaWxsRnJhbWVzID0gdGhpcy5fbmV0QXVkaW9CdWZmZXIuc2FtcGxlUmF0ZSAqIHRoaXMuX2J1ZmZlckZpbGxTZWNvbmRzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZ2V0IGJ1ZmZlckZpbGxTZWNvbmRzKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fYnVmZmVyRmlsbFNlY29uZHM7XHJcbiAgfVxyXG5cclxuICBzZXQgYnVmZmVyRmlsbFNlY29uZHModmFsdWU6IG51bWJlcikge1xyXG4gICAgdGhpcy5fYnVmZmVyRmlsbFNlY29uZHMgPSB2YWx1ZTtcclxuICB9XHJcblxyXG4gIHN0YXJ0KHdoZW4/OiBudW1iZXIgfCB1bmRlZmluZWQsb2Zmc2V0PzogbnVtYmVyIHwgdW5kZWZpbmVkLGR1cmF0aW9uPzogbnVtYmVyIHwgdW5kZWZpbmVkKTogdm9pZCB7XHJcbiAgICBpZiAod2hlbikge1xyXG4gICAgICB0aHJvdyBFcnJvcihcIndoZW4gcGFyYW1ldGVyIGN1cnJlbnRseSBub3Qgc3VwcG9ydGVkIGJ5IEluZGV4ZWREYkF1ZGlvQnVmZmVyU291cmNlTm9kZSBjbGFzc1wiKVxyXG4gICAgfVxyXG4gICAgdGhpcy5fcGxheVN0YXJ0VGltZT1udWxsO1xyXG4gICAgdGhpcy5zdG9wRW5kVGltZT1udWxsO1xyXG4gICAgdGhpcy5zdGFsbGVkVGltZT0wO1xyXG4gICAgdGhpcy5zdGFsbGVkU3RhcnRUaW1lPW51bGw7XHJcblxyXG4gICAgaWYgKHRoaXMuX25ldEF1ZGlvQnVmZmVyKSB7XHJcbiAgICAgIGxldCBhcnJBaXM9bmV3IE5ldEF1ZGlvSW5wdXRTdHJlYW0odGhpcy5fbmV0QXVkaW9CdWZmZXIpO1xyXG4gICAgICBpZihvZmZzZXQ9PT11bmRlZmluZWQgJiYgZHVyYXRpb249PT11bmRlZmluZWQpe1xyXG4gICAgICAgIHRoaXMuX2F1ZGlvSW5wdXRTdHJlYW0gPSBhcnJBaXM7XHJcbiAgICAgIH1lbHNle1xyXG4gICAgICAgIGxldCBvZmZzZXRGcmFtZXM9MDtcclxuICAgICAgICBsZXQgZHVyYXRpb25GcmFtZXM9dW5kZWZpbmVkO1xyXG4gICAgICAgIGlmKG9mZnNldCE9PXVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgb2Zmc2V0RnJhbWVzPU1hdGguZmxvb3Iob2Zmc2V0ICogdGhpcy5fbmV0QXVkaW9CdWZmZXIuc2FtcGxlUmF0ZSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKGR1cmF0aW9uIT09dW5kZWZpbmVkKXtcclxuICAgICAgICAgIGR1cmF0aW9uRnJhbWVzPU1hdGguZmxvb3IoZHVyYXRpb24gKiB0aGlzLl9uZXRBdWRpb0J1ZmZlci5zYW1wbGVSYXRlKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgLy9jb25zb2xlLmVycm9yKFwiUGxheWJhY2sgc2VsZWN0aW9uIG5vdCBzdXBwb3J0ZWQgeWV0IVwiKTtcclxuICAgICAgICB0aGlzLl9hdWRpb0lucHV0U3RyZWFtPW5ldyBBc3luY0VkaXRGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbShhcnJBaXMsb2Zmc2V0RnJhbWVzLGR1cmF0aW9uRnJhbWVzKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgbGV0IGNocz10aGlzLl9uZXRBdWRpb0J1ZmZlci5jaGFubmVsQ291bnQ7XHJcbiAgICAgIHRoaXMuX2Fpc0J1ZnM9bmV3IEFycmF5PEZsb2F0MzJBcnJheT4oY2hzKTtcclxuICAgICAgZm9yKGxldCBjaD0wO2NoPGNocztjaCsrKXtcclxuICAgICAgICB0aGlzLl9haXNCdWZzW2NoXT1uZXcgRmxvYXQzMkFycmF5KHRoaXMuX3N0cmVhbVJlYWRGcmFtZUxlbik7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHRoaXMuZmlsbEJ1ZmZlck9icygpLnN1YnNjcmliZSh7XHJcbiAgICAgICAgY29tcGxldGU6ICgpPT57XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJJbmRleGVkRGJBdWRpb0J1ZmZlclNvdXJjZU5vZGU6OnN0YXJ0OiBBc3luYyBwbGF5IGJ1ZmZlciBmaWxsIGNvbXBsZXRlZC4gU2VuZGluZyBzdGFydCBjb21tYW5kIHRvIGF1ZGlvIHdvcmtsZXQuXCIpO1xyXG4gICAgICAgICAgaWYodGhpcy5fYWN0aXZlKSB7XHJcbiAgICAgICAgICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZSh7Y21kOiAnc3RhcnQnfSk7XHJcbiAgICAgICAgICAgIGlmKG9mZnNldCkge1xyXG4gICAgICAgICAgICAgIHRoaXMuX3BsYXlTdGFydFRpbWUgPSB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgLSBvZmZzZXQ7XHJcbiAgICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAgIHRoaXMuX3BsYXlTdGFydFRpbWUgPSB0aGlzLmNvbnRleHQuY3VycmVudFRpbWU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0pXHJcbiAgICAgIHRoaXMuX2FjdGl2ZT10cnVlO1xyXG5cclxuICAgIH1cclxuICB9XHJcblxyXG4gIHN0b3AoKSB7XHJcbiAgICB0aGlzLl9hY3RpdmU9ZmFsc2U7XHJcbiAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2Uoe2NtZDogJ3N0b3AnfSk7XHJcbiAgICB0aGlzLm9uZW5kZWQ/LmNhbGwodGhpcyk7XHJcbiAgfVxyXG5cclxuICBnZXQgcGxheVBvc2l0aW9uVGltZSgpOm51bWJlcnxudWxsIHtcclxuICAgIGxldCBwcHQ6bnVtYmVyfG51bGw9bnVsbDtcclxuICAgIGlmKHRoaXMuX3BsYXlTdGFydFRpbWUhPT1udWxsKSB7XHJcbiAgICAgIGlmKHRoaXMuX2FjdGl2ZSkge1xyXG4gICAgICAgIGlmICh0aGlzLnN0YWxsZWRTdGFydFRpbWUgPT0gbnVsbCkge1xyXG4gICAgICAgICAgcHB0ID0gdGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lIC0gdGhpcy5fcGxheVN0YXJ0VGltZSAtIHRoaXMuc3RhbGxlZFRpbWU7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHBwdCA9IHRoaXMuc3RhbGxlZFN0YXJ0VGltZSAtIHRoaXMuX3BsYXlTdGFydFRpbWUgLSB0aGlzLnN0YWxsZWRUaW1lO1xyXG4gICAgICAgIH1cclxuICAgICAgfWVsc2V7XHJcbiAgICAgICAgaWYodGhpcy5zdG9wRW5kVGltZSE9bnVsbCl7XHJcbiAgICAgICAgICAvLyBpZih0aGlzLl9uZXRBdWRpb0J1ZmZlcj8uZnJhbWVMZW4gIT1udWxsICYmIHRoaXMuX25ldEF1ZGlvQnVmZmVyLnNhbXBsZVJhdGUpIHtcclxuICAgICAgICAgIC8vICAgcHB0ID0gdGhpcy5fbmV0QXVkaW9CdWZmZXI/LmZyYW1lTGVuIC8gdGhpcy5fbmV0QXVkaW9CdWZmZXI/LnNhbXBsZVJhdGU7XHJcbiAgICAgICAgICAvLyB9ZWxzZXtcclxuICAgICAgICAgICAgcHB0ID0gdGhpcy5zdG9wRW5kVGltZSAtIHRoaXMuX3BsYXlTdGFydFRpbWUgLSB0aGlzLnN0YWxsZWRUaW1lO1xyXG4gICAgICAgICAgLy99XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcHB0O1xyXG4gIH1cclxuXHJcbn1cclxuXHJcbiJdfQ==
|