AudioSource pitch question (C#)

Hello, I’m trying to change the pitch of a sound played on the fly (footsteps foley). I can create and play the sound in one line, which is really neat:

AudioSource.PlayClipAtPoint((AudioClip)Resources.Load("Foley/footstep04"), rightFootJoint.position, .25f);

Now however I would like to change the pitch at random (within a reasonable range), but I don’t know how to access the “pitch()” method and apply it to that sound. Could someone please help me?

Thanks,
Seith

You can use audio.pitch on the game object that has an audiosource attached to it.

For example you can use

audio.pitch = Random.Range(0.1,1.0);

If you use this in the update function, the number of the pitch changes every frame because of the Random.Range function. Ofcourse this will sound strange but you want a pitch that changes at random :slight_smile:

This is a neat function, however, it doesn’t seem to do the whole job you need. Fear not though, this is an incredibly simple function. Create an object at a point, add an audiosource to it, play a clip and destroy it when its done.

Here is a simple function that will do exactly that, with the pitch added. :wink:

	GameObject PlayClipAtPoint(AudioClip clip, Vector3 position, float volume, float pitch){
		GameObject obj = new GameObject();
		obj.transform.position = position;
		obj.AddComponent<AudioSource>();
		obj.audio.pitch = pitch;
		obj.audio.PlayOneShot(clip, volume);
		Destroy (obj, clip.length / pitch);
		return obj;
	}

@willemsenzo: Well that’s the thing. This line of code works without having to add an AudioSource component to any object. As long as there is an AudioListener component on the main camera it just works.

But I’ve tried adding a AudioSource component to the object and using your random code. I’m still using the same line of code to play the sound itself, and while I can see the pitch slider indeed move to random places, the sounds themselves are absolutely unaffected by it (?!).

@misterb: Thanks for the function. It works fine! The only thing I’m wondering is: Is it really efficient (in terms of speed) when compared to the “PlayClipAtPoint” built-in command?

Yes, it is 99% as effective in speed as the original function, That is really all that function does.

I can’t tell exactly what’s wrong but if the game object which has the audiosource on it doesn’t have an audioclip attached, the pitch won’t affect the playing sound.

Whats wrong is AudioClip.PlayClipAtPoint is a static function that creates a gameobject, audiosource and plays it from a position. it does not return the game object at all, so you cannot affect it like you could a normal audio clip.

@willemsenzo: Yep, that’s probably the reason. Makes sense.

@bigmisterb: Ok, great. Then I’ll integrate it in my scripts. And regarding your previous post, that’s exactly what I was afraid of. Maybe the Unity ninjas will add a “pitch” argument in the “PlayClipAtPoint” command in the future!

Thank you guys for your help! :slight_smile:

Ok I understand and to be honest I never used that particular function so I’m lacking some experience in that field. I guess it depends on what your needs/expectations are how you put the script together :slight_smile:

bigmisterb, your code doesn’t compile because you don’t return a GameObject.

I think the functionality of PlayClipAtPoint should be in the AudioClip class:

static List<AudioSource> playAtPointAudioSources;
public static void PlayAtPoint(this AudioClip clip, Vector3 position, float volume = 1, float pitch = 1) {
	var audioSources = playAtPointAudioSources ?? (playAtPointAudioSources = new List<AudioSource>());
	AudioSource availableAudioSource = audioSources.Find(audioSource => !audioSource.isPlaying);
	if (!availableAudioSource) {
		audioSources.Add(availableAudioSource = new GameObject().AddComponent<AudioSource>());
		availableAudioSource.gameObject.hideFlags = HideFlags.HideInHierarchy;
	}
	availableAudioSource.clip = clip;
    availableAudioSource.transform.position = position;
	availableAudioSource.volume = volume;
    availableAudioSource.pitch = pitch;
    availableAudioSource.Play();		
}
1 Like

or, u could just return the game object. :wink:

I don’t see why you wouldn’t want to use an extension method. You put in in one class, and from every other class, you can do:

(Resources.Load("Foley/footstep04") as AudioClip).PlayAtPoint(rightFootJoint.position, .25F);

Even if you don’t want to include the pitch parameter, it’s cleaner than the original

AudioSource.PlayClipAtPoint((AudioClip)Resources.Load("Foley/footstep04"), rightFootJoint.position, .25f);

Using a method from a class that isn’t AudioClip, I don’t understand it.

I do appreciate the extension method. Thank you for sharing @ Jessy, thank you for starting the thread @ Seith, and thank you for the preliminary function that showed exactly what Unity was doing under the hood @ bigmisterb

1 Like

This is still needed and works great in Unity 2020.3.3f1 but there is some deprecation. Here is the updated version of this… :slight_smile:

GameObject PlayClipAtPoint(AudioClip clip, Vector3 position, float volume, float pitch)
    {
        GameObject obj = new GameObject();
        obj.transform.position = position;
        obj.AddComponent<AudioSource>();
        obj.GetComponent<AudioSource>().pitch = pitch;
        obj.GetComponent<AudioSource>().PlayOneShot(clip, volume);
        Destroy(obj, clip.length / pitch);
        return obj;
    }

That code is pretty lousy to be honest. It allocate uses Get Component etc.
Also you can’t control attenuation curve etc, it’s not set to spatial.

I do not understand why unity thought it was a good idea to set the audio properties on the actual audiosource like they did. It would have been better if the audio properties was a scriptable object that you could carry over to any audio source.

We did our own system were we can pool audiosource, parent them to transforms etc. All this because unity can’t design proper audiosource with good seperation and cohesion :slight_smile:

I tried this.
I can use OnDisable to detach from a transform if the gameobject is going to be destroyed, but it won’t let me detach from something that is being disabled. Any ideas?

We did a complete custom system for that. We have a interface IInteractible the transform you parent to doesn’t need to inherit this interface but our parented play method requires you supply it with one, that interface have its own lifetime event system custom for our game that makes sure the audiosource instance is returned to pool if the interactable happens happens to be put in sleep mode.

So you basically have a custom Disable, a sleep?
And yeah, I’m also having to build my own AudioSourceSettings scriptable, and I was also wondering why it wouldn’t be separated from the audiosource.

Yes. For example our firearms when a player dies and drops a firearm the firearm is returned to the pool after 30 seconds if there is no player close by or looking at it. If no other have asked for a audiosource with those settings it will still be on the firearm and then the audiosource will be returned to the pool.

Yeah really sad we need to reinvent the wheel like this.

GameObject PlayClipAtPoint(AudioClip clip, Vector3 position, float volume, float pitch)
{
    GameObject obj = new GameObject();
    obj.transform.position = position;
    AudioSource source = obj.AddComponent<AudioSource>();
    source.pitch = pitch;
    source.PlayOneShot(clip, volume);
    Destroy(obj, clip.length / pitch);
    return obj;
}

this seems to fix the redundancy of “GetComponent”