├── .gitignore ├── LICENSE ├── README.md ├── binding.gyp ├── demo.js ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── src └── volume_controller.cc /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .vscode 64 | 65 | *.obj 66 | 67 | *.exe 68 | 69 | build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jarno Lonardi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Native Node.js volume controls for Windows 2 | 3 | A native Windows API binding to control the default audio device. Supports set/get the volume and get the muted state and set the muted state. 4 | 5 | [![npm](https://img.shields.io/npm/dm/node-audio-windows.svg)](https://www.npmjs.com/package/node-audio-windows) 6 | [![npm](https://img.shields.io/npm/v/node-audio-windows.svg)](https://www.npmjs.com/package/node-audio-windows) 7 | 8 | ## How to Use 9 | ```javascript 10 | const { volume } = require('node-audio-windows'); 11 | 12 | // the functions the volume controller exposes 13 | const { getVolume, setVolume, isMuted, setMute } = volume; 14 | 15 | // to get the system volume 16 | const currentVolume = getVolume(); 17 | 18 | // to set the volume to 43. 19 | setVolume(0.43); 20 | 21 | // to know if the system is muted 22 | isMuted(); 23 | 24 | // to mute 25 | setMute(true); 26 | 27 | // to unmute 28 | setMute(false); 29 | 30 | // to toggle mute 31 | setMute(!isMuted()); 32 | ``` 33 | #### Note 34 | Windows displays the audio at the scale from 0-100, but the library uses instead the scale 0.0 - 1.0 to match the scale Windows API actually uses. 35 | 36 | ## Development 37 | To build the project you need in Windows to install [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) in an elevated PowerShell prompt `npm install --global --production windows-build-tools` and then `npm install` or if you have `node-gyp` installed globally 38 | ```bash 39 | $ node-gyp configure 40 | $ node-gyp build 41 | ``` 42 | The repository contains a demo of the usage 43 | ```bash 44 | $ node demo.js 45 | ``` 46 | 47 | 48 | ## Next steps 49 | - Expose the microphone API 50 | - Support controlling multiple audio devices 51 | 52 | ## Licence & Copyright 53 | Copyright (c) 2018 Jarno Lonardi 54 | The package `node-audio-windows` is licensed under MIT license. -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "volume_controller", 5 | "sources": [ "src/volume_controller.cc" ], 6 | "libraries": [ "ole32.lib"], 7 | "include_dirs": [ 8 | " { 20 | switch (key.name) { 21 | case 'escape': 22 | process.exit(); 23 | case 'right': 24 | case 'up': 25 | volumeControl.setVolume(Math.min(1.0, volumeControl.getVolume() + 0.01)); 26 | break; 27 | case 'down': 28 | case 'left': 29 | volumeControl.setVolume(Math.max(0.0, volumeControl.getVolume() - 0.01)); 30 | break; 31 | case 'm': { 32 | volumeControl.setMuted(!volumeControl.isMuted()); 33 | break; 34 | } 35 | } 36 | drawBar(volumeControl.getVolume()); 37 | }); 38 | 39 | 40 | const getBar = (length, char) => { 41 | let str = ''; 42 | for (let i = 0; i < length; i += 1) { 43 | str += char; 44 | } 45 | return str; 46 | }; 47 | 48 | const drawBar = (current) => { 49 | const barLength = 50; 50 | const percentageProgress = (current * 100).toFixed(0); 51 | this.currentProgress = percentageProgress; 52 | 53 | const filledBarLength = volumeControl.isMuted() ? 0 : (current * barLength).toFixed(0); 54 | 55 | 56 | const emptyBarLength = barLength - filledBarLength; 57 | 58 | const filledBar = bgWhite(getBar(filledBarLength, ' ')); 59 | const emptyBar = getBar(emptyBarLength, '·'); 60 | const title = 'Volume: '; 61 | let emoticon = '\u{1F50A}'; 62 | if (percentageProgress < 40) { 63 | emoticon = '\u{1F509}'; 64 | } 65 | if (percentageProgress < 10) { 66 | emoticon = '\u{1F508}'; 67 | } 68 | if (volumeControl.isMuted()) { 69 | emoticon = '\u{1F507}'; 70 | } 71 | 72 | process.stdout.clearLine(); 73 | process.stdout.cursorTo(0); 74 | process.stdout.write(`${title} ${emoticon} [${filledBar}${emptyBar}] | ${percentageProgress}% `); 75 | } 76 | 77 | drawBar(volumeControl.getVolume()); -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export class VolumeControl { 2 | getVolume(): number; 3 | setVolume(volume: number): void; 4 | isMuted(): boolean; 5 | setMuted(muted: boolean); 6 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/Release/volume_controller.node'); 2 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-audio-windows", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 11 | }, 12 | "ajv": { 13 | "version": "5.5.2", 14 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 15 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 16 | "requires": { 17 | "co": "4.6.0", 18 | "fast-deep-equal": "1.1.0", 19 | "fast-json-stable-stringify": "2.0.0", 20 | "json-schema-traverse": "0.3.1" 21 | } 22 | }, 23 | "ansi-regex": { 24 | "version": "2.1.1", 25 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 26 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 27 | }, 28 | "ansi-styles": { 29 | "version": "3.2.1", 30 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 31 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 32 | "dev": true, 33 | "requires": { 34 | "color-convert": "1.9.3" 35 | } 36 | }, 37 | "aproba": { 38 | "version": "1.2.0", 39 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 40 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 41 | }, 42 | "are-we-there-yet": { 43 | "version": "1.1.5", 44 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 45 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 46 | "requires": { 47 | "delegates": "1.0.0", 48 | "readable-stream": "2.3.6" 49 | } 50 | }, 51 | "asn1": { 52 | "version": "0.2.4", 53 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 54 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 55 | "requires": { 56 | "safer-buffer": "2.1.2" 57 | } 58 | }, 59 | "assert-plus": { 60 | "version": "1.0.0", 61 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 62 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 63 | }, 64 | "asynckit": { 65 | "version": "0.4.0", 66 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 67 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 68 | }, 69 | "aws-sign2": { 70 | "version": "0.7.0", 71 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 72 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 73 | }, 74 | "aws4": { 75 | "version": "1.8.0", 76 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 77 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 78 | }, 79 | "balanced-match": { 80 | "version": "1.0.0", 81 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 82 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 83 | }, 84 | "bcrypt-pbkdf": { 85 | "version": "1.0.2", 86 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 87 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 88 | "requires": { 89 | "tweetnacl": "0.14.5" 90 | } 91 | }, 92 | "block-stream": { 93 | "version": "0.0.9", 94 | "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", 95 | "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", 96 | "requires": { 97 | "inherits": "2.0.3" 98 | } 99 | }, 100 | "brace-expansion": { 101 | "version": "1.1.11", 102 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 103 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 104 | "requires": { 105 | "balanced-match": "1.0.0", 106 | "concat-map": "0.0.1" 107 | } 108 | }, 109 | "caseless": { 110 | "version": "0.12.0", 111 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 112 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 113 | }, 114 | "chalk": { 115 | "version": "2.4.1", 116 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 117 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 118 | "dev": true, 119 | "requires": { 120 | "ansi-styles": "3.2.1", 121 | "escape-string-regexp": "1.0.5", 122 | "supports-color": "5.5.0" 123 | } 124 | }, 125 | "co": { 126 | "version": "4.6.0", 127 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 128 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 129 | }, 130 | "code-point-at": { 131 | "version": "1.1.0", 132 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 133 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 134 | }, 135 | "color-convert": { 136 | "version": "1.9.3", 137 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 138 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 139 | "dev": true, 140 | "requires": { 141 | "color-name": "1.1.3" 142 | } 143 | }, 144 | "color-name": { 145 | "version": "1.1.3", 146 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 147 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 148 | "dev": true 149 | }, 150 | "combined-stream": { 151 | "version": "1.0.7", 152 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 153 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 154 | "requires": { 155 | "delayed-stream": "1.0.0" 156 | } 157 | }, 158 | "concat-map": { 159 | "version": "0.0.1", 160 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 161 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 162 | }, 163 | "console-control-strings": { 164 | "version": "1.1.0", 165 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 166 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 167 | }, 168 | "core-util-is": { 169 | "version": "1.0.2", 170 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 171 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 172 | }, 173 | "dashdash": { 174 | "version": "1.14.1", 175 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 176 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 177 | "requires": { 178 | "assert-plus": "1.0.0" 179 | } 180 | }, 181 | "delayed-stream": { 182 | "version": "1.0.0", 183 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 184 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 185 | }, 186 | "delegates": { 187 | "version": "1.0.0", 188 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 189 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 190 | }, 191 | "ecc-jsbn": { 192 | "version": "0.1.2", 193 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 194 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 195 | "requires": { 196 | "jsbn": "0.1.1", 197 | "safer-buffer": "2.1.2" 198 | } 199 | }, 200 | "escape-string-regexp": { 201 | "version": "1.0.5", 202 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 203 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 204 | "dev": true 205 | }, 206 | "extend": { 207 | "version": "3.0.2", 208 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 209 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 210 | }, 211 | "extsprintf": { 212 | "version": "1.3.0", 213 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 214 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 215 | }, 216 | "fast-deep-equal": { 217 | "version": "1.1.0", 218 | "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 219 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 220 | }, 221 | "fast-json-stable-stringify": { 222 | "version": "2.0.0", 223 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 224 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 225 | }, 226 | "forever-agent": { 227 | "version": "0.6.1", 228 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 229 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 230 | }, 231 | "form-data": { 232 | "version": "2.3.3", 233 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 234 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 235 | "requires": { 236 | "asynckit": "0.4.0", 237 | "combined-stream": "1.0.7", 238 | "mime-types": "2.1.21" 239 | } 240 | }, 241 | "fs.realpath": { 242 | "version": "1.0.0", 243 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 244 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 245 | }, 246 | "fstream": { 247 | "version": "1.0.12", 248 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", 249 | "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", 250 | "requires": { 251 | "graceful-fs": "4.1.11", 252 | "inherits": "2.0.3", 253 | "mkdirp": "0.5.1", 254 | "rimraf": "2.6.2" 255 | } 256 | }, 257 | "gauge": { 258 | "version": "2.7.4", 259 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 260 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 261 | "requires": { 262 | "aproba": "1.2.0", 263 | "console-control-strings": "1.1.0", 264 | "has-unicode": "2.0.1", 265 | "object-assign": "4.1.1", 266 | "signal-exit": "3.0.2", 267 | "string-width": "1.0.2", 268 | "strip-ansi": "3.0.1", 269 | "wide-align": "1.1.3" 270 | } 271 | }, 272 | "getpass": { 273 | "version": "0.1.7", 274 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 275 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 276 | "requires": { 277 | "assert-plus": "1.0.0" 278 | } 279 | }, 280 | "glob": { 281 | "version": "7.1.3", 282 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 283 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 284 | "requires": { 285 | "fs.realpath": "1.0.0", 286 | "inflight": "1.0.6", 287 | "inherits": "2.0.3", 288 | "minimatch": "3.0.4", 289 | "once": "1.4.0", 290 | "path-is-absolute": "1.0.1" 291 | } 292 | }, 293 | "graceful-fs": { 294 | "version": "4.1.11", 295 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 296 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 297 | }, 298 | "har-schema": { 299 | "version": "2.0.0", 300 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 301 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 302 | }, 303 | "har-validator": { 304 | "version": "5.1.0", 305 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", 306 | "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", 307 | "requires": { 308 | "ajv": "5.5.2", 309 | "har-schema": "2.0.0" 310 | } 311 | }, 312 | "has-flag": { 313 | "version": "3.0.0", 314 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 315 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 316 | "dev": true 317 | }, 318 | "has-unicode": { 319 | "version": "2.0.1", 320 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 321 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 322 | }, 323 | "http-signature": { 324 | "version": "1.2.0", 325 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 326 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 327 | "requires": { 328 | "assert-plus": "1.0.0", 329 | "jsprim": "1.4.1", 330 | "sshpk": "1.15.1" 331 | } 332 | }, 333 | "inflight": { 334 | "version": "1.0.6", 335 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 336 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 337 | "requires": { 338 | "once": "1.4.0", 339 | "wrappy": "1.0.2" 340 | } 341 | }, 342 | "inherits": { 343 | "version": "2.0.3", 344 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 345 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 346 | }, 347 | "is-fullwidth-code-point": { 348 | "version": "1.0.0", 349 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 350 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 351 | "requires": { 352 | "number-is-nan": "1.0.1" 353 | } 354 | }, 355 | "is-typedarray": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 358 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 359 | }, 360 | "isarray": { 361 | "version": "1.0.0", 362 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 363 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 364 | }, 365 | "isexe": { 366 | "version": "2.0.0", 367 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 368 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 369 | }, 370 | "isstream": { 371 | "version": "0.1.2", 372 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 373 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 374 | }, 375 | "jsbn": { 376 | "version": "0.1.1", 377 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 378 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 379 | }, 380 | "json-schema": { 381 | "version": "0.2.3", 382 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 383 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 384 | }, 385 | "json-schema-traverse": { 386 | "version": "0.3.1", 387 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 388 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 389 | }, 390 | "json-stringify-safe": { 391 | "version": "5.0.1", 392 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 393 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 394 | }, 395 | "jsprim": { 396 | "version": "1.4.1", 397 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 398 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 399 | "requires": { 400 | "assert-plus": "1.0.0", 401 | "extsprintf": "1.3.0", 402 | "json-schema": "0.2.3", 403 | "verror": "1.10.0" 404 | } 405 | }, 406 | "mime-db": { 407 | "version": "1.37.0", 408 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 409 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 410 | }, 411 | "mime-types": { 412 | "version": "2.1.21", 413 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 414 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 415 | "requires": { 416 | "mime-db": "1.37.0" 417 | } 418 | }, 419 | "minimatch": { 420 | "version": "3.0.4", 421 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 422 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 423 | "requires": { 424 | "brace-expansion": "1.1.11" 425 | } 426 | }, 427 | "minimist": { 428 | "version": "0.0.8", 429 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 430 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 431 | }, 432 | "mkdirp": { 433 | "version": "0.5.1", 434 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 435 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 436 | "requires": { 437 | "minimist": "0.0.8" 438 | } 439 | }, 440 | "nan": { 441 | "version": "2.11.1", 442 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", 443 | "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==" 444 | }, 445 | "node-gyp": { 446 | "version": "3.8.0", 447 | "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", 448 | "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", 449 | "requires": { 450 | "fstream": "1.0.12", 451 | "glob": "7.1.3", 452 | "graceful-fs": "4.1.11", 453 | "mkdirp": "0.5.1", 454 | "nopt": "3.0.6", 455 | "npmlog": "4.1.2", 456 | "osenv": "0.1.5", 457 | "request": "2.88.0", 458 | "rimraf": "2.6.2", 459 | "semver": "5.3.0", 460 | "tar": "2.2.2", 461 | "which": "1.3.1" 462 | } 463 | }, 464 | "nopt": { 465 | "version": "3.0.6", 466 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 467 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 468 | "requires": { 469 | "abbrev": "1.1.1" 470 | } 471 | }, 472 | "npmlog": { 473 | "version": "4.1.2", 474 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 475 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 476 | "requires": { 477 | "are-we-there-yet": "1.1.5", 478 | "console-control-strings": "1.1.0", 479 | "gauge": "2.7.4", 480 | "set-blocking": "2.0.0" 481 | } 482 | }, 483 | "number-is-nan": { 484 | "version": "1.0.1", 485 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 486 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 487 | }, 488 | "oauth-sign": { 489 | "version": "0.9.0", 490 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 491 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 492 | }, 493 | "object-assign": { 494 | "version": "4.1.1", 495 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 496 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 497 | }, 498 | "once": { 499 | "version": "1.4.0", 500 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 501 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 502 | "requires": { 503 | "wrappy": "1.0.2" 504 | } 505 | }, 506 | "os-homedir": { 507 | "version": "1.0.2", 508 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 509 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 510 | }, 511 | "os-tmpdir": { 512 | "version": "1.0.2", 513 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 514 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 515 | }, 516 | "osenv": { 517 | "version": "0.1.5", 518 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 519 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 520 | "requires": { 521 | "os-homedir": "1.0.2", 522 | "os-tmpdir": "1.0.2" 523 | } 524 | }, 525 | "path-is-absolute": { 526 | "version": "1.0.1", 527 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 528 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 529 | }, 530 | "performance-now": { 531 | "version": "2.1.0", 532 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 533 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 534 | }, 535 | "process-nextick-args": { 536 | "version": "2.0.0", 537 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 538 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 539 | }, 540 | "psl": { 541 | "version": "1.1.29", 542 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 543 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 544 | }, 545 | "punycode": { 546 | "version": "1.4.1", 547 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 548 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 549 | }, 550 | "qs": { 551 | "version": "6.5.2", 552 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 553 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 554 | }, 555 | "readable-stream": { 556 | "version": "2.3.6", 557 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 558 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 559 | "requires": { 560 | "core-util-is": "1.0.2", 561 | "inherits": "2.0.3", 562 | "isarray": "1.0.0", 563 | "process-nextick-args": "2.0.0", 564 | "safe-buffer": "5.1.2", 565 | "string_decoder": "1.1.1", 566 | "util-deprecate": "1.0.2" 567 | } 568 | }, 569 | "request": { 570 | "version": "2.88.0", 571 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 572 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 573 | "requires": { 574 | "aws-sign2": "0.7.0", 575 | "aws4": "1.8.0", 576 | "caseless": "0.12.0", 577 | "combined-stream": "1.0.7", 578 | "extend": "3.0.2", 579 | "forever-agent": "0.6.1", 580 | "form-data": "2.3.3", 581 | "har-validator": "5.1.0", 582 | "http-signature": "1.2.0", 583 | "is-typedarray": "1.0.0", 584 | "isstream": "0.1.2", 585 | "json-stringify-safe": "5.0.1", 586 | "mime-types": "2.1.21", 587 | "oauth-sign": "0.9.0", 588 | "performance-now": "2.1.0", 589 | "qs": "6.5.2", 590 | "safe-buffer": "5.1.2", 591 | "tough-cookie": "2.4.3", 592 | "tunnel-agent": "0.6.0", 593 | "uuid": "3.3.2" 594 | } 595 | }, 596 | "rimraf": { 597 | "version": "2.6.2", 598 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 599 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 600 | "requires": { 601 | "glob": "7.1.3" 602 | } 603 | }, 604 | "safe-buffer": { 605 | "version": "5.1.2", 606 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 607 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 608 | }, 609 | "safer-buffer": { 610 | "version": "2.1.2", 611 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 612 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 613 | }, 614 | "semver": { 615 | "version": "5.3.0", 616 | "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", 617 | "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" 618 | }, 619 | "set-blocking": { 620 | "version": "2.0.0", 621 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 622 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 623 | }, 624 | "signal-exit": { 625 | "version": "3.0.2", 626 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 627 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 628 | }, 629 | "sshpk": { 630 | "version": "1.15.1", 631 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz", 632 | "integrity": "sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA==", 633 | "requires": { 634 | "asn1": "0.2.4", 635 | "assert-plus": "1.0.0", 636 | "bcrypt-pbkdf": "1.0.2", 637 | "dashdash": "1.14.1", 638 | "ecc-jsbn": "0.1.2", 639 | "getpass": "0.1.7", 640 | "jsbn": "0.1.1", 641 | "safer-buffer": "2.1.2", 642 | "tweetnacl": "0.14.5" 643 | } 644 | }, 645 | "string-width": { 646 | "version": "1.0.2", 647 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 648 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 649 | "requires": { 650 | "code-point-at": "1.1.0", 651 | "is-fullwidth-code-point": "1.0.0", 652 | "strip-ansi": "3.0.1" 653 | } 654 | }, 655 | "string_decoder": { 656 | "version": "1.1.1", 657 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 658 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 659 | "requires": { 660 | "safe-buffer": "5.1.2" 661 | } 662 | }, 663 | "strip-ansi": { 664 | "version": "3.0.1", 665 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 666 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 667 | "requires": { 668 | "ansi-regex": "2.1.1" 669 | } 670 | }, 671 | "supports-color": { 672 | "version": "5.5.0", 673 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 674 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 675 | "dev": true, 676 | "requires": { 677 | "has-flag": "3.0.0" 678 | } 679 | }, 680 | "tar": { 681 | "version": "2.2.2", 682 | "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", 683 | "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", 684 | "requires": { 685 | "block-stream": "0.0.9", 686 | "fstream": "1.0.12", 687 | "inherits": "2.0.3" 688 | } 689 | }, 690 | "tough-cookie": { 691 | "version": "2.4.3", 692 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 693 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 694 | "requires": { 695 | "psl": "1.1.29", 696 | "punycode": "1.4.1" 697 | } 698 | }, 699 | "tunnel-agent": { 700 | "version": "0.6.0", 701 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 702 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 703 | "requires": { 704 | "safe-buffer": "5.1.2" 705 | } 706 | }, 707 | "tweetnacl": { 708 | "version": "0.14.5", 709 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 710 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 711 | }, 712 | "util-deprecate": { 713 | "version": "1.0.2", 714 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 715 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 716 | }, 717 | "uuid": { 718 | "version": "3.3.2", 719 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 720 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 721 | }, 722 | "verror": { 723 | "version": "1.10.0", 724 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 725 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 726 | "requires": { 727 | "assert-plus": "1.0.0", 728 | "core-util-is": "1.0.2", 729 | "extsprintf": "1.3.0" 730 | } 731 | }, 732 | "which": { 733 | "version": "1.3.1", 734 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 735 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 736 | "requires": { 737 | "isexe": "2.0.0" 738 | } 739 | }, 740 | "wide-align": { 741 | "version": "1.1.3", 742 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 743 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 744 | "requires": { 745 | "string-width": "1.0.2" 746 | } 747 | }, 748 | "wrappy": { 749 | "version": "1.0.2", 750 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 751 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 752 | } 753 | } 754 | } 755 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-audio-windows", 3 | "version": "1.0.1", 4 | "description": "Native Node.js audio controls for Windows", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "os": [ 8 | "win32" 9 | ], 10 | "gypfile": true, 11 | "scripts": { 12 | "install": "node-gyp rebuild" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/jlonardi/node-audio-windows.git" 17 | }, 18 | "keywords": [ 19 | "node", 20 | "windows", 21 | "volume", 22 | "audio" 23 | ], 24 | "author": "Jarno Lonardi", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/jlonardi/node-audio-windows/issues" 28 | }, 29 | "homepage": "https://github.com/jlonardi/node-audio-windows#readme", 30 | "dependencies": { 31 | "nan": "^2.11.1", 32 | "node-gyp": "^3.8.0" 33 | }, 34 | "devDependencies": { 35 | "chalk": "^2.4.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/volume_controller.cc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _WINSOCKAPI_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using Microsoft::WRL::ComPtr; 12 | 13 | template 14 | std::string string_format(std::string format, Args ... args) { 15 | size_t size = std::snprintf( nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0' 16 | std::unique_ptr buf( new char[ size ] ); 17 | std::snprintf( buf.get(), size, format.c_str(), args ...); 18 | return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside 19 | } 20 | 21 | void checkErrors(HRESULT hr, std::string error_message) { 22 | if (FAILED(hr)) { 23 | throw string_format("%s (0x%X)", error_message.c_str() , hr); 24 | } 25 | } 26 | 27 | class VolumeControl { 28 | private: 29 | ComPtr device; 30 | 31 | public: 32 | 33 | VolumeControl() { 34 | // The IMMDeviceEnumerator interface provides methods for enumerating multimedia device resources. Basically the interface of the requested object. 35 | ComPtr deviceEnumerator; 36 | 37 | // Creates a single uninitialized object of the class associated with a specified CLSID 38 | checkErrors( 39 | CoCreateInstance( 40 | __uuidof(MMDeviceEnumerator), // Requested COM device enumerator id 41 | NULL, // If NULL, indicates that the object is not being created as part of an aggregate. 42 | CLSCTX_INPROC_SERVER, // Context in which the code that manages the newly created object will run (Same process). 43 | IID_PPV_ARGS(&deviceEnumerator) // Address of pointer variable that receives the interface pointer requested in riid. 44 | ), 45 | "Error when trying to get a handle to MMDeviceEnumerator device enumerator" 46 | ); 47 | 48 | // Device interface pointer where we will dig the audio device endpoint 49 | ComPtr defaultDevice; 50 | 51 | checkErrors( 52 | deviceEnumerator->GetDefaultAudioEndpoint( 53 | eRender, // Audio rendering stream. Audio data flows from the application to the audio endpoint device, which renders the stream. eCapture would be the opposite 54 | eConsole, // The role that the system has assigned to an audio endpoint device. eConsole for games, system notification sounds, and voice commands 55 | &defaultDevice // Pointer to default audio enpoint device 56 | ), 57 | "Error when trying to get a handle to the default audio enpoint" 58 | ); 59 | 60 | checkErrors( 61 | defaultDevice->Activate( // Creates a COM object with the specified interface. 62 | __uuidof(IAudioEndpointVolume), // Reference to a GUID that identifies the interface that the caller requests be activated 63 | CLSCTX_INPROC_SERVER, // Context in which the code that manages the newly created object will run (Same process). 64 | NULL, // Set NULL to activate the IAudioEndpointVolume endpoint https://msdn.microsoft.com/en-us/library/ms679029.aspx 65 | &device // Pointer to a pointer variable into which the method writes the address of the interface specified by parameter iid. Through this method, the caller obtains a counted reference to the interface. 66 | ), 67 | "Error when trying to get a handle to the volume endpoint" 68 | ); 69 | } 70 | 71 | BOOL isMuted() { 72 | BOOL muted = false; 73 | 74 | checkErrors(device->GetMute(&muted), "getting muted state"); 75 | 76 | return muted; 77 | } 78 | 79 | void setMuted(BOOL muted) { 80 | checkErrors(device->SetMute(muted, NULL), "setting mute"); 81 | } 82 | 83 | float getVolume() { 84 | float currentVolume = 0; 85 | 86 | checkErrors( 87 | device->GetMasterVolumeLevelScalar(¤tVolume), 88 | "getting volume" 89 | ); 90 | 91 | return currentVolume; 92 | } 93 | 94 | void setVolume(float volume) { 95 | if (volume < 0.0 || volume > 1.0) { 96 | throw std::string("Volume needs to be between 0.0 and 1.0 inclusive"); 97 | } 98 | 99 | checkErrors( 100 | device->SetMasterVolumeLevelScalar(volume, NULL), 101 | "setting volume" 102 | ); 103 | 104 | } 105 | }; 106 | 107 | class VolumeControlWrapper : public Nan::ObjectWrap { 108 | public: 109 | static NAN_MODULE_INIT(Init) { 110 | auto tpl = Nan::New(New); 111 | tpl->SetClassName(Nan::New("VolumeControl").ToLocalChecked()); 112 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 113 | 114 | Nan::SetPrototypeMethod(tpl, "getVolume", GetVolume); 115 | Nan::SetPrototypeMethod(tpl, "setVolume", SetVolume); 116 | Nan::SetPrototypeMethod(tpl, "isMuted", IsMuted); 117 | Nan::SetPrototypeMethod(tpl, "setMuted", SetMuted); 118 | 119 | constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked()); 120 | Nan::Set(target, Nan::New("VolumeControl").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); 121 | } 122 | 123 | private: 124 | VolumeControl device; 125 | 126 | static NAN_METHOD(New) { 127 | if (info.IsConstructCall()) { 128 | std::cout << "Constructing new object" << std::endl; 129 | try { 130 | auto obj = new VolumeControlWrapper(); 131 | obj->Wrap(info.This()); 132 | info.GetReturnValue().Set(info.This()); 133 | std::cout << "Constructed new object" << std::endl; 134 | } catch (std::string e) { 135 | return Nan::ThrowError(Nan::New(e).ToLocalChecked()); 136 | } 137 | } else { 138 | return Nan::ThrowError(Nan::New("The constructor cannot be called as a function.").ToLocalChecked()); 139 | } 140 | } 141 | 142 | static NAN_METHOD(GetVolume) { 143 | auto obj = Nan::ObjectWrap::Unwrap(info.Holder()); 144 | try { 145 | info.GetReturnValue().Set(obj->device.getVolume()); 146 | } catch (std::string e) { 147 | return Nan::ThrowError(Nan::New(e).ToLocalChecked()); 148 | } 149 | } 150 | 151 | static NAN_METHOD(SetVolume) { 152 | if (info.Length() != 1) { 153 | return Nan::ThrowError(Nan::New("Exactly one number parameter is required.").ToLocalChecked()); 154 | } 155 | 156 | float volume = (float)info[0]->NumberValue(); 157 | auto obj = Nan::ObjectWrap::Unwrap(info.Holder()); 158 | try { 159 | obj->device.setVolume(volume); 160 | } catch (std::string e) { 161 | return Nan::ThrowError(Nan::New(e).ToLocalChecked()); 162 | } 163 | } 164 | 165 | static NAN_METHOD(IsMuted) { 166 | auto obj = Nan::ObjectWrap::Unwrap(info.Holder()); 167 | try { 168 | info.GetReturnValue().Set(obj->device.isMuted()); 169 | } catch (std::string e) { 170 | return Nan::ThrowError(Nan::New(e).ToLocalChecked()); 171 | } 172 | } 173 | 174 | static NAN_METHOD(SetMuted) { 175 | if (info.Length() != 1) { 176 | return Nan::ThrowError(Nan::New("Exactly one boolean parameter is required.").ToLocalChecked()); 177 | } 178 | 179 | bool muted = info[0]->BooleanValue(); 180 | auto obj = Nan::ObjectWrap::Unwrap(info.Holder()); 181 | try { 182 | obj->device.setMuted(muted); 183 | } catch (std::string e) { 184 | return Nan::ThrowError(Nan::New(e).ToLocalChecked()); 185 | } 186 | } 187 | 188 | static inline Nan::Persistent & constructor() { 189 | static Nan::Persistent constructorFunction; 190 | return constructorFunction; 191 | } 192 | }; 193 | 194 | void UnInitialize(void*) { 195 | CoUninitialize(); 196 | } 197 | 198 | NAN_MODULE_INIT(InitModule) { 199 | CoInitialize(NULL); 200 | 201 | VolumeControlWrapper::Init(target); 202 | 203 | node::AtExit(UnInitialize); 204 | } 205 | 206 | NODE_MODULE(addon, InitModule) 207 | --------------------------------------------------------------------------------