OLD | NEW |
1 // Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 1 // Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
2 // | 2 // |
3 // Use of this source code is governed by a BSD-style license | 3 // Use of this source code is governed by a BSD-style license |
4 // that can be found in the LICENSE file in the root of the source | 4 // that can be found in the LICENSE file in the root of the source |
5 // tree. An additional intellectual property rights grant can be found | 5 // tree. An additional intellectual property rights grant can be found |
6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
8 | 8 |
9 /** | 9 var inspector = null; |
10 * Inspector UI class. | 10 |
| 11 /** |
| 12 * Opens the score stats inspector dialog. |
| 13 * @param {String} dialogId: identifier of the dialog to show. |
| 14 */ |
| 15 function openScoreStatsInspector(dialogId) { |
| 16 var dialog = document.getElementById(dialogId); |
| 17 dialog.showModal(); |
| 18 } |
| 19 |
| 20 /** |
| 21 * Closes the score stats inspector dialog. |
| 22 * @param {String} dialogId: identifier of the dialog to close. |
| 23 */ |
| 24 function closeScoreStatsInspector(dialogId) { |
| 25 var dialog = document.getElementById(dialogId); |
| 26 dialog.close(); |
| 27 if (inspector != null) { |
| 28 inspector.stopAudio(); |
| 29 } |
| 30 } |
| 31 |
| 32 /** |
| 33 * Instance and initialize the audio inspector. |
| 34 */ |
| 35 function initialize() { |
| 36 inspector = new AudioInspector(); |
| 37 inspector.init(); |
| 38 } |
| 39 |
| 40 /** |
| 41 * Audio inspector class. |
11 * @constructor | 42 * @constructor |
12 */ | 43 */ |
13 function Inspector() { | 44 function AudioInspector() { |
14 this.audioPlayer_ = new Audio(); | 45 this.audioPlayer_ = new Audio(); |
15 this.inspectorNode_ = document.createElement('div'); | 46 this.metadata_ = {}; |
16 this.divTestDataGeneratorName_ = document.createElement('div'); | 47 this.currentScore_ = null; |
17 this.divTestDataGenParameters_ = document.createElement('div'); | 48 this.audioInspector_ = null; |
18 this.buttonPlayAudioIn_ = document.createElement('button'); | |
19 this.buttonPlayAudioOut_ = document.createElement('button'); | |
20 this.buttonPlayAudioRef_ = document.createElement('button'); | |
21 this.buttonStopAudio_ = document.createElement('button'); | |
22 | |
23 this.selectedItem_ = null; | |
24 this.audioInUrl_ = null; | |
25 this.audioOutUrl_ = null; | |
26 this.audioRefUrl_ = null; | |
27 } | 49 } |
28 | 50 |
29 /** | 51 /** |
30 * Initialize. | 52 * Initialize. |
31 */ | 53 */ |
32 Inspector.prototype.init = function() { | 54 AudioInspector.prototype.init = function() { |
33 window.event.stopPropagation(); | 55 window.event.stopPropagation(); |
34 | 56 this.createAudioInspector_(); |
35 // Create inspector UI. | 57 this.initializeEventHandlers_(); |
36 this.buildInspector_(); | 58 }; |
37 var body = document.getElementsByTagName('body')[0]; | 59 |
38 body.appendChild(this.inspectorNode_); | 60 /** |
39 | 61 * Set up the inspector for a new score. |
40 // Bind click handler. | 62 * @param {DOMElement} element: Element linked to the selected score. |
| 63 */ |
| 64 AudioInspector.prototype.selectedScoreChange = function(element) { |
| 65 if (this.currentScore_ == element) { return; } |
| 66 if (this.currentScore_ != null) { |
| 67 this.currentScore_.classList.remove('selected-score'); |
| 68 } |
| 69 this.currentScore_ = element; |
| 70 this.currentScore_.classList.add('selected-score'); |
| 71 this.stopAudio(); |
| 72 |
| 73 // Read metadata. |
| 74 var matches = element.querySelectorAll('input[type=hidden]'); |
| 75 this.metadata_ = {}; |
| 76 for (var index = 0; index < matches.length; ++index) { |
| 77 this.metadata_[matches[index].name] = matches[index].value; |
| 78 } |
| 79 |
| 80 // Show the audio inspector interface. |
| 81 var container = element.parentNode.parentNode.parentNode.parentNode; |
| 82 var audioInspectorPlaceholder = container.querySelector( |
| 83 '.audio-inspector-placeholder'); |
| 84 this.moveInspector_(audioInspectorPlaceholder); |
| 85 }; |
| 86 |
| 87 /** |
| 88 * Stop playing audio. |
| 89 */ |
| 90 AudioInspector.prototype.stopAudio = function() { |
| 91 this.audioPlayer_.pause(); |
| 92 }; |
| 93 |
| 94 /** |
| 95 * Move the audio inspector DOM node into the given parent. |
| 96 * @param {DOMElement} newParentNode: New parent for the inspector. |
| 97 */ |
| 98 AudioInspector.prototype.moveInspector_ = function(newParentNode) { |
| 99 newParentNode.appendChild(this.audioInspector_); |
| 100 }; |
| 101 |
| 102 /** |
| 103 * Play audio file from url. |
| 104 * @param {string} metadataFieldName: Metadata field name. |
| 105 */ |
| 106 AudioInspector.prototype.playAudio = function(metadataFieldName) { |
| 107 if (this.metadata_[metadataFieldName] == undefined) { return; } |
| 108 if (this.metadata_[metadataFieldName] == 'None') { |
| 109 alert('The selected stream was not used during the experiment.'); |
| 110 return; |
| 111 } |
| 112 this.stopAudio(); |
| 113 this.audioPlayer_.src = this.metadata_[metadataFieldName]; |
| 114 this.audioPlayer_.play(); |
| 115 }; |
| 116 |
| 117 /** |
| 118 * Initialize event handlers. |
| 119 */ |
| 120 AudioInspector.prototype.createAudioInspector_ = function() { |
| 121 var buttonIndex = 0; |
| 122 function getButtonHtml(icon, toolTipText, caption, metadataFieldName) { |
| 123 var buttonId = 'audioInspectorButton' + buttonIndex++; |
| 124 html = caption == null ? '' : caption; |
| 125 html += '<button class="mdl-button mdl-js-button mdl-button--icon ' + |
| 126 'mdl-js-ripple-effect" id="' + buttonId + '">' + |
| 127 '<i class="material-icons">' + icon + '</i>' + |
| 128 '<div class="mdl-tooltip" data-mdl-for="' + buttonId + '">' + |
| 129 toolTipText + |
| 130 '</div>'; |
| 131 if (metadataFieldName != null) { |
| 132 html += '<input type="hidden" value="' + metadataFieldName + '">' |
| 133 } |
| 134 html += '</button>' |
| 135 |
| 136 return html; |
| 137 } |
| 138 |
| 139 this.audioInspector_ = document.createElement('div'); |
| 140 this.audioInspector_.classList.add('audio-inspector'); |
| 141 this.audioInspector_.innerHTML = |
| 142 '<div class="mdl-grid">' + |
| 143 '<div class="mdl-layout-spacer"></div>' + |
| 144 '<div class="mdl-cell mdl-cell--2-col">' + |
| 145 getButtonHtml('play_arrow', 'Simulated echo', 'E<sub>in</sub>', |
| 146 'echo_filepath') + |
| 147 '</div>' + |
| 148 '<div class="mdl-cell mdl-cell--2-col">' + |
| 149 getButtonHtml('stop', 'Stop playing [S]', null, '__stop__') + |
| 150 '</div>' + |
| 151 '<div class="mdl-cell mdl-cell--2-col">' + |
| 152 getButtonHtml('play_arrow', 'Render stream', 'R<sub>in</sub>', |
| 153 'render_filepath') + |
| 154 '</div>' + |
| 155 '<div class="mdl-layout-spacer"></div>' + |
| 156 '</div>' + |
| 157 '<div class="mdl-grid">' + |
| 158 '<div class="mdl-layout-spacer"></div>' + |
| 159 '<div class="mdl-cell mdl-cell--2-col">' + |
| 160 getButtonHtml('play_arrow', 'Capture stream (APM input) [1]', |
| 161 'Y\'<sub>in</sub>', 'capture_filepath') + |
| 162 '</div>' + |
| 163 '<div class="mdl-cell mdl-cell--2-col"><strong>APM</strong></div>' + |
| 164 '<div class="mdl-cell mdl-cell--2-col">' + |
| 165 getButtonHtml('play_arrow', 'APM output [2]', 'Y<sub>out</sub>', |
| 166 'apm_output_filepath') + |
| 167 '</div>' + |
| 168 '<div class="mdl-layout-spacer"></div>' + |
| 169 '</div>' + |
| 170 '<div class="mdl-grid">' + |
| 171 '<div class="mdl-layout-spacer"></div>' + |
| 172 '<div class="mdl-cell mdl-cell--2-col">' + |
| 173 getButtonHtml('play_arrow', 'Echo-free capture stream', |
| 174 'Y<sub>in</sub>', 'echo_free_capture_filepath') + |
| 175 '</div>' + |
| 176 '<div class="mdl-cell mdl-cell--2-col">' + |
| 177 getButtonHtml('play_arrow', 'Clean capture stream', |
| 178 'Y<sub>clean</sub>', 'clean_capture_input_filepath') + |
| 179 '</div>' + |
| 180 '<div class="mdl-cell mdl-cell--2-col">' + |
| 181 getButtonHtml('play_arrow', 'APM reference [3]', 'Y<sub>ref</sub>', |
| 182 'apm_reference_filepath') + |
| 183 '</div>' + |
| 184 '<div class="mdl-layout-spacer"></div>' + |
| 185 '</div>'; |
| 186 |
| 187 // Add an invisible node as initial container for the audio inspector. |
| 188 var parent = document.createElement('div'); |
| 189 parent.style.display = 'none'; |
| 190 this.moveInspector_(parent); |
| 191 document.body.appendChild(parent); |
| 192 }; |
| 193 |
| 194 /** |
| 195 * Initialize event handlers. |
| 196 */ |
| 197 AudioInspector.prototype.initializeEventHandlers_ = function() { |
41 var self = this; | 198 var self = this; |
42 var items = document.getElementsByClassName('score'); | 199 |
43 for (var index = 0; index < items.length; index++) { | 200 // Score cells. |
44 items[index].onclick = function() { | 201 document.querySelectorAll('td.single-score-cell').forEach(function(element) { |
45 self.openInspector(this); | 202 element.onclick = function() { |
| 203 self.selectedScoreChange(this); |
| 204 } |
| 205 }); |
| 206 |
| 207 // Audio inspector buttons. |
| 208 this.audioInspector_.querySelectorAll('button').forEach(function(element) { |
| 209 var target = element.querySelector('input[type=hidden]'); |
| 210 if (target == null) { return; } |
| 211 element.onclick = function() { |
| 212 if (target.value == '__stop__') { |
| 213 self.stopAudio(); |
| 214 } else { |
| 215 self.playAudio(target.value); |
| 216 } |
46 }; | 217 }; |
47 } | 218 }); |
48 | 219 |
49 // Bind pressed key handlers. | 220 // Keyboard shortcuts. |
50 var self = this; | |
51 window.onkeyup = function(e) { | 221 window.onkeyup = function(e) { |
52 var key = e.keyCode ? e.keyCode : e.which; | 222 var key = e.keyCode ? e.keyCode : e.which; |
53 switch (key) { | 223 switch (key) { |
54 case 49: // 1. | 224 case 49: // 1. |
55 self.playAudioIn(); | 225 self.playAudio('capture_filepath'); |
56 break; | 226 break; |
57 case 50: // 2. | 227 case 50: // 2. |
58 self.playAudioOut(); | 228 self.playAudio('apm_output_filepath'); |
59 break; | 229 break; |
60 case 51: // 3. | 230 case 51: // 3. |
61 self.playAudioRef(); | 231 self.playAudio('apm_reference_filepath'); |
62 break; | 232 break; |
63 case 83: // S. | 233 case 83: // S. |
64 case 115: // s. | 234 case 115: // s. |
65 self.stopAudio(); | 235 self.stopAudio(); |
66 break; | 236 break; |
67 } | 237 } |
68 }; | 238 }; |
69 }; | 239 }; |
70 | |
71 /** | |
72 * Open the inspector. | |
73 * @param {DOMElement} target: score element that has been clicked. | |
74 */ | |
75 Inspector.prototype.openInspector = function(target) { | |
76 if (this.selectedItem_ != null) { | |
77 this.selectedItem_.classList.remove('selected'); | |
78 } | |
79 this.selectedItem_ = target; | |
80 this.selectedItem_.classList.add('selected'); | |
81 | |
82 var target = this.selectedItem_.querySelector('.test-data-gen-desc'); | |
83 var testDataGenName = target.querySelector('input[name=gen_name]').value; | |
84 var testDataGenParams = target.querySelector('input[name=gen_params]').value; | |
85 var audioIn = target.querySelector('input[name=audio_in]').value; | |
86 var audioOut = target.querySelector('input[name=audio_out]').value; | |
87 var audioRef = target.querySelector('input[name=audio_ref]').value; | |
88 | |
89 this.divTestDataGeneratorName_.innerHTML = testDataGenName; | |
90 this.divTestDataGenParameters_.innerHTML = testDataGenParams; | |
91 | |
92 this.audioInUrl_ = audioIn; | |
93 this.audioOutUrl_ = audioOut; | |
94 this.audioRefUrl_ = audioRef; | |
95 }; | |
96 | |
97 /** | |
98 * Play APM audio input signal. | |
99 */ | |
100 Inspector.prototype.playAudioIn = function() { | |
101 this.play_(this.audioInUrl_); | |
102 }; | |
103 | |
104 /** | |
105 * Play APM audio output signal. | |
106 */ | |
107 Inspector.prototype.playAudioOut = function() { | |
108 this.play_(this.audioOutUrl_); | |
109 }; | |
110 | |
111 /** | |
112 * Play APM audio reference signal. | |
113 */ | |
114 Inspector.prototype.playAudioRef = function() { | |
115 this.play_(this.audioRefUrl_); | |
116 }; | |
117 | |
118 /** | |
119 * Stop playing audio. | |
120 */ | |
121 Inspector.prototype.stopAudio = function() { | |
122 this.audioPlayer_.pause(); | |
123 }; | |
124 | |
125 /** | |
126 * Play audio file from url. | |
127 * @param {string} url | |
128 */ | |
129 Inspector.prototype.play_ = function(url) { | |
130 if (url == null) { | |
131 alert('Select a score first.'); | |
132 return; | |
133 } | |
134 | |
135 this.audioPlayer_.src = url; | |
136 this.audioPlayer_.play(); | |
137 }; | |
138 | |
139 /** | |
140 * Build inspector. | |
141 */ | |
142 Inspector.prototype.buildInspector_ = function() { | |
143 var self = this; | |
144 | |
145 this.inspectorNode_.setAttribute('class', 'inspector'); | |
146 this.inspectorNode_.innerHTML = | |
147 '<div class="property test-data-gen-name">' + | |
148 '<div class="name">test data generator</div>' + | |
149 '</div>' + | |
150 '<div class="property test-data-gen-parmas">' + | |
151 '<div class="name">parameters</div>' + | |
152 '</div>' + | |
153 '<div class="buttons"></div>'; | |
154 | |
155 // Add value nodes. | |
156 function addValueNode(node, parent_selector) { | |
157 node.setAttribute('class', 'value'); | |
158 node.innerHTML = '-'; | |
159 var parentNode = self.inspectorNode_.querySelector(parent_selector); | |
160 parentNode.appendChild(node); | |
161 } | |
162 addValueNode(this.divTestDataGeneratorName_, 'div.test-data-gen-name'); | |
163 addValueNode(this.divTestDataGenParameters_, 'div.test-data-gen-parmas'); | |
164 | |
165 // Add buttons. | |
166 var buttonsNode = this.inspectorNode_.querySelector('div.buttons'); | |
167 function addButton(node, caption, callback) { | |
168 node.innerHTML = caption; | |
169 buttonsNode.appendChild(node); | |
170 node.onclick = callback.bind(self); | |
171 } | |
172 addButton(this.buttonPlayAudioIn_, 'A_in (<strong>1</strong>)', | |
173 this.playAudioIn); | |
174 addButton(this.buttonPlayAudioOut_, 'A_out (<strong>2</strong>)', | |
175 this.playAudioOut); | |
176 addButton(this.buttonPlayAudioRef_, 'A_ref (<strong>3</strong>)', | |
177 this.playAudioRef); | |
178 addButton(this.buttonStopAudio_, '<strong>S</strong>top', this.stopAudio); | |
179 }; | |
180 | |
181 /** | |
182 * Instance and initialize the inspector. | |
183 */ | |
184 function initialize() { | |
185 var inspector = new Inspector(); | |
186 inspector.init(); | |
187 } | |
OLD | NEW |