Problems with Vector2.SmoothDamp

So, I am making a mock speaker in Unity and I want it to flex under bass notes like a normal speaker. I want it to look smooth, like it would normally. Unfortunately, with what I have here, it isn’t smooth.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent (typeof (AudioSource))]
public class SoundDisplay : MonoBehaviour {
    AudioSource audioSource;
    RectTransform logo;
    public Vector2 defaultSize;
    public Vector2 velocity;
    public float[] samples = new float[512];
    public float[] bassSamples = new float [6];
    public float flexAmount;
    public bool canPunch = true;
    public bool canVibrate = true;
   
    void Start() {
        audioSource = GetComponent<AudioSource>();
        logo = GameObject.Find("LogoPlaceholder").GetComponent<RectTransform>();
        defaultSize = new Vector2(250, 250);
        velocity = new Vector2(100, 100);
    }
   
    void Update() {
        GetAudioFrequencies();
        LogoFlex();
        Array.Copy(samples, 0, bassSamples, 0, 6);
    }
   
    void GetAudioFrequencies() {
        audioSource.GetSpectrumData(samples, 0, FFTWindow.Blackman);
    }
   
    void LogoFlex() {
        flexAmount = ((bassSamples[0] + bassSamples[1] + bassSamples[2] + bassSamples[3] + bassSamples[4] + bassSamples[5]) / 6) * 50;
        Debug.Log(flexAmount);
        if (flexAmount > 0.2) {
            StartCoroutine("Vibrate");
        }
        if (flexAmount > 3) {
            StartCoroutine("Punch");
        }
    }
   
    public IEnumerator Punch() {
        if (canPunch) {
            canPunch = false;
            logo.sizeDelta = Vector2.SmoothDamp(defaultSize, new Vector2(logo.sizeDelta.x + flexAmount * 2, logo.sizeDelta.y + flexAmount * 2), ref velocity, 0.01f, Mathf.Infinity, Time.deltaTime);
            yield return new WaitForSeconds(0.1f);
            logo.sizeDelta = Vector2.SmoothDamp(logo.sizeDelta, defaultSize, ref velocity, 0.01f, Mathf.Infinity, Time.deltaTime);
            canPunch = true;
        }
    }
   
    public IEnumerator Vibrate() {
        if (canVibrate) {
            canVibrate = false;
            logo.anchoredPosition = new Vector2(0, logo.anchoredPosition.y + 1f);
            yield return new WaitForSeconds(0.01f);
            logo.anchoredPosition = new Vector2(0, 0);
            canVibrate = true;
        }
    }
}

Take a look at the Punch coroutine and the variables associated with it. That’s where the problem lies. The sound I am passing to the AudioSource responds to the low frequency notes, the movement just isn’t smooth. Any suggestions?

This isn’t really a coroutine problem.

What you want to do is just a simple Update() function. You would probably alternate the speaker “in and out” every other frame (typically 60hz), and then decide “by how much” based on the intensity of the lower band of your FFT output.

If there is no low freqency output, then the speaker is still. Otherwise it would track the FFT band you’re looking at.

That way as the bass becomes stronger, the deflection in/out will increase.

Not sure of the window on your FFT either though, so that would determine if the FFT output is even going to be responsive enough to show you true beats in the given frequency bands. It takes more sample time (lag) to determine if a lower-frequency component really has changed a lot in its amplitude over time.

1 Like

I removed the coroutines between posting and your answer, and my solution was more or less what you recommended. I thought it would be better to update the size of the object/position every unit of time but failed to realize that Update does that every frame. Figured it all out. Thanks!

1 Like