const { MediaStream , nonstandard : { RTCAudioSource } } = require ( 'wrtc' ) ; import fetch from 'node-fetch' ; const path = require ( 'path' ) ; const decode = require ( 'audio-decode' ) ; const format = require ( 'audio-format' ) ; const convert = require ( 'pcm-convert' ) ; import { HiFiAudioAPIData , HiFiCommunicator , Point3D , preciseInterval } from 'hifi-spatial-audio' ; const { generateHiFiJWT } = require ( './utilities' ) ; const auth = require ( '../../auth.json' ) ; param { string } providedUserID param { string } audioURL param { object } position param { number } hiFiGain async function startAudioBot ( providedUserID , audioURL , position , hiFiGain ) { if ( ! audioURL ) { console . error ( `Audio URL not specified!` ) ; return ; } let audioFileExtension = path . extname ( audioURL ) . toLowerCase ( ) ; if ( ! ( audioFileExtension === ".mp3" || audioFileExtension === ".wav" ) ) { console . error ( `Specified audio file must be a \`.mp3\` or a \`.wav\`!\nInstead, it's a \`${ audioFileExtension }\`` ) ; return ; } let response , fileBuffer ; try { response = await fetch ( audioURL ) ; } catch ( e ) { console . error ( `Error when fetching ${ audioURL }:\n${ e }` ) ; return ; } try { fileBuffer = await response . buffer ( ) ; } catch ( e ) { console . error ( `Error when transforming resopnse into buffer:\n${ e }` ) ; return ; } const audioBuffer = await decode ( fileBuffer ) , { numberOfChannels , sampleRate , length , duration } = audioBuffer , parsed = format . detect ( audioBuffer ) , convertedAudioBuffer = convert ( audioBuffer , parsed , 'int16' ) , BITS_PER_SAMPLE = 16 , TICK_INTERVAL_MS = 10 , MS_PER_SEC = 1000 , TICKS_PER_SECOND = MS_PER_SEC / TICK_INTERVAL_MS , SAMPLES_PER_TICK = sampleRate / TICKS_PER_SECOND , currentSamples = new Int16Array ( numberOfChannels * SAMPLES_PER_TICK ) , currentAudioData = { samples : currentSamples , sampleRate , bitsPerSample : BITS_PER_SAMPLE , channelCount : numberOfChannels , numberOfFrames : SAMPLES_PER_TICK } , source = new RTCAudioSource ( ) , track = source . createTrack ( ) , inputAudioMediaStream = new MediaStream ( [ track ] ) , initialHiFiAudioAPIData = new HiFiAudioAPIData ( { position : position , hiFiGain : hiFiGain } ) , hifiCommunicator = new HiFiCommunicator ( { initialHiFiAudioAPIData } ) ; await hifiCommunicator . setInputAudioMediaStream ( inputAudioMediaStream ) ; let sampleNumber = 0 ; let tick = ( ) => { for ( let frameNumber = 0 ; frameNumber < SAMPLES_PER_TICK ; frameNumber ++ , sampleNumber ++ ) { for ( let channelNumber = 0 ; channelNumber < numberOfChannels ; channelNumber ++ ) { currentSamples [ frameNumber * numberOfChannels + channelNumber ] = convertedAudioBuffer [ sampleNumber * numberOfChannels + channelNumber ] || 0 ; } } source . onData ( currentAudioData ) ; if ( sampleNumber > length ) { sampleNumber = 0 ; } } ; let hiFiJWT = await generateHiFiJWT ( providedUserID , auth . HIFI_DEFAULT_SPACE_NAME , false ) ; if ( ! hiFiJWT ) { return ; } let connectResponse ; try { connectResponse = await hifiCommunicator . connectToHiFiAudioAPIServer ( hiFiJWT , auth . HIFI_SIGNALING_URL ) ; } catch ( e ) { console . error ( `Call to \`connectToHiFiAudioAPIServer()\` failed! Error:\n${ JSON . stringify ( e ) }` ) ; return ; } preciseInterval ( tick , TICK_INTERVAL_MS ) ; console . log ( `Audio bot started! Audio URL: ${ audioURL }` ) ; } startAudioBot ( "Tree Sounds" , "https://hifi-content.s3.amazonaws.com/Audio/EnvSounds/WindandWater/ELWW%20Wind%20through%20large%20bamboo%20bush%2C%20movement%20leaves%20branches%20creak%2C%20very%20faint%20birds.mp3" , new Point3D ( { x : - 1.1937015896635599 , z : 0.5384111441848216 } ) , 0.12 ) ; startAudioBot ( "Tree Sounds" , "https://hifi-content.s3.amazonaws.com/Audio/EnvSounds/WindandWater/ELWW%20Wind%20through%20bamboo%20bush%2C%20leaf%20and%20branch%20movement.mp3" , new Point3D ( { x : 1.4091348594515265 , z : 0.24351004935432052 } ) , 0.12 ) ;