Skip to content

Instantly share code, notes, and snippets.

@timetocode
Last active May 6, 2019 09:41
Show Gist options
  • Save timetocode/c07193efa0628c9d25298631568ff16d to your computer and use it in GitHub Desktop.
Save timetocode/c07193efa0628c9d25298631568ff16d to your computer and use it in GitHub Desktop.
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