declare class webkitAudioContext extends AudioContext { } declare class mozAudioContext extends AudioContext { } class Fallback_Settings { public Formats : Array < { Mime : string ; Name : string ; } > ; public MaxVolume : number ; public InitialBufferLength : number ; public AutoCorrectSpeed : boolean ; constructor ( ) { this . Formats = [ { "Mime" : "audio/mpeg" , "Name" : "mp3" } , { "Mime" : "audio/wave" , "Name" : "wav" } ] ; this . MaxVolume = 1.0 ; this . AutoCorrectSpeed = false ; this . InitialBufferLength = 1.0 / 3.0 ; } } class Fallback { private readonly Audio : AudioContext ; private readonly Logger : Logging ; private readonly Settings : Fallback_Settings ; private readonly FormatReader : IAudioFormatReader ; private readonly Player : LiveAudioPlayer ; private readonly SelectedFormatMime : string ; private readonly SelectedFormatName : string ; private WebSocket : WebSocketClient ; public ActivityCallback : ( ) => void ; constructor ( logger , settings ) { this . Logger = logger ; if ( ! this . Logger ) { this . Logger = new Logging ( null , null ) ; } if ( typeof AudioContext !== "undefined" ) this . Audio = new AudioContext ( ) ; else if ( typeof webkitAudioContext !== "undefined" ) this . Audio = new webkitAudioContext ( ) ; else if ( typeof mozAudioContext !== "undefined" ) this . Audio = new mozAudioContext ( ) ; else { this . Logger . Log ( '3LAS: Browser does not support "AudioContext".' ) ; throw new Error ( ) ; } this . Settings = settings ; this . Logger . Log ( "Detected: " + ( OSName == "MacOSX" ? "Mac OSX" : ( OSName == "Unknown" ? "Unknown OS" : OSName ) ) + ", " + ( BrowserName == "IE" ? "Internet Explorer" : ( BrowserName == "NativeChrome" ? "Chrome legacy" : ( BrowserName == "Unknown" ? "Unknown Browser" : BrowserName ) ) ) ) ; this . SelectedFormatMime = "" ; this . SelectedFormatName = "" ; for ( let i = 0 ; i < this . Settings . Formats . length ; i ++ ) { if ( ! AudioFormatReader . CanDecodeTypes ( [ this . Settings . Formats [ i ] . Mime ] ) ) continue ; this . SelectedFormatMime = this . Settings . Formats [ i ] . Mime ; this . SelectedFormatName = this . Settings . Formats [ i ] . Name ; break ; } if ( this . SelectedFormatMime == "" || this . SelectedFormatName == "" ) { this . Logger . Log ( "None of the available MIME types are supported." ) ; throw new Error ( ) ; } this . Logger . Log ( "Using websocket fallback with MIME: " + this . SelectedFormatMime ) ; try { this . Player = new LiveAudioPlayer ( this . Audio , this . Logger , this . Settings . MaxVolume , this . Settings . InitialBufferLength , this . Settings . AutoCorrectSpeed ) ; this . Logger . Log ( "Init of LiveAudioPlayer succeeded" ) ; } catch ( e ) { this . Logger . Log ( "Init of LiveAudioPlayer failed: " + e ) ; throw new Error ( ) ; } try { this . FormatReader = AudioFormatReader . Create ( this . SelectedFormatMime , this . Audio , this . Logger , this . OnReaderError . bind ( this ) , this . Player . CheckBeforeDecode , this . OnReaderDataReady . bind ( this ) , AudioFormatReader . DefaultSettings ( ) ) ; this . Logger . Log ( "Init of AudioFormatReader succeeded" ) ; } catch ( e ) { this . Logger . Log ( "Init of AudioFormatReader failed: " + e ) ; throw new Error ( ) ; } this . PacketModCounter = 0 ; this . LastCheckTime = 0 ; this . FocusChecker = 0 ; } public Init ( webSocket ) { this . MobileUnmute ( ) ; this . WebSocket = webSocket ; this . WebSocket . Send ( JSON . stringify ( { "type" : "fallback" , "data" : this . SelectedFormatName } ) ) ; this . StartFocusChecker ( ) ; } public MobileUnmute ( ) { let amplification = this . Audio . createGain ( ) ; amplification . gain . value = 1.0 ; amplification . connect ( this . Audio . destination ) ; let audioBuffer = this . Audio . createBuffer ( 2 , this . Audio . sampleRate , this . Audio . sampleRate ) ; let sourceNode = this . Audio . createBufferSource ( ) ; sourceNode . onended = function ( _ev ) { sourceNode . disconnect ( ) ; amplification . disconnect ( ) ; } ; sourceNode . buffer = audioBuffer ; sourceNode . connect ( amplification ) ; sourceNode . start ( ) ; } public set Volume ( value ) { this . Player . Volume = value * this . Settings . MaxVolume ; } public get Volume ( ) : number { return this . Player . Volume / this . Settings . MaxVolume ; } private OnReaderError ( ) { this . Logger . Log ( "Reader error: Decoding failed." ) ; } private OnReaderDataReady ( ) { while ( this . FormatReader . SamplesAvailable ( ) ) { this . Player . PushBuffer ( this . FormatReader . PopSamples ( ) ) ; } } public OnSocketError ( message ) { } public OnSocketConnect ( ) { } public OnSocketDisconnect ( ) { } private PacketModCounter : number ; public OnSocketDataReady ( data ) { this . PacketModCounter ++ ; if ( this . PacketModCounter > 100 ) { if ( this . ActivityCallback ) this . ActivityCallback ( ) ; this . PacketModCounter = 0 ; } this . FormatReader . PushData ( new Uint8Array ( data ) ) ; } private LastCheckTime : number ; private FocusChecker : number ; private StartFocusChecker ( ) { if ( ! this . FocusChecker ) { this . LastCheckTime = Date . now ( ) ; this . FocusChecker = window . setInterval ( this . CheckFocus . bind ( this ) , 2000 ) ; } } private StopFocusChecker ( ) { if ( this . FocusChecker ) { window . clearInterval ( this . FocusChecker ) ; this . FocusChecker = 0 ; } } private CheckFocus ( ) { let checkTime = Date . now ( ) ; if ( checkTime - this . LastCheckTime > 10000 ) { this . Logger . Log ( "Focus lost, purging format reader." ) ; this . FormatReader . PurgeData ( ) ; } this . LastCheckTime = checkTime ; } public Reset ( ) { this . StopFocusChecker ( ) ; this . FormatReader . Reset ( ) ; this . Player . Reset ( ) ; this . WebSocket = null ; } }