Hacer que las luces reaccionen al audio: 5 pasos

Hacer que las luces reaccionen al audio: 5 pasos

Tabla de contenido:

Anonim

Antes de comenzar un proyecto, debe hacer un concepto aproximado de lo que debería poder hacer. La idea básica es simple: una señal de audio estéreo es leída por un controlador, que luego realiza algunos cálculos matemáticos y genera un patrón en una matriz de LED. Además, debe haber una pantalla de retroalimentación y de alguna manera para controlar el sistema.

Concepto de hardware

El controlador para este proyecto debe poder leer 2 valores analógicos una vez, lo que significa que el controlador necesita 2 unidades ADC. También debe admitir DMA, para que la adquisición de datos sea independiente de la CPU. El DMA también ayudaría a enviar los datos a los controladores LED. Si bien un controlador Cortex M4 le daría más poder de cómputo, decidí usar un controlador atxmega128A3U, porque son más baratos y más fáciles de obtener (se venden con un cargador de arranque preprogramado en la página de eBay alemana para las personas que no tienen programador) a la vez que proporciona todas las funciones requeridas.

La señal de audio necesita ser amplificada y llevada a un nivel para que el controlador la lea correctamente. Además, la señal debe filtrarse para eliminar los errores de cálculo en la FFT. Si bien no es absolutamente necesario, se agregó un AGC. Esto sirve como protección de la etapa de entrada de audio y suaviza los cambios extremos en el nivel de la señal.

Como LED elijo WS2812 LED, principalmente porque todavía tenía algunos, pero también porque son cómodos de usar. Los LED se colocaron detrás de pelotas de ping-pong, que sirven como difusor para la luz. Los LEDs se dispusieron como una matriz de 7x6.

Por último, agregué una pantalla OLED basada en I2C y un codificador rotatorio con botón como interfaz de entrada-salida para el usuario. Esto nos permite depurar fácilmente el programa e implementar un menú simple para controlar el proyecto terminado.

Esta configuración nos permite muestrear los datos de audio, procesarlos y hacer una buena animación en la matriz de LED.

En la imagen también se puede ver un amplificador de audio con altavoz. Esto se usa para escuchar la señal, que es muestreada por el controlador. Para la versión final del proyecto no será utilizado.

La idea básica de la funcionalidad.

El análisis de audio en tiempo real utiliza una gran cantidad de recursos, que el controlador utilizado no tiene. Con el sistema actual es posible muestrear datos y hacer todos los cálculos 20 veces por segundo. Si bien esto es suficiente para hacer que ciertas reacciones se vean bastante bien, las animaciones más complejas se verán descuidadas, si las presentas con 20 cuadros por segundo.

El controlador actualiza la pantalla a una velocidad de 60 cuadros por segundo. Esto hace que las animaciones se vean muy suaves, especialmente si desvaneces el color. El sistema de animación se alimenta con nuevos datos 20 veces por segundo y utiliza estos datos como plantilla para la animación. Si bien este enfoque está lejos de ser una reacción en tiempo real a la música, es suficiente para engañar a la percepción humana en una reacción inmediata.

Paso 2: Hardware

Puedes encontrar los esquemas como proyecto KiCAD en github

Si bien los esquemas parecen abrumadores a primera vista, en realidad son bastante simples. Hay 2 etapas de entrada de audio, porque hay dos entradas de audio. Uno del canal izquierdo y otro del canal derecho. Cada etapa de entrada consta de un divisor de voltaje, un AGC y un filtro. El divisor de voltaje sirve como protección, porque tiene una alta resistencia y reduce el nivel de voltaje de la señal. A continuación, el AGC es una segunda forma de protección, pero la razón principal es igualar los cambios extremos en la amplitud de la señal. El filtro es un paso de banda, que permite frecuencias de entre 16Hz y 16kHz. Esto es aproximadamente el rango de frecuencia que un adulto humano puede escuchar. Si bien no es completamente necesario, siempre se debe usar un filtro para reducir la cantidad de alias / error de cálculo en la FFT.

El resto de los esquemas son los circuitos predeterminados necesarios para que el controlador funcione. La fuente de alimentación tiene varios inductores para filtrar y suavizar la ondulación de la tensión de alimentación. Esto aumenta la calidad de las señales analógicas. Esta etapa de entrada fue tomada de este proyecto.

El amplificador entre el filtro y el controlador (U5) se agregó en este prototipo en caso de que la señal fuera demasiado débil. Pero resultó que esta etapa de amplificador no es necesaria. Aunque no es necesario, todavía estabiliza la señal. El conector para el potenciómetro estéreo se puede acortar entre los pines 1-3 y 2-4.

Paso 3: Software

Puede encontrar el software como Atmel Studio Project en github

Hay 2 proyectos en este git. El proyecto Básico incluye todas las funciones básicas para medir y calcular los datos. También incluye una clase para WS2812 LED. El proyecto Matrix incluye todos los datos de mi proyecto basado en una matriz de LED de 7x6 con WS2812 LED. Como esto es muy específico, este código es más una inspiración y un ejemplo de lo que puede hacer.

Esta parte es la más compleja de este proyecto, ya que el software necesita hacer muchas cosas.

Muestreo de los datos

Primero necesitas ingresar los datos al controlador. Esto se hace muestreando la señal de audio con un ADC. La frecuencia de muestreo depende del rango de frecuencia con el que desea trabajar. Si muestrea la señal con una frecuencia de 32 kHz, puede recrear señales de hasta 16 kHz con la transformación de Fourier (FT). Este es el rango de frecuencia con el que queremos trabajar.

Para hacer que esto funcione, se configura un temporizador que se dispara con una frecuencia de 32 kHz y lee un valor del ADC cada vez que esto sucede. Dado que el controlador atxmega tiene un controlador DMA, se usa para automatizar el proceso. DMA (Acceso directo a memoria) le permite mover datos de un punto en la memoria a otro completamente en hardware. El DMA se configura de una manera, que mueve el resultado del ADC a un lugar específico en la RAM cada vez que es disparado por el temporizador. Muestreamos 128 valores por canal.

Transformación rápida de Fourier (FFT)

Usamos la biblioteca FFT de Elmchan para este proyecto.

Generalmente, un FT usa mucho tiempo de cálculo, pero le permite recrear cada frecuencia dentro del espectro de señal dado. La FFT simplifica este proceso y lo acelera. La desventaja es que no le da frecuencias específicas sino bandas de frecuencia. Para los propósitos de este proyecto, estos datos son lo suficientemente buenos.

Como muestreamos 128 valores, la FFT nos dará 64 resultados. Debido a la frecuencia de muestreo (32 kHz) podemos recrear frecuencias de hasta 16 kHz, que serán divididas en 64 bandas por la FFT. 16000/64 = 250. Esto significa que cada banda FFT describe el nivel de 250Hz. La banda 1 de FFT proporciona un valor para 1Hz a 250Hz, la banda 2 para 251Hz a 500Hz, la banda 3 para 501Hz a 750Hz y así sucesivamente. Al aumentar el número de muestras de entrada, podría aumentar esta resolución, pero también aumentar el tiempo de cálculo necesario.

Esas 64 bandas se consolidan en 7 bandas, que se utilizan para la matriz, que tiene un ancho de 7 LED. Algunos efectos todavía se basan en las 64 bandas originales.

El algoritmo para calcular la FFT está codificado de forma que no realiza todos los pasos de cálculo a la vez, sino que los divide en partes más pequeñas. Esto se hace para que el controlador tenga tiempo para calcular otra cosa entre esos pasos. Si no haces esto, las animaciones a veces tartamudearán, lo que no se ve bien.

El oído humano reacciona de manera diferente a ciertas frecuencias. Es muy sensible a las frecuencias de alrededor de 4 kHz y no es sensible a frecuencias muy bajas y altas. En una señal de audio, las frecuencias bajas tendrán una amplitud alta, lo que conduce a resultados FFT altos, y amplitudes bajas en las frecuencias medias, lo que conduce a resultados FFT bajos. Esto hace que sea difícil hacer cálculos con. Para contrarrestar esto se utiliza un sistema de ponderación inversa muy simple. Los coeficientes se calculan previamente para cada banda y se multiplican con cada una de las 64 bandas FFT.

Detección de golpes

La idea básica detrás de esto es simple: promediar la potencia del nivel de sonido. Si el nivel de potencia actual es muy superior a este promedio, tiene un ritmo. Para hacer esto correctamente, necesitaría muestrear la señal de audio continuamente, lo que no hacemos. Pero todavía vamos a utilizar esta idea.

Los resultados de la FFT se dividen en 3 bandas: baja, media y alta. Esos valores se promedian a lo largo del tiempo como media móvil. Si el nivel actual es superior al 50% al 100% (según la banda), este promedio se evalúa como tiempo. Esto funciona muy bien con un metrónomo, que produce un sonido simple, pero no funciona muy bien con señales complejas como las canciones. La detección de tiempos en la banda inferior funciona mejor y la banda media es peor cuando hay voces en la canción.

¿Qué hacer con esos datos?

Como ejemplo explicaré cómo funciona la visualización de espectro mono. Puede encontrar el código en el proyecto completo en animations.cpp en la función void anim_monospektrum_step (). Esta animación se utiliza en el video de introducción.

Los valores de las 7 bandas consolidadas se establecen como valores objetivo para la altura de las columnas. Se ha escalado de manera que este valor oscile entre 0 y 9000. El desplazamiento del color de destino se establece mediante la amplitud. Los valores reales, que se utilizarán para calcular la salida, cambian lentamente su valor para coincidir con el valor objetivo. Esto hace que la animación se vea muy fluida y evita saltos repentinos.

La matriz tiene una altura de 6 LED. Esto significa que cada LED cubre 9000/6 = 1500 conteos del valor real. Si el valor real de una banda es 1500, solo se encenderá el LED en la parte inferior. Si el valor fuera 3000, se encenderían los dos LED más inferiores. Mientras el valor real sea mayor que 1500, encendemos un LED más y restamos 1500 del valor. Si, después de este proceso, el valor no es 0, el LED que se encuentra sobre el último LED completamente iluminado también se ilumina, pero el brillo se escala con el valor. Cuanto menor sea el valor restante, menor será el brillo.

Como detalle adicional, las bandas parpadearán cuando se detecte un tiempo dentro de esta banda.

Paso 4: Versión simplificada y primeros pasos

La parte más compleja del hardware es la etapa de entrada de audio. Si no desea utilizar un estéreo, sino una señal mono, simplemente puede cortar una de las etapas de entrada. Si desea que sea aún más simple, solo podría ser una etapa de entrada muy simple, que tiene una alta resistencia y solo un filtro. Puede que necesite agregar otra etapa de amplificador a esto.

Si está utilizando un Arduino, no es posible copiar y pegar el código fuente provisto, ya que la placa Arduino no se basa en un controlador atxmega.

Si desea utilizar este proyecto con otro controlador, debe adaptar el código usted mismo. Los siguientes pasos deberían darle una idea sobre qué hacer:

Lo primero que debe hacer es muestrear la señal de audio. Necesitamos 128 puntos de datos y queremos una frecuencia de muestreo de aproximadamente 32 kHz. La forma más sencilla de hacer esto es hacer un bucle, que lea el ADC y luego se detenga durante unos 30µs. El retraso de 30 µs en combinación con el tiempo necesario para leer los datos debe proporcionar una frecuencia de muestreo aproximadamente precisa.

El siguiente paso es la FFT. Un buen tipo puso la biblioteca FFT utilizada en este proyecto en una biblioteca conveniente para Arduino. Esto viene con un ejemplo, que explica cómo lo usas. El ejemplo utiliza el modo de ejecución libre del ADC, que desafortunadamente no se ejecuta a 30kHz. Esto no es necesariamente algo malo, pero su rango de frecuencia se verá afectado y sus depósitos de FFT tendrán una resolución diferente. Por supuesto, también puede consultar el proyecto oficial de la biblioteca FFT.

Si desea implementar una detección de latido, eche un vistazo a la explicación en la parte del software o el código de mi proyecto. Esta es matemática básica y puede ser copiada / pegada.

Lo que pasa después de eso es tu imaginación. Los métodos más utilizados para hacer animaciones dulces, colores difuminados o lo que quieras hacer son estos:

  • valor objetivo / real: el valor objetivo se deriva de los datos de FFT. El valor real cambia lentamente hasta que alcanza el valor objetivo.
  • media móvil: Recuerdas los últimos valores de X. Sume esos valores y divídalos por X. Esto le da el promedio móvil
  • promedio móvil ponderado: Esto es algo así como el promedio móvil, pero los valores más nuevos tienen una mayor influencia en el resultado que los valores anteriores. value = ((value * (NUM - 1)) + new_value) / NUM. NUM puede ser elegido libremente; Más alto significa que se adapta más lento / más suave.

Esos métodos te permiten hacer animaciones de aspecto fluido. Si toma los datos directamente de la FFT, es posible que vea saltos repentinos, p. Ej. su color se desvanece. Utilizo el sistema de valores real / objetivo y el promedio móvil ponderado la mayor parte del tiempo.

Un ejemplo muy simple sobre cómo usar estas fórmulas es esta línea de código: color = ((color * 15) + fft_bucket_h_l) / 16;

Calcula un promedio móvil ponderado de la cubeta FFT con el valor más alto en el canal izquierdo. Este valor se puede usar para establecer el color de un LED RGB. ¡Felicidades! Acaba de crear un LED, que cambia de color según la frecuencia más dominante.

A primera vista, todas estas matemáticas pueden parecer abrumadoras, pero afortunadamente todo el trabajo duro ya está hecho. Si dedicas un tiempo a trabajar en este proyecto, verás que puedes lograr efectos asombrosos con algunas matemáticas simples que aprendiste en la escuela.

Datos importantes para su uso.

Se puede acceder a los datos FFT a través de la clase fft así:

fft_result_t * fft_left = fft.getLeft (); para el canal 1

fft_result_t * fft_right = fft.getRight (); para el canal 2

La estructura de resultados se ve así:

typedef struct {

espectro uint16_t FFT_N / 2;

uint16_t adc_min, adc_max;

} fft_result_t;

espectro es una matriz de 64 elementos, que contienen el resultado de la FFT. adc_min y adc_max son los valores mínimo y máximo de la señal.

Dentro de animation.cpp, donde debe colocar su código de animación, tiene acceso a las siguientes variables globales

uint16_t bands_l ANIM_BAND_NUM, bands_r ANIM_BAND_NUM; Arreglos que contienen los datos de los 7 condensados.

bandas uint16_t amplitude_l, amplitude_r; amplitud, derivada de los valores adc_min y adc_max

uint8_t beats, bpm_h, bpm_m, bpm_l, bpm_all; beats contiene una máscara de bits, que le permite verificar si hubo un beat en una banda específica. La otra variable le da los tiempos por minuto para la banda alta, media, baja y todas (cualquiera)

uint8_t fft_bucket_h_l, fft_bucket_h_r, fft_bucket_l_l, fft_bucket_l_r; número (0 … 63) del grupo FFT con el valor más alto y más bajo Para todas las variables, _l se refiere al canal izquierdo y _r a la derecha.

Paso 5: Conclusión

Si bien este sistema, especialmente si creas tu propia versión con un Arduino, no parece muy potente, te permite crear luces y animaciones de aspecto elegante que reaccionan al sonido. La versión "avanzada" basada en el controlador atxmega ofrece mucha potencia, gracias a las opciones DMA. En contraste con la mayoría de los otros proyectos de este tipo, tiene muchas opciones sobre cómo desea reaccionar. También es completamente independiente de otros dispositivos. Solo necesita una fuente de alimentación y la señal de audio como entrada y puede controlar diferentes dispositivos.

Este proyecto se centra en la salida en una matriz de LED, pero con la ayuda de un FFT también puede realizar diferentes proyectos. Una idea diferente sería hacer un bloqueo, que se abre cuando tocas una cierta melodía. Para esto, la melodía debe ser muy simple y las frecuencias de los tonos no deben estar muy juntas, para que pueda hacer una mejor distinción entre ellos. Piensa en algo así como las canciones de Ocarina en Zelda. También debe aumentar el número de muestras para la FFT, para aumentar la resolución de frecuencia. Luego, debe buscar un patrón determinado y cuando esto sea correcto, puede mover un servo para abrir / cerrar un bloqueo.

Espero que esta guía te haya inspirado y que hayas aprendido algo nuevo para tus propios proyectos.