Пример #1
0
    /// <summary>
    /// Returns the current output volume of the scene's AudioListener, using the RMS method.
    /// </summary>
    /// <param name="sampleSize">The number of samples to take, as a power of two. Higher values mean more precise volume.</param>
    /// <param name="channelUsed">The audio channel to take data from.</param>
    public static float GetRMS(int sampleSize, int channelUsed = 0)
    {
#if WEB_MODE
        return(SSWebInteract.GetLoudness());
#else
        sampleSize = Mathf.ClosestPowerOfTwo(sampleSize);
        float[] outputSamples = new float[sampleSize];
        AudioListener.GetOutputData(outputSamples, channelUsed);

        float rms = 0;
        foreach (float f in outputSamples)
        {
            rms += f * f;                                 //sum of squares
        }
        return(Mathf.Sqrt(rms / (outputSamples.Length))); //mean and root
#endif
    }
Пример #2
0
    /// <summary>
    /// Returns a logarithmically scaled and proportionate array of spectrum data from the AudioListener.
    /// </summary>
    /// <param name="spectrumSize">The size of the returned array.</param>
    /// <param name="sampleSize">The size of sample to take from the AudioListener. Must be a power of two. Will only be used in WebGL if no samples have been taken yet.</param>
    /// <param name="windowUsed">The FFTWindow to use when sampling. Unused in WebGL.</param>
    /// <param name="channelUsed">The audio channel to use when sampling. Unused in WebGL.</param>
    /// <returns>A logarithmically scaled and proportionate array of spectrum data from the AudioListener.</returns>
    public static float[] GetLogarithmicSpectrumData(int spectrumSize, int sampleSize, FFTWindow windowUsed = FFTWindow.BlackmanHarris, int channelUsed = 0)
    {
#if WEB_MODE
        sampleSize = SSWebInteract.SetFFTSize(sampleSize); //set the WebGL sampleSize if not already done, otherwise get the current sample size.
#endif
        float[] spectrum = new float[spectrumSize];

        channelUsed = Mathf.Clamp(channelUsed, 0, 1);

        float[] samples = new float[Mathf.ClosestPowerOfTwo(sampleSize)];

#if WEB_MODE
        SSWebInteract.GetSpectrumData(samples); //get the spectrum data from the JS lib
#else
        AudioListener.GetSpectrumData(samples, channelUsed, windowUsed);
#endif

        float highestLogSampleFreq = Mathf.Log(spectrum.Length + 1, 2); //gets the highest possible logged frequency, used to calculate which sample of the spectrum to use for a bar

        float logSampleFreqMultiplier = sampleSize / highestLogSampleFreq;

        for (int i = 0; i < spectrum.Length; i++)                                                                             //for each float in the output
        {
            float trueSampleIndex = (highestLogSampleFreq - Mathf.Log(spectrum.Length + 1 - i, 2)) * logSampleFreqMultiplier; //gets the index equiv of the logified frequency

            //the true sample is usually a decimal, so we need to lerp between the floor and ceiling of it.

            int sampleIndexFloor = Mathf.FloorToInt(trueSampleIndex);
            sampleIndexFloor = Mathf.Clamp(sampleIndexFloor, 0, samples.Length - 2);                                                        //just keeping it within the spectrum array's range

            float value = Mathf.SmoothStep(spectrum[sampleIndexFloor], spectrum[sampleIndexFloor + 1], trueSampleIndex - sampleIndexFloor); //smoothly interpolate between the two samples using the true index's decimal.

#if WEB_MODE
            value = value * (Mathf.Log(trueSampleIndex + 1) + 1); //different due to how the WebAudioAPI outputs spectrum data.
#else
            value = value * (trueSampleIndex + 1);                //multiply value by its position to make it proportionate
            value = Mathf.Sqrt(value);                            //compress the amplitude values by sqrt(x)
#endif
            spectrum[i] = value;
        }
        return(spectrum);
    }
Пример #3
0
    void Start()
    {
        if (outputType == OutputType.PrefabBar)
        {
            bar  = Instantiate(prefab) as GameObject;
            barT = bar.transform;
            barT.SetParent(transform, false);
            barT.localPosition = Vector3.zero;

            Renderer rend = barT.GetChild(0).GetComponent <Renderer>();
            if (rend != null)
            {
                mat = rend.material;
            }
            else
            {
                Image img = barT.GetChild(0).GetComponent <Image>();
                if (img != null)
                {
                    img.material = new Material(img.material);
                    mat          = img.material;
                }
                else
                {
                    Debug.LogWarning("Warning from OutputVolume: The Bar Prefab you're using doesn't have a Renderer or Image component as its first child. Dynamic colouring will not work.");
                    materialColourCanBeUsed = false;
                }
            }
            mat_ValId = Shader.PropertyToID("_Val");
            mat.SetColor("_Color1", MinColor);
            mat.SetColor("_Color2", MaxColor);
        }

#if WEB_MODE
        sampleAmount = SSWebInteract.SetFFTSize(sampleAmount);
#endif
    }
Пример #4
0
    void Update()
    {
        if (sourceType == SourceType.AudioSource && !audioSource.isPlaying)
        {
            return;                                                                 //Freeze bars as they are if the audio source is not playing.
        }
        if (isEnabled)
        {
            //sampleChannel = Mathf.Clamp(sampleChannel, 0, 1); //force the channel to be valid

            if (sourceType != SourceType.Custom)
            {
                if (sourceType == SourceType.AudioListener)
                {
#if WEB_MODE
                    SSWebInteract.GetSpectrumData(spectrum);                            //get the spectrum data from the JS lib
#else
                    AudioListener.GetSpectrumData(spectrum, sampleChannel, windowUsed); //get the spectrum data
                    //Debug.Log(spectrum[0]);
#endif
                }
                else
                {
                    audioSource.GetSpectrumData(spectrum, sampleChannel, windowUsed); //get the spectrum data
                }
            }

#if UNITY_EDITOR    //allows for editing curve while in play mode, disabled in build for optimisation
            float spectrumLength = bars.Length * (1 + barXSpacing);
            float midPoint       = spectrumLength / 2;

            float   curveAngleRads = 0, curveRadius = 0, halfwayAngleR = 0, halfwayAngleD = 0;
            Vector3 curveCentreVector = Vector3.zero;
            if (barCurveAngle > 0)
            {
                curveAngleRads = (barCurveAngle / 360) * (2 * Mathf.PI);
                curveRadius    = spectrumLength / curveAngleRads;

                halfwayAngleR     = curveAngleRads / 2;
                halfwayAngleD     = barCurveAngle / 2;
                curveCentreVector = new Vector3(0, 0, -curveRadius);
                if (barCurveAngle == 360)
                {
                    curveCentreVector = new Vector3(0, 0, 0);
                }
            }
#endif
#if WEB_MODE
            float freqLim = frequencyLimitHigh * 0.76f; //AnalyserNode.getFloatFrequencyData doesn't fill the array, for some reason
#else
            float freqLim = frequencyLimitHigh;
#endif

            for (int i = 0; i < bars.Length; i++)
            {
                Transform bar = bars [i];

                float value;
                float trueSampleIndex;

                //GET SAMPLES
                if (useLogarithmicFrequency)
                {
                    //LOGARITHMIC FREQUENCY SAMPLING

                    //trueSampleIndex = highFrequencyTrim * (highestLogFreq - Mathf.Log(barAmount + 1 - i, 2)) * logFreqMultiplier; //old version

                    trueSampleIndex = Mathf.Lerp(frequencyLimitLow, freqLim, (highestLogFreq - Mathf.Log(barAmount + 1 - i, 2)) / highestLogFreq) * frequencyScaleFactor;

                    //'logarithmic frequencies' just means we want to bias to the lower frequencies.
                    //by doing log2(max(i)) - log2(max(i) - i), we get a flipped log graph
                    //(make a graph of log2(64)-log2(64-x) to see what I mean)
                    //this isn't finished though, because that graph doesn't actually map the bar index (x) to the spectrum index (y).
                    //then we divide by highestLogFreq to make the graph to map 0-barAmount on the x axis to 0-1 in the y axis.
                    //we then use this to Lerp between frequency limits, and then an index is calculated.
                    //also 1 gets added to barAmount pretty much everywhere, because without it, the log hits (barAmount-1,max(freq))
                }
                else
                {
                    //LINEAR (SCALED) FREQUENCY SAMPLING
                    //trueSampleIndex = i * linearSampleStretch; //don't like this anymore

                    trueSampleIndex = Mathf.Lerp(frequencyLimitLow, freqLim, ((float)i) / barAmount) * frequencyScaleFactor;
                    //sooooo this one's gotten fancier...
                    //firstly a lerp is used between frequency limits to get the 'desired frequency', then it's divided by the outputSampleRate (/2, who knows why) to get its location in the array, then multiplied by numSamples to get an index instead of a fraction.
                }

                //the true sample is usually a decimal, so we need to lerp between the floor and ceiling of it.

                int sampleIndexFloor = Mathf.FloorToInt(trueSampleIndex);
                sampleIndexFloor = Mathf.Clamp(sampleIndexFloor, 0, spectrum.Length - 2);                                                 //just keeping it within the spectrum array's range

                value = Mathf.SmoothStep(spectrum[sampleIndexFloor], spectrum[sampleIndexFloor + 1], trueSampleIndex - sampleIndexFloor); //smoothly interpolate between the two samples using the true index's decimal.

                //MANIPULATE & APPLY SAMPLES
                if (multiplyByFrequency) //multiplies the amplitude by the true sample index
                {
#if WEB_MODE
                    value = value * (Mathf.Log(trueSampleIndex + 1) + 1);  //different due to how the WebAudioAPI outputs spectrum data.
#else
                    value = value * (trueSampleIndex + 1);
#endif
                }

#if !WEB_MODE
                value = Mathf.Sqrt(value); //compress the amplitude values by sqrt(x)
#endif

                //Scale by curve
                value = barYScaling.Evaluate(Mathf.Clamp01(value));

                //DAMPENING
                //Vector3 oldScale = bar.localScale;
                float oldYScale = oldYScales[i], newYScale;
                if (value * barYScale > oldYScale)
                {
                    newYScale = Mathf.Lerp(oldYScale, Mathf.Max(value * barYScale, barMinYScale), attackDamp);
                }
                else
                {
                    newYScale = Mathf.Lerp(oldYScale, Mathf.Max(value * barYScale, barMinYScale), decayDamp);
                }

                bar.localScale = new Vector3(barXScale, newYScale, 1);

                oldYScales[i] = newYScale;

                //set colour
                if (useColorGradient && materialColourCanBeUsed)
                {
                    float newColorVal = colorValueCurve.Evaluate(value);
                    float oldColorVal = oldColorValues[i];

                    if (newColorVal > oldColorVal)
                    {
                        if (colorAttackDamp != 1)
                        {
                            newColorVal = Mathf.Lerp(oldColorVal, newColorVal, colorAttackDamp);
                        }
                    }
                    else
                    {
                        if (colorDecayDamp != 1)
                        {
                            newColorVal = Mathf.Lerp(oldColorVal, newColorVal, colorDecayDamp);
                        }
                    }

                    barMaterials[i].SetFloat(materialValId, newColorVal);

                    oldColorValues[i] = newColorVal;
                }

#if UNITY_EDITOR
                //realtime modifications for Editor only
                if (barCurveAngle > 0)
                {
                    float position      = ((float)i / bars.Length);
                    float thisBarAngleR = (position * curveAngleRads) - halfwayAngleR;
                    float thisBarAngleD = (position * barCurveAngle) - halfwayAngleD;
                    bar.localRotation = Quaternion.Euler(barXRotation, thisBarAngleD, 0);
                    bar.localPosition = new Vector3(Mathf.Sin(thisBarAngleR) * curveRadius, 0, Mathf.Cos(thisBarAngleR) * curveRadius) + curveCentreVector;
                }
                else
                {
                    bar.localPosition = new Vector3(i * (1 + barXSpacing) - midPoint, 0, 0);
                }
#endif
            }
        }
        else           //switched off
        {
            foreach (Transform bar in bars)
            {
                bar.localScale = Vector3.Lerp(bar.localScale, new Vector3(1, barMinYScale, 1), decayDamp);
            }
        }
        if ((Time.unscaledTime - lastMicRestartTime) > micRestartWait)
        {
            RestartMicrophone();
        }
    }
Пример #5
0
    /// <summary>
    /// Rebuilds this instance of Spectrum, applying any changes.
    /// </summary>
    public void RebuildSpectrum()
    {
        isEnabled = false;      //just in case

        //clear all the existing children
        int childs = transform.childCount;

        for (int i = 0; i < childs; i++)
        {
            Destroy(transform.GetChild(i).gameObject);
        }

        RestartMicrophone();

        numSamples = Mathf.ClosestPowerOfTwo(numSamples);

#if WEB_MODE
        numSamples = SSWebInteract.SetFFTSize(numSamples);
#endif

        //initialise arrays
        spectrum       = new float[numSamples];
        bars           = new Transform[barAmount];
        barMaterials   = new Material[barAmount];
        oldYScales     = new float[barAmount];
        oldColorValues = new float[barAmount];

        materialColourCanBeUsed = true;

        float spectrumLength = barAmount * (1 + barXSpacing);
        float midPoint       = spectrumLength / 2;

        //spectrum bending calculations
        float   curveAngleRads = 0, curveRadius = 0, halfwayAngleR = 0, halfwayAngleD = 0;
        Vector3 curveCentreVector = Vector3.zero;
        if (barCurveAngle > 0)
        {
            curveAngleRads = (barCurveAngle / 360) * (2 * Mathf.PI);
            curveRadius    = spectrumLength / curveAngleRads;

            halfwayAngleR     = curveAngleRads / 2;
            halfwayAngleD     = barCurveAngle / 2;
            curveCentreVector = new Vector3(0, 0, 1 * -curveRadius);
            if (barCurveAngle == 360)
            {
                curveCentreVector = new Vector3(0, 0, 0);
            }
        }

        //bar instantiation loop
        for (int i = 0; i < barAmount; i++)
        {
            GameObject barClone = Instantiate(barPrefab, transform, false) as GameObject; //create the bars and assign the parent
            //barClone.name = i.ToString();
            barClone.transform.localScale = new Vector3(barXScale, barMinYScale, 1);

            if (barCurveAngle > 0) //apply spectrum bending
            {
                float position      = ((float)i / barAmount);
                float thisBarAngleR = (position * curveAngleRads) - halfwayAngleR;
                float thisBarAngleD = (position * barCurveAngle) - halfwayAngleD;
                barClone.transform.localPosition = new Vector3(Mathf.Sin(thisBarAngleR) * curveRadius, 0, Mathf.Cos(thisBarAngleR) * curveRadius) + curveCentreVector;
                barClone.transform.localRotation = Quaternion.Euler(barXRotation, thisBarAngleD, 0);
            }
            else //standard positioning
            {
                barClone.transform.localPosition = new Vector3(i * (1 + barXSpacing) - midPoint, 0, 0);
            }

            bars[i] = barClone.transform;
            Renderer rend = barClone.transform.GetChild(0).GetComponent <Renderer>();
            if (rend != null)
            {
                barMaterials[i] = rend.material;
            }
            else
            {
                Image img = barClone.transform.GetChild(0).GetComponent <Image>();
                if (img != null)
                {
                    img.material    = new Material(img.material);
                    barMaterials[i] = img.material;
                }
                else
                {
                    if (materialColourCanBeUsed)
                    {
                        Debug.LogWarning("Warning from SimpleSpectrum: The Bar Prefab you're using doesn't have a Renderer or Image component as its first child. Dynamic colouring will not work.");
                        materialColourCanBeUsed = false;
                    }
                }
            }

            int color1Id = Shader.PropertyToID("_Color1"), color2Id = Shader.PropertyToID("_Color2");
            barMaterials[i].SetColor(color1Id, colorMin);
            barMaterials[i].SetColor(color2Id, colorMax);
        }

        materialValId = Shader.PropertyToID("_Val");

        highestLogFreq       = Mathf.Log(barAmount + 1, 2); //gets the highest possible logged frequency, used to calculate which sample of the spectrum to use for a bar
        frequencyScaleFactor = 1.0f / (AudioSettings.outputSampleRate / 2) * numSamples;


        isEnabled = true;
    }