How to use AudioClips?

im trying to pass array of AudioClips to this script, but it wont show up in the editor. Is this the wrong AudioClip class?

You can see the old C# script that im converting to TS (it’s disabled). You can see it has a public AudioClips array that shows in editor. That’s how the new TS script should be too.


Original Post on Discord

by user 352282921056468993

Audio clips are at runtime in the browser just urls. So you’ll receive string[]
AudioClipModel is a special type from the timeline

Make sure to make using serializable() a habit too

@serializable()
audioClips : string[];

@serializable(Object3D)
myObjectReference : Object3D;

@serializable(Object3D)
myObjects : Object3D[]

you can omit the type parameter in serializable() for strings, numbers, bools (js primitive types) and for arrays it’s the array content type (see third example above :slightly_smiling_face: )

If you want to use codegen with audioclips you can do a little trick like this:

// declare custom AudioClip type somewhere
type AudioClip = string;
// in your script:
@serializable()
myClips : AudioClip[]; // < the component compiler will use known types, in this case it'll know AudioClip from UnityEngine.AudioClip

Ahh amazing i think that will work. Is there a certain folder assets should go in for a Needle project? I ask because when AudioClip tries to play i get this error here.

Also, i see in the web project assets folder that these assets did transfer from unity, so im a bit confused why they are not found


by user 352282921056468993

Are you trying to use them directly? Then try call resolveUrl(this.sourceIdentifier, myAudioClipUrl) from your script

was using them like this. I’ll try resolveUrl if i can figure out what to put for the arguments

by user 352282921056468993

this.audioSource.play(this.audioClips[i]) should work as well

resolveUrl(this.sourceIdentifier, yourUrl) is what you use - but you dont have to if you use the AudioSource and set the clip there

So there’s a way to do it without AudioSource? How’s that work? The resolveUrl stuff worked to fix those errors, but still didnt play audio. Just gave warning in console saying “failed getting sound.” - I think may be due to bug in AudioSource. Right here it uses “this.Sound” but everywhere else it uses “this.sound”

by user 352282921056468993

here’s warning in console in case that’s useful

by user 352282921056468993

Can you share your code again?

If you just want to play the clip you can use the three audio code yourself or just a html audio element :slightly_smiling_face: (depending on your needs)

import { AudioSource, Behaviour, GameObject, Text, resolveUrl, serializable } from "@needle-tools/engine"

type AudioClip = string;

export class SubtitleController extends Behaviour {

    @serializable()
    audioClips: AudioClip[]

    public subtitles: string[]
    @serializable()
    private audioSource: AudioSource
    private tMP_Text: Text
    public blankTimeAtEnd: number[]
    public upperDoor: GameObject
    public videoPanel: GameObject

    constructor() {
        super()
        this.audioClips = []
        this.subtitles = []
        this.blankTimeAtEnd = []
    }

    start() {
        this.audioSource = this.gameObject.getComponent(AudioSource) as any
        console.log('audioSource==', this.audioSource)
        this.tMP_Text = this.gameObject.getComponent(Text) as any

        if (this.gameObject.name !== "market down") {
            this.audio()
        }
    }

    async audio() {
        if (this.gameObject.name === "intro") {
            await new Promise(resolve => setTimeout(resolve, 2000))
        }
        for (let i = 0; i < this.audioClips.length; i++) {
            this.audioSource.clip = this.audioClips[i]
            this.tMP_Text.text = this.subtitles[i]
            // this.audioSource.play(this.audioSource.clip)
            this.audioSource.play(resolveUrl(this.sourceId, this.audioSource.clip))

            await new Promise(resolve => setTimeout(resolve, (this.audioClips[i].length + this.blankTimeAtEnd[i]) * 1000))

            if (i === this.audioClips.length - 1) {
                this.tMP_Text.text = ""
                if (this.gameObject.name === "intro") {
                    this.upperDoor.traverse((entity) => {
                        const audioComponent = entity.getComponent(AudioSource)
                        if (audioComponent) {
                            audioComponent.play()
                        }
                    })
                }
            }
        }
    }
}

by user 352282921056468993

can you do this same thing with AudioSource? But it’s not a string so would you just do type AudioSource = any;?

by user 352282921056468993

You dont need to do it for audiosource because it already matches the type name in unity (UnityEngine.AudioSource)

Its only relevant if your c# component is being generated from typescript btw

i tried this same thing with SceneManager from Unity, but it didnt work. Although it’s used a bit differently in this new context. I did this:

type SceneManager = any;

@serializable()
private sceneManager: SceneManager

start() {
  this.sceneManager = new SceneManager()
}

but it still doesnt know what SceneManager is - it ends up undefined

by user 352282921056468993

If it doesnt find the type you can annotate it too, one second