Sure! The last version where everything worked fine was 7.51.3. After upgrading to 7.52.2, I noticed that sounds were no longer audible. There were no errors in the console, so I suspect the issue might be on my side.
I apologize for not providing a Playground example. My project is quite large, and it will take some time to extract a minimal reproducible case. However, I can show you the class I use for sound playback, which stopped working after the update. Again, it’s entirely possible that the issue is due to something on my end.
Here is a standalone class I wrote as an abstraction layer for handling sounds:
import { AbstractMesh, AssetsManager, Scene, Sound } from 'babylonjs'
class SoundPlayer {
private readonly baseUrl: string = '/sounds/loader/'
private readonly scene: Scene
private assetsManager: AssetsManager
private mesh: AbstractMesh | null = null
sounds: { [key: string]: Sound[] } = {}
volume: number
constructor(scene: Scene, baseUrl: string, mesh: AbstractMesh | null, volume: number = 1) {
this.scene = scene
this.assetsManager = new AssetsManager(this.scene)
this.baseUrl = baseUrl
this.mesh = mesh
this.volume = volume
}
setVolume(volume: number) {
Object.values(this.sounds).forEach((soundArray) => {
soundArray.forEach((sound) => {
if (sound.isPlaying) {
if (this.volume > 0) sound.setVolume(this.volume)
}
})
})
this.volume = volume
}
setMesh(mesh: AbstractMesh | null) {
this.mesh = mesh
}
playSound(name: string, volume: number = 1.0, loop: boolean = false, fade: boolean = false, playbackRate: number = 1): Sound | undefined {
if (name in this.sounds) {
const randomIndex = Math.floor(Math.random() * this.sounds[name].length)
return this.playSoundFile(this.sounds[name][randomIndex], volume, loop, fade, playbackRate)
} else {
console.error(`Sound play '${name}' is not defined.`)
}
}
stopSoundFadeOut(name: string, fadeout: boolean = true): void {
if (name in this.sounds) {
this.sounds[name].forEach((sound: Sound) => {
if (fadeout) {
const fadeDuration = 3000 // Adjust fade duration as needed
const intervalDuration = 50 // Adjust interval duration as needed
const initialVolume = sound.getVolume()
let currentVolume = initialVolume
const interval = setInterval(() => {
currentVolume -= initialVolume / (fadeDuration / intervalDuration)
sound.setVolume(Math.max(currentVolume, 0))
if (currentVolume <= 0) {
clearInterval(interval)
sound.stop()
}
}, intervalDuration)
} else {
sound.stop()
}
})
} else {
console.error(`Sound '${name}' is not defined.`)
}
}
getSound(name: string): Sound | undefined {
if (name in this.sounds) {
const sounds = this.sounds[name]
if (sounds.length > 0) {
return sounds[0]
} else {
console.error(`No sounds available for '${name}'.`)
return undefined
}
} else {
console.error(`Sound '${name}' is not defined.`)
return undefined
}
}
stopSound(name: string) {
if (name in this.sounds) {
this.sounds[name].forEach((sound: Sound) => {
sound.stop()
})
} else {
console.error(`Sound '${name}' is not defined.`)
}
}
playSoundFile(
sound: Sound,
volume: number = 1.0,
loop: boolean = false,
fade: boolean = false,
playbackRate: number = 1,
): Sound | undefined {
sound.setVolume(0)
sound.loop = loop
sound.setPlaybackRate(playbackRate)
sound.autoplay = true
if (this.mesh) {
sound.spatialSound = true
sound.setPosition(this.mesh.position)
if (this.scene.activeCamera) {
sound.setLocalDirectionToMesh(this.scene.activeCamera.position)
}
} else {
sound.spatialSound = false
}
if (fade) {
const fadeDuration = 1 // Adjust fade duration as needed
sound.play()
sound.setVolume(0, fadeDuration / 2) // Start with silence and gradually increase volume
setTimeout(() => {
sound.setVolume(volume * this.volume, fadeDuration / 2) // Gradually increase volume to target
}, fadeDuration / 2)
return
}
sound.setVolume(volume * this.volume)
sound.play()
return sound
}
async fetchUrls(urls: Record<string, string[]>) {
try {
for (const action in urls) {
if (Object.prototype.hasOwnProperty.call(urls, action)) {
const actionKey = action as keyof typeof urls
const soundTasks: Promise<Sound>[] = []
urls[actionKey].forEach((url) => {
const soundTask = this.assetsManager.addBinaryFileTask(`${actionKey}-${url}`, this.baseUrl + url)
soundTasks.push(
new Promise((resolve, reject) => {
soundTask.onSuccess = (task) => {
const sound = new Sound(url, task.data as ArrayBuffer, this.scene)
resolve(sound)
}
soundTask.onError = (_, e) => {
reject(`Failed to load sound '${url}': ${e}`)
}
}),
)
})
Promise.all(soundTasks)
.then((sounds) => {
this.sounds[actionKey] = sounds
})
.catch((error) => {
console.error(error)
})
}
}
return await this.assetsManager.loadAsync()
} catch (error) {
console.error('Failed to load sounds:', error)
throw error
}
}
}
export default SoundPlayer
I will dig it more.