Визуализация музыки

Визуализация музыки, это работа со спектром звука. Спектр аудиосигнала, это совокупность простых гармонических волн, на которые можно разложить звуковую волну. Проще говоря, с помощью специального метода Unity, мы из ассета аудио, получаем массив значений типа float, определенной длинны. И уже имея подобный массив, далее можно делать что угодно с этими значениями. Мы предлагаем один из способов визуализации, с помощью баров, выстроенных в линию или в окружность. Дополнительно, добавим в наш визуализатор контроль цвета, три уровня - низкий, средний и высокий, которые будут меняться в зависимости от интенсивности звука.


Нам понадобиться какой-нибудь объект, в роли бара. Например, обычный 3D Cylinder, сделаем из него префаб. Ну и конечно нужен аудио файл, который будем визуализировать.

Добавляем на сцену визуализатор:

using System.Collections;
using UnityEngine;

public class MusicVisulizer : MonoBehaviour {
	
	private enum ControlAxis {x,y,z}

	[SerializeField] private Color high; // максимальный уровень бара
	[SerializeField] private Color middle; // средняя высота
	[SerializeField] private Color low; // низкий бар
	[SerializeField] private float multiplier = 100; // усилитель спектра
	[SerializeField] private float barsHeight = 10; // макс. высота/размер
	[SerializeField] [Range(0, 1)] private float damping = .25f; // сглаживание
	[SerializeField] private ControlAxis controlScale; // выбор оси трансформа
	[SerializeField] private AudioClip[] audioClips; // здесь музыка
	[SerializeField] private AudioSource audioSource; // целевой компонент
	[SerializeField] private MeshRenderer barSample; // шаблон бара
	[SerializeField] private float barSize = 1; // размер бара (смотреть в шаблоне)
	[SerializeField] private bool drawCircle = true; // выстроить бары в окружность
	[SerializeField] private bool showColors = true; // использовать цвета баров

	private int barCount = 128; // сколько баров создать (степени двойки: 32, 64, 128)
	private float[] musicData = new float[1024]; // массив аудио спектра (степени двойки начиная от 64, 128, 256, 512, 1024)
	private int index = -1;
	private MeshRenderer[] bars;
	private float min = 0.01f; // мин. размер бара

	void CreateBars() // создаем бары и ставим их в линию
	{
		float x = -(barCount * barSize) / 2;
		bars = new MeshRenderer[barCount];
		for(int i = 0; i < barCount; i++)
		{
			MeshRenderer b = Instantiate(barSample, transform) as MeshRenderer;
			b.transform.position = new Vector3(x, 0, 0);
			if(controlScale == ControlAxis.x) b.transform.localScale = new Vector3(min, b.transform.localScale.y, b.transform.localScale.z);
			else if(controlScale == ControlAxis.y) b.transform.localScale = new Vector3(b.transform.localScale.x, min, b.transform.localScale.z);
			else b.transform.localScale = new Vector3(b.transform.localScale.x, b.transform.localScale.y, min);
			bars[i] = b;
			x += barSize;
		}
	}

	void Start()
	{
		audioSource = GetComponent<AudioSource>();
		ChangeSound();
		CreateBars();
		if(drawCircle) DrawCircle(transform.position, bars);
	}

	void Update()
	{
		Visulization();
		if(Input.GetKeyDown(KeyCode.Space)) ChangeSound(); // смена трека клавишей "Space"
	}

	void ChangeSound() // следующий трек
	{
		index++;
		if(index > audioClips.Length - 1) index = 0;
		audioSource.clip = audioClips[index];
		audioSource.Play();
	}

	void DrawCircle(Vector3 center, MeshRenderer[] array) // окружность из баров
	{
		float radius = (array.Length / Mathf.PI) / 2f * barSize;
		float step = 360f / array.Length;
		float j = step;

		for(int i = 0; i < array.Length; i++)
		{
			float angle = j * Mathf.PI / 180f;
			float x = (center.x + radius * Mathf.Cos(angle));
			float z = (center.z + radius * Mathf.Sin(angle));
			array[i].transform.position = new Vector3(x, center.y, z);
			j += step;
		}
	}

	void Visulization() // визуализация аудио спектра
	{
		float value = 0;

		audioSource.GetSpectrumData(musicData, 0, FFTWindow.Triangle); // получаем спектр аудио файла

		for(int i = 0; i < bars.Length; i++)
		{
			value = musicData[i] * multiplier;
			value = Mathf.Clamp(value, min, barsHeight);

			if(controlScale == ControlAxis.x)
			{
				bars[i].transform.localScale = Vector3.Lerp(bars[i].transform.localScale, new Vector3(value, bars[i].transform.localScale.y, bars[i].transform.localScale.z), damping);
			}
			else if(controlScale == ControlAxis.y)
			{
				bars[i].transform.localScale = Vector3.Lerp(bars[i].transform.localScale, new Vector3(bars[i].transform.localScale.x, value, bars[i].transform.localScale.z), damping);
			}
			else
			{
				bars[i].transform.localScale = Vector3.Lerp(bars[i].transform.localScale, new Vector3(bars[i].transform.localScale.x, bars[i].transform.localScale.y, value), damping);
			}

			if(showColors) bars[i].material.color = Color.Lerp(bars[i].material.color, GetColor(value/barsHeight), damping);
		}
	}

	Color GetColor(float value)
	{
		if(value > .75f) return high; else if(value > .35f) return middle; else return low;
	}
}

Как видим, сложностей особых нет, можно поиграть с опциями, поэксперементировать с длинной спектра и числом баров, главное соблюдать правило, степени двойки, так как спектр можно получить только в таком формате.

Скачать демо:

У вас нет доступа!
Тестировалось на: Unity 2017.2.0

Комментариев 5

Офлайн
Frejk 6 декабря 2017
как ты делаешь подобное?
Офлайн
Light 6 декабря 2017
Frejk, что делаю? Скрипт делает, я пишу...
Офлайн
TeDj 13 декабря 2017
Light,
Привет! А можно ли эту визуализацию вытащить как .dll, чтобы впоследствии интегрировать в Aimp-муз.плеер, например?
Офлайн
Light 13 декабря 2017
TeDj, нет, так нельзя сделать.
Офлайн
AnfORy 16 декабря 2017
привпт, а можешь сделать ai для шутера, просто я немного не понимаю(для 3 д шутера). затанее спасибо!
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика