Seems like you can’t get spectrum data unless the audio is ACTUALLY playing (ie, i tried setting source time, and then GetSpectrum, but its all zeros)
So your probably stuck with (something like) playing the clip in edit mode, and using editor scripts to capture the data, here’s a starting point for you:
using UnityEngine;
using UnityEditor;
using System.Collections;
public class MeshSpectrum : EditorWindow {
Mesh mesh;
AudioSource source;
AudioClip clip;
Vector3[] meshVerts;
float readInterval, startTime, lastTime, min, max;
bool reading;
Vector3 meshSize = new Vector3(10,2,50), meshSizeOld;
int currentIndex, widthResolution = 64, lengthResolution = 100;
int[] widthOptions = new int[] { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
string[] widthOptionsStrings = new string[] { "64", "128", "256", "512", "1024", "2048", "4096", "8192" };
[MenuItem( "EDITORS/Mesh Spectrum" )]
static void Init () {
MeshSpectrum window = (MeshSpectrum) EditorWindow.GetWindow( typeof( MeshSpectrum ) );
window.Show();
window.position = new Rect( 20, 80, 200, 193 );
}
void Update () {
if ( reading ) {
if ( Time.realtimeSinceStartup - readInterval > lastTime ) {
float[] spectrum = new float[widthResolution];
source.GetSpectrumData( spectrum, 0, FFTWindow.BlackmanHarris );
for ( int x = 0; x < widthResolution; x++ ) {
float f = Mathf.Max( Mathf.Log( spectrum[x] ), -10 );
if ( f > max ) max = f;
if ( f < min ) min = f;
meshVerts[currentIndex * widthResolution + x] = new Vector3( ( x / ( widthResolution - 1f ) ) * meshSize.x, f, ( currentIndex / ( lengthResolution - 1f ) ) * meshSize.z );
}
SceneView.RepaintAll();
lastTime = Time.realtimeSinceStartup;
currentIndex++;
if ( currentIndex == lengthResolution || !source.isPlaying ) {
float scale = 1f / ( max - min );
Vector2[] uvs = new Vector2[lengthResolution * widthResolution];
for ( int y = 0; y < lengthResolution; y++ ) {
for ( int x = 0; x < widthResolution; x++ ) {
meshVerts[y * widthResolution + x].y = ( meshVerts[y * widthResolution + x].y - min ) * scale * meshSize.y;
uvs[y * widthResolution + x] = new Vector2( x / ( widthResolution - 1f ), y / ( lengthResolution - 1f ) );
}
}
int[] tris = new int[( widthResolution - 1 ) * ( lengthResolution - 1 ) * 6];
for ( int y = 0; y < lengthResolution - 1; y++ ) {
for ( int x = 0; x < widthResolution - 1; x++ ) {
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 0] = y * widthResolution + x;
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 1] = ( y + 1 ) * widthResolution + x;
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 2] = y * widthResolution + x + 1;
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 3] = y * widthResolution + x + 1;
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 4] = ( y + 1 ) * widthResolution + x;
tris[( ( y * ( widthResolution - 1 ) ) + x ) * 6 + 5] = ( y + 1 ) * widthResolution + x + 1;
}
}
mesh.Clear();
mesh.vertices = meshVerts;
mesh.triangles = tris;
mesh.uv = uvs;
mesh.RecalculateNormals();
reading = false;
source.Stop();
currentIndex = 0;
}
}
}
}
void OnFocus () {
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
SceneView.onSceneGUIDelegate += this.OnSceneGUI;
}
void OnDestroy () {
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
}
void OnSceneGUI ( SceneView sceneView ) {
Vector3[] verts = new Vector3[] { new Vector3( 0, 0, 0 ), new Vector3( meshSize.x, 0, 0 ), new Vector3( meshSize.x, 0, meshSize.z ), new Vector3( 0, 0, meshSize.z ), new Vector3( 0, meshSize.y, meshSize.z ), meshSize, new Vector3( meshSize.x, meshSize.y, 0 ), new Vector3( 0, meshSize.y, 0 ) };
for ( int i = 0; i < 8; i++ ) {
Handles.DrawLine( verts[i], verts[( i + 1 ) % 8] );
}
Handles.DrawLine( verts[0], verts[3] );
Handles.DrawLine( verts[4], verts[7] );
Handles.DrawLine( verts[2], verts[5] );
Handles.DrawLine( verts[1], verts[6] );
}
void OnGUI () {
EditorGUIUtility.labelWidth = 90;
source = (AudioSource) EditorGUILayout.ObjectField( "Audio source:", source, typeof( AudioSource ), true );
mesh = (Mesh) EditorGUILayout.ObjectField( "Terrain:", mesh, typeof( Mesh ), true );
meshSize = EditorGUILayout.Vector3Field( "Approx size of mesh", meshSize );
if ( meshSizeOld != meshSize ) {
meshSizeOld = meshSize;
SceneView.RepaintAll();
}
widthResolution = EditorGUILayout.IntPopup( "X resolution", widthResolution, widthOptionsStrings, widthOptions );
lengthResolution = Mathf.Max( EditorGUILayout.IntField( "Z resolution", lengthResolution ), 3 );
if ( GUILayout.Button( "I need a mesh!" ) ) {
mesh = new Mesh();
AssetDatabase.CreateAsset( mesh, "Assets/SpectrumMesh.asset" );
Selection.activeObject = mesh;
EditorGUIUtility.PingObject( mesh );
}
GUI.enabled = mesh != null source != null;
if ( GUILayout.Button( "Play and Create Mesh!" ) ) {
float sourceLength = source.clip.length;
readInterval = sourceLength / lengthResolution;
lastTime = Time.realtimeSinceStartup;
currentIndex = 0;
max = float.MinValue;
min = float.MaxValue;
meshVerts = new Vector3[lengthResolution * widthResolution];
reading = true;
source.Play();
}
GUI.enabled = reading;
if ( GUILayout.Button( "Stop" ) ) {
source.Stop();
reading = false;
currentIndex = 0;
}
Rect rect = GUILayoutUtility.GetRect( new GUIContent( "" ), "Button" );
GUI.Box( rect, "" );
if ( reading ) {
GUI.color = new Color(0.45f, 0.6f, 0.85f);
GUI.Box( new Rect( rect.x, rect.y, rect.width * ( (float) currentIndex / lengthResolution ), rect.height ), "" );
GUI.Label( rect, "Working..." );
this.Repaint();
}
}
}
Open the editor window by clicking EDITORS->Mesh Spectrum.
Drag the audio source and a mesh asset into the fields, set values, and click the play button. The clip will play (you might have to toggle audio on in the editor to hear it; you might not actually have to hear it for it to work), and when finished (it takes the length of time that the clip is), the mesh will be set.
You might want to do something with the values, on line 32 I simple take the Log of the spectrum value.
You might also want to do some smoothing, I dunno.