import type { VoiceFocusFetchBehavior , VoiceFocusPaths } from '../../libs/voicefocus/types' ; import { Logger as VoiceFocusLogger } from '../../libs/voicefocus/types' ; import type { NodeArguments } from '../../libs/voicefocus/voicefocus' ; import { VoiceFocus } from '../../libs/voicefocus/voicefocus' ; import type Device from '../devicecontroller/Device' ; import Logger from '../logger/Logger' ; import { isIFramed } from '../utils/Utils' ; import Versioning from '../versioning/Versioning' ; import type AssetSpec from './AssetSpec' ; import LoggerAdapter from './LoggerAdapter' ; import type VoiceFocusConfig from './VoiceFocusConfig' ; import type VoiceFocusDeviceOptions from './VoiceFocusDeviceOptions' ; import type VoiceFocusSpec from './VoiceFocusSpec' ; import VoiceFocusTransformDevice from './VoiceFocusTransformDevice' ; import VoiceFocusTransformDeviceDelegate from './VoiceFocusTransformDeviceDelegate' ; /**
 * `VoiceFocusDeviceTransformer` is used to create  VoiceFocusTransformDevice * that apply Amazon Voice Focus noise suppression to audio input.
 *
 * This transformer captures relevant configuration. You should check for support, initialize,
 * and then create a device as follows:
 *
 * ```typescript
 * const deviceID = null;
 *
 * // This check for support is cheap and quick, and should be used to gate use
 * // of this feature.
 * if (!(await VoiceFocusDeviceTransformer.isSupported()) {
 *   console.log('Amazon Voice Focus not supported in this browser.');
 *   return deviceID;
 * }
 *
 * let transformer: VoiceFocusDeviceTransformer;
 * try {
 *   // This operation can fail in ways that do not indicate no support,
 *   // but do indicate an inability to apply Amazon Voice Focus. Trying again
 *   // might succeed.
 *   transformer = await VoiceFocusDeviceTransformer.create({});
 * } catch (e) {
 *   // Something went wrong.
 *   console.log('Unable to instantiate Amazon Voice Focus.');
 *   return deviceID;
 * }
 *
 * if (!transformer.isSupported()) {
 *   // The transformer will fall through, but your UI might care.
 *   console.log('Amazon Voice Focus not supported in this browser.');
 * }
 *
 * return await transformer.createTransformDevice(deviceID);
 * ```
  export class VoiceFocusDeviceTransformer { private logger : Logger ; private vfLogger : VoiceFocusLogger ; private preload : boolean ; private configuration : Promise < VoiceFocusConfig > ; private fetchBehavior : VoiceFocusFetchBehavior ; private pendingVoiceFocus : Promise < [ VoiceFocus , VoiceFocusTransformDeviceDelegate ] > ; private supported = true ; /**
     * Quickly check whether Amazon Voice Focus is supported on this platform.
     *
     * This will return `false` if key technologies are absent. A value of `true` does not
     * necessarily mean that adding Amazon Voice Focus will succeed: it is still possible that the
     * configuration of the page or the CPU speed of the device are limiting factors.
     *
     *  VoiceFocusDeviceTransformer . create will return an instance whose
     * `isSupported` method more accurately reflects whether Amazon Voice Focus
     * is supported in the current environment.
     *
     * This method will only reject if you provide invalid inputs.
     *
     *  param spec An optional asset group and URL paths to use when fetching. You can pass
     *             a complete  VoiceFocusSpec here for convenience, matching the signature of  VoiceFocusDeviceTransformer . create .
     *  param options static isSupported ( spec ? , options ? ) { const fetchBehavior = VoiceFocusDeviceTransformer . defaultFetchBehavior ( ) ; const logger = options ?. logger ? new LoggerAdapter ( options . logger ) : undefined ; const opts = { fetchBehavior , logger , } ; if ( isIFramed ( ) ) { if ( options ?. allowIFrame === false ) { options ?. logger ?. error ( 'Amazon Voice Focus support check inside iframe: not supported.' ) ; return Promise . resolve ( false ) ; } else { options ?. logger ?. warn ( 'Amazon Voice Focus support check inside iframe: not recommended.' ) ; } } return VoiceFocus . isSupported ( VoiceFocusDeviceTransformer . augmentSpec ( spec ) , opts ) ; } param spec A definition of how you want Amazon Voice Focus to behave. See the declaration of
     *              VoiceFocusSpec for details.
     *  param options static async create ( spec = { } , options = { } , config ? , createMeetingResponse ? , createAttendeeResponse ? ) { if ( createMeetingResponse ) { if ( createMeetingResponse . Meeting . Meeting ) { createMeetingResponse = createMeetingResponse . Meeting ; } } const meetingFeaturesAllowsES = createMeetingResponse ?. Meeting ?. MeetingFeatures ?. Audio ?. EchoReduction === 'AVAILABLE' ; const forbiddenConfig = config && config . supported === true && config . model . name === 'ns_es' && ! meetingFeaturesAllowsES ; const forbiddenSpec = spec . name === 'ns_es' && ! meetingFeaturesAllowsES ; if ( forbiddenConfig || forbiddenSpec ) { throw new Error ( 'Echo Reduction requested but not enabled.' ) ; } const transformer = new VoiceFocusDeviceTransformer ( spec , options , config ) ; await transformer . init ( ) ; return transformer ; } /**
     * Given a spec and options, perform the configuration work that is
     * ordinarily performed during creation of a transformer.
     *
     * The computed configuration is not portable between devices or sessions,
     * but is useful for 'moving' transformers between windows.
     *
     * Pass the returned configuration as the third argument to a call to
     *  VoiceFocusDeviceTransformer . create with the matching spec.
      static async configure ( spec = { } , options = { } ) { const transformer = new VoiceFocusDeviceTransformer ( spec , options , undefined ) ; return transformer . configure ( true ) ; } /**
     * Return the computed configuration for this transformer.
     */ getConfiguration ( ) { return this . configuration ; } /**
     * Return whether this transformer is able to function in this environment.
     * If not, calls to
     *  VoiceFocusDeviceTransformer . createTransformDevice * will pass through an unmodified device.
      isSupported ( ) { return this . supported ; } /**
     * Apply Amazon Voice Focus to the selected  Device .
     *
     * If this is a stream, it should be one that does not include other noise suppression features,
     * and you should consider whether to disable automatic gain control (AGC) on the stream, because
     * it can interact with noise suppression.
     *
     *  returns a device promise. This will always resolve to either a
     *           VoiceFocusTransformDevice or undefined; it will never reject.
      async createTransformDevice ( device , nodeOptions ? ) { if ( ! this . supported ) { return undefined ; } try { const preload = true ; const [ vf , delegate ] = await this . allocateVoiceFocus ( preload ) ; const options = { ... nodeOptions , es : this . spec . name === 'ns_es' } ; return new VoiceFocusTransformDevice ( device , vf , delegate , options ) ; } catch ( e ) { return undefined ; } } private constructor ( private spec , { preload = true , logger , fetchBehavior = VoiceFocusDeviceTransformer . defaultFetchBehavior ( ) , } , config ) { this . logger = logger ; this . vfLogger = logger ? new LoggerAdapter ( logger ) : undefined ; this . preload = preload ; this . fetchBehavior = fetchBehavior ; this . spec = VoiceFocusDeviceTransformer . augmentSpec ( this . spec ) ; if ( config ) { this . configuration = Promise . resolve ( config ) ; } } private static augmentSpec ( spec ) { if ( ! spec || ( ! spec . assetGroup && ! spec . revisionID ) ) { return { ... spec , assetGroup : VoiceFocusDeviceTransformer . currentSDKAssetGroup ( ) , } ; } return spec ; } private async configure ( preResolve = false ) { const options = { fetchBehavior : this . fetchBehavior , preResolve , logger : this . vfLogger , } ; return VoiceFocus . configure ( this . spec , options ) ; } private async init ( ) { if ( ! this . configuration ) { this . configuration = this . configure ( ) ; } const config = await this . configuration ; if ( ! config . supported ) { this . supported = false ; return ; } this . pendingVoiceFocus = this . createVoiceFocus ( config , this . preload ) ; try { await this . pendingVoiceFocus ; } catch ( e ) { this . logger ?. error ( `Unable to initialize Amazon Voice Focus: ${ e }` ) ; this . supported = false ; } } private async createVoiceFocus ( config , preload ) { const delegate = new VoiceFocusTransformDeviceDelegate ( ) ; const vf = await VoiceFocus . init ( config , { delegate , preload , logger : this . vfLogger } ) ; return [ vf , delegate ] ; } private async allocateVoiceFocus ( preload ) { if ( ! this . supported ) { throw new Error ( 'Not supported.' ) ; } if ( this . pendingVoiceFocus ) { const vf = this . pendingVoiceFocus ; this . pendingVoiceFocus = undefined ; return vf ; } return this . createVoiceFocus ( await this . configuration , preload ) ; } private static majorVersion ( ) { return Versioning . sdkVersion . match ( /^[1-9][0-9]*\.(?:0|[1-9][0-9]*)/ ) [ 0 ] ; } private static majorMinorVersion ( ) { return Versioning . sdkVersion . match ( /^[1-9][0-9]*\.(?:0|(?:[1-9][0-9]*))\.(?:0|[1-9][0-9]*)/ ) [ 0 ] ; } private static currentSDKAssetGroup ( ) { const v = this . majorVersion ( ) ; if ( ! v ) { return `stable-v1` ; } return `sdk-${ v }` ; } private static defaultFetchBehavior ( ) { const version = VoiceFocusDeviceTransformer . majorMinorVersion ( ) || 'unknown' ; const ua = Versioning . sdkUserAgentLowResolution ; return { escapedQueryString : `sdk=${ encodeURIComponent ( version ) }&ua=${ encodeURIComponent ( ua ) }` , } ; } } export default VoiceFocusDeviceTransformer ;