|
| 1 | + |
| 2 | +const sClass = function(){ |
| 3 | + this.Init=function(){ |
| 4 | + init(); |
| 5 | + }; |
| 6 | + this.PrecacheSound = function(name){ |
| 7 | + if (this.nosound !== 0) |
| 8 | + return name; |
| 9 | + if (this.precache === 0) |
| 10 | + return name; |
| 11 | + return precacheSound(name); |
| 12 | + }; |
| 13 | + this.LoadSound = function(source){ |
| 14 | + if (this.nosound.value !== 0) |
| 15 | + return; |
| 16 | + return loadSound(source); |
| 17 | + }; |
| 18 | + this.SounList = function(){ |
| 19 | + for(let i in sfxs) |
| 20 | + Con.Print( |
| 21 | + sizeGet(i) + ' : ' + i + '\n' |
| 22 | + ); |
| 23 | + Con.Print( |
| 24 | + 'Total resident: ' + |
| 25 | + total + |
| 26 | + '\n' |
| 27 | + ); |
| 28 | + }; |
| 29 | + this.StartSound = function(entnum, entchannel, sfx, origin, vol, attenuation){ |
| 30 | + startSound(entnum, entchannel, sfx, origin, vol, attenuation); |
| 31 | + } |
| 32 | + this.StartAmbient = function(name){ |
| 33 | + return startAmbient(name); |
| 34 | + } |
| 35 | + this.listAmbient = function(){ |
| 36 | + let out = []; |
| 37 | + for(let i in ambient_channels) |
| 38 | + out.push(i); |
| 39 | + return out; |
| 40 | + } |
| 41 | + this.LocalSound = function(sound){ |
| 42 | + startSound( |
| 43 | + CL.state.viewentity, |
| 44 | + 1, |
| 45 | + sound, |
| 46 | + Vec.origin, |
| 47 | + 1.0, |
| 48 | + 1.0 |
| 49 | + ); |
| 50 | + }; |
| 51 | + this.StopAllSounds = function(){ |
| 52 | + stopAllSounds(); |
| 53 | + } |
| 54 | + this.StopSound = function(etnum, etchannel){ |
| 55 | + stopSound(etnum, etchannel); |
| 56 | + } |
| 57 | + this.Play = function(){ |
| 58 | + |
| 59 | + } |
| 60 | + this.PlayVol = function(){ |
| 61 | + |
| 62 | + } |
| 63 | + this.StaticSound = async function(sfx, origin, vol, attenuation){ |
| 64 | + await startSound( |
| 65 | + "static", |
| 66 | + staticSerial, |
| 67 | + sfx, |
| 68 | + origin, |
| 69 | + vol, |
| 70 | + attenuation |
| 71 | + ); |
| 72 | + channels["static"].sub[staticSerial].sx.loop = true; |
| 73 | + staticSerial++; |
| 74 | + if(staticSerial >10) // 11 static channels |
| 75 | + staticSerial = 0; |
| 76 | + |
| 77 | + } |
| 78 | + this.UpdateDynamicSounds = function(){ |
| 79 | + for (let i of activeChannels){ //ia:string |
| 80 | + if(Host.realtime >= channels[ |
| 81 | + activeChannels[i].entnum |
| 82 | + ].sub[ |
| 83 | + activeChannels[i].entchannel |
| 84 | + ].end |
| 85 | + ){ |
| 86 | + activeChannels.splice(i, 1); |
| 87 | + continue; |
| 88 | + } |
| 89 | + spatialize( |
| 90 | + activeChannels[i].entnum, |
| 91 | + activeChannels[i].entchannel |
| 92 | + ); |
| 93 | + channelVolume( |
| 94 | + activeChannels[i].entnum, |
| 95 | + activeChannels[i].entchannel |
| 96 | + ); |
| 97 | + } |
| 98 | + } |
| 99 | + this.UpdateAmbientSounds = function(){} |
| 100 | + this.UpdateStaticSounds = function(){} |
| 101 | + this.Update = function(origin, forward, right, up){ |
| 102 | + listener_origin = origin; |
| 103 | + listener_forward = forward; |
| 104 | + listener_right = right; |
| 105 | + listener_up = up; |
| 106 | + if (volume.value < 0.0) |
| 107 | + Cvar.SetValue('volume', 0.0); |
| 108 | + else if (volume.value > 1.0) |
| 109 | + Cvar.SetValue('volume', 1.0); |
| 110 | + } |
| 111 | + this.Spatialize = function(ch){} |
| 112 | + this.size = 0; |
| 113 | + this.bgmvolume = 0; |
| 114 | + this.volume = 0; |
| 115 | + this.ambient_level = 0.3; |
| 116 | + this.ambient_fade = 100; |
| 117 | + this.precache = 1; |
| 118 | + this.nosound = 0; |
| 119 | + this.static_channels = []; |
| 120 | + this.known_sfx = []; |
| 121 | + this.started = false; |
| 122 | + let staticSerial = 0; |
| 123 | + let activeChannels = []; |
| 124 | + let channels = {}; |
| 125 | + let ambient_channels = {}; |
| 126 | + let listener_origin = [0.0, 0.0, 0.0]; |
| 127 | + let listener_forward = [0.0, 0.0, 0.0]; |
| 128 | + let listener_right = [0.0, 0.0, 0.0]; |
| 129 | + let listener_up = [0.0, 0.0, 0.0]; |
| 130 | + let sfxs = {}; |
| 131 | + let audioCTX; |
| 132 | + let init = async function(){ |
| 133 | + Con.Print('\nSound Initialization\n'); |
| 134 | + Cmd.AddCommand('play', S.Play); |
| 135 | + Cmd.AddCommand('playvol', S.PlayVol); |
| 136 | + Cmd.AddCommand('stopsound', S.StopAllSounds); |
| 137 | + Cmd.AddCommand('soundlist', S.SoundList); |
| 138 | + this.nosound = Cvar.RegisterVariable( |
| 139 | + 'nosound', |
| 140 | + (COM.CheckParm( |
| 141 | + '-nosound' |
| 142 | + ) != null) ? '1' : '0' |
| 143 | + ); |
| 144 | + this.volume = Cvar.RegisterVariable( |
| 145 | + 'volume', |
| 146 | + '0.7', |
| 147 | + true |
| 148 | + ); |
| 149 | + this.precache = Cvar.RegisterVariable( |
| 150 | + 'precache', |
| 151 | + '1' |
| 152 | + ); |
| 153 | + this.bgmvolume = Cvar.RegisterVariable( |
| 154 | + 'bgmvolume', |
| 155 | + '1', |
| 156 | + true |
| 157 | + ); |
| 158 | + this.ambient_level = Cvar.RegisterVariable( |
| 159 | + 'ambient_level', |
| 160 | + '0.3' |
| 161 | + ); |
| 162 | + this.ambient_fade = Cvar.RegisterVariable( |
| 163 | + 'ambient_fade', |
| 164 | + '100' |
| 165 | + ); |
| 166 | + this.started = true; |
| 167 | + audioCTX = new (window.AudioContext || window.webkitAudioContext)({ |
| 168 | + latencyHint: 'interactive', |
| 169 | + sampleRate: 44100, |
| 170 | + }); |
| 171 | + let ambient_sfx = ['water1', 'wind2']; |
| 172 | + for (let i of ambient_sfx) |
| 173 | + startAmbient('ambience/' + i + '.wav'); |
| 174 | + |
| 175 | + } |
| 176 | + let upldate = function(){ |
| 177 | + |
| 178 | + } |
| 179 | + let spatialize = function(en, ec){ |
| 180 | + "use asm" |
| 181 | + if(en === CL.state.viewentity){ |
| 182 | + channels[en].sub[ec].volLeft = channels[en].sub[ec].vol; |
| 183 | + channels[en].sub[ec].volRight = channels[en].sub[ec].vol; |
| 184 | + return ; |
| 185 | + } |
| 186 | + let source = [ |
| 187 | + channels[en].sub[ec].origin[0] - listener_origin[0], |
| 188 | + channels[en].sub[ec].origin[1] - listener_origin[1], |
| 189 | + channels[en].sub[ec].origin[2] - listener_origin[2] |
| 190 | + ]; |
| 191 | + let dist = Math.sqrt(source[0] * source[0] + source[1] * source[1] + source[2] * source[2]); |
| 192 | + |
| 193 | + if (dist !== 0.0){ |
| 194 | + source[0] /= dist; |
| 195 | + source[1] /= dist; |
| 196 | + source[2] /= dist; |
| 197 | + } |
| 198 | + dist *= (channels[en].sub[ec].mult*0.4); |
| 199 | + var dot = listener_right[0] * source[0] |
| 200 | + + listener_right[1] * source[1] |
| 201 | + + listener_right[2] * source[2]; |
| 202 | + channels[en].sub[ec].volRight = channels[en].sub[ec].vol * (1.0 - dist) * (1.0 + dot); |
| 203 | + if (channels[en].sub[ec].volRight < 0.0) |
| 204 | + channels[en].sub[ec].volRight = 0.0; |
| 205 | + if (channels[en].sub[ec].volRight > 1.0) |
| 206 | + channels[en].sub[ec].volRight = 1.0; |
| 207 | + channels[en].sub[ec].volLeft = channels[en].sub[ec].vol * (1.0 - dist) * (1.0 - dot); |
| 208 | + if (channels[en].sub[ec].volLeft < 0.0) |
| 209 | + channels[en].sub[ec].volLeft = 0.0; |
| 210 | + if (channels[en].sub[ec].volLeft > 1.0) |
| 211 | + channels[en].sub[ec].volLeft = 1.0; |
| 212 | + } |
| 213 | + let channelVolume = function(en,ec){ |
| 214 | + channels[en].sub[ec].g1.gain.value = |
| 215 | + channels[en].sub[ec].volLeft * volume.value; |
| 216 | + channels[en].sub[ec].g2.gain.value = |
| 217 | + channels[en].sub[ec].volRight * volume.value; |
| 218 | + } |
| 219 | + let startSound = async function(entnum, entchannel, sfx, origin, vol, attenuation){ |
| 220 | + pickChannel(entnum, entchannel); |
| 221 | + await loadSound(sfx); |
| 222 | + channels[entnum].sub[entchannel].vol = vol; |
| 223 | + channels[entnum].sub[entchannel].mult = attenuation * 0.001; |
| 224 | + spatialize(entnum, entchannel); |
| 225 | + if ( |
| 226 | + (channels[entnum].sub[entchannel].volLeft === 0.0)&& |
| 227 | + (channels[entnum].sub[entchannel].volRight === 0.0) |
| 228 | + ) |
| 229 | + return; |
| 230 | + |
| 231 | + if(channels[entnum].sub[entchannel].activeChannels === false){ |
| 232 | + channels[entnum].sub[entchannel].activeChannels = parseInt(activeChannels.length); |
| 233 | + activeChannels.push({ |
| 234 | + entnum, |
| 235 | + entchannel |
| 236 | + }); |
| 237 | + } |
| 238 | + channels[entnum].sub[entchannel].sx.buffer = sfxs[sfx].cache; |
| 239 | +// channels[entnum].sub[entchannel].sx.connect(audioCTX.destination); |
| 240 | + channels[entnum].sub[entchannel].end = Host.realtime + sfxs[sfx].cache.length; |
| 241 | + channels[entnum].sub[entchannel].sx.connect( |
| 242 | + channels[entnum].sub[entchannel].m1 |
| 243 | + ); |
| 244 | + channels[entnum].sub[entchannel].sx.connect( |
| 245 | + channels[entnum].sub[entchannel].m1, 0, 1 |
| 246 | + ); |
| 247 | + channels[entnum].sub[entchannel].m1.connect( |
| 248 | + channels[entnum].sub[entchannel].sp |
| 249 | + ); |
| 250 | + channels[entnum].sub[entchannel].sp.connect( |
| 251 | + channels[entnum].sub[entchannel].g1 |
| 252 | + ); |
| 253 | + channels[entnum].sub[entchannel].sp.connect( |
| 254 | + channels[entnum].sub[entchannel].g2 |
| 255 | + ); |
| 256 | + channels[entnum].sub[entchannel].g1.connect( |
| 257 | + channels[entnum].sub[entchannel].m2, 0, 0 |
| 258 | + ); |
| 259 | + channels[entnum].sub[entchannel].g2.connect( |
| 260 | + channels[entnum].sub[entchannel].m2, 0, 1 |
| 261 | + ); |
| 262 | + channels[entnum].sub[entchannel].m2.connect( |
| 263 | + audioCTX.destination |
| 264 | + ); |
| 265 | + channelVolume(entnum,entchannel); |
| 266 | + return channels[entnum].sub[entchannel].sx.start(); |
| 267 | + } |
| 268 | + let startAmbient = async (sfx)=>{ |
| 269 | + try{ |
| 270 | + ambient_channels[sfx].stop(); |
| 271 | + }catch(e){}; |
| 272 | + loadSound(sfx); |
| 273 | + ambient_channels[sfx] = audioCTX.createBufferSource(); |
| 274 | + ambient_channels[sfx].loop = true ; |
| 275 | + ambient_channels[sfx].connect(audioCTX.destination); |
| 276 | + setTimeout(function(){ |
| 277 | + try{ |
| 278 | + console.info(sfx); |
| 279 | + ambient_channels[sfx].buffer = sfxs[sfx].cache; |
| 280 | + ambient_channels[sfx].start(); |
| 281 | + }catch(e){ |
| 282 | + console.error(e); |
| 283 | + } |
| 284 | + },2000); |
| 285 | + } |
| 286 | + let startAmbientAll = function(){ |
| 287 | + for (let i in ambient_channels) |
| 288 | + startAmbient(i); |
| 289 | + } |
| 290 | + let precacheSound = function(name){ |
| 291 | + if(typeof sfxs[name] === "undefined") |
| 292 | + sfxs[name] = {name: name}; |
| 293 | + loadSound(name); |
| 294 | + return name; |
| 295 | + } |
| 296 | + let loadSound = async function(name){ |
| 297 | + if (sfxs[name] == null) |
| 298 | + sfxs[name] = { |
| 299 | + name : name, |
| 300 | + cache : null |
| 301 | + }; |
| 302 | + if (sfxs[name].cache != null) |
| 303 | + return true; |
| 304 | + let sc = {}; |
| 305 | + let data = COM.LoadFile('sound/' + name); |
| 306 | + if (data == null){ |
| 307 | + Con.Print( |
| 308 | + 'Couldn\'t load sound/' + |
| 309 | + name + |
| 310 | + '\n' |
| 311 | + ); |
| 312 | + return; |
| 313 | + } |
| 314 | + (await (async function(dataout, name){ |
| 315 | + audioCTX.decodeAudioData( |
| 316 | + data, |
| 317 | + function(buffer){ |
| 318 | + sfxs[name].cache = buffer; |
| 319 | + }, |
| 320 | + (e) => { reject(e); } |
| 321 | + ); |
| 322 | + })(data,name)); |
| 323 | + return true; |
| 324 | + }; |
| 325 | + let sizeGet = function(name){ |
| 326 | + if(sfxs[name].cache== null) |
| 327 | + return ''; |
| 328 | + let size = sfxs.cache.size.toString(); |
| 329 | + if (sfxs.cache.loopstart != null) |
| 330 | + size = 'L' + size; |
| 331 | + return size.padStart(6, ' '); |
| 332 | + }; |
| 333 | + let pickChannel = function(entnum,entchannel){ |
| 334 | + if(channels[entnum] == null) |
| 335 | + newChannel(entnum); |
| 336 | + if(channels[entnum].sub[entchannel] != null) |
| 337 | + stopSound(entnum, entchannel); |
| 338 | + return channels[entnum].sub[entchannel]= |
| 339 | + emptyChannel(); |
| 340 | + |
| 341 | + }; |
| 342 | + let emptyChannel = function(){ |
| 343 | + return { |
| 344 | + vol : 1, |
| 345 | + volLeft : 1, |
| 346 | + volRight : 1, |
| 347 | + origin : [0,0,0], |
| 348 | + mult : 0.0001, |
| 349 | + end : null, |
| 350 | + sx : audioCTX.createBufferSource(), |
| 351 | + m1 : audioCTX.createChannelMerger(2), |
| 352 | + m2 : audioCTX.createChannelMerger(2), |
| 353 | + sp : audioCTX.createChannelSplitter(2), |
| 354 | + g1 : audioCTX.createGain(), |
| 355 | + g2 : audioCTX.createGain() |
| 356 | + } |
| 357 | + } |
| 358 | + let newChannel = function(entnum, sub){ |
| 359 | + if((1 > sub)||(typeof sub === "undefined")) |
| 360 | + sub = 2; |
| 361 | + channels[entnum] = { |
| 362 | + sub:[] |
| 363 | + }; |
| 364 | + }; |
| 365 | + let stopAllSounds = function(){ |
| 366 | + for(let iOne in channels) |
| 367 | + for(let iTwo in channels[iOne].sub) |
| 368 | + stopSound(iOne,iTwo); |
| 369 | + for(let iOne in ambient_channels) |
| 370 | + stopSoundAmbient(iOne); |
| 371 | + } |
| 372 | + let stopSoundAmbient = function(sfx){ |
| 373 | + if(ambient_channels[sfx] == null) |
| 374 | + return ; |
| 375 | + try{ |
| 376 | + ambient_channels[sfx].stop(); |
| 377 | + ambient_channels[sfx]=null; |
| 378 | + }catch(e){ |
| 379 | + |
| 380 | + } |
| 381 | + } |
| 382 | + let stopSound = function(entnum, entchannel){ |
| 383 | + if(channels[entnum].sub[entchannel].sx == null) |
| 384 | + return ; |
| 385 | + try{ |
| 386 | + channels[entnum].sub[entchannel].sx.stop(); |
| 387 | + channels[entnum].sub[entchannel].sx=null; |
| 388 | + }catch(e){ |
| 389 | + |
| 390 | + } |
| 391 | + } |
| 392 | + |
| 393 | +}; |
| 394 | + |
| 395 | +const S = new sClass(); |
0 commit comments