declare let LibAV , NoiseRepellent , NoiseRepellentFactory , Vosk , WebRtcVad , __filename ; const libavVersion = "3.6b.4.4.1" ; const libavPath = "../libav/libav-" + libavVersion + "-ennuicastr.js" ; const voskModelVersion = "en-us-0.15" ; const voskModelPath = "../libs/vosk-model-small-" + voskModelVersion + ".tar.gz" ; const vadExtension = 2000 ; const rtcVadExtension = 1000 ; const waitWorkerCode = `
onmessage = function(ev) {
    var prevVal = 0;
    var buf = ev.data;
    while (Atomics.wait(buf, 1, prevVal)) {
        var ts = Date.now();
        var newVal = Atomics.load(buf, 1);
        if (prevVal !== newVal) {
            postMessage([ts, prevVal, newVal]);
            prevVal = newVal;
        }
    }
};
` ; class AWPHandler { port : MessagePort ; ondata : ( ts : number , data : Float32Array [ ] ) => unknown ; incoming : Float32Array [ ] ; incomingRW : Int32Array ; outgoing : Float32Array [ ] ; outgoingRW : Int32Array ; waitWorker : Worker ; ts : number ; buf : Float32Array [ ] ; constructor ( port , ondata ) { this . port = port ; this . ondata = ondata ; port . onmessage = this . onmessage . bind ( this ) ; } onmessage ( ev ) { const msg = ev . data ; if ( typeof msg === "object" && msg . c === "buffers" ) { this . incoming = msg . outgoing ; this . incomingRW = msg . outgoingRW ; this . outgoing = msg . incoming ; this . outgoingRW = msg . incomingRW ; const ww = this . waitWorker = new Worker ( "data:application/javascript," + encodeURIComponent ( waitWorkerCode ) ) ; ww . onmessage = ( ev ) => { const msg = ev . data ; const ts = msg [ 0 ] ; const start = msg [ 1 ] ; const end = msg [ 2 ] ; const buf = [ ] ; const bufSz = this . incoming [ 0 ] . length ; let len = end - start ; Atomics . load ( this . incomingRW , 1 ) ; if ( end < start ) { len += bufSz ; const brk = bufSz - start ; for ( let i = 0 ; i < this . incoming . length ; i ++ ) { const sbuf = new Float32Array ( len ) ; sbuf . set ( this . incoming [ i ] . subarray ( start ) , 0 ) ; sbuf . set ( this . incoming [ i ] . subarray ( 0 , end ) , brk ) ; buf . push ( sbuf ) ; } } else { for ( let i = 0 ; i < this . incoming . length ; i ++ ) buf . push ( this . incoming [ i ] . slice ( start , end ) ) ; } this . ondata ( ts , buf ) ; } ; ww . postMessage ( this . incomingRW ) ; return ; } else if ( typeof msg === "number" ) { this . ts = msg ; } else { this . buf = msg ; } if ( this . ts && this . buf ) { const ts = this . ts ; const buf = this . buf ; this . ts = null ; this . buf = null ; this . ondata ( ts , buf ) ; } } sendData ( buf ) { if ( this . outgoing ) { const bufSz = this . outgoing [ 0 ] . length ; let len = buf [ 0 ] . length ; if ( len > bufSz ) { len = bufSz ; } let writeHead = this . outgoingRW [ 1 ] ; if ( writeHead + len > bufSz ) { const brk = bufSz - writeHead ; for ( let i = 0 ; i < this . outgoing . length ; i ++ ) { this . outgoing [ i ] . set ( buf [ i % buf . length ] . subarray ( 0 , brk ) , writeHead ) ; this . outgoing [ i ] . set ( buf [ i % buf . length ] . subarray ( brk ) , 0 ) ; } } else { for ( let i = 0 ; i < this . outgoing . length ; i ++ ) this . outgoing [ i ] . set ( buf [ i % buf . length ] , writeHead ) ; } writeHead = ( writeHead + len ) % bufSz ; Atomics . store ( this . outgoingRW , 1 , writeHead ) ; Atomics . notify ( this . outgoingRW , 1 ) ; } else { this . port . postMessage ( { c : "data" , d : buf } ) ; } } } onmessage = function ( ev ) { const msg = ev . data ; switch ( msg . c ) { case "encoder" : doEncoder ( msg ) ; break ; case "filter" : doFilter ( msg ) ; break ; case "max" : doMax ( msg ) ; break ; case "dynaudnorm" : doDynaudnorm ( msg ) ; break ; } } ; function doEncoder ( msg ) { const inPort = msg . port ; const inSampleRate = msg . inSampleRate || 48000 ; const outSampleRate = msg . outSampleRate || 48000 ; const format = msg . format || "opus" ; const channelLayout = msg . channelLayout || 4 ; const channelCount = msg . channelCount || 1 ; let p = Promise . all ( [ ] ) ; let pts = 0 ; let seq = 0 ; let libav ; const encOptions = { sample_rate : outSampleRate , frame_size : outSampleRate * 20 / 1000 , channel_layout : 4 , channels : 1 } ; let c , frame , pkt ; let buffersrc_ctx , buffersink_ctx ; LibAV = { nolibavworker : true , base : "../libav" } ; __filename = libavPath ; importScripts ( __filename ) ; return LibAV . LibAV ( { noworker : true } ) . then ( ( la ) => { libav = la ; if ( format === "flac" ) { encOptions . sample_fmt = libav . AV_SAMPLE_FMT_S32 ; } else { encOptions . sample_fmt = libav . AV_SAMPLE_FMT_FLT ; encOptions . bit_rate = 128000 ; } return libav . ff_init_encoder ( ( format === "flac" ) ? "flac" : "libopus" , { ctx : encOptions , time_base : [ 1 , outSampleRate ] } ) ; } ) . then ( ( ret ) => { c = ret [ 1 ] ; frame = ret [ 2 ] ; pkt = ret [ 3 ] ; encOptions . frame_size = ret [ 4 ] ; return libav . ff_init_filter_graph ( "anull" , { sample_rate : inSampleRate , sample_fmt : libav . AV_SAMPLE_FMT_FLTP , channels : channelCount , channel_layout : channelLayout } , { sample_rate : encOptions . sample_rate , sample_fmt : encOptions . sample_fmt , channel_layout : 4 , frame_size : encOptions . frame_size } ) ; } ) . then ( ( ret ) => { buffersrc_ctx = ret [ 1 ] ; buffersink_ctx = ret [ 2 ] ; new AWPHandler ( inPort , ondata ) ; } ) . catch ( console . error ) ; function ondata ( ts , data ) { while ( data . length < channelCount ) data = data . concat ( data ) ; const frames = [ { data : data , channels : channelCount , channel_layout : channelLayout , format : libav . AV_SAMPLE_FMT_FLTP , pts : pts , sample_rate : inSampleRate } ] ; pts += data [ 0 ] . length ; p = p . then ( ( ) => { return libav . ff_filter_multi ( buffersrc_ctx , buffersink_ctx , frame , frames ) ; } ) . then ( frames => { if ( frames . length === 0 ) return [ ] ; return libav . ff_encode_multi ( c , frame , pkt , frames ) ; } ) . then ( encPackets => { if ( encPackets . length === 0 ) return ; const packets = [ ] ; for ( let pi = 0 ; pi < encPackets . length ; pi ++ ) packets . push ( encPackets [ pi ] . data ) ; postMessage ( { c : "packets" , t : Date . now ( ) - ts , ts : ts , s : seq , d : packets } ) ; seq += packets . length ; } ) . catch ( console . error ) ; } } function doFilter ( msg ) { let awpHandler ; const inPort = msg . port ; const sampleRate = msg . sampleRate ; let useNR = msg . useNR ; let sentRecently = msg . sentRecently ; let lastVadSensitivity = msg . vadSensitivity ; let vadSensitivity = msg . vadSensitivity ; let vadNoiseGate = msg . vadNoiseGate ; let vadNoiseGateLvl = Math . pow ( 10 , vadNoiseGate / 20 ) ; const useTranscription = msg . useTranscription ; onmessage = function ( ev ) { const msg = ev . data ; if ( msg . c !== "state" ) return ; useNR = msg . useNR ; sentRecently = msg . sentRecently ; vadSensitivity = msg . vadSensitivity ; vadNoiseGate = msg . vadNoiseGate ; vadNoiseGateLvl = Math . pow ( 10 , vadNoiseGate / 20 ) ; } ; let rawVadLvl = 0 ; let rawVadOn = false ; let rtcVadOn = false ; let vadOn = false ; let max = 0 ; let maxCtr = 0 ; let vad = null ; let vadHandleLo = null , vadHandleHi = null ; let vadDataPtr = null ; const vadBufSz = 640 ; let vadBuf = null ; let bi = 0 ; let nr = null ; let timeout = null , rtcTimeout = null ; const step = sampleRate / 32000 ; const vosk = { model : < any > null , recognizer : < any > null , inSamples : 0 , inTime : 0 , outTime : 0 } ; let triggerVadCeil = 0 , triggerVadFloor = 0 ; let curVadVolume = 0 , curVadPeak = 0 ; let lastVolume = 0 , lastPeak = 0 ; Promise . all ( [ ] ) . then ( function ( ) { __filename = "../libs/vad/vad-m2.wasm.js" ; importScripts ( __filename ) ; return WebRtcVad ( ) ; } ) . then ( function ( ret ) { vad = ret ; function mkVad ( lvl ) { let vadHandle = vad . Create ( ) ; if ( vadHandle === 0 ) { postMessage ( { c : "log" , i : "failvad" , m : "Failed to create VAD." } ) ; throw new Error ( ) ; } if ( vad . Init ( vadHandle ) < 0 ) { postMessage ( { c : "log" , i : "failvad" , m : "Failed to initialize VAD." } ) ; throw new Error ( ) ; } vad . set_mode ( vadHandle , lvl ) ; return vadHandle ; } vadHandleLo = mkVad ( vadSensitivity ) ; lastVadSensitivity = vadSensitivity ; vadHandleHi = mkVad ( 3 ) ; vadDataPtr = vad . malloc ( vadBufSz * 2 ) ; vadBuf = new Int16Array ( vad . HEAPU8 . buffer , vadDataPtr , vadBufSz * 2 ) ; __filename = "../noise-repellent/noise-repellent-m.wasm.js?v=2" ; importScripts ( __filename ) ; NoiseRepellent = { NoiseRepellentFactory : NoiseRepellentFactory } ; __filename = "../noise-repellent/noise-repellent-m.js?v=2" ; importScripts ( __filename ) ; return NoiseRepellent . NoiseRepellent ( sampleRate ) ; } ) . then ( function ( ret ) { nr = ret ; nr . set ( NoiseRepellent . N_ADAPTIVE , 1 ) ; nr . set ( NoiseRepellent . AMOUNT , 20 ) ; nr . set ( NoiseRepellent . WHITENING , 50 ) ; if ( useTranscription ) { __filename = "../libs/vosk.js?v=3" ; importScripts ( __filename ) ; } } ) . then ( function ( ) { if ( useTranscription ) loadVosk ( ) ; awpHandler = new AWPHandler ( inPort , ondata ) ; } ) . catch ( console . error ) ; function loadVosk ( ) { Vosk . createModel ( voskModelPath ) . then ( ret => { vosk . model = ret ; vosk . recognizer = new vosk . model . KaldiRecognizer ( sampleRate ) ; vosk . recognizer . setWords ( true ) ; vosk . recognizer . on ( "partialresult" , msg => { voskResult ( msg , false ) ; } ) ; vosk . recognizer . on ( "result" , msg => { voskResult ( msg , true ) ; } ) ; } ) . catch ( console . error ) ; } function ondata ( ts , data ) { const ib = data [ 0 ] ; const cc = data . length ; if ( cc !== 1 ) { for ( let i = 1 ; i < cc ; i ++ ) { const ibc = data [ i ] ; for ( let j = 0 ; j < ib . length ; j ++ ) ib [ j ] += ibc [ j ] ; } for ( let i = 0 ; i < ib . length ; i ++ ) ib [ i ] /= cc ; } let nrbuf = ib ; if ( nr ) { let ob = ib ; nrbuf = nr . run ( ib ) ; if ( useNR ) ob = nrbuf ; const od = [ ] ; if ( ! sentRecently ) { ob = ob . slice ( 0 ) ; ob . fill ( 0 ) ; } while ( od . length < data . length ) od . push ( ob . slice ( 0 ) ) ; awpHandler . sendData ( od ) ; } let vadLvl = rawVadLvl ; let vadSet = rawVadOn ; for ( let i = 0 ; i < ib . length ; i += step ) { const v = nrbuf [ ~ ~ i ] ; const a = Math . abs ( v ) ; curVadVolume += a ; if ( a > curVadPeak ) curVadPeak = a ; vadBuf [ bi ++ ] = v * 0x7FFF ; if ( bi == vadBufSz ) { if ( lastVadSensitivity !== vadSensitivity ) { vad . set_mode ( vadHandleLo , vadSensitivity ) ; lastVadSensitivity = vadSensitivity ; } rawVadLvl = vadLvl = vad . Process ( vadHandleLo , 32000 , vadDataPtr , vadBufSz ) + vad . Process ( vadHandleHi , 32000 , vadDataPtr , vadBufSz ) ; vadSet = ! ! vadLvl ; bi = 0 ; if ( vadLvl === 2 ) { const triggerTarget = curVadVolume / vadBufSz ; if ( triggerTarget > triggerVadCeil ) { triggerVadCeil = triggerTarget ; } else { triggerVadCeil = ( triggerVadCeil * 1023 + triggerTarget ) / 1024 ; } } else if ( vadLvl === 0 ) { const triggerTarget = curVadVolume / vadBufSz * 2 ; triggerVadFloor = ( triggerVadFloor * 511 + triggerTarget ) / 512 ; } lastVolume = curVadVolume ; curVadVolume = 0 ; lastPeak = curVadPeak ; curVadPeak = 0 ; } } if ( vadSet && ( vadLvl < 2 || vadSensitivity >= 3 ) ) { const relVolume = lastVolume / vadBufSz ; vadSet = false ; if ( relVolume >= triggerVadFloor ) { if ( triggerVadCeil < triggerVadFloor * 2 || relVolume - triggerVadFloor >= ( triggerVadCeil - triggerVadFloor ) / 32 ) { vadSet = true ; } } } if ( vadSet && vadNoiseGate > - 100 ) { if ( lastPeak < vadNoiseGateLvl ) vadSet = false ; } if ( vadSet ) { if ( ! rtcVadOn ) { rtcVadOn = true ; } else if ( rtcTimeout ) { clearTimeout ( rtcTimeout ) ; rtcTimeout = null ; } if ( timeout ) { clearTimeout ( timeout ) ; timeout = null ; } if ( ! rawVadOn ) { rawVadOn = true ; vadOn = true ; curVadVolume = 0 ; } } else { if ( rtcVadOn ) { if ( ! rtcTimeout ) { rtcTimeout = setTimeout ( function ( ) { rtcTimeout = null ; rtcVadOn = false ; } , rtcVadExtension ) ; } } if ( rawVadOn ) { rawVadOn = false ; if ( ! timeout ) { timeout = setTimeout ( function ( ) { timeout = null ; vadOn = false ; } , vadExtension ) ; } } } for ( let i = 0 ; i < ib . length ; i ++ ) { let v = ib [ i ] ; if ( v < 0 ) v = - v ; if ( v > max ) max = v ; if ( ++ maxCtr >= 1024 ) { postMessage ( { c : "max" , m : max } ) ; max = maxCtr = 0 ; } } postMessage ( { c : "state" , rawVadOn : rawVadOn , rtcVadOn : rtcVadOn , vadOn : vadOn } ) ; if ( useTranscription && vosk . recognizer ) { vosk . inSamples += ib . length ; vosk . inTime = vosk . inSamples / sampleRate ; vosk . outTime = ts / 1000 ; vosk . recognizer . acceptWaveformFloat ( ib , sampleRate ) ; } } function voskResult ( msg , complete ) { const result = msg . result ; if ( complete && result . text === "" ) return ; if ( ! complete && result . partial === "" ) return ; if ( result . result ) { const offset = vosk . outTime - vosk . inTime ; for ( let i = 0 ; i < result . result . length ; i ++ ) { const word = result . result [ i ] ; word . start += offset ; word . end += offset ; } } postMessage ( { c : "vosk" , result : result , complete : complete } ) ; } } function doMax ( msg ) { const inPort = msg . port ; let max = 0 ; let maxCtr = 0 ; const awpHandler = new AWPHandler ( inPort , ondata ) ; function ondata ( ts , data ) { const ib = data [ 0 ] ; for ( let i = 0 ; i < ib . length ; i ++ ) { let v = ib [ i ] ; if ( v < 0 ) v = - v ; if ( v > max ) max = v ; if ( ++ maxCtr >= 1024 ) { postMessage ( { c : "max" , m : max } ) ; max = maxCtr = 0 ; } } awpHandler . sendData ( data ) ; } } function doDynaudnorm ( msg ) { let awpHandler ; const inPort = msg . port ; const sampleRate = msg . sampleRate ; let la ; let frame ; let buffersrc_ctx , buffersink_ctx ; let pts = 0 ; LibAV = { nolibavworker : true , base : "../libav" } ; __filename = libavPath ; importScripts ( __filename ) ; return LibAV . LibAV ( { noworker : true } ) . then ( ( ret ) => { la = ret ; return la . av_frame_alloc ( ) ; } ) . then ( ( ret ) => { frame = ret ; return la . ff_init_filter_graph ( "dynaudnorm=f=10:g=3" , { sample_rate : sampleRate , sample_fmt : la . AV_SAMPLE_FMT_FLT , channels : 1 , channel_layout : 4 } , { sample_rate : sampleRate , sample_fmt : la . AV_SAMPLE_FMT_FLT , channels : 1 , channel_layout : 4 , frame_size : 1024 } ) ; } ) . then ( ( ret ) => { buffersrc_ctx = ret [ 1 ] ; buffersink_ctx = ret [ 2 ] ; awpHandler = new AWPHandler ( inPort , ondata ) ; } ) . catch ( console . error ) ; function ondata ( ts , data ) { const ib = data [ 0 ] ; const frames = [ { data : ib , channels : 1 , channel_layout : 4 , format : la . AV_SAMPLE_FMT_FLT , pts : pts , sample_rate : sampleRate } ] ; pts += ib . length ; return la . ff_filter_multi ( buffersrc_ctx , buffersink_ctx , frame , frames , false ) . then ( ( frames ) => { for ( let fi = 0 ; fi < frames . length ; fi ++ ) { const frame = [ frames [ fi ] . data ] ; while ( frame . length < data . length ) frame . push ( frame [ 0 ] ) ; awpHandler . sendData ( frame ) ; } } ) . catch ( console . error ) ; } }