@twilio/voice-sdk
Version:
Twilio's JavaScript Voice SDK
565 lines • 42.8 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @packageDocumentation
* @module Voice
*/
var events_1 = require("events");
var device_1 = require("./device");
var errors_1 = require("./errors");
var log_1 = require("./log");
var outputdevicecollection_1 = require("./outputdevicecollection");
var mediadeviceinfo_1 = require("./shims/mediadeviceinfo");
var mediadevices_1 = require("./shims/mediadevices");
var util_1 = require("./util");
/**
* Aliases for audio kinds, used for labelling.
* @private
*/
var kindAliases = {
audioinput: 'Audio Input',
audiooutput: 'Audio Output',
};
/**
* Provides input and output audio-based functionality in one convenient class.
* @publicapi
*/
var AudioHelper = /** @class */ (function (_super) {
__extends(AudioHelper, _super);
/**
* @constructor
* @private
* @param onActiveOutputsChanged - A callback to be called when the user changes the active output devices.
* @param onActiveInputChanged - A callback to be called when the user changes the active input device.
* @param getUserMedia - The getUserMedia method to use.
* @param [options]
*/
function AudioHelper(onActiveOutputsChanged, onActiveInputChanged, getUserMedia, options) {
var _a;
var _this = _super.call(this) || this;
/**
* A Map of all audio input devices currently available to the browser by their device ID.
*/
_this.availableInputDevices = new Map();
/**
* A Map of all audio output devices currently available to the browser by their device ID.
*/
_this.availableOutputDevices = new Map();
/**
* The currently set audio constraints set by setAudioConstraints().
*/
_this._audioConstraints = null;
/**
* Whether each sound is enabled.
*/
_this._enabledSounds = (_a = {},
_a[device_1.default.SoundName.Disconnect] = true,
_a[device_1.default.SoundName.Incoming] = true,
_a[device_1.default.SoundName.Outgoing] = true,
_a);
/**
* The current input device.
*/
_this._inputDevice = null;
/**
* The current input stream.
*/
_this._inputStream = null;
/**
* Whether the {@link AudioHelper} is currently polling the input stream's volume.
*/
_this._isPollingInputVolume = false;
/**
* An instance of Logger to use.
*/
_this._log = log_1.default.getInstance();
/**
* A record of unknown devices (Devices without labels)
*/
_this._unknownDeviceIndexes = {
audioinput: {},
audiooutput: {},
};
/**
* Remove an input device from inputs
* @param lostDevice
* @returns Whether the device was active
*/
_this._removeLostInput = function (lostDevice) {
if (!_this.inputDevice || _this.inputDevice.deviceId !== lostDevice.deviceId) {
return false;
}
_this._replaceStream(null);
_this._inputDevice = null;
_this._maybeStopPollingVolume();
var defaultDevice = _this.availableInputDevices.get('default')
|| Array.from(_this.availableInputDevices.values())[0];
if (defaultDevice) {
_this.setInputDevice(defaultDevice.deviceId);
}
return true;
};
/**
* Remove an input device from outputs
* @param lostDevice
* @returns Whether the device was active
*/
_this._removeLostOutput = function (lostDevice) {
var wasSpeakerLost = _this.speakerDevices.delete(lostDevice);
var wasRingtoneLost = _this.ringtoneDevices.delete(lostDevice);
return wasSpeakerLost || wasRingtoneLost;
};
/**
* Update the available input and output devices
*/
_this._updateAvailableDevices = function () {
if (!_this._mediaDevices || !_this._enumerateDevices) {
return Promise.reject('Enumeration not supported');
}
return _this._enumerateDevices().then(function (devices) {
_this._updateDevices(devices.filter(function (d) { return d.kind === 'audiooutput'; }), _this.availableOutputDevices, _this._removeLostOutput);
_this._updateDevices(devices.filter(function (d) { return d.kind === 'audioinput'; }), _this.availableInputDevices, _this._removeLostInput);
var defaultDevice = _this.availableOutputDevices.get('default')
|| Array.from(_this.availableOutputDevices.values())[0];
[_this.speakerDevices, _this.ringtoneDevices].forEach(function (outputDevices) {
if (!outputDevices.get().size && _this.availableOutputDevices.size && _this.isOutputSelectionSupported) {
outputDevices.set(defaultDevice.deviceId)
.catch(function (reason) {
_this._log.warn("Unable to set audio output devices. " + reason);
});
}
});
});
};
options = Object.assign({
AudioContext: typeof AudioContext !== 'undefined' && AudioContext,
setSinkId: typeof HTMLAudioElement !== 'undefined' && HTMLAudioElement.prototype.setSinkId,
}, options);
_this._getUserMedia = getUserMedia;
_this._mediaDevices = options.mediaDevices || mediadevices_1.default();
_this._onActiveInputChanged = onActiveInputChanged;
_this._enumerateDevices = typeof options.enumerateDevices === 'function'
? options.enumerateDevices
: _this._mediaDevices && _this._mediaDevices.enumerateDevices;
var isAudioContextSupported = !!(options.AudioContext || options.audioContext);
var isEnumerationSupported = !!_this._enumerateDevices;
if (options.enabledSounds) {
_this._enabledSounds = options.enabledSounds;
}
var isSetSinkSupported = typeof options.setSinkId === 'function';
_this.isOutputSelectionSupported = isEnumerationSupported && isSetSinkSupported;
_this.isVolumeSupported = isAudioContextSupported;
if (_this.isVolumeSupported) {
_this._audioContext = options.audioContext || options.AudioContext && new options.AudioContext();
if (_this._audioContext) {
_this._inputVolumeAnalyser = _this._audioContext.createAnalyser();
_this._inputVolumeAnalyser.fftSize = 32;
_this._inputVolumeAnalyser.smoothingTimeConstant = 0.3;
}
}
_this.ringtoneDevices = new outputdevicecollection_1.default('ringtone', _this.availableOutputDevices, onActiveOutputsChanged, _this.isOutputSelectionSupported);
_this.speakerDevices = new outputdevicecollection_1.default('speaker', _this.availableOutputDevices, onActiveOutputsChanged, _this.isOutputSelectionSupported);
_this.addListener('newListener', function (eventName) {
if (eventName === 'inputVolume') {
_this._maybeStartPollingVolume();
}
});
_this.addListener('removeListener', function (eventName) {
if (eventName === 'inputVolume') {
_this._maybeStopPollingVolume();
}
});
_this.once('newListener', function () {
// NOTE (rrowland): Ideally we would only check isEnumerationSupported here, but
// in at least one browser version (Tested in FF48) enumerateDevices actually
// returns bad data for the listed devices. Instead, we check for
// isOutputSelectionSupported to avoid these quirks that may negatively affect customers.
if (!_this.isOutputSelectionSupported) {
_this._log.warn('Warning: This browser does not support audio output selection.');
}
if (!_this.isVolumeSupported) {
_this._log.warn("Warning: This browser does not support Twilio's volume indicator feature.");
}
});
if (isEnumerationSupported) {
_this._initializeEnumeration();
}
return _this;
}
Object.defineProperty(AudioHelper.prototype, "audioConstraints", {
/**
* The currently set audio constraints set by setAudioConstraints(). Starts as null.
*/
get: function () { return this._audioConstraints; },
enumerable: false,
configurable: true
});
Object.defineProperty(AudioHelper.prototype, "inputDevice", {
/**
* The active input device. Having no inputDevice specified by `setInputDevice()`
* will disable input selection related functionality.
*/
get: function () { return this._inputDevice; },
enumerable: false,
configurable: true
});
Object.defineProperty(AudioHelper.prototype, "inputStream", {
/**
* The current input stream.
*/
get: function () { return this._inputStream; },
enumerable: false,
configurable: true
});
/**
* Current state of the enabled sounds
* @private
*/
AudioHelper.prototype._getEnabledSounds = function () {
return this._enabledSounds;
};
/**
* Start polling volume if it's supported and there's an input stream to poll.
* @private
*/
AudioHelper.prototype._maybeStartPollingVolume = function () {
var _this = this;
if (!this.isVolumeSupported || !this._inputStream) {
return;
}
this._updateVolumeSource();
if (this._isPollingInputVolume || !this._inputVolumeAnalyser) {
return;
}
var bufferLength = this._inputVolumeAnalyser.frequencyBinCount;
var buffer = new Uint8Array(bufferLength);
this._isPollingInputVolume = true;
var emitVolume = function () {
if (!_this._isPollingInputVolume) {
return;
}
if (_this._inputVolumeAnalyser) {
_this._inputVolumeAnalyser.getByteFrequencyData(buffer);
var inputVolume = util_1.average(buffer);
_this.emit('inputVolume', inputVolume / 255);
}
requestAnimationFrame(emitVolume);
};
requestAnimationFrame(emitVolume);
};
/**
* Stop polling volume if it's currently polling and there are no listeners.
* @private
*/
AudioHelper.prototype._maybeStopPollingVolume = function () {
if (!this.isVolumeSupported) {
return;
}
if (!this._isPollingInputVolume || (this._inputStream && this.listenerCount('inputVolume'))) {
return;
}
if (this._inputVolumeSource) {
this._inputVolumeSource.disconnect();
delete this._inputVolumeSource;
}
this._isPollingInputVolume = false;
};
/**
* Unbind the listeners from mediaDevices.
* @private
*/
AudioHelper.prototype._unbind = function () {
if (!this._mediaDevices || !this._enumerateDevices) {
throw new errors_1.NotSupportedError('Enumeration is not supported');
}
if (this._mediaDevices.removeEventListener) {
this._mediaDevices.removeEventListener('devicechange', this._updateAvailableDevices);
this._mediaDevices.removeEventListener('deviceinfochange', this._updateAvailableDevices);
}
};
/**
* Enable or disable the disconnect sound.
* @param doEnable Passing `true` will enable the sound and `false` will disable the sound.
* Not passing this parameter will not alter the enable-status of the sound.
* @returns The enable-status of the sound.
*/
AudioHelper.prototype.disconnect = function (doEnable) {
return this._maybeEnableSound(device_1.default.SoundName.Disconnect, doEnable);
};
/**
* Enable or disable the incoming sound.
* @param doEnable Passing `true` will enable the sound and `false` will disable the sound.
* Not passing this parameter will not alter the enable-status of the sound.
* @returns The enable-status of the sound.
*/
AudioHelper.prototype.incoming = function (doEnable) {
return this._maybeEnableSound(device_1.default.SoundName.Incoming, doEnable);
};
/**
* Enable or disable the outgoing sound.
* @param doEnable Passing `true` will enable the sound and `false` will disable the sound.
* Not passing this parameter will not alter the enable-status of the sound.
* @returns The enable-status of the sound.
*/
AudioHelper.prototype.outgoing = function (doEnable) {
return this._maybeEnableSound(device_1.default.SoundName.Outgoing, doEnable);
};
/**
* Set the MediaTrackConstraints to be applied on every getUserMedia call for new input
* device audio. Any deviceId specified here will be ignored. Instead, device IDs should
* be specified using {@link AudioHelper#setInputDevice}. The returned Promise resolves
* when the media is successfully reacquired, or immediately if no input device is set.
* @param audioConstraints - The MediaTrackConstraints to apply.
*/
AudioHelper.prototype.setAudioConstraints = function (audioConstraints) {
this._audioConstraints = Object.assign({}, audioConstraints);
delete this._audioConstraints.deviceId;
return this.inputDevice
? this._setInputDevice(this.inputDevice.deviceId, true)
: Promise.resolve();
};
/**
* Replace the current input device with a new device by ID.
* @param deviceId - An ID of a device to replace the existing
* input device with.
*/
AudioHelper.prototype.setInputDevice = function (deviceId) {
return !util_1.isFirefox()
? this._setInputDevice(deviceId, false)
: Promise.reject(new errors_1.NotSupportedError('Firefox does not currently support opening multiple ' +
'audio input tracks simultaneously, even across different tabs. As a result, ' +
'Device.audio.setInputDevice is disabled on Firefox until support is added.\n' +
'Related BugZilla thread: https://bugzilla.mozilla.org/show_bug.cgi?id=1299324'));
};
/**
* Unset the MediaTrackConstraints to be applied on every getUserMedia call for new input
* device audio. The returned Promise resolves when the media is successfully reacquired,
* or immediately if no input device is set.
*/
AudioHelper.prototype.unsetAudioConstraints = function () {
this._audioConstraints = null;
return this.inputDevice
? this._setInputDevice(this.inputDevice.deviceId, true)
: Promise.resolve();
};
/**
* Unset the input device, stopping the tracks. This should only be called when not in a connection, and
* will not allow removal of the input device during a live call.
*/
AudioHelper.prototype.unsetInputDevice = function () {
var _this = this;
if (!this.inputDevice) {
return Promise.resolve();
}
return this._onActiveInputChanged(null).then(function () {
_this._replaceStream(null);
_this._inputDevice = null;
_this._maybeStopPollingVolume();
});
};
/**
* Get the index of an un-labeled Device.
* @param mediaDeviceInfo
* @returns The index of the passed MediaDeviceInfo
*/
AudioHelper.prototype._getUnknownDeviceIndex = function (mediaDeviceInfo) {
var id = mediaDeviceInfo.deviceId;
var kind = mediaDeviceInfo.kind;
var index = this._unknownDeviceIndexes[kind][id];
if (!index) {
index = Object.keys(this._unknownDeviceIndexes[kind]).length + 1;
this._unknownDeviceIndexes[kind][id] = index;
}
return index;
};
/**
* Initialize output device enumeration.
*/
AudioHelper.prototype._initializeEnumeration = function () {
var _this = this;
if (!this._mediaDevices || !this._enumerateDevices) {
throw new errors_1.NotSupportedError('Enumeration is not supported');
}
if (this._mediaDevices.addEventListener) {
this._mediaDevices.addEventListener('devicechange', this._updateAvailableDevices);
this._mediaDevices.addEventListener('deviceinfochange', this._updateAvailableDevices);
}
this._updateAvailableDevices().then(function () {
if (!_this.isOutputSelectionSupported) {
return;
}
Promise.all([
_this.speakerDevices.set('default'),
_this.ringtoneDevices.set('default'),
]).catch(function (reason) {
_this._log.warn("Warning: Unable to set audio output devices. " + reason);
});
});
};
/**
* Set whether the sound is enabled or not
* @param soundName
* @param doEnable
* @returns Whether the sound is enabled or not
*/
AudioHelper.prototype._maybeEnableSound = function (soundName, doEnable) {
if (typeof doEnable !== 'undefined') {
this._enabledSounds[soundName] = doEnable;
}
return this._enabledSounds[soundName];
};
/**
* Stop the tracks on the current input stream before replacing it with the passed stream.
* @param stream - The new stream
*/
AudioHelper.prototype._replaceStream = function (stream) {
if (this._inputStream) {
this._inputStream.getTracks().forEach(function (track) {
track.stop();
});
}
this._inputStream = stream;
};
/**
* Replace the current input device with a new device by ID.
* @param deviceId - An ID of a device to replace the existing
* input device with.
* @param forceGetUserMedia - If true, getUserMedia will be called even if
* the specified device is already active.
*/
AudioHelper.prototype._setInputDevice = function (deviceId, forceGetUserMedia) {
var _this = this;
if (typeof deviceId !== 'string') {
return Promise.reject(new errors_1.InvalidArgumentError('Must specify the device to set'));
}
var device = this.availableInputDevices.get(deviceId);
if (!device) {
return Promise.reject(new errors_1.InvalidArgumentError("Device not found: " + deviceId));
}
if (this._inputDevice && this._inputDevice.deviceId === deviceId && this._inputStream) {
if (!forceGetUserMedia) {
return Promise.resolve();
}
// If the currently active track is still in readyState `live`, gUM may return the same track
// rather than returning a fresh track.
this._inputStream.getTracks().forEach(function (track) {
track.stop();
});
}
var constraints = { audio: Object.assign({ deviceId: { exact: deviceId } }, this.audioConstraints) };
return this._getUserMedia(constraints).then(function (stream) {
return _this._onActiveInputChanged(stream).then(function () {
_this._replaceStream(stream);
_this._inputDevice = device;
_this._maybeStartPollingVolume();
});
});
};
/**
* Update a set of devices.
* @param updatedDevices - An updated list of available Devices
* @param availableDevices - The previous list of available Devices
* @param removeLostDevice - The method to call if a previously available Device is
* no longer available.
*/
AudioHelper.prototype._updateDevices = function (updatedDevices, availableDevices, removeLostDevice) {
var _this = this;
var updatedDeviceIds = updatedDevices.map(function (d) { return d.deviceId; });
var knownDeviceIds = Array.from(availableDevices.values()).map(function (d) { return d.deviceId; });
var lostActiveDevices = [];
// Remove lost devices
var lostDeviceIds = util_1.difference(knownDeviceIds, updatedDeviceIds);
lostDeviceIds.forEach(function (lostDeviceId) {
var lostDevice = availableDevices.get(lostDeviceId);
if (lostDevice) {
availableDevices.delete(lostDeviceId);
if (removeLostDevice(lostDevice)) {
lostActiveDevices.push(lostDevice);
}
}
});
// Add any new devices, or devices with updated labels
var deviceChanged = false;
updatedDevices.forEach(function (newDevice) {
var existingDevice = availableDevices.get(newDevice.deviceId);
var newMediaDeviceInfo = _this._wrapMediaDeviceInfo(newDevice);
if (!existingDevice || existingDevice.label !== newMediaDeviceInfo.label) {
availableDevices.set(newDevice.deviceId, newMediaDeviceInfo);
deviceChanged = true;
}
});
if (deviceChanged || lostDeviceIds.length) {
// Force a new gUM in case the underlying tracks of the active stream have changed. One
// reason this might happen is when `default` is selected and set to a USB device,
// then that device is unplugged or plugged back in. We can't check for the 'ended'
// event or readyState because it is asynchronous and may take upwards of 5 seconds,
// in my testing. (rrowland)
if (this.inputDevice !== null && this.inputDevice.deviceId === 'default') {
this._log.warn("Calling getUserMedia after device change to ensure that the tracks of the active device (default) have not gone stale.");
this._setInputDevice(this.inputDevice.deviceId, true);
}
this.emit('deviceChange', lostActiveDevices);
}
};
/**
* Disconnect the old input volume source, and create and connect a new one with the current
* input stream.
*/
AudioHelper.prototype._updateVolumeSource = function () {
if (!this._inputStream || !this._audioContext || !this._inputVolumeAnalyser) {
return;
}
if (this._inputVolumeSource) {
this._inputVolumeSource.disconnect();
}
try {
this._inputVolumeSource = this._audioContext.createMediaStreamSource(this._inputStream);
this._inputVolumeSource.connect(this._inputVolumeAnalyser);
}
catch (ex) {
this._log.warn('Unable to update volume source', ex);
delete this._inputVolumeSource;
}
};
/**
* Convert a MediaDeviceInfo to a IMediaDeviceInfoShim.
* @param mediaDeviceInfo - The info to convert
* @returns The converted shim
*/
AudioHelper.prototype._wrapMediaDeviceInfo = function (mediaDeviceInfo) {
var options = {
deviceId: mediaDeviceInfo.deviceId,
groupId: mediaDeviceInfo.groupId,
kind: mediaDeviceInfo.kind,
label: mediaDeviceInfo.label,
};
if (!options.label) {
if (options.deviceId === 'default') {
options.label = 'Default';
}
else {
var index = this._getUnknownDeviceIndex(mediaDeviceInfo);
options.label = "Unknown " + kindAliases[options.kind] + " Device " + index;
}
}
return new mediadeviceinfo_1.default(options);
};
return AudioHelper;
}(events_1.EventEmitter));
(function (AudioHelper) {
})(AudioHelper || (AudioHelper = {}));
exports.default = AudioHelper;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXVkaW9oZWxwZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvdHdpbGlvL2F1ZGlvaGVscGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBOzs7R0FHRztBQUNILGlDQUFzQztBQUN0QyxtQ0FBOEI7QUFDOUIsbUNBQW1FO0FBQ25FLDZCQUF3QjtBQUN4QixtRUFBOEQ7QUFDOUQsMkRBQTBEO0FBQzFELHFEQUEyRDtBQUMzRCwrQkFBd0Q7QUFFeEQ7OztHQUdHO0FBQ0gsSUFBTSxXQUFXLEdBQTJCO0lBQzFDLFVBQVUsRUFBRSxhQUFhO0lBQ3pCLFdBQVcsRUFBRSxjQUFjO0NBQzVCLENBQUM7QUFFRjs7O0dBR0c7QUFDSDtJQUEwQiwrQkFBWTtJQW9JcEM7Ozs7Ozs7T0FPRztJQUNILHFCQUFZLHNCQUE0RixFQUM1RixvQkFBbUUsRUFDbkUsWUFBMkUsRUFDM0UsT0FBNkI7O1FBSHpDLFlBSUUsaUJBQU8sU0FvRVI7UUE5TUQ7O1dBRUc7UUFDSCwyQkFBcUIsR0FBaUMsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUVoRTs7V0FFRztRQUNILDRCQUFzQixHQUFpQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBeUNqRTs7V0FFRztRQUNLLHVCQUFpQixHQUFpQyxJQUFJLENBQUM7UUFPL0Q7O1dBRUc7UUFDSyxvQkFBYztZQUNwQixHQUFDLGdCQUFNLENBQUMsU0FBUyxDQUFDLFVBQVUsSUFBRyxJQUFJO1lBQ25DLEdBQUMsZ0JBQU0sQ0FBQyxTQUFTLENBQUMsUUFBUSxJQUFHLElBQUk7WUFDakMsR0FBQyxnQkFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLElBQUcsSUFBSTtnQkFDakM7UUFZRjs7V0FFRztRQUNLLGtCQUFZLEdBQTJCLElBQUksQ0FBQztRQUVwRDs7V0FFRztRQUNLLGtCQUFZLEdBQXVCLElBQUksQ0FBQztRQVloRDs7V0FFRztRQUNLLDJCQUFxQixHQUFZLEtBQUssQ0FBQztRQUUvQzs7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