├── .gitignore ├── README.md ├── assets └── style.css ├── webpack.config.babel.js ├── dist ├── index.js ├── index.umd.min.js ├── index.umd.js ├── index.umd.js.map └── index.umd.min.js.map ├── src └── index.js ├── index.html └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Play RTTTL files online 2 | 3 | [Play RTTTL Online](https://adamonsoon.github.io/rtttl-play/) 4 | 5 | For RTTTL parsing use [rtttl-parse](https://github.com/adamonsoon/rtttl-parse) 6 | -------------------------------------------------------------------------------- /assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color:#f5f5f5; 3 | } 4 | 5 | h1 { 6 | font-weight:bold; 7 | text-align:center; 8 | } 9 | 10 | textarea { 11 | width:100%; 12 | border:0; 13 | margin:0 auto; 14 | padding:15px; 15 | font-size:20px; 16 | } 17 | 18 | .rttl-parse { 19 | margin-top: 20px; 20 | } -------------------------------------------------------------------------------- /webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | import {join} from 'path' 2 | 3 | const include = join(__dirname, 'src') 4 | 5 | export default { 6 | entry: './src/index', 7 | output: { 8 | path: join(__dirname, 'dist'), 9 | libraryTarget: 'umd', 10 | library: 'rtttlPlay', 11 | }, 12 | devtool: 'source-map', 13 | module: { 14 | loaders: [ 15 | {test: /\.js$/, loader: 'babel-loader', include}, 16 | {test: /\.json$/, 'loader': 'json-loader', include}, 17 | ] 18 | } 19 | } 20 | 0 21 | 0 22 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var rtttlParse = require('rtttl-parse'); 4 | 5 | module.exports = { 6 | play: play, 7 | stop: stop 8 | }; 9 | 10 | var shouldStop = false; 11 | 12 | function play(rtttl) { 13 | 14 | try { 15 | var parsedRtttl = rtttlParse.parse(rtttl); 16 | var audioCtx = new (AudioContext || webkitAudioContext)(); 17 | _playMelody(parsedRtttl.melody, audioCtx); 18 | } catch (err) { 19 | alert(err); 20 | } 21 | } 22 | 23 | function stop() { 24 | shouldStop = true; 25 | } 26 | 27 | function _playMelody(melody, audioCtx) { 28 | 29 | if (shouldStop) { 30 | shouldStop = false; 31 | return; 32 | } 33 | 34 | var osc = audioCtx.createOscillator(); 35 | osc.type = 'triangle'; 36 | osc.start(0); 37 | 38 | if (melody.length === 0) { 39 | return; 40 | } 41 | 42 | var note = melody[0]; 43 | 44 | osc.frequency.value = note.frequency; 45 | osc.connect(audioCtx.destination); 46 | 47 | setTimeout(function () { 48 | osc.disconnect(audioCtx.destination); 49 | _playMelody(melody.slice(1), audioCtx, osc); 50 | }, note.duration); 51 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const rtttlParse = require('rtttl-parse'); 2 | 3 | module.exports = { 4 | play, 5 | stop 6 | }; 7 | 8 | let shouldStop = false; 9 | 10 | function play(rtttl) { 11 | 12 | try { 13 | const parsedRtttl = rtttlParse.parse(rtttl); 14 | const audioCtx = new (AudioContext || webkitAudioContext)(); 15 | _playMelody(parsedRtttl.melody, audioCtx); 16 | } catch(err) { 17 | alert(err); 18 | } 19 | 20 | } 21 | 22 | function stop() { 23 | shouldStop = true; 24 | } 25 | 26 | function _playMelody(melody, audioCtx) { 27 | 28 | if (shouldStop) { 29 | shouldStop = false; 30 | return; 31 | } 32 | 33 | const osc = audioCtx.createOscillator(); 34 | osc.type = 'triangle'; 35 | osc.start(0); 36 | 37 | if (melody.length === 0) { 38 | return; 39 | } 40 | 41 | const note = melody[0]; 42 | 43 | osc.frequency.value = note.frequency; 44 | osc.connect(audioCtx.destination); 45 | 46 | setTimeout(() => { 47 | osc.disconnect(audioCtx.destination); 48 | _playMelody(melody.slice(1), audioCtx, osc); 49 | }, note.duration); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Play RTTTL Online 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 17 | 18 |
19 | 20 |
21 | 25 | 29 |
Not enough? Use the rtttl-parse library to build your own player or send a PR on rtttl-play.
30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtttl-play", 3 | "version": "1.0.0", 4 | "description": "Play RTTTL files online", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "commit": "git-cz", 8 | "check-coverage": "istanbul check-coverage --statements 100 --branches 100 --functions 100 --lines 100", 9 | "report-coverage": "cat ./coverage/lcov.info | codecov", 10 | "build": "npm-run-all --parallel build:*", 11 | "build:main": "babel --out-dir dist --ignore *.test.js src", 12 | "build:umd": "webpack --output-filename index.umd.js", 13 | "build:umd.min": "webpack --output-filename index.umd.min.js -p", 14 | "prebuild": "rimraf dist", 15 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/adamonsoon/rtttl-play.git" 20 | }, 21 | "keywords": [ 22 | "rtttl", 23 | "nokia" 24 | ], 25 | "author": "Adam Rahwane (https://github.com/adamonsoon)", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/adamonsoon/rtttl-play/issues" 29 | }, 30 | "homepage": "https://github.com/adamonsoon/rtttl-play#readme", 31 | "devDependencies": { 32 | "babel-cli": "6.24.0", 33 | "babel-loader": "6.4.1", 34 | "babel-preset-es2015": "6.24.0", 35 | "babel-preset-stage-2": "6.22.0", 36 | "chai": "3.5.0", 37 | "istanbul": "0.4.5", 38 | "json-loader": "0.5.4", 39 | "mocha": "3.2.0", 40 | "npm-run-all": "4.0.2", 41 | "rimraf": "2.6.1", 42 | "sinon": "2.1.0", 43 | "webpack": "2.3.2" 44 | }, 45 | "czConfig": { 46 | "path": "node_modules/cz-conventional-changelog" 47 | }, 48 | "config": { 49 | "ghooks": { 50 | "pre-commit": "npm run test:single && npm run check-coverage" 51 | } 52 | }, 53 | "babel": { 54 | "presets": [ 55 | "es2015", 56 | "stage-2" 57 | ] 58 | }, 59 | "dependencies": { 60 | "rtttl-parse": "1.3.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /dist/index.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.rtttlPlay=n():t.rtttlPlay=n()}(this,function(){return function(t){function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var e={};return n.m=t,n.c=e,n.i=function(t){return t},n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=1)}([function(t,n,e){"use strict";function r(t){var n=t.split(":");if(3!==n.length)throw new Error("Invalid RTTTL file.");var e=o(n[0]),r=a(n[1]);return{name:e,defaults:r,melody:u(n[2],r)}}function o(t){return t.length>10&&console.warn("Tune name should not exceed 10 characters."),t?t:"Unknown"}function a(t){var n=t.split(","),e=n.map(function(t){if(""===t)return{};var n=t.split("=");if(2!==n.length)throw new Error("Invalid setting "+t);var e=n[0],r=n[1],o=["1","2","4","8","16","32"],a=["4","5","6","7"],i=["25","28","31","35","40","45","50","56","63","70","80","90","100","112","125","140","160","180","200","225","250","285","320","355","400","450","500","565","635","715","800","900"];switch(e){case"d":if(o.indexOf(r)!==-1)return{duration:r};throw new Error("Invalid duration "+r);case"o":return a.indexOf(r)===-1&&console.warn("Invalid octave "+r),{octave:r};case"b":return i.indexOf(r)===-1&&console.warn("Invalid BPM "+r),{bpm:r}}}),r=i({},e),o={duration:"4",octave:"6",bpm:"63"};return Object.assign(o,r)}function i(t,n){return 0===n.length?t:i(Object.assign(t,n[0]),n.slice(1))}function u(t,n){var e=t.split(","),r=6e4/parseInt(n.bpm);return e.map(function(t){var e=t.match(/(1|2|4|8|16|32|64)?((?:[a-g]|h|p)#?){1}(\.?)(4|5|6|7)?/),o=e[1]||parseInt(n.duration),a="h"===e[2]?"b":e[2],i="."===e[3],u=e[4]||parseInt(n.octave);return{note:a,duration:s(r,parseFloat(o),i),frequency:c(a,u)}})}function c(t,n){if("p"===t)return 0;var e=Math.pow(2,1/12),r=f(t,n),o=261.63*Math.pow(e,r);return Math.round(10*o)/10}function f(t,n){var e=["c","c#","d","d#","e","f","f#","g","g#","a","a#","b"],r=12*(n-4);return e.indexOf(t)+r}function s(t,n,e){var r=4*t/n;return r+(e?r/2:0)}t.exports={parse:r,getName:o,getDefaults:a,getData:u}},function(t,n,e){"use strict";function r(t){try{var n=i.parse(t),e=new(AudioContext||webkitAudioContext);a(n.melody,e)}catch(t){alert(t)}}function o(){u=!0}function a(t,n){if(u)return void(u=!1);var e=n.createOscillator();if(e.type="triangle",e.start(0),0!==t.length){var r=t[0];e.frequency.value=r.frequency,e.connect(n.destination),setTimeout(function(){e.disconnect(n.destination),a(t.slice(1),n,e)},r.duration)}}var i=e(0);t.exports={play:r,stop:o};var u=!1}])}); 2 | //# sourceMappingURL=index.umd.min.js.map -------------------------------------------------------------------------------- /dist/index.umd.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["rtttlPlay"] = factory(); 8 | else 9 | root["rtttlPlay"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // identity function for calling harmony imports with the correct context 47 | /******/ __webpack_require__.i = function(value) { return value; }; 48 | /******/ 49 | /******/ // define getter function for harmony exports 50 | /******/ __webpack_require__.d = function(exports, name, getter) { 51 | /******/ if(!__webpack_require__.o(exports, name)) { 52 | /******/ Object.defineProperty(exports, name, { 53 | /******/ configurable: false, 54 | /******/ enumerable: true, 55 | /******/ get: getter 56 | /******/ }); 57 | /******/ } 58 | /******/ }; 59 | /******/ 60 | /******/ // getDefaultExport function for compatibility with non-harmony modules 61 | /******/ __webpack_require__.n = function(module) { 62 | /******/ var getter = module && module.__esModule ? 63 | /******/ function getDefault() { return module['default']; } : 64 | /******/ function getModuleExports() { return module; }; 65 | /******/ __webpack_require__.d(getter, 'a', getter); 66 | /******/ return getter; 67 | /******/ }; 68 | /******/ 69 | /******/ // Object.prototype.hasOwnProperty.call 70 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 71 | /******/ 72 | /******/ // __webpack_public_path__ 73 | /******/ __webpack_require__.p = ""; 74 | /******/ 75 | /******/ // Load entry module and return exports 76 | /******/ return __webpack_require__(__webpack_require__.s = 1); 77 | /******/ }) 78 | /************************************************************************/ 79 | /******/ ([ 80 | /* 0 */ 81 | /***/ (function(module, exports, __webpack_require__) { 82 | 83 | "use strict"; 84 | 85 | 86 | module.exports = { 87 | parse: parse, 88 | getName: getName, 89 | getDefaults: getDefaults, 90 | getData: getData 91 | }; 92 | 93 | /** 94 | * Parse RTTTL 95 | * 96 | * @param {string} rtttl - RTTTL String 97 | * @returns {object} - An object specifying frequency and duration for each note 98 | */ 99 | function parse(rtttl) { 100 | 101 | var REQUIRED_SECTIONS_NUM = 3; 102 | var SECTIONS = rtttl.split(':'); 103 | 104 | if (SECTIONS.length !== REQUIRED_SECTIONS_NUM) { 105 | throw new Error('Invalid RTTTL file.'); 106 | } 107 | 108 | var NAME = getName(SECTIONS[0]); 109 | var DEFAULTS = getDefaults(SECTIONS[1]); 110 | var MELODY = getData(SECTIONS[2], DEFAULTS); 111 | 112 | return { 113 | name: NAME, 114 | defaults: DEFAULTS, 115 | melody: MELODY 116 | }; 117 | } 118 | 119 | /** 120 | * Get ring tone name 121 | * 122 | * @param {string} name 123 | * @returns {string} 124 | */ 125 | function getName(name) { 126 | 127 | var MAX_LENGTH = 10; 128 | 129 | if (name.length > MAX_LENGTH) { 130 | console.warn('Tune name should not exceed 10 characters.'); 131 | } 132 | 133 | if (!name) { 134 | return 'Unknown'; 135 | } 136 | 137 | return name; 138 | } 139 | 140 | /** 141 | * Get duration, octave and BPM 142 | * 143 | * @param {string} defaults 144 | * @returns {object} 145 | */ 146 | function getDefaults(defaults) { 147 | 148 | var VALUES = defaults.split(','); 149 | 150 | var VALUES_ARR = VALUES.map(function (value) { 151 | 152 | if (value === '') { 153 | return {}; 154 | } 155 | 156 | var KEY_VAL = value.split('='); 157 | 158 | if (KEY_VAL.length !== 2) { 159 | throw new Error('Invalid setting ' + value); 160 | } 161 | 162 | var KEY = KEY_VAL[0]; 163 | var VAL = KEY_VAL[1]; 164 | 165 | var ALLOWED_DURATION = ['1', '2', '4', '8', '16', '32']; 166 | var ALLOWED_OCTAVE = ['4', '5', '6', '7']; 167 | var ALLOWED_BPM = ['25', '28', '31', '35', '40', '45', '50', '56', '63', '70', '80', '90', '100', '112', '125', '140', '160', '180', '200', '225', '250', '285', '320', '355', '400', '450', '500', '565', '635', '715', '800', '900']; 168 | 169 | switch (KEY) { 170 | case 'd': 171 | if (ALLOWED_DURATION.indexOf(VAL) !== -1) { 172 | return { duration: VAL }; 173 | } else { 174 | throw new Error('Invalid duration ' + VAL); 175 | } 176 | case 'o': 177 | if (ALLOWED_OCTAVE.indexOf(VAL) === -1) { 178 | console.warn('Invalid octave ' + VAL); 179 | } 180 | return { octave: VAL }; 181 | case 'b': 182 | if (ALLOWED_BPM.indexOf(VAL) === -1) { 183 | console.warn('Invalid BPM ' + VAL); 184 | } 185 | return { bpm: VAL }; 186 | } 187 | }); 188 | 189 | var VALUES_OBJ = _toObject({}, VALUES_ARR); 190 | 191 | var DEFAULT_VALUES = { 192 | duration: '4', 193 | octave: '6', 194 | bpm: '63' 195 | }; 196 | 197 | return Object.assign(DEFAULT_VALUES, VALUES_OBJ); 198 | } 199 | 200 | /** 201 | * Convert an array of objects into an object 202 | * 203 | * @param {object} obj 204 | * @param {Array} arr 205 | * @returns {object} 206 | * @private 207 | */ 208 | function _toObject(obj, arr) { 209 | 210 | if (arr.length === 0) { 211 | return obj; 212 | } 213 | 214 | var newObj = Object.assign(obj, arr[0]); 215 | 216 | return _toObject(newObj, arr.slice(1)); 217 | } 218 | 219 | /** 220 | * Get the parsed melody data 221 | * 222 | * @param {string} melody 223 | * @param {object} defaults 224 | * @returns {Array} 225 | */ 226 | function getData(melody, defaults) { 227 | 228 | var NOTES = melody.split(','); 229 | var BEAT_EVERY = 60000 / parseInt(defaults.bpm); 230 | 231 | return NOTES.map(function (note) { 232 | 233 | var NOTE_REGEX = /(1|2|4|8|16|32|64)?((?:[a-g]|h|p)#?){1}(\.?)(4|5|6|7)?/; 234 | var NOTE_PARTS = note.match(NOTE_REGEX); 235 | 236 | var NOTE_DURATION = NOTE_PARTS[1] || parseInt(defaults.duration); 237 | var NOTE = NOTE_PARTS[2] === 'h' ? 'b' : NOTE_PARTS[2]; 238 | var NOTE_DOTTED = NOTE_PARTS[3] === '.'; 239 | var NOTE_OCTAVE = NOTE_PARTS[4] || parseInt(defaults.octave); 240 | 241 | return { 242 | note: NOTE, 243 | duration: _calculateDuration(BEAT_EVERY, parseFloat(NOTE_DURATION), NOTE_DOTTED), 244 | frequency: _calculateFrequency(NOTE, NOTE_OCTAVE) 245 | }; 246 | }); 247 | } 248 | 249 | /** 250 | * Calculate the frequency of a note 251 | * 252 | * @param {string} note 253 | * @param {number} octave 254 | * @returns {number} 255 | * @private 256 | */ 257 | function _calculateFrequency(note, octave) { 258 | 259 | if (note === 'p') { 260 | return 0; 261 | } 262 | 263 | var C4 = 261.63; 264 | var TWELFTH_ROOT = Math.pow(2, 1 / 12); 265 | var N = _calculateSemitonesFromC4(note, octave); 266 | var FREQUENCY = C4 * Math.pow(TWELFTH_ROOT, N); 267 | 268 | return Math.round(FREQUENCY * 1e1) / 1e1; 269 | } 270 | 271 | function _calculateSemitonesFromC4(note, octave) { 272 | 273 | var NOTE_ORDER = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b']; 274 | var MIDDLE_OCTAVE = 4; 275 | var SEMITONES_IN_OCTAVE = 12; 276 | 277 | var OCTAVE_JUMP = (octave - MIDDLE_OCTAVE) * SEMITONES_IN_OCTAVE; 278 | 279 | return NOTE_ORDER.indexOf(note) + OCTAVE_JUMP; 280 | } 281 | 282 | /** 283 | * Calculate the duration a note should be played 284 | * 285 | * @param {number} beatEvery 286 | * @param {number} noteDuration 287 | * @param {boolean} isDotted 288 | * @returns {number} 289 | * @private 290 | */ 291 | function _calculateDuration(beatEvery, noteDuration, isDotted) { 292 | var DURATION = beatEvery * 4 / noteDuration; 293 | var PROLONGED = isDotted ? DURATION / 2 : 0; 294 | return DURATION + PROLONGED; 295 | } 296 | 297 | /***/ }), 298 | /* 1 */ 299 | /***/ (function(module, exports, __webpack_require__) { 300 | 301 | "use strict"; 302 | 303 | 304 | var rtttlParse = __webpack_require__(0); 305 | 306 | module.exports = { 307 | play: play, 308 | stop: stop 309 | }; 310 | 311 | var shouldStop = false; 312 | 313 | function play(rtttl) { 314 | 315 | try { 316 | var parsedRtttl = rtttlParse.parse(rtttl); 317 | var audioCtx = new (AudioContext || webkitAudioContext)(); 318 | _playMelody(parsedRtttl.melody, audioCtx); 319 | } catch (err) { 320 | alert(err); 321 | } 322 | } 323 | 324 | function stop() { 325 | shouldStop = true; 326 | } 327 | 328 | function _playMelody(melody, audioCtx) { 329 | 330 | if (shouldStop) { 331 | shouldStop = false; 332 | return; 333 | } 334 | 335 | var osc = audioCtx.createOscillator(); 336 | osc.type = 'triangle'; 337 | osc.start(0); 338 | 339 | if (melody.length === 0) { 340 | return; 341 | } 342 | 343 | var note = melody[0]; 344 | 345 | osc.frequency.value = note.frequency; 346 | osc.connect(audioCtx.destination); 347 | 348 | setTimeout(function () { 349 | osc.disconnect(audioCtx.destination); 350 | _playMelody(melody.slice(1), audioCtx, osc); 351 | }, note.duration); 352 | } 353 | 354 | /***/ }) 355 | /******/ ]); 356 | }); 357 | //# sourceMappingURL=index.umd.js.map -------------------------------------------------------------------------------- /dist/index.umd.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 616c6df84e2e399f034b","webpack:///./~/rtttl-parse/dist/index.js","webpack:///./src/index.js"],"names":["rtttlParse","require","module","exports","play","stop","shouldStop","rtttl","parsedRtttl","parse","audioCtx","AudioContext","webkitAudioContext","_playMelody","melody","err","alert","osc","createOscillator","type","start","length","note","frequency","value","connect","destination","setTimeout","disconnect","slice","duration"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AChEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,aAAa,OAAO;AACpB;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,aAAa;AACb;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,aAAa;AACb;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,kBAAkB;AAClB,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB;AAChB;AACA;AACA;AACA;AACA,gBAAgB;AAChB;AACA,GAAG;;AAEH,+BAA+B;;AAE/B;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,MAAM;AACjB,aAAa;AACb;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa;AACb;AACA;;AAEA;AACA;;AAEA;;AAEA,2DAA2D,EAAE;AAC7D;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa;AACb;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,QAAQ;AACnB,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,C;;;;;;;;;ACnNA,IAAMA,aAAa,mBAAAC,CAAQ,CAAR,CAAnB;;AAEAC,OAAOC,OAAP,GAAiB;AACfC,YADe;AAEfC;AAFe,CAAjB;;AAKA,IAAIC,aAAa,KAAjB;;AAEA,SAASF,IAAT,CAAcG,KAAd,EAAqB;;AAEnB,MAAI;AACF,QAAMC,cAAcR,WAAWS,KAAX,CAAiBF,KAAjB,CAApB;AACA,QAAMG,WAAc,KAAKC,gBAAgBC,kBAArB,GAApB;AACAC,gBAAYL,YAAYM,MAAxB,EAAgCJ,QAAhC;AACD,GAJD,CAIE,OAAMK,GAAN,EAAW;AACXC,UAAMD,GAAN;AACD;AAEF;;AAED,SAASV,IAAT,GAAgB;AACdC,eAAa,IAAb;AACD;;AAED,SAASO,WAAT,CAAqBC,MAArB,EAA6BJ,QAA7B,EAAuC;;AAErC,MAAIJ,UAAJ,EAAgB;AACdA,iBAAa,KAAb;AACA;AACD;;AAED,MAAMW,MAAMP,SAASQ,gBAAT,EAAZ;AACMD,MAAIE,IAAJ,GAAW,UAAX;AACAF,MAAIG,KAAJ,CAAU,CAAV;;AAEN,MAAIN,OAAOO,MAAP,KAAkB,CAAtB,EAAyB;AACvB;AACD;;AAED,MAAMC,OAAOR,OAAO,CAAP,CAAb;;AAEAG,MAAIM,SAAJ,CAAcC,KAAd,GAAsBF,KAAKC,SAA3B;AACAN,MAAIQ,OAAJ,CAAYf,SAASgB,WAArB;;AAEAC,aAAW,YAAM;AACfV,QAAIW,UAAJ,CAAelB,SAASgB,WAAxB;AACAb,gBAAYC,OAAOe,KAAP,CAAa,CAAb,CAAZ,EAA6BnB,QAA7B,EAAuCO,GAAvC;AACD,GAHD,EAGGK,KAAKQ,QAHR;AAKD,C","file":"index.umd.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"rtttlPlay\"] = factory();\n\telse\n\t\troot[\"rtttlPlay\"] = factory();\n})(this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 616c6df84e2e399f034b","\"use strict\";\n\nmodule.exports = {\n parse: parse,\n getName: getName,\n getDefaults: getDefaults,\n getData: getData\n};\n\n/**\n * Parse RTTTL\n *\n * @param {string} rtttl - RTTTL String\n * @returns {object} - An object specifying frequency and duration for each note\n */\nfunction parse(rtttl) {\n\n var REQUIRED_SECTIONS_NUM = 3;\n var SECTIONS = rtttl.split(':');\n\n if (SECTIONS.length !== REQUIRED_SECTIONS_NUM) {\n throw new Error('Invalid RTTTL file.');\n }\n\n var NAME = getName(SECTIONS[0]);\n var DEFAULTS = getDefaults(SECTIONS[1]);\n var MELODY = getData(SECTIONS[2], DEFAULTS);\n\n return {\n name: NAME,\n defaults: DEFAULTS,\n melody: MELODY\n };\n}\n\n/**\n * Get ring tone name\n *\n * @param {string} name\n * @returns {string}\n */\nfunction getName(name) {\n\n var MAX_LENGTH = 10;\n\n if (name.length > MAX_LENGTH) {\n console.warn('Tune name should not exceed 10 characters.');\n }\n\n if (!name) {\n return 'Unknown';\n }\n\n return name;\n}\n\n/**\n * Get duration, octave and BPM\n *\n * @param {string} defaults\n * @returns {object}\n */\nfunction getDefaults(defaults) {\n\n var VALUES = defaults.split(',');\n\n var VALUES_ARR = VALUES.map(function (value) {\n\n if (value === '') {\n return {};\n }\n\n var KEY_VAL = value.split('=');\n\n if (KEY_VAL.length !== 2) {\n throw new Error('Invalid setting ' + value);\n }\n\n var KEY = KEY_VAL[0];\n var VAL = KEY_VAL[1];\n\n var ALLOWED_DURATION = ['1', '2', '4', '8', '16', '32'];\n var ALLOWED_OCTAVE = ['4', '5', '6', '7'];\n var ALLOWED_BPM = ['25', '28', '31', '35', '40', '45', '50', '56', '63', '70', '80', '90', '100', '112', '125', '140', '160', '180', '200', '225', '250', '285', '320', '355', '400', '450', '500', '565', '635', '715', '800', '900'];\n\n switch (KEY) {\n case 'd':\n if (ALLOWED_DURATION.indexOf(VAL) !== -1) {\n return { duration: VAL };\n } else {\n throw new Error('Invalid duration ' + VAL);\n }\n case 'o':\n if (ALLOWED_OCTAVE.indexOf(VAL) === -1) {\n console.warn('Invalid octave ' + VAL);\n }\n return { octave: VAL };\n case 'b':\n if (ALLOWED_BPM.indexOf(VAL) === -1) {\n console.warn('Invalid BPM ' + VAL);\n }\n return { bpm: VAL };\n }\n });\n\n var VALUES_OBJ = _toObject({}, VALUES_ARR);\n\n var DEFAULT_VALUES = {\n duration: '4',\n octave: '6',\n bpm: '63'\n };\n\n return Object.assign(DEFAULT_VALUES, VALUES_OBJ);\n}\n\n/**\n * Convert an array of objects into an object\n *\n * @param {object} obj\n * @param {Array} arr\n * @returns {object}\n * @private\n */\nfunction _toObject(obj, arr) {\n\n if (arr.length === 0) {\n return obj;\n }\n\n var newObj = Object.assign(obj, arr[0]);\n\n return _toObject(newObj, arr.slice(1));\n}\n\n/**\n * Get the parsed melody data\n *\n * @param {string} melody\n * @param {object} defaults\n * @returns {Array}\n */\nfunction getData(melody, defaults) {\n\n var NOTES = melody.split(',');\n var BEAT_EVERY = 60000 / parseInt(defaults.bpm);\n\n return NOTES.map(function (note) {\n\n var NOTE_REGEX = /(1|2|4|8|16|32|64)?((?:[a-g]|h|p)#?){1}(\\.?)(4|5|6|7)?/;\n var NOTE_PARTS = note.match(NOTE_REGEX);\n\n var NOTE_DURATION = NOTE_PARTS[1] || parseInt(defaults.duration);\n var NOTE = NOTE_PARTS[2] === 'h' ? 'b' : NOTE_PARTS[2];\n var NOTE_DOTTED = NOTE_PARTS[3] === '.';\n var NOTE_OCTAVE = NOTE_PARTS[4] || parseInt(defaults.octave);\n\n return {\n note: NOTE,\n duration: _calculateDuration(BEAT_EVERY, parseFloat(NOTE_DURATION), NOTE_DOTTED),\n frequency: _calculateFrequency(NOTE, NOTE_OCTAVE)\n };\n });\n}\n\n/**\n * Calculate the frequency of a note\n *\n * @param {string} note\n * @param {number} octave\n * @returns {number}\n * @private\n */\nfunction _calculateFrequency(note, octave) {\n\n if (note === 'p') {\n return 0;\n }\n\n var C4 = 261.63;\n var TWELFTH_ROOT = Math.pow(2, 1 / 12);\n var N = _calculateSemitonesFromC4(note, octave);\n var FREQUENCY = C4 * Math.pow(TWELFTH_ROOT, N);\n\n return Math.round(FREQUENCY * 1e1) / 1e1;\n}\n\nfunction _calculateSemitonesFromC4(note, octave) {\n\n var NOTE_ORDER = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b'];\n var MIDDLE_OCTAVE = 4;\n var SEMITONES_IN_OCTAVE = 12;\n\n var OCTAVE_JUMP = (octave - MIDDLE_OCTAVE) * SEMITONES_IN_OCTAVE;\n\n return NOTE_ORDER.indexOf(note) + OCTAVE_JUMP;\n}\n\n/**\n * Calculate the duration a note should be played\n *\n * @param {number} beatEvery\n * @param {number} noteDuration\n * @param {boolean} isDotted\n * @returns {number}\n * @private\n */\nfunction _calculateDuration(beatEvery, noteDuration, isDotted) {\n var DURATION = beatEvery * 4 / noteDuration;\n var PROLONGED = isDotted ? DURATION / 2 : 0;\n return DURATION + PROLONGED;\n}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/rtttl-parse/dist/index.js\n// module id = 0\n// module chunks = 0","const rtttlParse = require('rtttl-parse');\n\nmodule.exports = {\n play,\n stop\n};\n\nlet shouldStop = false;\n\nfunction play(rtttl) {\n\n try {\n const parsedRtttl = rtttlParse.parse(rtttl);\n const audioCtx = new (AudioContext || webkitAudioContext)();\n _playMelody(parsedRtttl.melody, audioCtx);\n } catch(err) {\n alert(err);\n }\n\n}\n\nfunction stop() {\n shouldStop = true;\n}\n\nfunction _playMelody(melody, audioCtx) {\n\n if (shouldStop) {\n shouldStop = false;\n return;\n }\n\n const osc = audioCtx.createOscillator();\n osc.type = 'triangle';\n osc.start(0);\n\n if (melody.length === 0) {\n return;\n }\n\n const note = melody[0];\n\n osc.frequency.value = note.frequency;\n osc.connect(audioCtx.destination);\n\n setTimeout(() => {\n osc.disconnect(audioCtx.destination);\n _playMelody(melody.slice(1), audioCtx, osc);\n }, note.duration);\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.js"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/index.umd.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///index.umd.min.js","webpack:///webpack/bootstrap fa4d3bc25cd0532fef32","webpack:///./~/rtttl-parse/dist/index.js","webpack:///./src/index.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","i","l","call","m","c","value","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","parse","rtttl","SECTIONS","split","length","Error","NAME","getName","DEFAULTS","getDefaults","defaults","melody","getData","console","warn","VALUES","VALUES_ARR","map","KEY_VAL","KEY","VAL","ALLOWED_DURATION","ALLOWED_OCTAVE","ALLOWED_BPM","indexOf","duration","octave","bpm","VALUES_OBJ","_toObject","DEFAULT_VALUES","assign","obj","arr","slice","NOTES","BEAT_EVERY","parseInt","note","NOTE_PARTS","match","NOTE_DURATION","NOTE","NOTE_DOTTED","NOTE_OCTAVE","_calculateDuration","parseFloat","frequency","_calculateFrequency","TWELFTH_ROOT","Math","pow","N","_calculateSemitonesFromC4","FREQUENCY","round","NOTE_ORDER","OCTAVE_JUMP","beatEvery","noteDuration","isDotted","DURATION","play","parsedRtttl","rtttlParse","audioCtx","AudioContext","webkitAudioContext","_playMelody","err","alert","stop","shouldStop","osc","createOscillator","type","start","connect","destination","setTimeout","disconnect"],"mappings":"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","file":"index.umd.min.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"rtttlPlay\"] = factory();\n\telse\n\t\troot[\"rtttlPlay\"] = factory();\n})(this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"rtttlPlay\"] = factory();\n\telse\n\t\troot[\"rtttlPlay\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = {\n parse: parse,\n getName: getName,\n getDefaults: getDefaults,\n getData: getData\n};\n\n/**\n * Parse RTTTL\n *\n * @param {string} rtttl - RTTTL String\n * @returns {object} - An object specifying frequency and duration for each note\n */\nfunction parse(rtttl) {\n\n var REQUIRED_SECTIONS_NUM = 3;\n var SECTIONS = rtttl.split(':');\n\n if (SECTIONS.length !== REQUIRED_SECTIONS_NUM) {\n throw new Error('Invalid RTTTL file.');\n }\n\n var NAME = getName(SECTIONS[0]);\n var DEFAULTS = getDefaults(SECTIONS[1]);\n var MELODY = getData(SECTIONS[2], DEFAULTS);\n\n return {\n name: NAME,\n defaults: DEFAULTS,\n melody: MELODY\n };\n}\n\n/**\n * Get ring tone name\n *\n * @param {string} name\n * @returns {string}\n */\nfunction getName(name) {\n\n var MAX_LENGTH = 10;\n\n if (name.length > MAX_LENGTH) {\n console.warn('Tune name should not exceed 10 characters.');\n }\n\n if (!name) {\n return 'Unknown';\n }\n\n return name;\n}\n\n/**\n * Get duration, octave and BPM\n *\n * @param {string} defaults\n * @returns {object}\n */\nfunction getDefaults(defaults) {\n\n var VALUES = defaults.split(',');\n\n var VALUES_ARR = VALUES.map(function (value) {\n\n if (value === '') {\n return {};\n }\n\n var KEY_VAL = value.split('=');\n\n if (KEY_VAL.length !== 2) {\n throw new Error('Invalid setting ' + value);\n }\n\n var KEY = KEY_VAL[0];\n var VAL = KEY_VAL[1];\n\n var ALLOWED_DURATION = ['1', '2', '4', '8', '16', '32'];\n var ALLOWED_OCTAVE = ['4', '5', '6', '7'];\n var ALLOWED_BPM = ['25', '28', '31', '35', '40', '45', '50', '56', '63', '70', '80', '90', '100', '112', '125', '140', '160', '180', '200', '225', '250', '285', '320', '355', '400', '450', '500', '565', '635', '715', '800', '900'];\n\n switch (KEY) {\n case 'd':\n if (ALLOWED_DURATION.indexOf(VAL) !== -1) {\n return { duration: VAL };\n } else {\n throw new Error('Invalid duration ' + VAL);\n }\n case 'o':\n if (ALLOWED_OCTAVE.indexOf(VAL) === -1) {\n console.warn('Invalid octave ' + VAL);\n }\n return { octave: VAL };\n case 'b':\n if (ALLOWED_BPM.indexOf(VAL) === -1) {\n console.warn('Invalid BPM ' + VAL);\n }\n return { bpm: VAL };\n }\n });\n\n var VALUES_OBJ = _toObject({}, VALUES_ARR);\n\n var DEFAULT_VALUES = {\n duration: '4',\n octave: '6',\n bpm: '63'\n };\n\n return Object.assign(DEFAULT_VALUES, VALUES_OBJ);\n}\n\n/**\n * Convert an array of objects into an object\n *\n * @param {object} obj\n * @param {Array} arr\n * @returns {object}\n * @private\n */\nfunction _toObject(obj, arr) {\n\n if (arr.length === 0) {\n return obj;\n }\n\n var newObj = Object.assign(obj, arr[0]);\n\n return _toObject(newObj, arr.slice(1));\n}\n\n/**\n * Get the parsed melody data\n *\n * @param {string} melody\n * @param {object} defaults\n * @returns {Array}\n */\nfunction getData(melody, defaults) {\n\n var NOTES = melody.split(',');\n var BEAT_EVERY = 60000 / parseInt(defaults.bpm);\n\n return NOTES.map(function (note) {\n\n var NOTE_REGEX = /(1|2|4|8|16|32|64)?((?:[a-g]|h|p)#?){1}(\\.?)(4|5|6|7)?/;\n var NOTE_PARTS = note.match(NOTE_REGEX);\n\n var NOTE_DURATION = NOTE_PARTS[1] || parseInt(defaults.duration);\n var NOTE = NOTE_PARTS[2] === 'h' ? 'b' : NOTE_PARTS[2];\n var NOTE_DOTTED = NOTE_PARTS[3] === '.';\n var NOTE_OCTAVE = NOTE_PARTS[4] || parseInt(defaults.octave);\n\n return {\n note: NOTE,\n duration: _calculateDuration(BEAT_EVERY, parseFloat(NOTE_DURATION), NOTE_DOTTED),\n frequency: _calculateFrequency(NOTE, NOTE_OCTAVE)\n };\n });\n}\n\n/**\n * Calculate the frequency of a note\n *\n * @param {string} note\n * @param {number} octave\n * @returns {number}\n * @private\n */\nfunction _calculateFrequency(note, octave) {\n\n if (note === 'p') {\n return 0;\n }\n\n var C4 = 261.63;\n var TWELFTH_ROOT = Math.pow(2, 1 / 12);\n var N = _calculateSemitonesFromC4(note, octave);\n var FREQUENCY = C4 * Math.pow(TWELFTH_ROOT, N);\n\n return Math.round(FREQUENCY * 1e1) / 1e1;\n}\n\nfunction _calculateSemitonesFromC4(note, octave) {\n\n var NOTE_ORDER = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b'];\n var MIDDLE_OCTAVE = 4;\n var SEMITONES_IN_OCTAVE = 12;\n\n var OCTAVE_JUMP = (octave - MIDDLE_OCTAVE) * SEMITONES_IN_OCTAVE;\n\n return NOTE_ORDER.indexOf(note) + OCTAVE_JUMP;\n}\n\n/**\n * Calculate the duration a note should be played\n *\n * @param {number} beatEvery\n * @param {number} noteDuration\n * @param {boolean} isDotted\n * @returns {number}\n * @private\n */\nfunction _calculateDuration(beatEvery, noteDuration, isDotted) {\n var DURATION = beatEvery * 4 / noteDuration;\n var PROLONGED = isDotted ? DURATION / 2 : 0;\n return DURATION + PROLONGED;\n}\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar rtttlParse = __webpack_require__(0);\n\nmodule.exports = {\n play: play,\n stop: stop\n};\n\nvar shouldStop = false;\n\nfunction play(rtttl) {\n\n try {\n var parsedRtttl = rtttlParse.parse(rtttl);\n var audioCtx = new (AudioContext || webkitAudioContext)();\n _playMelody(parsedRtttl.melody, audioCtx);\n } catch (err) {\n alert(err);\n }\n}\n\nfunction stop() {\n shouldStop = true;\n}\n\nfunction _playMelody(melody, audioCtx) {\n\n if (shouldStop) {\n shouldStop = false;\n return;\n }\n\n var osc = audioCtx.createOscillator();\n osc.type = 'triangle';\n osc.start(0);\n\n if (melody.length === 0) {\n return;\n }\n\n var note = melody[0];\n\n osc.frequency.value = note.frequency;\n osc.connect(audioCtx.destination);\n\n setTimeout(function () {\n osc.disconnect(audioCtx.destination);\n _playMelody(melody.slice(1), audioCtx, osc);\n }, note.duration);\n}\n\n/***/ })\n/******/ ]);\n});\n\n\n// WEBPACK FOOTER //\n// index.umd.min.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap fa4d3bc25cd0532fef32","\"use strict\";\n\nmodule.exports = {\n parse: parse,\n getName: getName,\n getDefaults: getDefaults,\n getData: getData\n};\n\n/**\n * Parse RTTTL\n *\n * @param {string} rtttl - RTTTL String\n * @returns {object} - An object specifying frequency and duration for each note\n */\nfunction parse(rtttl) {\n\n var REQUIRED_SECTIONS_NUM = 3;\n var SECTIONS = rtttl.split(':');\n\n if (SECTIONS.length !== REQUIRED_SECTIONS_NUM) {\n throw new Error('Invalid RTTTL file.');\n }\n\n var NAME = getName(SECTIONS[0]);\n var DEFAULTS = getDefaults(SECTIONS[1]);\n var MELODY = getData(SECTIONS[2], DEFAULTS);\n\n return {\n name: NAME,\n defaults: DEFAULTS,\n melody: MELODY\n };\n}\n\n/**\n * Get ring tone name\n *\n * @param {string} name\n * @returns {string}\n */\nfunction getName(name) {\n\n var MAX_LENGTH = 10;\n\n if (name.length > MAX_LENGTH) {\n console.warn('Tune name should not exceed 10 characters.');\n }\n\n if (!name) {\n return 'Unknown';\n }\n\n return name;\n}\n\n/**\n * Get duration, octave and BPM\n *\n * @param {string} defaults\n * @returns {object}\n */\nfunction getDefaults(defaults) {\n\n var VALUES = defaults.split(',');\n\n var VALUES_ARR = VALUES.map(function (value) {\n\n if (value === '') {\n return {};\n }\n\n var KEY_VAL = value.split('=');\n\n if (KEY_VAL.length !== 2) {\n throw new Error('Invalid setting ' + value);\n }\n\n var KEY = KEY_VAL[0];\n var VAL = KEY_VAL[1];\n\n var ALLOWED_DURATION = ['1', '2', '4', '8', '16', '32'];\n var ALLOWED_OCTAVE = ['4', '5', '6', '7'];\n var ALLOWED_BPM = ['25', '28', '31', '35', '40', '45', '50', '56', '63', '70', '80', '90', '100', '112', '125', '140', '160', '180', '200', '225', '250', '285', '320', '355', '400', '450', '500', '565', '635', '715', '800', '900'];\n\n switch (KEY) {\n case 'd':\n if (ALLOWED_DURATION.indexOf(VAL) !== -1) {\n return { duration: VAL };\n } else {\n throw new Error('Invalid duration ' + VAL);\n }\n case 'o':\n if (ALLOWED_OCTAVE.indexOf(VAL) === -1) {\n console.warn('Invalid octave ' + VAL);\n }\n return { octave: VAL };\n case 'b':\n if (ALLOWED_BPM.indexOf(VAL) === -1) {\n console.warn('Invalid BPM ' + VAL);\n }\n return { bpm: VAL };\n }\n });\n\n var VALUES_OBJ = _toObject({}, VALUES_ARR);\n\n var DEFAULT_VALUES = {\n duration: '4',\n octave: '6',\n bpm: '63'\n };\n\n return Object.assign(DEFAULT_VALUES, VALUES_OBJ);\n}\n\n/**\n * Convert an array of objects into an object\n *\n * @param {object} obj\n * @param {Array} arr\n * @returns {object}\n * @private\n */\nfunction _toObject(obj, arr) {\n\n if (arr.length === 0) {\n return obj;\n }\n\n var newObj = Object.assign(obj, arr[0]);\n\n return _toObject(newObj, arr.slice(1));\n}\n\n/**\n * Get the parsed melody data\n *\n * @param {string} melody\n * @param {object} defaults\n * @returns {Array}\n */\nfunction getData(melody, defaults) {\n\n var NOTES = melody.split(',');\n var BEAT_EVERY = 60000 / parseInt(defaults.bpm);\n\n return NOTES.map(function (note) {\n\n var NOTE_REGEX = /(1|2|4|8|16|32|64)?((?:[a-g]|h|p)#?){1}(\\.?)(4|5|6|7)?/;\n var NOTE_PARTS = note.match(NOTE_REGEX);\n\n var NOTE_DURATION = NOTE_PARTS[1] || parseInt(defaults.duration);\n var NOTE = NOTE_PARTS[2] === 'h' ? 'b' : NOTE_PARTS[2];\n var NOTE_DOTTED = NOTE_PARTS[3] === '.';\n var NOTE_OCTAVE = NOTE_PARTS[4] || parseInt(defaults.octave);\n\n return {\n note: NOTE,\n duration: _calculateDuration(BEAT_EVERY, parseFloat(NOTE_DURATION), NOTE_DOTTED),\n frequency: _calculateFrequency(NOTE, NOTE_OCTAVE)\n };\n });\n}\n\n/**\n * Calculate the frequency of a note\n *\n * @param {string} note\n * @param {number} octave\n * @returns {number}\n * @private\n */\nfunction _calculateFrequency(note, octave) {\n\n if (note === 'p') {\n return 0;\n }\n\n var C4 = 261.63;\n var TWELFTH_ROOT = Math.pow(2, 1 / 12);\n var N = _calculateSemitonesFromC4(note, octave);\n var FREQUENCY = C4 * Math.pow(TWELFTH_ROOT, N);\n\n return Math.round(FREQUENCY * 1e1) / 1e1;\n}\n\nfunction _calculateSemitonesFromC4(note, octave) {\n\n var NOTE_ORDER = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b'];\n var MIDDLE_OCTAVE = 4;\n var SEMITONES_IN_OCTAVE = 12;\n\n var OCTAVE_JUMP = (octave - MIDDLE_OCTAVE) * SEMITONES_IN_OCTAVE;\n\n return NOTE_ORDER.indexOf(note) + OCTAVE_JUMP;\n}\n\n/**\n * Calculate the duration a note should be played\n *\n * @param {number} beatEvery\n * @param {number} noteDuration\n * @param {boolean} isDotted\n * @returns {number}\n * @private\n */\nfunction _calculateDuration(beatEvery, noteDuration, isDotted) {\n var DURATION = beatEvery * 4 / noteDuration;\n var PROLONGED = isDotted ? DURATION / 2 : 0;\n return DURATION + PROLONGED;\n}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/rtttl-parse/dist/index.js\n// module id = 0\n// module chunks = 0","const rtttlParse = require('rtttl-parse');\n\nmodule.exports = {\n play,\n stop\n};\n\nlet shouldStop = false;\n\nfunction play(rtttl) {\n\n try {\n const parsedRtttl = rtttlParse.parse(rtttl);\n const audioCtx = new (AudioContext || webkitAudioContext)();\n _playMelody(parsedRtttl.melody, audioCtx);\n } catch(err) {\n alert(err);\n }\n\n}\n\nfunction stop() {\n shouldStop = true;\n}\n\nfunction _playMelody(melody, audioCtx) {\n\n if (shouldStop) {\n shouldStop = false;\n return;\n }\n\n const osc = audioCtx.createOscillator();\n osc.type = 'triangle';\n osc.start(0);\n\n if (melody.length === 0) {\n return;\n }\n\n const note = melody[0];\n\n osc.frequency.value = note.frequency;\n osc.connect(audioCtx.destination);\n\n setTimeout(() => {\n osc.disconnect(audioCtx.destination);\n _playMelody(melody.slice(1), audioCtx, osc);\n }, note.duration);\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.js"],"sourceRoot":""} --------------------------------------------------------------------------------