import { DefaultBrowserBehavior , DefaultDeviceController , MeetingSession , VoiceFocusDeviceTransformer , VoiceFocusSpec , VoiceFocusTransformDevice } from "amazon-chime-sdk-js" ; export const NoiseSuppressionType = { None : "None" , c100 : "c100" , c50 : "c50" , c20 : "c20" , c10 : "c10" , auto : "auto" , } as const ; export type NoiseSuppressionType = typeof NoiseSuppressionType [ keyof typeof NoiseSuppressionType ] ; export class AudioInputDeviceSetting { private meetingSession : MeetingSession ; private voiceFocusDeviceTransformer : VoiceFocusDeviceTransformer | null = null ; private voiceFocusTransformDevice : VoiceFocusTransformDevice | null = null ; private midNode : AudioNode | null = null ; private mixNode : AudioNode | null = null ; private mixSoundeVolume : number = 0.3 ; private mixGainNode : GainNode | null = null ; private outputNode : MediaStreamAudioDestinationNode | null = null ; private deviceMediaStream : MediaStream | null = null ; audioInput : MediaStream | string | null = null ; audioInputEnable : boolean = true ; audioSuppressionEnable : boolean = true ; voiceFocusSpec : VoiceFocusSpec = { variant : "c10" } ; audioInputForRecord : MediaStream | null = null ; constructor ( meetingSession ) { this . meetingSession = meetingSession ; } private setAudioInputCommon = async ( device , enable , suppression , suppressionSpec ? ) => { if ( this . deviceMediaStream ) { this . deviceMediaStream . getAudioTracks ( ) . forEach ( ( x ) => { x . stop ( ) ; } ) ; } this . deviceMediaStream = null ; if ( device === null || enable === false ) { console . log ( "[DeviceSetting] AudioInput is null or disabled." ) ; this . audioInputForRecord = null ; if ( this . outputNode ) { this . midNode ?. disconnect ( ) ; } return ; } let inputMediaStream ; if ( device instanceof MediaStream ) { inputMediaStream = device ; } else if ( device === "dummy" ) { const audioContext = DefaultDeviceController . getAudioContext ( ) ; const dummyOutputNode = audioContext . createMediaStreamDestination ( ) ; const gainNode = audioContext . createGain ( ) ; gainNode . gain . value = 0.0 ; gainNode . connect ( dummyOutputNode ) ; const oscillatorNode = audioContext . createOscillator ( ) ; oscillatorNode . frequency . value = 440 ; oscillatorNode . connect ( gainNode ) ; oscillatorNode . start ( ) ; inputMediaStream = dummyOutputNode . stream ; } else { const proposedConstraints = this . calculateAudioMediaStreamConstraints ( device ) ; console . log ( proposedConstraints ) ; inputMediaStream = await navigator . mediaDevices . getUserMedia ( proposedConstraints ! ) ; this . deviceMediaStream = inputMediaStream ; } if ( suppression === true ) { let newTransformerCreated = false ; if ( this . voiceFocusDeviceTransformer === null || suppressionSpec ) { if ( await VoiceFocusDeviceTransformer . isSupported ( ) ) { this . voiceFocusDeviceTransformer = await VoiceFocusDeviceTransformer . create ( suppressionSpec ) ; newTransformerCreated = true ; console . log ( "[DeviceSetting] Transformer for suppression is created." ) ; if ( this . voiceFocusDeviceTransformer . isSupported ( ) === false ) { console . log ( "[DeviceSetting] Transformer for suppression is created, but not supported." ) ; this . voiceFocusDeviceTransformer = null ; } } } else { console . log ( "[DeviceSetting] Existing transformer for suppression is used." ) ; } if ( this . voiceFocusDeviceTransformer === null ) { if ( this . voiceFocusTransformDevice ) { this . voiceFocusTransformDevice . stop ( ) ; } this . voiceFocusTransformDevice = null ; } else if ( newTransformerCreated === true || ! this . voiceFocusTransformDevice ) { console . log ( "[DeviceSetting] new TransformDevice Device." ) ; this . voiceFocusTransformDevice = ( await this . voiceFocusDeviceTransformer . createTransformDevice ( inputMediaStream ) ) || null ; } else { console . log ( "[DeviceSetting] reuse TransformDevice Device." ) ; this . voiceFocusTransformDevice = await this . voiceFocusTransformDevice ! . chooseNewInnerDevice ( inputMediaStream ) ; } console . log ( "[DeviceSetting]  " , this . voiceFocusTransformDevice ) ; } else { if ( this . voiceFocusTransformDevice ) { this . voiceFocusTransformDevice . stop ( ) ; } this . voiceFocusTransformDevice = null ; } const audioContext = DefaultDeviceController . getAudioContext ( ) ; this . midNode ?. disconnect ( ) ; if ( this . voiceFocusTransformDevice ) { const nodeToVF = audioContext . createMediaStreamSource ( inputMediaStream ) ; const nodeOfVF = await this . voiceFocusTransformDevice . createAudioNode ( audioContext ) ; nodeToVF . connect ( nodeOfVF . start ) ; this . midNode = nodeOfVF . end ; } else { this . midNode = audioContext . createMediaStreamSource ( inputMediaStream ) ; } if ( ! this . outputNode ) { this . outputNode = audioContext . createMediaStreamDestination ( ) ; } this . midNode . connect ( this . outputNode ) ; await this . meetingSession . audioVideo . chooseAudioInputDevice ( this . outputNode . stream ) ; this . audioInputForRecord = this . outputNode . stream ; } ; private browserBehavior : DefaultBrowserBehavior = new DefaultBrowserBehavior ( ) ; private calculateAudioMediaStreamConstraints ( deviceId ) { let trackConstraints = { } ; if ( this . browserBehavior . requiresNoExactMediaStreamConstraints ( ) && this . browserBehavior . requiresGroupIdMediaStreamConstraints ( ) ) { trackConstraints . deviceId = deviceId ; } else if ( this . browserBehavior . requiresNoExactMediaStreamConstraints ( ) ) { trackConstraints . deviceId = deviceId ; } else { trackConstraints . deviceId = { exact : deviceId } ; } const defaultSampleRate = 48000 ; const defaultSampleSize = 16 ; const defaultChannelCount = 1 ; if ( this . supportSampleRateConstraint ( ) ) { trackConstraints . sampleRate = { ideal : defaultSampleRate } ; } if ( this . supportSampleSizeConstraint ( ) ) { trackConstraints . sampleSize = { ideal : defaultSampleSize } ; } if ( this . supportChannelCountConstraint ( ) ) { trackConstraints . channelCount = { ideal : defaultChannelCount } ; } const augmented = { echoCancellation : true , ... trackConstraints , } ; trackConstraints = augmented as MediaTrackConstraints ; return { audio : trackConstraints } ; } private supportSampleRateConstraint ( ) { return ! ! navigator . mediaDevices . getSupportedConstraints ( ) . sampleRate ; } private supportSampleSizeConstraint ( ) { return ! ! navigator . mediaDevices . getSupportedConstraints ( ) . sampleSize ; } private supportChannelCountConstraint ( ) { return ! ! navigator . mediaDevices . getSupportedConstraints ( ) . channelCount ; } setAudioInput = async ( val ) => { this . audioInput = val ; await this . setAudioInputCommon ( this . audioInput , this . audioInputEnable , this . audioSuppressionEnable ) ; } ; setAudioInputEnable = async ( val ) => { this . audioInputEnable = val ; await this . setAudioInputCommon ( this . audioInput , this . audioInputEnable , this . audioSuppressionEnable ) ; } ; setNoiseSuppressionEnable = async ( val ) => { this . audioSuppressionEnable = val ; await this . setAudioInputCommon ( this . audioInput , this . audioInputEnable , this . audioSuppressionEnable ) ; } ; setNoiseSuppressionType = async ( val ) => { this . voiceFocusSpec = { variant : val } ; await this . setAudioInputCommon ( this . audioInput , this . audioInputEnable , this . audioSuppressionEnable , this . voiceFocusSpec ) ; } ; setBackgroundMusic = async ( stream ) => { this . mixNode ?. disconnect ( ) ; const audioContext = DefaultDeviceController . getAudioContext ( ) ; this . mixNode = audioContext . createMediaStreamSource ( stream ) ; if ( ! this . outputNode ) { this . outputNode = audioContext . createMediaStreamDestination ( ) ; } if ( ! this . mixGainNode ) { this . mixGainNode = audioContext . createGain ( ) ; } this . mixGainNode . gain . value = this . mixSoundeVolume ; this . mixGainNode . connect ( this . outputNode ) ; this . mixNode . connect ( this . mixGainNode ) ; } ; setBackgroundMusicVolume = async ( volume ) => { if ( this . mixGainNode ) { this . mixGainNode . gain . value = volume ; } this . mixSoundeVolume = volume ; } ; getBackgroundMusicVolume = ( ) => { return this . mixSoundeVolume ; } ; mute = ( ) => { this . meetingSession . audioVideo . realtimeMuteLocalAudio ( ) ; } ; unmute = ( ) => { this . meetingSession . audioVideo . realtimeUnmuteLocalAudio ( ) ; } ; }