Skip to content

Instantly share code, notes, and snippets.

@timetocode

timetocode/SoundAtlas.js

Last active May 6, 2019
Embed
What would you like to do?
Pooled sounds for Babylon.js
let basePath = 'http://localhost:8086'
if (typeof window === 'undefined') {
basePath = 'http://localhost:8086/'
}
if (process && process.env && process.env.NODE_ENV === 'production') {
basePath = `https://somedomain.io/`
}
const sounds = new Map()
let assetsManager = null
let scene = null
const setScene = (value) => {
scene = value
}
const setAssetsManager = (value) => {
assetsManager = value
}
const add = (soundFilename) => {
if (!assetsManager || !scene) {
throw new Error('SoundAtlas scene or assetsManager was null, unable to add sound.')
}
const binaryTask = assetsManager.addBinaryFileTask(`BinaryTask load ${soundFilename}`, `${basePath}${soundFilename}`)
binaryTask.onSuccess = (task) => {
// solo sounds are non-spatial variants (was too difficult to change a spatial to sound to non-spatial after creation
const soloSound = new BABYLON.Sound(soundFilename + '-solo', task.data, scene, null, { spatialSound: false })
sounds.set(soundFilename + '-solo', soloSound)
//console.log('creating...', soundFilename, soundFilename + '-solo')
const sound = soloSound.clone()
sound.spatialSound = true//new BABYLON.Sound(soundFilename, task.data, scene, null, { spatialSound: true })
sounds.set(soundFilename, sound)
}
binaryTask.onError = (task, message, err) => {
console.log('Sound Load Error', message, err)
}
}
const dispose = () => {
sounds.forEach(sound => {
sound.dispose()
})
sounds.clear()
}
// does not work as well as it may seem.. some of these options can't be mutated
const applyOptions = (sound, options) => {
if (options) {
sound.autoplay = options.autoplay || false
sound.loop = options.loop || false
if (typeof options.volume !== 'undefined') {
sound._volume = options.volume
sound.setVolume(options.volume)
}
sound.spatialSound = options.spatialSound || false
sound.maxDistance = options.maxDistance || 100
sound.useCustomAttenuation = options.useCustomAttenuation || false
sound.rolloffFactor = options.rolloffFactor || 1
sound.refDistance = options.refDistance || 1
sound.distanceModel = options.distanceModel || 'linear'
sound._playbackRate = options.playbackRate || 1
sound.setPlaybackRate(options.playbackRate || 1)
// doubt this works
sound._streaming = options.streaming || false
}
}
BABYLON.Sound.FromAtlas = (name, filepath, scene, options) => {
const cachedSound = sounds.get(filepath)
if (!cachedSound) {
throw new Error(filepath + ' Sound not found in SoundAtlas')
}
const sound = cachedSound.clone()
sound.name = name
applyOptions(sound, options)
// forces autoplay if it was set
if (options && options.autoplay) {
sound.play()
}
return sound
}
export default { setScene, setAssetsManager, add, dispose }
class SoundPool {
constructor() {
this.scene = null
this.soundsManual = {}
this.sounds = {}
this.allocations = 0
this.allocationsManual = 0
// debug check
//setInterval(() => {
// console.log('sound pool', this.sounds, this.allocations, this.allocationsManual)
//}, 3000)
}
init(scene) {
this.scene = scene
}
allocate(name) {
const sound = BABYLON.Sound.FromAtlas(name, name, this.scene)
sound.onEndedObservable.add(() => {
//console.log('returning sound')
this.release(sound)
})
this.sounds[name].push(sound)
this.allocations++
}
allocateManual(name) {
const sound = BABYLON.Sound.FromAtlas(name, name, this.scene)
this.soundsManual[name].push(sound)
this.allocationsManual++
}
// gets or creates a sound and sound is automatically returned to pool when play finishes
get(name) {
if (!this.sounds[name]) {
this.sounds[name] = []
}
if (this.sounds[name].length === 0) {
this.allocate(name)
}
return this.sounds[name].pop()
}
// gets or creates a sound, does not return to pool (must call releaseManual to return it)
// for use when you have some code that may start playing the sound again on its own
getManual(name) {
if (!this.soundsManual[name]) {
this.soundsManual[name] = []
}
if (this.soundsManual[name].length === 0) {
this.allocateManual(name)
}
return this.soundsManual[name].pop()
}
release(obj) {
this.sounds[obj.name].push(obj)
}
releaseManual(obj) {
this.soundsManual[obj.name].push(obj)
}
}
const singleton = new SoundPool()
export default singleton
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.