UNPKG

talkr-tts

Version:

Wraps speechSynthesis calls for applications that want to animate to the voice

1,746 lines 54.5 kB
(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define("talkr-tts", [], factory);
	else if(typeof exports === 'object')
		exports["talkr-tts"] = factory();
	else
		root["talkr-tts"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 6);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _events = __webpack_require__(3);

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Base class for TTS objects
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */


var Base = function (_EventEmitter) {
  _inherits(Base, _EventEmitter);

  function Base(type, id) {
    _classCallCheck(this, Base);

    if (id === undefined) {
      id = Math.random().toString(36).substr(2, 9);
    } else if (typeof id !== 'string') {
      throw new Error('Invalid id');
    }

    var _this = _possibleConstructorReturn(this, (Base.__proto__ || Object.getPrototypeOf(Base)).call(this));

    _this._id = id;
    _this._type = type;

    if (!(_this._type in Base.objects)) {
      Base.objects[_this._type] = {};
    }
    Base.lastID[_this._type] = _this._id;
    Base.objects[_this._type][_this._id] = _this;
    return _this;
  }

  /**
   * Get the id.
   * @return {string}
   */


  _createClass(Base, [{
    key: 'delete',
    value: function _delete() {
      delete Base.objects[this._type][this._id];
    }
  }, {
    key: 'id',
    get: function get() {
      return this._id;
    },
    set: function set(id) {
      if (this._id in Base.objects[this._type]) {
        delete Base.objects[this._type][this._id];
      }
      this._id = id;
      Base.objects[this._type][this._id] = this;
      Base.lastID[this._type] = this._id;
    }
  }], [{
    key: 'getlastID',
    value: function getlastID(type) {
      return Base.lastID[type];
    }
  }, {
    key: 'getByTypeAndID',
    value: function getByTypeAndID(type, id) {
      if (type in Base.objects) {
        if (id in Base.objects[type]) {
          Base.lastID[type] = id;
          return Base.objects[type][id];
        }
      }
      return null;
    }
  }, {
    key: 'store',
    get: function get() {
      return Base.objects;
    },
    set: function set(store) {
      Base.objects = store;
    }
  }]);

  return Base;
}(_events2.default);

exports.default = Base;

Base.lastID = {};
Base.objects = {};
module.exports = exports['default'];

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var hasWindow = (typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window !== null && window.self === window && window.navigator !== null;
var bIsiOS = hasWindow && /iPad|iPhone|iPod/.test(window.navigator.userAgent) && !window.MSStream;
var hasSpeechSynthesis = hasWindow && window.speechSynthesis !== null;

// const iOSEnglishVoices = ['Karen', 'Serena', 'Moira', 'Tessa', 'Samantha'];
// const bIsAndroid = /android/i.test(navigator.userAgent);
var iOSVoiceNames = ['Maged', 'Zuzana', 'Sara', 'Anna', 'Melina', 'Karen', 'Daniel', 'Moira', 'Tessa', 'Alex', 'Monica', 'Paulina', 'Satu', 'Amelie', 'Thomas', 'Carmit', 'Lekha', 'Mariska', 'Damayanti', 'Alice', 'Kyoko', 'Yuna', 'Ellen', 'Xander', 'Nora', 'Zosia', 'Luciana', 'Joana', 'Ioana', 'Milena', 'Laura', 'Alva', 'Kanya', 'Yelda', 'Ting-Ting', 'Sin-Ji', 'Mei-Jia'];

var minimumPriority = 10;

var voiceProperties = {
  'Microsoft David Desktop - English (United States)': { male: true, priority: 13 },
  'Microsoft Hazel Desktop - English (Great Britain)': { male: false, priority: 14 },
  'Microsoft Zira Desktop - English (United States)': { male: false, priority: 14 },
  'Google US English': { male: false, priority: 24 },
  'Google UK English Female': { male: false, priority: 24 },
  'Google UK English Male': { male: true, priority: 23 },
  'Lee': { male: true, priority: 33 },
  'Fred': { male: true, priority: 8 },
  'Alex': { male: true, priority: 34 },
  'Daniel': { male: true, priority: 33 },
  'Daniel (Enhanced)': { male: true, priority: 43 },
  'Oliver': { male: true, priority: 33 },
  'Oliver (Enhanced)': { male: true, priority: 43 },
  'Samantha': { male: false, priority: 38 },
  'Victoria': { male: false, priority: 9 },
  'Veena': { male: false, priority: 32 },
  'Tessa': { male: false, priority: 37 },
  'Fiona': { male: false, priority: 37 },
  'Moira': { male: false, priority: 37 },
  'Karen': { male: false, priority: 37 }
};

var languageProperties = {
  'en': 'English',
  'zh': 'Chinese',
  'ru': 'Russian',
  'de': 'German',
  'it': 'Italian',
  'pt': 'Portuguese',
  'vi': 'Vietnamese',
  'tr': 'Turkish',
  'th': 'Thai',
  'es': 'Spanish',
  'fr': 'French',
  'sk': 'Slovak',
  'ro': 'Romanian',
  'pl': 'Polish',
  'nl': 'Dutch',
  'ko': 'Korean',
  'ja': 'Japanese',
  'he': 'Hebrew',
  'el': 'Greek',
  'fi': 'Finish',
  'no': 'Norwegian',
  'da': 'Danish',
  'cs': 'Czech',
  'ar': 'Arabic'
};

var languagePropertiesInverted = {
  'English': 'en',
  'Chinese': 'zh',
  'Russian': 'ru',
  'German': 'de',
  'Italian': 'it',
  'Portuguese': 'pt',
  'Vietnamese': 'vi',
  'Turkish': 'tr',
  'Thai': 'th',
  'Spanish': 'es',
  'French': 'fr',
  'Slovak': 'sk',
  'Romanian': 'ro',
  'Polish': 'pl',
  'Dutch': 'nl',
  'Korean': 'ko',
  'Japanese': 'ja',
  'Hebrew': 'he',
  'Greek': 'el',
  'Finish': 'fi',
  'Norwegian': 'no',
  'Danish': 'da',
  'Czech': 'cs',
  'Arabic': 'ar'
};

/*
var langs =
[['Afrikaans',       ['af-ZA']],
 ['Bahasa Indonesia',['id-ID']],
 ['Bahasa Melayu',   ['ms-MY']],
 ['Català',          ['ca-ES']],
 ['Čeština',         ['cs-CZ']],
 ['Deutsch',         ['de-DE']],
 ['English',         ['en-AU', 'Australia'],
 ['Español',         ['es-AR', 'Argentina'],
 ['Euskara',         ['eu-ES']],
 ['Français',        ['fr-FR']],
 ['Galego',          ['gl-ES']],
 ['Hrvatski',        ['hr_HR']],
 ['IsiZulu',         ['zu-ZA']],
 ['Íslenska',        ['is-IS']],
 ['Italiano',        ['it-IT', 'Italia'],
 ['Magyar',          ['hu-HU']],
 ['Nederlands',      ['nl-NL']],
 ['Norsk bokmål',    ['nb-NO']],
 ['Polski',          ['pl-PL']],
 ['Português',       ['pt-BR', 'Brasil'],
 ['Română',          ['ro-RO']],
 ['Slovenčina',      ['sk-SK']],
 ['Suomi',           ['fi-FI']],
 ['Svenska',         ['sv-SE']],
 ['Türkçe',          ['tr-TR']],
 ['български',       ['bg-BG']],
 ['Pусский',         ['ru-RU']],
 ['Српски',          ['sr-RS']],
 ['한국어',            ['ko-KR']],
 ['中文',             ['cmn-Hans-CN', '普通话 (中国大陆)'],
 ['日本語',           ['ja-JP']],
 ['Lingua latīna',   ['la']]];
*/

var Voice = function () {
  _createClass(Voice, null, [{
    key: 'getLangauges',
    value: function getLangauges() {
      var codes = [];
      var languages = [];

      function onlyUnique(value, index, self) {
        return self.indexOf(value) === index;
      }
      if (hasSpeechSynthesis) {
        speechSynthesis.getVoices().forEach(function (v) {
          codes.push(v.lang.substring(0, 2));
        });
      }

      var unique = codes.filter(onlyUnique);

      unique.forEach(function (l) {
        if (l in languageProperties) {
          languages.push(languageProperties[l]);
        } else {
          languages.push(l);
        }
      });
      return languages;
    }
  }, {
    key: 'getVoicesForLangauge',
    value: function getVoicesForLangauge(language) {
      var code = languagePropertiesInverted[language];
      var names = [];
      var voices = [];

      if (code) {
        voices = Voice.getVoicesWithLangSubstring(code);
        voices.forEach(function (v) {
          names.push(v.name);
        });
        return names;
      }
      return [];
    }
  }, {
    key: 'getVoicesWithLangSubstring',
    value: function getVoicesWithLangSubstring(langSubstr) {
      var filteredvoices = [];

      if (hasSpeechSynthesis) {
        filteredvoices = speechSynthesis.getVoices().filter(function (v) {
          if (voiceProperties[v.name]) {
            if (voiceProperties[v.name].priority < minimumPriority) {
              return false;
            }
          }
          return v.lang.substring(0, langSubstr.length) === langSubstr;
        });
      }
      // iOS returns more than they let you select.
      if (bIsiOS) {
        var newVoices = [];
        var last = filteredvoices.length - 1;

        for (var i = last; i >= 0; --i) {
          for (var j = 0; j < iOSVoiceNames.length; ++j) {
            if (filteredvoices[i].name === iOSVoiceNames[j]) {
              newVoices.push(filteredvoices[i]);
            }
          }
        }
        filteredvoices = newVoices;
      }

      // So does Android...all versions?

      function sortCompare(a, b) {
        var aPriority = 0;
        var bPriority = 0;

        if (voiceProperties[a.name]) {
          aPriority = voiceProperties[a.name].priority;
        }
        if (voiceProperties[b.name]) {
          bPriority = voiceProperties[b.name].priority;
        }
        if (a.name.includes('Enhanced')) {
          aPriority++;
        }
        if (b.name.includes('Enhanced')) {
          bPriority++;
        }
        return bPriority - aPriority;
      }
      return filteredvoices.sort(sortCompare);
    }
  }, {
    key: 'getTTSVoice',
    value: function getTTSVoice(voiceObj) {
      return Voice._getBestVoice(voiceObj.name, voiceObj.locale, voiceObj.isMale, voiceObj.index);
    }
  }, {
    key: '_getBestVoice',
    value: function _getBestVoice(name, locale, bIsMale, index) {
      if (name) {
        var namedVoice = Voice._getVoiceWithName(name);

        if (namedVoice) {
          return namedVoice;
        }
      }
      var matchingVoices = Voice._getBestVoices(locale, bIsMale);

      if (matchingVoices.length > 0) {
        if (index != null) {
          index = Math.abs(parseInt(index, 10));
          index = index % matchingVoices.length;
        } else {
          index = 0;
        }
        return matchingVoices[index];
      }

      // Try english voices if we didn't get any for the provided locale.
      if (locale !== 'en') {
        console.warn('No voices for locale: ' + locale);
        return Voice._getBestVoice(null, 'en', bIsMale, index);
      }

      var lastResorts = [];

      if (hasSpeechSynthesis) {
        lastResorts = speechSynthesis.getVoices();
      }
      if (lastResorts[0]) {
        return lastResorts[0];
      }
      return null;
    }
  }, {
    key: '_getBestVoices',
    value: function _getBestVoices(locale, bIsMale) {

      var voices = [];
      var genderVoices = [];

      if (locale && locale.length > 1) {
        voices = Voice.getVoicesWithLangSubstring(locale);
      }

      voices.forEach(function (v) {
        if (v.name in voiceProperties) {
          if (bIsMale === voiceProperties[v.name].male) {
            genderVoices.push(v);
          }
        } else {
          // We don't know the gender of this voice.  Let's cross our fingers.
          genderVoices.push(v);
        }
      });

      // Try with only the first two letters of the locale.
      if (genderVoices.length === 0 && locale.length > 2) {
        return this._getBestVoices(locale.substring(0, 2), bIsMale);
      }

      // Any gender will do at this point.
      if (genderVoices.length === 0) {
        voices.forEach(function (v) {
          genderVoices.push(v);
        });
      }

      return genderVoices;
    }
  }, {
    key: '_getVoicesWithName',
    value: function _getVoicesWithName(name) {
      if (hasSpeechSynthesis) {
        return speechSynthesis.getVoices().filter(function (v) {
          return v.name === name;
        });
      }
      return [];
    }
  }, {
    key: '_getVoiceWithName',
    value: function _getVoiceWithName(name) {
      if (hasSpeechSynthesis) {
        var voices = speechSynthesis.getVoices().filter(function (v) {
          return v.name === name;
        });

        if (voices.length > 0) {
          return voices[0];
        }
      }

      return null;
    }
  }, {
    key: 'doesSupportPitchAndRate',
    value: function doesSupportPitchAndRate(name) {
      var namedVoice = Voice._getVoiceWithName(name);

      if (namedVoice) {
        return namedVoice.localService === true;
      }
      return false;
    }
  }]);

  function Voice(locale, bIsMale, preference) {
    _classCallCheck(this, Voice);

    this._locale = locale;
    this._bIsMale = bIsMale;
    this._name = preference;
    this._index = null;
    if (this._locale === undefined) {
      this._locale = Voice.defaultlocale;
    }
    if (this._bIsMale === undefined) {
      this._bIsMale = Voice.defaultbIsMale;
    }
    if (this._name !== undefined) {
      if (this._name in voiceProperties) {
        this._bIsMale = voiceProperties[this._name].male;
      }
    }
    // A number for the name indicates we should choose a different
    // voice than the first.
    if (this._name && !isNaN(this._name)) {
      this._index = this._name;
      this._name = null;
    }

    Voice.defaultlocale = this._locale;
    Voice.defaulbIsMale = this._bIsMale;
  }

  _createClass(Voice, [{
    key: 'index',
    get: function get() {
      return this._index;
    }
  }, {
    key: 'name',
    get: function get() {
      return this._name;
    }
  }, {
    key: 'locale',
    get: function get() {
      return this._locale;
    }
  }, {
    key: 'isMale',
    get: function get() {
      return this._bIsMale;
    }
  }, {
    key: 'language',
    get: function get() {
      return languageProperties[this._locale.substring(0, 2)];
    }
  }]);

  return Voice;
}();

exports.default = Voice;

Voice.defaultlocale = 'en-US';
Voice.defaultbIsMale = false;
module.exports = exports['default'];

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _base = __webpack_require__(0);

var _base2 = _interopRequireDefault(_base);

var _voice = __webpack_require__(1);

var _voice2 = _interopRequireDefault(_voice);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Face = function (_Base) {
  _inherits(Face, _Base);

  // The id is the first parameter.  It is used to store the URL
  function Face(id, voice) {
    _classCallCheck(this, Face);

    var _this = _possibleConstructorReturn(this, (Face.__proto__ || Object.getPrototypeOf(Face)).call(this, 'face', id));

    if (voice === undefined) {
      voice = new _voice2.default();
    }
    _this._voice = voice;
    return _this;
  }

  // TODO: remove


  _createClass(Face, [{
    key: 'url',
    get: function get() {
      return this._id;
    },
    set: function set(url) {
      this._id = url;
    }
  }, {
    key: 'voice',
    get: function get() {
      return this._voice;
    },
    set: function set(voice) {
      this._voice = voice;
    }
  }], [{
    key: 'getByID',
    value: function getByID(id) {
      return _base2.default.getByTypeAndID('face', id);
    }
  }, {
    key: 'getlastID',
    value: function getlastID() {
      return _base2.default.getlastID('face');
    }
  }, {
    key: 'fromJSON',
    value: function fromJSON(json) {
      if (json['_id']) {
        var v = json['_voice'];
        var voice = null;

        if (v) {
          voice = new _voice2.default(v['_locale'], v['_bIsMale'], v['_name']);
        }
        return new Face(json['_id'], voice);
      }
      return null;
    }
  }]);

  return Face;
}(_base2.default);

exports.default = Face;
module.exports = exports['default'];

/***/ }),
/* 3 */
/***/ (function(module, exports) {

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError('n must be a positive number');
  this._maxListeners = n;
  return this;
};

EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;

  if (!this._events)
    this._events = {};

  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events.error ||
        (isObject(this._events.error) && !this._events.error.length)) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er; // Unhandled 'error' event
      } else {
        // At least give some kind of context to the user
        var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
        err.context = er;
        throw err;
      }
    }
  }

  handler = this._events[type];

  if (isUndefined(handler))
    return false;

  if (isFunction(handler)) {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    args = Array.prototype.slice.call(arguments, 1);
    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

  return true;
};

EventEmitter.prototype.addListener = function(type, listener) {
  var m;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events)
    this._events = {};

  // To avoid recursion in the case that type === "newListener"! Before
  // adding it to the listeners, first emit "newListener".
  if (this._events.newListener)
    this.emit('newListener', type,
              isFunction(listener.listener) ?
              listener.listener : listener);

  if (!this._events[type])
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  else if (isObject(this._events[type]))
    // If we've already got an array, just append.
    this._events[type].push(listener);
  else
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];

  // Check for listener leak
  if (isObject(this._events[type]) && !this._events[type].warned) {
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }

    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
      if (typeof console.trace === 'function') {
        // not supported in IE 10
        console.trace();
      }
    }
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  var fired = false;

  function g() {
    this.removeListener(type, g);

    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }

  g.listener = listener;
  this.on(type, g);

  return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events || !this._events[type])
    return this;

  list = this._events[type];
  length = list.length;
  position = -1;

  if (list === listener ||
      (isFunction(list.listener) && list.listener === listener)) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit('removeListener', type, listener);

  } else if (isObject(list)) {
    for (i = length; i-- > 0;) {
      if (list[i] === listener ||
          (list[i].listener && list[i].listener === listener)) {
        position = i;
        break;
      }
    }

    if (position < 0)
      return this;

    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }

    if (this._events.removeListener)
      this.emit('removeListener', type, listener);
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;

  if (!this._events)
    return this;

  // not listening for removeListener, no need to emit
  if (!this._events.removeListener) {
    if (arguments.length === 0)
      this._events = {};
    else if (this._events[type])
      delete this._events[type];
    return this;
  }

  // emit removeListener for all listeners on all events
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === 'removeListener') continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners('removeListener');
    this._events = {};
    return this;
  }

  listeners = this._events[type];

  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else if (listeners) {
    // LIFO order
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];

  return this;
};

EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type])
    ret = [];
  else if (isFunction(this._events[type]))
    ret = [this._events[type]];
  else
    ret = this._events[type].slice();
  return ret;
};

EventEmitter.prototype.listenerCount = function(type) {
  if (this._events) {
    var evlistener = this._events[type];

    if (isFunction(evlistener))
      return 1;
    else if (evlistener)
      return evlistener.length;
  }
  return 0;
};

EventEmitter.listenerCount = function(emitter, type) {
  return emitter.listenerCount(type);
};

function isFunction(arg) {
  return typeof arg === 'function';
}

function isNumber(arg) {
  return typeof arg === 'number';
}

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}

function isUndefined(arg) {
  return arg === void 0;
}


/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Utterance = function () {
  function Utterance(text, pitch, rate, delay) {
    _classCallCheck(this, Utterance);

    this._text = text;
    this._pitch = pitch;
    this._rate = rate;
    this._delay = delay;
    if (this._pitch === undefined) {
      this._pitch = 1.0;
    }
    if (this._rate === undefined) {
      this._rate = 1.0;
    }
    if (this._delay === undefined) {
      this._delay = 0.0;
    }
  }

  _createClass(Utterance, [{
    key: "text",
    get: function get() {
      return this._text;
    },
    set: function set(val) {
      this._text = val;
    }
  }, {
    key: "pitch",
    get: function get() {
      return this._pitch;
    },
    set: function set(val) {
      this._pitch = val;
    }
  }, {
    key: "rate",
    get: function get() {
      return this._rate;
    },
    set: function set(val) {
      this._rate = val;
    }
  }, {
    key: "delay",
    get: function get() {
      return this._delay;
    },
    set: function set(val) {
      this._delay = val;
    }
  }]);

  return Utterance;
}();

exports.default = Utterance;
module.exports = exports["default"];

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _utterance = __webpack_require__(4);

var _utterance2 = _interopRequireDefault(_utterance);

var _voice = __webpack_require__(1);

var _voice2 = _interopRequireDefault(_voice);

var _face = __webpack_require__(2);

var _face2 = _interopRequireDefault(_face);

var _base = __webpack_require__(0);

var _base2 = _interopRequireDefault(_base);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var hasWindow = (typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window !== null && window.self === window;
var speechSynthesis = hasWindow && 'speechSynthesis' in window ? window.speechSynthesis : null;

var Clip = function (_Base) {
  _inherits(Clip, _Base);

  function Clip(utterances, face, id) {
    _classCallCheck(this, Clip);

    var _this = _possibleConstructorReturn(this, (Clip.__proto__ || Object.getPrototypeOf(Clip)).call(this, 'clip', id));

    if (utterances === undefined) {
      utterances = [];
    }
    if (typeof utterances === 'string') {
      _this._utterances = _this._splitUtterances(utterances);
    } else if (Array.isArray(utterances)) {
      utterances.forEach(function (ut) {
        if (!(ut instanceof _utterance2.default)) {
          throw Error('Invalid utterance array.');
        }
      });
      _this._utterances = utterances;
    } else {
      throw Error('Unknown type for utterances.');
    }
    if (typeof face === 'string') {
      _this._faceid = face;
    } else if (face instanceof _face2.default) {
      _this._faceid = face.id;
    } else {
      var lastID = _face2.default.getlastID();

      if (!lastID) {
        lastID = new _face2.default().id;
      }
      _this._faceid = lastID;
    }
    _this._stopped = true;
    return _this;
  }

  /**
   * Get the utterances.
   * @return {Utterance[]}
   */


  _createClass(Clip, [{
    key: 'playAfterDelay',
    value: function playAfterDelay(msg) {
      if (this._stopped) {
        return;
      }
      speechSynthesis.speak(msg);
      // The delay parameter is added to the SpeechSynthesisUtterance object
      if (msg.delay > 0) {

        speechSynthesis.pause();

        setTimeout(function () {
          var event = new Event('start');

          // Trigger the start events again.
          msg.delay = 0;
          msg.dispatchEvent(event);
          speechSynthesis.resume();
        }, msg.delay * 1000);
      }
    }
  }, {
    key: 'playUtterances',
    value: function playUtterances(onPlayUtteranceWithDur, onFinishedClip, onStartClip) {
      var _this2 = this;

      this._stopped = false;
      Clip.playing = this;

      if (speechSynthesis) {
        if (speechSynthesis.speaking) {
          // console.log("Can't play TTS when we are already speaking");
          // if (onFinishedClip) onFinishedClip();
          // return;
          speechSynthesis.cancel();
        }

        this._ssUtterances = [];
        for (var i = 0, l = this._utterances.length; i < l; ++i) {
          var utterance = this._utterances[i];
          var utteranceText = utterance.text;

          if (!utteranceText || utteranceText.length === 0) {
            continue;
          }
          var msg = new SpeechSynthesisUtterance();
          // if you change the rate, you would have to adjust
          var speakingDurationEstimate = utteranceText.length * 50;

          // Chinese needs a different calculation.  Haven't tried other Asian languages.
          if (utteranceText.match(/[\u3400-\u9FBF]/)) {
            speakingDurationEstimate = utteranceText.length * 200;
          }

          msg.rate = utterance.rate;
          msg.pitch = utterance.pitch;
          msg.text = utteranceText;

          // Add delay as paramater for ease of use
          msg.delay = utterance.delay;
          if (this.voice) {
            msg.voice = _voice2.default.getTTSVoice(this.voice);
            if (msg.voice) {
              msg.voiceURI = msg.voice.voiceURI;
            }
          }
          this._ssUtterances.push([msg, speakingDurationEstimate]);
        }
        // Now setup the events
        if (this._ssUtterances.length > 0) {
          this._ssUtterances[0][0].addEventListener('start', function () {
            if (onStartClip) {
              if (_this2._ssUtterances[0][0].delay === 0) {
                onStartClip();
              }
            }
          });
          this._ssUtterances[this._ssUtterances.length - 1][0].addEventListener('end', function () {
            if (onFinishedClip) {
              onFinishedClip();
            }
            _this2._stopped = true;
            Clip.playing = null;
            // Safe to clear this out now.
            _this2._ssUtterances = [];
          });
          for (var _i = 0, _l = this._ssUtterances.length; _i < _l; ++_i) {
            (function (msg, dur) {
              msg.addEventListener('start', function () {
                if (onPlayUtteranceWithDur) {
                  if (msg.delay === 0) {
                    onPlayUtteranceWithDur(dur, _this2.face);
                  }
                }
              });
            })(this._ssUtterances[_i][0], this._ssUtterances[_i][1]);
          }
          for (var _i2 = 0, _l2 = this._ssUtterances.length - 1; _i2 < _l2; ++_i2) {
            (function (msg1, msg2) {
              msg1.addEventListener('end', function () {
                _this2.playAfterDelay(msg2);
              });
            })(this._ssUtterances[_i2][0], this._ssUtterances[_i2 + 1][0]);
          }
          this.playAfterDelay(this._ssUtterances[0][0]);
        }
      }
    }
  }, {
    key: 'updateText',
    value: function updateText(text) {
      var newUtterances = this._splitUtterances(text);

      var curLen = this._utterances.length;
      var newLen = newUtterances.length;

      // Easy case, the number of Utterances didn't change
      if (curLen === newLen) {
        for (var i = 0; i < curLen; ++i) {
          this._utterances[i].text = newUtterances[i].text;
        }
        return;
      }

      // Store the original utterances in a dictionary with text as key.
      var utteranceDict = {};

      for (var _i3 = 0; _i3 < curLen; ++_i3) {
        if (this._utterances[_i3].text in utteranceDict) {
          // handle duplicate utterances with different pitch/rate
          utteranceDict[this._utterances[_i3].text].push(this._utterances[_i3]);
        } else {
          utteranceDict[this._utterances[_i3].text] = [this._utterances[_i3]];
        }
      }

      var defaultRateIndices = [];

      for (var _i4 = 0; _i4 < newLen; ++_i4) {
        var newText = newUtterances[_i4].text;

        if (newText in utteranceDict) {
          newUtterances[_i4] = utteranceDict[newText].shift();
          if (utteranceDict[newText].length === 0) {
            delete utteranceDict[newText];
          }
        } else {
          defaultRateIndices.push(_i4);
        }
      }

      // Match the leftovers
      if (defaultRateIndices.length > 0 && Object.values(utteranceDict).length > 0) {
        var cachedText = newUtterances[defaultRateIndices[0]].text;

        newUtterances[defaultRateIndices[0]] = Object.values(utteranceDict)[0][0];
        newUtterances[defaultRateIndices[0]].text = cachedText;
      }
      this._utterances = newUtterances;
    }

    /**
     * Convert a string containing punctuation-separated utterances into an array
     * of utterances
     * @param {string} text - The string containing punctuation-separated utterances.
     * @return {Utterance[]} An array of Utterance objects
     */

  }, {
    key: '_splitUtterances',
    value: function _splitUtterances(text) {

      // Splitting each utterance up using punctuation is important.  Intra-utterance
      // punctuation will add silence to the tts which looks bad unless the mouth stops moving
      // correctly. Better to split it into separate utterances so play_for_duration will move when
      // talking, and be on frame 0 when not.
      var utterances = [];

      // split everything betwen deliminators [.?,!], but include the deliminator.
      var substrings = text.match(/[^.?,!]+[.?,!]?/g);

      if (substrings) {
        for (var i = 0, l = substrings.length; i < l; ++i) {
          var str = substrings[i].trim();

          // Make sure there is something to say other than the deliminator
          var numpunc = (str.match(/[.?,!]/g) || []).length;

          if (str.length - numpunc > 0) {
            utterances.push(new _utterance2.default(str));
          }
        }
      }
      return utterances;
    }
  }, {
    key: 'utterances',
    get: function get() {
      return this._utterances;
    }
  }, {
    key: 'face',


    /**
     * Gets the Face
     * @return {Face}
     */
    get: function get() {
      return _face2.default.getByID(this._faceid);
    },
    set: function set(face) {
      if (typeof face === 'string') {
        if (_face2.default.getByID(face)) {
          this._faceid = face;
        }
      }
      this._faceid = face.id;
    }
  }, {
    key: 'voice',

    /**
     * Gets the Voice
     * @return {Voice}
     */
    get: function get() {
      return this.face.voice;
    },
    set: function set(voice) {
      this.face.voice = voice;
    }
  }, {
    key: 'text',
    get: function get() {
      var combinedText = '';
      var numUtterances = this._utterances.length;

      for (var i = 0; i < numUtterances; ++i) {
        combinedText = combinedText + this._utterances[i].text + ' ';
      }
      return combinedText.trim();
    }
  }, {
    key: 'sceneid',
    get: function get() {
      return this._sceneid;
    },
    set: function set(id) {
      this._sceneid = id;
    }
  }], [{
    key: 'stop',
    value: function stop() {
      if (Clip.playing) {
        Clip.playing._stopped = true;
      }
      Clip.playing = null;
      if (speechSynthesis) {
        speechSynthesis.cancel();
      }
    }
  }, {
    key: 'getByID',
    value: function getByID(id) {
      return _base2.default.getByTypeAndID('clip', id);
    }
  }, {
    key: 'fromJSON',
    value: function fromJSON(json) {
      if (json['_utterances'] && json['_id']) {
        var utterances = [];

        for (var i = 0, l = json['_utterances'].length; i < l; ++i) {
          var ut = json['_utterances'][i];

          utterances.push(new _utterance2.default(ut._text, ut._rate, ut._pitch, ut._delay));
        }

        var newClip = new Clip(utterances, json['_faceid'], json['_id']);

        newClip._sceneid = json['_sceneid'];

        return newClip;
      }
      return null;
    }
  }]);

  return Clip;
}(_base2.default);

exports.default = Clip;

Clip.playing = null;
module.exports = exports['default'];

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Scene = exports.Clip = exports.Utterance = exports.Voice = exports.Face = exports.Base = undefined;

var _face = __webpack_require__(2);

var _face2 = _interopRequireDefault(_face);

var _voice = __webpack_require__(1);

var _voice2 = _interopRequireDefault(_voice);

var _utterance = __webpack_require__(4);

var _utterance2 = _interopRequireDefault(_utterance);

var _clip = __webpack_require__(5);

var _clip2 = _interopRequireDefault(_clip);

var _scene = __webpack_require__(7);

var _scene2 = _interopRequireDefault(_scene);

var _base = __webpack_require__(0);

var _base2 = _interopRequireDefault(_base);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

exports.Base = _base2.default;
exports.Face = _face2.default;
exports.Voice = _voice2.default;
exports.Utterance = _utterance2.default;
exports.Clip = _clip2.default;
exports.Scene = _scene2.default;

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _clip = __webpack_require__(5);

var _clip2 = _interopRequireDefault(_clip);

var _base = __webpack_require__(0);

var _base2 = _interopRequireDefault(_base);

var _events = __webpack_require__(3);

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Scene = function (_Base) {
  _inherits(Scene, _Base);

  function Scene(clipIDs, id) {
    _classCallCheck(this, Scene);

    var _this = _possibleConstructorReturn(this, (Scene.__proto__ || Object.getPrototypeOf(Scene)).call(this, 'scene', id));

    if (clipIDs === undefined) {
      clipIDs = [];
    }
    if (Array.isArray(clipIDs)) {
      clipIDs.forEach(function (id) {
        if (typeof id !== 'string') {
          throw Error('Invalid clip id.');
        }
      });
    } else {
      throw Error('clipIDs were not an array.');
    }
    _this._clips = clipIDs;
    _this._stopped = true;
    _this._name = null;
    return _this;
  }

  _createClass(Scene, [{
    key: 'deleteClip',
    value: function deleteClip(id) {
      var i = this._clips.indexOf(id);

      if (i > -1) {
        var clip = _clip2.default.getByID(id);

        this._clips.splice(i, 1);
        if (clip) {
          clip.delete();
        }
      }
    }
  }, {
    key: 'addClip',
    value: function addClip(id) {
      var clip = _clip2.default.getByID(id);

      if (clip) {
        this._clips.push(id);
        clip.sceneid = this._id;
      }
    }
  }, {
    key: 'delete',
    value: function _delete() {
      this._clips.forEach(function (clipid) {
        delete _base2.default.objects['clip'][clipid];
      });
      _get(Scene.prototype.__proto__ || Object.getPrototypeOf(Scene.prototype), 'delete', this).call(this);
    }
  }, {
    key: 'playClips',
    value: function playClips(startIndex) {
      var _this2 = this;

      Scene.playing = this;
      if (startIndex === undefined) {
        startIndex = 0;
      }
      var numclips = this._clips.length;

      this._stopped = false;
      if (numclips <= startIndex) {
        return;
      }
      var onPlayUtteranceWithDur = function onPlayUtteranceWithDur(dur, face) {
        Scene.eventEmitter.emit('utterancestart', { 'duration': dur, 'face': face });
      };

      var that = this;
      var onPlayNextClip = null;
      var playClipAtIndex = function playClipAtIndex(clipIndex) {
        if (_this2._stopped) {
          return;
        }
        var clip = _clip2.default.getByID(that.clipIDs[clipIndex]);

        if (clip) {
          Scene.eventEmitter.emit('clipstart', clip);
          clip.playUtterances(onPlayUtteranceWithDur, onPlayNextClip);
        } else {
          throw new Error('unknown clip ID: ' + that.clipIDs[clipIndex]);
        }
      };
      var clipIndex = startIndex;

      onPlayNextClip = function onPlayNextClip() {
        if (clipIndex === numclips - 1) {
          Scene.eventEmitter.emit('scenefinished');
          Scene.playing = null;
          return;
        }
        Scene.eventEmitter.emit('clipfinished', clipIndex);
        clipIndex += 1;
        playClipAtIndex(clipIndex);
      };
      playClipAtIndex(startIndex);
    }
  }, {
    key: 'clipIDs',
    get: function get() {
      return this._clips;
    }
  }, {
    key: 'name',
    get: function get() {
      return this._name;
    },
    set: function set(name) {
      this._name = name;
    }
  }, {
    key: 'text',
    get: function get() {
      var text = '';

      this._clips.forEach(function (clipid) {
        var clip = _clip2.default.getByID(clipid);

        if (!clip) {
          throw new Error('Invalid clip ID.');
        }
        text += clip.text + ' ';
      });
      return text.trim();
    }
  }, {
    key: 'clips',
    get: function get() {
      var clips = [];

      this._clips.forEach(function (clipid) {
        var clip = _clip2.default.getByID(clipid);

        if (!clip) {
          throw new Error('Invalid clip ID.');
        }
        clips.push(clip);
      });
      return clips;
    }
  }], [{
    key: 'stop',
    value: function stop() {
      if (Scene.playing) {
        Scene.playing._stopped = true;
      }
      Scene.playing = null;
      _clip2.default.stop();
    }
  }, {
    key: 'fromTextAndFace',
    value: function fromTextAndFace(text, face) {
      var newClip = new _clip2.default(text, face);

      return new Scene([newClip.id]);
    }
  }, {
    key: 'getByID',
    value: function getByID(id) {
      return _base2.default.getByTypeAndID('scene', id);
    }
  }, {
    key: 'fromJSON',
    value: function fromJSON(json) {
      if (json['_clips'] && json['_id']) {
        var scene = new Scene(json['_clips'], json['_id']);
        var name = json['_name'];

        if (name) {
          scene.name = name;
        }
        return scene;
      }
      return null;
    }
  }]);

  return Scene;
}(_base2.default);

exports.default = Scene;

Scene.playing = null;
Scene.eventEmitter = new _events2.default();
_base2.default.objects['scene'] = {};
module.exports = exports['default'];

/***/ })
/******/ ]);
});
//# sourceMappingURL=talkr-tts.js.map