Skip to content
This repository was archived by the owner on Nov 12, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/audio/Audio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Audio as StaticAudio, PositionalAudio, AudioLoader } from 'three'
import { defineComponent, watch } from 'vue'
import Object3D from '../core/Object3D'

type ConcreteAudio = StaticAudio | PositionalAudio

export interface AudioSetupInterface {
audio?: ConcreteAudio
streamedAudio?: HTMLAudioElement
}

export default defineComponent({
extends: Object3D,
name: 'Audio',
props: {
src: { type: String, required: false },
volume: { type: Number, default: 1.0 },
isStreamed: { type: Boolean, default: true }
},
setup (): AudioSetupInterface {
let streamedAudio = new Audio();
return { streamedAudio }
},
watch: {
volume: function(value) {
this.audio?.setVolume(value)
},
src: function(value) {
this.stop()
this.loadAudioAndPlay()
}
},
methods: {
initAudio(audio: ConcreteAudio) {
this.audio = audio
this.initObject3D(this.audio)
this.loadAudioAndPlay()
},
loadAudioAndPlay() {
if (!this.src) return undefined

if (this.isStreamed) {
this.loadAudioFromStream()
} else {
this.loadAudioFromMemory()
}
},
loadAudioFromMemory() {
const audioLoader = new AudioLoader();
const instance = this
audioLoader.load(this.src!, function( buffer ) {
console.log('loaded audio from memory')
instance.audio?.setBuffer( buffer );
instance.play()
});
},
loadAudioFromStream() {
this.streamedAudio = new Audio(this.src);
this.streamedAudio.loop = true;
this.streamedAudio.preload = 'metadata';
this.streamedAudio.crossOrigin = 'anonymous';
const instance = this
this.streamedAudio.addEventListener("loadedmetadata", function(_event) {
console.log('loaded audio from stream')
instance.play()
});
this.audio?.setMediaElementSource(this.streamedAudio)
},
play() {
if (this.isStreamed) {
this.streamedAudio?.play()
} else {
this.audio?.play()
}
},
stop() {
if (this.isStreamed) {
if (!this.streamedAudio) { return }
this.streamedAudio.pause()
this.streamedAudio.currentTime = 0;
} else {
this.audio?.stop()
}
}
},
__hmrId: 'Audio',
})
31 changes: 31 additions & 0 deletions src/audio/AudioListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineComponent, ComponentPublicInstance, InjectionKey, inject } from 'vue'
import { AudioListener } from 'three'
import Object3D from '../core/Object3D'

//TODO: currently the AudioListener is inheriting Object3D so it must be a
// child of Scene. However the AudioListener should actually be possible to be
// a child of Camera... Then again Camera is said to extend Object3D in the
// future... so might be better to refactor Camera first?

export interface AudioListenerSetupInterface {
audioListener?: AudioListener
}

export default defineComponent({
extends: Object3D,
name: 'AudioListener',
setup():AudioListenerSetupInterface {
return {}
},
created() {
this.audioListener = new AudioListener()
this.initObject3D(this.audioListener)

if (!this.renderer) {
console.error('Renderer not found')
return
}
this.renderer.audioListener = this.audioListener
},
__hmrId: 'AudioListener',
})
40 changes: 40 additions & 0 deletions src/audio/PositionalAudio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { defineComponent, inject } from 'vue'
import { PositionalAudio } from 'three'
import Audio from './Audio'
import { bindProp } from '../tools'

export default defineComponent({
extends: Audio,
name: 'PositionalAudio',
props: {
refDistance: { type: Number, default: 1.0 },
maxDistance: { type: Number, default: 1.0 },
rolloffFactor: { type: Number, default: 1.0 },
distanceModel: { type: String, default: 'inverse' },
},
created() {

if (!this.renderer) {
console.error('Renderer not found')
return
}

if (!this.renderer.audioListener) {
console.error("No AudioListener component found in the Renderer's child components tree!")
return
}

const positionalAudio = new PositionalAudio(this.renderer.audioListener)
this.initAudio(positionalAudio)
this.bindProps()
},
methods: {
bindProps() {
['refDistance', 'maxDistance', 'rolloffFactor', 'distanceModel'].forEach(p => {
console.log('initialised positionalAudio props')
bindProp(this.$props, p, (this.audio as PositionalAudio).panner)
})
},
},
__hmrId: 'AudioTest',
})
24 changes: 24 additions & 0 deletions src/audio/StaticAudio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineComponent, inject } from 'vue'
import { Audio as StaticAudio } from 'three'
import Audio from './Audio'

export default defineComponent({
extends: Audio,
name: 'StaticAudio',
created() {

if (!this.renderer) {
console.error('Renderer not found')
return
}

if (!this.renderer.audioListener) {
console.error("No AudioListener component found in the Renderer's child components tree!")
return
}

const staticAudio = new StaticAudio(this.renderer.audioListener)
this.initAudio( staticAudio )
},
__hmrId: 'StaticAudio',
})
3 changes: 3 additions & 0 deletions src/audio/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as AudioListener } from './AudioListener'
export { default as PositionalAudio } from './PositionalAudio'
export { default as StaticAudio } from './StaticAudio'
7 changes: 6 additions & 1 deletion src/core/Renderer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-use-before-define */
import { Camera, Scene, WebGLRenderer, WebGLRendererParameters } from 'three'
import { Camera, Scene, WebGLRenderer, WebGLRendererParameters, AudioListener } from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { ComponentPublicInstance, defineComponent, InjectionKey, PropType, watchEffect } from 'vue'
import { bindObjectProp } from '../tools'
Expand Down Expand Up @@ -75,6 +75,7 @@ export interface RendererInterface extends RendererSetupInterface {
scene?: Scene
camera?: Camera
composer?: EffectComposer
audioListener?: AudioListener

onInit(cb: InitCallbackType): void
onMounted(cb: MountedCallbackType): void
Expand Down Expand Up @@ -181,6 +182,10 @@ export default defineComponent({
get: function(): EffectComposer | undefined { return this.three.composer },
set: function(composer: EffectComposer): void { this.three.composer = composer },
},
audioListener: {
get: function(): AudioListener | undefined { return this.three.audioListener },
set: function(audioListener: AudioListener): void { this.three.audioListener = audioListener },
}
},
provide() {
return {
Expand Down
3 changes: 2 additions & 1 deletion src/core/useThree.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Camera, Object3D, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer, WebGLRendererParameters } from 'three'
import { Camera, Object3D, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer, WebGLRendererParameters, AudioListener } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import usePointer, { PointerConfigInterface, PointerPublicConfigInterface, PointerInterface } from './usePointer'
Expand Down Expand Up @@ -32,6 +32,7 @@ export interface ThreeInterface {
composer?: EffectComposer
camera?: Camera
cameraCtrl?: OrbitControls
audioListener?: AudioListener
scene?: Scene
pointer?: PointerInterface
size: SizeInterface
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './materials/index'
export * from './meshes/index'
export * from './models/index'
export * from './effects/index'
export * from './audio/index'

// export * from './components/index'

Expand Down
4 changes: 4 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export const TroisJSVuePlugin = {
'TiltShiftPass',
'UnrealBloomPass',
'ZoomBlurPass',

'AudioListener',
'PositionalAudio',
'StaticAudio'
]

comps.forEach(comp => {
Expand Down