pages235/renderer/viewer/three/threeJsSound.ts
2025-03-29 04:48:39 +03:00

67 lines
2 KiB
TypeScript

import * as THREE from 'three'
import { WorldRendererThree } from './worldrendererThree'
export interface SoundSystem {
playSound: (position: { x: number, y: number, z: number }, path: string, volume?: number, pitch?: number) => void
destroy: () => void
}
export class ThreeJsSound implements SoundSystem {
audioListener: THREE.AudioListener | undefined
private readonly activeSounds = new Set<THREE.PositionalAudio>()
private readonly audioContext: AudioContext | undefined
constructor (public worldRenderer: WorldRendererThree) {
}
initAudioListener () {
if (this.audioListener) return
this.audioListener = new THREE.AudioListener()
this.worldRenderer.camera.add(this.audioListener)
}
playSound (position: { x: number, y: number, z: number }, path: string, volume = 1, pitch = 1) {
this.initAudioListener()
const sound = new THREE.PositionalAudio(this.audioListener!)
this.activeSounds.add(sound)
const audioLoader = new THREE.AudioLoader()
const start = Date.now()
void audioLoader.loadAsync(path).then((buffer) => {
if (Date.now() - start > 500) return
// play
sound.setBuffer(buffer)
sound.setRefDistance(20)
sound.setVolume(volume)
sound.setPlaybackRate(pitch) // set the pitch
this.worldRenderer.scene.add(sound)
// set sound position
sound.position.set(position.x, position.y, position.z)
sound.onEnded = () => {
this.worldRenderer.scene.remove(sound)
sound.disconnect()
this.activeSounds.delete(sound)
audioLoader.manager.itemEnd(path)
}
sound.play()
})
}
destroy () {
// Stop and clean up all active sounds
for (const sound of this.activeSounds) {
sound.stop()
sound.disconnect()
}
// Remove and cleanup audio listener
if (this.audioListener) {
this.audioListener.removeFromParent()
this.audioListener = undefined
}
}
playTestSound () {
this.playSound(this.worldRenderer.camera.position, '/sound.mp3')
}
}